wormclaude 1.0.190 → 1.0.191

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.
@@ -0,0 +1,188 @@
1
+ // SAST — kaynak kod güvenlik taraması. TAMAMEN YEREL (kod sunucuya gitmez; gizlilik).
2
+ // Desen-tabanlı dedektörler (gizli sır, tehlikeli fonksiyon, SQLi/cmd-injection, zayıf kripto) +
3
+ // bağımlılık-CVE (küratörlü liste). Bulgular dosya:satır + severity + çözüm ile döner.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ const CODE_EXT = new Set(['.py', '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.php', '.rb', '.go',
7
+ '.java', '.kt', '.cs', '.c', '.cc', '.cpp', '.h', '.hpp', '.rs', '.sh', '.bash', '.ps1', '.sql',
8
+ '.html', '.vue', '.svelte', '.env', '.yml', '.yaml', '.tf', '.dockerfile']);
9
+ const SKIP_DIR = new Set(['node_modules', '.git', 'dist', 'build', 'out', 'vendor', '.next', '.venv',
10
+ 'venv', '__pycache__', 'coverage', '.cache', 'target', 'bin', 'obj', '_archives']);
11
+ // ── Gizli SIR desenleri (gitleaks tarzı). Değer env-ref/placeholder/boşsa elenir (FP). ──
12
+ const SECRET_RULES2 = [
13
+ { id: 'AWS access key', re: /\bAKIA[0-9A-Z]{16}\b/, sev: 'critical' },
14
+ { id: 'AWS secret key', re: /aws.{0,20}['"][0-9a-zA-Z/+]{40}['"]/i, sev: 'critical' },
15
+ { id: 'Özel anahtar (PEM)', re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/, sev: 'critical' },
16
+ { id: 'GitHub token', re: /\bgh[pousr]_[0-9A-Za-z]{36}\b/, sev: 'critical' },
17
+ { id: 'Slack token', re: /\bxox[baprs]-[0-9A-Za-z-]{10,}\b/, sev: 'high' },
18
+ { id: 'Google API key', re: /\bAIza[0-9A-Za-z_-]{35}\b/, sev: 'high' },
19
+ { id: 'Stripe key', re: /\b(?:sk|rk)_live_[0-9A-Za-z]{20,}\b/, sev: 'critical' },
20
+ { id: 'JWT (gömülü)', re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/, sev: 'high' },
21
+ { id: 'Genel sır (key/secret/token/password=...)', re: /(?:api[_-]?key|apikey|secret|token|passwd|password|pwd|access[_-]?token|auth[_-]?token|private[_-]?key)\s*[:=]\s*['"][^'"\s]{8,}['"]/i, sev: 'high' },
22
+ ];
23
+ const PLACEHOLDER = /(your|example|placeholder|changeme|xxx+|<[^>]+>|\$\{|process\.env|os\.environ|getenv|import\.meta\.env|dotenv|todo|fixme|test|dummy|sample|redacted|\*\*\*)/i;
24
+ // ── Tehlikeli fonksiyon / kod-deseni dedektörleri ──
25
+ const PATTERN_RULES = [
26
+ { id: 'eval() kullanımı', re: /(?<![.\w$])eval\s*\(/, sev: 'high', cwe: 'CWE-95', fix: 'eval kaldır; JSON.parse / güvenli ayrıştırıcı kullan' },
27
+ { id: 'new Function() (dinamik kod)', re: /\bnew\s+Function\s*\(/, sev: 'high', cwe: 'CWE-95', fix: 'dinamik kod çalıştırmayı kaldır' },
28
+ { id: 'os.system / shell çağrısı', re: /\bos\.system\s*\(|subprocess\.[A-Za-z]+\([^)]*shell\s*=\s*True|child_process|shell_exec\s*\(|\bsystem\s*\(|passthru\s*\(/, sev: 'high', cwe: 'CWE-78', fix: 'kabuk yerine argüman dizisi (shell=False / execFile) kullan; girdiyi asla birleştirme' },
29
+ { id: 'innerHTML / dangerouslySetInnerHTML', re: /\.innerHTML\s*=|dangerouslySetInnerHTML|document\.write\s*\(/, sev: 'medium', cwe: 'CWE-79', fix: 'textContent kullan ya da çıktıyı escape/sanitize et (DOMPurify)' },
30
+ { id: 'pickle/yaml.load (güvensiz deser.)', re: /\bpickle\.loads?\s*\(|yaml\.load\s*\((?![^)]*Safe)|unserialize\s*\(/, sev: 'high', cwe: 'CWE-502', fix: 'güvenli deserializasyon (yaml.safe_load / json); güvenilmeyen veriyi pickle etme' },
31
+ { id: 'Parola için zayıf hash (md5/sha1)', re: /(?:password|passwd|pwd|şifre|sifre|parola)[\s\S]{0,30}(?:md5|sha1)|(?:md5|sha1)\s*\([^)]{0,40}(?:password|passwd|pwd|şifre|sifre|parola)/i, sev: 'medium', cwe: 'CWE-327', fix: 'parola için bcrypt/argon2/scrypt (md5/sha1 kırılabilir)' },
32
+ { id: 'SQL string birleştirme', re: /(?:execute|query|cursor\.execute|\.raw)\s*\(\s*[`'"][^`'"]*(?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)[^`'"]*['"`]?\s*(?:\+|%|\.format|f['"]|\$\{)/i, sev: 'high', cwe: 'CWE-89', fix: 'parametreli sorgu / prepared statement kullan (asla string birleştirme)' },
33
+ { id: 'TLS doğrulama kapalı', re: /verify\s*=\s*False|rejectUnauthorized\s*:\s*false|InsecureSkipVerify\s*:\s*true|CURLOPT_SSL_VERIFYPEER.{0,6}(?:false|0)/i, sev: 'medium', cwe: 'CWE-295', fix: 'sertifika doğrulamayı açık tut' },
34
+ { id: 'Hardcoded localhost/IP+port (config sızıntısı)', re: /(?:password|secret|token)\s*[:=]\s*['"][^'"]{6,}/i, sev: 'low', cwe: 'CWE-798', fix: 'gizli değerleri env/secret-manager\'a taşı' },
35
+ { id: 'debug=True / hata ifşası', re: /debug\s*=\s*True|app\.run\([^)]*debug\s*=\s*True|DEBUG\s*=\s*true/i, sev: 'low', cwe: 'CWE-489', fix: 'üretimde debug kapalı olmalı' },
36
+ ];
37
+ // ── Bağımlılık CVE (küratörlü; tam liste için npm audit / pip-audit öner) ──
38
+ const VULN_DEPS = {
39
+ lodash: { below: '4.17.21', cve: 'CVE-2021-23337 (prototype pollution / cmd inj)', sev: 'high' },
40
+ axios: { below: '0.21.2', cve: 'CVE-2021-3749 (ReDoS/SSRF)', sev: 'medium' },
41
+ minimist: { below: '1.2.6', cve: 'CVE-2021-44906 (prototype pollution)', sev: 'high' },
42
+ 'node-fetch': { below: '2.6.7', cve: 'CVE-2022-0235 (bilgi sızıntısı)', sev: 'medium' },
43
+ express: { below: '4.17.3', cve: 'CVE-2022-24999 (qs DoS)', sev: 'medium' },
44
+ jsonwebtoken: { below: '9.0.0', cve: 'CVE-2022-23529 (zayıf doğrulama)', sev: 'high' },
45
+ flask: { below: '2.2.5', cve: 'CVE-2023-30861 (cookie sızıntısı)', sev: 'medium' },
46
+ requests: { below: '2.31.0', cve: 'CVE-2023-32681 (proxy-auth sızıntısı)', sev: 'medium' },
47
+ };
48
+ function _semverLt(a, b) {
49
+ const pa = a.replace(/[^0-9.]/g, '').split('.').map(Number);
50
+ const pb = b.split('.').map(Number);
51
+ for (let i = 0; i < 3; i++) {
52
+ const x = pa[i] || 0, y = pb[i] || 0;
53
+ if (x < y)
54
+ return true;
55
+ if (x > y)
56
+ return false;
57
+ }
58
+ return false;
59
+ }
60
+ function _walk(dir, out, depth = 0) {
61
+ if (depth > 12 || out.length > 5000)
62
+ return;
63
+ let entries;
64
+ try {
65
+ entries = fs.readdirSync(dir, { withFileTypes: true });
66
+ }
67
+ catch {
68
+ return;
69
+ }
70
+ for (const e of entries) {
71
+ const full = path.join(dir, e.name);
72
+ if (e.isDirectory()) {
73
+ if (!SKIP_DIR.has(e.name) && !e.name.startsWith('.'))
74
+ _walk(full, out, depth + 1);
75
+ }
76
+ else if (e.isFile()) {
77
+ const ext = path.extname(e.name).toLowerCase();
78
+ if (CODE_EXT.has(ext) || /^(dockerfile|\.env)/i.test(e.name))
79
+ out.push(full);
80
+ }
81
+ }
82
+ }
83
+ function _depAudit(root, findings) {
84
+ // package.json
85
+ try {
86
+ const pj = path.join(root, 'package.json');
87
+ if (fs.existsSync(pj)) {
88
+ const j = JSON.parse(fs.readFileSync(pj, 'utf8'));
89
+ const deps = { ...(j.dependencies || {}), ...(j.devDependencies || {}) };
90
+ for (const [name, ver] of Object.entries(deps)) {
91
+ const v = VULN_DEPS[name];
92
+ if (v && typeof ver === 'string' && _semverLt(ver, v.below)) {
93
+ findings.push({ severity: v.sev, type: 'dependency', file: 'package.json', line: 0,
94
+ evidence: `${name}@${ver} — ${v.cve}`, fix: `${name} >= ${v.below}'e yükselt`, cwe: 'CWE-1104' });
95
+ }
96
+ }
97
+ }
98
+ }
99
+ catch { }
100
+ // requirements.txt
101
+ try {
102
+ const rq = path.join(root, 'requirements.txt');
103
+ if (fs.existsSync(rq)) {
104
+ for (const ln of fs.readFileSync(rq, 'utf8').split('\n')) {
105
+ const m = ln.match(/^([A-Za-z0-9_.-]+)\s*==\s*([0-9.]+)/);
106
+ if (m) {
107
+ const v = VULN_DEPS[m[1].toLowerCase()];
108
+ if (v && _semverLt(m[2], v.below))
109
+ findings.push({ severity: v.sev, type: 'dependency', file: 'requirements.txt', line: 0, evidence: `${m[1]}==${m[2]} — ${v.cve}`, fix: `${m[1]} >= ${v.below}`, cwe: 'CWE-1104' });
110
+ }
111
+ }
112
+ }
113
+ }
114
+ catch { }
115
+ }
116
+ /** Bir dizini SAST tara → bulgular (yerel, kod hiçbir yere gitmez). */
117
+ export function auditCode(root, opts) {
118
+ const files = [];
119
+ try {
120
+ if (fs.statSync(root).isFile())
121
+ files.push(root);
122
+ else
123
+ _walk(root, files);
124
+ }
125
+ catch {
126
+ return { findings: [], filesScanned: 0 };
127
+ }
128
+ const max = opts?.maxFiles || 1500;
129
+ const scan = files.slice(0, max);
130
+ const findings = [];
131
+ const seen = new Set();
132
+ for (const f of scan) {
133
+ let text = '';
134
+ try {
135
+ const st = fs.statSync(f);
136
+ if (st.size > 1_500_000)
137
+ continue;
138
+ text = fs.readFileSync(f, 'utf8');
139
+ }
140
+ catch {
141
+ continue;
142
+ }
143
+ const rel = path.relative(root, f) || path.basename(f);
144
+ const base = path.basename(f).toLowerCase();
145
+ const isEnv = base === '.env'; // .env BEKLENEN sır deposu → düşük; .env.bak vb. = SIZINTI
146
+ const isEnvBak = /^\.env\.|\.env\.(bak|old|orig|save|backup|[0-9])|\.env~$/.test(base) || /\.bak\d*$/.test(base);
147
+ const lines = text.split('\n');
148
+ for (let i = 0; i < lines.length; i++) {
149
+ const ln = lines[i];
150
+ if (ln.length > 4000)
151
+ continue;
152
+ const trimmed = ln.trimStart();
153
+ const isComment = /^(#|\/\/|\*|\/\*|<!--|--\s|;)/.test(trimmed); // yorum satırı (FP kaynağı)
154
+ // sırlar (yorumda da sayar — sızan anahtar önemli)
155
+ for (const r of SECRET_RULES2) {
156
+ if (r.re.test(ln) && !PLACEHOLDER.test(ln)) {
157
+ const key = rel + ':secret:' + r.id + ':' + i;
158
+ if (seen.has(key))
159
+ continue;
160
+ seen.add(key);
161
+ let sev = r.sev;
162
+ if (isEnv)
163
+ sev = 'info'; // .env'de sır beklenir (ama .gitignore'da olmalı)
164
+ else if (isEnvBak)
165
+ sev = 'high'; // yedek dosyada sır = gerçek sızıntı
166
+ findings.push({ severity: sev, type: 'secret', file: rel, line: i + 1,
167
+ evidence: `${r.id} — ${ln.trim().slice(0, 80)}`,
168
+ fix: isEnv ? '.env .gitignore\'da mı doğrula; repoya girmesin' : (isEnvBak ? 'YEDEK dosyayı SİL (sır sızdırıyor) + anahtarı ROTATE et' : 'sırrı koddan kaldır → env/secret-manager; sızan anahtarı ROTATE et'), cwe: 'CWE-798' });
169
+ }
170
+ }
171
+ if (isComment)
172
+ continue; // tehlikeli-fonksiyon dedektörü yorumda çalışmaz (FP)
173
+ for (const r of PATTERN_RULES) {
174
+ if (r.re.test(ln)) {
175
+ const key = rel + ':' + r.id + ':' + i;
176
+ if (seen.has(key))
177
+ continue;
178
+ seen.add(key);
179
+ findings.push({ severity: r.sev, type: 'code', file: rel, line: i + 1, evidence: `${r.id} — ${ln.trim().slice(0, 80)}`, fix: r.fix, cwe: r.cwe });
180
+ }
181
+ }
182
+ }
183
+ }
184
+ _depAudit(root, findings);
185
+ const order = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
186
+ findings.sort((a, b) => order[a.severity] - order[b.severity]);
187
+ return { findings, filesScanned: scan.length };
188
+ }
package/dist/commands.js CHANGED
@@ -60,6 +60,7 @@ export const COMMANDS = [
60
60
  { name: '/dirscan', desc: '[seviye 3+] dizin/içerik keşfi (yönetim/api/yedek/sızıntı yolları): /dirscan <alan>' },
61
61
  { name: '/report', desc: 'son taramadan profesyonel pentest raporu üret (CVSS/etki/çözüm, dosyaya yazar)' },
62
62
  { name: '/fullscan', desc: '[seviye 3+] tam kapsamlı DETERMİNİSTİK tarama: recon+XSS+SQLi tek komutta: /fullscan <hedef>' },
63
+ { name: '/audit', desc: 'kaynak kod güvenlik denetimi (SAST — sır/açık/CVE, yerel): /audit [klasör]' },
63
64
  { name: '/exploit', desc: 'yetkili testte doğrulanmış PoC üret: /exploit <cve|açıklama>' },
64
65
  { name: '/harden', desc: 'savunma çıktısı üret (YARA/Sigma + yama önerisi): /harden <hedef|tehdit>' },
65
66
  { name: '/phish', desc: '[yetkili] phishing simülasyonu: pretext + e-posta şablonu: /phish <hedef|senaryo>' },
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.190';
19
+ export const VERSION = '1.0.191';
package/dist/tools.js CHANGED
@@ -242,6 +242,20 @@ export const toolSchemas = [
242
242
  },
243
243
  },
244
244
  },
245
+ {
246
+ type: 'function',
247
+ function: {
248
+ name: 'CodeAudit',
249
+ description: "Static code security audit (SAST) — scan SOURCE CODE files locally for vulnerabilities. Use when the user asks to audit/review their code/project/repo for security (\"kodumu güvenlik için tara\", \"audit\", \"sast\", \"bu projeyi tara\", \"açık var mı kodda\", \"secret/sır kontrolü\"). Reads code FILES (does NOT send HTTP requests like SecurityScan). Finds: hardcoded secrets/API keys, dangerous functions (eval/exec/os.system), SQL/command injection patterns, weak crypto, insecure deserialization, vulnerable dependencies. Fully LOCAL — code never leaves the machine. Returns findings with file:line + severity + fix.",
250
+ parameters: {
251
+ type: 'object',
252
+ properties: {
253
+ path: { type: 'string', description: 'Directory or file to audit. Default: current working directory. e.g. ".", "src/", "C:/project".' },
254
+ },
255
+ required: [],
256
+ },
257
+ },
258
+ },
245
259
  {
246
260
  type: 'function',
247
261
  function: {
@@ -626,7 +640,7 @@ const computerToolSchemas = [
626
640
  // çağırıp işi yapar, planı ekrana basmaz. (Gerekirse buraya yeni araç eklenir.)
627
641
  const CORE_TOOLS = new Set([
628
642
  'Bash', 'PowerShell', 'Read', 'Write', 'Edit', 'Glob', 'Grep',
629
- 'WebFetch', 'WebSearch', 'Sleep', 'SecurityScan',
643
+ 'WebFetch', 'WebSearch', 'Sleep', 'SecurityScan', 'CodeAudit',
630
644
  'TaskOutput', // uzun komutları arka planda çalıştırıp bitişini yoklamak için (run_in_background)
631
645
  'AskUserQuestion', // GERİ EKLENDİ: FLAT şema (options: string[]) ile 32B temiz çağrı üretiyor
632
646
  // (test edildi). Eski degenere NESTED şemadandı ([{label,description}]). Handler string→{label}
@@ -654,6 +668,7 @@ const TOOL_META = {
654
668
  Key: { needsPermission: true, validate: (a) => (a && a.keys ? null : 'keys gerekli') },
655
669
  Scroll: { needsPermission: true, validate: (a) => (a && a.direction ? null : 'direction gerekli') },
656
670
  WebSearch: { readOnly: true, validate: (a) => (a && a.query ? null : 'query gerekli') },
671
+ CodeAudit: { readOnly: true, concurrencySafe: true },
657
672
  TodoWrite: { readOnly: true, validate: (a) => (a && Array.isArray(a.todos) ? null : 'todos dizisi gerekli') },
658
673
  SaveMemory: { validate: (a) => (a && a.fact && String(a.fact).trim() ? null : 'fact gerekli') },
659
674
  PowerShell: { needsPermission: true, validate: (a) => (a && a.command ? null : 'command gerekli') },
@@ -915,6 +930,41 @@ async function execOne(call, hooks) {
915
930
  return { ok: false, output: 'Tarama hatası: ' + (e?.message || e), args };
916
931
  }
917
932
  }
933
+ // 3.45) SAST — kaynak kod güvenlik denetimi (YEREL; kod sunucuya gitmez)
934
+ if (call.name === 'CodeAudit') {
935
+ try {
936
+ const { auditCode } = await import('./codeaudit.js');
937
+ const fsMod = await import('node:fs');
938
+ let root = String(args?.path || '').trim() || process.cwd();
939
+ // Model var-olmayan/uydurma yol verebilir → yoksa çalışma dizinine düş (boş sonuç olmasın).
940
+ try {
941
+ if (!fsMod.existsSync(root))
942
+ root = process.cwd();
943
+ }
944
+ catch {
945
+ root = process.cwd();
946
+ }
947
+ const { findings, filesScanned } = auditCode(root);
948
+ let out = `Kod güvenlik denetimi (SAST) — ${root}\n${filesScanned} dosya tarandı, ${findings.length} bulgu.\n`;
949
+ if (!findings.length) {
950
+ out += 'Bilinen güvenlik deseni bulunamadı (temiz ya da kapsam dışı). NOT: SAST desen-tabanlıdır, her açığı bulmaz.';
951
+ }
952
+ else {
953
+ const bySev = {};
954
+ for (const f of findings)
955
+ bySev[f.severity] = (bySev[f.severity] || 0) + 1;
956
+ out += 'Özet: ' + Object.entries(bySev).map(([s, n]) => `${n} ${s}`).join(' · ') + '\n';
957
+ out += findings.slice(0, 80).map((f) => `[${f.severity.toUpperCase()}]${f.cwe ? ' ' + f.cwe : ''} ${f.type} — ${f.file}:${f.line}\n ${f.evidence}\n çözüm: ${f.fix}`).join('\n');
958
+ if (findings.length > 80)
959
+ out += `\n… +${findings.length - 80} bulgu daha`;
960
+ }
961
+ out += '\n[sast-done] Kod denetimi bitti. Bulguları kullanıcıya severity-sıralı özetle (en kritikten) + öncelikli düzeltmeler. Derin bağımlılık taraması için "npm audit"/"pip-audit" öner.';
962
+ return { ok: true, output: out, args };
963
+ }
964
+ catch (e) {
965
+ return { ok: false, output: 'Kod denetimi hatası: ' + (e?.message || e), args };
966
+ }
967
+ }
918
968
  // 3.5) Komut güvenliği (Bash/PowerShell) — cmdsec: deny→blokla, allow→izinsiz, confirm→izin akışı
919
969
  if ((call.name === 'Bash' || call.name === 'PowerShell') && args && args.command) {
920
970
  const chk = checkCommand(String(args.command), { shell: call.name === 'PowerShell' ? 'powershell' : 'bash' });
@@ -1065,6 +1115,8 @@ export function toolLabel(name, args) {
1065
1115
  return `Skill(${args.name})`;
1066
1116
  if (name === 'WebSearch')
1067
1117
  return `🔍 Web arandı: ${String(args.query).slice(0, 50)}`;
1118
+ if (name === 'CodeAudit')
1119
+ return `🛡️ Kod denetimi (SAST): ${String(args.path || '.').slice(0, 50)}`;
1068
1120
  if (name === 'TodoWrite')
1069
1121
  return `TodoWrite(${(args.todos || []).length} öğe)`;
1070
1122
  if (name === 'PowerShell')
package/dist/tui.js CHANGED
@@ -836,6 +836,12 @@ export async function runTui() {
836
836
  quit();
837
837
  return;
838
838
  }
839
+ // /audit — kaynak kod güvenlik denetimi (SAST, yerel); modele CodeAudit çağırt
840
+ if (tok === '/audit') {
841
+ const a = v.slice(tok.length).trim() || '.';
842
+ runTurn(`CodeAudit aracıyla şu yolu kaynak kod güvenliği için tara (SAST): ${a}. Bulguları severity-sıralı özetle + öncelikli düzeltmeler.`, v);
843
+ return;
844
+ }
839
845
  // /exploit · /harden — aktif tarama değil, model-üretim (PoC / YARA-Sigma) → çerçeveli istemle modele
840
846
  if (tok === '/exploit') {
841
847
  const a = v.slice(tok.length).trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.190",
3
+ "version": "1.0.191",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {