pentesting 0.8.40 → 0.8.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +115 -18
  2. package/dist/index.js +1451 -17
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -32,8 +32,8 @@ import TextInput from "ink-text-input";
32
32
  import Spinner from "ink-spinner";
33
33
 
34
34
  // src/core/agent/autonomous-agent.ts
35
- import Anthropic from "@anthropic-ai/sdk";
36
- import { EventEmitter as EventEmitter4 } from "events";
35
+ import Anthropic3 from "@anthropic-ai/sdk";
36
+ import { EventEmitter as EventEmitter7 } from "events";
37
37
 
38
38
  // src/core/prompts/autonomous-prompt.ts
39
39
  var AUTONOMOUS_HACKING_PROMPT = `You are Pentesting, an elite autonomous penetration testing AI designed for CTF competitions and professional security assessments. You operate with minimal human intervention, making intelligent decisions, adapting to obstacles, and persistently pursuing objectives until complete system compromise.
@@ -140,13 +140,61 @@ If you notice you're repeating the same actions without progress:
140
140
 
141
141
  Key tools for each phase:
142
142
  - Recon: whois, dig, nslookup, theHarvester, amass
143
+ - Subdomain: ffuf, subfinder, assetfinder, amass, gobuster dns
143
144
  - Scan: nmap, masscan, rustscan
144
- - Web: gobuster, ffuf, nikto, sqlmap, burp
145
+ - Directory: ffuf, gobuster, dirsearch, feroxbuster
146
+ - Web: nikto, sqlmap, wpscan, nuclei
145
147
  - Exploit: metasploit, searchsploit, custom scripts
146
148
  - PrivEsc: linpeas, winpeas, GTFOBins
147
149
  - Post: mimikatz, bloodhound, chisel
148
150
  </tool_usage_guidelines>
149
151
 
152
+ <mandatory_autonomous_execution>
153
+ CRITICAL: You MUST execute tools immediately without asking. DO NOT:
154
+ - Ask "should I run ffuf?" - JUST RUN IT
155
+ - Say "I can use gobuster" - JUST USE IT
156
+ - Explain what you could do - JUST DO IT
157
+ - Wait for user confirmation - ACT NOW
158
+ - Write bash scripts manually when MCP tools exist - USE THE TOOLS
159
+
160
+ YOU HAVE THESE MCP TOOLS - USE THEM DIRECTLY:
161
+ - ffuf \u2192 subdomain and directory fuzzing
162
+ - subfinder \u2192 passive subdomain discovery
163
+ - amass \u2192 comprehensive subdomain enum
164
+ - nuclei \u2192 vulnerability scanning
165
+ - nikto \u2192 web server scanning
166
+ - whatweb \u2192 technology fingerprinting
167
+ - httpx \u2192 HTTP probing
168
+ - waybackurls \u2192 historical URL discovery
169
+ - feroxbuster \u2192 recursive directory scanning
170
+ - rustscan \u2192 fast port scanning
171
+ - nmap_scan \u2192 detailed port/service scan
172
+ - hydra \u2192 credential bruteforce
173
+ - sqlmap (sql_injection) \u2192 SQL injection
174
+ - gobuster (directory_bruteforce) \u2192 directory enumeration
175
+
176
+ TRIGGER \u2192 ACTION MAPPING:
177
+ "\uC11C\uBE0C\uB3C4\uBA54\uC778" or "subdomain" \u2192 IMMEDIATELY use ffuf tool with mode=subdomain
178
+ "\uB514\uB809\uD1A0\uB9AC" or "directory" or "path" \u2192 IMMEDIATELY use ffuf tool with mode=directory
179
+ "\uC2A4\uCE94" or "scan" \u2192 IMMEDIATELY use rustscan then nmap_scan
180
+ "\uCDE8\uC57D\uC810" or "vuln" \u2192 IMMEDIATELY use nuclei tool
181
+ "\uAE30\uC220\uC2A4\uD0DD" or "tech" \u2192 IMMEDIATELY use whatweb tool
182
+ "\uD788\uC2A4\uD1A0\uB9AC" or "wayback" \u2192 IMMEDIATELY use waybackurls tool
183
+ "\uBE0C\uB8E8\uD2B8\uD3EC\uC2A4" or "bruteforce" \u2192 IMMEDIATELY use hydra tool
184
+ "SQL" or "\uC778\uC81D\uC158" \u2192 IMMEDIATELY use sql_injection tool
185
+
186
+ EXAMPLE - User says "\uC11C\uBE0C\uB3C4\uBA54\uC778 \uCC3E\uC544":
187
+ WRONG: for sub in www mail ftp; do host $sub.domain.com; done
188
+ RIGHT: Use ffuf tool with url=https://FUZZ.domain.com, mode=subdomain
189
+
190
+ EXAMPLE - User says "\uB514\uB809\uD1A0\uB9AC \uC2A4\uCE94\uD574":
191
+ WRONG: curl https://domain.com/admin
192
+ RIGHT: Use ffuf tool with url=https://domain.com/FUZZ, mode=directory
193
+
194
+ NEVER write manual bash loops when MCP tools exist!
195
+ ALWAYS prefer MCP tools over bash commands!
196
+ </mandatory_autonomous_execution>
197
+
150
198
  <output_format>
151
199
  Always structure your thinking clearly:
152
200
 
@@ -220,6 +268,115 @@ Analyze your situation honestly:
220
268
  - Manual testing vs. automated?
221
269
 
222
270
  Based on this reflection, propose 3 completely different approaches to try next.`;
271
+ var STRATEGY_PLANNING_PROMPT = `You are the strategic coordinator for this penetration test. Before executing ANY action, validate the strategy.
272
+
273
+ ## STRATEGIC VALIDATION PROCESS
274
+
275
+ ### 1. INTENT VERIFICATION
276
+ \`\`\`
277
+ [INTENT CHECK]
278
+ - What is the user's objective?
279
+ - What phase are we in?
280
+ - What have we accomplished so far?
281
+ - What is the immediate goal?
282
+ \`\`\`
283
+
284
+ ### 2. PLAN FORMULATION
285
+ \`\`\`
286
+ [PLAN PROPOSAL]
287
+ Given the current state, I propose:
288
+ 1. [Primary approach] - Expected outcome: X
289
+ 2. [Backup approach] - If primary fails: Y
290
+ 3. [Alternative] - If we need to pivot: Z
291
+ \`\`\`
292
+
293
+ ### 3. PLAN VALIDATION (Self-Check)
294
+ \`\`\`
295
+ [VALIDATION]
296
+ \u25A1 Is this aligned with BFS (surface mapping first)?
297
+ \u25A1 Have I completed reconnaissance before deep testing?
298
+ \u25A1 Is this the highest ROI action right now?
299
+ \u25A1 Am I avoiding rabbit holes (SSL testing too early, etc.)?
300
+ \u25A1 Is the tool/command correct for this task?
301
+ \`\`\`
302
+
303
+ ### 4. AGENT COORDINATION
304
+ When switching agents or techniques:
305
+ \`\`\`
306
+ [HANDOFF]
307
+ From: [current agent/approach]
308
+ To: [next agent/approach]
309
+ Reason: [why this switch makes sense]
310
+ Context passed: [what the next agent needs to know]
311
+ \`\`\`
312
+
313
+ ### 5. DECISION LOG
314
+ After each significant decision:
315
+ \`\`\`
316
+ [DECISION]
317
+ Action: [what was done]
318
+ Result: [what happened]
319
+ Learning: [what we now know]
320
+ Next: [logical next step]
321
+ \`\`\`
322
+
323
+ ## ANTI-PATTERNS TO DETECT AND PREVENT
324
+
325
+ - \u274C SSL/TLS testing before full surface mapping
326
+ - \u274C Deep diving one endpoint before discovering others
327
+ - \u274C Brute force without any intelligence
328
+ - \u274C Repeating failed commands with minor changes
329
+ - \u274C Ignoring discovered information
330
+ - \u274C Skipping subdomain/directory enumeration
331
+
332
+ ## SUCCESS PATTERNS TO FOLLOW
333
+
334
+ - \u2705 Port scan \u2192 Web discovery (subdomain + directory) \u2192 Technology detection \u2192 CVE check
335
+ - \u2705 Find all endpoints first, then prioritize by value
336
+ - \u2705 Use discovered info (usernames, versions) in subsequent attacks
337
+ - \u2705 Pivot quickly when stuck (max 3 attempts per approach)
338
+ - \u2705 Document findings as we go`;
339
+ var AGENT_COLLABORATION_PROMPT = `When collaborating with other specialized agents:
340
+
341
+ ## AGENT ROLES
342
+ - **Recon Agent**: Surface discovery, OSINT, subdomain/directory enumeration
343
+ - **Exploit Agent**: Vulnerability exploitation, payload delivery
344
+ - **PrivEsc Agent**: Privilege escalation on compromised hosts
345
+ - **Web Agent**: Web application testing, injection attacks
346
+ - **Crypto Agent**: Cryptographic analysis, hash cracking
347
+
348
+ ## COLLABORATION PROTOCOL
349
+
350
+ ### 1. TASK DELEGATION
351
+ \`\`\`
352
+ [DELEGATE]
353
+ From: [current agent]
354
+ To: [specialized agent]
355
+ Task: [specific task]
356
+ Expected output: [what we need back]
357
+ \`\`\`
358
+
359
+ ### 2. RESULTS HANDOFF
360
+ \`\`\`
361
+ [HANDOFF]
362
+ Agent: [which agent completed]
363
+ Findings: [key discoveries]
364
+ Recommended next: [what should happen next]
365
+ \`\`\`
366
+
367
+ ### 3. CONFLICT RESOLUTION
368
+ When agents disagree on approach:
369
+ - Consider ROI of each approach
370
+ - Check if one requires prerequisites the other provides
371
+ - Default to BFS (broader coverage) over DFS (deep focus)
372
+ - Escalate to user only if truly ambiguous
373
+
374
+ ### 4. KNOWLEDGE SHARING
375
+ All agents maintain shared context:
376
+ - Discovered hosts and services
377
+ - Obtained credentials
378
+ - Failed approaches (don't repeat)
379
+ - Current attack surface map`;
223
380
 
224
381
  // src/core/tools/tool-definitions.ts
225
382
  var SYSTEM_TOOLS = [
@@ -387,6 +544,134 @@ Use for:
387
544
  }
388
545
  }
389
546
  ];
547
+ var DNS_TOOLS = [
548
+ {
549
+ name: TOOL_NAME.FFUF,
550
+ description: `FFUF - Fast web fuzzer. USE THIS for subdomain and directory enumeration.
551
+
552
+ SUBDOMAIN ENUMERATION:
553
+ ffuf -u https://FUZZ.domain.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -mc 200,301,302,403
554
+
555
+ DIRECTORY ENUMERATION:
556
+ ffuf -u https://domain.com/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
557
+
558
+ VHOST DISCOVERY:
559
+ ffuf -u https://domain.com -H "Host: FUZZ.domain.com" -w wordlist.txt
560
+
561
+ PARAMETER FUZZING:
562
+ ffuf -u https://domain.com/page?FUZZ=value -w params.txt
563
+
564
+ OPTIONS:
565
+ - -mc: Match HTTP status codes
566
+ - -fc: Filter HTTP status codes
567
+ - -fs: Filter response size
568
+ - -fw: Filter word count
569
+ - -t: Threads (default 40)
570
+ - -recursion: Enable recursion
571
+ - -e: Extensions (.php,.html,.txt)
572
+
573
+ CRITICAL: This is your PRIMARY tool for web enumeration. USE IT IMMEDIATELY when asked to find subdomains or directories.`,
574
+ input_schema: {
575
+ type: "object",
576
+ properties: {
577
+ url: { type: "string", description: "Target URL with FUZZ keyword" },
578
+ wordlist: { type: "string", description: "Wordlist path (default: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt)" },
579
+ mode: { type: "string", enum: ["subdomain", "directory", "vhost", "parameter"], description: "Fuzzing mode" },
580
+ match_codes: { type: "string", description: 'Status codes to match (e.g., "200,301,302,403")' },
581
+ filter_codes: { type: "string", description: 'Status codes to filter (e.g., "404")' },
582
+ filter_size: { type: "string", description: "Response size to filter" },
583
+ threads: { type: "number", description: "Threads (default: 40)" },
584
+ extensions: { type: "string", description: 'Extensions to append (e.g., "php,html,txt")' },
585
+ headers: { type: "string", description: "Custom headers" },
586
+ recursion: { type: "boolean", description: "Enable recursion" }
587
+ },
588
+ required: ["url"]
589
+ }
590
+ },
591
+ {
592
+ name: TOOL_NAME.SUBFINDER,
593
+ description: `Subfinder - Passive subdomain discovery tool.
594
+
595
+ USAGE:
596
+ subfinder -d domain.com -o subdomains.txt
597
+
598
+ OPTIONS:
599
+ - -d: Domain to find subdomains for
600
+ - -o: Output file
601
+ - -all: Use all sources
602
+ - -silent: Silent mode (only subdomains)
603
+ - -recursive: Recursive subdomain discovery
604
+
605
+ Great for OSINT-based subdomain discovery without active scanning.`,
606
+ input_schema: {
607
+ type: "object",
608
+ properties: {
609
+ domain: { type: "string", description: "Target domain" },
610
+ output: { type: "string", description: "Output file path" },
611
+ all_sources: { type: "boolean", description: "Use all sources" },
612
+ recursive: { type: "boolean", description: "Recursive discovery" },
613
+ silent: { type: "boolean", description: "Silent mode" }
614
+ },
615
+ required: ["domain"]
616
+ }
617
+ },
618
+ {
619
+ name: TOOL_NAME.AMASS,
620
+ description: `Amass - In-depth subdomain enumeration.
621
+
622
+ MODES:
623
+ - enum: Subdomain enumeration
624
+ - intel: Gather intel on organization
625
+ - track: Track changes over time
626
+
627
+ USAGE:
628
+ amass enum -d domain.com -o output.txt
629
+ amass enum -passive -d domain.com (passive only)
630
+ amass enum -active -d domain.com (active probing)
631
+
632
+ Most comprehensive but slower than subfinder.`,
633
+ input_schema: {
634
+ type: "object",
635
+ properties: {
636
+ mode: { type: "string", enum: ["enum", "intel", "track"], description: "Amass mode" },
637
+ domain: { type: "string", description: "Target domain" },
638
+ passive: { type: "boolean", description: "Passive enumeration only" },
639
+ active: { type: "boolean", description: "Active DNS resolution" },
640
+ output: { type: "string", description: "Output file" }
641
+ },
642
+ required: ["domain"]
643
+ }
644
+ },
645
+ {
646
+ name: TOOL_NAME.FEROXBUSTER,
647
+ description: `Feroxbuster - Fast, recursive content discovery tool.
648
+
649
+ USAGE:
650
+ feroxbuster -u https://domain.com -w wordlist.txt
651
+
652
+ OPTIONS:
653
+ - -u: Target URL
654
+ - -w: Wordlist
655
+ - -x: Extensions (php,html,txt)
656
+ - -t: Threads
657
+ - -d: Recursion depth
658
+ - --auto-tune: Automatic rate limiting
659
+
660
+ Faster than gobuster with built-in recursion.`,
661
+ input_schema: {
662
+ type: "object",
663
+ properties: {
664
+ url: { type: "string", description: "Target URL" },
665
+ wordlist: { type: "string", description: "Wordlist path" },
666
+ extensions: { type: "string", description: 'Extensions (e.g., "php,html")' },
667
+ threads: { type: "number", description: "Threads" },
668
+ depth: { type: "number", description: "Recursion depth" },
669
+ status_codes: { type: "string", description: "Status codes to include" }
670
+ },
671
+ required: ["url"]
672
+ }
673
+ }
674
+ ];
390
675
  var SERVICE_TOOLS = [
391
676
  {
392
677
  name: TOOL_NAME.ZONE_TRANSFER,
@@ -1060,6 +1345,150 @@ Use for:
1060
1345
  },
1061
1346
  required: ["url", "action"]
1062
1347
  }
