wormclaude 1.0.165 → 1.0.167

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
@@ -10,7 +10,7 @@ import * as usage from './usage.js';
10
10
  import { runCompact, contextPercent, autoCompactThreshold } from './compact.js';
11
11
  import { cmdDesc, setLang, saveLang, getLang } from './i18n.js';
12
12
  import { listCheckpoints, rewindLast, rewindTo, rewindAll } from './checkpoint.js';
13
- import { setPlanMode } from './tools.js';
13
+ import { setPlanMode, getBashCwd } from './tools.js';
14
14
  import { loadSkills, getSkills, getSkillsDir, installSkill, updateSkill, removeSkill, getRegistry } from './skills.js';
15
15
  import { getApprovedCommands, approveCommands, unapproveCommands, clearApproved } from './cmdsec.js';
16
16
  import { isLearnEnabled, setLearnEnabled, getLearnFile, getLearnCount } from './learn.js';
@@ -58,6 +58,7 @@ export const COMMANDS = [
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
60
  { name: '/dirscan', desc: '[seviye 3+] dizin/içerik keşfi (yönetim/api/yedek/sızıntı yolları): /dirscan <alan>' },
61
+ { name: '/report', desc: 'son taramadan profesyonel pentest raporu üret (CVSS/etki/çözüm, dosyaya yazar)' },
61
62
  { name: '/fullscan', desc: '[seviye 3+] tam kapsamlı DETERMİNİSTİK tarama: recon+XSS+SQLi tek komutta: /fullscan <hedef>' },
62
63
  { name: '/exploit', desc: 'yetkili testte doğrulanmış PoC üret: /exploit <cve|açıklama>' },
63
64
  { name: '/harden', desc: 'savunma çıktısı üret (YARA/Sigma + yama önerisi): /harden <hedef|tehdit>' },
@@ -152,6 +153,98 @@ const PT_REASON_TR = {
152
153
  upstream_error: 'Sunucuya ulaşılamadı.',
153
154
  };
154
155
  const ptReason = (r) => (getLang() === 'tr' ? PT_REASON_TR : PT_REASON_EN)[r] || r;
156
+ // ── Pentest RAPORU (Tier 3) — son taramanın GERÇEK bulgularından profesyonel rapor üretir.
157
+ // Uydurma YOK: bulgular motordan gelir; aşağıdaki harita yalnız etki/çözüm bilgisini ekler (statik, küratörlü).
158
+ let _lastScan = null;
159
+ export function setLastScan(target, findings) {
160
+ if (findings && findings.length)
161
+ _lastScan = { target, findings, ts: Date.now() };
162
+ }
163
+ // type → {başlık, CWE, etki, çözüm} (CWE-temelli küratörlü bilgi)
164
+ const _REMED = {
165
+ xss: { t: 'Cross-Site Scripting (XSS)', cwe: 'CWE-79', impact: 'Saldırgan kurbanın tarayıcısında script çalıştırır — oturum çalma, hesap ele geçirme, sayfa tahrifi.', fix: 'Çıktıyı bağlama göre encode et (HTML/JS/URL), CSP uygula, girdiyi doğrula; framework auto-escaping kullan.' },
166
+ sqli: { t: 'SQL Injection', cwe: 'CWE-89', impact: 'Veritabanı okuma/yazma, kimlik doğrulama atlama, tüm verinin sızması.', fix: 'Parametreli sorgu (prepared statement) kullan; ORM; girdi doğrulama; en-az-yetki DB kullanıcısı.' },
167
+ ssti: { t: 'Server-Side Template Injection', cwe: 'CWE-1336', impact: 'Şablon motorunda kod çalıştırma → çoğu zaman RCE.', fix: 'Kullanıcı girdisini şablona koyma; sandbox’lı motor; logicless şablon (Mustache).' },
168
+ lfi: { t: 'Local File Inclusion / Path Traversal', cwe: 'CWE-22', impact: 'Sunucu dosyalarını okuma (config, /etc/passwd), zincirle RCE.', fix: 'Dosya yolunu allowlist’le; ../ temizle; kullanıcı girdisini dosya yoluna koyma.' },
169
+ ssrf: { t: 'Server-Side Request Forgery', cwe: 'CWE-918', impact: 'İç servislere/metadata’ya (cloud) erişim, iç ağ tarama.', fix: 'Hedef host’u allowlist’le; iç IP’leri (169.254, 10.x, localhost) reddet; redirect takip etme.' },
170
+ 'cmd-injection': { t: 'Komut Enjeksiyonu', cwe: 'CWE-78', impact: 'Sunucuda işletim sistemi komutu çalıştırma → tam sistem ele geçirme.', fix: 'Shell çağırma; dil-içi API kullan; argümanları allowlist’le; kullanıcı girdisini komuta koyma.' },
171
+ 'nosql-injection': { t: 'NoSQL Injection', cwe: 'CWE-943', impact: 'Sorgu manipülasyonu, kimlik doğrulama atlama, veri sızması.', fix: 'Girdiyi tip-doğrula; operatör ($ne/$gt) enjeksiyonunu engelle; ODM güvenli bağlama.' },
172
+ 'open-redirect': { t: 'Açık Yönlendirme', cwe: 'CWE-601', impact: 'Oltalama/phishing — kullanıcı güvenilen alandan kötücül siteye yönlenir.', fix: 'Yönlendirme hedefini allowlist’le; harici URL’e doğrudan yönlendirme yapma.' },
173
+ crlf: { t: 'CRLF / HTTP Başlık Enjeksiyonu', cwe: 'CWE-113', impact: 'Başlık enjeksiyonu, önbellek zehirleme, oturum sabitleme.', fix: 'Yanıt başlığına giren girdiden CR/LF (%0d%0a) temizle.' },
174
+ 'graphql-introspection': { t: 'GraphQL Introspection Açık', cwe: 'CWE-200', impact: 'Tüm API şeması/gizli uçlar ifşa → saldırı yüzeyi haritalanır.', fix: 'Üretimde introspection’ı kapat; şema ifşasını sınırla.' },
175
+ path: { t: 'Açıkta Dizin/Dosya', cwe: 'CWE-538', impact: 'Hassas dosya/uç ifşası (kaynak, yedek, yönetim) → bilgi sızması.', fix: 'Hassas yolları kaldır/kısıtla; .git/.env vb. web kökünden çıkar; erişim kontrolü.' },
176
+ weak_headers: { t: 'Eksik Güvenlik Başlıkları', cwe: 'CWE-693', impact: 'Clickjacking/MIME-sniffing/MITM riskini artırır.', fix: 'CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy ekle.' },
177
+ cors: { t: 'CORS Yanlış Yapılandırması', cwe: 'CWE-942', impact: 'Kötücül site kullanıcı adına yetkili istek yapabilir, veri çalar.', fix: 'Origin’i allowlist’le; ACAO:* + credentials birlikte kullanma.' },
178
+ exposure: { t: 'Hassas Dosya İfşası', cwe: 'CWE-538', impact: 'Sır/yapılandırma sızması (.git/.env/yedek).', fix: 'Dosyaları web kökünden kaldır; erişimi engelle; sızan sırları rotate et.' },
179
+ subdomain_takeover: { t: 'Subdomain Takeover', cwe: 'CWE-350', impact: 'Saldırgan sahipsiz alt-alanı ele geçirir → phishing/çerez çalma.', fix: 'Kullanılmayan DNS kayıtlarını (CNAME) sil; 3.parti servisi geri talep et.' },
180
+ };
181
+ export function buildReport(scan) {
182
+ const sevRank = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
183
+ const fs2 = [...scan.findings].sort((a, b) => (sevRank[a.severity] ?? 9) - (sevRank[b.severity] ?? 9));
184
+ const cnt = (s) => scan.findings.filter((f) => f.severity === s).length;
185
+ const d = new Date(scan.ts);
186
+ const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
187
+ const L = getLang() === 'tr';
188
+ const out = [];
189
+ out.push(`# ${L ? 'Güvenlik Değerlendirme Raporu' : 'Security Assessment Report'} — ${scan.target}`);
190
+ out.push(`\n**${L ? 'Tarih' : 'Date'}:** ${date} · **${L ? 'Araç' : 'Tool'}:** WormClaude v2 · **${L ? 'Kapsam' : 'Scope'}:** ${scan.target} (${L ? 'yetkili' : 'authorized'})\n`);
191
+ out.push(`## ${L ? 'Yönetici Özeti' : 'Executive Summary'}`);
192
+ out.push(`${scan.findings.length} ${L ? 'bulgu' : 'findings'} — **High: ${cnt('high')}**, Medium: ${cnt('medium')}, Low: ${cnt('low')}, Info: ${cnt('info')}.`);
193
+ const top = fs2.filter((f) => ['high', 'medium'].includes(f.severity)).slice(0, 5).map((f) => (_REMED[f.type]?.t || f.type));
194
+ if (top.length)
195
+ out.push(`${L ? 'Öne çıkan riskler' : 'Key risks'}: ${[...new Set(top)].join(', ')}.`);
196
+ else
197
+ out.push(L ? 'Yüksek/orta seviye doğrulanmış bulgu yok; yüzey büyük ölçüde temiz görünüyor.' : 'No high/medium confirmed findings; surface appears largely clean.');
198
+ out.push(`\n## ${L ? 'Bulgular' : 'Findings'}`);
199
+ let n = 0;
200
+ for (const f of fs2) {
201
+ if (f.type === 'reflection')
202
+ continue;
203
+ n++;
204
+ const r = _REMED[f.type];
205
+ const sev = (f.severity || 'info').toUpperCase();
206
+ out.push(`\n### ${n}. [${sev}] ${r?.t || f.type}${(r?.cwe || f.cwe) ? ' (' + (r?.cwe || f.cwe) + ')' : ''}`);
207
+ if (f.url)
208
+ out.push(`- **${L ? 'Uç' : 'Endpoint'}:** ${f.url}${f.param ? ` (param: \`${f.param}\`)` : ''}`);
209
+ if (f.evidence)
210
+ out.push(`- **${L ? 'Kanıt' : 'Evidence'}:** ${f.evidence}`);
211
+ else if (f.missing)
212
+ out.push(`- **${L ? 'Eksik' : 'Missing'}:** ${(f.missing || []).join(', ')}`);
213
+ if (r) {
214
+ out.push(`- **${L ? 'Etki' : 'Impact'}:** ${r.impact}`);
215
+ out.push(`- **${L ? 'Çözüm' : 'Remediation'}:** ${r.fix}`);
216
+ }
217
+ }
218
+ if (!n)
219
+ out.push(L ? '_Raporlanacak bulgu yok._' : '_No findings to report._');
220
+ out.push(`\n## ${L ? 'Metodoloji' : 'Methodology'}`);
221
+ out.push(L
222
+ ? 'WormClaude deterministik motoru: keşif (alt-alan/başlık/ifşa) + dizin/içerik keşfi + XSS/SQLi/SSTI/LFI/SSRF/komut-enjeksiyonu/NoSQL/açık-yönlendirme/CRLF + subdomain-takeover. Crawl-tabanlı param keşfi; trafik yetkili kapsamda kullanıcı IP’sinden çıktı.'
223
+ : 'WormClaude deterministic engine: recon (subdomains/headers/exposure) + content discovery + XSS/SQLi/SSTI/LFI/SSRF/cmd-injection/NoSQL/open-redirect/CRLF + subdomain-takeover. Crawl-based param discovery; traffic from the operator IP within authorized scope.');
224
+ out.push(`\n## ${L ? 'Sınırlar' : 'Limitations'}`);
225
+ out.push(L
226
+ ? 'Stored/DOM XSS, kimlik-doğrulama arkası akışlar, iş-mantığı ve post-exploitation kapsam dışıdır; bunlar için manuel test / tam araç (Burp Pro, sqlmap) önerilir. "Bulgu yok" = "güvenli" anlamına gelmez.'
227
+ : 'Stored/DOM XSS, authenticated flows, business logic and post-exploitation are out of scope; manual testing / full tooling (Burp Pro, sqlmap) recommended. "No findings" does not mean "secure".');
228
+ return out.join('\n');
229
+ }
230
+ async function reportCmd(ctx) {
231
+ if (!_lastScan) {
232
+ ctx.note(tt('No scan yet. Run /fullscan <target> (or /recon /xss /sqli) first, then /report.', 'Henüz tarama yok. Önce /fullscan <hedef> (ya da /recon /xss /sqli) çalıştır, sonra /report.'));
233
+ return;
234
+ }
235
+ const md = buildReport(_lastScan);
236
+ const host = (_lastScan.target.replace(/^https?:\/\//, '').replace(/[^\w.-]/g, '_')).slice(0, 40);
237
+ const fname = `wormclaude-report-${host}.md`;
238
+ const fpath = path.resolve(getBashCwd(), fname);
239
+ try {
240
+ fs.writeFileSync(fpath, md, 'utf8');
241
+ ctx.assistant(tt(`Report written: ${fpath}\n(${_lastScan.findings.length} findings from the last scan of ${_lastScan.target})`, `Rapor yazıldı: ${fpath}\n(${_lastScan.target} son taramasından ${_lastScan.findings.length} bulgu)`));
242
+ }
243
+ catch (e) {
244
+ // Yazılamazsa ekrana bas
245
+ ctx.assistant(md);
246
+ }
247
+ }
155
248
  // Bekleyen tarama onayı — kullanıcı uyarıdan sonra sadece "run"/"onayla" yazınca çalıştırmak için.
156
249
  const PT_BUILTIN = new Set(['recon', 'scan', 'xss', 'sqli', 'dirscan']);
157
250
  let pendingPentest = null;
@@ -170,19 +263,71 @@ function formatFinding(x) {
170
263
  return `${sev}SQLi (${x.technique || '?'}) · param ${x.param || '?'} — ${x.evidence || ''}`;
171
264
  if (x.type === 'subdomain')
172
265
  return `${tt('subdomain', 'alt-alan')}: ${x.value}`;
266
+ if (x.type === 'subdomain_takeover')
267
+ return `${sev}${tt('subdomain takeover', 'subdomain takeover')}: ${x.url || ''}${x.service ? ' (' + x.service + ')' : ''}`;
173
268
  if (x.type === 'host')
174
269
  return `${x.status || ''} ${x.url || ''}${x.title ? ' · ' + x.title : ''}${x.server ? ' · ' + x.server : ''}`;
175
270
  if (x.type === 'tech')
176
271
  return `${tt('tech', 'teknoloji')}: ${x.value}`;
177
272
  if (x.type === 'weak_headers')
178
273
  return `${sev}${tt('missing security headers', 'eksik güvenlik başlıkları')}: ${(x.missing || []).join(', ')}`;
179
- if (x.type === 'cors')
180
- return `${sev}${tt('CORS misconfiguration', 'CORS yanlış yapılandırma')}: ${x.url || ''} ${x.evidence || ''}`;
181
- if (x.type === 'exposure')
182
- return `${sev}${tt('exposure', 'ifşa')}: ${x.url || ''} — ${x.evidence || ''}`;
274
+ if (x.type === 'path')
275
+ return `${sev}${tt('path', 'yol')} ${x.url || ''} ${x.status || ''}`;
183
276
  if (x.value)
184
277
  return `${x.value}`;
185
- return JSON.stringify(x);
278
+ // GENEL: şablon bulguları (ssrf/ssti/lfi/ldap/cmd-injection/nosql/open-redirect/crlf/graphql/cors/exposure…)
279
+ const r = _REMED[x.type];
280
+ const label = (r && r.t) || x.name || x.type;
281
+ let s = `${sev}${label}`;
282
+ if (x.param)
283
+ s += ` · param ${x.param}`;
284
+ if (x.url)
285
+ s += ` — ${x.url}`;
286
+ return s;
287
+ }
288
+ // Tarama sonrası YORUMLU özet — ne ciddi, ne minör, ne yapmalı (deterministik, _REMED'den; uydurma yok).
289
+ export function summarizeScan(target, findings) {
290
+ const L = getLang() === 'tr';
291
+ const bySev = (s) => findings.filter((f) => f.severity === s && f.type !== 'reflection');
292
+ const high = bySev('high'), med = bySev('medium'), low = bySev('low'), info = bySev('info');
293
+ // tip bazında grupla → "XSS ×4 (searchTerm) — <etki>"
294
+ const grp = (list, withImpact) => {
295
+ const byType = {};
296
+ for (const f of list)
297
+ (byType[f.type] = byType[f.type] || []).push(f);
298
+ return Object.entries(byType).map(([t, fs]) => {
299
+ const r = _REMED[t];
300
+ const label = (r && r.t) || (fs[0] && fs[0].name) || t;
301
+ const params = [...new Set(fs.map((f) => f.param).filter(Boolean))];
302
+ const where = params.length ? ` (${params.join(', ')})` : '';
303
+ const head = `${label}${fs.length > 1 ? ` ×${fs.length}` : ''}${where}`;
304
+ return withImpact && r ? ` • ${head} — ${r.impact}` : ` • ${head}`;
305
+ });
306
+ };
307
+ const out = [];
308
+ out.push(L ? `📋 ÖZET — ${target}` : `📋 SUMMARY — ${target}`);
309
+ if (high.length) {
310
+ out.push(L ? `🔴 ${high.length} YÜKSEK risk (acil, sömürülebilir):` : `🔴 ${high.length} HIGH (urgent, exploitable):`);
311
+ out.push(...grp(high, true));
312
+ }
313
+ if (med.length) {
314
+ out.push(L ? `🟠 ${med.length} ORTA:` : `🟠 ${med.length} MEDIUM:`);
315
+ out.push(...grp(med, true));
316
+ }
317
+ if (low.length)
318
+ out.push((L ? `🔵 ${low.length} düşük (minör): ` : `🔵 ${low.length} low (minor): `) + grp(low, false).map((s) => s.replace(/^ {2}• /, '')).join('; '));
319
+ if (info.length)
320
+ out.push((L ? `⚪ ${info.length} bilgi (korumalı/bilgilendirme): ` : `⚪ ${info.length} info: `) + grp(info, false).map((s) => s.replace(/^ {2}• /, '')).join('; '));
321
+ // verdict
322
+ if (high.length)
323
+ out.push(L ? `\n➤ Sonuç: ${high.length} GERÇEK yüksek-risk açık — acil düzeltilmeli. Detaylı rapor için: /report`
324
+ : `\n➤ Verdict: ${high.length} REAL high-risk issues — fix urgently. Full report: /report`);
325
+ else if (med.length)
326
+ out.push(L ? `\n➤ Sonuç: orta-risk bulgu(lar) var, gözden geçir. Rapor: /report` : `\n➤ Verdict: medium-risk findings, review. Report: /report`);
327
+ else
328
+ out.push(L ? `\n➤ Sonuç: yüksek/orta açık yok; test edilen yüzey büyük ölçüde temiz. (Stored/DOM/auth-arkası kapsam dışı — "temiz" ≠ "%100 güvenli")`
329
+ : `\n➤ Verdict: no high/medium issues; tested surface largely clean. (Stored/DOM/behind-auth out of scope)`);
330
+ return out;
186
331
  }
187
332
  // /recon /scan /xss /sqli — ince runner'ı sürer. Zorunlu yetki onayı + trust 3+ teaser.
188
333
  // Slash'sız komut tespiti: kullanıcı "recon target.com" (slash'sız) yazınca "/recon target.com" öner.
@@ -259,6 +404,7 @@ async function pentestCmd(tool, arg, ctx) {
259
404
  const rep = out.report || {};
260
405
  const lines = [`✓ ${label} ${tt('complete', 'tamamlandı')} — ${rep.target || target}`];
261
406
  const f = rep.findings || [];
407
+ setLastScan(rep.target || target, f); // /report için sakla
262
408
  if (!f.length) {
263
409
  lines.push(tt('No vulnerabilities found — the target looks clean, OR it did not respond/was unreachable (down, redirect, blocking). Try again on a live, genuinely vulnerable target.', 'Zafiyet bulunamadı — hedef temiz görünüyor YA DA yanıt vermedi/erişilemiyor (down, yönlendirme, bloklama). Ayakta + gerçekten zafiyetli bir hedefte tekrar dene.'));
264
410
  }
@@ -334,13 +480,17 @@ async function fullScanCmd(arg, ctx) {
334
480
  }
335
481
  const order = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
336
482
  all.sort((a, b) => (order[a.severity] ?? 9) - (order[b.severity] ?? 9));
337
- const lines = [`✓ ${tt('Full scan complete', 'Tam tarama tamamlandı')} ${target}`];
483
+ setLastScan(target, all); // /report için sakla
484
+ const lines = [];
338
485
  if (!all.length) {
339
- lines.push(tt('No findings across all scanners. Target looks clean or was unreachable.', 'Tüm tarayıcılarda bulgu yok. Hedef temiz görünüyor ya da erişilemedi.'));
486
+ lines.push(`✓ ${tt('Full scan complete', 'Tam tarama tamamlandı')} ${target}`);
487
+ lines.push(tt('No findings across all scanners. Target looks clean or was unreachable. ("clean" ≠ "100% secure")', 'Tüm tarayıcılarda bulgu yok. Hedef temiz görünüyor ya da erişilemedi. ("temiz" ≠ "%100 güvenli")'));
340
488
  }
341
489
  else {
342
- const sev = (s) => all.filter((x) => x.severity === s).length;
343
- lines.push(`${all.length} ${tt('finding(s)', 'bulgu')} — high:${sev('high')} medium:${sev('medium')} low:${sev('low')} info:${sev('info')}`);
490
+ // ÖNCE yorumlu özet (ne ciddi/ne minör/ne yapmalı), SONRA detay liste.
491
+ lines.push(...summarizeScan(target, all));
492
+ lines.push('');
493
+ lines.push(`— ${tt('details', 'detay')} (${all.length} ${tt('finding(s)', 'bulgu')}) —`);
344
494
  for (const x of all.slice(0, 60))
345
495
  lines.push(' ' + formatFinding(x));
346
496
  if (all.length > 60)
@@ -977,6 +1127,9 @@ export async function runSlashCommand(input, ctx) {
977
1127
  case '/fullscan':
978
1128
  await fullScanCmd(arg, ctx);
979
1129
  return true;
1130
+ case '/report':
1131
+ await reportCmd(ctx);
1132
+ return true;
980
1133
  case '/skill': {
981
1134
  const m = (arg || '').trim().split(/\s+/).filter(Boolean);
982
1135
  const sub = (m.shift() || '').toLowerCase();
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.165';
19
+ export const VERSION = '1.0.167';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.165",
3
+ "version": "1.0.167",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {