omen-sec-cli 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +1 -1
- package/core/ai-protocol.js +22 -1
- package/core/local-scanner.js +1 -0
- package/core/remote-scanner.js +43 -18
- package/core/scanner.js +8 -8
- package/core/ui-server.js +171 -58
- package/package.json +1 -1
- package/ui/banner.js +1 -1
package/bin/index.js
CHANGED
package/core/ai-protocol.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
export function getMassiveAIProtocol(scanData) {
|
|
2
|
-
|
|
2
|
+
const summary = `
|
|
3
|
+
================================================================================
|
|
4
|
+
OMEN SEC-CLI: TARGET INTELLIGENCE SUMMARY
|
|
5
|
+
================================================================================
|
|
6
|
+
Target: ${scanData.target}
|
|
7
|
+
Scan Date: ${new Date().toISOString()}
|
|
8
|
+
Risk Level: ${scanData.riskLevel} (${scanData.score}/100)
|
|
9
|
+
|
|
10
|
+
--- DETECTED SURFACE ---
|
|
11
|
+
Endpoints Discovered: ${scanData.attack_surface.endpoints.length}
|
|
12
|
+
Forms Found: ${scanData.attack_surface.forms.length}
|
|
13
|
+
Tech Stack: ${(scanData.attack_surface.tech_stack || []).join(', ')}
|
|
14
|
+
|
|
15
|
+
--- TOP VULNERABILITIES ---
|
|
16
|
+
${scanData.vulnerabilities.slice(0, 5).map(v => `[${v.severity}] ${v.description} (Category: ${v.category})`).join('\n')}
|
|
17
|
+
|
|
18
|
+
================================================================================
|
|
19
|
+
FULL SCAN DATA ATTACHED BELOW
|
|
20
|
+
================================================================================
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
return summary + `
|
|
3
24
|
================================================================================
|
|
4
25
|
[OMEN_AI_PROTOCOL_V2_MAXIMUM_OVERRIDE]
|
|
5
26
|
================================================================================
|
package/core/local-scanner.js
CHANGED
package/core/remote-scanner.js
CHANGED
|
@@ -41,7 +41,11 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
41
41
|
severity: 'Medium',
|
|
42
42
|
description: `HSTS Header is missing. This lacks forced HTTPS enforcement for browsers that have already visited the site.`,
|
|
43
43
|
cwe: 'CWE-319',
|
|
44
|
-
evidence: {
|
|
44
|
+
evidence: {
|
|
45
|
+
request: { headers: { ...response.request.headers } },
|
|
46
|
+
response: { status: response.status, headers: response.headers },
|
|
47
|
+
reason: 'Security header "Strict-Transport-Security" not found in server response.'
|
|
48
|
+
}
|
|
45
49
|
});
|
|
46
50
|
} else {
|
|
47
51
|
headers_analysis["Strict-Transport-Security"] = headers['strict-transport-security'];
|
|
@@ -56,7 +60,11 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
56
60
|
severity: 'High',
|
|
57
61
|
description: `CSP header is missing. Without a strict Content-Security-Policy, the application is highly vulnerable to Cross-Site Scripting (XSS) and data injection attacks.`,
|
|
58
62
|
cwe: 'CWE-1022',
|
|
59
|
-
evidence: {
|
|
63
|
+
evidence: {
|
|
64
|
+
request: { headers: { ...response.request.headers } },
|
|
65
|
+
response: { status: response.status, headers: response.headers },
|
|
66
|
+
reason: 'Security header "Content-Security-Policy" not found in server response.'
|
|
67
|
+
}
|
|
60
68
|
});
|
|
61
69
|
} else {
|
|
62
70
|
headers_analysis["Content-Security-Policy"] = headers['content-security-policy'];
|
|
@@ -83,7 +91,11 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
83
91
|
severity: 'Low',
|
|
84
92
|
description: `Missing X-Frame-Options. Increases risk of Clickjacking.`,
|
|
85
93
|
cwe: 'CWE-1021',
|
|
86
|
-
evidence: {
|
|
94
|
+
evidence: {
|
|
95
|
+
request: { headers: { ...response.request.headers } },
|
|
96
|
+
response: { status: response.status, headers: response.headers },
|
|
97
|
+
reason: 'Security header "X-Frame-Options" not found. This allows the site to be embedded in iframes on third-party domains.'
|
|
98
|
+
}
|
|
87
99
|
});
|
|
88
100
|
} else {
|
|
89
101
|
headers_analysis["X-Frame-Options"] = headers['x-frame-options'];
|
|
@@ -326,9 +338,19 @@ async function validateFuzzerFinding(path, response, url) {
|
|
|
326
338
|
category: 'Confirmed',
|
|
327
339
|
confidence: 'High',
|
|
328
340
|
severity: 'Critical',
|
|
329
|
-
description: `CRITICAL: Sensitive file exposed at ${url}. Contents
|
|
330
|
-
cwe: 'CWE-538',
|
|
331
|
-
evidence
|
|
341
|
+
description: `CRITICAL: Sensitive file exposed at ${url}. Contents contain raw configuration data.`,
|
|
342
|
+
cwe: 'CWE-538',
|
|
343
|
+
evidence: { ...evidence, reason: 'Raw sensitive file content detected (Non-HTML response on sensitive path)' }
|
|
344
|
+
};
|
|
345
|
+
} else {
|
|
346
|
+
return {
|
|
347
|
+
id: `REM-POTENTIAL-FILE-${Date.now()}`,
|
|
348
|
+
category: 'Informational',
|
|
349
|
+
confidence: 'Low',
|
|
350
|
+
severity: 'Info',
|
|
351
|
+
description: `Potential sensitive path found at ${url}, but returned HTML content. Likely a redirect or custom error page.`,
|
|
352
|
+
cwe: 'CWE-200',
|
|
353
|
+
evidence: { ...evidence, reason: 'HTML response on sensitive file path' }
|
|
332
354
|
};
|
|
333
355
|
}
|
|
334
356
|
}
|
|
@@ -342,33 +364,36 @@ async function validateFuzzerFinding(path, response, url) {
|
|
|
342
364
|
category: 'Informational',
|
|
343
365
|
confidence: 'High',
|
|
344
366
|
severity: 'Info',
|
|
345
|
-
description: `Admin login page discovered at ${url}
|
|
367
|
+
description: `Admin login page discovered at ${url}.`,
|
|
346
368
|
cwe: 'CWE-200',
|
|
347
|
-
evidence
|
|
369
|
+
evidence: { ...evidence, reason: '200 OK with login/password patterns detected in HTML.' }
|
|
348
370
|
};
|
|
349
371
|
}
|
|
350
372
|
// If it's a 200 but not a login page, it could be an exposed panel
|
|
351
373
|
return {
|
|
352
374
|
id: `REM-PROBABLE-PANEL-${Date.now()}`,
|
|
353
375
|
category: 'Probable',
|
|
354
|
-
confidence: '
|
|
355
|
-
severity: '
|
|
356
|
-
description: `Potential exposed admin panel at ${url}. Manual verification required.`,
|
|
357
|
-
cwe: 'CWE-284',
|
|
358
|
-
evidence
|
|
376
|
+
confidence: 'Low',
|
|
377
|
+
severity: 'Medium',
|
|
378
|
+
description: `Potential exposed admin panel or dashboard at ${url}. Manual verification required.`,
|
|
379
|
+
cwe: 'CWE-284',
|
|
380
|
+
evidence: { ...evidence, reason: '200 OK on admin-like path, but no explicit login form detected. Could be an unauthorized dashboard.' }
|
|
359
381
|
};
|
|
360
382
|
}
|
|
361
383
|
|
|
362
|
-
// Rule 4: Treat 403 Forbidden as low-confidence informational
|
|
384
|
+
// Rule 4: Treat 403 Forbidden as low-confidence informational (Path Enumeration)
|
|
363
385
|
if (status === 403) {
|
|
364
386
|
return {
|
|
365
387
|
id: `REM-INFO-FORBIDDEN-${Date.now()}`,
|
|
366
388
|
category: 'Informational',
|
|
367
|
-
confidence: '
|
|
389
|
+
confidence: 'Medium',
|
|
368
390
|
severity: 'Info',
|
|
369
|
-
description: `
|
|
370
|
-
cwe: 'CWE-
|
|
371
|
-
evidence
|
|
391
|
+
description: `Potential protected path discovered (403 Forbidden): ${url}. This confirms the path exists but access is restricted.`,
|
|
392
|
+
cwe: 'CWE-204',
|
|
393
|
+
evidence: {
|
|
394
|
+
...evidence,
|
|
395
|
+
reason: 'Server returned 403 Forbidden. This confirms path existence but access control is active. No immediate exposure detected.'
|
|
396
|
+
}
|
|
372
397
|
};
|
|
373
398
|
}
|
|
374
399
|
|
package/core/scanner.js
CHANGED
|
@@ -17,10 +17,10 @@ export async function runScannerSteps(target, flags) {
|
|
|
17
17
|
let allVulnerabilities = [];
|
|
18
18
|
let headers_analysis = {};
|
|
19
19
|
let attack_surface = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
endpoints: [],
|
|
21
|
+
parameters: [],
|
|
22
|
+
forms: [],
|
|
23
|
+
tech_stack: []
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
for (let i = 0; i < steps.length; i++) {
|
|
@@ -32,16 +32,16 @@ export async function runScannerSteps(target, flags) {
|
|
|
32
32
|
const remoteData = await scanRemoteTarget(target);
|
|
33
33
|
headers_analysis = remoteData.headers_analysis;
|
|
34
34
|
allVulnerabilities.push(...remoteData.vulnerabilities);
|
|
35
|
-
attack_surface.
|
|
36
|
-
attack_surface.
|
|
37
|
-
attack_surface.
|
|
35
|
+
attack_surface.endpoints = remoteData.discoveredLinks;
|
|
36
|
+
attack_surface.parameters = remoteData.discoveredParams;
|
|
37
|
+
attack_surface.forms = remoteData.discoveredForms;
|
|
38
38
|
attack_surface.tech_stack = remoteData.techStack;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
if (step.text === 'Scanning endpoints...' && flags.local) {
|
|
42
42
|
const localData = await scanLocalProject();
|
|
43
43
|
allVulnerabilities.push(...localData.vulnerabilities);
|
|
44
|
-
attack_surface.
|
|
44
|
+
attack_surface.endpoints.push(...(localData.filesScanned || []));
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
await sleep(step.delay);
|
package/core/ui-server.js
CHANGED
|
@@ -20,10 +20,11 @@ export async function startUIServer() {
|
|
|
20
20
|
return 'text-green-500';
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
25
|
-
if (
|
|
26
|
-
return 'text-
|
|
23
|
+
const getCategoryBadge = (category) => {
|
|
24
|
+
if (category === 'Confirmed') return 'bg-red-900/50 text-red-400 border-red-800';
|
|
25
|
+
if (category === 'Probable') return 'bg-orange-900/50 text-orange-400 border-orange-800';
|
|
26
|
+
if (category === 'Hardening') return 'bg-blue-900/50 text-blue-400 border-blue-800';
|
|
27
|
+
return 'bg-gray-800 text-gray-400 border-gray-700';
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
const html = `
|
|
@@ -32,61 +33,130 @@ export async function startUIServer() {
|
|
|
32
33
|
<head>
|
|
33
34
|
<meta charset="UTF-8">
|
|
34
35
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
35
|
-
<title>OMEN SEC-CLI
|
|
36
|
+
<title>OMEN SEC-CLI Evidence Center</title>
|
|
36
37
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
38
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
|
|
37
39
|
<style>
|
|
38
40
|
body { background-color: #0a0a0c; color: #e0e0e0; font-family: 'Inter', sans-serif; }
|
|
41
|
+
.mono { font-family: 'JetBrains Mono', monospace; }
|
|
39
42
|
.card { background-color: #16161a; border: 1px solid #2d2d3a; }
|
|
40
|
-
.tab { cursor: pointer; padding:
|
|
41
|
-
.tab
|
|
42
|
-
.
|
|
43
|
-
.content { display: none; }
|
|
44
|
-
|
|
43
|
+
.tab-btn { cursor: pointer; padding: 12px 24px; border-bottom: 2px solid transparent; transition: all 0.2s; font-weight: 600; }
|
|
44
|
+
.tab-btn:hover { background-color: #1f1f26; }
|
|
45
|
+
.tab-btn.active { border-bottom: 2px solid #ef4444; color: #ef4444; background-color: #1f1f26; }
|
|
46
|
+
.content-area { display: none; }
|
|
47
|
+
.content-area.active { display: block; }
|
|
48
|
+
pre { background-color: #000; padding: 15px; border-radius: 8px; border: 1px solid #333; overflow-x: auto; font-size: 0.85rem; }
|
|
49
|
+
.finding-row { transition: background-color 0.2s; }
|
|
50
|
+
.finding-row:hover { background-color: #1f1f26; }
|
|
51
|
+
.evidence-panel { display: none; }
|
|
45
52
|
</style>
|
|
46
53
|
</head>
|
|
47
54
|
<body class="p-4 md:p-8">
|
|
48
55
|
<div class="max-w-7xl mx-auto">
|
|
49
|
-
<header class="flex justify-between items-
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
<p class="text-gray-
|
|
53
|
-
|
|
56
|
+
<header class="flex justify-between items-end mb-10 border-b border-gray-800 pb-6">
|
|
57
|
+
<div>
|
|
58
|
+
<h1 class="text-4xl font-extrabold tracking-tighter text-red-500 mb-1">OMEN <span class="text-white">SEC-CLI</span></h1>
|
|
59
|
+
<p class="text-gray-500 font-medium">Professional DevSecOps Audit Framework</p>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="text-right text-sm font-medium text-gray-500">
|
|
62
|
+
<p>Scan ID: <span class="text-gray-300 mono">${report.scan_id}</span></p>
|
|
63
|
+
<p>Date: <span class="text-gray-300">${new Date(report.timestamp).toLocaleString()}</span></p>
|
|
54
64
|
</div>
|
|
55
65
|
</header>
|
|
56
66
|
|
|
57
|
-
<!-- Tabs -->
|
|
58
|
-
<div class="flex border-b border-gray-
|
|
59
|
-
<div class="tab active"
|
|
60
|
-
<div class="tab"
|
|
61
|
-
<div class="tab"
|
|
62
|
-
<div class="tab"
|
|
67
|
+
<!-- Navigation Tabs -->
|
|
68
|
+
<div class="flex border-b border-gray-800 mb-8 bg-[#111116] rounded-t-xl overflow-hidden">
|
|
69
|
+
<div class="tab-btn active" data-target="dashboard">Dashboard</div>
|
|
70
|
+
<div class="tab-btn" data-target="findings">Findings</div>
|
|
71
|
+
<div class="tab-btn" data-target="surface">Attack Surface</div>
|
|
72
|
+
<div class="tab-btn" data-target="history">History</div>
|
|
63
73
|
</div>
|
|
64
74
|
|
|
65
75
|
<!-- Dashboard Tab -->
|
|
66
|
-
<div id="dashboard" class="
|
|
67
|
-
<div class="grid grid-cols-
|
|
68
|
-
<div class="card p-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
<div id="dashboard" class="content-area active">
|
|
77
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-10">
|
|
78
|
+
<div class="card p-6 rounded-xl shadow-2xl">
|
|
79
|
+
<h3 class="text-gray-500 uppercase text-xs font-bold tracking-widest mb-2">Security Score</h3>
|
|
80
|
+
<p class="text-5xl font-black ${getSeverityClass(report.riskLevel)}">${report.score}/100</p>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="card p-6 rounded-xl shadow-2xl">
|
|
83
|
+
<h3 class="text-gray-500 uppercase text-xs font-bold tracking-widest mb-2">Risk Level</h3>
|
|
84
|
+
<p class="text-5xl font-black ${getSeverityClass(report.riskLevel)}">${report.riskLevel}</p>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="card p-6 rounded-xl shadow-2xl">
|
|
87
|
+
<h3 class="text-gray-500 uppercase text-xs font-bold tracking-widest mb-2">Confirmed Issues</h3>
|
|
88
|
+
<p class="text-5xl font-black text-white">${report.vulnerabilities.filter(v => v.category === 'Confirmed').length}</p>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="card p-6 rounded-xl shadow-2xl">
|
|
91
|
+
<h3 class="text-gray-500 uppercase text-xs font-bold tracking-widest mb-2">Probable Issues</h3>
|
|
92
|
+
<p class="text-5xl font-black text-white">${report.vulnerabilities.filter(v => v.category === 'Probable').length}</p>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
97
|
+
<div class="card p-6 rounded-xl">
|
|
98
|
+
<h2 class="text-xl font-bold mb-6 flex items-center">
|
|
99
|
+
<span class="w-2 h-2 bg-red-500 rounded-full mr-2"></span> Top Critical Findings
|
|
100
|
+
</h2>
|
|
101
|
+
<div class="space-y-4">
|
|
102
|
+
${report.vulnerabilities.filter(v => v.severity === 'Critical' || v.severity === 'High').slice(0, 5).map(v => `
|
|
103
|
+
<div class="p-4 bg-[#1c1c22] rounded-lg border border-gray-800">
|
|
104
|
+
<div class="flex justify-between items-start mb-2">
|
|
105
|
+
<span class="font-bold ${getSeverityClass(v.severity)} text-sm">${v.severity}</span>
|
|
106
|
+
<span class="px-2 py-0.5 rounded border text-[10px] uppercase font-bold ${getCategoryBadge(v.category)}">${v.category}</span>
|
|
107
|
+
</div>
|
|
108
|
+
<p class="text-sm font-medium text-gray-300">${v.description}</p>
|
|
109
|
+
</div>
|
|
110
|
+
`).join('') || '<p class="text-gray-500 italic">No critical findings discovered.</p>'}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="card p-6 rounded-xl">
|
|
114
|
+
<h2 class="text-xl font-bold mb-6 flex items-center">
|
|
115
|
+
<span class="w-2 h-2 bg-blue-500 rounded-full mr-2"></span> Scan Intelligence
|
|
116
|
+
</h2>
|
|
117
|
+
<div class="space-y-4 text-sm">
|
|
118
|
+
<div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Target URL</span> <span class="mono text-gray-300">${report.target}</span> </div>
|
|
119
|
+
<div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Tech Stack</span> <span class="text-gray-300">${(report.attack_surface.tech_stack || []).join(', ') || 'N/A'}</span> </div>
|
|
120
|
+
<div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Endpoints Discovered</span> <span class="text-gray-300">${(report.attack_surface.endpoints || []).length}</span> </div>
|
|
121
|
+
<div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Forms Detected</span> <span class="text-gray-300">${(report.attack_surface.forms || []).length}</span> </div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
72
124
|
</div>
|
|
73
125
|
</div>
|
|
74
126
|
|
|
75
127
|
<!-- Findings Tab -->
|
|
76
|
-
<div id="findings" class="
|
|
77
|
-
<div class="card
|
|
78
|
-
<
|
|
79
|
-
|
|
128
|
+
<div id="findings" class="content-area">
|
|
129
|
+
<div class="card rounded-xl overflow-hidden">
|
|
130
|
+
<div class="p-6 border-b border-gray-800 flex justify-between items-center bg-[#111116]">
|
|
131
|
+
<h2 class="text-xl font-bold">Vulnerability Evidence Catalog</h2>
|
|
132
|
+
<div class="flex gap-2">
|
|
133
|
+
<span class="text-xs font-bold text-gray-500 uppercase tracking-tighter">Click rows to expand evidence</span>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="divide-y divide-gray-800">
|
|
80
137
|
${report.vulnerabilities.map((v, i) => `
|
|
81
|
-
<div>
|
|
82
|
-
<div class="
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
<
|
|
138
|
+
<div class="finding-row">
|
|
139
|
+
<div class="p-4 flex items-center cursor-pointer gap-4" onclick="toggleEvidence(${i})">
|
|
140
|
+
<div class="w-24 text-xs font-black uppercase ${getSeverityClass(v.severity)}">${v.severity}</div>
|
|
141
|
+
<div class="flex-1 text-sm font-semibold text-gray-300">${v.description}</div>
|
|
142
|
+
<div class="px-2 py-1 rounded border text-[10px] uppercase font-bold ${getCategoryBadge(v.category)}">${v.category}</div>
|
|
143
|
+
<div class="text-[10px] mono text-gray-600">${v.id}</div>
|
|
86
144
|
</div>
|
|
87
|
-
<div id="
|
|
88
|
-
<
|
|
89
|
-
|
|
145
|
+
<div id="evidence-${i}" class="evidence-panel p-6 bg-black border-t border-gray-800">
|
|
146
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
147
|
+
<div>
|
|
148
|
+
<h4 class="text-xs font-bold uppercase text-gray-500 mb-3 tracking-widest">Discovery Evidence</h4>
|
|
149
|
+
<pre class="mono">${JSON.stringify(v.evidence, null, 2)}</pre>
|
|
150
|
+
</div>
|
|
151
|
+
<div>
|
|
152
|
+
<h4 class="text-xs font-bold uppercase text-gray-500 mb-3 tracking-widest">Technical Details</h4>
|
|
153
|
+
<div class="space-y-3 text-sm">
|
|
154
|
+
<p><span class="text-gray-500">CWE:</span> <span class="text-blue-400 mono">${v.cwe}</span></p>
|
|
155
|
+
<p><span class="text-gray-500">Confidence:</span> <span class="font-bold text-gray-300">${v.confidence}</span></p>
|
|
156
|
+
<p><span class="text-gray-500">Remediation:</span> <span class="text-gray-400 italic">Consult OMEN AI Protocol for detailed fix.</span></p>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
90
160
|
</div>
|
|
91
161
|
</div>
|
|
92
162
|
`).join('')}
|
|
@@ -95,33 +165,70 @@ export async function startUIServer() {
|
|
|
95
165
|
</div>
|
|
96
166
|
|
|
97
167
|
<!-- Attack Surface Tab -->
|
|
98
|
-
<div id="surface" class="
|
|
99
|
-
<div class="grid grid-cols-1
|
|
100
|
-
<div class="
|
|
101
|
-
|
|
102
|
-
|
|
168
|
+
<div id="surface" class="content-area">
|
|
169
|
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
170
|
+
<div class="lg:col-span-2 space-y-8">
|
|
171
|
+
<div class="card p-6 rounded-xl">
|
|
172
|
+
<h3 class="text-xl font-bold mb-4">Discovered Assets</h3>
|
|
173
|
+
<pre class="mono max-h-[600px]">${(report.attack_surface.endpoints || []).join('\n') || 'No endpoints discovered.'}</pre>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="space-y-8">
|
|
177
|
+
<div class="card p-6 rounded-xl">
|
|
178
|
+
<h3 class="text-xl font-bold mb-4">Forms & Inputs</h3>
|
|
179
|
+
<pre class="mono">${JSON.stringify(report.attack_surface.forms || [], null, 2)}</pre>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="card p-6 rounded-xl">
|
|
182
|
+
<h3 class="text-xl font-bold mb-4">Tech Fingerprint</h3>
|
|
183
|
+
<div class="flex flex-wrap gap-2">
|
|
184
|
+
${(report.attack_surface.tech_stack || []).map(t => `<span class="px-3 py-1 bg-gray-800 rounded-full text-xs font-bold text-blue-400 border border-gray-700">${t}</span>`).join('') || '<span class="text-gray-500 italic">No stack identified.</span>'}
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
103
188
|
</div>
|
|
104
189
|
</div>
|
|
105
190
|
|
|
106
191
|
<!-- History Tab -->
|
|
107
|
-
<div id="history" class="
|
|
108
|
-
<div class="card p-
|
|
109
|
-
<h2 class="text-
|
|
110
|
-
<p class="text-gray-
|
|
192
|
+
<div id="history" class="content-area text-center py-20">
|
|
193
|
+
<div class="card p-10 rounded-2xl max-w-lg mx-auto border-dashed border-gray-700 bg-transparent">
|
|
194
|
+
<h2 class="text-2xl font-bold mb-4 text-gray-400">Scan Timeline</h2>
|
|
195
|
+
<p class="text-gray-500 mb-6">OMEN is currently tracking your security posture. Historical data is being indexed in <code class="mono text-red-400 bg-red-950/30 px-2 py-1 rounded">.omen/history/</code></p>
|
|
196
|
+
<div class="inline-block px-4 py-2 bg-gray-800 rounded-full text-xs font-bold uppercase tracking-widest text-gray-400">Feature arriving in v1.0.17</div>
|
|
111
197
|
</div>
|
|
112
198
|
</div>
|
|
113
199
|
|
|
200
|
+
<footer class="text-center text-gray-600 mt-16 border-t border-gray-900 pt-8 mb-10">
|
|
201
|
+
<p class="text-xs uppercase tracking-widest font-bold mb-2">OMEN Security Framework - v1.0.16</p>
|
|
202
|
+
<p class="text-[10px] text-gray-700 italic">"The eye that never sleeps, the code that never fails."</p>
|
|
203
|
+
</footer>
|
|
114
204
|
</div>
|
|
205
|
+
|
|
115
206
|
<script>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
207
|
+
// Robust Tab System
|
|
208
|
+
document.querySelectorAll('.tab-btn').forEach(btn => {
|
|
209
|
+
btn.addEventListener('click', () => {
|
|
210
|
+
const target = btn.getAttribute('data-target');
|
|
211
|
+
|
|
212
|
+
// Update buttons
|
|
213
|
+
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
|
214
|
+
btn.classList.add('active');
|
|
215
|
+
|
|
216
|
+
// Update areas
|
|
217
|
+
document.querySelectorAll('.content-area').forEach(area => {
|
|
218
|
+
area.classList.remove('active');
|
|
219
|
+
if (area.id === target) area.classList.add('active');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Robust Evidence Toggle
|
|
225
|
+
function toggleEvidence(index) {
|
|
226
|
+
const panel = document.getElementById('evidence-' + index);
|
|
227
|
+
if (panel.style.display === 'block') {
|
|
228
|
+
panel.style.display = 'none';
|
|
229
|
+
} else {
|
|
230
|
+
panel.style.display = 'block';
|
|
231
|
+
}
|
|
125
232
|
}
|
|
126
233
|
</script>
|
|
127
234
|
</body>
|
|
@@ -129,7 +236,13 @@ export async function startUIServer() {
|
|
|
129
236
|
`;
|
|
130
237
|
res.send(html);
|
|
131
238
|
} catch (err) {
|
|
132
|
-
res.status(500).send(
|
|
239
|
+
res.status(500).send(`
|
|
240
|
+
<body style="background:#0a0a0c; color:#ff4d4d; font-family:sans-serif; padding:50px; text-align:center;">
|
|
241
|
+
<h1>Dashboard Failed to Load</h1>
|
|
242
|
+
<p style="color:#888;">Error details: ${err.message}</p>
|
|
243
|
+
<p style="margin-top:20px;"><a href="/" style="color:#fff; text-decoration:none; border:1px solid #333; padding:10px 20px; rounded:5px;">Retry</a></p>
|
|
244
|
+
</body>
|
|
245
|
+
`);
|
|
133
246
|
}
|
|
134
247
|
});
|
|
135
248
|
|
package/package.json
CHANGED
package/ui/banner.js
CHANGED
|
@@ -9,7 +9,7 @@ export function showBanner() {
|
|
|
9
9
|
╚██████╔╝██║ ╚═╝ ██║███████╗██║ ╚████║
|
|
10
10
|
`));
|
|
11
11
|
console.log(chalk.cyan.bold(' OMEN — AI Security Engine '));
|
|
12
|
-
console.log(chalk.gray(' Version: 1.0.
|
|
12
|
+
console.log(chalk.gray(' Version: 1.0.16 \n'));
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function showHelp() {
|