omen-sec-cli 1.0.17 → 1.0.18

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/core/ui-server.js CHANGED
@@ -13,17 +13,25 @@ export async function startUIServer() {
13
13
  const data = await fs.readFile(reportPath, 'utf-8');
14
14
  const report = JSON.parse(data);
15
15
 
16
- const getSeverityClass = (severity) => {
17
- if (severity === 'Critical') return 'text-red-500';
18
- if (severity === 'High') return 'text-orange-500';
19
- if (severity === 'Medium') return 'text-yellow-500';
16
+ const historyDir = path.join(process.cwd(), '.omen', 'history');
17
+ let historyFiles = [];
18
+ try {
19
+ historyFiles = await fs.readdir(historyDir);
20
+ } catch (e) {}
21
+
22
+ const getSeverityClass = (severity = '') => {
23
+ const s = severity.toLowerCase();
24
+ if (s === 'critical') return 'text-red-500';
25
+ if (s === 'high') return 'text-orange-500';
26
+ if (s === 'medium') return 'text-yellow-500';
20
27
  return 'text-green-500';
21
28
  };
22
29
 
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';
30
+ const getCategoryBadge = (category = '') => {
31
+ const c = category.toLowerCase();
32
+ if (c === 'confirmed') return 'bg-red-900/50 text-red-400 border-red-800';
33
+ if (c === 'probable') return 'bg-orange-900/50 text-orange-400 border-orange-800';
34
+ if (c === 'hardening') return 'bg-blue-900/50 text-blue-400 border-blue-800';
27
35
  return 'bg-gray-800 text-gray-400 border-gray-700';
28
36
  };
29
37
 
@@ -68,6 +76,7 @@ export async function startUIServer() {
68
76
  <div class="flex border-b border-gray-800 mb-8 bg-[#111116] rounded-t-xl overflow-hidden">
69
77
  <div class="tab-btn active" data-target="dashboard">Dashboard</div>
70
78
  <div class="tab-btn" data-target="findings">Findings</div>
79
+ <div class="tab-btn" data-target="fixplan">Fix Plan</div>
71
80
  <div class="tab-btn" data-target="surface">Attack Surface</div>
72
81
  <div class="tab-btn" data-target="history">History</div>
73
82
  </div>
@@ -85,11 +94,11 @@ export async function startUIServer() {
85
94
  </div>
86
95
  <div class="card p-6 rounded-xl shadow-2xl">
87
96
  <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>
97
+ <p class="text-5xl font-black text-white">${(report.vulnerabilities || []).filter(v => v.category?.toLowerCase() === 'confirmed').length}</p>
89
98
  </div>
90
99
  <div class="card p-6 rounded-xl shadow-2xl">
91
100
  <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>
101
+ <p class="text-5xl font-black text-white">${(report.vulnerabilities || []).filter(v => v.category?.toLowerCase() === 'probable').length}</p>
93
102
  </div>
94
103
  </div>
95
104
 
@@ -99,7 +108,7 @@ export async function startUIServer() {
99
108
  <span class="w-2 h-2 bg-red-500 rounded-full mr-2"></span> Top Critical Findings
100
109
  </h2>
101
110
  <div class="space-y-4">
102
- ${(report.vulnerabilities || []).filter(v => v.severity === 'critical' || v.severity === 'high').slice(0, 5).map(v => `
111
+ ${(report.vulnerabilities || []).filter(v => v.severity?.toLowerCase() === 'critical' || v.severity?.toLowerCase() === 'high').slice(0, 5).map(v => `
103
112
  <div class="p-4 bg-[#1c1c22] rounded-lg border border-gray-800">
104
113
  <div class="flex justify-between items-start mb-2">
105
114
  <span class="font-bold ${getSeverityClass(v.severity)} text-sm uppercase">${v.severity}</span>
@@ -112,18 +121,57 @@ export async function startUIServer() {
112
121
  </div>
113
122
  <div class="card p-6 rounded-xl">
114
123
  <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
124
+ <span class="w-2 h-2 bg-blue-500 rounded-full mr-2"></span> Phase Intelligence
116
125
  </h2>
117
126
  <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 || 'N/A'}</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>
127
+ <div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Stack Detected</span> <span class="text-gray-300 font-bold">${report.discovery?.stack || 'N/A'}</span> </div>
128
+ <div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Boot Strategy</span> <span class="mono text-gray-300">${report.discovery?.boot_strategy || 'None'}</span> </div>
129
+ <div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Execution Steps</span> <span class="text-gray-300">${(report.plan?.steps || []).length} steps</span> </div>
130
+ <div class="flex justify-between border-b border-gray-800 pb-2"> <span class="text-gray-500">Last Scan Status</span> <span class="text-green-500 font-bold uppercase">${report.execution?.status || 'N/A'}</span> </div>
122
131
  </div>
123
132
  </div>
124
133
  </div>
125
134
  </div>
126
135
 
136
+ <!-- Findings Tab (already good) -->
137
+
138
+ <!-- Fix Plan Tab -->
139
+ <div id="fixplan" class="content-area">
140
+ <div class="card p-6 rounded-xl mb-8 border-l-4 border-blue-500 bg-blue-900/10">
141
+ <h2 class="text-2xl font-black mb-2 tracking-tight">REMEDIATION CHECKLIST</h2>
142
+ <p class="text-gray-400">Step-by-step guide to secure your application. These findings are prioritized by risk level.</p>
143
+ </div>
144
+ <div class="space-y-6">
145
+ ${(report.vulnerabilities || []).sort((a,b) => {
146
+ const w = {critical:4, high:3, medium:2, low:1, info:0};
147
+ return (w[b.severity?.toLowerCase()] || 0) - (w[a.severity?.toLowerCase()] || 0);
148
+ }).map((v, i) => `
149
+ <div class="card p-6 rounded-xl border-l-4 ${v.severity?.toLowerCase() === 'critical' ? 'border-red-500' : 'border-gray-700'}">
150
+ <div class="flex justify-between items-start mb-4">
151
+ <div>
152
+ <h3 class="text-lg font-bold text-white mb-1">${v.title || v.description}</h3>
153
+ <span class="text-[10px] mono text-gray-500 uppercase tracking-widest">ID: ${v.id}</span>
154
+ </div>
155
+ <div class="text-right">
156
+ <span class="block text-xs font-black ${getSeverityClass(v.severity)} uppercase">${v.severity}</span>
157
+ <span class="block text-[10px] text-gray-500 mono">${v.cwe || 'CWE-N/A'}</span>
158
+ </div>
159
+ </div>
160
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
161
+ <div class="bg-black/50 p-4 rounded-lg border border-gray-800">
162
+ <h4 class="text-[10px] font-bold text-gray-500 uppercase mb-2">How to Fix</h4>
163
+ <p class="text-sm text-gray-300 leading-relaxed">${v.remediation || 'No remediation provided.'}</p>
164
+ </div>
165
+ <div class="bg-black/50 p-4 rounded-lg border border-gray-800">
166
+ <h4 class="text-[10px] font-bold text-gray-500 uppercase mb-2">Verification Command</h4>
167
+ <code class="block text-xs text-blue-400 mono mt-2">npx omen-sec-cli verify --id ${v.id}</code>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ `).join('') || '<p class="text-center py-20 text-gray-500 italic">No vulnerabilities to fix. You are secure!</p>'}
172
+ </div>
173
+ </div>
174
+
127
175
  <!-- Findings Tab -->
128
176
  <div id="findings" class="content-area">
129
177
  <div class="card rounded-xl overflow-hidden">
@@ -174,18 +222,18 @@ export async function startUIServer() {
174
222
  <div class="lg:col-span-2 space-y-8">
175
223
  <div class="card p-6 rounded-xl">
176
224
  <h3 class="text-xl font-bold mb-4">Discovered Assets</h3>
177
- <pre class="mono max-h-[600px]">${(report.attack_surface?.endpoints || []).join('\n') || 'No endpoints discovered.'}</pre>
225
+ <pre class="mono max-h-[600px]">${(report.discovery?.entrypoints || []).concat(report.discovery?.critical_files || []).join('\n') || 'No assets discovered.'}</pre>
178
226
  </div>
179
227
  </div>
180
228
  <div class="space-y-8">
181
229
  <div class="card p-6 rounded-xl">
182
- <h3 class="text-xl font-bold mb-4">Forms & Inputs</h3>
183
- <pre class="mono">${JSON.stringify(report.attack_surface?.forms || [], null, 2)}</pre>
230
+ <h3 class="text-xl font-bold mb-4">Phase Log</h3>
231
+ <pre class="mono text-[10px]">${(report.execution?.logs || []).join('\n') || 'No execution logs.'}</pre>
184
232
  </div>
185
233
  <div class="card p-6 rounded-xl">
186
234
  <h3 class="text-xl font-bold mb-4">Tech Fingerprint</h3>
187
235
  <div class="flex flex-wrap gap-2">
188
- ${(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>'}
236
+ ${(report.attack_surface?.tech_stack || report.discovery?.stack ? [report.discovery.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>'}
189
237
  </div>
190
238
  </div>
191
239
  </div>
@@ -193,11 +241,23 @@ export async function startUIServer() {
193
241
  </div>
194
242
 
195
243
  <!-- History Tab -->
196
- <div id="history" class="content-area text-center py-20">
197
- <div class="card p-10 rounded-2xl max-w-lg mx-auto border-dashed border-gray-700 bg-transparent">
198
- <h2 class="text-2xl font-bold mb-4 text-gray-400">Scan Timeline</h2>
199
- <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>
200
- <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>
244
+ <div id="history" class="content-area">
245
+ <div class="card rounded-xl overflow-hidden">
246
+ <div class="p-6 border-b border-gray-800 bg-[#111116]">
247
+ <h2 class="text-xl font-bold">Scan History</h2>
248
+ <p class="text-sm text-gray-500">Historical snapshots saved in .omen/history/</p>
249
+ </div>
250
+ <div class="divide-y divide-gray-800">
251
+ ${historyFiles.reverse().map(file => `
252
+ <div class="p-4 flex justify-between items-center hover:bg-[#1c1c22]">
253
+ <div class="flex items-center gap-3">
254
+ <div class="w-2 h-2 bg-green-500 rounded-full"></div>
255
+ <span class="text-sm font-medium text-gray-300">${file}</span>
256
+ </div>
257
+ <span class="text-[10px] mono text-gray-600">${new Date(parseInt(file.split('-').pop()) || Date.now()).toLocaleString()}</span>
258
+ </div>
259
+ `).join('') || '<div class="p-20 text-center text-gray-500 italic">No historical scans found yet.</div>'}
260
+ </div>
201
261
  </div>
202
262
  </div>
203
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omen-sec-cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "description": "OMEN — AI Security Engine",
5
5
  "engines": {
6
6
  "node": ">=20.0.0"
@@ -18,7 +18,9 @@
18
18
  "axios": "^1.13.6",
19
19
  "chalk": "^5.3.0",
20
20
  "cheerio": "^1.1.0",
21
+ "commander": "^13.1.0",
21
22
  "dotenv": "^17.3.1",
23
+ "execa": "^9.6.1",
22
24
  "express": "^5.2.1",
23
25
  "glob": "^13.0.6",
24
26
  "js-yaml": "^4.1.1",