wormclaude 1.0.84 → 1.0.86

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 CHANGED
@@ -15,6 +15,7 @@ import { isLearnEnabled, setLearnEnabled, getLearnFile, getLearnCount } from './
15
15
  import { saveMemoryFact, getMemoryPath, loadMemoryContext } from './memory.js';
16
16
  import { fetchAccount, getVerifyStatus, submitVerification } from './api.js';
17
17
  import { programText, tier } from './program.js';
18
+ import { runPentest } from './pentest.js';
18
19
  export const COMMANDS = [
19
20
  { name: '/help', desc: 'komutları ve ipuçlarını göster' },
20
21
  { name: '/clear', desc: 'sohbeti ve geçmişi temizle' },
@@ -44,6 +45,10 @@ export const COMMANDS = [
44
45
  { name: '/izinler', desc: 'onayli shell komutlarini yonet (list/add/remove/clear)' },
45
46
  { name: '/program', desc: 'Doğrulanmış Araştırmacı Programı — güven seviyeni göster' },
46
47
  { name: '/dogrula', desc: 'Doğrulanmış Araştırmacı başvurusu gönder: /dogrula <profil/şirket/gerekçe>' },
48
+ { name: '/recon', desc: '[seviye 3+] yetkili hedefte keşif (alt-alan/host/crawl): /recon <alan>' },
49
+ { name: '/scan', desc: '[seviye 3+] yetkili hedefte zafiyet taraması (nuclei): /scan <url>' },
50
+ { name: '/xss', desc: '[seviye 3+] yetkili hedefte XSS taraması (dalfox): /xss <url>' },
51
+ { name: '/sqli', desc: '[seviye 3+] yetkili hedefte SQLi taraması (sqlmap): /sqli <url>' },
47
52
  { name: '/export', desc: 'sohbeti dosyaya kaydet' },
48
53
  { name: '/resume', desc: 'en son kaydedilen oturumu yükle' },
49
54
  { name: '/quit', desc: 'çıkış' },
@@ -101,6 +106,85 @@ function tsStamp() {
101
106
  return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`;
102
107
  }
103
108
  // Komut yürütücü. true → komut işlendi, false → komut değil (normal mesaj).
109
+ const PT_LABELS = {
110
+ recon: 'Keşif (subfinder+httpx+katana)', scan: 'Zafiyet tarama (nuclei)',
111
+ xss: 'XSS (dalfox)', sqli: 'SQL injection (sqlmap)',
112
+ };
113
+ const PT_REASON = {
114
+ trust_required: 'Bu komut Doğrulanmış Araştırmacı (seviye 3+) gerektirir. Başvuru: /dogrula',
115
+ scope_required: 'Yetki onayı gerekli.',
116
+ no_quota: 'Bu ay tarama kotan yok (planını yükselt).',
117
+ quota_exceeded: 'Aylık tarama kotan doldu.',
118
+ invalid_target: 'Geçersiz hedef. URL/alan adı ver (örn https://test.com/p?id=1).',
119
+ unknown_tool: 'Bilinmeyen araç.',
120
+ auth: 'Giriş gerekli. /config key <anahtar>',
121
+ timeout: 'Sunucu zaman aşımı.',
122
+ upstream_error: 'Sunucuya ulaşılamadı.',
123
+ };
124
+ function formatFinding(x) {
125
+ const sev = x.severity ? `[${String(x.severity).toUpperCase()}] ` : '';
126
+ if (x.type === 'vuln')
127
+ return `${sev}${x.name || ''} — ${x.url || ''}`;
128
+ if (x.type === 'xss')
129
+ return `${sev}XSS ${x.param ? 'param ' + x.param : ''} — ${x.url || x.poc || ''}`;
130
+ if (x.type === 'sqli')
131
+ return `${sev}SQLi ${x.vulnerable ? 'ZAFİYETLİ' : ''} ${x.params ? '(' + x.params.join(',') + ')' : ''}`;
132
+ if (x.type === 'host')
133
+ return `${x.status || ''} ${x.url || ''}${x.title ? ' · ' + x.title : ''}${x.server ? ' · ' + x.server : ''}`;
134
+ if (x.value)
135
+ return `${x.value}`;
136
+ return JSON.stringify(x);
137
+ }
138
+ // /recon /scan /xss /sqli — ince runner'ı sürer. Zorunlu yetki onayı + trust 3+ teaser.
139
+ async function pentestCmd(tool, arg, ctx) {
140
+ const parts = (arg || '').trim().split(/\s+/).filter(Boolean);
141
+ let scopeAck = false;
142
+ if (parts.length && /^(onayla|--yes|-y|yes|evet)$/i.test(parts[parts.length - 1])) {
143
+ scopeAck = true;
144
+ parts.pop();
145
+ }
146
+ const target = parts.join(' ').trim();
147
+ const label = PT_LABELS[tool] || tool;
148
+ if (!target) {
149
+ ctx.note(`${label}\nKullanım: /${tool} <hedef>\nÖrnek: /${tool} https://test.com/sayfa?id=1`);
150
+ return;
151
+ }
152
+ if (!scopeAck) {
153
+ ctx.note(`⚠️ YETKİ ONAYI GEREKLİ — ${label}\n` +
154
+ `Hedef: ${target}\n\n` +
155
+ `Bu tarama hedefe GERÇEK istekler gönderir ve trafik SENİN IP'nden çıkar. Yalnız sahibi olduğun ya da\n` +
156
+ `yazılı izin/angajman bulunan sistemlerde çalıştır. Yetkisiz tarama yasa dışıdır ve kayıt altına alınır.\n\n` +
157
+ `Onaylıyorsan tekrar çalıştır: /${tool} ${target} onayla`);
158
+ return;
159
+ }
160
+ ctx.note(`${label} başlatılıyor — ${target}`);
161
+ const out = await runPentest(ctx.config, tool, target, true, (s) => ctx.note(s));
162
+ if (!out.ok) {
163
+ const r = out.reason || out.plan?.reason || 'error';
164
+ let msg = PT_REASON[r] || r;
165
+ if (r === 'quota_exceeded' && out.plan)
166
+ msg += ` (${out.plan.used}/${out.plan.quota})`;
167
+ ctx.note(`Tarama yapılamadı: ${msg}`);
168
+ return;
169
+ }
170
+ const rep = out.report || {};
171
+ 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
+ const f = rep.findings || [];
176
+ if (!f.length) {
177
+ lines.push('Bulgu yok (hedef yanıt vermedi, temiz, ya da araç kurulu değil).');
178
+ }
179
+ else {
180
+ lines.push(`${rep.found_count} bulgu:`);
181
+ for (const x of f.slice(0, 40))
182
+ lines.push(' ' + formatFinding(x));
183
+ if (f.length > 40)
184
+ lines.push(` … +${f.length - 40} daha`);
185
+ }
186
+ ctx.assistant(lines.join('\n'));
187
+ }
104
188
  export async function runSlashCommand(input, ctx) {
105
189
  const v = input.trim();
106
190
  if (!v.startsWith('/'))
@@ -642,6 +726,12 @@ export async function runSlashCommand(input, ctx) {
642
726
  : `Başvuru gönderilemedi: ${res.error}`);
643
727
  return true;
644
728
  }
729
+ case '/recon':
730
+ case '/scan':
731
+ case '/xss':
732
+ case '/sqli':
733
+ await pentestCmd(cmd.slice(1), arg, ctx);
734
+ return true;
645
735
  case '/export': {
646
736
  fs.mkdirSync(SESSION_DIR, { recursive: true });
647
737
  const file = path.join(SESSION_DIR, `session-${tsStamp()}.json`);
@@ -0,0 +1,132 @@
1
+ // WormClaude güvenlik tarama — İSTEMCİ ince runner.
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
+ }
50
+ async function postJson(config, p, body, timeoutMs) {
51
+ try {
52
+ const r = await fetch(`${config.baseUrl}${p}`, {
53
+ method: 'POST',
54
+ headers: { 'Content-Type': 'application/json', ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}) },
55
+ body: JSON.stringify(body),
56
+ signal: AbortSignal.timeout(timeoutMs),
57
+ });
58
+ const d = await r.json().catch(() => ({ ok: false, reason: 'bad_response' }));
59
+ if (!r.ok && !d.reason)
60
+ return { ok: false, reason: `HTTP ${r.status}` };
61
+ return d;
62
+ }
63
+ catch (e) {
64
+ return { ok: false, reason: e?.name === 'TimeoutError' ? 'timeout' : 'upstream_error' };
65
+ }
66
+ }
67
+ function runStep(step) {
68
+ return new Promise((resolve) => {
69
+ // İstemci allowlist zorlaması — sunucu ne derse desin, izinsiz ikili ÇALIŞMAZ.
70
+ if (!PT_ALLOWED.has(step.bin)) {
71
+ resolve({ id: step.id, ran: false, exit: -1, stdout: '', stderr: `engellendi: izinli olmayan ikili "${step.bin}"` });
72
+ return;
73
+ }
74
+ // Windows dahil PATH'te çöz (yoksa = kurulu değil).
75
+ const found = resolveBin(step.bin);
76
+ if (!found) {
77
+ resolve({ id: step.id, ran: false, exit: -1, stdout: '', stderr: `çalıştırılamadı (kurulu değil?): ${step.bin}` });
78
+ return;
79
+ }
80
+ let out = '', err = '', done = false;
81
+ let child;
82
+ try {
83
+ child = found.viaCmd
84
+ ? spawn('cmd.exe', ['/d', '/s', '/c', found.path, ...step.args], { shell: false, windowsHide: true })
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');
93
+ }
94
+ catch { /* */ } }, Math.max(5, step.timeout) * 1000);
95
+ child.stdout?.on('data', (d) => { if (out.length < MAX_OUT)
96
+ out += d.toString(); });
97
+ child.stderr?.on('data', (d) => { if (err.length < 4000)
98
+ err += d.toString(); });
99
+ child.on('error', () => {
100
+ if (done)
101
+ return;
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
+ });
114
+ }
115
+ // Tam akış: plan al → yerelde çalıştır → rapor gönder. log() ile ilerleme bildirir.
116
+ export async function runPentest(config, tool, target, scopeAck, log) {
117
+ const plan = await postJson(config, '/pentest/plan', { tool, target, scope_ack: scopeAck }, 15000);
118
+ if (!plan.ok)
119
+ return { ok: false, reason: plan.reason, plan };
120
+ log(`Plan alındı (${plan.steps.length} adım). Yerelde çalıştırılıyor — trafik senin IP'nden çıkıyor…`);
121
+ const results = [];
122
+ const missing = [];
123
+ for (const s of plan.steps) {
124
+ log(` → ${s.bin} ${s.args.slice(0, 3).join(' ')}…`);
125
+ const r = await runStep(s);
126
+ if (!r.ran && /kurulu değil/.test(r.stderr))
127
+ missing.push(s.bin);
128
+ results.push(r);
129
+ }
130
+ const report = await postJson(config, '/pentest/report', { job_id: plan.job_id, results }, 30000);
131
+ return { ok: !!report.ok, plan, report, missing: [...new Set(missing)] };
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.84",
3
+ "version": "1.0.86",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {