omen-sec-cli 1.0.15 → 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/remote-scanner.js +15 -12
- package/core/ui-server.js +170 -60
- package/package.json +1 -1
- package/ui/banner.js +1 -1
package/bin/index.js
CHANGED
package/core/remote-scanner.js
CHANGED
|
@@ -364,33 +364,36 @@ async function validateFuzzerFinding(path, response, url) {
|
|
|
364
364
|
category: 'Informational',
|
|
365
365
|
confidence: 'High',
|
|
366
366
|
severity: 'Info',
|
|
367
|
-
description: `Admin login page discovered at ${url}
|
|
367
|
+
description: `Admin login page discovered at ${url}.`,
|
|
368
368
|
cwe: 'CWE-200',
|
|
369
|
-
evidence
|
|
369
|
+
evidence: { ...evidence, reason: '200 OK with login/password patterns detected in HTML.' }
|
|
370
370
|
};
|
|
371
371
|
}
|
|
372
372
|
// If it's a 200 but not a login page, it could be an exposed panel
|
|
373
373
|
return {
|
|
374
374
|
id: `REM-PROBABLE-PANEL-${Date.now()}`,
|
|
375
375
|
category: 'Probable',
|
|
376
|
-
confidence: '
|
|
377
|
-
severity: '
|
|
378
|
-
description: `Potential exposed admin panel at ${url}. Manual verification required.`,
|
|
379
|
-
cwe: 'CWE-284',
|
|
380
|
-
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.' }
|
|
381
381
|
};
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
-
// Rule 4: Treat 403 Forbidden as low-confidence informational
|
|
384
|
+
// Rule 4: Treat 403 Forbidden as low-confidence informational (Path Enumeration)
|
|
385
385
|
if (status === 403) {
|
|
386
386
|
return {
|
|
387
387
|
id: `REM-INFO-FORBIDDEN-${Date.now()}`,
|
|
388
388
|
category: 'Informational',
|
|
389
|
-
confidence: '
|
|
389
|
+
confidence: 'Medium',
|
|
390
390
|
severity: 'Info',
|
|
391
|
-
description: `
|
|
392
|
-
cwe: 'CWE-204',
|
|
393
|
-
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
|
+
}
|
|
394
397
|
};
|
|
395
398
|
}
|
|
396
399
|
|
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,36 +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
|
|
|
114
|
-
<footer class="text-center text-gray-600 mt-
|
|
115
|
-
<p>OMEN Security Framework - v1.0.
|
|
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>
|
|
116
203
|
</footer>
|
|
117
204
|
</div>
|
|
205
|
+
|
|
118
206
|
<script>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
}
|
|
128
232
|
}
|
|
129
233
|
</script>
|
|
130
234
|
</body>
|
|
@@ -132,7 +236,13 @@ export async function startUIServer() {
|
|
|
132
236
|
`;
|
|
133
237
|
res.send(html);
|
|
134
238
|
} catch (err) {
|
|
135
|
-
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
|
+
`);
|
|
136
246
|
}
|
|
137
247
|
});
|
|
138
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() {
|