omen-sec-cli 1.0.8 → 1.0.12
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 +88 -51
- 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,42 +6,53 @@ 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
|
} else {
|
|
47
58
|
headers_analysis["Content-Security-Policy"] = headers['content-security-policy'];
|
|
@@ -95,9 +106,11 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
95
106
|
});
|
|
96
107
|
}
|
|
97
108
|
|
|
98
|
-
// --- SPIDER / CRAWLER ---
|
|
109
|
+
// --- SPIDER / CRAWLER (DEEP) ---
|
|
99
110
|
if (typeof html === 'string') {
|
|
100
111
|
const $ = cheerio.load(html);
|
|
112
|
+
|
|
113
|
+
// Discover Links & Params
|
|
101
114
|
$('a').each((i, link) => {
|
|
102
115
|
const href = $(link).attr('href');
|
|
103
116
|
if (href && !href.startsWith('#') && !href.startsWith('mailto:')) {
|
|
@@ -105,85 +118,105 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
105
118
|
const absoluteUrl = new URL(href, targetUrl).href;
|
|
106
119
|
if (absoluteUrl.startsWith(targetUrl)) {
|
|
107
120
|
discoveredLinks.add(absoluteUrl);
|
|
121
|
+
|
|
122
|
+
// Extract query parameters
|
|
123
|
+
const urlObj = new URL(absoluteUrl);
|
|
124
|
+
urlObj.searchParams.forEach((value, name) => discoveredParams.add(name));
|
|
108
125
|
}
|
|
109
|
-
} catch (e) {
|
|
110
|
-
// Invalid URL
|
|
111
|
-
}
|
|
126
|
+
} catch (e) {}
|
|
112
127
|
}
|
|
113
128
|
});
|
|
129
|
+
|
|
130
|
+
// Discover Forms
|
|
131
|
+
$('form').each((i, form) => {
|
|
132
|
+
const action = $(form).attr('action') || '';
|
|
133
|
+
const method = ($(form).attr('method') || 'GET').toUpperCase();
|
|
134
|
+
const inputs = [];
|
|
135
|
+
$(form).find('input, textarea, select').each((j, input) => {
|
|
136
|
+
const name = $(input).attr('name');
|
|
137
|
+
if (name) inputs.push(name);
|
|
138
|
+
});
|
|
139
|
+
discoveredForms.push({ action, method, inputs });
|
|
140
|
+
});
|
|
114
141
|
}
|
|
115
142
|
|
|
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'
|
|
143
|
+
// --- FUZZER (Path Discovery - Aggressive) ---
|
|
144
|
+
const aggressivePaths = [
|
|
145
|
+
'/.env', '/.git/config', '/admin', '/wp-admin', '/config.php', '/.vscode/settings.json',
|
|
146
|
+
'/phpinfo.php', '/api/v1/users', '/robots.txt', '/.env.local', '/.env.production',
|
|
147
|
+
'/server-status', '/_next/static/chunks/pages/index.js', '/package.json',
|
|
148
|
+
'/api/auth/session', '/api/graphql', '/actuator/health', '/.ssh/id_rsa'
|
|
127
149
|
];
|
|
128
150
|
|
|
129
|
-
for (const path of
|
|
151
|
+
for (const path of aggressivePaths) {
|
|
130
152
|
try {
|
|
131
153
|
const fuzzUrl = new URL(path, targetUrl).href;
|
|
132
154
|
const fuzzRes = await axios.get(fuzzUrl, {
|
|
133
155
|
timeout: 5000,
|
|
134
|
-
validateStatus: (status) => status === 200
|
|
156
|
+
validateStatus: (status) => status === 200 || status === 403
|
|
135
157
|
});
|
|
136
158
|
|
|
137
159
|
if (fuzzRes.status === 200) {
|
|
138
160
|
vulnerabilities.push({
|
|
139
161
|
id: `REM-FUZZ-${Date.now()}-${path.replace(/\//g, '-')}`,
|
|
140
162
|
type: 'Sensitive Path Exposed',
|
|
141
|
-
severity: path.includes('.env') || path.includes('.git') ? 'Critical' : '
|
|
142
|
-
description: `Exposed sensitive path discovered
|
|
163
|
+
severity: path.includes('.env') || path.includes('.git') || path.includes('.ssh') ? 'Critical' : 'High',
|
|
164
|
+
description: `Exposed sensitive path discovered: ${fuzzUrl}. This path reveals internal configurations or credentials.`,
|
|
143
165
|
cwe: 'CWE-200'
|
|
144
166
|
});
|
|
167
|
+
} else if (fuzzRes.status === 403) {
|
|
168
|
+
vulnerabilities.push({
|
|
169
|
+
id: `REM-FUZZ-${Date.now()}-${path.replace(/\//g, '-')}`,
|
|
170
|
+
type: 'Potential Sensitive Path',
|
|
171
|
+
severity: 'Low',
|
|
172
|
+
description: `Path discovered but access forbidden (403): ${fuzzUrl}. Might indicate internal structure exposure.`,
|
|
173
|
+
cwe: 'CWE-204'
|
|
174
|
+
});
|
|
145
175
|
}
|
|
146
|
-
} catch (e) {
|
|
147
|
-
// Path not found or error, skip
|
|
148
|
-
}
|
|
176
|
+
} catch (e) {}
|
|
149
177
|
}
|
|
150
178
|
|
|
151
|
-
// ---
|
|
179
|
+
// --- OFFENSIVE PARAMETER FUZZING ---
|
|
152
180
|
const injectionPayloads = [
|
|
153
|
-
{ type: 'SQLi', param: "
|
|
154
|
-
{ type: 'XSS', param: "
|
|
181
|
+
{ type: 'SQLi', param: "' OR 1=1--", severity: 'Critical' },
|
|
182
|
+
{ type: 'XSS', param: "<script>alert('OMEN')</script>", severity: 'High' },
|
|
183
|
+
{ type: 'LFI', param: "/etc/passwd", severity: 'Critical' }
|
|
155
184
|
];
|
|
156
185
|
|
|
157
|
-
|
|
158
|
-
|
|
186
|
+
// Combine params from links and forms
|
|
187
|
+
const allParams = Array.from(discoveredParams);
|
|
188
|
+
discoveredForms.forEach(f => f.inputs.forEach(i => allParams.push(i)));
|
|
189
|
+
|
|
190
|
+
const uniqueParams = [...new Set(allParams)].slice(0, 5); // Fuzz top 5 params
|
|
191
|
+
|
|
192
|
+
for (const param of uniqueParams) {
|
|
159
193
|
for (const payload of injectionPayloads) {
|
|
160
194
|
try {
|
|
161
|
-
const testUrl =
|
|
162
|
-
|
|
195
|
+
const testUrl = new URL(targetUrl);
|
|
196
|
+
testUrl.searchParams.append(param, payload.param);
|
|
163
197
|
|
|
164
|
-
|
|
165
|
-
|
|
198
|
+
const res = await axios.get(testUrl.href, { timeout: 5000, validateStatus: () => true });
|
|
199
|
+
|
|
200
|
+
if (payload.type === 'XSS' && typeof res.data === 'string' && res.data.includes(payload.param)) {
|
|
166
201
|
vulnerabilities.push({
|
|
167
202
|
id: `REM-INJ-${Date.now()}-XSS`,
|
|
168
203
|
type: 'Reflected XSS',
|
|
169
|
-
severity:
|
|
170
|
-
description: `
|
|
204
|
+
severity: 'High',
|
|
205
|
+
description: `Confirmed XSS at ${targetUrl} via parameter '${param}'. Payload was reflected in HTML.`,
|
|
171
206
|
cwe: 'CWE-79'
|
|
172
207
|
});
|
|
173
208
|
}
|
|
174
|
-
|
|
175
|
-
if (payload.type === 'SQLi' && res.status === 500) {
|
|
209
|
+
|
|
210
|
+
if (payload.type === 'SQLi' && (res.status === 500 || (typeof res.data === 'string' && /SQL|database|syntax/i.test(res.data)))) {
|
|
176
211
|
vulnerabilities.push({
|
|
177
212
|
id: `REM-INJ-${Date.now()}-SQLI`,
|
|
178
|
-
type: '
|
|
179
|
-
severity:
|
|
180
|
-
description: `Potential SQLi detected
|
|
213
|
+
type: 'SQL Injection',
|
|
214
|
+
severity: 'Critical',
|
|
215
|
+
description: `Potential SQLi detected via parameter '${param}'. Server showed error patterns on payload.`,
|
|
181
216
|
cwe: 'CWE-89'
|
|
182
217
|
});
|
|
183
218
|
}
|
|
184
|
-
} catch (e) {
|
|
185
|
-
// Skip
|
|
186
|
-
}
|
|
219
|
+
} catch (e) {}
|
|
187
220
|
}
|
|
188
221
|
}
|
|
189
222
|
|
|
@@ -192,7 +225,7 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
192
225
|
id: `REM-ERR-${Date.now()}`,
|
|
193
226
|
type: 'Availability',
|
|
194
227
|
severity: 'Info',
|
|
195
|
-
description: `
|
|
228
|
+
description: `Target ${targetUrl} connection error: ${err.message}`,
|
|
196
229
|
cwe: 'N/A'
|
|
197
230
|
});
|
|
198
231
|
}
|
|
@@ -200,6 +233,10 @@ export async function scanRemoteTarget(targetUrl) {
|
|
|
200
233
|
return {
|
|
201
234
|
serverStatus,
|
|
202
235
|
headers_analysis,
|
|
236
|
+
techStack,
|
|
237
|
+
discoveredLinks: Array.from(discoveredLinks),
|
|
238
|
+
discoveredForms,
|
|
239
|
+
discoveredParams: Array.from(discoveredParams),
|
|
203
240
|
vulnerabilities
|
|
204
241
|
};
|
|
205
242
|
}
|
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;
|