wormclaude 1.0.86 → 1.0.88
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/dist/commands.js +8 -7
- package/dist/pentest.js +58 -106
- package/package.json +1 -1
package/dist/commands.js
CHANGED
|
@@ -123,14 +123,18 @@ const PT_REASON = {
|
|
|
123
123
|
};
|
|
124
124
|
function formatFinding(x) {
|
|
125
125
|
const sev = x.severity ? `[${String(x.severity).toUpperCase()}] ` : '';
|
|
126
|
-
if (x.type === 'vuln')
|
|
127
|
-
return `${sev}${x.name || ''} — ${x.url || ''}`;
|
|
128
126
|
if (x.type === 'xss')
|
|
129
|
-
return `${sev}XSS
|
|
127
|
+
return `${sev}XSS · param ${x.param || '?'} — ${x.url || ''}`;
|
|
130
128
|
if (x.type === 'sqli')
|
|
131
|
-
return `${sev}SQLi ${x.
|
|
129
|
+
return `${sev}SQLi (${x.technique || '?'}) · param ${x.param || '?'} — ${x.evidence || ''}`;
|
|
130
|
+
if (x.type === 'subdomain')
|
|
131
|
+
return `alt-alan: ${x.value}`;
|
|
132
132
|
if (x.type === 'host')
|
|
133
133
|
return `${x.status || ''} ${x.url || ''}${x.title ? ' · ' + x.title : ''}${x.server ? ' · ' + x.server : ''}`;
|
|
134
|
+
if (x.type === 'weak_headers')
|
|
135
|
+
return `${sev}eksik güvenlik başlıkları: ${(x.missing || []).join(', ')}`;
|
|
136
|
+
if (x.type === 'exposure')
|
|
137
|
+
return `${sev}ifşa: ${x.url || ''} — ${x.evidence || ''}`;
|
|
134
138
|
if (x.value)
|
|
135
139
|
return `${x.value}`;
|
|
136
140
|
return JSON.stringify(x);
|
|
@@ -169,9 +173,6 @@ async function pentestCmd(tool, arg, ctx) {
|
|
|
169
173
|
}
|
|
170
174
|
const rep = out.report || {};
|
|
171
175
|
const lines = [`✓ ${label} tamamlandı — ${rep.target || target}`];
|
|
172
|
-
if (out.missing && out.missing.length) {
|
|
173
|
-
lines.push(`⚠ Kurulu olmayan araç(lar) atlandı: ${out.missing.join(', ')}`);
|
|
174
|
-
}
|
|
175
176
|
const f = rep.findings || [];
|
|
176
177
|
if (!f.length) {
|
|
177
178
|
lines.push('Bulgu yok (hedef yanıt vermedi, temiz, ya da araç kurulu değil).');
|
package/dist/pentest.js
CHANGED
|
@@ -1,52 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Bu modül JENERİK bir çalıştırıcıdır: hiç payload/şablon/araç-seçimi içermez.
|
|
4
|
-
// Sunucudan bir komut PLANI alır, komutları YEREL makinede çalıştırır (trafik kullanıcının
|
|
5
|
-
// IP'sinden çıkar), ham çıktıyı sunucuya geri gönderir. Tüm zeka sunucuda kalır.
|
|
6
|
-
//
|
|
7
|
-
// Güvenlik: yalnız PT_ALLOWED'daki ikililer çalıştırılır (ele geçirilmiş/kötü sunucunun
|
|
8
|
-
// istemcide rastgele komut çalıştırmasını engeller). Komutlar SHELL'siz spawn edilir (enjeksiyon yok).
|
|
9
|
-
import { spawn } from 'node:child_process';
|
|
10
|
-
import { statSync } from 'node:fs';
|
|
11
|
-
import * as path from 'node:path';
|
|
12
|
-
// İstemci-tarafı allowlist (sunucudakiyle aynı; çift güvence).
|
|
13
|
-
export const PT_ALLOWED = new Set(['httpx', 'nuclei', 'dalfox', 'sqlmap', 'subfinder', 'katana', 'nmap', 'ffuf']);
|
|
14
|
-
const MAX_OUT = 200_000;
|
|
15
|
-
// İkiliyi PATH üzerinde çöz. Windows'ta Node spawn(shell:false) PATHEXT aramaz →
|
|
16
|
-
// 'dalfox' verilse 'dalfox.exe'yi bulamaz; bu yüzden elle çözüyoruz.
|
|
17
|
-
// Döner: {path, viaCmd} — viaCmd ise .cmd/.bat olduğu için cmd.exe ile sarılır.
|
|
18
|
-
export function resolveBin(bin) {
|
|
19
|
-
const isWin = process.platform === 'win32';
|
|
20
|
-
const exts = isWin ? (process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean) : [''];
|
|
21
|
-
const isFile = (p) => { try {
|
|
22
|
-
return statSync(p).isFile();
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return false;
|
|
26
|
-
} };
|
|
27
|
-
const classify = (p) => ({ path: p, viaCmd: /\.(cmd|bat)$/i.test(p) });
|
|
28
|
-
// Mutlak/parçalı yol verildiyse doğrudan dene.
|
|
29
|
-
if (bin.includes('/') || bin.includes('\\')) {
|
|
30
|
-
if (isFile(bin))
|
|
31
|
-
return classify(bin);
|
|
32
|
-
for (const e of exts)
|
|
33
|
-
if (e && isFile(bin + e))
|
|
34
|
-
return classify(bin + e);
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
const dirs = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
38
|
-
for (const d of dirs) {
|
|
39
|
-
const base = path.join(d, bin);
|
|
40
|
-
if (!isWin && isFile(base))
|
|
41
|
-
return classify(base);
|
|
42
|
-
for (const e of exts) {
|
|
43
|
-
const cand = e ? base + e : base;
|
|
44
|
-
if (isFile(cand))
|
|
45
|
-
return classify(cand);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
1
|
+
const MAX_BODY = 262_144; // 256 KB cevap gövdesi üst sınırı
|
|
50
2
|
async function postJson(config, p, body, timeoutMs) {
|
|
51
3
|
try {
|
|
52
4
|
const r = await fetch(`${config.baseUrl}${p}`, {
|
|
@@ -64,69 +16,69 @@ async function postJson(config, p, body, timeoutMs) {
|
|
|
64
16
|
return { ok: false, reason: e?.name === 'TimeoutError' ? 'timeout' : 'upstream_error' };
|
|
65
17
|
}
|
|
66
18
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
19
|
+
function hostOf(u) {
|
|
20
|
+
try {
|
|
21
|
+
return new URL(u).hostname.toLowerCase();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Tek bir istek-spec'ini yerelde çalıştır. allowed dışı host → atlanır (SSRF koruması).
|
|
28
|
+
async function execReq(req, allowed) {
|
|
29
|
+
const t0 = Date.now();
|
|
30
|
+
const h = hostOf(req.url);
|
|
31
|
+
if (!allowed.has(h)) {
|
|
32
|
+
return { id: req.id, ms: 0, error: `izin-dışı host: ${h}` };
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const r = await fetch(req.url, {
|
|
36
|
+
method: req.method || 'GET',
|
|
37
|
+
headers: req.headers,
|
|
38
|
+
body: req.body,
|
|
39
|
+
redirect: 'follow',
|
|
40
|
+
signal: AbortSignal.timeout(Math.max(3, req.timeout || 20) * 1000),
|
|
41
|
+
});
|
|
42
|
+
let body = '';
|
|
82
43
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
: spawn(found.path, step.args, { shell: false, windowsHide: true });
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
resolve({ id: step.id, ran: false, exit: -1, stdout: '', stderr: `başlatılamadı (kurulu değil?): ${step.bin}` });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const to = setTimeout(() => { try {
|
|
92
|
-
child.kill('SIGKILL');
|
|
44
|
+
const buf = await r.arrayBuffer();
|
|
45
|
+
body = Buffer.from(buf).toString('utf8').slice(0, MAX_BODY);
|
|
93
46
|
}
|
|
94
|
-
catch { /* */ }
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
done = true;
|
|
103
|
-
clearTimeout(to);
|
|
104
|
-
resolve({ id: step.id, ran: false, exit: -1, stdout: out, stderr: `çalıştırılamadı (kurulu değil?): ${step.bin}` });
|
|
105
|
-
});
|
|
106
|
-
child.on('close', (code) => {
|
|
107
|
-
if (done)
|
|
108
|
-
return;
|
|
109
|
-
done = true;
|
|
110
|
-
clearTimeout(to);
|
|
111
|
-
resolve({ id: step.id, ran: true, exit: code ?? -1, stdout: out, stderr: err });
|
|
112
|
-
});
|
|
113
|
-
});
|
|
47
|
+
catch { /* gövde okunamadı */ }
|
|
48
|
+
const headers = {};
|
|
49
|
+
r.headers.forEach((v, k) => { headers[k] = v; });
|
|
50
|
+
return { id: req.id, status: r.status, headers, body, ms: Date.now() - t0, url: r.url };
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return { id: req.id, ms: Date.now() - t0, error: e?.name === 'TimeoutError' ? 'timeout' : (e?.message || 'fetch error').slice(0, 80) };
|
|
54
|
+
}
|
|
114
55
|
}
|
|
115
|
-
// Tam akış: plan al → yerelde
|
|
56
|
+
// Tam akış: plan al → her round'da istekleri yerelde at → /exec ile analiz ettir → done'a dek döngü.
|
|
116
57
|
export async function runPentest(config, tool, target, scopeAck, log) {
|
|
117
58
|
const plan = await postJson(config, '/pentest/plan', { tool, target, scope_ack: scopeAck }, 15000);
|
|
118
59
|
if (!plan.ok)
|
|
119
60
|
return { ok: false, reason: plan.reason, plan };
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
61
|
+
const allowed = new Set((plan.allowed_hosts || [plan.host || '']).map((x) => x.toLowerCase()));
|
|
62
|
+
let requests = plan.requests || [];
|
|
63
|
+
let round = plan.round || 1;
|
|
64
|
+
let report = null;
|
|
65
|
+
for (let guard = 0; guard < 8 && requests.length; guard++) {
|
|
66
|
+
log(`Tur ${round}: ${requests.length} istek yerelde atılıyor — trafik senin IP'nden çıkıyor…`);
|
|
67
|
+
// İstekleri sınırlı eşzamanlılıkla çalıştır (hedefi yormamak için 6'şarlı)
|
|
68
|
+
const responses = [];
|
|
69
|
+
const CONC = 6;
|
|
70
|
+
for (let i = 0; i < requests.length; i += CONC) {
|
|
71
|
+
const batch = requests.slice(i, i + CONC);
|
|
72
|
+
const rs = await Promise.all(batch.map((req) => execReq(req, allowed)));
|
|
73
|
+
responses.push(...rs);
|
|
74
|
+
}
|
|
75
|
+
report = await postJson(config, '/pentest/exec', { job_id: plan.job_id, responses }, 30000);
|
|
76
|
+
if (!report.ok)
|
|
77
|
+
return { ok: false, reason: report.reason, plan, report };
|
|
78
|
+
if (report.done)
|
|
79
|
+
break;
|
|
80
|
+
requests = report.requests || [];
|
|
81
|
+
round = report.round || round + 1;
|
|
129
82
|
}
|
|
130
|
-
|
|
131
|
-
return { ok: !!report.ok, plan, report, missing: [...new Set(missing)] };
|
|
83
|
+
return { ok: true, plan, report };
|
|
132
84
|
}
|