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 CHANGED
@@ -15,7 +15,7 @@ async function main() {
15
15
  }
16
16
 
17
17
  if (args.flags.version) {
18
- console.log('1.0.14');
18
+ console.log('1.0.16');
19
19
  return;
20
20
  }
21
21
 
@@ -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}. This is part of the attack surface.`,
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: 'Medium',
377
- severity: 'High',
378
- description: `Potential exposed admin panel at ${url}. Manual verification required.`,
379
- cwe: 'CWE-284', // Improper Access Control
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: 'Low',
389
+ confidence: 'Medium',
390
390
  severity: 'Info',
391
- description: `Path exists but access is restricted (403 Forbidden): ${url}. This confirms the path's existence but does not prove exposure.`,
392
- cwe: 'CWE-204', // Response Discrepancy (Path Enumeration)
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 getConfidenceClass = (confidence) => {
24
- if (confidence === 'High') return 'text-green-400';
25
- if (confidence === 'Medium') return 'text-yellow-400';
26
- return 'text-red-400';
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 Dashboard</title>
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: 10px 15px; border-bottom: 2px solid transparent; }
41
- .tab.active { border-bottom: 2px solid #ef4444; color: #ef4444; }
42
- .collapsible { cursor: pointer; }
43
- .content { display: none; }
44
- pre { background-color: #000; padding: 10px; border-radius: 5px; overflow-x: auto; }
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-center mb-8 border-b border-gray-800 pb-4">
50
- <h1 class="text-3xl font-bold tracking-tighter text-red-500">OMEN <span class="text-white">EVIDENCE-CENTER</span></h1>
51
- <div class="text-right text-sm">
52
- <p class="text-gray-400">Scan ID: ${report.scan_id}</p>
53
- <p class="text-gray-400">${new Date(report.timestamp).toLocaleString()}</p>
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-700 mb-6">
59
- <div class="tab active" onclick="showTab('dashboard')">Dashboard</div>
60
- <div class="tab" onclick="showTab('findings')">Findings</div>
61
- <div class="tab" onclick="showTab('surface')">Attack Surface</div>
62
- <div class="tab" onclick="showTab('history')">History</div>
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="tab-content block">
67
- <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
68
- <div class="card p-4 rounded-lg"><h3 class="text-gray-400">Security Score</h3><p class="text-4xl font-bold ${getSeverityClass(report.riskLevel)}">${report.score}/100</p></div>
69
- <div class="card p-4 rounded-lg"><h3 class="text-gray-400">Risk Level</h3><p class="text-4xl font-bold ${getSeverityClass(report.riskLevel)}">${report.riskLevel}</p></div>
70
- <div class="card p-4 rounded-lg"><h3 class="text-gray-400">Confirmed</h3><p class="text-4xl font-bold">${report.vulnerabilities.filter(v => v.category === 'Confirmed').length}</p></div>
71
- <div class="card p-4 rounded-lg"><h3 class="text-gray-400">Probable</h3><p class="text-4xl font-bold">${report.vulnerabilities.filter(v => v.category === 'Probable').length}</p></div>
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="tab-content hidden">
77
- <div class="card p-4 rounded-lg">
78
- <h2 class="text-xl font-bold mb-4">Vulnerability Details</h2>
79
- <div class="space-y-3">
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="collapsible flex justify-between items-center p-3 bg-gray-800 rounded-t-lg" onclick="toggleCollapse(${i})">
83
- <span class="font-bold ${getSeverityClass(v.severity)}">${v.severity}</span>
84
- <span class="flex-1 mx-4">${v.description}</span>
85
- <span class="${getConfidenceClass(v.confidence)}">${v.confidence} Confidence</span>
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="content-${i}" class="content p-4 bg-gray-900 rounded-b-lg">
88
- <h4 class="font-bold mb-2">Evidence</h4>
89
- <pre>${JSON.stringify(v.evidence, null, 2)}</pre>
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="tab-content hidden">
99
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
100
- <div class="card p-4 rounded-lg"> <h3 class="font-bold mb-2">Tech Stack</h3> <pre>${Array.isArray(report.attack_surface.tech_stack) ? report.attack_surface.tech_stack.join('\n') : (report.attack_surface.tech_stack || '')}</pre> </div>
101
- <div class="card p-4 rounded-lg"> <h3 class="font-bold mb-2">Forms</h3> <pre>${JSON.stringify(report.attack_surface.forms || report.attack_surface.forms_detected || [], null, 2)}</pre> </div>
102
- <div class="card p-4 rounded-lg col-span-1 md:col-span-2"> <h3 class="font-bold mb-2">Discovered Links</h3> <pre class="max-h-96">${Array.isArray(report.attack_surface.endpoints) ? report.attack_surface.endpoints.join('\n') : (report.attack_surface.endpoints || report.attack_surface.endpoints_discovered || '')}</pre> </div>
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="tab-content hidden">
108
- <div class="card p-4 rounded-lg">
109
- <h2 class="text-xl font-bold mb-4">Scan History</h2>
110
- <p class="text-gray-400">Feature coming soon. Historical data is being saved in the .omen/history/ directory.</p>
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-12 border-t border-gray-800 pt-4">
115
- <p>OMEN Security Framework - v1.0.14</p>
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
- function showTab(tabName) {
120
- document.querySelectorAll('.tab-content').forEach(tab => tab.classList.add('hidden'));
121
- document.getElementById(tabName).classList.remove('hidden');
122
- document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
123
- document.querySelector(`[onclick="showTab('${tabName}')"]`).classList.add('active');
124
- }
125
- function toggleCollapse(index) {
126
- const content = document.getElementById('content-' + index);
127
- content.style.display = content.style.display === 'block' ? 'none' : 'block';
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(`<h1>Error loading report</h1><p>Please run a scan first to generate omen-report.json. Error: ${err.message}</p>`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omen-sec-cli",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "OMEN — AI Security Engine",
5
5
  "main": "bin/index.js",
6
6
  "type": "module",
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.14 \n'));
12
+ console.log(chalk.gray(' Version: 1.0.16 \n'));
13
13
  }
14
14
 
15
15
  export function showHelp() {