wormclaude 1.0.160 → 1.0.162

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/api.js CHANGED
@@ -111,6 +111,26 @@ async function fetchWithRetry(url, init) {
111
111
  }
112
112
  throw lastErr ?? new Error('retry exhausted');
113
113
  }
114
+ // Konuşmadaki EN SON URL/host'u bul (SecurityScan narration-recovery hedefi). Dosya adlarını
115
+ // (.js/.json/.html…) ELER ki "package.json" gibi şeyler hedef sanılmasın.
116
+ function _lastTargetFromMessages(messages) {
117
+ const urlRe = /https?:\/\/[^\s"'<>)\]]+/i;
118
+ const hostRe = /\b((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,24})\b/i;
119
+ const badExt = /\.(?:js|ts|tsx|jsx|json|html?|css|png|jpe?g|gif|svg|md|txt|py|sh|yml|yaml|lock|map|ico|woff2?|mjs|cjs)$/i;
120
+ for (let i = messages.length - 1; i >= 0; i--) {
121
+ const c = messages[i]?.content;
122
+ const s = typeof c === 'string' ? c : Array.isArray(c) ? c.map((x) => x?.text || '').join(' ') : '';
123
+ if (!s)
124
+ continue;
125
+ const u = s.match(urlRe);
126
+ if (u)
127
+ return u[0];
128
+ const h = s.match(hostRe);
129
+ if (h && !badExt.test(h[1]) && !/\.(localhost)$/i.test(h[1]))
130
+ return h[1];
131
+ }
132
+ return '';
133
+ }
114
134
  export async function* streamChat(messages, tools, config, signal) {
115
135
  const body = {
116
136
  model: config.model,
@@ -263,8 +283,9 @@ export async function* streamChat(messages, tools, config, signal) {
263
283
  args: safeJsonStringify(c.args),
264
284
  }));
265
285
  // Yedek: upstream yapısal tool_call vermediyse modelin metne gömdüğü çağrıyı kurtar.
286
+ // scanTarget: SecurityScan'i "anlatıp" çağırmayan 32B için hedefi konuşmadan al (en son URL/host).
266
287
  if (toolCalls.length === 0 && fullText) {
267
- const recovered = recoverInlineToolCalls(fullText);
288
+ const recovered = recoverInlineToolCalls(fullText, { scanTarget: _lastTargetFromMessages(messages) });
268
289
  if (recovered.length)
269
290
  toolCalls = recovered;
270
291
  }
package/dist/commands.js CHANGED
@@ -57,6 +57,7 @@ export const COMMANDS = [
57
57
  { name: '/scan', desc: '[seviye 3+] yetkili hedefte genel tarama (keşif+başlıklar): /scan <alan>' },
58
58
  { name: '/xss', desc: '[seviye 3+] yetkili hedefte XSS taraması (kendi motorumuz): /xss <url>' },
59
59
  { name: '/sqli', desc: '[seviye 3+] yetkili hedefte SQLi taraması (kendi motorumuz): /sqli <url>' },
60
+ { name: '/fullscan', desc: '[seviye 3+] tam kapsamlı DETERMİNİSTİK tarama: recon+XSS+SQLi tek komutta: /fullscan <hedef>' },
60
61
  { name: '/exploit', desc: 'yetkili testte doğrulanmış PoC üret: /exploit <cve|açıklama>' },
61
62
  { name: '/harden', desc: 'savunma çıktısı üret (YARA/Sigma + yama önerisi): /harden <hedef|tehdit>' },
62
63
  { name: '/export', desc: 'sohbeti dosyaya kaydet' },
@@ -187,7 +188,7 @@ function formatFinding(x) {
187
188
  // FALSE-POZİTİF guard: ilk kelime tam komut adı + argüman var + soru DEĞİL ("recon nedir?" tetiklemez).
188
189
  // Yalnız hedef-alan komutları; ve hedef URL/domain GİBİ görünmeli ("xss ile tara" → komut DEĞİL,
189
190
  // model SecurityScan ile halleder). "?'li URL kabul edilir (parametreli xss/sqli hedefi).
190
- const _BARE_CMDS = new Set(['recon', 'scan', 'xss', 'sqli']);
191
+ const _BARE_CMDS = new Set(['recon', 'scan', 'xss', 'sqli', 'fullscan']);
191
192
  const _TARGET_RE = /(^https?:\/\/)|(\.[a-z]{2,})|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/i;
192
193
  export function detectBareCommand(input) {
193
194
  const s = (input || '').trim();
@@ -269,6 +270,76 @@ async function pentestCmd(tool, arg, ctx) {
269
270
  }
270
271
  ctx.assistant(lines.join('\n'));
271
272
  }
273
+ // /fullscan — DETERMİNİSTİK tam tarama. Modelin doğaçlamasına (nmap/sqlmap orkestrasyonu →
274
+ // narration/uydurma/PATH derdi) GÜVENMEZ: recon → XSS → SQLi motorunu KOD sırayla, her hedefte
275
+ // aynı şekilde koşar, gerçek bulguları tek raporda birleştirir. Tekrarlanabilir (red team/öğrenci).
276
+ async function fullScanCmd(arg, ctx) {
277
+ const parts = (arg || '').trim().split(/\s+/).filter(Boolean);
278
+ let scopeAck = false;
279
+ if (parts.length && /^(run|onayla|çalıştır|calistir|--yes|-y|yes|evet)$/i.test(parts[parts.length - 1])) {
280
+ scopeAck = true;
281
+ parts.pop();
282
+ }
283
+ const cleaned = [];
284
+ for (let i = 0; i < parts.length; i++) {
285
+ if (parts[i].startsWith('-')) {
286
+ if (parts[i + 1] && !parts[i + 1].startsWith('-'))
287
+ i++;
288
+ continue;
289
+ }
290
+ cleaned.push(parts[i]);
291
+ }
292
+ const target = cleaned.join(' ').trim();
293
+ if (!target) {
294
+ ctx.note(tt('Full scan (deterministic: recon + XSS + SQLi)\nUsage: /fullscan <target>\nExample: /fullscan testphp.vulnweb.com', 'Tam tarama (deterministik: recon + XSS + SQLi)\nKullanım: /fullscan <hedef>\nÖrnek: /fullscan testphp.vulnweb.com'));
295
+ return;
296
+ }
297
+ if (!scopeAck) {
298
+ pendingPentest = `/fullscan ${target} run`;
299
+ ctx.note(tt(`⚠️ AUTHORIZATION REQUIRED — Full scan (recon + XSS + SQLi)\nTarget: ${target}\n\n` +
300
+ `Sends REAL requests from YOUR IP. Only on systems you own or have written authorization for.\n` +
301
+ `To confirm, type "run" (Enter) · or: /fullscan ${target} run`, `⚠️ YETKİ ONAYI GEREKLİ — Tam tarama (recon + XSS + SQLi)\nHedef: ${target}\n\n` +
302
+ `Hedefe GERÇEK istekler SENİN IP'nden gider. Yalnız sahibi olduğun ya da yazılı izinli sistemlerde.\n` +
303
+ `Onaylamak için "run" yaz (Enter) · ya da: /fullscan ${target} run`));
304
+ return;
305
+ }
306
+ pendingPentest = null;
307
+ ctx.note(tt(`Full scan starting — ${target} (recon → XSS → SQLi, deterministic, repeatable)`, `Tam tarama başlıyor — ${target} (recon → XSS → SQLi, deterministik, tekrarlanabilir)`));
308
+ const steps = [['recon', ptLabel('recon')], ['xss', ptLabel('xss')], ['sqli', ptLabel('sqli')]];
309
+ const all = [];
310
+ for (const [tool, label] of steps) {
311
+ ctx.note(`▶ ${label} …`);
312
+ const out = await runPentest(ctx.config, tool, target, true, (s) => ctx.note(s));
313
+ if (!out.ok) {
314
+ const r = out.reason || out.plan?.reason || 'error';
315
+ // Yetki/kota hatası tüm adımlarda aynı → dur. Diğer hatada o adımı atla, devam et.
316
+ if (r === 'trust_required' || r === 'quota_exceeded' || r === 'no_quota' || r === 'auth') {
317
+ ctx.note(`${tt('Full scan stopped', 'Tam tarama durdu')}: ${ptReason(r)}`);
318
+ return;
319
+ }
320
+ ctx.note(` ${label}: ${ptReason(r)} — ${tt('skipped', 'atlandı')}`);
321
+ continue;
322
+ }
323
+ const f = (out.report?.findings || []).map((x) => ({ ...x, _step: tool }));
324
+ all.push(...f);
325
+ ctx.note(` ${label}: ${f.length} ${tt('finding(s)', 'bulgu')}`);
326
+ }
327
+ const order = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
328
+ all.sort((a, b) => (order[a.severity] ?? 9) - (order[b.severity] ?? 9));
329
+ const lines = [`✓ ${tt('Full scan complete', 'Tam tarama tamamlandı')} — ${target}`];
330
+ if (!all.length) {
331
+ lines.push(tt('No findings on the tested surface (recon/XSS/SQLi). Target looks clean or was unreachable.', 'Test edilen yüzeyde bulgu yok (recon/XSS/SQLi). Hedef temiz görünüyor ya da erişilemedi.'));
332
+ }
333
+ else {
334
+ const sev = (s) => all.filter((x) => x.severity === s).length;
335
+ lines.push(`${all.length} ${tt('finding(s)', 'bulgu')} — high:${sev('high')} medium:${sev('medium')} low:${sev('low')} info:${sev('info')}`);
336
+ for (const x of all.slice(0, 60))
337
+ lines.push(' ' + formatFinding(x));
338
+ if (all.length > 60)
339
+ lines.push(` … +${all.length - 60} ${tt('more', 'daha')}`);
340
+ }
341
+ ctx.assistant(lines.join('\n'));
342
+ }
272
343
  // /exploit ve /harden — AKTİF tarama DEĞİL, model-üretim görevleri (PoC / YARA-Sigma yazar).
273
344
  // Güçlü, yetkili-kapsam çerçevesiyle modele gider (skill gibi). Çıktı kullanıcının dilinde.
274
345
  export function buildExploitPrompt(arg) {
@@ -894,6 +965,9 @@ export async function runSlashCommand(input, ctx) {
894
965
  case '/sqli':
895
966
  await pentestCmd(cmd.slice(1), arg, ctx);
896
967
  return true;
968
+ case '/fullscan':
969
+ await fullScanCmd(arg, ctx);
970
+ return true;
897
971
  case '/skill': {
898
972
  const m = (arg || '').trim().split(/\s+/).filter(Boolean);
899
973
  const sub = (m.shift() || '').toLowerCase();
@@ -232,11 +232,19 @@ function extractTopLevelJsonObjects(text) {
232
232
  }
233
233
  /** Metindeki gömülü araç çağrılarını kurtarır. Gürültüden kaçınmak için yalnız açık sarmalları
234
234
  * (<tool_call>, ```json) veya mesajın TAMAMI tek JSON çağrısıysa onu alır. */
235
- export function recoverInlineToolCalls(text) {
235
+ // SecurityScan'i ÇAĞIRMAYIP "anlatan" 32B için: tarama-ismi + aksiyon-fiili + hedef → gerçek çağrı.
236
+ // (Model "Recon işlemini başlatıyorum:" deyip takılıyordu; kullanıcı 'başla' deyip duruyordu.)
237
+ // Stem eşleşmesi (Türkçe ekleri için sondaki \b yok): "tarayacağım/tarıyorum", "keşfederek".
238
+ // tara(?!f) → "taraf/tarafından" YANLIŞ yakalanmaz; "keşif" (isim) + "keşf" (keşfet fiili) ayrı.
239
+ const _SCAN_NOUN_RE = /\b(?:recon|keşif|keşf|kesif|tar[aı](?!f)|scan|securityscan|zafiyet|enjeksiyon|injection|sqli|xss)/i;
240
+ const _SCAN_ACT_RE = /(başl|basl|çalıştır|calistir|gerçekleştir|gerceklestir|kullan|aca[gğ]|ece[gğ]|acak|ecek|[ıiuü]yor|start|run\b|launch|initiat|perform)/i;
241
+ function _scanIntent(s) { return _SCAN_NOUN_RE.test(s) && _SCAN_ACT_RE.test(s); }
242
+ export function recoverInlineToolCalls(text, opts) {
236
243
  const t = (text || '').trim();
237
- // JSON/prose/ToolName{…}/ToolName(…) çağrısı yoksa erken çık.
244
+ const scanTarget = (opts?.scanTarget || '').trim();
245
+ // JSON/prose/ToolName{…}/ToolName(…)/SecurityScan-niyeti çağrısı yoksa erken çık.
238
246
  // (Kod fence'i varsa çıkma — dosya-adı etiketli blok olabilir, adım 7 karar verir.)
239
- if (!t || (!t.includes('"name"') && !t.includes('"tool"') && !t.includes('"function"') && !/AskUserQuestion/i.test(t) && !TOOL_PREFIX_RE.test(t) && !TOOL_PAREN_RE.test(t) && !t.includes('```')))
247
+ if (!t || (!t.includes('"name"') && !t.includes('"tool"') && !t.includes('"function"') && !/AskUserQuestion/i.test(t) && !TOOL_PREFIX_RE.test(t) && !TOOL_PAREN_RE.test(t) && !t.includes('```') && !(scanTarget && _scanIntent(t))))
240
248
  return [];
241
249
  const out = [];
242
250
  const seen = new Set();
@@ -334,6 +342,20 @@ export function recoverInlineToolCalls(text) {
334
342
  }
335
343
  }
336
344
  }
345
+ // 6.5) SecurityScan NARRASYON → gerçek çağrı. 32B "recon başlatıyorum / XSS taraması yapacağım /
346
+ // SecurityScan ile tarayacağız" diye NİYET yazıp aracı çağırmıyor → takılıyordu. Hedef
347
+ // (konuşmadan) + niyet varsa SecurityScan'i kendimiz emit ediyoruz. Araç metinden seçilir.
348
+ if (!out.length && scanTarget && _scanIntent(t)) {
349
+ const low = t.toLowerCase();
350
+ let tool = 'recon'; // varsayılan: genel "tara" → önce keşif
351
+ if (/\bxss\b/.test(low))
352
+ tool = 'xss';
353
+ else if (/sql/.test(low))
354
+ tool = 'sqli';
355
+ else if (/(recon|keşif|keşf|kesif)/.test(low))
356
+ tool = 'recon';
357
+ push({ name: 'SecurityScan', arguments: { tool, target: scanTarget } });
358
+ }
337
359
  // 7) SON ÇARE: hiç çağrı kurtarılamadıysa, dosya-adı etiketli ```kod bloklarını
338
360
  // Write'a çevir (model build'de Write yerine kod yapıştırınca dosya kaybolmasın).
339
361
  // Adım 7, gerçek tool-call YOKKEN çalışır → "kod göster"i bozmaz (etiket gerekir).
package/dist/theme.js CHANGED
@@ -16,4 +16,4 @@ export const theme = {
16
16
  synType: '#a78bfa', // tip/sınıf adları, sabitler
17
17
  synProp: '#e0e0e0', // özellik/anahtar adları
18
18
  };
19
- export const VERSION = '1.0.160';
19
+ export const VERSION = '1.0.162';
package/dist/tools.js CHANGED
@@ -747,13 +747,11 @@ async function execOne(call, hooks) {
747
747
  txt += 'Zafiyet bulunamadı (hedef temiz ya da yanıt vermedi).';
748
748
  else
749
749
  txt += `${rep.found_count} bulgu:\n` + f.slice(0, 50).map((x) => ' ' + JSON.stringify(x)).join('\n');
750
- // HYBRID: bu gömülü ilk-geçiştir. Modele, kullanıcıya derin taramayı TEKLİF ettir.
751
- txt += '\n\n[hybrid-note] This was WormClaude\'s fast embedded first-pass scan. After reporting these'
752
- + ' findings, OFFER the user (in their language) a deeper full scan with real tools'
753
- + ' (nmap, nuclei, sqlmap, dalfox, ffuf), paired with the authorization reminder: for'
754
- + ' educational/authorized use only — run it only if the user owns the target or holds a written'
755
- + ' authorization document. Escalate ONLY if the user accepts AND has confirmed ownership/written'
756
- + ' authorization (ask once if unstated).';
750
+ // HYBRID: bu gömülü ilk-geçiştir. Derin tarama = DETERMİNİSTİK /fullscan (model doğaçlamasın).
751
+ txt += `\n\n[hybrid-note] This was WormClaude's fast first-pass scan. For a COMPREHENSIVE scan, tell`
752
+ + ` the user to run the deterministic command "/fullscan ${target}" it chains recon + XSS + SQLi`
753
+ + ` in one reliable, repeatable pass. Do NOT hand-orchestrate nmap/sqlmap yourself; recommend`
754
+ + ` /fullscan. Pair it with the authorization reminder (only on owned / written-authorized targets).`;
757
755
  return { ok: true, output: txt, args };
758
756
  }
759
757
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.160",
3
+ "version": "1.0.162",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {