wormclaude 1.0.190 → 1.0.192
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/codeaudit.js +188 -0
- package/dist/commands.js +1 -0
- package/dist/theme.js +1 -1
- package/dist/tools.js +55 -3
- package/dist/tui.js +6 -0
- package/package.json +1 -1
|
@@ -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
package/dist/tools.js
CHANGED
|
@@ -230,7 +230,7 @@ export const toolSchemas = [
|
|
|
230
230
|
parameters: {
|
|
231
231
|
type: 'object',
|
|
232
232
|
properties: {
|
|
233
|
-
tool: { type: 'string', enum: ['full', 'recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor', 'portscan', 'osint'], description: 'full → tam kapsamlı web
|
|
233
|
+
tool: { type: 'string', enum: ['full', 'recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor', 'portscan', 'osint', 'cloudscan'], description: 'full → tam kapsamlı web taraması; recon/scan/dirscan/portscan/osint/cloudscan → domain/host/şirket; xss/sqli/idor → parameterized URL. portscan = TCP port/servis. osint = DNS + e-posta güvenliği (SPF/DMARC). cloudscan = açık bulut bucket keşfi (S3/GCP/Azure — şirket/domain adından aday üretip public erişim test eder; "bucket tara"/"s3 tara"/"açık depolama"/"cloud" denince)' },
|
|
234
234
|
target: { type: 'string', description: 'Domain for recon/scan/dirscan (example.com) OR parameterized URL for xss/sqli (https://example.com/p?id=1)' },
|
|
235
235
|
cookie: { type: 'string', description: 'Optional. Session cookie for AUTHENTICATED / behind-login scanning, e.g. "sessionid=abc123; csrftoken=xyz". The user copies it from their logged-in browser (DevTools → Application → Cookies, or the Cookie request header). Sent only to the target from the user IP — never stored on the server. Provide it whenever the user wants to scan pages behind a login.' },
|
|
236
236
|
auth_header: { type: 'string', description: 'Optional. Authorization header value for token-based authenticated scanning, e.g. "Bearer eyJhbGci..." or "Basic dXNlcjpwYXNz". Use instead of / together with cookie when the site authenticates via a token header.' },
|
|
@@ -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') },
|
|
@@ -737,7 +752,7 @@ async function execOne(call, hooks) {
|
|
|
737
752
|
if (call.name === 'SecurityScan') {
|
|
738
753
|
let tool = String(args?.tool || '').toLowerCase().trim();
|
|
739
754
|
const target = String(args?.target || '').trim();
|
|
740
|
-
if (!['recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor', 'portscan', 'osint', 'full', 'fullscan'].includes(tool) || !target) {
|
|
755
|
+
if (!['recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor', 'portscan', 'osint', 'cloud', 'cloudscan', 'full', 'fullscan'].includes(tool) || !target) {
|
|
741
756
|
return { ok: false, output: 'SecurityScan: tool (recon|scan|xss|sqli|dirscan|full) ve target gerekli.', args };
|
|
742
757
|
}
|
|
743
758
|
if (tool === 'scan')
|
|
@@ -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();
|