omen-sec-cli 1.0.16 → 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/README.md +56 -63
- package/bin/index.js +67 -25
- package/core/discover/stack-detector.js +67 -0
- package/core/engine-v2.js +170 -0
- package/core/generator.js +43 -11
- package/core/local-scanner.js +154 -41
- package/core/remote-scanner.js +220 -98
- package/core/reporters/fix-plan-reporter.js +46 -0
- package/core/reporters/markdown-reporter.js +25 -0
- package/core/runners/local-app-runner.js +39 -0
- package/core/scanner.js +34 -24
- package/core/state/state-manager.js +43 -0
- package/core/ui-server.js +101 -37
- package/package.json +6 -1
- package/ui/banner.js +1 -1
package/README.md
CHANGED
|
@@ -1,78 +1,84 @@
|
|
|
1
|
-
# <p align="center"> <img src="https://img.icons8.com/nolan/128/security-shield.png" width="100" /> <br> OMEN SEC-CLI </p>
|
|
1
|
+
# <p align="center"> <img src="https://img.icons8.com/nolan/128/security-shield.png" width="100" /> <br> OMEN SEC-CLI v1.0.18 </p>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="https://img.shields.io/badge/Version-1.0.
|
|
5
|
-
<img src="https://img.shields.io/badge/
|
|
6
|
-
<img src="https://img.shields.io/badge/
|
|
4
|
+
<img src="https://img.shields.io/badge/Version-1.0.17-red?style=for-the-badge" />
|
|
5
|
+
<img src="https://img.shields.io/badge/Phase--Based-DevSecOps-000000?style=for-the-badge&logo=openai" />
|
|
6
|
+
<img src="https://img.shields.io/badge/Zero--Copy-AI--Protocol-green?style=for-the-badge" />
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## 🛡️ O que é o OMEN?
|
|
12
12
|
|
|
13
|
-
O **OMEN SEC-CLI**
|
|
13
|
+
O **OMEN SEC-CLI** evoluiu de um simples scanner para um **Framework de Auditoria DevSecOps Profissional**. Inspirado no modelo operacional de ferramentas de elite, o OMEN automatiza o ciclo completo de segurança: desde a descoberta da stack até a verificação das correções.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Ele é o coração do seu fluxo de trabalho de segurança, permitindo que IAs locais (como o **Trae** ou **Cursor**) ajam como auditores e arquitetos de segurança, executando correções diretamente no código através do protocolo **Zero-Copy AI Handover**.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
## ⚡
|
|
19
|
+
## ⚡ Workflow por Fases (Inspirado em get-shit-done-cc)
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
Integração nativa com IAs locais. Ao rodar com a flag `--auto-fix`, o OMEN emite um protocolo de **Dual Personality (Auditor & Arquiteto)** que força sua IA a assumir o controle e corrigir o código sem que você precise copiar uma única linha.
|
|
21
|
+
O OMEN 2.0 agora opera em um ciclo stateful de 5 fases:
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
### 3. 🔍 Scan Local Profundo & CVEs Reais
|
|
31
|
-
- **Integração OSV.dev (Google):** Consulta em tempo real o banco de dados de vulnerabilidades para suas dependências no `package.json`.
|
|
32
|
-
- **SAST (Static Application Security Testing):** Detecta segredos hardcoded, uso de `eval()`, injeções de SQL e padrões inseguros no código fonte.
|
|
33
|
-
|
|
34
|
-
### 4. 🛠️ Sistema de Plugins (Regras Customizadas)
|
|
35
|
-
Crie suas próprias regras de segurança em um arquivo `omen-rules.yaml` na raiz do seu projeto. Defina padrões Regex, severidade e alertas personalizados que o OMEN deve seguir.
|
|
36
|
-
|
|
37
|
-
### 5. 📊 Dashboard Local (Cyberpunk Web UI)
|
|
38
|
-
Visualize seus relatórios de forma profissional. O OMEN sobe um servidor local com uma interface moderna para análise detalhada de scores e riscos.
|
|
23
|
+
1. **🔍 Discover**: Detecta automaticamente a stack (Node, React, Python, PHP), entrypoints e estratégia de boot.
|
|
24
|
+
2. **📋 Plan**: Gera um plano de execução personalizado baseado na estrutura do seu projeto.
|
|
25
|
+
3. **🚀 Execute**: Orquestra analisadores estáticos (SAST) e dinâmicos (DAST), incluindo o boot automático da aplicação local com healthchecks.
|
|
26
|
+
4. **📊 Report**: Gera relatórios multi-formato, incluindo o novo **Fix Plan** (checklist de remediação).
|
|
27
|
+
5. **✅ Verify**: Re-escaneia o projeto e compara os resultados para confirmar se as falhas foram resolvidas.
|
|
39
28
|
|
|
40
29
|
---
|
|
41
30
|
|
|
42
31
|
## 🚀 Instalação e Uso
|
|
43
32
|
|
|
44
|
-
|
|
33
|
+
O OMEN deve ser usado via `npx` para garantir que você sempre tenha a versão mais recente e estável:
|
|
45
34
|
|
|
46
|
-
###
|
|
35
|
+
### Fluxo Completo de Auditoria
|
|
47
36
|
```bash
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
# 1. Descobrir a estrutura do projeto
|
|
38
|
+
npx omen-sec-cli discover
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
npx omen-sec-cli robotscan https://seu-alvo.com
|
|
54
|
-
```
|
|
40
|
+
# 2. Planejar o scan
|
|
41
|
+
npx omen-sec-cli plan
|
|
55
42
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
npx omen-sec-cli robotscan --local
|
|
59
|
-
```
|
|
43
|
+
# 3. Executar o scan (SAST + DAST com Boot Local)
|
|
44
|
+
npx omen-sec-cli execute
|
|
60
45
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
46
|
+
# 4. Gerar relatórios e Fix Plan
|
|
47
|
+
npx omen-sec-cli report
|
|
48
|
+
|
|
49
|
+
# 5. Verificar correções (pós-fix)
|
|
50
|
+
npx omen-sec-cli verify
|
|
64
51
|
```
|
|
65
52
|
|
|
66
|
-
###
|
|
53
|
+
### Dashboard Cyberpunk
|
|
54
|
+
Visualize tudo em uma interface moderna e detalhada:
|
|
67
55
|
```bash
|
|
68
56
|
npx omen-sec-cli ui
|
|
69
57
|
```
|
|
70
58
|
|
|
71
59
|
---
|
|
72
60
|
|
|
73
|
-
##
|
|
61
|
+
## 🧠 Zero-Copy AI (Protocolo OMEN PRIME)
|
|
62
|
+
|
|
63
|
+
O OMEN foi desenhado para ser "assimilado" por IAs. Ao abrir o arquivo `omen-reports/omen-fix-plan.md` ou `omen-ai.txt` no seu editor (Trae/Cursor), a IA assume o protocolo **Dual Personality**:
|
|
64
|
+
- **O AUDITOR:** Analisa a causa raiz da falha caractere por caractere.
|
|
65
|
+
- **O ARQUITETO:** Implementa a correção sugerida diretamente no seu código.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🔍 Funcionalidades de Elite
|
|
70
|
+
|
|
71
|
+
- **SAST Profundo**: Detecção de segredos, eval(), LFI, Insecure JWT, Prototype Pollution e mais.
|
|
72
|
+
- **DAST Inteligente**: Crawler/Spider, Path Fuzzer (com validação de falso positivo), CORS e Cookie Security.
|
|
73
|
+
- **Integração OSV.dev**: Consulta em tempo real de CVEs para dependências NPM.
|
|
74
|
+
- **Boot Local Automático**: O OMEN sobe sua aplicação, aguarda o healthcheck e executa o scan dinâmico sem intervenção manual.
|
|
75
|
+
- **Security Score Weighted**: Pontuação de 0 a 100 baseada na severidade e confiança das vulnerabilidades.
|
|
76
|
+
|
|
77
|
+
---
|
|
74
78
|
|
|
75
|
-
|
|
79
|
+
## ⚙️ Configuração (omen-rules.yaml)
|
|
80
|
+
|
|
81
|
+
Estenda o scanner com suas próprias regras de segurança:
|
|
76
82
|
|
|
77
83
|
```yaml
|
|
78
84
|
rules:
|
|
@@ -81,37 +87,24 @@ rules:
|
|
|
81
87
|
severity: "Critical"
|
|
82
88
|
description: "Palavra proibida detectada no código!"
|
|
83
89
|
cwe: "CWE-798"
|
|
84
|
-
- pattern: "localStorage\\.set"
|
|
85
|
-
type: "Insecure Storage"
|
|
86
|
-
severity: "Medium"
|
|
87
|
-
description: "Evite salvar dados sensíveis no localStorage."
|
|
88
90
|
```
|
|
89
91
|
|
|
90
92
|
---
|
|
91
93
|
|
|
92
|
-
## 📂 Estrutura de Relatórios
|
|
93
|
-
|
|
94
|
-
Após cada scan, o OMEN gera uma pasta `omen-reports/` com:
|
|
95
|
-
- `omen-report.json`: Dados brutos para integração.
|
|
96
|
-
- `omen-report.txt`: Resumo legível para humanos.
|
|
97
|
-
- `omen-ai.txt`: Super Prompt pronto para qualquer IA externa.
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## 🛡️ Protocolo OMEN PRIME
|
|
94
|
+
## 📂 Estrutura de Relatórios (`omen-reports/`)
|
|
102
95
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
96
|
+
- `omen-report.json`: Estado completo do scan (Schema 1.0).
|
|
97
|
+
- `omen-fix-plan.md`: **Checklist prioritário** para remediação com guias de "How to Fix".
|
|
98
|
+
- `omen-report.md`: Relatório executivo para stakeholders.
|
|
99
|
+
- `omen-ai.txt`: Super-protocolo para handover de IA.
|
|
106
100
|
|
|
107
101
|
---
|
|
108
102
|
|
|
109
103
|
## 🤝 Comunidade e Suporte
|
|
110
104
|
|
|
111
|
-
- **
|
|
105
|
+
- **GitHub**: [Report a Bug](https://github.com/omen-sec/omen-cli)
|
|
112
106
|
- **Discord**: [Join our Community](https://discord.gg/omen-security)
|
|
113
|
-
- **GitHub**: [Report a Bug](https://github.com/omen)
|
|
114
107
|
|
|
115
108
|
<p align="center">
|
|
116
|
-
<i>
|
|
109
|
+
<i>OMEN: Security for developers, by developers.</i>
|
|
117
110
|
</p>
|
package/bin/index.js
CHANGED
|
@@ -1,36 +1,78 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { Command } from 'commander';
|
|
3
4
|
import { runScan } from '../core/engine.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { discover, plan, execute, report, verify } from '../core/engine-v2.js';
|
|
6
|
+
import { showBanner } from '../ui/banner.js';
|
|
6
7
|
import { startUIServer } from '../core/ui-server.js';
|
|
8
|
+
import { parseArgs } from '../utils/args.js';
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
const args = parseArgs(process.argv);
|
|
10
|
+
const program = new Command();
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
program
|
|
13
|
+
.name('omen')
|
|
14
|
+
.description('OMEN — AI Security Engine & DevSecOps Audit Framework')
|
|
15
|
+
.version('1.0.17');
|
|
16
|
+
|
|
17
|
+
// Old command for backward compatibility
|
|
18
|
+
program
|
|
19
|
+
.command('robotscan [target]')
|
|
20
|
+
.description('Run full automated scan (Legacy)')
|
|
21
|
+
.option('--local', 'Scan local project')
|
|
22
|
+
.option('--auto-fix', 'AI Auto-fix')
|
|
23
|
+
.action(async (target, options) => {
|
|
12
24
|
showBanner();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
25
|
+
const args = { command: 'robotscan', target, flags: options };
|
|
26
|
+
await runScan(args);
|
|
27
|
+
});
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
// New Phase-based commands
|
|
30
|
+
program
|
|
31
|
+
.command('discover [path]')
|
|
32
|
+
.description('Phase 1: Detect stack, entrypoints and structure')
|
|
33
|
+
.action(async (path) => {
|
|
34
|
+
showBanner();
|
|
35
|
+
await discover(path || process.cwd());
|
|
36
|
+
});
|
|
21
37
|
|
|
22
|
-
|
|
38
|
+
program
|
|
39
|
+
.command('plan')
|
|
40
|
+
.description('Phase 2: Generate execution plan based on discovery')
|
|
41
|
+
.action(async () => {
|
|
42
|
+
showBanner();
|
|
43
|
+
await plan();
|
|
44
|
+
});
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
program
|
|
47
|
+
.command('execute')
|
|
48
|
+
.description('Phase 3: Run static and dynamic analyzers')
|
|
49
|
+
.action(async () => {
|
|
50
|
+
showBanner();
|
|
51
|
+
await execute();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
program
|
|
55
|
+
.command('report')
|
|
56
|
+
.description('Phase 4: Generate audit reports')
|
|
57
|
+
.action(async () => {
|
|
58
|
+
showBanner();
|
|
59
|
+
await report();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
program
|
|
63
|
+
.command('verify')
|
|
64
|
+
.description('Phase 5: Confirm if issues were resolved')
|
|
65
|
+
.action(async () => {
|
|
66
|
+
showBanner();
|
|
67
|
+
await verify();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
program
|
|
71
|
+
.command('ui')
|
|
72
|
+
.description('Start local Web Dashboard')
|
|
73
|
+
.action(async () => {
|
|
74
|
+
showBanner();
|
|
27
75
|
await startUIServer();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
main().catch(err => {
|
|
34
|
-
console.error('\n[Error]', err.message);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
|
|
5
|
+
export async function runDiscovery(projectPath = process.cwd()) {
|
|
6
|
+
const discovery = {
|
|
7
|
+
path: projectPath,
|
|
8
|
+
stack: 'Unknown',
|
|
9
|
+
entrypoints: [],
|
|
10
|
+
boot_strategy: null,
|
|
11
|
+
critical_files: [],
|
|
12
|
+
dependencies: {}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// 1. Load package.json if exists
|
|
16
|
+
try {
|
|
17
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
18
|
+
const pkgData = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));
|
|
19
|
+
discovery.dependencies = { ...pkgData.dependencies, ...pkgData.devDependencies };
|
|
20
|
+
|
|
21
|
+
if (discovery.dependencies['next']) discovery.stack = 'Next.js';
|
|
22
|
+
else if (discovery.dependencies['express']) discovery.stack = 'Node/Express';
|
|
23
|
+
else if (discovery.dependencies['react']) discovery.stack = 'React SPA';
|
|
24
|
+
|
|
25
|
+
// Boot strategy for Node
|
|
26
|
+
if (pkgData.scripts) {
|
|
27
|
+
if (pkgData.scripts.dev) discovery.boot_strategy = 'npm run dev';
|
|
28
|
+
else if (pkgData.scripts.start) discovery.boot_strategy = 'npm start';
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {}
|
|
31
|
+
|
|
32
|
+
// 2. Detect Python
|
|
33
|
+
try {
|
|
34
|
+
const requirementsPath = path.join(projectPath, 'requirements.txt');
|
|
35
|
+
await fs.access(requirementsPath);
|
|
36
|
+
discovery.stack = discovery.stack === 'Unknown' ? 'Python' : discovery.stack;
|
|
37
|
+
discovery.critical_files.push('requirements.txt');
|
|
38
|
+
} catch (e) {}
|
|
39
|
+
|
|
40
|
+
// 3. Detect PHP/Laravel/Wordpress
|
|
41
|
+
try {
|
|
42
|
+
const composerPath = path.join(projectPath, 'composer.json');
|
|
43
|
+
await fs.access(composerPath);
|
|
44
|
+
discovery.stack = 'PHP/Laravel';
|
|
45
|
+
discovery.critical_files.push('composer.json');
|
|
46
|
+
} catch (e) {}
|
|
47
|
+
|
|
48
|
+
// 4. Find entrypoints
|
|
49
|
+
const commonEntrypoints = ['server.js', 'app.js', 'index.js', 'src/index.js', 'main.py', 'app.py'];
|
|
50
|
+
for (const entry of commonEntrypoints) {
|
|
51
|
+
try {
|
|
52
|
+
await fs.access(path.join(projectPath, entry));
|
|
53
|
+
discovery.entrypoints.push(entry);
|
|
54
|
+
} catch (e) {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 5. Critical Security Files
|
|
58
|
+
const securityFiles = ['.env', '.env.local', '.env.production', '.git/config', 'docker-compose.yml', 'Dockerfile'];
|
|
59
|
+
for (const file of securityFiles) {
|
|
60
|
+
try {
|
|
61
|
+
await fs.access(path.join(projectPath, file));
|
|
62
|
+
discovery.critical_files.push(file);
|
|
63
|
+
} catch (e) {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return discovery;
|
|
67
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { runDiscovery } from './discover/stack-detector.js';
|
|
3
|
+
import { saveState, loadState } from './state/state-manager.js';
|
|
4
|
+
import { bootLocalApp } from './runners/local-app-runner.js';
|
|
5
|
+
import { scanLocalProject } from './local-scanner.js';
|
|
6
|
+
import { scanRemoteTarget } from './remote-scanner.js';
|
|
7
|
+
import { generateOutputs } from './generator.js';
|
|
8
|
+
import { compareWithPreviousScan } from './diff-engine.js';
|
|
9
|
+
|
|
10
|
+
export async function discover(path) {
|
|
11
|
+
console.log(chalk.bold.cyan('\n--- Phase 1: Discovery ---'));
|
|
12
|
+
const discoveryData = await runDiscovery(path);
|
|
13
|
+
console.log(chalk.white(`Stack detected: ${discoveryData.stack}`));
|
|
14
|
+
console.log(chalk.white(`Entrypoints: ${discoveryData.entrypoints.join(', ') || 'None'}`));
|
|
15
|
+
console.log(chalk.white(`Critical files: ${discoveryData.critical_files.join(', ')}`));
|
|
16
|
+
|
|
17
|
+
await saveState({ discovery: discoveryData });
|
|
18
|
+
return discoveryData;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function plan() {
|
|
22
|
+
console.log(chalk.bold.cyan('\n--- Phase 2: Planning ---'));
|
|
23
|
+
const state = await loadState();
|
|
24
|
+
const discovery = state.discovery;
|
|
25
|
+
|
|
26
|
+
if (!discovery || !discovery.stack) {
|
|
27
|
+
console.log(chalk.red('No discovery data found. Run "omen discover" first.'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const plan = {
|
|
32
|
+
steps: [
|
|
33
|
+
{ id: 'static', action: 'Static Analysis', status: 'pending' },
|
|
34
|
+
{ id: 'dependencies', action: 'Dependency Audit', status: 'pending' }
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (discovery.boot_strategy) {
|
|
39
|
+
plan.steps.push({ id: 'boot', action: `Boot App (${discovery.boot_strategy})`, status: 'pending' });
|
|
40
|
+
plan.steps.push({ id: 'dynamic', action: 'Dynamic Analysis (Localhost)', status: 'pending' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(chalk.white(`Plan generated with ${plan.steps.length} steps.`));
|
|
44
|
+
await saveState({ plan });
|
|
45
|
+
return plan;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function execute() {
|
|
49
|
+
console.log(chalk.bold.cyan('\n--- Phase 3: Execution ---'));
|
|
50
|
+
const state = await loadState();
|
|
51
|
+
if (!state.plan || !state.plan.steps) {
|
|
52
|
+
console.log(chalk.red('No plan found. Run "omen plan" first.'));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let vulnerabilities = [];
|
|
57
|
+
let executionLogs = [];
|
|
58
|
+
|
|
59
|
+
for (const step of state.plan.steps) {
|
|
60
|
+
console.log(chalk.yellow(`\nExecuting: ${step.action}...`));
|
|
61
|
+
|
|
62
|
+
if (step.id === 'static') {
|
|
63
|
+
const localResult = await scanLocalProject();
|
|
64
|
+
vulnerabilities.push(...localResult.vulnerabilities);
|
|
65
|
+
executionLogs.push(`Static analysis scanned ${localResult.localFilesScanned} files.`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (step.id === 'boot') {
|
|
69
|
+
const bootResult = await bootLocalApp(state.discovery.boot_strategy);
|
|
70
|
+
if (!bootResult.success) {
|
|
71
|
+
vulnerabilities.push({
|
|
72
|
+
id: 'OMEN-OPERATIONAL-BOOT-FAIL',
|
|
73
|
+
kind: 'operational',
|
|
74
|
+
category: 'confirmed',
|
|
75
|
+
severity: 'high',
|
|
76
|
+
confidence: 'high',
|
|
77
|
+
title: 'App Boot Failure',
|
|
78
|
+
description: `The application failed to start using ${state.discovery.boot_strategy}.`,
|
|
79
|
+
evidence: { logs: bootResult.logs, error: bootResult.error },
|
|
80
|
+
remediation: 'Check application logs and environment variables.'
|
|
81
|
+
});
|
|
82
|
+
} else {
|
|
83
|
+
// Run dynamic analysis if boot succeeded
|
|
84
|
+
const dynamicResult = await scanRemoteTarget(`http://localhost:${bootResult.port}`);
|
|
85
|
+
vulnerabilities.push(...dynamicResult.vulnerabilities);
|
|
86
|
+
if (bootResult.subprocess) bootResult.subprocess.kill(); // Kill after scan
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Update state with execution results
|
|
92
|
+
const execution = {
|
|
93
|
+
status: 'completed',
|
|
94
|
+
vulnerabilities,
|
|
95
|
+
logs: executionLogs,
|
|
96
|
+
timestamp: new Date().toISOString()
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Calculate score
|
|
100
|
+
const baseScore = 100;
|
|
101
|
+
// Penalty based on severity
|
|
102
|
+
const penalties = { critical: 20, high: 10, medium: 5, low: 2, info: 0 };
|
|
103
|
+
const totalPenalty = vulnerabilities.reduce((acc, v) => acc + (penalties[v.severity] || 0), 0);
|
|
104
|
+
const score = Math.max(0, baseScore - totalPenalty);
|
|
105
|
+
|
|
106
|
+
const resultData = {
|
|
107
|
+
execution,
|
|
108
|
+
score,
|
|
109
|
+
vulnerabilities,
|
|
110
|
+
riskLevel: score < 40 ? 'Critical' : score < 70 ? 'High' : score < 90 ? 'Medium' : 'Low',
|
|
111
|
+
timestamp: new Date().toISOString(),
|
|
112
|
+
target: state.discovery?.path || process.cwd(),
|
|
113
|
+
scan_id: `OMEN-${Date.now()}`
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
await saveState(resultData);
|
|
117
|
+
console.log(chalk.green('\n[✔] Execution finished. Found', vulnerabilities.length, 'issues.'));
|
|
118
|
+
console.log(chalk.bold(`Security Score: ${score}/100 (${resultData.riskLevel})`));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function report() {
|
|
122
|
+
console.log(chalk.bold.cyan('\n--- Phase 4: Reporting ---'));
|
|
123
|
+
const state = await loadState();
|
|
124
|
+
await generateOutputs(state);
|
|
125
|
+
console.log(chalk.green('[✔] Reports generated in /omen-reports/'));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function verify() {
|
|
129
|
+
console.log(chalk.bold.cyan('\n--- Phase 5: Verification ---'));
|
|
130
|
+
const oldState = await loadState();
|
|
131
|
+
|
|
132
|
+
// Re-run execution to get fresh results
|
|
133
|
+
await execute();
|
|
134
|
+
|
|
135
|
+
const diff = await compareWithPreviousScan();
|
|
136
|
+
|
|
137
|
+
if (!diff) {
|
|
138
|
+
console.log(chalk.yellow('Not enough history to perform a diff verification.'));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(chalk.bold('\nVerification Results:'));
|
|
143
|
+
|
|
144
|
+
if (diff.fixedVulnerabilities.length > 0) {
|
|
145
|
+
console.log(chalk.green(`[✔] ${diff.fixedVulnerabilities.length} Issues RESOLVED:`));
|
|
146
|
+
diff.fixedVulnerabilities.forEach(v => console.log(` - ${v.title || v.description}`));
|
|
147
|
+
} else {
|
|
148
|
+
console.log(chalk.red('[!] No previously found issues were resolved.'));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (diff.newVulnerabilities.length > 0) {
|
|
152
|
+
console.log(chalk.yellow(`[+] ${diff.newVulnerabilities.length} NEW Issues found:`));
|
|
153
|
+
diff.newVulnerabilities.forEach(v => console.log(` - ${v.title || v.description}`));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (diff.scoreDiff !== 0) {
|
|
157
|
+
const color = diff.scoreDiff > 0 ? chalk.green : chalk.red;
|
|
158
|
+
const sign = diff.scoreDiff > 0 ? '+' : '';
|
|
159
|
+
console.log(color(`\nSecurity Score Change: ${sign}${diff.scoreDiff} points`));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await saveState({
|
|
163
|
+
verification: {
|
|
164
|
+
timestamp: new Date().toISOString(),
|
|
165
|
+
resolved_count: diff.fixedVulnerabilities.length,
|
|
166
|
+
new_count: diff.newVulnerabilities.length,
|
|
167
|
+
score_change: diff.scoreDiff
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
package/core/generator.js
CHANGED
|
@@ -2,33 +2,66 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { getMassiveAIProtocol } from './ai-protocol.js';
|
|
5
|
+
import { generateMarkdownReport } from './reporters/markdown-reporter.js';
|
|
6
|
+
import { generateFixPlan } from './reporters/fix-plan-reporter.js';
|
|
5
7
|
|
|
6
8
|
export async function generateOutputs(scanData) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const historyDir = path.join(cwd, '.omen', 'history');
|
|
9
|
+
const outputDir = path.join(process.cwd(), 'omen-reports');
|
|
10
|
+
const historyDir = path.join(process.cwd(), '.omen', 'history');
|
|
10
11
|
|
|
11
12
|
// Criar as pastas se não existirem
|
|
12
13
|
try {
|
|
13
14
|
await fs.mkdir(outputDir, { recursive: true });
|
|
14
15
|
await fs.mkdir(historyDir, { recursive: true });
|
|
15
|
-
} catch (err) {
|
|
16
|
-
console.error(chalk.red(`Failed to create output directories: ${err.message}`));
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
16
|
+
} catch (err) {}
|
|
19
17
|
|
|
20
18
|
const jsonContent = JSON.stringify(scanData, null, 2);
|
|
21
|
-
|
|
19
|
+
|
|
22
20
|
// JSON Report
|
|
23
21
|
const jsonReportPath = path.join(outputDir, 'omen-report.json');
|
|
24
22
|
await fs.writeFile(jsonReportPath, jsonContent);
|
|
23
|
+
console.log(chalk.cyan(`\n[Generator] Outputs saved in:`));
|
|
25
24
|
console.log(` /omen-reports/omen-report.json`);
|
|
26
25
|
|
|
27
26
|
// TXT Report
|
|
28
27
|
const txtReportPath = path.join(outputDir, 'omen-report.txt');
|
|
29
|
-
|
|
28
|
+
let txtContent = `OMEN SECURITY REPORT\n`;
|
|
29
|
+
txtContent += `====================\n`;
|
|
30
|
+
txtContent += `Target: ${scanData.target || 'Local Project'}\n`;
|
|
31
|
+
txtContent += `Scan ID: ${scanData.scan_id || 'N/A'}\n`;
|
|
32
|
+
txtContent += `Date: ${scanData.timestamp || new Date().toISOString()}\n`;
|
|
33
|
+
txtContent += `Score: ${scanData.score || 0}/100\n`;
|
|
34
|
+
txtContent += `Risk Level: ${scanData.riskLevel || 'Unknown'}\n\n`;
|
|
35
|
+
|
|
36
|
+
txtContent += `VULNERABILITIES FOUND:\n`;
|
|
37
|
+
txtContent += `----------------------\n`;
|
|
38
|
+
|
|
39
|
+
const vulnerabilities = scanData.vulnerabilities || [];
|
|
40
|
+
if (vulnerabilities.length === 0) {
|
|
41
|
+
txtContent += `No vulnerabilities detected.\n`;
|
|
42
|
+
} else {
|
|
43
|
+
vulnerabilities.forEach(v => {
|
|
44
|
+
txtContent += `[${v.severity?.toUpperCase() || 'INFO'}] ${v.title || v.description}\n`;
|
|
45
|
+
txtContent += `Category: ${v.category || 'N/A'} | Confidence: ${v.confidence || 'medium'}\n`;
|
|
46
|
+
txtContent += `Description: ${v.description}\n`;
|
|
47
|
+
if (v.remediation) txtContent += `Remediation: ${v.remediation}\n`;
|
|
48
|
+
txtContent += `----------------------\n`;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
30
51
|
await fs.writeFile(txtReportPath, txtContent);
|
|
31
52
|
console.log(` /omen-reports/omen-report.txt`);
|
|
53
|
+
|
|
54
|
+
// Markdown Report
|
|
55
|
+
const mdReportPath = path.join(outputDir, 'omen-report.md');
|
|
56
|
+
const mdContent = generateMarkdownReport(scanData);
|
|
57
|
+
await fs.writeFile(mdReportPath, mdContent);
|
|
58
|
+
console.log(` /omen-reports/omen-report.md`);
|
|
59
|
+
|
|
60
|
+
// Fix Plan
|
|
61
|
+
const fixPlanPath = path.join(outputDir, 'omen-fix-plan.md');
|
|
62
|
+
const fixPlanContent = generateFixPlan(scanData);
|
|
63
|
+
await fs.writeFile(fixPlanPath, fixPlanContent);
|
|
64
|
+
console.log(` /omen-reports/omen-fix-plan.md`);
|
|
32
65
|
|
|
33
66
|
// AI Protocol
|
|
34
67
|
const aiReportPath = path.join(outputDir, 'omen-ai.txt');
|
|
@@ -37,6 +70,5 @@ export async function generateOutputs(scanData) {
|
|
|
37
70
|
|
|
38
71
|
// Save historical report
|
|
39
72
|
const timestamp = new Date().toISOString().replace(/:/g, '-');
|
|
40
|
-
|
|
41
|
-
await fs.writeFile(historyReportPath, jsonContent);
|
|
73
|
+
await fs.writeFile(path.join(historyDir, `report-${timestamp}.json`), jsonContent);
|
|
42
74
|
}
|