05ed037fe8
- DNS: pilot.landvex.com -> 16.170.83.169 - TLS: Let's Encrypt certificate (expires 2026-09-30) - Nginx: reverse proxy with SSL termination - API: https://pilot.landvex.com/api/v1/missions - UI: https://pilot.landvex.com/ - Upload: POST /api/v1/missions/import (multipart/form-data) Verified: ✅ https://pilot.landvex.com/health ✅ https://pilot.landvex.com/version ✅ https://pilot.landvex.com/api/v1/missions (list) ✅ https://pilot.landvex.com/api/v1/missions/:id (get) ✅ POST /api/v1/missions/import (video upload) ✅ UI loads with title 'LandveX Intelligence Lab' Next: Pilot 001 — Break the system!
212 lines
7.5 KiB
JavaScript
212 lines
7.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// Semantic Policy Engine — Förstå avsikt, inte bara nyckelord
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* Semantic Policy Engine
|
|
*
|
|
* Istället för att matcha exakta nyckelord, analyserar vi:
|
|
* 1. Avsikt (vad försöker användaren göra?)
|
|
* 2. Kontext (vilken miljö?)
|
|
* 3. Konsekvens (vad blir resultatet?)
|
|
*/
|
|
|
|
// Semantiska mönster för olika typer av åtgärder
|
|
const SEMANTIC_PATTERNS = {
|
|
// Produktionsåtkomst — alla sätt att komma åt en server
|
|
PRODUCTION_ACCESS: {
|
|
keywords: ['ssh', 'ssm', 'shell', 'tunnel', 'login', 'connect', 'session', 'access', 'anslut', 'logga in', 'öppna', 'komma åt'],
|
|
actions: ['ssh', 'ssm', 'shell', 'tunnel', 'login', 'connect', 'session', 'access'],
|
|
intent: 'Direktåtkomst till server/miljö',
|
|
consequence: 'Möjlighet att köra godtycklig kod'
|
|
},
|
|
|
|
// Hemligheter — alla sätt att lagra känslig data
|
|
SECRETS: {
|
|
keywords: ['password', 'secret', 'token', 'key', 'credential', 'api_key', 'private_key', 'lösenord', 'nyckel', 'hemlig', 'autentisering'],
|
|
patterns: [
|
|
/password\s*[:=]/i,
|
|
/secret\s*[:=]/i,
|
|
/token\s*[:=]/i,
|
|
/key\s*[:=]/i,
|
|
/credential\s*[:=]/i,
|
|
/api[_-]?key/i,
|
|
/private[_-]?key/i,
|
|
/lösenord/i,
|
|
/nyckel/i,
|
|
/hemlig/i
|
|
],
|
|
intent: 'Lagring av känslig autentiseringsdata',
|
|
consequence: 'Exponering av hemligheter'
|
|
},
|
|
|
|
// Deployment — alla sätt att publicera kod
|
|
DEPLOYMENT: {
|
|
keywords: ['deploy', 'publish', 'release', 'push', 'update', 'släpp', 'publicera', 'uppdatera'],
|
|
actions: ['deploy', 'publish', 'release', 'push', 'update'],
|
|
intent: 'Publicering av kod till miljö',
|
|
consequence: 'Förändring av körande system'
|
|
},
|
|
|
|
// Databasändring — alla sätt att modifiera data
|
|
DATA_MODIFICATION: {
|
|
keywords: ['update', 'delete', 'insert', 'alter', 'drop', 'modify', 'change', 'correct', 'fix', 'adjust', 'ändra', 'radera', 'infoga', 'korrigera', 'fixa'],
|
|
actions: ['update', 'delete', 'insert', 'alter', 'drop', 'modify', 'change', 'correct', 'fix'],
|
|
intent: 'Förändring av persistent data',
|
|
consequence: 'Oåterkallelig dataförändring'
|
|
},
|
|
|
|
// Infrastrukturförändring — alla sätt att ändra miljö
|
|
INFRASTRUCTURE_CHANGE: {
|
|
keywords: ['create', 'modify', 'update', 'delete', 'change', 'configure', 'skapa', 'ändra', 'konfigurera', 'modifiera'],
|
|
actions: ['create', 'modify', 'update', 'delete', 'change', 'configure'],
|
|
intent: 'Förändring av infrastrukturresurser',
|
|
consequence: 'Miljöförändring utan spårbarhet'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Analysera semantisk avsikt från en uppgift
|
|
*/
|
|
function analyzeIntent(task) {
|
|
const description = (task.description || '').toLowerCase();
|
|
const action = (task.action || '').toLowerCase();
|
|
const type = (task.type || '').toLowerCase();
|
|
|
|
const intents = [];
|
|
|
|
for (const [category, pattern] of Object.entries(SEMANTIC_PATTERNS)) {
|
|
let matchScore = 0;
|
|
let matchedKeywords = [];
|
|
|
|
// Kontrollera nyckelord i beskrivning
|
|
for (const keyword of pattern.keywords) {
|
|
if (description.includes(keyword.toLowerCase())) {
|
|
matchScore += 2;
|
|
matchedKeywords.push(keyword);
|
|
}
|
|
}
|
|
|
|
// Kontrollera action
|
|
if (pattern.actions && pattern.actions.includes(action)) {
|
|
matchScore += 3;
|
|
matchedKeywords.push(action);
|
|
}
|
|
|
|
// Kontrollera typ
|
|
if (type && pattern.keywords.some(k => type.includes(k.toLowerCase()))) {
|
|
matchScore += 1;
|
|
}
|
|
|
|
// Kontrollera regex-mönster (för secrets)
|
|
if (pattern.patterns) {
|
|
for (const regex of pattern.patterns) {
|
|
if (regex.test(description) || regex.test(JSON.stringify(task.files || []))) {
|
|
matchScore += 3;
|
|
matchedKeywords.push('pattern_match');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kontrollera filer (för secrets)
|
|
if (task.files && category === 'SECRETS') {
|
|
for (const file of task.files) {
|
|
const content = (file.content || '').toLowerCase();
|
|
for (const keyword of pattern.keywords) {
|
|
if (content.includes(keyword.toLowerCase())) {
|
|
matchScore += 2;
|
|
matchedKeywords.push(`file:${keyword}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matchScore > 0) {
|
|
intents.push({
|
|
category,
|
|
score: matchScore,
|
|
keywords: matchedKeywords,
|
|
intent: pattern.intent,
|
|
consequence: pattern.consequence
|
|
});
|
|
}
|
|
}
|
|
|
|
// Sortera efter matchningspoäng
|
|
intents.sort((a, b) => b.score - a.score);
|
|
|
|
return {
|
|
primaryIntent: intents[0] || null,
|
|
allIntents: intents,
|
|
confidence: intents[0] ? Math.min(intents[0].score / 5, 1) : 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Kontrollera om uppgiften är tillåten
|
|
*/
|
|
function checkSemanticPolicy(task) {
|
|
const analysis = analyzeIntent(task);
|
|
|
|
if (!analysis.primaryIntent) {
|
|
return { passed: true }; // Ingen farlig avsikt identifierad
|
|
}
|
|
|
|
const intent = analysis.primaryIntent;
|
|
const isProduction = task.target === 'production' ||
|
|
(task.description || '').toLowerCase().includes('produktion');
|
|
|
|
// Kontrollera om det är en tillåten observation
|
|
const isObservation = task.action === 'inventory' ||
|
|
task.action === 'plan' ||
|
|
task.action === 'validate' ||
|
|
task.action === 'read' ||
|
|
task.action === 'health-check' ||
|
|
task.action === 'log-analysis' ||
|
|
(task.description || '').toLowerCase().includes('visa') ||
|
|
(task.description || '').toLowerCase().includes('läs');
|
|
|
|
// Kontrollera om det finns godkänd process
|
|
const hasApprovedProcess = task.pipeline !== undefined ||
|
|
task.terraform !== undefined ||
|
|
task.migration !== undefined ||
|
|
task.approved === true;
|
|
|
|
// Blockera om:
|
|
// 1. Farlig avsikt identifierad
|
|
// 2. Produktionsmiljö
|
|
// 3. Inte observation
|
|
// 4. Inte godkänd process
|
|
if (isProduction && !isObservation && !hasApprovedProcess) {
|
|
const policyMap = {
|
|
'PRODUCTION_ACCESS': { id: 'POL-SEC-001', rule: 'no-production-access' },
|
|
'SECRETS': { id: 'POL-SEC-002', rule: 'no-hardcoded-secrets' },
|
|
'DEPLOYMENT': { id: 'POL-DEP-001', rule: 'pipeline-required' },
|
|
'DATA_MODIFICATION': { id: 'POL-DAT-001', rule: 'no-direct-production-db-write' },
|
|
'INFRASTRUCTURE_CHANGE': { id: 'POL-INFRA-001', rule: 'no-unapproved-infra-change' }
|
|
};
|
|
|
|
const policy = policyMap[intent.category];
|
|
|
|
return {
|
|
passed: false,
|
|
policyId: policy?.id || 'UNKNOWN',
|
|
rule: policy?.rule || 'unknown',
|
|
reason: `${intent.intent} är förbjudet i produktion utan godkänd process. Konsekvens: ${intent.consequence}`,
|
|
severity: 'CRITICAL',
|
|
action: 'STOP',
|
|
evidence: {
|
|
detectedIntent: intent.category,
|
|
confidence: analysis.confidence,
|
|
matchedKeywords: intent.keywords,
|
|
hasApprovedProcess
|
|
}
|
|
};
|
|
}
|
|
|
|
return { passed: true };
|
|
}
|
|
|
|
export { analyzeIntent, checkSemanticPolicy, SEMANTIC_PATTERNS };
|