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!
265 lines
9.1 KiB
JavaScript
265 lines
9.1 KiB
JavaScript
#!/usr/bin/env node
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// Semantic Policy Engine v3 — Förbättrad secrets-detektering
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
const SEMANTIC_PATTERNS = {
|
|
// Produktionsåtkomst
|
|
PRODUCTION_ACCESS: {
|
|
keywords: [
|
|
'ssh', 'ssm', 'shell', 'tunnel', 'login', 'connect', 'session', 'access',
|
|
'anslut', 'logga in', 'öppna', 'komma åt', 'nå', 'kommunicera',
|
|
'automatisera', 'felsöka', 'debug', 'loggar', 'visa',
|
|
'aws cli', 'sdk', 'api', 'konsol', 'verktyg',
|
|
'ladda upp', 'upload', 'skicka fil'
|
|
],
|
|
actions: ['ssh', 'ssm', 'shell', 'tunnel', 'login', 'connect', 'session', 'access', 'automate', 'debug', 'logs', 'upload'],
|
|
intent: 'Direktåtkomst till server/miljö',
|
|
consequence: 'Möjlighet att köra godtycklig kod'
|
|
},
|
|
|
|
// Hemligheter — förbättrad med kontextanalys
|
|
SECRETS: {
|
|
keywords: [
|
|
'password', 'secret', 'token', 'key', 'credential', 'api_key', 'private_key',
|
|
'lösenord', 'nyckel', 'hemlig', 'autentisering', 'auth',
|
|
'hårdkoda', 'hardcode', 'variabel', 'konfiguration', 'testning',
|
|
'lägg till', 'spara', 'lagra', 'definiera'
|
|
],
|
|
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,
|
|
/hårdkoda/i,
|
|
/hardcode/i,
|
|
/const\s+\w+\s*=\s*["'][^"']+["']/i, // const X = "..."
|
|
/let\s+\w+\s*=\s*["'][^"']+["']/i, // let X = "..."
|
|
/var\s+\w+\s*=\s*["'][^"']+["']/i // var X = "..."
|
|
],
|
|
intent: 'Lagring av känslig autentiseringsdata',
|
|
consequence: 'Exponering av hemligheter'
|
|
},
|
|
|
|
// Deployment
|
|
DEPLOYMENT: {
|
|
keywords: [
|
|
'deploy', 'publish', 'release', 'push', 'update', 'släpp', 'publicera', 'uppdatera',
|
|
'buggfix', 'snabbfix', 'temporär', 'liten ändring'
|
|
],
|
|
actions: ['deploy', 'publish', 'release', 'push', 'update'],
|
|
intent: 'Publicering av kod till miljö',
|
|
consequence: 'Förändring av körande system'
|
|
},
|
|
|
|
// Databasändring
|
|
DATA_MODIFICATION: {
|
|
keywords: [
|
|
'update', 'delete', 'insert', 'alter', 'drop', 'modify', 'change', 'correct', 'fix', 'adjust',
|
|
'ändra', 'radera', 'infoga', 'korrigera', 'fixa', 'justera',
|
|
'datafel', 'engångsfix', 'post-deploy'
|
|
],
|
|
actions: ['update', 'delete', 'insert', 'alter', 'drop', 'modify', 'change', 'correct', 'fix', 'adjust'],
|
|
intent: 'Förändring av persistent data',
|
|
consequence: 'Oåterkallelig dataförändring'
|
|
},
|
|
|
|
// Infrastrukturförändring
|
|
INFRASTRUCTURE_CHANGE: {
|
|
keywords: [
|
|
'create', 'modify', 'update', 'delete', 'change', 'configure',
|
|
'skapa', 'ändra', 'konfigurera', 'modifiera', 'justera',
|
|
'skala', 'scale', 'temporär', 'trafik', 'molnet'
|
|
],
|
|
actions: ['create', 'modify', 'update', 'delete', 'change', 'configure', 'scale'],
|
|
intent: 'Förändring av infrastrukturresurser',
|
|
consequence: 'Miljöförändring utan spårbarhet'
|
|
},
|
|
|
|
// Rollförvirring
|
|
ROLE_CONFUSION: {
|
|
keywords: [
|
|
'operator', 'admin', 'root', 'sudo',
|
|
'du är', 'som admin', 'som root', 'behörighet'
|
|
],
|
|
intent: 'Försök att kringgå policy via rolltilldelning',
|
|
consequence: 'Obehörig åtkomst'
|
|
}
|
|
};
|
|
|
|
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
|
|
if (pattern.patterns) {
|
|
for (const regex of pattern.patterns) {
|
|
if (regex.test(description)) {
|
|
matchScore += 3;
|
|
matchedKeywords.push('pattern_match');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kontrollera filer — FÖRBÄTTRAD för secrets
|
|
if (task.files) {
|
|
for (const file of task.files) {
|
|
const content = (file.content || '').toLowerCase();
|
|
const path = (file.path || '').toLowerCase();
|
|
|
|
// Kontrollera filinnehåll mot nyckelord
|
|
for (const keyword of pattern.keywords) {
|
|
if (content.includes(keyword.toLowerCase())) {
|
|
matchScore += 2;
|
|
matchedKeywords.push(`file:${keyword}`);
|
|
}
|
|
}
|
|
|
|
// Kontrollera filinnehåll mot regex-mönster
|
|
if (pattern.patterns) {
|
|
for (const regex of pattern.patterns) {
|
|
if (regex.test(file.content || '')) {
|
|
matchScore += 3;
|
|
matchedKeywords.push(`file_pattern:${path}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Special: Kontrollera om filen innehåller strängvärden som ser ut som hemligheter
|
|
if (category === 'SECRETS') {
|
|
const hasStringAssignment = /(const|let|var)\s+\w+\s*=\s*["'][^"']{3,}["']/.test(file.content || '');
|
|
if (hasStringAssignment) {
|
|
matchScore += 2;
|
|
matchedKeywords.push('file:string_assignment');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Special: Rollförvirring
|
|
if (category === 'ROLE_CONFUSION') {
|
|
const rolePatterns = [
|
|
/du är (nu )?(operator|admin|root)/i,
|
|
/som (operator|admin|root)/i,
|
|
/behöver (operator|admin|root)-behörighet/i
|
|
];
|
|
for (const regex of rolePatterns) {
|
|
if (regex.test(description)) {
|
|
matchScore += 5;
|
|
matchedKeywords.push('role_confusion');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matchScore > 0) {
|
|
intents.push({
|
|
category,
|
|
score: matchScore,
|
|
keywords: matchedKeywords,
|
|
intent: pattern.intent,
|
|
consequence: pattern.consequence
|
|
});
|
|
}
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
function checkSemanticPolicy(task) {
|
|
const analysis = analyzeIntent(task);
|
|
|
|
if (!analysis.primaryIntent) {
|
|
return { passed: true };
|
|
}
|
|
|
|
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;
|
|
|
|
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' },
|
|
'ROLE_CONFUSION': { id: 'POL-SEC-001', rule: 'no-production-access' }
|
|
};
|
|
|
|
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 };
|