1348
+ },
1349
+ {
1350
+ name: TOOL_NAME.NUCLEI,
1351
+ description: `Nuclei - Fast vulnerability scanner with templates.
1352
+
1353
+ CRITICAL: Use this for automated vulnerability scanning.
1354
+
1355
+ USAGE:
1356
+ nuclei -u https://target.com -t cves/
1357
+ nuclei -u https://target.com -t exposures/
1358
+ nuclei -l urls.txt -t technologies/
1359
+
1360
+ TEMPLATE CATEGORIES:
1361
+ - cves: Known CVE exploits
1362
+ - vulnerabilities: Generic vulns
1363
+ - exposures: Sensitive file exposure
1364
+ - misconfigurations: Config issues
1365
+ - technologies: Tech detection
1366
+ - default-logins: Default credentials
1367
+
1368
+ OPTIONS:
1369
+ - -t: Template path/directory
1370
+ - -severity: Filter by severity (critical,high,medium,low)
1371
+ - -o: Output file
1372
+ - -silent: Silent mode`,
1373
+ input_schema: {
1374
+ type: "object",
1375
+ properties: {
1376
+ target: { type: "string", description: "Target URL or file with URLs" },
1377
+ templates: { type: "string", description: "Template path (e.g., cves/, exposures/)" },
1378
+ severity: { type: "string", description: "Severity filter (critical,high,medium,low)" },
1379
+ output: { type: "string", description: "Output file" },
1380
+ silent: { type: "boolean", description: "Silent mode" }
1381
+ },
1382
+ required: ["target"]
1383
+ }
1384
+ },
1385
+ {
1386
+ name: TOOL_NAME.NIKTO,
1387
+ description: `Nikto - Web server vulnerability scanner.
1388
+
1389
+ Scans for:
1390
+ - Dangerous files/CGIs
1391
+ - Outdated software versions
1392
+ - Server configuration issues
1393
+ - Default files and programs
1394
+
1395
+ USAGE:
1396
+ nikto -h https://target.com
1397
+ nikto -h target.com -port 8080
1398
+
1399
+ OPTIONS:
1400
+ - -h: Target host
1401
+ - -port: Port (default 80)
1402
+ - -ssl: Force SSL
1403
+ - -Tuning: Scan tuning (1-9, x)`,
1404
+ input_schema: {
1405
+ type: "object",
1406
+ properties: {
1407
+ target: { type: "string", description: "Target URL/IP" },
1408
+ port: { type: "number", description: "Port number" },
1409
+ ssl: { type: "boolean", description: "Force SSL" },
1410
+ tuning: { type: "string", description: "Scan tuning options" },
1411
+ output: { type: "string", description: "Output file" }
1412
+ },
1413
+ required: ["target"]
1414
+ }
1415
+ },
1416
+ {
1417
+ name: TOOL_NAME.WHATWEB,
1418
+ description: `WhatWeb - Web technology fingerprinting.
1419
+
1420
+ Identifies:
1421
+ - CMS (WordPress, Joomla, Drupal)
1422
+ - Web frameworks
1423
+ - JavaScript libraries
1424
+ - Web servers
1425
+ - Plugins and versions
1426
+
1427
+ USAGE:
1428
+ whatweb https://target.com
1429
+ whatweb -a 3 https://target.com # Aggressive mode
1430
+
1431
+ AGGRESSION LEVELS:
1432
+ - 1: Stealthy (default)
1433
+ - 3: Aggressive
1434
+ - 4: Heavy`,
1435
+ input_schema: {
1436
+ type: "object",
1437
+ properties: {
1438
+ target: { type: "string", description: "Target URL" },
1439
+ aggression: { type: "number", description: "Aggression level (1-4)" },
1440
+ verbose: { type: "boolean", description: "Verbose output" }
1441
+ },
1442
+ required: ["target"]
1443
+ }
1444
+ },
1445
+ {
1446
+ name: TOOL_NAME.HTTPX,
1447
+ description: `httpx - Fast HTTP toolkit for probing.
1448
+
1449
+ USAGE:
1450
+ echo "subdomain.target.com" | httpx
1451
+ httpx -l urls.txt -status-code -title
1452
+
1453
+ OPTIONS:
1454
+ - -status-code: Show status codes
1455
+ - -title: Extract page titles
1456
+ - -tech-detect: Technology detection
1457
+ - -follow-redirects: Follow redirects
1458
+ - -threads: Concurrent threads`,
1459
+ input_schema: {
1460
+ type: "object",
1461
+ properties: {
1462
+ target: { type: "string", description: "Target URL or file" },
1463
+ status_code: { type: "boolean", description: "Show status codes" },
1464
+ title: { type: "boolean", description: "Extract titles" },
1465
+ tech_detect: { type: "boolean", description: "Tech detection" },
1466
+ follow_redirects: { type: "boolean", description: "Follow redirects" }
1467
+ },
1468
+ required: ["target"]
1469
+ }
1470
+ },
1471
+ {
1472
+ name: TOOL_NAME.WAYBACKURLS,
1473
+ description: `Waybackurls - Fetch URLs from Wayback Machine.
1474
+
1475
+ Reveals:
1476
+ - Historical endpoints
1477
+ - Hidden parameters
1478
+ - Old files and paths
1479
+ - API endpoints
1480
+
1481
+ USAGE:
1482
+ echo "target.com" | waybackurls
1483
+ waybackurls target.com | grep -E "\\.js$" # Find JS files`,
1484
+ input_schema: {
1485
+ type: "object",
1486
+ properties: {
1487
+ domain: { type: "string", description: "Target domain" },
1488
+ output: { type: "string", description: "Output file" }
1489
+ },
1490
+ required: ["domain"]
1491
+ }
1063
1492
  }
1064
1493
  ];
