omen-sec-cli 1.0.19 → 1.0.20

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.
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
- import { glob } from 'glob';
3
+ import axios from 'axios';
4
+ import * as cheerio from 'cheerio';
4
5
 
5
6
  export async function runDiscovery(projectPath = process.cwd()) {
6
7
  const discovery = {
@@ -9,18 +10,27 @@ export async function runDiscovery(projectPath = process.cwd()) {
9
10
  entrypoints: [],
10
11
  boot_strategy: null,
11
12
  critical_files: [],
12
- dependencies: {}
13
+ dependencies: {},
14
+ is_remote: false
13
15
  };
14
16
 
17
+ // Check if it's a URL
18
+ if (projectPath.startsWith('http')) {
19
+ discovery.is_remote = true;
20
+ return await runRemoteDiscovery(projectPath, discovery);
21
+ }
22
+
15
23
  // 1. Load package.json if exists
16
24
  try {
17
25
  const pkgPath = path.join(projectPath, 'package.json');
18
- const pkgData = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));
26
+ const pkgContent = await fs.readFile(pkgPath, 'utf-8');
27
+ const pkgData = JSON.parse(pkgContent);
19
28
  discovery.dependencies = { ...pkgData.dependencies, ...pkgData.devDependencies };
20
29
 
21
30
  if (discovery.dependencies['next']) discovery.stack = 'Next.js';
22
31
  else if (discovery.dependencies['express']) discovery.stack = 'Node/Express';
23
32
  else if (discovery.dependencies['react']) discovery.stack = 'React SPA';
33
+ else discovery.stack = 'Node.js';
24
34
 
25
35
  // Boot strategy for Node
26
36
  if (pkgData.scripts) {
@@ -33,7 +43,7 @@ export async function runDiscovery(projectPath = process.cwd()) {
33
43
  try {
34
44
  const requirementsPath = path.join(projectPath, 'requirements.txt');
35
45
  await fs.access(requirementsPath);
36
- discovery.stack = discovery.stack === 'Unknown' ? 'Python' : discovery.stack;
46
+ if (discovery.stack === 'Unknown') discovery.stack = 'Python';
37
47
  discovery.critical_files.push('requirements.txt');
38
48
  } catch (e) {}
39
49
 
@@ -46,7 +56,7 @@ export async function runDiscovery(projectPath = process.cwd()) {
46
56
  } catch (e) {}
47
57
 
48
58
  // 4. Find entrypoints
49
- const commonEntrypoints = ['server.js', 'app.js', 'index.js', 'src/index.js', 'main.py', 'app.py'];
59
+ const commonEntrypoints = ['server.js', 'app.js', 'index.js', 'src/index.js', 'main.py', 'app.py', 'index.php'];
50
60
  for (const entry of commonEntrypoints) {
51
61
  try {
52
62
  await fs.access(path.join(projectPath, entry));
@@ -55,13 +65,52 @@ export async function runDiscovery(projectPath = process.cwd()) {
55
65
  }
56
66
 
57
67
  // 5. Critical Security Files
58
- const securityFiles = ['.env', '.env.local', '.env.production', '.git/config', 'docker-compose.yml', 'Dockerfile'];
68
+ const securityFiles = ['.env', '.env.local', '.env.production', '.git/config', 'docker-compose.yml', 'Dockerfile', 'package.json', 'composer.json', 'requirements.txt'];
59
69
  for (const file of securityFiles) {
60
70
  try {
61
71
  await fs.access(path.join(projectPath, file));
62
- discovery.critical_files.push(file);
72
+ if (!discovery.critical_files.includes(file)) discovery.critical_files.push(file);
63
73
  } catch (e) {}
64
74
  }
65
75
 
66
76
  return discovery;
67
77
  }
78
+
79
+ async function runRemoteDiscovery(url, discovery) {
80
+ try {
81
+ const response = await axios.get(url, {
82
+ timeout: 10000,
83
+ validateStatus: () => true,
84
+ headers: { 'User-Agent': 'OMEN-SEC-CLI/1.0.20 (Discovery)' }
85
+ });
86
+
87
+ const headers = response.headers;
88
+ const html = response.data || '';
89
+
90
+ // Header-based detection
91
+ if (headers['x-powered-by']) discovery.stack = headers['x-powered-by'];
92
+ if (headers['server']) discovery.stack = headers['server'];
93
+
94
+ // HTML-based detection
95
+ if (html.includes('next-head') || html.includes('_next/static')) discovery.stack = 'Next.js';
96
+ else if (html.includes('react-root') || html.includes('data-react')) discovery.stack = 'React';
97
+ else if (html.includes('wp-content')) discovery.stack = 'WordPress';
98
+ else if (html.includes('nuxt')) discovery.stack = 'Nuxt.js';
99
+
100
+ // Entrypoints as routes
101
+ const $ = cheerio.load(html);
102
+ $('a').each((i, el) => {
103
+ const href = $(el).attr('href');
104
+ if (href && !href.startsWith('#') && !href.startsWith('http') && discovery.entrypoints.length < 5) {
105
+ discovery.entrypoints.push(href);
106
+ }
107
+ });
108
+
109
+ discovery.entrypoints = [...new Set(discovery.entrypoints)];
110
+ discovery.boot_strategy = 'Remote URL';
111
+
112
+ } catch (err) {
113
+ discovery.stack = 'Unknown (Remote unreachable)';
114
+ }
115
+ return discovery;
116
+ }
package/core/engine-v2.js CHANGED
@@ -23,33 +23,49 @@ export async function plan() {
23
23
  const state = await loadState();
24
24
  const discovery = state.discovery;
25
25
 
26
- if (!discovery || !discovery.stack) {
26
+ if (!discovery || !discovery.stack || discovery.stack === 'Unknown') {
27
27
  console.log(chalk.red('No discovery data found. Run "omen discover" first.'));
28
28
  return;
29
29
  }
30
30
 
31
31
  const plan = {
32
32
  steps: [
33
- { id: 'static', action: 'Static Analysis', status: 'pending' },
34
- { id: 'dependencies', action: 'Dependency Audit', status: 'pending' }
33
+ { id: 'static', action: 'Static Analysis', status: 'pending' }
35
34
  ]
36
35
  };
37
36
 
38
- if (discovery.boot_strategy) {
37
+ // If local, check for dependencies
38
+ if (!discovery.is_remote) {
39
+ plan.steps.push({ id: 'dependencies', action: 'Dependency Audit', status: 'pending' });
40
+ }
41
+
42
+ if (discovery.boot_strategy && discovery.boot_strategy !== 'Remote URL') {
39
43
  plan.steps.push({ id: 'boot', action: `Boot App (${discovery.boot_strategy})`, status: 'pending' });
40
44
  plan.steps.push({ id: 'dynamic', action: 'Dynamic Analysis (Localhost)', status: 'pending' });
45
+ } else if (discovery.is_remote) {
46
+ plan.steps.push({ id: 'dynamic', action: 'Dynamic Analysis (Remote)', status: 'pending' });
41
47
  }
42
48
 
43
49
  console.log(chalk.white(`Plan generated with ${plan.steps.length} steps.`));
50
+ // IMPORTANTE: Retornar o plano e salvar no estado
44
51
  await saveState({ plan });
45
52
  return plan;
46
53
  }
47
54
 
48
55
  export async function execute() {
49
56
  console.log(chalk.bold.cyan('\n--- Phase 3: Execution ---'));
50
- const state = await loadState();
57
+
58
+ // Recarregar o estado para garantir que temos o plano
59
+ let state = await loadState();
60
+
61
+ if (!state.plan || !state.plan.steps || state.plan.steps.length === 0) {
62
+ console.log(chalk.yellow('No plan found in state. Attempting to generate one...'));
63
+ await plan();
64
+ state = await loadState(); // Recarregar após plan()
65
+ }
66
+
51
67
  if (!state.plan || !state.plan.steps) {
52
- console.log(chalk.red('No plan found. Run "omen plan" first.'));
68
+ console.log(chalk.red('Failed to generate or load plan. Run "omen discover" first.'));
53
69
  return;
54
70
  }
55
71
 
@@ -59,12 +75,18 @@ export async function execute() {
59
75
  for (const step of state.plan.steps) {
60
76
  console.log(chalk.yellow(`\nExecuting: ${step.action}...`));
61
77
 
62
- if (step.id === 'static') {
78
+ if (step.id === 'static' && !state.discovery.is_remote) {
63
79
  const localResult = await scanLocalProject();
64
80
  vulnerabilities.push(...localResult.vulnerabilities);
65
81
  executionLogs.push(`Static analysis scanned ${localResult.localFilesScanned} files.`);
66
82
  }
67
83
 
84
+ if (step.id === 'dynamic' && state.discovery.is_remote) {
85
+ const dynamicResult = await scanRemoteTarget(state.discovery.path);
86
+ vulnerabilities.push(...dynamicResult.vulnerabilities);
87
+ executionLogs.push(`Dynamic analysis performed on ${state.discovery.path}`);
88
+ }
89
+
68
90
  if (step.id === 'boot') {
69
91
  const bootResult = await bootLocalApp(state.discovery.boot_strategy);
70
92
  if (!bootResult.success) {
@@ -106,11 +128,13 @@ export async function execute() {
106
128
  const resultData = {
107
129
  execution,
108
130
  score,
109
- vulnerabilities,
131
+ vulnerabilities: vulnerabilities || [],
110
132
  riskLevel: score < 40 ? 'Critical' : score < 70 ? 'High' : score < 90 ? 'Medium' : 'Low',
111
133
  timestamp: new Date().toISOString(),
112
134
  target: state.discovery?.path || process.cwd(),
113
- scan_id: `OMEN-${Date.now()}`
135
+ scan_id: `OMEN-${Date.now()}`,
136
+ discovery: state.discovery || {},
137
+ plan: state.plan || {}
114
138
  };
115
139
 
116
140
  await saveState(resultData);
@@ -15,7 +15,7 @@ export async function scanRemoteTarget(targetUrl) {
15
15
  const response = await axios.get(targetUrl, {
16
16
  timeout: 15000,
17
17
  validateStatus: () => true,
18
- headers: { 'User-Agent': 'OMEN-SEC-CLI/1.0.19 (Security Audit)' }
18
+ headers: { 'User-Agent': 'OMEN-SEC-CLI/1.0.20 (Security Audit)' }
19
19
  });
20
20
 
21
21
  serverStatus = response.status;
@@ -338,7 +338,7 @@ export async function scanRemoteTarget(targetUrl) {
338
338
  const res = await axios.get(testUrl.href, {
339
339
  timeout: 5000,
340
340
  validateStatus: () => true,
341
- headers: { 'User-Agent': 'OMEN-SEC-CLI/1.0.19 (Security Audit)' }
341
+ headers: { 'User-Agent': 'OMEN-SEC-CLI/1.0.20 (Security Audit)' }
342
342
  });
343
343
 
344
344
  const evidence = {
@@ -41,6 +41,6 @@ export function generateFixPlan(scanData) {
41
41
  });
42
42
  }
43
43
 
44
- md += `\n*Gerado automaticamente pelo OMEN SEC-CLI v1.0.19 - Protocolo Zero-Copy AI Ativo*\n`;
44
+ md += `\n*Gerado automaticamente pelo OMEN SEC-CLI v1.0.20 - Protocolo Zero-Copy AI Ativo*\n`;
45
45
  return md;
46
46
  }
package/core/ui-server.js CHANGED
@@ -44,6 +44,10 @@ export async function startUIServer() {
44
44
  <title>OMEN SEC-CLI Evidence Center</title>
45
45
  <script src="https://cdn.tailwindcss.com"></script>
46
46
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
47
+ <script>
48
+ // Ensure data safety for JS
49
+ const report = ${JSON.stringify(report)};
50
+ </script>
47
51
  <style>
48
52
  body { background-color: #0a0a0c; color: #e0e0e0; font-family: 'Inter', sans-serif; }
49
53
  .mono { font-family: 'JetBrains Mono', monospace; }
@@ -124,10 +128,10 @@ export async function startUIServer() {
124
128
  <span class="w-2 h-2 bg-blue-500 rounded-full mr-2"></span> Phase Intelligence
125
129
  </h2>
126
130
  <div class="space-y-4 text-sm">
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>
131
+ <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 && report.discovery && report.discovery.stack) || 'N/A'}</span> </div>
132
+ <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 && report.discovery && report.discovery.boot_strategy) || 'None'}</span> </div>
133
+ <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 && report.plan && report.plan.steps && report.plan.steps.length) || 0} steps</span> </div>
134
+ <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 && report.execution && report.execution.status) || 'N/A'}</span> </div>
131
135
  </div>
132
136
  </div>
133
137
  </div>
@@ -222,18 +226,18 @@ export async function startUIServer() {
222
226
  <div class="lg:col-span-2 space-y-8">
223
227
  <div class="card p-6 rounded-xl">
224
228
  <h3 class="text-xl font-bold mb-4">Discovered Assets</h3>
225
- <pre class="mono max-h-[600px]">${(report.discovery?.entrypoints || []).concat(report.discovery?.critical_files || []).join('\n') || 'No assets discovered.'}</pre>
229
+ <pre class="mono max-h-[600px]">${((report && report.discovery && report.discovery.entrypoints) || []).concat((report && report.discovery && report.discovery.critical_files) || []).join('\n') || 'No assets discovered.'}</pre>
226
230
  </div>
227
231
  </div>
228
232
  <div class="space-y-8">
229
233
  <div class="card p-6 rounded-xl">
230
234
  <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>
235
+ <pre class="mono text-[10px]">${(report && report.execution && report.execution.logs && report.execution.logs.join('\n')) || 'No execution logs.'}</pre>
232
236
  </div>
233
237
  <div class="card p-6 rounded-xl">
234
238
  <h3 class="text-xl font-bold mb-4">Tech Fingerprint</h3>
235
239
  <div class="flex flex-wrap gap-2">
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>'}
240
+ ${(report && report.discovery && 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>'}
237
241
  </div>
238
242
  </div>
239
243
  </div>
@@ -262,7 +266,7 @@ export async function startUIServer() {
262
266
  </div>
263
267
 
264
268
  <footer class="text-center text-gray-600 mt-16 border-t border-gray-900 pt-8 mb-10">
265
- <p class="text-xs uppercase tracking-widest font-bold mb-2">OMEN Security Framework - v1.0.19</p>
269
+ <p class="text-xs uppercase tracking-widest font-bold mb-2">OMEN Security Framework - v1.0.20</p>
266
270
  <p class="text-[10px] text-gray-700 italic">"The eye that never sleeps, the code that never fails."</p>
267
271
  </footer>
268
272
  </div>
@@ -0,0 +1,139 @@
1
+ # 🛡️ OMEN Security Fix Plan: https://www.fnstore.com.br/
2
+
3
+ > **Data do Scan:** 2026-03-24T16:59:36.550Z
4
+ > **Score Atual:** 71/100
5
+ > **Vulnerabilidades:** 9
6
+
7
+ Este documento é um guia passo-a-passo para remediar as falhas encontradas. Após aplicar as correções, execute `omen verify` para confirmar a resolução.
8
+
9
+ ## 🚨 Checklist de Remediação
10
+
11
+ ### [ ] 🟠 [HIGH] Content-Security-Policy Missing
12
+ - **ID:** `REM-VULN-1774371576147-2`
13
+ - **CWE:** CWE-1022
14
+ - **Confiança:** high
15
+
16
+ #### 🔍 Evidência
17
+ #### 🛠️ Estratégia de Correção
18
+ Define a strict Content-Security-Policy to restrict source domains for scripts, styles, and other resources.
19
+
20
+ #### ✅ Comando de Verificação
21
+ `npx omen-sec-cli verify --id REM-VULN-1774371576147-2` (ou apenas `omen verify` para checar tudo)
22
+
23
+ ---
24
+
25
+ ### [ ] 🟠 [HIGH] Permissive CORS Policy
26
+ - **ID:** `REM-CORS-1774371576147`
27
+ - **CWE:** CWE-942
28
+ - **Confiança:** high
29
+
30
+ #### 🔍 Evidência
31
+ - **Detalhe:** Access-Control-Allow-Origin: *
32
+ #### 🛠️ Estratégia de Correção
33
+ Restrict Access-Control-Allow-Origin to trusted domains only.
34
+
35
+ #### ✅ Comando de Verificação
36
+ `npx omen-sec-cli verify --id REM-CORS-1774371576147` (ou apenas `omen verify` para checar tudo)
37
+
38
+ ---
39
+
40
+ ### [ ] 🟡 [MEDIUM] Potential Admin Panel Exposure
41
+ - **ID:** `REM-PROBABLE-PANEL-1774371576279`
42
+ - **CWE:** CWE-284
43
+ - **Confiança:** low
44
+
45
+ #### 🔍 Evidência
46
+ #### 🛠️ Estratégia de Correção
47
+ Restrict access to the admin panel using IP whitelisting or other access control mechanisms.
48
+
49
+ #### ✅ Comando de Verificação
50
+ `npx omen-sec-cli verify --id REM-PROBABLE-PANEL-1774371576279` (ou apenas `omen verify` para checar tudo)
51
+
52
+ ---
53
+
54
+ ### [ ] 🔵 [LOW] X-Frame-Options Missing
55
+ - **ID:** `REM-VULN-1774371576147-4`
56
+ - **CWE:** CWE-1021
57
+ - **Confiança:** high
58
+
59
+ #### 🔍 Evidência
60
+ #### 🛠️ Estratégia de Correção
61
+ Set the X-Frame-Options header to DENY or SAMEORIGIN.
62
+
63
+ #### ✅ Comando de Verificação
64
+ `npx omen-sec-cli verify --id REM-VULN-1774371576147-4` (ou apenas `omen verify` para checar tudo)
65
+
66
+ ---
67
+
68
+ ### [ ] 🔵 [LOW] X-Content-Type-Options Missing
69
+ - **ID:** `REM-VULN-1774371576147-6`
70
+ - **CWE:** CWE-116
71
+ - **Confiança:** high
72
+
73
+ #### 🔍 Evidência
74
+ #### 🛠️ Estratégia de Correção
75
+ Add the "X-Content-Type-Options: nosniff" header to all responses.
76
+
77
+ #### ✅ Comando de Verificação
78
+ `npx omen-sec-cli verify --id REM-VULN-1774371576147-6` (ou apenas `omen verify` para checar tudo)
79
+
80
+ ---
81
+
82
+ ### [ ] 🔵 [INFO] Technology Stack Identified
83
+ - **ID:** `REM-TECH-1774371576168`
84
+ - **CWE:** CWE-200
85
+ - **Confiança:** high
86
+
87
+ #### 🔍 Evidência
88
+ #### 🛠️ Estratégia de Correção
89
+ Minimal tech disclosure is recommended to prevent targeted attacks.
90
+
91
+ #### ✅ Comando de Verificação
92
+ `npx omen-sec-cli verify --id REM-TECH-1774371576168` (ou apenas `omen verify` para checar tudo)
93
+
94
+ ---
95
+
96
+ ### [ ] 🔵 [INFO] Protected Path Discovered
97
+ - **ID:** `REM-INFO-FORBIDDEN-1774371576298`
98
+ - **CWE:** CWE-204
99
+ - **Confiança:** medium
100
+
101
+ #### 🔍 Evidência
102
+ #### 🛠️ Estratégia de Correção
103
+ None required, but ensure that the 403 response does not leak information about the internal structure.
104
+
105
+ #### ✅ Comando de Verificação
106
+ `npx omen-sec-cli verify --id REM-INFO-FORBIDDEN-1774371576298` (ou apenas `omen verify` para checar tudo)
107
+
108
+ ---
109
+
110
+ ### [ ] 🔵 [INFO] Protected Path Discovered
111
+ - **ID:** `REM-INFO-FORBIDDEN-1774371576313`
112
+ - **CWE:** CWE-204
113
+ - **Confiança:** medium
114
+
115
+ #### 🔍 Evidência
116
+ #### 🛠️ Estratégia de Correção
117
+ None required, but ensure that the 403 response does not leak information about the internal structure.
118
+
119
+ #### ✅ Comando de Verificação
120
+ `npx omen-sec-cli verify --id REM-INFO-FORBIDDEN-1774371576313` (ou apenas `omen verify` para checar tudo)
121
+
122
+ ---
123
+
124
+ ### [ ] 🔵 [INFO] Protected Path Discovered
125
+ - **ID:** `REM-INFO-FORBIDDEN-1774371576351`
126
+ - **CWE:** CWE-204
127
+ - **Confiança:** medium
128
+
129
+ #### 🔍 Evidência
130
+ #### 🛠️ Estratégia de Correção
131
+ None required, but ensure that the 403 response does not leak information about the internal structure.
132
+
133
+ #### ✅ Comando de Verificação
134
+ `npx omen-sec-cli verify --id REM-INFO-FORBIDDEN-1774371576351` (ou apenas `omen verify` para checar tudo)
135
+
136
+ ---
137
+
138
+
139
+ *Gerado automaticamente pelo OMEN SEC-CLI v1.0.20 - Protocolo Zero-Copy AI Ativo*