omen-sec-cli 1.0.8 → 1.0.10
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 +101 -24
- package/core/local-scanner.js +24 -2
- package/core/remote-scanner.js +90 -52
- package/core/scanner.js +4 -1
- package/core/ui-server.js +7 -1
- package/package.json +1 -1
- package/utils/args.js +3 -0
package/README.md
CHANGED
|
@@ -1,40 +1,117 @@
|
|
|
1
|
-
#
|
|
1
|
+
# <p align="center"> <img src="https://img.icons8.com/nolan/128/security-shield.png" width="100" /> <br> OMEN SEC-CLI </p>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://img.shields.io/badge/Version-1.0.6-red?style=for-the-badge" />
|
|
5
|
+
<img src="https://img.shields.io/badge/AI--Powered-DevSecOps-000000?style=for-the-badge&logo=openai" />
|
|
6
|
+
<img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" />
|
|
7
|
+
</p>
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
---
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
## 💀 O que é o OMEN?
|
|
8
12
|
|
|
13
|
+
O **OMEN SEC-CLI** não é apenas um script de segurança; é um **Framework de Auditoria DevSecOps** projetado para elevar o nível de proteção das suas aplicações. Ele transforma sua IA local (Trae, Cursor, Copilot) em um **Arquiteto de Segurança de Elite**, capaz de auditar, detectar e corrigir vulnerabilidades em tempo real.
|
|
14
|
+
|
|
15
|
+
Diferente de outras ferramentas, o OMEN utiliza o conceito de **Zero-Copy AI Handover**, onde a CLI "sequestra" o contexto da sua IA de desenvolvimento para realizar correções automáticas (Auto-Fix) baseadas em protocolos militares de segurança.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## ⚡ Funcionalidades Principais
|
|
20
|
+
|
|
21
|
+
### 1. 🧠 AI Zero-Copy (Auto-Fix)
|
|
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.
|
|
23
|
+
|
|
24
|
+
### 2. 🌐 Scan Remoto Inteligente (Spider & Fuzzer)
|
|
25
|
+
Não olhamos apenas a superfície. Nosso scanner remoto possui:
|
|
26
|
+
- **Spider/Crawler:** Mapeia todos os links internos para encontrar vulnerabilidades escondidas.
|
|
27
|
+
- **Fuzzer de Caminhos:** Tenta localizar arquivos sensíveis (`.env`, `.git`, `/admin`, `/config`).
|
|
28
|
+
- **Injection Tester:** Testes automatizados de XSS Refletido e SQL Injection.
|
|
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.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🚀 Instalação e Uso
|
|
43
|
+
|
|
44
|
+
Você pode instalar o OMEN globalmente ou rodar direto via `npx`:
|
|
45
|
+
|
|
46
|
+
### Instalação Global
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g omen-sec-cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Scan Remoto
|
|
9
52
|
```bash
|
|
10
|
-
npx omen robotscan https://
|
|
53
|
+
npx omen-sec-cli robotscan https://seu-alvo.com
|
|
11
54
|
```
|
|
12
55
|
|
|
13
|
-
###
|
|
56
|
+
### Scan Local (Projeto Atual)
|
|
57
|
+
```bash
|
|
58
|
+
npx omen-sec-cli robotscan --local
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Modo de Auto-Correção com IA (Recomendado no Trae/Cursor)
|
|
62
|
+
```bash
|
|
63
|
+
npx omen-sec-cli robotscan --local --auto-fix
|
|
64
|
+
```
|
|
14
65
|
|
|
66
|
+
### Abrir Dashboard Web
|
|
15
67
|
```bash
|
|
16
|
-
npx omen
|
|
68
|
+
npx omen-sec-cli ui
|
|
17
69
|
```
|
|
18
70
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- `--full`: Run all modules
|
|
23
|
-
- `--ai`: Force AI output
|
|
24
|
-
- `--export`: Select output format
|
|
25
|
-
- `--silent`: Minimal output
|
|
26
|
-
- `--version`: Show version
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## ⚙️ Configuração de Regras Customizadas
|
|
27
74
|
|
|
28
|
-
|
|
75
|
+
Crie um arquivo `omen-rules.yaml` para estender o poder do OMEN:
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
rules:
|
|
79
|
+
- pattern: "senha_banco"
|
|
80
|
+
type: "Sensitive Keyword"
|
|
81
|
+
severity: "Critical"
|
|
82
|
+
description: "Palavra proibida detectada no código!"
|
|
83
|
+
cwe: "CWE-798"
|
|
84
|
+
- pattern: "localStorage\\.set"
|
|
85
|
+
type: "Insecure Storage"
|
|
86
|
+
severity: "Medium"
|
|
87
|
+
description: "Evite salvar dados sensíveis no localStorage."
|
|
88
|
+
```
|
|
29
89
|
|
|
30
|
-
|
|
31
|
-
- `omen-report.json`: Structured security data.
|
|
32
|
-
- `omen-report.txt`: Human-readable summary of the security audit.
|
|
33
|
-
- `omen-ai.txt`: A pre-formatted AI prompt designed for AI engineers to immediately address the vulnerabilities.
|
|
90
|
+
---
|
|
34
91
|
|
|
35
|
-
##
|
|
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
|
|
102
|
+
|
|
103
|
+
Quando o OMEN detecta uma IA ativa, ele ativa o protocolo **OMEN PRIME**, dividindo a inteligência em:
|
|
104
|
+
1. **O AUDITOR:** Analisa falhas caractere por caractere.
|
|
105
|
+
2. **O ARQUITETO:** Implementa correções prontas para produção.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 🤝 Comunidade e Suporte
|
|
36
110
|
|
|
37
|
-
Support the project:
|
|
38
111
|
- **Donate**: [GitHub Sponsors](https://github.com/sponsors/omen)
|
|
39
|
-
- **
|
|
40
|
-
- **
|
|
112
|
+
- **Discord**: [Join our Community](https://discord.gg/omen-security)
|
|
113
|
+
- **GitHub**: [Report a Bug](https://github.com/omen)
|
|
114
|
+
|
|
115
|
+
<p align="center">
|
|
116
|
+
<i>Desenvolvido para desenvolvedores que levam a segurança a sério.</i>
|
|
117
|
+
</p>
|
package/core/local-scanner.js
CHANGED
|
@@ -121,12 +121,34 @@ export async function scanLocalProject() {
|
|
|
121
121
|
vulnerabilities.push({
|
|
122
122
|
id: `LOC-VULN-${Date.now()}-5`,
|
|
123
123
|
type: 'SQL Injection',
|
|
124
|
-
severity: '
|
|
125
|
-
description: `
|
|
124
|
+
severity: 'Critical',
|
|
125
|
+
description: `Critical SQL Injection vulnerability (raw string concatenation) in ${path.basename(file)} at line ${index + 1}. Attacker can bypass authentication or dump the entire database.`,
|
|
126
126
|
cwe: 'CWE-89'
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// Regra 6: Local File Inclusion (LFI) via require/import dinâmico
|
|
131
|
+
if (/(require|import)\s*\(['"]?.*(\+|`|\${)/i.test(line)) {
|
|
132
|
+
vulnerabilities.push({
|
|
133
|
+
id: `LOC-VULN-${Date.now()}-6`,
|
|
134
|
+
type: 'Local File Inclusion',
|
|
135
|
+
severity: 'High',
|
|
136
|
+
description: `Potential LFI detected in ${path.basename(file)} at line ${index + 1}. Dynamic loading of files based on user input can lead to sensitive data exposure.`,
|
|
137
|
+
cwe: 'CWE-22'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Regra 7: Insecure Prototype Extension
|
|
142
|
+
if (/\.prototype\.[a-zA-Z0-9_]+\s*=\s*/.test(line)) {
|
|
143
|
+
vulnerabilities.push({
|
|
144
|
+
id: `LOC-VULN-${Date.now()}-7`,
|
|
145
|
+
type: 'Prototype Pollution Risk',
|
|
146
|
+
severity: 'Medium',
|
|
147
|
+
description: `Direct prototype modification in ${path.basename(file)} at line ${index + 1}. This can lead to Prototype Pollution if user input reaches this assignment.`,
|
|
148
|
+
cwe: 'CWE-1321'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
130
152
|
// --- Custom Rules Integration ---
|
|
131
153
|
customRules.forEach((rule, ruleIndex) => {
|
|
132
154
|
try {
|
package/core/remote-scanner.js
CHANGED
|
@@ -6,44 +6,56 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
6
6
|
const headers_analysis = {};
|
|
7
7
|
let serverStatus = 'Unknown';
|
|
8
8
|
let discoveredLinks = new Set();
|
|
9
|
+
let discoveredForms = [];
|
|
10
|
+
let discoveredParams = new Set();
|
|
11
|
+
let techStack = [];
|
|
9
12
|
|
|
10
13
|
try {
|
|
11
14
|
// 1. Initial GET to analyze headers and page content
|
|
12
15
|
const response = await axios.get(targetUrl, {
|
|
13
|
-
timeout:
|
|
14
|
-
validateStatus: () => true
|
|
16
|
+
timeout: 15000,
|
|
17
|
+
validateStatus: () => true,
|
|
18
|
+
headers: { 'User-Agent': 'OMEN-SEC-CLI/1.0.6 (Security Audit)' }
|
|
15
19
|
});
|
|
16
20
|
|
|
17
21
|
serverStatus = response.status;
|
|
18
22
|
const headers = response.headers;
|
|
19
23
|
const html = response.data;
|
|
20
24
|
|
|
21
|
-
// ---
|
|
22
|
-
|
|
25
|
+
// --- TECHNOLOGY FINGERPRINTING ---
|
|
26
|
+
if (headers['x-powered-by']) techStack.push(headers['x-powered-by']);
|
|
27
|
+
if (headers['server']) techStack.push(headers['server']);
|
|
28
|
+
if (html.includes('next-head')) techStack.push('Next.js');
|
|
29
|
+
if (html.includes('_next/static')) techStack.push('Next.js');
|
|
30
|
+
if (html.includes('react-root') || html.includes('data-react')) techStack.push('React');
|
|
31
|
+
if (html.includes('wp-content')) techStack.push('WordPress');
|
|
32
|
+
if (html.includes('nuxt')) techStack.push('Nuxt.js');
|
|
33
|
+
|
|
34
|
+
// --- Header Analysis (Existing - Refined Descriptions) ---
|
|
23
35
|
if (!headers['strict-transport-security']) {
|
|
24
36
|
headers_analysis["Strict-Transport-Security"] = "Missing";
|
|
25
37
|
vulnerabilities.push({
|
|
26
38
|
id: `REM-VULN-${Date.now()}-1`,
|
|
27
39
|
type: 'Security Misconfiguration',
|
|
28
40
|
severity: 'Medium',
|
|
29
|
-
description: `
|
|
41
|
+
description: `HSTS Header is missing. This lacks forced HTTPS enforcement for browsers that have already visited the site.`,
|
|
30
42
|
cwe: 'CWE-319'
|
|
31
43
|
});
|
|
32
44
|
} else {
|
|
33
45
|
headers_analysis["Strict-Transport-Security"] = headers['strict-transport-security'];
|
|
34
46
|
}
|
|
35
47
|
|
|
36
|
-
// 2. Analisar Content-Security-Policy (CSP)
|
|
37
48
|
if (!headers['content-security-policy']) {
|
|
38
49
|
headers_analysis["Content-Security-Policy"] = "Missing";
|
|
39
50
|
vulnerabilities.push({
|
|
40
51
|
id: `REM-VULN-${Date.now()}-2`,
|
|
41
52
|
type: 'Security Misconfiguration',
|
|
42
|
-
severity: '
|
|
43
|
-
description: `
|
|
44
|
-
cwe: 'CWE-
|
|
53
|
+
severity: 'High',
|
|
54
|
+
description: `CSP header is missing. Without a strict Content-Security-Policy, the application is highly vulnerable to Cross-Site Scripting (XSS) and data injection attacks.`,
|
|
55
|
+
cwe: 'CWE-1022'
|
|
45
56
|
});
|
|
46
|
-
}
|
|
57
|
+
}
|
|
58
|
+
// ... (rest of header analysis) else {
|
|
47
59
|
headers_analysis["Content-Security-Policy"] = headers['content-security-policy'];
|
|
48
60
|
if (headers['content-security-policy'].includes("unsafe-inline")) {
|
|
49
61
|
vulnerabilities.push({
|
|
@@ -95,9 +107,11 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
95
107
|
});
|
|
96
108
|
}
|
|
97
109
|
|
|
98
|
-
// --- SPIDER / CRAWLER ---
|
|
110
|
+
// --- SPIDER / CRAWLER (DEEP) ---
|
|
99
111
|
if (typeof html === 'string') {
|
|
100
112
|
const $ = cheerio.load(html);
|
|
113
|
+
|
|
114
|
+
// Discover Links & Params
|
|
101
115
|
$('a').each((i, link) => {
|
|
102
116
|
const href = $(link).attr('href');
|
|
103
117
|
if (href && !href.startsWith('#') && !href.startsWith('mailto:')) {
|
|
@@ -105,85 +119,105 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
105
119
|
const absoluteUrl = new URL(href, targetUrl).href;
|
|
106
120
|
if (absoluteUrl.startsWith(targetUrl)) {
|
|
107
121
|
discoveredLinks.add(absoluteUrl);
|
|
122
|
+
|
|
123
|
+
// Extract query parameters
|
|
124
|
+
const urlObj = new URL(absoluteUrl);
|
|
125
|
+
urlObj.searchParams.forEach((value, name) => discoveredParams.add(name));
|
|
108
126
|
}
|
|
109
|
-
} catch (e) {
|
|
110
|
-
// Invalid URL
|
|
111
|
-
}
|
|
127
|
+
} catch (e) {}
|
|
112
128
|
}
|
|
113
129
|
});
|
|
130
|
+
|
|
131
|
+
// Discover Forms
|
|
132
|
+
$('form').each((i, form) => {
|
|
133
|
+
const action = $(form).attr('action') || '';
|
|
134
|
+
const method = ($(form).attr('method') || 'GET').toUpperCase();
|
|
135
|
+
const inputs = [];
|
|
136
|
+
$(form).find('input, textarea, select').each((j, input) => {
|
|
137
|
+
const name = $(input).attr('name');
|
|
138
|
+
if (name) inputs.push(name);
|
|
139
|
+
});
|
|
140
|
+
discoveredForms.push({ action, method, inputs });
|
|
141
|
+
});
|
|
114
142
|
}
|
|
115
143
|
|
|
116
|
-
// --- FUZZER (Path Discovery) ---
|
|
117
|
-
const
|
|
118
|
-
'/.env',
|
|
119
|
-
'
|
|
120
|
-
'/
|
|
121
|
-
'/
|
|
122
|
-
'/config.php',
|
|
123
|
-
'/.vscode/settings.json',
|
|
124
|
-
'/phpinfo.php',
|
|
125
|
-
'/api/v1/users',
|
|
126
|
-
'/robots.txt'
|
|
144
|
+
// --- FUZZER (Path Discovery - Aggressive) ---
|
|
145
|
+
const aggressivePaths = [
|
|
146
|
+
'/.env', '/.git/config', '/admin', '/wp-admin', '/config.php', '/.vscode/settings.json',
|
|
147
|
+
'/phpinfo.php', '/api/v1/users', '/robots.txt', '/.env.local', '/.env.production',
|
|
148
|
+
'/server-status', '/_next/static/chunks/pages/index.js', '/package.json',
|
|
149
|
+
'/api/auth/session', '/api/graphql', '/actuator/health', '/.ssh/id_rsa'
|
|
127
150
|
];
|
|
128
151
|
|
|
129
|
-
for (const path of
|
|
152
|
+
for (const path of aggressivePaths) {
|
|
130
153
|
try {
|
|
131
154
|
const fuzzUrl = new URL(path, targetUrl).href;
|
|
132
155
|
const fuzzRes = await axios.get(fuzzUrl, {
|
|
133
156
|
timeout: 5000,
|
|
134
|
-
validateStatus: (status) => status === 200
|
|
157
|
+
validateStatus: (status) => status === 200 || status === 403
|
|
135
158
|
});
|
|
136
159
|
|
|
137
160
|
if (fuzzRes.status === 200) {
|
|
138
161
|
vulnerabilities.push({
|
|
139
162
|
id: `REM-FUZZ-${Date.now()}-${path.replace(/\//g, '-')}`,
|
|
140
163
|
type: 'Sensitive Path Exposed',
|
|
141
|
-
severity: path.includes('.env') || path.includes('.git') ? 'Critical' : '
|
|
142
|
-
description: `Exposed sensitive path discovered
|
|
164
|
+
severity: path.includes('.env') || path.includes('.git') || path.includes('.ssh') ? 'Critical' : 'High',
|
|
165
|
+
description: `Exposed sensitive path discovered: ${fuzzUrl}. This path reveals internal configurations or credentials.`,
|
|
143
166
|
cwe: 'CWE-200'
|
|
144
167
|
});
|
|
168
|
+
} else if (fuzzRes.status === 403) {
|
|
169
|
+
vulnerabilities.push({
|
|
170
|
+
id: `REM-FUZZ-${Date.now()}-${path.replace(/\//g, '-')}`,
|
|
171
|
+
type: 'Potential Sensitive Path',
|
|
172
|
+
severity: 'Low',
|
|
173
|
+
description: `Path discovered but access forbidden (403): ${fuzzUrl}. Might indicate internal structure exposure.`,
|
|
174
|
+
cwe: 'CWE-204'
|
|
175
|
+
});
|
|
145
176
|
}
|
|
146
|
-
} catch (e) {
|
|
147
|
-
// Path not found or error, skip
|
|
148
|
-
}
|
|
177
|
+
} catch (e) {}
|
|
149
178
|
}
|
|
150
179
|
|
|
151
|
-
// ---
|
|
180
|
+
// --- OFFENSIVE PARAMETER FUZZING ---
|
|
152
181
|
const injectionPayloads = [
|
|
153
|
-
{ type: 'SQLi', param: "
|
|
154
|
-
{ type: 'XSS', param: "
|
|
182
|
+
{ type: 'SQLi', param: "' OR 1=1--", severity: 'Critical' },
|
|
183
|
+
{ type: 'XSS', param: "<script>alert('OMEN')</script>", severity: 'High' },
|
|
184
|
+
{ type: 'LFI', param: "/etc/passwd", severity: 'Critical' }
|
|
155
185
|
];
|
|
156
186
|
|
|
157
|
-
|
|
158
|
-
|
|
187
|
+
// Combine params from links and forms
|
|
188
|
+
const allParams = Array.from(discoveredParams);
|
|
189
|
+
discoveredForms.forEach(f => f.inputs.forEach(i => allParams.push(i)));
|
|
190
|
+
|
|
191
|
+
const uniqueParams = [...new Set(allParams)].slice(0, 5); // Fuzz top 5 params
|
|
192
|
+
|
|
193
|
+
for (const param of uniqueParams) {
|
|
159
194
|
for (const payload of injectionPayloads) {
|
|
160
195
|
try {
|
|
161
|
-
const testUrl =
|
|
162
|
-
|
|
196
|
+
const testUrl = new URL(targetUrl);
|
|
197
|
+
testUrl.searchParams.append(param, payload.param);
|
|
198
|
+
|
|
199
|
+
const res = await axios.get(testUrl.href, { timeout: 5000, validateStatus: () => true });
|
|
163
200
|
|
|
164
|
-
|
|
165
|
-
if (payload.type === 'XSS' && typeof res.data === 'string' && res.data.includes("<script>alert('OMEN')</script>")) {
|
|
201
|
+
if (payload.type === 'XSS' && typeof res.data === 'string' && res.data.includes(payload.param)) {
|
|
166
202
|
vulnerabilities.push({
|
|
167
203
|
id: `REM-INJ-${Date.now()}-XSS`,
|
|
168
204
|
type: 'Reflected XSS',
|
|
169
|
-
severity:
|
|
170
|
-
description: `
|
|
205
|
+
severity: 'High',
|
|
206
|
+
description: `Confirmed XSS at ${targetUrl} via parameter '${param}'. Payload was reflected in HTML.`,
|
|
171
207
|
cwe: 'CWE-79'
|
|
172
208
|
});
|
|
173
209
|
}
|
|
174
|
-
|
|
175
|
-
if (payload.type === 'SQLi' && res.status === 500) {
|
|
210
|
+
|
|
211
|
+
if (payload.type === 'SQLi' && (res.status === 500 || (typeof res.data === 'string' && /SQL|database|syntax/i.test(res.data)))) {
|
|
176
212
|
vulnerabilities.push({
|
|
177
213
|
id: `REM-INJ-${Date.now()}-SQLI`,
|
|
178
|
-
type: '
|
|
179
|
-
severity:
|
|
180
|
-
description: `Potential SQLi detected
|
|
214
|
+
type: 'SQL Injection',
|
|
215
|
+
severity: 'Critical',
|
|
216
|
+
description: `Potential SQLi detected via parameter '${param}'. Server showed error patterns on payload.`,
|
|
181
217
|
cwe: 'CWE-89'
|
|
182
218
|
});
|
|
183
219
|
}
|
|
184
|
-
} catch (e) {
|
|
185
|
-
// Skip
|
|
186
|
-
}
|
|
220
|
+
} catch (e) {}
|
|
187
221
|
}
|
|
188
222
|
}
|
|
189
223
|
|
|
@@ -192,7 +226,7 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
192
226
|
id: `REM-ERR-${Date.now()}`,
|
|
193
227
|
type: 'Availability',
|
|
194
228
|
severity: 'Info',
|
|
195
|
-
description: `
|
|
229
|
+
description: `Target ${targetUrl} connection error: ${err.message}`,
|
|
196
230
|
cwe: 'N/A'
|
|
197
231
|
});
|
|
198
232
|
}
|
|
@@ -200,6 +234,10 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
200
234
|
return {
|
|
201
235
|
serverStatus,
|
|
202
236
|
headers_analysis,
|
|
237
|
+
techStack,
|
|
238
|
+
discoveredLinks: Array.from(discoveredLinks),
|
|
239
|
+
discoveredForms,
|
|
240
|
+
discoveredParams: Array.from(discoveredParams),
|
|
203
241
|
vulnerabilities
|
|
204
242
|
};
|
|
205
243
|
}
|
package/core/scanner.js
CHANGED
|
@@ -32,7 +32,10 @@ export async function runScannerSteps(target, flags) {
|
|
|
32
32
|
const remoteData = await scanRemoteTarget(target);
|
|
33
33
|
headers_analysis = remoteData.headers_analysis;
|
|
34
34
|
allVulnerabilities.push(...remoteData.vulnerabilities);
|
|
35
|
-
attack_surface.endpoints_discovered
|
|
35
|
+
attack_surface.endpoints_discovered = remoteData.discoveredLinks.length;
|
|
36
|
+
attack_surface.parameters_extracted = remoteData.discoveredParams.length;
|
|
37
|
+
attack_surface.forms_detected = remoteData.discoveredForms.length;
|
|
38
|
+
attack_surface.tech_stack = remoteData.techStack;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
if (step.text === 'Scanning endpoints...' && flags.local) {
|
package/core/ui-server.js
CHANGED
|
@@ -40,7 +40,7 @@ export async function startUIServer() {
|
|
|
40
40
|
</div>
|
|
41
41
|
</header>
|
|
42
42
|
|
|
43
|
-
<div class="grid grid-cols-1 md:grid-cols-
|
|
43
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
|
|
44
44
|
<div class="card p-6 rounded-lg shadow-xl">
|
|
45
45
|
<h3 class="text-gray-400 mb-2">Security Score</h3>
|
|
46
46
|
<p class="text-5xl font-bold ${report.score < 50 ? 'critical' : report.score < 75 ? 'high' : 'low'}">${report.score}/100</p>
|
|
@@ -53,6 +53,12 @@ export async function startUIServer() {
|
|
|
53
53
|
<h3 class="text-gray-400 mb-2">Vulnerabilities</h3>
|
|
54
54
|
<p class="text-5xl font-bold text-white">${report.vulnerabilities.length}</p>
|
|
55
55
|
</div>
|
|
56
|
+
<div class="card p-6 rounded-lg shadow-xl">
|
|
57
|
+
<h3 class="text-gray-400 mb-2">Attack Surface</h3>
|
|
58
|
+
<p class="text-sm text-gray-400">Endpoints: ${report.attack_surface.endpoints_discovered}</p>
|
|
59
|
+
<p class="text-sm text-gray-400">Forms: ${report.attack_surface.forms_detected}</p>
|
|
60
|
+
<p class="text-sm text-gray-400">Tech: ${(report.attack_surface.tech_stack || []).join(', ')}</p>
|
|
61
|
+
</div>
|
|
56
62
|
</div>
|
|
57
63
|
|
|
58
64
|
<div class="card p-6 rounded-lg shadow-xl mb-8">
|
package/package.json
CHANGED
package/utils/args.js
CHANGED
|
@@ -25,6 +25,8 @@ export function parseArgs(argv) {
|
|
|
25
25
|
if (args[1] && !args[1].startsWith('--')) {
|
|
26
26
|
result.target = args[1];
|
|
27
27
|
}
|
|
28
|
+
} else if (args[0] === 'ui') {
|
|
29
|
+
result.command = 'ui';
|
|
28
30
|
} else if (args[0] === '--help' || args[0] === '-h') {
|
|
29
31
|
result.flags.help = true;
|
|
30
32
|
} else if (args[0] === '--version' || args[0] === '-v') {
|
|
@@ -36,6 +38,7 @@ export function parseArgs(argv) {
|
|
|
36
38
|
if (arg === '--local') result.flags.local = true;
|
|
37
39
|
if (arg === '--full') result.flags.full = true;
|
|
38
40
|
if (arg === '--ai') result.flags.ai = true;
|
|
41
|
+
if (arg === '--auto-fix') result.flags['auto-fix'] = true;
|
|
39
42
|
if (arg === '--export') result.flags.export = true;
|
|
40
43
|
if (arg === '--silent') result.flags.silent = true;
|
|
41
44
|
if (arg === '--help') result.flags.help = true;
|