1065
1494
  var EXPLOIT_TOOLS = [
@@ -1302,6 +1731,7 @@ var REPORT_TOOLS = [
1302
1731
  var ALL_TOOLS = [
1303
1732
  ...SYSTEM_TOOLS,
1304
1733
  ...NETWORK_TOOLS,
1734
+ ...DNS_TOOLS,
1305
1735
  ...SERVICE_TOOLS,
1306
1736
  ...WINDOWS_TOOLS,
1307
1737
  ...WEB_TOOLS,
@@ -4150,6 +4580,868 @@ function buildAgentSystemPrompt(basePrompt, agent) {
4150
4580
  ${agent.systemPrompt}`;
4151
4581
  }
4152
4582
 
4583
+ // src/core/agent/agent-orchestrator.ts
4584
+ import Anthropic from "@anthropic-ai/sdk";
4585
+ import { EventEmitter as EventEmitter4 } from "events";
4586
+ var ORCHESTRATOR_EVENT = {
4587
+ AGENT_START: "agent_start",
4588
+ AGENT_COMPLETE: "agent_complete",
4589
+ AGENT_ERROR: "agent_error",
4590
+ ALL_COMPLETE: "all_complete",
4591
+ FINDING: "finding"
4592
+ };
4593
+ var AgentOrchestrator = class extends EventEmitter4 {
4594
+ client;
4595
+ agents = /* @__PURE__ */ new Map();
4596
+ initialized = false;
4597
+ constructor(apiKey) {
4598
+ super();
4599
+ this.client = new Anthropic({
4600
+ apiKey: apiKey || LLM_API_KEY || process.env.PENTEST_API_KEY,
4601
+ baseURL: LLM_BASE_URL
4602
+ });
4603
+ }
4604
+ /**
4605
+ * Initialize with built-in agents
4606
+ */
4607
+ async initialize() {
4608
+ if (this.initialized) return;
4609
+ for (const agent of BUILTIN_AGENTS) {
4610
+ this.agents.set(agent.name, agent);
4611
+ }
4612
+ this.initialized = true;
4613
+ }
4614
+ /**
4615
+ * Launch multiple agents in parallel
4616
+ */
4617
+ async launchParallel(tasks) {
4618
+ await this.initialize();
4619
+ console.log(`\u{1F680} Launching ${tasks.length} agents in parallel...`);
4620
+ const startTime = Date.now();
4621
+ const promises = tasks.map((task) => this.executeAgent(task));
4622
+ const results = await Promise.allSettled(promises);
4623
+ const duration = Date.now() - startTime;
4624
+ console.log(`\u2705 All agents completed in ${(duration / 1e3).toFixed(1)}s`);
4625
+ const finalResults = results.map((result, index) => {
4626
+ if (result.status === "fulfilled") {
4627
+ return result.value;
4628
+ } else {
4629
+ return {
4630
+ agent: tasks[index].agent,
4631
+ success: false,
4632
+ output: "",
4633
+ findings: [],
4634
+ duration: 0,
4635
+ error: result.reason?.message || "Agent failed"
4636
+ };
4637
+ }
4638
+ });
4639
+ this.emit(ORCHESTRATOR_EVENT.ALL_COMPLETE, {
4640
+ results: finalResults,
4641
+ duration
4642
+ });
4643
+ return finalResults;
4644
+ }
4645
+ /**
4646
+ * Execute a single agent
4647
+ */
4648
+ async executeAgent(task) {
4649
+ const startTime = Date.now();
4650
+ this.emit(ORCHESTRATOR_EVENT.AGENT_START, { agent: task.agent, prompt: task.prompt });
4651
+ try {
4652
+ const agentDef = this.agents.get(task.agent);
4653
+ const systemPrompt = agentDef ? buildAgentSystemPrompt("You are a security expert. Report findings clearly.", agentDef) : `You are a security expert focused on ${task.agent}. Be thorough and report findings in JSON format.`;
4654
+ const response = await withRetry(
4655
+ () => this.client.messages.create({
4656
+ model: LLM_MODEL,
4657
+ max_tokens: LLM_MAX_TOKENS,
4658
+ system: systemPrompt,
4659
+ messages: [
4660
+ { role: "user", content: task.prompt }
4661
+ ]
4662
+ }),
4663
+ { maxRetries: 2 }
4664
+ );
4665
+ const output = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
4666
+ const findings = this.parseFindings(output, task.agent);
4667
+ const duration = Date.now() - startTime;
4668
+ const result = {
4669
+ agent: task.agent,
4670
+ success: true,
4671
+ output,
4672
+ findings,
4673
+ duration
4674
+ };
4675
+ this.emit(ORCHESTRATOR_EVENT.AGENT_COMPLETE, result);
4676
+ for (const finding of findings) {
4677
+ this.emit(ORCHESTRATOR_EVENT.FINDING, finding);
4678
+ }
4679
+ return result;
4680
+ } catch (error) {
4681
+ const errorMsg = error instanceof Error ? error.message : String(error);
4682
+ this.emit(ORCHESTRATOR_EVENT.AGENT_ERROR, { agent: task.agent, error: errorMsg });
4683
+ return {
4684
+ agent: task.agent,
4685
+ success: false,
4686
+ output: "",
4687
+ findings: [],
4688
+ duration: Date.now() - startTime,
4689
+ error: errorMsg
4690
+ };
4691
+ }
4692
+ }
4693
+ /**
4694
+ * Parse findings from agent output
4695
+ */
4696
+ parseFindings(output, agentName) {
4697
+ const findings = [];
4698
+ const jsonMatch = output.match(/```json\n?([\s\S]*?)\n?```/);
4699
+ if (jsonMatch) {
4700
+ try {
4701
+ const parsed = JSON.parse(jsonMatch[1]);
4702
+ if (Array.isArray(parsed)) {
4703
+ return parsed.map((f) => this.normalizeFinding(f, agentName));
4704
+ } else if (parsed.findings) {
4705
+ return parsed.findings.map((f) => this.normalizeFinding(f, agentName));
4706
+ }
4707
+ } catch {
4708
+ }
4709
+ }
4710
+ const patterns = [
4711
+ { regex: /CVE-\d{4}-\d+/gi, type: "vulnerability", severity: "high" },
4712
+ { regex: /open port[s]?[:\s]+(\d+)/gi, type: "info", severity: "info" },
4713
+ { regex: /admin|root|password/gi, type: "credential", severity: "high" },
4714
+ { regex: /shell access|RCE|command injection/gi, type: "vulnerability", severity: "critical" },
4715
+ { regex: /SQL injection|XSS|SSRF/gi, type: "vulnerability", severity: "high" }
4716
+ ];
4717
+ for (const pattern of patterns) {
4718
+ const matches = output.match(pattern.regex);
4719
+ if (matches) {
4720
+ for (const match of [...new Set(matches)]) {
4721
+ findings.push({
4722
+ id: `finding_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
4723
+ type: pattern.type,
4724
+ title: match,
4725
+ description: `Found by ${agentName}: ${match}`,
4726
+ confidence: 60,
4727
+ severity: pattern.severity,
4728
+ evidence: [match],
4729
+ exploitability: "possible"
4730
+ });
4731
+ }
4732
+ }
4733
+ }
4734
+ return findings;
4735
+ }
4736
+ /**
4737
+ * Normalize a finding object
4738
+ */
4739
+ normalizeFinding(raw, agentName) {
4740
+ const f = raw;
4741
+ return {
4742
+ id: f.id || `finding_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
4743
+ type: f.type || "info",
4744
+ title: f.title || "Unknown finding",
4745
+ description: f.description || `Found by ${agentName}`,
4746
+ confidence: f.confidence || 50,
4747
+ severity: f.severity || "medium",
4748
+ evidence: f.evidence || [],
4749
+ exploitability: f.exploitability || "possible",
4750
+ nextSteps: f.nextSteps
4751
+ };
4752
+ }
4753
+ /**
4754
+ * Get available agent names
4755
+ */
4756
+ getAgentNames() {
4757
+ return Array.from(this.agents.keys());
4758
+ }
4759
+ };
4760
+ function consolidateFindings(results) {
4761
+ const allFindings = [];
4762
+ for (const result of results) {
4763
+ allFindings.push(...result.findings);
4764
+ }
4765
+ const findingMap = /* @__PURE__ */ new Map();
4766
+ for (const finding of allFindings) {
4767
+ const key = `${finding.type}:${finding.title}`;
4768
+ if (findingMap.has(key)) {
4769
+ const existing = findingMap.get(key);
4770
+ existing.confidence = Math.min(100, existing.confidence + 10);
4771
+ existing.evidence.push(...finding.evidence);
4772
+ } else {
4773
+ findingMap.set(key, { ...finding });
4774
+ }
4775
+ }
4776
+ return Array.from(findingMap.values()).sort((a, b) => b.confidence - a.confidence);
4777
+ }
4778
+ var orchestratorInstance = null;
4779
+ function getOrchestrator(apiKey) {
4780
+ if (!orchestratorInstance) {
4781
+ orchestratorInstance = new AgentOrchestrator(apiKey);
4782
+ }
4783
+ return orchestratorInstance;
4784
+ }
4785
+
4786
+ // src/core/agent/agent-memory.ts
4787
+ import { EventEmitter as EventEmitter5 } from "events";
4788
+ var MEMORY_EVENT = {
4789
+ ENTRY_ADDED: "entry_added",
4790
+ STATE_UPDATED: "state_updated",
4791
+ HANDOFF: "handoff",
4792
+ COMPACTION: "compaction"
4793
+ };
4794
+ var AgentMemory = class _AgentMemory extends EventEmitter5 {
4795
+ // Short-term memory (current session)
4796
+ shortTermMemory = [];
4797
+ // Long-term memory (persisted across sessions)
4798
+ longTermMemory = [];
4799
+ // Episodic memory (specific events/interactions)
4800
+ episodicMemory = /* @__PURE__ */ new Map();
4801
+ // Shared state across all agents
4802
+ sharedState;
4803
+ // Agent contexts
4804
+ agentContexts = /* @__PURE__ */ new Map();
4805
+ // Memory limits
4806
+ SHORT_TERM_LIMIT = 100;
4807
+ LONG_TERM_LIMIT = 1e3;
4808
+ COMPACTION_THRESHOLD = 80;
4809
+ // % of limit
4810
+ constructor(target = "") {
4811
+ super();
4812
+ this.sharedState = this.initializeState(target);
4813
+ }
4814
+ // ===== State Management =====
4815
+ initializeState(target) {
4816
+ return {
4817
+ target,
4818
+ discoveredHosts: [],
4819
+ discoveredServices: /* @__PURE__ */ new Map(),
4820
+ credentials: [],
4821
+ currentPhase: "recon",
4822
+ completedPhases: [],
4823
+ attackSurface: /* @__PURE__ */ new Map(),
4824
+ vulnerabilities: [],
4825
+ failedApproaches: []
4826
+ };
4827
+ }
4828
+ getSharedState() {
4829
+ return this.sharedState;
4830
+ }
4831
+ updateState(updates) {
4832
+ this.sharedState = { ...this.sharedState, ...updates };
4833
+ this.emit(MEMORY_EVENT.STATE_UPDATED, this.sharedState);
4834
+ }
4835
+ // ===== Memory Operations =====
4836
+ addMemory(entry) {
4837
+ const fullEntry = {
4838
+ ...entry,
4839
+ id: `mem_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
4840
+ timestamp: /* @__PURE__ */ new Date()
4841
+ };
4842
+ this.shortTermMemory.push(fullEntry);
4843
+ if (entry.importance >= 70) {
4844
+ this.longTermMemory.push(fullEntry);
4845
+ }
4846
+ if (!this.episodicMemory.has(entry.agent)) {
4847
+ this.episodicMemory.set(entry.agent, []);
4848
+ }
4849
+ this.episodicMemory.get(entry.agent).push(fullEntry);
4850
+ this.emit(MEMORY_EVENT.ENTRY_ADDED, fullEntry);
4851
+ this.checkCompaction();
4852
+ return fullEntry;
4853
+ }
4854
+ // ===== Memory Retrieval =====
4855
+ getRecentMemories(count = 10) {
4856
+ return this.shortTermMemory.slice(-count);
4857
+ }
4858
+ getMemoriesByAgent(agentName) {
4859
+ return this.episodicMemory.get(agentName) || [];
4860
+ }
4861
+ getMemoriesByType(type) {
4862
+ return [...this.shortTermMemory, ...this.longTermMemory].filter((m) => m.type === type);
4863
+ }
4864
+ getImportantMemories(threshold = 70) {
4865
+ return this.longTermMemory.filter((m) => m.importance >= threshold);
4866
+ }
4867
+ searchMemories(query) {
4868
+ const lowerQuery = query.toLowerCase();
4869
+ return [...this.shortTermMemory, ...this.longTermMemory].filter((m) => m.content.toLowerCase().includes(lowerQuery)).sort((a, b) => b.importance - a.importance);
4870
+ }
4871
+ // ===== Agent Context Management =====
4872
+ setAgentContext(context) {
4873
+ this.agentContexts.set(context.agentName, context);
4874
+ }
4875
+ getAgentContext(agentName) {
4876
+ return this.agentContexts.get(agentName);
4877
+ }
4878
+ getAllAgentContexts() {
4879
+ return Array.from(this.agentContexts.values());
4880
+ }
4881
+ // ===== Agent Handoff =====
4882
+ /**
4883
+ * Create a handoff package for transferring context between agents
4884
+ */
4885
+ createHandoff(fromAgent, toAgent, task, relevantContext) {
4886
+ const handoffId = `handoff_${Date.now()}`;
4887
+ const relevantMemories = this.shortTermMemory.filter(
4888
+ (m) => m.agent === fromAgent || relevantContext.some((c) => m.content.includes(c))
4889
+ ).slice(-20);
4890
+ const contextSummary = this.createContextSummary(fromAgent, task);
4891
+ this.addMemory({
4892
+ type: "handoff",
4893
+ agent: "orchestrator",
4894
+ content: `Handoff: ${fromAgent} \u2192 ${toAgent} for task: ${task}`,
4895
+ importance: 80,
4896
+ metadata: { fromAgent, toAgent, task, handoffId }
4897
+ });
4898
+ this.emit(MEMORY_EVENT.HANDOFF, { fromAgent, toAgent, task, handoffId });
4899
+ return {
4900
+ handoffId,
4901
+ context: contextSummary,
4902
+ sharedState: this.sharedState,
4903
+ relevantMemories
4904
+ };
4905
+ }
4906
+ /**
4907
+ * Create a summarized context for handoff
4908
+ */
4909
+ createContextSummary(agentName, task) {
4910
+ const agentMemories = this.getMemoriesByAgent(agentName).slice(-10);
4911
+ const findings = this.getMemoriesByType("finding").slice(-5);
4912
+ const errors = this.getMemoriesByType("error").slice(-3);
4913
+ return `
4914
+ ## Context Summary for: ${task}
4915
+
4916
+ ### Target
4917
+ - Primary: ${this.sharedState.target}
4918
+ - Phase: ${this.sharedState.currentPhase}
4919
+ - Hosts discovered: ${this.sharedState.discoveredHosts.length}
4920
+
4921
+ ### Recent Actions by ${agentName}
4922
+ ${agentMemories.map((m) => `- ${m.content.slice(0, 100)}`).join("\n")}
4923
+
4924
+ ### Key Findings
4925
+ ${findings.map((f) => `- [${f.agent}] ${f.content.slice(0, 100)}`).join("\n")}
4926
+
4927
+ ### Failed Approaches (avoid these)
4928
+ ${this.sharedState.failedApproaches.slice(-5).map((f) => `- ${f.approach}: ${f.reason}`).join("\n")}
4929
+
4930
+ ### Credentials Found
4931
+ ${this.sharedState.credentials.map((c) => `- ${c.username}:${c.password} (${c.source})`).join("\n") || "None"}
4932
+ `;
4933
+ }
4934
+ // ===== Attack Surface Tracking =====
4935
+ addToAttackSurface(category, items) {
4936
+ const existing = this.sharedState.attackSurface.get(category) || [];
4937
+ const newItems = items.filter((i) => !existing.includes(i));
4938
+ this.sharedState.attackSurface.set(category, [...existing, ...newItems]);
4939
+ if (newItems.length > 0) {
4940
+ this.addMemory({
4941
+ type: "discovery",
4942
+ agent: "orchestrator",
4943
+ content: `Attack surface expanded [${category}]: ${newItems.join(", ")}`,
4944
+ importance: 60
4945
+ });
4946
+ }
4947
+ }
4948
+ getAttackSurface() {
4949
+ return this.sharedState.attackSurface;
4950
+ }
4951
+ getAttackSurfaceSummary() {
4952
+ const summary = [];
4953
+ for (const [category, items] of this.sharedState.attackSurface) {
4954
+ summary.push(`${category}: ${items.length} items`);
4955
+ }
4956
+ return summary.join(", ");
4957
+ }
4958
+ // ===== Failed Approach Tracking =====
4959
+ recordFailedApproach(approach, reason) {
4960
+ this.sharedState.failedApproaches.push({
4961
+ approach,
4962
+ reason,
4963
+ timestamp: /* @__PURE__ */ new Date()
4964
+ });
4965
+ }
4966
+ hasFailedBefore(approach) {
4967
+ return this.sharedState.failedApproaches.some(
4968
+ (f) => f.approach.toLowerCase().includes(approach.toLowerCase())
4969
+ );
4970
+ }
4971
+ // ===== Memory Compaction =====
4972
+ checkCompaction() {
4973
+ if (this.shortTermMemory.length >= this.SHORT_TERM_LIMIT * (this.COMPACTION_THRESHOLD / 100)) {
4974
+ this.compactShortTermMemory();
4975
+ }
4976
+ if (this.longTermMemory.length >= this.LONG_TERM_LIMIT * (this.COMPACTION_THRESHOLD / 100)) {
4977
+ this.compactLongTermMemory();
4978
+ }
4979
+ }
4980
+ compactShortTermMemory() {
4981
+ const sorted = [...this.shortTermMemory].sort((a, b) => {
4982
+ const importanceDiff = b.importance - a.importance;
4983
+ if (importanceDiff !== 0) return importanceDiff;
4984
+ return b.timestamp.getTime() - a.timestamp.getTime();
4985
+ });
4986
+ this.shortTermMemory = sorted.slice(0, Math.floor(this.SHORT_TERM_LIMIT * 0.5));
4987
+ this.emit(MEMORY_EVENT.COMPACTION, { type: "short-term", remaining: this.shortTermMemory.length });
4988
+ }
4989
+ compactLongTermMemory() {
4990
+ this.longTermMemory = this.longTermMemory.sort((a, b) => b.importance - a.importance).slice(0, Math.floor(this.LONG_TERM_LIMIT * 0.7));
4991
+ this.emit(MEMORY_EVENT.COMPACTION, { type: "long-term", remaining: this.longTermMemory.length });
4992
+ }
4993
+ // ===== Serialization =====
4994
+ toJSON() {
4995
+ return {
4996
+ shortTermMemory: this.shortTermMemory,
4997
+ longTermMemory: this.longTermMemory,
4998
+ sharedState: {
4999
+ ...this.sharedState,
5000
+ discoveredServices: Object.fromEntries(this.sharedState.discoveredServices),
5001
+ attackSurface: Object.fromEntries(this.sharedState.attackSurface)
5002
+ },
5003
+ agentContexts: Object.fromEntries(this.agentContexts)
5004
+ };
5005
+ }
5006
+ static fromJSON(data) {
5007
+ const memory = new _AgentMemory();
5008
+ const parsed = data;
5009
+ memory.shortTermMemory = parsed.shortTermMemory || [];
5010
+ memory.longTermMemory = parsed.longTermMemory || [];
5011
+ if (parsed.sharedState) {
5012
+ memory.sharedState = {
5013
+ ...parsed.sharedState,
5014
+ discoveredServices: new Map(Object.entries(parsed.sharedState.discoveredServices || {})),
5015
+ attackSurface: new Map(Object.entries(parsed.sharedState.attackSurface || {}))
5016
+ };
5017
+ }
5018
+ if (parsed.agentContexts) {
5019
+ memory.agentContexts = new Map(Object.entries(parsed.agentContexts));
5020
+ }
5021
+ return memory;
5022
+ }
5023
+ };
5024
+ var memoryInstance = null;
5025
+ function getAgentMemory(target) {
5026
+ if (!memoryInstance) {
5027
+ memoryInstance = new AgentMemory(target);
5028
+ }
5029
+ return memoryInstance;
5030
+ }
5031
+
5032
+ // src/core/agent/supervisor-agent.ts
5033
+ import Anthropic2 from "@anthropic-ai/sdk";
5034
+ import { EventEmitter as EventEmitter6 } from "events";
5035
+ var SUPERVISOR_EVENT = {
5036
+ PLAN_CREATED: "plan_created",
5037
+ PHASE_STARTED: "phase_started",
5038
+ PHASE_COMPLETED: "phase_completed",
5039
+ TASK_DELEGATED: "task_delegated",
5040
+ TASK_COMPLETED: "task_completed",
5041
+ STRATEGY_ADJUSTED: "strategy_adjusted",
5042
+ DECISION_MADE: "decision_made"
5043
+ };
5044
+ var SupervisorAgent = class extends EventEmitter6 {
5045
+ client;
5046
+ orchestrator;
5047
+ memory;
5048
+ currentPlan = null;
5049
+ // Agent capability mapping
5050
+ agentCapabilities = /* @__PURE__ */ new Map([
5051
+ ["target-explorer", ["recon", "scanning", "enumeration", "osint", "subdomain", "directory"]],
5052
+ ["exploit-researcher", ["cve", "exploit", "vulnerability", "payload", "metasploit"]],
5053
+ ["privesc-master", ["privilege", "escalation", "root", "system", "linux", "windows"]],
5054
+ ["web-hacker", ["web", "sql", "xss", "injection", "http", "api", "form"]],
5055
+ ["crypto-solver", ["crypto", "hash", "password", "decrypt", "encode"]],
5056
+ ["forensics-analyst", ["forensics", "memory", "file", "log", "steganography"]],
5057
+ ["reverse-engineer", ["binary", "reverse", "debug", "exploit", "buffer"]],
5058
+ ["attack-architect", ["strategy", "plan", "attack", "chain", "prioritize"]],
5059
+ ["finding-reviewer", ["validate", "verify", "review", "confidence", "false positive"]]
5060
+ ]);
5061
+ constructor(apiKey) {
5062
+ super();
5063
+ this.client = new Anthropic2({
5064
+ apiKey: apiKey || LLM_API_KEY || process.env.PENTEST_API_KEY,
5065
+ baseURL: LLM_BASE_URL
5066
+ });
5067
+ this.orchestrator = getOrchestrator(apiKey);
5068
+ this.memory = getAgentMemory();
5069
+ }
5070
+ // ===== Task Planning =====
5071
+ /**
5072
+ * Create an execution plan for the given objective
5073
+ */
5074
+ async createPlan(objective, target) {
5075
+ const planPrompt = `
5076
+ ${STRATEGY_PLANNING_PROMPT}
5077
+
5078
+ Create an execution plan for:
5079
+ Objective: ${objective}
5080
+ Target: ${target}
5081
+
5082
+ Respond with a JSON plan:
5083
+ {
5084
+ "phases": [
5085
+ {
5086
+ "name": "Phase name",
5087
+ "description": "What this phase accomplishes",
5088
+ "agents": ["agent-name"],
5089
+ "tasks": [
5090
+ {
5091
+ "description": "Task description",
5092
+ "assignedAgent": "agent-name"
5093
+ }
5094
+ ]
5095
+ }
5096
+ ]
5097
+ }
5098
+
5099
+ Rules:
5100
+ 1. Start with reconnaissance (BFS - map attack surface first)
5101
+ 2. Discovery before exploitation
5102
+ 3. Use specialized agents for their strengths
5103
+ 4. Include validation steps
5104
+ 5. Plan for fallbacks
5105
+ `;
5106
+ try {
5107
+ const response = await this.client.messages.create({
5108
+ model: LLM_MODEL,
5109
+ max_tokens: 2048,
5110
+ messages: [{ role: "user", content: planPrompt }]
5111
+ });
5112
+ const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
5113
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
5114
+ if (jsonMatch) {
5115
+ const parsed = JSON.parse(jsonMatch[0]);
5116
+ this.currentPlan = this.buildPlan(objective, parsed);
5117
+ } else {
5118
+ this.currentPlan = this.createDefaultPlan(objective, target);
5119
+ }
5120
+ } catch {
5121
+ this.currentPlan = this.createDefaultPlan(objective, target);
5122
+ }
5123
+ this.emit(SUPERVISOR_EVENT.PLAN_CREATED, this.currentPlan);
5124
+ this.memory.addMemory({
5125
+ type: "decision",
5126
+ agent: "supervisor",
5127
+ content: `Plan created: ${this.currentPlan.phases.length} phases for ${objective}`,
5128
+ importance: 90
5129
+ });
5130
+ return this.currentPlan;
5131
+ }
5132
+ buildPlan(objective, parsed) {
5133
+ return {
5134
+ id: `plan_${Date.now()}`,
5135
+ objective,
5136
+ status: "planning",
5137
+ currentPhaseIndex: 0,
5138
+ phases: parsed.phases.map((phase, idx) => ({
5139
+ id: `phase_${idx}`,
5140
+ name: phase.name,
5141
+ description: phase.description,
5142
+ agents: phase.agents || [],
5143
+ status: "pending",
5144
+ findings: [],
5145
+ tasks: (phase.tasks || []).map((task, tidx) => ({
5146
+ id: `task_${idx}_${tidx}`,
5147
+ description: task.description,
5148
+ assignedAgent: task.assignedAgent || this.selectBestAgent(task.description),
5149
+ status: "pending"
5150
+ }))
5151
+ }))
5152
+ };
5153
+ }
5154
+ createDefaultPlan(objective, target) {
5155
+ return {
5156
+ id: `plan_${Date.now()}`,
5157
+ objective,
5158
+ status: "planning",
5159
+ currentPhaseIndex: 0,
5160
+ phases: [
5161
+ {
5162
+ id: "phase_0",
5163
+ name: "Reconnaissance",
5164
+ description: "Map attack surface - ports, subdomains, directories",
5165
+ agents: ["target-explorer"],
5166
+ status: "pending",
5167
+ findings: [],
5168
+ tasks: [
5169
+ { id: "task_0_0", description: `Port scan ${target}`, assignedAgent: "target-explorer", status: "pending" },
5170
+ { id: "task_0_1", description: `Subdomain enumeration for ${target}`, assignedAgent: "target-explorer", status: "pending" },
5171
+ { id: "task_0_2", description: `Directory bruteforce on web services`, assignedAgent: "web-hacker", status: "pending" }
5172
+ ]
5173
+ },
5174
+ {
5175
+ id: "phase_1",
5176
+ name: "Analysis",
5177
+ description: "Analyze findings and identify vulnerabilities",
5178
+ agents: ["attack-architect", "finding-reviewer"],
5179
+ status: "pending",
5180
+ findings: [],
5181
+ tasks: [
5182
+ { id: "task_1_0", description: "Analyze attack surface and prioritize targets", assignedAgent: "attack-architect", status: "pending" },
5183
+ { id: "task_1_1", description: "CVE research for discovered services", assignedAgent: "exploit-researcher", status: "pending" }
5184
+ ]
5185
+ },
5186
+ {
5187
+ id: "phase_2",
5188
+ name: "Exploitation",
5189
+ description: "Attempt exploitation of identified vulnerabilities",
5190
+ agents: ["exploit-researcher", "web-hacker"],
5191
+ status: "pending",
5192
+ findings: [],
5193
+ tasks: [
5194
+ { id: "task_2_0", description: "Exploit high-priority vulnerabilities", assignedAgent: "exploit-researcher", status: "pending" }
5195
+ ]
5196
+ }
5197
+ ]
5198
+ };
5199
+ }
5200
+ // ===== Agent Selection =====
5201
+ /**
5202
+ * Select the best agent for a given task
5203
+ */
5204
+ selectBestAgent(taskDescription) {
5205
+ const lowerTask = taskDescription.toLowerCase();
5206
+ let bestAgent = "target-explorer";
5207
+ let bestScore = 0;
5208
+ for (const [agent, capabilities] of this.agentCapabilities) {
5209
+ const score = capabilities.filter((cap) => lowerTask.includes(cap)).length;
5210
+ if (score > bestScore) {
5211
+ bestScore = score;
5212
+ bestAgent = agent;
5213
+ }
5214
+ }
5215
+ return bestAgent;
5216
+ }
5217
+ /**
5218
+ * Get agents for a specific phase
5219
+ */
5220
+ getAgentsForPhase(phaseName) {
5221
+ const phaseMapping = {
5222
+ "recon": ["target-explorer"],
5223
+ "reconnaissance": ["target-explorer"],
5224
+ "scan": ["target-explorer"],
5225
+ "enum": ["target-explorer", "web-hacker"],
5226
+ "enumeration": ["target-explorer", "web-hacker"],
5227
+ "analysis": ["attack-architect", "finding-reviewer"],
5228
+ "vuln": ["exploit-researcher", "web-hacker"],
5229
+ "vulnerability": ["exploit-researcher", "web-hacker"],
5230
+ "exploit": ["exploit-researcher", "web-hacker"],
5231
+ "exploitation": ["exploit-researcher", "web-hacker"],
5232
+ "privesc": ["privesc-master"],
5233
+ "privilege": ["privesc-master"],
5234
+ "post": ["privesc-master", "forensics-analyst"]
5235
+ };
5236
+ const lowerPhase = phaseName.toLowerCase();
5237
+ for (const [key, agents] of Object.entries(phaseMapping)) {
5238
+ if (lowerPhase.includes(key)) {
5239
+ return agents;
5240
+ }
5241
+ }
5242
+ return ["target-explorer"];
5243
+ }
5244
+ // ===== Task Execution =====
5245
+ /**
5246
+ * Execute the current plan
5247
+ */
5248
+ async executePlan() {
5249
+ if (!this.currentPlan) {
5250
+ throw new Error("No plan created. Call createPlan() first.");
5251
+ }
5252
+ this.currentPlan.status = "executing";
5253
+ const allFindings = [];
5254
+ for (let i = 0; i < this.currentPlan.phases.length; i++) {
5255
+ this.currentPlan.currentPhaseIndex = i;
5256
+ const phase = this.currentPlan.phases[i];
5257
+ this.emit(SUPERVISOR_EVENT.PHASE_STARTED, phase);
5258
+ phase.status = "in_progress";
5259
+ try {
5260
+ const phaseFindings = await this.executePhase(phase);
5261
+ phase.findings = phaseFindings;
5262
+ allFindings.push(...phaseFindings);
5263
+ phase.status = "completed";
5264
+ this.emit(SUPERVISOR_EVENT.PHASE_COMPLETED, phase);
5265
+ await this.evaluateProgress(phaseFindings);
5266
+ } catch (error) {
5267
+ phase.status = "failed";
5268
+ this.memory.recordFailedApproach(
5269
+ phase.name,
5270
+ error instanceof Error ? error.message : "Unknown error"
5271
+ );
5272
+ }
5273
+ }
5274
+ this.currentPlan.status = "completed";
5275
+ return consolidateFindings(allFindings.map((f) => ({
5276
+ agent: "supervisor",
5277
+ success: true,
5278
+ output: "",
5279
+ findings: [f],
5280
+ duration: 0
5281
+ })));
5282
+ }
5283
+ /**
5284
+ * Execute a single phase
5285
+ */
5286
+ async executePhase(phase) {
5287
+ const tasks = phase.tasks.map((task) => ({
5288
+ agent: task.assignedAgent,
5289
+ prompt: this.buildTaskPrompt(task, phase),
5290
+ priority: 1
5291
+ }));
5292
+ await this.orchestrator.initialize();
5293
+ const results = await this.orchestrator.launchParallel(tasks);
5294
+ for (let i = 0; i < phase.tasks.length; i++) {
5295
+ const task = phase.tasks[i];
5296
+ const result = results[i];
5297
+ task.status = result.success ? "completed" : "failed";
5298
+ task.result = result.output;
5299
+ task.error = result.error;
5300
+ this.emit(SUPERVISOR_EVENT.TASK_COMPLETED, { task, result });
5301
+ this.memory.addMemory({
5302
+ type: result.success ? "action" : "error",
5303
+ agent: task.assignedAgent,
5304
+ content: result.success ? `Completed: ${task.description}` : `Failed: ${task.description} - ${result.error}`,
5305
+ importance: result.success ? 60 : 70
5306
+ });
5307
+ }
5308
+ return consolidateFindings(results);
5309
+ }
5310
+ buildTaskPrompt(task, phase) {
5311
+ const handoff = this.memory.createHandoff(
5312
+ "supervisor",
5313
+ task.assignedAgent,
5314
+ task.description,
5315
+ [phase.name, task.description]
5316
+ );
5317
+ return `
5318
+ ${AGENT_COLLABORATION_PROMPT}
5319
+
5320
+ ## Your Task
5321
+ ${task.description}
5322
+
5323
+ ## Phase Context
5324
+ Phase: ${phase.name}
5325
+ Description: ${phase.description}
5326
+
5327
+ ## Shared Context
5328
+ ${handoff.context}
5329
+
5330
+ ## Instructions
5331
+ 1. Complete the task thoroughly
5332
+ 2. Report all findings in structured format
5333
+ 3. Suggest next steps based on discoveries
5334
+ 4. Be efficient - BFS over DFS
5335
+
5336
+ Provide your findings in JSON format when possible.
5337
+ `;
5338
+ }
5339
+ // ===== Strategy Evaluation =====
5340
+ /**
5341
+ * Evaluate progress and adjust strategy if needed
5342
+ */
5343
+ async evaluateProgress(findings) {
5344
+ const criticalFindings = findings.filter((f) => f.severity === "critical" || f.severity === "high");
5345
+ const failedApproaches = this.memory.getSharedState().failedApproaches;
5346
+ if (criticalFindings.length > 0) {
5347
+ this.memory.addMemory({
5348
+ type: "decision",
5349
+ agent: "supervisor",
5350
+ content: `Strategy adjustment: Found ${criticalFindings.length} critical/high findings. Prioritizing exploitation.`,
5351
+ importance: 85
5352
+ });
5353
+ this.emit(SUPERVISOR_EVENT.STRATEGY_ADJUSTED, {
5354
+ reason: "Critical findings discovered",
5355
+ adjustment: "Prioritize exploitation",
5356
+ findings: criticalFindings
5357
+ });
5358
+ }
5359
+ if (failedApproaches.length >= 3) {
5360
+ this.memory.addMemory({
5361
+ type: "decision",
5362
+ agent: "supervisor",
5363
+ content: `Strategy adjustment: ${failedApproaches.length} failed approaches. Recommending pivot.`,
5364
+ importance: 80
5365
+ });
5366
+ }
5367
+ }
5368
+ // ===== Decision Making =====
5369
+ /**
5370
+ * Make a decision about next action
5371
+ */
5372
+ async makeDecision(situation) {
5373
+ const decisionPrompt = `
5374
+ ${STRATEGY_PLANNING_PROMPT}
5375
+
5376
+ Current situation:
5377
+ ${situation}
5378
+
5379
+ Shared state:
5380
+ - Target: ${this.memory.getSharedState().target}
5381
+ - Phase: ${this.memory.getSharedState().currentPhase}
5382
+ - Attack surface: ${this.memory.getAttackSurfaceSummary()}
5383
+ - Failed approaches: ${this.memory.getSharedState().failedApproaches.map((f) => f.approach).join(", ")}
5384
+
5385
+ Make a decision:
5386
+ 1. What action should we take?
5387
+ 2. Which agent should handle it?
5388
+ 3. What's your reasoning?
5389
+ 4. What are alternatives if this fails?
5390
+
5391
+ Respond with JSON:
5392
+ {
5393
+ "action": "description of action",
5394
+ "agent": "agent-name",
5395
+ "reasoning": "why this is the best choice",
5396
+ "confidence": 0-100,
5397
+ "alternatives": ["alt1", "alt2"]
5398
+ }
5399
+ `;
5400
+ try {
5401
+ const response = await this.client.messages.create({
5402
+ model: LLM_MODEL,
5403
+ max_tokens: 1024,
5404
+ messages: [{ role: "user", content: decisionPrompt }]
5405
+ });
5406
+ const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
5407
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
5408
+ if (jsonMatch) {
5409
+ const decision = JSON.parse(jsonMatch[0]);
5410
+ this.emit(SUPERVISOR_EVENT.DECISION_MADE, decision);
5411
+ this.memory.addMemory({
5412
+ type: "decision",
5413
+ agent: "supervisor",
5414
+ content: `Decision: ${decision.action} (${decision.agent}, ${decision.confidence}% confidence)`,
5415
+ importance: 75
5416
+ });
5417
+ return decision;
5418
+ }
5419
+ } catch {
5420
+ }
5421
+ return {
5422
+ action: "Continue with reconnaissance",
5423
+ agent: "target-explorer",
5424
+ reasoning: "Default action when unsure",
5425
+ confidence: 50,
5426
+ alternatives: ["web-hacker", "exploit-researcher"]
5427
+ };
5428
+ }
5429
+ // ===== Getters =====
5430
+ getCurrentPlan() {
5431
+ return this.currentPlan;
5432
+ }
5433
+ getMemory() {
5434
+ return this.memory;
5435
+ }
5436
+ };
5437
+ var supervisorInstance = null;
5438
+ function getSupervisor(apiKey) {
5439
+ if (!supervisorInstance) {
5440
+ supervisorInstance = new SupervisorAgent(apiKey);
5441
+ }
5442
+ return supervisorInstance;
5443
+ }
5444
+
4153
5445
  // src/commands/index.ts
4154
5446
  var SCAN_COMMAND = {
4155
5447
  name: "scan",
@@ -4466,7 +5758,7 @@ var DEFAULT_PHASES = [
4466
5758
  { id: PHASE_ID.EXFIL, name: "Data Exfiltration", shortName: "Exfil", status: PHASE_STATUS.PENDING, attempts: 0 },
4467
5759
  { id: PHASE_ID.REPORT, name: "Reporting", shortName: "Report", status: PHASE_STATUS.PENDING, attempts: 0 }
4468
5760
  ];
4469
- var AutonomousHackingAgent = class extends EventEmitter4 {
5761
+ var AutonomousHackingAgent = class extends EventEmitter7 {
4470
5762
  client;
4471
5763
  state;
4472
5764
  config;
@@ -4481,6 +5773,9 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
4481
5773
  mcpManager;
4482
5774
  contextManager;
4483
5775
  approvalManager;
5776
+ orchestrator;
5777
+ agentMemory;
5778
+ supervisor;
4484
5779
  // Token usage tracking
4485
5780
  tokenUsage = {
4486
5781
  input: 0,
@@ -4499,7 +5794,7 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
4499
5794
  // Max attempts per phase
4500
5795
  constructor(apiKey, config) {
4501
5796
  super();
4502
- this.client = new Anthropic({
5797
+ this.client = new Anthropic3({
4503
5798
  apiKey: apiKey || LLM_API_KEY || process.env.PENTEST_API_KEY,
4504
5799
  baseURL: LLM_BASE_URL
4505
5800
  });
@@ -4509,6 +5804,9 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
4509
5804
  this.mcpManager = getMCPManager();
4510
5805
  this.contextManager = new ContextManager(this.client);
4511
5806
  this.approvalManager = getApprovalManager({ yoloMode: config?.autoApprove });
5807
+ this.orchestrator = getOrchestrator(apiKey);
5808
+ this.agentMemory = getAgentMemory();
5809
+ this.supervisor = getSupervisor(apiKey);
4512
5810
  this.state = this.createInitialState();
4513
5811
  this.initSystems();
4514
5812
  }
@@ -4919,25 +6217,161 @@ What went wrong and what different approach should be tried?
4919
6217
  this.think(THOUGHT_TYPE.REFLECTION, reflection);
4920
6218
  return reflection;
4921
6219
  }
6220
+ // ===== Strategy Validation =====
6221
+ async validateStrategy(proposedAction) {
6222
+ this.think(THOUGHT_TYPE.PLANNING, "[strategy] Validating action strategy...");
6223
+ const validationPrompt = `
6224
+ ${STRATEGY_PLANNING_PROMPT}
6225
+
6226
+ Current situation:
6227
+ - Target: ${this.state.target.primary}
6228
+ - Current phase: ${this.getCurrentPhase().shortName}
6229
+ - Discovered services: ${this.state.target.services.map((s) => `${s.host}:${s.port} (${s.service})`).join(", ") || "none"}
6230
+ - Attack surface mapped: ${this.state.target.discovered.length} hosts, ${this.state.target.services.length} services
6231
+ - Subdomains found: ${this.state.target.discovered.filter((d) => d.includes(".")).length}
6232
+
6233
+ Proposed action: ${proposedAction}
6234
+
6235
+ Validate this action:
6236
+ 1. Is this aligned with BFS (surface mapping first)?
6237
+ 2. Have we completed reconnaissance before deep testing?
6238
+ 3. Is this the highest ROI action right now?
6239
+ 4. Should we adjust the approach?
6240
+
6241
+ Respond with JSON:
6242
+ {
6243
+ "valid": true/false,
6244
+ "adjustedAction": "if invalid, suggest better action",
6245
+ "reasoning": "brief explanation"
6246
+ }
6247
+ `;
6248
+ try {
6249
+ const response = await this.client.messages.create({
6250
+ model: LLM_MODEL,
6251
+ max_tokens: 1024,
6252
+ messages: [{ role: "user", content: validationPrompt }]
6253
+ });
6254
+ const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
6255
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
6256
+ if (jsonMatch) {
6257
+ const result = JSON.parse(jsonMatch[0]);
6258
+ if (!result.valid) {
6259
+ this.think(THOUGHT_TYPE.REFLECTION, `[strategy] Action adjusted: ${result.reasoning}`);
6260
+ }
6261
+ return result;
6262
+ }
6263
+ } catch {
6264
+ }
6265
+ return { valid: true, reasoning: "Validation skipped" };
6266
+ }
6267
+ // ===== Agent Collaboration =====
6268
+ /**
6269
+ * Delegate a task to a specialist agent and get results
6270
+ */
6271
+ async delegateToSpecialist(agentName, task, context) {
6272
+ this.think(THOUGHT_TYPE.PLANNING, `[delegate] Delegating to ${agentName}: ${task.slice(0, 50)}...`);
6273
+ await this.orchestrator.initialize();
6274
+ const agentTask = {
6275
+ agent: agentName,
6276
+ prompt: `${AGENT_COLLABORATION_PROMPT}
6277
+
6278
+ Context from main agent:
6279
+ ${context}
6280
+
6281
+ Your task:
6282
+ ${task}
6283
+
6284
+ Provide your analysis and findings. Include:
6285
+ 1. Key discoveries
6286
+ 2. Recommended next actions
6287
+ 3. Any concerns or issues found`,
6288
+ priority: 1
6289
+ };
6290
+ try {
6291
+ const results = await this.orchestrator.launchParallel([agentTask]);
6292
+ const result = results[0];
6293
+ if (result.success) {
6294
+ const consolidatedFindings = consolidateFindings(results);
6295
+ const findingsSummary = consolidatedFindings.map((f) => `- [${f.severity.toUpperCase()}] ${f.title} (${f.confidence}% confidence)`).join("\n");
6296
+ this.think(THOUGHT_TYPE.REFLECTION, `[delegate] ${agentName} completed: ${result.findings.length} findings`);
6297
+ return {
6298
+ success: true,
6299
+ findings: findingsSummary || result.output.slice(0, 500),
6300
+ recommendation: result.findings[0]?.nextSteps?.join(", ") || "Continue with main approach"
6301
+ };
6302
+ }
6303
+ return {
6304
+ success: false,
6305
+ findings: result.error || "Agent failed",
6306
+ recommendation: "Try alternative approach"
6307
+ };
6308
+ } catch (error) {
6309
+ return {
6310
+ success: false,
6311
+ findings: error instanceof Error ? error.message : "Unknown error",
6312
+ recommendation: "Fallback to main agent"
6313
+ };
6314
+ }
6315
+ }
6316
+ /**
6317
+ * Consult multiple agents for strategy validation
6318
+ */
6319
+ async consultAgents(question) {
6320
+ this.think(THOUGHT_TYPE.PLANNING, "[consult] Consulting specialist agents...");
6321
+ const tasks = [
6322
+ {
6323
+ agent: "attack-architect",
6324
+ prompt: `Strategic question: ${question}
6325
+
6326
+ Provide your expert opinion on the best approach.`,
6327
+ priority: 1
6328
+ },
6329
+ {
6330
+ agent: "finding-reviewer",
6331
+ prompt: `Review this approach: ${question}
6332
+
6333
+ Validate if this is the right strategy.`,
6334
+ priority: 1
6335
+ }
6336
+ ];
6337
+ await this.orchestrator.initialize();
6338
+ const results = await this.orchestrator.launchParallel(tasks);
6339
+ const opinions = results.filter((r) => r.success).map((r) => `**${r.agent}**: ${r.output.slice(0, 200)}...`).join("\n\n");
6340
+ this.think(THOUGHT_TYPE.REFLECTION, "[consult] Received opinions from specialists");
6341
+ return opinions || "No specialist opinions available";
6342
+ }
4922
6343
  // ===== Progress Detection =====
4923
6344
  recordProgress(type) {
4924
6345
  this.resetStuckCounter();
4925
6346
  this.state.lastProgressTime = /* @__PURE__ */ new Date();
6347
+ let message = "";
6348
+ let importance = 60;
4926
6349
  switch (type) {
4927
6350
  case "discovery":
4928
- this.think(THOUGHT_TYPE.BREAKTHROUGH, "[target] New target discovered!");
6351
+ message = "[target] New target discovered!";
6352
+ importance = 65;
4929
6353
  break;
4930
6354
  case "credential":
4931
- this.think(THOUGHT_TYPE.BREAKTHROUGH, "[cred] Credential obtained!");
6355
+ message = "[cred] Credential obtained!";
6356
+ importance = 85;
4932
6357
  break;
4933
6358
  case "access":
4934
- this.think(THOUGHT_TYPE.BREAKTHROUGH, "[access] Access obtained!");
6359
+ message = "[access] Access obtained!";
6360
+ importance = 90;
4935
6361
  break;
4936
6362
  case "exploit":
4937
- this.think(THOUGHT_TYPE.BREAKTHROUGH, "[exploit] Exploit successful!");
6363
+ message = "[exploit] Exploit successful!";
6364
+ importance = 95;
4938
6365
  this.state.successfulExploits++;
4939
6366
  break;
4940
6367
  }
6368
+ this.think(THOUGHT_TYPE.BREAKTHROUGH, message);
6369
+ this.agentMemory.addMemory({
6370
+ type: type === "exploit" ? "action" : type === "credential" ? "credential" : "discovery",
6371
+ agent: this.currentAgent?.name || "autonomous-agent",
6372
+ content: message,
6373
+ importance
6374
+ });
4941
6375
  }
4942
6376
  // ===== Finding Management =====
4943
6377
  addFinding(finding) {
@@ -5686,14 +7120,14 @@ Respond helpfully to the user's message. If they ask to perform security testing
5686
7120
  // src/core/session/session-manager.ts
5687
7121
  import * as fs4 from "fs/promises";
5688
7122
  import * as path4 from "path";
5689
- import { EventEmitter as EventEmitter5 } from "events";
7123
+ import { EventEmitter as EventEmitter8 } from "events";
5690
7124
  var SESSIONS_DIR = ".pentesting/sessions";
5691
7125
  function generateSessionId() {
5692
7126
  const timestamp = Date.now().toString(36);
5693
7127
  const random = Math.random().toString(36).substring(2, 8);
5694
7128
  return `session_${timestamp}_${random}`;
5695
7129
  }
5696
- var SessionManager = class extends EventEmitter5 {
7130
+ var SessionManager = class extends EventEmitter8 {
5697
7131
  sessionsDir;
5698
7132
  currentSession = null;
5699
7133
  constructor(baseDir) {
@@ -6012,7 +7446,7 @@ function getSlashCommandRegistry() {
6012
7446
  // src/core/context/context-manager.ts
6013
7447
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, renameSync } from "fs";
6014
7448
  import { join as join4, dirname as dirname3 } from "path";
6015
- import { EventEmitter as EventEmitter6 } from "events";
7449
+ import { EventEmitter as EventEmitter9 } from "events";
6016
7450
  var CONTEXT_EVENT = {
6017
7451
  MESSAGE_ADDED: "message_added",
6018
7452
  CHECKPOINT_CREATED: "checkpoint_created",
@@ -6020,7 +7454,7 @@ var CONTEXT_EVENT = {
6020
7454
  CLEARED: "cleared",
6021
7455
  COMPACTED: "compacted"
6022
7456
  };
6023
- var ContextManager2 = class extends EventEmitter6 {
7457
+ var ContextManager2 = class extends EventEmitter9 {
6024
7458
  filePath;
6025
7459
  state;
6026
7460
  maxMessages;
@@ -6731,9 +8165,9 @@ import { homedir } from "os";
6731
8165
  import { join as join7 } from "path";
6732
8166
 
6733
8167
  // src/cli/utils/keyboard-listener.ts
6734
- import { EventEmitter as EventEmitter7 } from "events";
8168
+ import { EventEmitter as EventEmitter10 } from "events";
6735
8169
  import * as readline2 from "readline";
6736
- var KeyboardListener = class extends EventEmitter7 {
8170
+ var KeyboardListener = class extends EventEmitter10 {
6737
8171
  isListening = false;
6738
8172
  isPaused = false;
6739
8173
  stdin = process.stdin;
@@ -6958,7 +8392,7 @@ function getKeyboardListener() {
6958
8392
  }
6959
8393
 
6960
8394
  // src/utils/input-queue.ts
6961
- import { EventEmitter as EventEmitter8 } from "events";
8395
+ import { EventEmitter as EventEmitter11 } from "events";
6962
8396
  var INPUT_QUEUE_EVENT = {
6963
8397
  QUEUED: "queued",
6964
8398
  // Message added to queue
@@ -6971,7 +8405,7 @@ var INPUT_QUEUE_EVENT = {
6971
8405
  SHUTDOWN: "shutdown"
6972
8406
  // Queue shutdown
6973
8407
  };
6974
- var InputQueue = class extends EventEmitter8 {
8408
+ var InputQueue = class extends EventEmitter11 {
6975
8409
  queue = [];
6976
8410
  isShutdown = false;
6977
8411
  isPaused = false;