pentesting 0.12.4 → 0.12.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -9
- package/dist/{auto-update-GKO64QMO.js → auto-update-QJJRAZRM.js} +2 -2
- package/dist/{chunk-M5JWJSPW.js → chunk-5K7T4DZW.js} +1 -1
- package/dist/chunk-6GEXOEUI.js +525 -0
- package/dist/chunk-AOJBE232.js +457 -0
- package/dist/index.js +1213 -919
- package/dist/{update-UMRID565.js → update-7HXYJ62R.js} +2 -2
- package/dist/web-search-XQYEM24B.js +43 -0
- package/package.json +9 -7
- package/dist/chunk-JXR7HH4V.js +0 -308
- package/dist/mcp/mcp-server.d.ts +0 -2
- package/dist/mcp/mcp-server.js +0 -0
package/dist/index.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ctfResearch,
|
|
4
|
+
searchADWriteups,
|
|
5
|
+
searchByScenario,
|
|
6
|
+
searchLinuxPrivesc,
|
|
7
|
+
searchMachineWriteup,
|
|
8
|
+
searchWindowsPrivesc,
|
|
9
|
+
searchWriteups,
|
|
10
|
+
securityResearch
|
|
11
|
+
} from "./chunk-AOJBE232.js";
|
|
2
12
|
import {
|
|
3
13
|
AGENT_CONFIG,
|
|
4
14
|
AGENT_EVENT,
|
|
@@ -15,8 +25,9 @@ import {
|
|
|
15
25
|
PHASE_ID,
|
|
16
26
|
PHASE_STATUS,
|
|
17
27
|
THOUGHT_TYPE,
|
|
18
|
-
TOOL_NAME
|
|
19
|
-
|
|
28
|
+
TOOL_NAME,
|
|
29
|
+
TOOL_TO_APT
|
|
30
|
+
} from "./chunk-6GEXOEUI.js";
|
|
20
31
|
import {
|
|
21
32
|
__require
|
|
22
33
|
} from "./chunk-3RG5ZIWI.js";
|
|
@@ -33,7 +44,7 @@ import Spinner from "ink-spinner";
|
|
|
33
44
|
|
|
34
45
|
// src/core/agent/autonomous-agent.ts
|
|
35
46
|
import Anthropic from "@anthropic-ai/sdk";
|
|
36
|
-
import { EventEmitter as
|
|
47
|
+
import { EventEmitter as EventEmitter17 } from "events";
|
|
37
48
|
|
|
38
49
|
// src/core/prompts/autonomous-prompt.ts
|
|
39
50
|
var AUTONOMOUS_HACKING_PROMPT = `You are Pentesting, an elite autonomous penetration testing AI designed for CTF competitions and professional security assessments. You operate with minimal human intervention, making intelligent decisions, adapting to obstacles, and persistently pursuing objectives until complete system compromise.
|
|
@@ -205,7 +216,16 @@ RIGHT: Use ffuf tool with url=https://domain.com/FUZZ, mode=directory
|
|
|
205
216
|
|
|
206
217
|
NEVER write manual bash loops when MCP tools exist!
|
|
207
218
|
ALWAYS prefer MCP tools over bash commands!
|
|
208
|
-
|
|
219
|
+
|
|
220
|
+
<explore_and_verify_strategy>
|
|
221
|
+
When performing research or vulnerability analysis:
|
|
222
|
+
1. **Search**: Start with broad searches (search_writeups, security_research).
|
|
223
|
+
2. **Explore**: Use fetchUrlContent to read the actual content of promising links.
|
|
224
|
+
3. **Recursive Search**: If you find specific keywords, CVEs, or tool names in the content, perform NEW searches for those specific items.
|
|
225
|
+
4. **Verify**: Use browser_automation to interact with target sites and verify if the techniques found in research actually apply to the current target.
|
|
226
|
+
5. **Trace**: Document the path from initial search to final verification.
|
|
227
|
+
</explore_and_verify_strategy>
|
|
228
|
+
</mandatory_autonomous_execution>
|
|
209
229
|
|
|
210
230
|
<output_format>
|
|
211
231
|
Always structure your thinking clearly:
|
|
@@ -2776,12 +2796,6 @@ var ALL_TOOLS = [
|
|
|
2776
2796
|
...RESEARCH_TOOLS
|
|
2777
2797
|
];
|
|
2778
2798
|
|
|
2779
|
-
// src/core/tools/tool-executor.ts
|
|
2780
|
-
import { exec, spawn as spawn2 } from "child_process";
|
|
2781
|
-
import { promisify } from "util";
|
|
2782
|
-
import * as fs from "fs/promises";
|
|
2783
|
-
import * as path from "path";
|
|
2784
|
-
|
|
2785
2799
|
// src/core/resource/resource-manager.ts
|
|
2786
2800
|
import { EventEmitter } from "events";
|
|
2787
2801
|
var RESOURCE_EVENT = {
|
|
@@ -3085,343 +3099,234 @@ function getResourceManager(config) {
|
|
|
3085
3099
|
return resourceManagerInstance;
|
|
3086
3100
|
}
|
|
3087
3101
|
|
|
3088
|
-
// src/core/tools/
|
|
3089
|
-
import { spawn } from "child_process";
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
"site:ippsec.rocks",
|
|
3106
|
-
"site:app.hackthebox.com",
|
|
3107
|
-
'"HackTheBox" OR "HTB"'
|
|
3108
|
-
],
|
|
3109
|
-
thm: [
|
|
3110
|
-
"site:tryhackme.com",
|
|
3111
|
-
'"TryHackMe" OR "THM"'
|
|
3112
|
-
],
|
|
3113
|
-
ctf: [
|
|
3114
|
-
"site:ctftime.org",
|
|
3115
|
-
"site:ctf.zeyu2001.com",
|
|
3116
|
-
"site:github.com CTF writeup"
|
|
3117
|
-
],
|
|
3118
|
-
vulnhub: [
|
|
3119
|
-
"site:vulnhub.com",
|
|
3120
|
-
'"VulnHub" writeup'
|
|
3121
|
-
]
|
|
3122
|
-
};
|
|
3123
|
-
var SCENARIO_KEYWORDS = {
|
|
3124
|
-
ad: "Active Directory Kerberos LDAP BloodHound",
|
|
3125
|
-
windows: "Windows privilege escalation PowerShell",
|
|
3126
|
-
linux: "Linux privilege escalation GTFOBins SUID",
|
|
3127
|
-
web: "web exploitation XSS SQLi SSTI RCE",
|
|
3128
|
-
crypto: "cryptography RSA AES cipher",
|
|
3129
|
-
forensics: "forensics memory dump volatility",
|
|
3130
|
-
pwn: "binary exploitation buffer overflow ROP",
|
|
3131
|
-
reversing: "reverse engineering IDA Ghidra",
|
|
3132
|
-
misc: "miscellaneous steganography OSINT"
|
|
3133
|
-
};
|
|
3134
|
-
async function searchDuckDuckGo(query, options = {}) {
|
|
3135
|
-
const { maxResults = 10, timeout = 3e4 } = options;
|
|
3136
|
-
return new Promise((resolve) => {
|
|
3137
|
-
const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
3138
|
-
const proc = spawn("curl", [
|
|
3139
|
-
"-s",
|
|
3140
|
-
"-A",
|
|
3141
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
3142
|
-
url
|
|
3143
|
-
], { timeout });
|
|
3144
|
-
let stdout = "";
|
|
3145
|
-
proc.stdout.on("data", (data) => {
|
|
3146
|
-
stdout += data.toString();
|
|
3147
|
-
});
|
|
3148
|
-
proc.on("close", () => {
|
|
3149
|
-
const results = [];
|
|
3150
|
-
const resultRegex = /<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>/g;
|
|
3151
|
-
const snippetRegex = /<a[^>]+class="result__snippet"[^>]*>([^<]+)<\/a>/g;
|
|
3152
|
-
let match;
|
|
3153
|
-
const urls = [];
|
|
3154
|
-
const titles = [];
|
|
3155
|
-
const snippets = [];
|
|
3156
|
-
while ((match = resultRegex.exec(stdout)) !== null) {
|
|
3157
|
-
urls.push(match[1]);
|
|
3158
|
-
titles.push(match[2]);
|
|
3159
|
-
}
|
|
3160
|
-
while ((match = snippetRegex.exec(stdout)) !== null) {
|
|
3161
|
-
snippets.push(match[1]);
|
|
3162
|
-
}
|
|
3163
|
-
for (let i = 0; i < Math.min(urls.length, maxResults); i++) {
|
|
3164
|
-
results.push({
|
|
3165
|
-
title: titles[i] || "",
|
|
3166
|
-
url: urls[i] || "",
|
|
3167
|
-
snippet: snippets[i] || ""
|
|
3168
|
-
});
|
|
3169
|
-
}
|
|
3170
|
-
resolve(results);
|
|
3171
|
-
});
|
|
3172
|
-
proc.on("error", () => {
|
|
3173
|
-
resolve([]);
|
|
3174
|
-
});
|
|
3175
|
-
});
|
|
3176
|
-
}
|
|
3177
|
-
async function searchCVE(query) {
|
|
3178
|
-
return searchDuckDuckGo(`${query} site:cve.mitre.org OR site:nvd.nist.gov`);
|
|
3179
|
-
}
|
|
3180
|
-
async function searchExploits(query) {
|
|
3181
|
-
return searchDuckDuckGo(`${query} site:exploit-db.com OR site:github.com exploit POC`);
|
|
3182
|
-
}
|
|
3183
|
-
async function searchWriteups(query, platform3 = "all") {
|
|
3184
|
-
const sources = platform3 === "all" ? WRITEUP_SOURCES.general : WRITEUP_SOURCES[platform3] || WRITEUP_SOURCES.general;
|
|
3185
|
-
const siteQuery = sources.join(" OR ");
|
|
3186
|
-
return searchDuckDuckGo(`${query} writeup walkthrough ${siteQuery}`);
|
|
3187
|
-
}
|
|
3188
|
-
async function searchMachineWriteup(machineName) {
|
|
3189
|
-
const [writeups, videos] = await Promise.all([
|
|
3190
|
-
searchDuckDuckGo(`"${machineName}" HackTheBox OR TryHackMe writeup walkthrough site:0xdf.gitlab.io OR site:medium.com`),
|
|
3191
|
-
searchDuckDuckGo(`"${machineName}" ippsec site:youtube.com OR site:ippsec.rocks`)
|
|
3192
|
-
]);
|
|
3193
|
-
return { writeups, videos };
|
|
3102
|
+
// src/core/tools/tool-utils.ts
|
|
3103
|
+
import { exec, spawn } from "child_process";
|
|
3104
|
+
import { promisify } from "util";
|
|
3105
|
+
import * as fs from "fs/promises";
|
|
3106
|
+
import * as path from "path";
|
|
3107
|
+
var execAsync = promisify(exec);
|
|
3108
|
+
var SEARCHED_PACKAGES = {};
|
|
3109
|
+
var USE_DOCKER = process.env.PENTESTING_DOCKER === "1";
|
|
3110
|
+
var DOCKER_CONTAINER = process.env.PENTESTING_CONTAINER || "pentesting-tools";
|
|
3111
|
+
var autoInstallEnabled = true;
|
|
3112
|
+
async function commandExists(cmd) {
|
|
3113
|
+
try {
|
|
3114
|
+
await execAsync(`which ${cmd}`);
|
|
3115
|
+
return true;
|
|
3116
|
+
} catch {
|
|
3117
|
+
return false;
|
|
3118
|
+
}
|
|
3194
3119
|
}
|
|
3195
|
-
async function
|
|
3196
|
-
|
|
3197
|
-
|
|
3120
|
+
async function searchForAptPackage(toolName) {
|
|
3121
|
+
if (SEARCHED_PACKAGES[toolName]) return SEARCHED_PACKAGES[toolName];
|
|
3122
|
+
console.log(`
|
|
3123
|
+
\u{1F50D} Searching for apt package name for '${toolName}' on Kali Linux...`);
|
|
3124
|
+
try {
|
|
3125
|
+
const { searchGoogle, deepSearch } = await import("./web-search-XQYEM24B.js");
|
|
3126
|
+
const query = `apt install package name for ${toolName} on Kali Linux`;
|
|
3127
|
+
const searchResults = await deepSearch(query, { maxResults: 3 });
|
|
3128
|
+
for (const res of searchResults) {
|
|
3129
|
+
const content = res.content || res.snippet;
|
|
3130
|
+
const match = content.match(/apt(?:-get)?\s+install\s+(?:-y\s+)?([a-z0-9._-]+)/i);
|
|
3131
|
+
if (match && match[1]) {
|
|
3132
|
+
const pkg = match[1].toLowerCase();
|
|
3133
|
+
console.log(`\u2728 Found potential package: ${pkg}`);
|
|
3134
|
+
SEARCHED_PACKAGES[toolName] = pkg;
|
|
3135
|
+
return pkg;
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
} catch (error) {
|
|
3139
|
+
console.error("Failed to search for package:", error);
|
|
3140
|
+
}
|
|
3141
|
+
return null;
|
|
3198
3142
|
}
|
|
3199
|
-
async function
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3143
|
+
async function installTool(toolName, customPackage) {
|
|
3144
|
+
const packageName = customPackage || TOOL_TO_APT[toolName] || toolName;
|
|
3145
|
+
try {
|
|
3146
|
+
console.log(`
|
|
3147
|
+
\u{1F4E6} Installing ${toolName} (${packageName})...`);
|
|
3148
|
+
const { stdout, stderr } = await execAsync(
|
|
3149
|
+
`sudo apt update -qq && sudo apt install -y ${packageName}`,
|
|
3150
|
+
{ timeout: 3e5 }
|
|
3151
|
+
// 5 minute timeout for installation
|
|
3152
|
+
);
|
|
3153
|
+
console.log(`\u2705 ${toolName} installed successfully
|
|
3154
|
+
`);
|
|
3155
|
+
return { success: true, output: stdout + stderr, duration: 0 };
|
|
3156
|
+
} catch (error) {
|
|
3157
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3158
|
+
console.log(`\u274C Failed to install ${toolName}: ${errorMessage}
|
|
3159
|
+
`);
|
|
3160
|
+
return { success: false, output: "", error: errorMessage, duration: 0 };
|
|
3161
|
+
}
|
|
3203
3162
|
}
|
|
3204
|
-
async function
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3163
|
+
async function ensureToolAvailable(toolName) {
|
|
3164
|
+
if (await commandExists(toolName)) {
|
|
3165
|
+
return true;
|
|
3166
|
+
}
|
|
3167
|
+
if (!autoInstallEnabled) {
|
|
3168
|
+
console.log(`\u26A0\uFE0F Tool '${toolName}' is missing but auto-install is disabled.`);
|
|
3169
|
+
return false;
|
|
3170
|
+
}
|
|
3171
|
+
let packageName = TOOL_TO_APT[toolName];
|
|
3172
|
+
if (!packageName) {
|
|
3173
|
+
packageName = await searchForAptPackage(toolName);
|
|
3174
|
+
}
|
|
3175
|
+
const result = await installTool(toolName, packageName);
|
|
3176
|
+
return result.success;
|
|
3208
3177
|
}
|
|
3209
|
-
async function
|
|
3210
|
-
return
|
|
3211
|
-
|
|
3212
|
-
|
|
3178
|
+
async function isDockerAvailable() {
|
|
3179
|
+
if (!USE_DOCKER) return false;
|
|
3180
|
+
try {
|
|
3181
|
+
const { stdout } = await execAsync(`docker inspect ${DOCKER_CONTAINER} --format='{{.State.Running}}'`);
|
|
3182
|
+
return stdout.trim() === "true";
|
|
3183
|
+
} catch {
|
|
3184
|
+
return false;
|
|
3185
|
+
}
|
|
3213
3186
|
}
|
|
3214
|
-
|
|
3215
|
-
const
|
|
3216
|
-
|
|
3217
|
-
searchCVE(query),
|
|
3218
|
-
searchExploits(query),
|
|
3219
|
-
searchWriteups(query)
|
|
3220
|
-
]);
|
|
3221
|
-
return { general, cves, exploits, writeups };
|
|
3187
|
+
function wrapForDocker(command) {
|
|
3188
|
+
const cleanCmd = command.replace(/^sudo\s+/, "");
|
|
3189
|
+
return `docker exec ${DOCKER_CONTAINER} sh -c "${cleanCmd.replace(/"/g, '\\"')}"`;
|
|
3222
3190
|
}
|
|
3223
|
-
async function
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
scenario ? searchByScenario(boxName, scenario) : Promise.resolve([]),
|
|
3227
|
-
searchExploits(boxName)
|
|
3228
|
-
]);
|
|
3229
|
-
return { machine, scenario: scenarioResults, exploits };
|
|
3191
|
+
async function shouldUseDocker(command) {
|
|
3192
|
+
if (!USE_DOCKER) return false;
|
|
3193
|
+
return await isDockerAvailable();
|
|
3230
3194
|
}
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
var DOCKER_CONTAINER = process.env.PENTESTING_CONTAINER || "pentesting-tools";
|
|
3236
|
-
var FORCE_DOCKER = process.env.PENTESTING_DOCKER === "1";
|
|
3237
|
-
var dockerStarted = false;
|
|
3238
|
-
var DOCKER_TOOLS = [
|
|
3239
|
-
// Network scanning
|
|
3240
|
-
TOOL_NAME.RUSTSCAN,
|
|
3241
|
-
TOOL_NAME.NMAP_SCAN,
|
|
3242
|
-
TOOL_NAME.MASSCAN,
|
|
3243
|
-
TOOL_NAME.TCPDUMP_CAPTURE,
|
|
3244
|
-
TOOL_NAME.PING,
|
|
3245
|
-
TOOL_NAME.TRACEROUTE,
|
|
3246
|
-
TOOL_NAME.MTR,
|
|
3247
|
-
TOOL_NAME.NETCAT,
|
|
3248
|
-
TOOL_NAME.TSHARK,
|
|
3249
|
-
TOOL_NAME.NGREP,
|
|
3250
|
-
TOOL_NAME.ARP_SCAN,
|
|
3251
|
-
TOOL_NAME.SOCAT,
|
|
3252
|
-
// DNS & Subdomain
|
|
3253
|
-
TOOL_NAME.DIG,
|
|
3254
|
-
TOOL_NAME.HOST,
|
|
3255
|
-
TOOL_NAME.NSLOOKUP,
|
|
3256
|
-
TOOL_NAME.WHOIS,
|
|
3257
|
-
TOOL_NAME.SUBFINDER,
|
|
3258
|
-
TOOL_NAME.AMASS,
|
|
3259
|
-
TOOL_NAME.DNSENUM,
|
|
3260
|
-
TOOL_NAME.DNSRECON,
|
|
3261
|
-
TOOL_NAME.DNSMAP,
|
|
3262
|
-
TOOL_NAME.ZONE_TRANSFER,
|
|
3263
|
-
// Service Enumeration
|
|
3264
|
-
TOOL_NAME.SNMP_WALK,
|
|
3265
|
-
TOOL_NAME.SNMP_CHECK,
|
|
3266
|
-
TOOL_NAME.ONESIXTYONE,
|
|
3267
|
-
TOOL_NAME.FTP_ENUM,
|
|
3268
|
-
TOOL_NAME.FTP_ANON,
|
|
3269
|
-
TOOL_NAME.NBTSCAN,
|
|
3270
|
-
TOOL_NAME.RPC_INFO,
|
|
3271
|
-
TOOL_NAME.SHOWMOUNT,
|
|
3272
|
-
TOOL_NAME.TELNET,
|
|
3273
|
-
// Web tools
|
|
3274
|
-
TOOL_NAME.FFUF,
|
|
3275
|
-
TOOL_NAME.GOBUSTER,
|
|
3276
|
-
TOOL_NAME.DIRB,
|
|
3277
|
-
TOOL_NAME.FEROXBUSTER,
|
|
3278
|
-
TOOL_NAME.WHATWEB,
|
|
3279
|
-
TOOL_NAME.HTTPX,
|
|
3280
|
-
TOOL_NAME.NUCLEI,
|
|
3281
|
-
TOOL_NAME.NIKTO,
|
|
3282
|
-
TOOL_NAME.DIRECTORY_BRUTEFORCE,
|
|
3283
|
-
TOOL_NAME.SQL_INJECTION,
|
|
3284
|
-
TOOL_NAME.WAYBACKURLS,
|
|
3285
|
-
TOOL_NAME.WAFW00F,
|
|
3286
|
-
TOOL_NAME.GOWITNESS,
|
|
3287
|
-
// Windows/SMB/AD
|
|
3288
|
-
TOOL_NAME.SMB_ENUM,
|
|
3289
|
-
TOOL_NAME.SMBMAP,
|
|
3290
|
-
TOOL_NAME.ENUM4LINUX,
|
|
3291
|
-
TOOL_NAME.CRACKMAPEXEC,
|
|
3292
|
-
TOOL_NAME.SMBCLIENT,
|
|
3293
|
-
TOOL_NAME.RPCCLIENT,
|
|
3294
|
-
TOOL_NAME.WINRM,
|
|
3295
|
-
TOOL_NAME.RDP_CHECK,
|
|
3296
|
-
TOOL_NAME.LDAP_SEARCH,
|
|
3297
|
-
TOOL_NAME.KERBRUTE,
|
|
3298
|
-
TOOL_NAME.BLOODHOUND,
|
|
3299
|
-
// Database
|
|
3300
|
-
TOOL_NAME.MSSQL_CLIENT,
|
|
3301
|
-
TOOL_NAME.MYSQL_CLIENT,
|
|
3302
|
-
TOOL_NAME.PSQL_CLIENT,
|
|
3303
|
-
TOOL_NAME.REDIS_CLI,
|
|
3304
|
-
TOOL_NAME.MONGO_CLIENT,
|
|
3305
|
-
// Bruteforce & Credentials
|
|
3306
|
-
TOOL_NAME.HYDRA,
|
|
3307
|
-
TOOL_NAME.MEDUSA,
|
|
3308
|
-
TOOL_NAME.BRUTEFORCE_LOGIN,
|
|
3309
|
-
TOOL_NAME.CRACK_HASH,
|
|
3310
|
-
TOOL_NAME.JOHN,
|
|
3311
|
-
TOOL_NAME.HASHCAT,
|
|
3312
|
-
TOOL_NAME.HASHID,
|
|
3313
|
-
// Exploitation
|
|
3314
|
-
TOOL_NAME.SEARCHSPLOIT,
|
|
3315
|
-
TOOL_NAME.METASPLOIT,
|
|
3316
|
-
TOOL_NAME.GENERATE_PAYLOAD,
|
|
3317
|
-
TOOL_NAME.MSFVENOM,
|
|
3318
|
-
// SSH & Tunneling
|
|
3319
|
-
TOOL_NAME.SSH,
|
|
3320
|
-
TOOL_NAME.CHISEL,
|
|
3321
|
-
TOOL_NAME.PROXYCHAINS,
|
|
3322
|
-
TOOL_NAME.SETUP_TUNNEL,
|
|
3323
|
-
TOOL_NAME.LATERAL_MOVEMENT,
|
|
3324
|
-
TOOL_NAME.REVERSE_SHELL,
|
|
3325
|
-
TOOL_NAME.DUMP_CREDENTIALS,
|
|
3326
|
-
// Listener & Payload
|
|
3327
|
-
TOOL_NAME.NC_LISTENER,
|
|
3328
|
-
TOOL_NAME.PYTHON_HTTP_SERVER,
|
|
3329
|
-
TOOL_NAME.RLWRAP,
|
|
3330
|
-
TOOL_NAME.PWNCAT,
|
|
3331
|
-
// Impacket
|
|
3332
|
-
TOOL_NAME.IMPACKET_SECRETSDUMP,
|
|
3333
|
-
TOOL_NAME.IMPACKET_PSEXEC,
|
|
3334
|
-
TOOL_NAME.IMPACKET_WMIEXEC,
|
|
3335
|
-
TOOL_NAME.IMPACKET_SMBEXEC,
|
|
3336
|
-
TOOL_NAME.IMPACKET_ATEXEC,
|
|
3337
|
-
TOOL_NAME.IMPACKET_DCOMEXEC,
|
|
3338
|
-
TOOL_NAME.IMPACKET_GETNPUSERS,
|
|
3339
|
-
TOOL_NAME.IMPACKET_GETUSERSPNS,
|
|
3340
|
-
// Forensics
|
|
3341
|
-
TOOL_NAME.BINWALK,
|
|
3342
|
-
TOOL_NAME.FOREMOST,
|
|
3343
|
-
TOOL_NAME.STEGHIDE,
|
|
3344
|
-
TOOL_NAME.EXIFTOOL,
|
|
3345
|
-
// Reversing
|
|
3346
|
-
TOOL_NAME.GDB,
|
|
3347
|
-
TOOL_NAME.RADARE2,
|
|
3348
|
-
// Privesc
|
|
3349
|
-
TOOL_NAME.RUN_PRIVESC_ENUM,
|
|
3350
|
-
TOOL_NAME.CHECK_SUDO,
|
|
3351
|
-
TOOL_NAME.FIND_SUID
|
|
3352
|
-
];
|
|
3353
|
-
async function ensureDockerContainer() {
|
|
3354
|
-
if (dockerStarted) return true;
|
|
3195
|
+
async function executeBash(command, options = {}) {
|
|
3196
|
+
const startTime = Date.now();
|
|
3197
|
+
const timeout = options.timeout || 6e4;
|
|
3198
|
+
const resourceManager = getResourceManager();
|
|
3355
3199
|
try {
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
}
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
return false;
|
|
3200
|
+
let finalCommand = command;
|
|
3201
|
+
const useDocker = options.useDocker ?? await shouldUseDocker(command);
|
|
3202
|
+
if (useDocker) {
|
|
3203
|
+
finalCommand = wrapForDocker(command);
|
|
3204
|
+
} else {
|
|
3205
|
+
const baseCmd = command.trim().split(/\s+/)[0].replace(/^sudo\s+/, "");
|
|
3206
|
+
const aptPackage = TOOL_TO_APT[baseCmd];
|
|
3207
|
+
if (aptPackage && !await commandExists(baseCmd)) {
|
|
3208
|
+
const installed = await ensureToolAvailable(baseCmd);
|
|
3209
|
+
if (!installed) {
|
|
3210
|
+
return {
|
|
3211
|
+
success: false,
|
|
3212
|
+
output: "",
|
|
3213
|
+
error: `Tool '${baseCmd}' is not installed and auto-install failed. Run: sudo apt install ${aptPackage}`,
|
|
3214
|
+
duration: 0
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3375
3218
|
}
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3219
|
+
if (options.background) {
|
|
3220
|
+
const child = spawn("/bin/bash", ["-c", finalCommand], {
|
|
3221
|
+
detached: true,
|
|
3222
|
+
stdio: "ignore",
|
|
3223
|
+
env: { ...process.env, LANG: "en_US.UTF-8", LC_ALL: "en_US.UTF-8" }
|
|
3381
3224
|
});
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
console.log(" Linux: sudo systemctl start docker");
|
|
3391
|
-
console.log(" Windows: Start Docker Desktop\n");
|
|
3392
|
-
} else {
|
|
3393
|
-
console.error(`[Docker] Failed to start container: ${createErr}`);
|
|
3225
|
+
if (child.pid) {
|
|
3226
|
+
resourceManager.trackProcess(child.pid);
|
|
3227
|
+
child.on("exit", () => {
|
|
3228
|
+
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
3229
|
+
});
|
|
3230
|
+
child.on("error", () => {
|
|
3231
|
+
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
3232
|
+
});
|
|
3394
3233
|
}
|
|
3395
|
-
|
|
3234
|
+
child.unref();
|
|
3235
|
+
return { success: true, output: `[Background] Command started with PID ${child.pid}`, duration: 0 };
|
|
3396
3236
|
}
|
|
3237
|
+
const promise = new Promise((resolve, reject) => {
|
|
3238
|
+
const child = exec(finalCommand, {
|
|
3239
|
+
timeout,
|
|
3240
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
3241
|
+
shell: "/bin/bash",
|
|
3242
|
+
encoding: "utf8",
|
|
3243
|
+
env: { ...process.env, LANG: "en_US.UTF-8", LC_ALL: "en_US.UTF-8" }
|
|
3244
|
+
}, (error, stdout2, stderr2) => {
|
|
3245
|
+
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
3246
|
+
if (error) reject({ ...error, stdout: stdout2, stderr: stderr2 });
|
|
3247
|
+
else resolve({ stdout: stdout2, stderr: stderr2 });
|
|
3248
|
+
});
|
|
3249
|
+
if (child.pid) resourceManager.trackProcess(child.pid);
|
|
3250
|
+
});
|
|
3251
|
+
const { stdout, stderr } = await promise;
|
|
3252
|
+
return {
|
|
3253
|
+
success: true,
|
|
3254
|
+
output: (useDocker ? "[Docker] " : "") + stdout + (stderr ? `
|
|
3255
|
+
[STDERR]
|
|
3256
|
+
${stderr}` : ""),
|
|
3257
|
+
duration: Math.round((Date.now() - startTime) / 1e3)
|
|
3258
|
+
};
|
|
3259
|
+
} catch (error) {
|
|
3260
|
+
const err = error;
|
|
3261
|
+
if (err.message?.includes("No such container")) {
|
|
3262
|
+
return { success: false, output: "", error: `Docker container '${DOCKER_CONTAINER}' not running.`, duration: 0 };
|
|
3263
|
+
}
|
|
3264
|
+
return { success: false, output: err.stdout || "", error: err.stderr || err.message, exitCode: err.code, duration: 0 };
|
|
3397
3265
|
}
|
|
3398
3266
|
}
|
|
3399
|
-
|
|
3400
|
-
|
|
3267
|
+
var _currentTarget = null;
|
|
3268
|
+
var _targetListeners = [];
|
|
3269
|
+
function onTargetChange(listener) {
|
|
3270
|
+
_targetListeners.push(listener);
|
|
3401
3271
|
}
|
|
3402
|
-
async function
|
|
3272
|
+
async function setTarget(target) {
|
|
3403
3273
|
try {
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3274
|
+
_currentTarget = target;
|
|
3275
|
+
_targetListeners.forEach((listener) => {
|
|
3276
|
+
try {
|
|
3277
|
+
listener(target);
|
|
3278
|
+
} catch (e) {
|
|
3279
|
+
}
|
|
3280
|
+
});
|
|
3281
|
+
return { success: true, output: `\u{1F3AF} Target set: ${target}
|
|
3282
|
+
|
|
3283
|
+
Now beginning reconnaissance...`, duration: 0 };
|
|
3284
|
+
} catch (error) {
|
|
3285
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3286
|
+
return { success: false, output: "", error: errorMessage, duration: 0 };
|
|
3408
3287
|
}
|
|
3409
3288
|
}
|
|
3410
|
-
async function
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3289
|
+
async function readFile2(filePath, startLine, endLine) {
|
|
3290
|
+
try {
|
|
3291
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
3292
|
+
let output = content;
|
|
3293
|
+
if (startLine || endLine) {
|
|
3294
|
+
const lines = content.split("\n");
|
|
3295
|
+
output = lines.slice((startLine || 1) - 1, endLine || lines.length).join("\n");
|
|
3296
|
+
}
|
|
3297
|
+
return { success: true, output, duration: 0 };
|
|
3298
|
+
} catch (error) {
|
|
3299
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3300
|
+
return { success: false, output: "", error: errorMessage, duration: 0 };
|
|
3415
3301
|
}
|
|
3416
|
-
|
|
3417
|
-
|
|
3302
|
+
}
|
|
3303
|
+
async function writeFile2(filePath, content, overwrite = false) {
|
|
3304
|
+
try {
|
|
3305
|
+
const exists = await fs.access(filePath).then(() => true).catch(() => false);
|
|
3306
|
+
if (exists && !overwrite) {
|
|
3307
|
+
return { success: false, output: "", error: "File exists. Set overwrite: true to replace.", duration: 0 };
|
|
3308
|
+
}
|
|
3309
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
3310
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
3311
|
+
return { success: true, output: `Written to ${filePath}`, duration: 0 };
|
|
3312
|
+
} catch (error) {
|
|
3313
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3314
|
+
return { success: false, output: "", error: errorMessage, duration: 0 };
|
|
3418
3315
|
}
|
|
3419
|
-
return await isDockerAvailable();
|
|
3420
3316
|
}
|
|
3421
|
-
function
|
|
3422
|
-
|
|
3423
|
-
|
|
3317
|
+
async function listDirectory(dirPath, recursive = false, hidden = false) {
|
|
3318
|
+
try {
|
|
3319
|
+
const flags = `-l${hidden ? "a" : ""}${recursive ? "R" : ""}`;
|
|
3320
|
+
const { stdout } = await execAsync(`ls ${flags} "${dirPath}"`);
|
|
3321
|
+
return { success: true, output: stdout, duration: 0 };
|
|
3322
|
+
} catch (error) {
|
|
3323
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3324
|
+
return { success: false, output: "", error: errorMessage, duration: 0 };
|
|
3325
|
+
}
|
|
3424
3326
|
}
|
|
3327
|
+
|
|
3328
|
+
// src/core/tools/tool-executor.ts
|
|
3329
|
+
import * as fs2 from "fs/promises";
|
|
3425
3330
|
async function executeToolCall(toolName, input) {
|
|
3426
3331
|
const startTime = Date.now();
|
|
3427
3332
|
const resourceManager = getResourceManager();
|
|
@@ -3796,28 +3701,28 @@ async function executeToolCall(toolName, input) {
|
|
|
3796
3701
|
result = await takeScreenshot(input);
|
|
3797
3702
|
break;
|
|
3798
3703
|
// Research & Writeup Tools
|
|
3799
|
-
case
|
|
3704
|
+
case TOOL_NAME.SEARCH_WRITEUPS:
|
|
3800
3705
|
result = await executeSearchWriteups(input);
|
|
3801
3706
|
break;
|
|
3802
|
-
case
|
|
3707
|
+
case TOOL_NAME.SEARCH_MACHINE:
|
|
3803
3708
|
result = await executeSearchMachine(input);
|
|
3804
3709
|
break;
|
|
3805
|
-
case
|
|
3710
|
+
case TOOL_NAME.SEARCH_BY_SCENARIO:
|
|
3806
3711
|
result = await executeSearchByScenario(input);
|
|
3807
3712
|
break;
|
|
3808
|
-
case
|
|
3713
|
+
case TOOL_NAME.SEARCH_AD_WRITEUPS:
|
|
3809
3714
|
result = await executeSearchADWriteups(input);
|
|
3810
3715
|
break;
|
|
3811
|
-
case
|
|
3716
|
+
case TOOL_NAME.SEARCH_LINUX_PRIVESC:
|
|
3812
3717
|
result = await executeSearchLinuxPrivesc(input);
|
|
3813
3718
|
break;
|
|
3814
|
-
case
|
|
3719
|
+
case TOOL_NAME.SEARCH_WINDOWS_PRIVESC:
|
|
3815
3720
|
result = await executeSearchWindowsPrivesc(input);
|
|
3816
3721
|
break;
|
|
3817
|
-
case
|
|
3722
|
+
case TOOL_NAME.CTF_RESEARCH:
|
|
3818
3723
|
result = await executeCtfResearch(input);
|
|
3819
3724
|
break;
|
|
3820
|
-
case
|
|
3725
|
+
case TOOL_NAME.SECURITY_RESEARCH:
|
|
3821
3726
|
result = await executeSecurityResearch(input);
|
|
3822
3727
|
break;
|
|
3823
3728
|
default:
|
|
@@ -3827,175 +3732,19 @@ async function executeToolCall(toolName, input) {
|
|
|
3827
3732
|
error: `Unknown tool: ${toolName}`,
|
|
3828
3733
|
duration: Date.now() - startTime
|
|
3829
3734
|
};
|
|
3830
|
-
}
|
|
3831
|
-
result.duration = Date.now() - startTime;
|
|
3832
|
-
return result;
|
|
3833
|
-
} catch (error) {
|
|
3834
|
-
return {
|
|
3835
|
-
success: false,
|
|
3836
|
-
output: "",
|
|
3837
|
-
error: error.message || String(error),
|
|
3838
|
-
duration: Date.now() - startTime
|
|
3839
|
-
};
|
|
3840
|
-
} finally {
|
|
3841
|
-
resourceManager.trackJobEnd();
|
|
3842
|
-
}
|
|
3843
|
-
}
|
|
3844
|
-
async function executeBash(command, options = {}) {
|
|
3845
|
-
const startTime = Date.now();
|
|
3846
|
-
const timeout = options.timeout || 6e4;
|
|
3847
|
-
const resourceManager = getResourceManager();
|
|
3848
|
-
try {
|
|
3849
|
-
let finalCommand = command;
|
|
3850
|
-
const useDocker = options.useDocker ?? await shouldUseDocker(command);
|
|
3851
|
-
if (useDocker) {
|
|
3852
|
-
finalCommand = wrapForDocker(command);
|
|
3853
|
-
console.log(`[Docker] Executing: ${finalCommand}`);
|
|
3854
|
-
}
|
|
3855
|
-
if (options.background) {
|
|
3856
|
-
const child = spawn2("/bin/bash", ["-c", finalCommand], {
|
|
3857
|
-
detached: true,
|
|
3858
|
-
stdio: "ignore",
|
|
3859
|
-
env: {
|
|
3860
|
-
...process.env,
|
|
3861
|
-
LANG: "en_US.UTF-8",
|
|
3862
|
-
LC_ALL: "en_US.UTF-8"
|
|
3863
|
-
}
|
|
3864
|
-
});
|
|
3865
|
-
if (child.pid) {
|
|
3866
|
-
resourceManager.trackProcess(child.pid);
|
|
3867
|
-
child.on("exit", () => {
|
|
3868
|
-
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
3869
|
-
});
|
|
3870
|
-
child.on("error", () => {
|
|
3871
|
-
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
3872
|
-
});
|
|
3873
|
-
}
|
|
3874
|
-
child.unref();
|
|
3875
|
-
return {
|
|
3876
|
-
success: true,
|
|
3877
|
-
output: `[Background] Command started with PID ${child.pid}`,
|
|
3878
|
-
duration: 0
|
|
3879
|
-
};
|
|
3880
|
-
}
|
|
3881
|
-
const promise = new Promise((resolve, reject) => {
|
|
3882
|
-
const child = exec(finalCommand, {
|
|
3883
|
-
timeout,
|
|
3884
|
-
maxBuffer: 50 * 1024 * 1024,
|
|
3885
|
-
// 50MB
|
|
3886
|
-
shell: "/bin/bash",
|
|
3887
|
-
encoding: "utf8",
|
|
3888
|
-
env: {
|
|
3889
|
-
...process.env,
|
|
3890
|
-
LANG: "en_US.UTF-8",
|
|
3891
|
-
LC_ALL: "en_US.UTF-8"
|
|
3892
|
-
}
|
|
3893
|
-
}, (error, stdout2, stderr2) => {
|
|
3894
|
-
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
3895
|
-
if (error) reject({ ...error, stdout: stdout2, stderr: stderr2 });
|
|
3896
|
-
else resolve({ stdout: stdout2, stderr: stderr2 });
|
|
3897
|
-
});
|
|
3898
|
-
if (child.pid) {
|
|
3899
|
-
resourceManager.trackProcess(child.pid);
|
|
3900
|
-
}
|
|
3901
|
-
});
|
|
3902
|
-
const { stdout, stderr } = await promise;
|
|
3903
|
-
return {
|
|
3904
|
-
success: true,
|
|
3905
|
-
output: (useDocker ? "[Docker] " : "") + stdout + (stderr ? `
|
|
3906
|
-
[STDERR]
|
|
3907
|
-
${stderr}` : ""),
|
|
3908
|
-
duration: Math.round((Date.now() - startTime) / 1e3)
|
|
3909
|
-
};
|
|
3910
|
-
} catch (error) {
|
|
3911
|
-
if (error.message?.includes("No such container")) {
|
|
3912
|
-
return {
|
|
3913
|
-
success: false,
|
|
3914
|
-
output: "",
|
|
3915
|
-
error: `Docker container '${DOCKER_CONTAINER}' not running. Start it with: cd docker && docker-compose up -d`,
|
|
3916
|
-
duration: 0
|
|
3917
|
-
};
|
|
3918
|
-
}
|
|
3919
|
-
return {
|
|
3920
|
-
success: false,
|
|
3921
|
-
output: error.stdout || "",
|
|
3922
|
-
error: error.stderr || error.message,
|
|
3923
|
-
exitCode: error.code,
|
|
3924
|
-
duration: 0
|
|
3925
|
-
};
|
|
3926
|
-
}
|
|
3927
|
-
}
|
|
3928
|
-
var _currentTarget = null;
|
|
3929
|
-
var _targetListeners = [];
|
|
3930
|
-
function onTargetChange(listener) {
|
|
3931
|
-
_targetListeners.push(listener);
|
|
3932
|
-
}
|
|
3933
|
-
async function setTarget(target) {
|
|
3934
|
-
try {
|
|
3935
|
-
_currentTarget = target;
|
|
3936
|
-
_targetListeners.forEach((listener) => {
|
|
3937
|
-
try {
|
|
3938
|
-
listener(target);
|
|
3939
|
-
} catch (e) {
|
|
3940
|
-
console.error("Target listener error:", e);
|
|
3941
|
-
}
|
|
3942
|
-
});
|
|
3943
|
-
return {
|
|
3944
|
-
success: true,
|
|
3945
|
-
output: `\u{1F3AF} Target set: ${target}
|
|
3946
|
-
|
|
3947
|
-
Now beginning reconnaissance...`,
|
|
3948
|
-
duration: 0
|
|
3949
|
-
};
|
|
3735
|
+
}
|
|
3736
|
+
result.duration = Date.now() - startTime;
|
|
3737
|
+
return result;
|
|
3950
3738
|
} catch (error) {
|
|
3739
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
3951
3740
|
return {
|
|
3952
3741
|
success: false,
|
|
3953
3742
|
output: "",
|
|
3954
|
-
error:
|
|
3955
|
-
duration:
|
|
3743
|
+
error: errMsg,
|
|
3744
|
+
duration: Date.now() - startTime
|
|
3956
3745
|
};
|
|
3957
|
-
}
|
|
3958
|
-
|
|
3959
|
-
async function readFile2(filePath, startLine, endLine) {
|
|
3960
|
-
try {
|
|
3961
|
-
const content = await fs.readFile(filePath, "utf-8");
|
|
3962
|
-
let output = content;
|
|
3963
|
-
if (startLine || endLine) {
|
|
3964
|
-
const lines = content.split("\n");
|
|
3965
|
-
const start = (startLine || 1) - 1;
|
|
3966
|
-
const end = endLine || lines.length;
|
|
3967
|
-
output = lines.slice(start, end).join("\n");
|
|
3968
|
-
}
|
|
3969
|
-
return { success: true, output, duration: 0 };
|
|
3970
|
-
} catch (error) {
|
|
3971
|
-
return { success: false, output: "", error: error.message, duration: 0 };
|
|
3972
|
-
}
|
|
3973
|
-
}
|
|
3974
|
-
async function writeFile2(filePath, content, overwrite = false) {
|
|
3975
|
-
try {
|
|
3976
|
-
const exists = await fs.access(filePath).then(() => true).catch(() => false);
|
|
3977
|
-
if (exists && !overwrite) {
|
|
3978
|
-
return {
|
|
3979
|
-
success: false,
|
|
3980
|
-
output: "",
|
|
3981
|
-
error: "File exists. Set overwrite: true to replace.",
|
|
3982
|
-
duration: 0
|
|
3983
|
-
};
|
|
3984
|
-
}
|
|
3985
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
3986
|
-
await fs.writeFile(filePath, content, "utf-8");
|
|
3987
|
-
return { success: true, output: `Written to ${filePath}`, duration: 0 };
|
|
3988
|
-
} catch (error) {
|
|
3989
|
-
return { success: false, output: "", error: error.message, duration: 0 };
|
|
3990
|
-
}
|
|
3991
|
-
}
|
|
3992
|
-
async function listDirectory(dirPath, recursive = false, hidden = false) {
|
|
3993
|
-
try {
|
|
3994
|
-
const flags = `-l${hidden ? "a" : ""}${recursive ? "R" : ""}`;
|
|
3995
|
-
const { stdout } = await execAsync(`ls ${flags} "${dirPath}"`);
|
|
3996
|
-
return { success: true, output: stdout, duration: 0 };
|
|
3997
|
-
} catch (error) {
|
|
3998
|
-
return { success: false, output: "", error: error.message, duration: 0 };
|
|
3746
|
+
} finally {
|
|
3747
|
+
resourceManager.trackJobEnd();
|
|
3999
3748
|
}
|
|
4000
3749
|
}
|
|
4001
3750
|
async function executeRustscan(input) {
|
|
@@ -4134,7 +3883,7 @@ const { chromium } = require('playwright');
|
|
|
4134
3883
|
})();
|
|
4135
3884
|
`;
|
|
4136
3885
|
const scriptPath = "/tmp/playwright_script.js";
|
|
4137
|
-
await
|
|
3886
|
+
await fs2.writeFile(scriptPath, playwrightScript);
|
|
4138
3887
|
return executeBash(`node ${scriptPath}`, { timeout: 6e4 });
|
|
4139
3888
|
}
|
|
4140
3889
|
async function executeSearchsploit(input) {
|
|
@@ -4306,12 +4055,12 @@ async function reportFinding(input) {
|
|
|
4306
4055
|
const findingsPath = "/tmp/hacker-code-findings.json";
|
|
4307
4056
|
let findings = [];
|
|
4308
4057
|
try {
|
|
4309
|
-
const existing = await
|
|
4058
|
+
const existing = await fs2.readFile(findingsPath, "utf-8");
|
|
4310
4059
|
findings = JSON.parse(existing);
|
|
4311
4060
|
} catch {
|
|
4312
4061
|
}
|
|
4313
4062
|
findings.push(finding);
|
|
4314
|
-
await
|
|
4063
|
+
await fs2.writeFile(findingsPath, JSON.stringify(findings, null, 2));
|
|
4315
4064
|
return {
|
|
4316
4065
|
success: true,
|
|
4317
4066
|
output: `Finding recorded: [${severity?.toString().toUpperCase()}] ${title}`,
|
|
@@ -4332,7 +4081,7 @@ const { chromium } = require('playwright');
|
|
|
4332
4081
|
console.log('Screenshot saved');
|
|
4333
4082
|
})();
|
|
4334
4083
|
`;
|
|
4335
|
-
await
|
|
4084
|
+
await fs2.writeFile("/tmp/screenshot.js", script);
|
|
4336
4085
|
return executeBash("node /tmp/screenshot.js");
|
|
4337
4086
|
}
|
|
4338
4087
|
return executeBash(`script -q /dev/null -c "cat" | tee "${filename || "terminal.txt"}"`);
|
|
@@ -4930,14 +4679,14 @@ async function executeSearchWriteups(input) {
|
|
|
4930
4679
|
const startTime = Date.now();
|
|
4931
4680
|
try {
|
|
4932
4681
|
const query = String(input.query || "");
|
|
4933
|
-
const platform3 = input.platform || "
|
|
4682
|
+
const platform3 = input.platform || "general";
|
|
4934
4683
|
const results = await searchWriteups(query, platform3);
|
|
4935
4684
|
const output = results.length > 0 ? results.map((r, i) => `[${i + 1}] ${r.title}
|
|
4936
4685
|
URL: ${r.url}
|
|
4937
4686
|
${r.snippet}`).join("\n\n") : "No writeups found.";
|
|
4938
4687
|
return { success: true, output, duration: Date.now() - startTime };
|
|
4939
4688
|
} catch (error) {
|
|
4940
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4689
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
4941
4690
|
}
|
|
4942
4691
|
}
|
|
4943
4692
|
async function executeSearchMachine(input) {
|
|
@@ -4957,7 +4706,7 @@ async function executeSearchMachine(input) {
|
|
|
4957
4706
|
${r.url}`).join("\n") : "No videos found.";
|
|
4958
4707
|
return { success: true, output, duration: Date.now() - startTime };
|
|
4959
4708
|
} catch (error) {
|
|
4960
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4709
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
4961
4710
|
}
|
|
4962
4711
|
}
|
|
4963
4712
|
async function executeSearchByScenario(input) {
|
|
@@ -4971,7 +4720,7 @@ async function executeSearchByScenario(input) {
|
|
|
4971
4720
|
${r.snippet}`).join("\n\n") : `No ${scenario} writeups found.`;
|
|
4972
4721
|
return { success: true, output, duration: Date.now() - startTime };
|
|
4973
4722
|
} catch (error) {
|
|
4974
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4723
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
4975
4724
|
}
|
|
4976
4725
|
}
|
|
4977
4726
|
async function executeSearchADWriteups(input) {
|
|
@@ -4984,7 +4733,7 @@ async function executeSearchADWriteups(input) {
|
|
|
4984
4733
|
${r.snippet}`).join("\n\n") : "No AD writeups found.";
|
|
4985
4734
|
return { success: true, output, duration: Date.now() - startTime };
|
|
4986
4735
|
} catch (error) {
|
|
4987
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4736
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
4988
4737
|
}
|
|
4989
4738
|
}
|
|
4990
4739
|
async function executeSearchLinuxPrivesc(input) {
|
|
@@ -4997,7 +4746,7 @@ async function executeSearchLinuxPrivesc(input) {
|
|
|
4997
4746
|
${r.snippet}`).join("\n\n") : "No Linux privesc writeups found.";
|
|
4998
4747
|
return { success: true, output, duration: Date.now() - startTime };
|
|
4999
4748
|
} catch (error) {
|
|
5000
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4749
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
5001
4750
|
}
|
|
5002
4751
|
}
|
|
5003
4752
|
async function executeSearchWindowsPrivesc(input) {
|
|
@@ -5010,7 +4759,7 @@ async function executeSearchWindowsPrivesc(input) {
|
|
|
5010
4759
|
${r.snippet}`).join("\n\n") : "No Windows privesc writeups found.";
|
|
5011
4760
|
return { success: true, output, duration: Date.now() - startTime };
|
|
5012
4761
|
} catch (error) {
|
|
5013
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4762
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
5014
4763
|
}
|
|
5015
4764
|
}
|
|
5016
4765
|
async function executeCtfResearch(input) {
|
|
@@ -5041,7 +4790,7 @@ async function executeCtfResearch(input) {
|
|
|
5041
4790
|
${r.url}`).join("\n") : "No exploits found.";
|
|
5042
4791
|
return { success: true, output, duration: Date.now() - startTime };
|
|
5043
4792
|
} catch (error) {
|
|
5044
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4793
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
5045
4794
|
}
|
|
5046
4795
|
}
|
|
5047
4796
|
async function executeSecurityResearch(input) {
|
|
@@ -5066,7 +4815,7 @@ async function executeSecurityResearch(input) {
|
|
|
5066
4815
|
${r.url}`).join("\n") : "No writeups found.";
|
|
5067
4816
|
return { success: true, output, duration: Date.now() - startTime };
|
|
5068
4817
|
} catch (error) {
|
|
5069
|
-
return { success: false, output: "", error: error.message, duration: Date.now() - startTime };
|
|
4818
|
+
return { success: false, output: "", error: error instanceof Error ? error.message : String(error), duration: Date.now() - startTime };
|
|
5070
4819
|
}
|
|
5071
4820
|
}
|
|
5072
4821
|
|
|
@@ -6939,12 +6688,14 @@ EFFORT: [low/medium/high]`;
|
|
|
6939
6688
|
for (const section of sections.slice(1)) {
|
|
6940
6689
|
const match = section.match(/(\w+)\s+DESCRIPTION:\s*(.+)\s+REASONING:\s*(.+)\s+IMPACT:\s*([\d.]+)\s+EFFORT:\s*(\w+)/is);
|
|
6941
6690
|
if (match) {
|
|
6691
|
+
const suggestionType = match[1].toLowerCase().trim();
|
|
6692
|
+
const suggestionEffort = match[5].toLowerCase().trim();
|
|
6942
6693
|
suggestions.push({
|
|
6943
|
-
type:
|
|
6694
|
+
type: suggestionType,
|
|
6944
6695
|
description: match[2].trim(),
|
|
6945
6696
|
reasoning: match[3].trim(),
|
|
6946
6697
|
estimatedImpact: parseFloat(match[4]) || 0.5,
|
|
6947
|
-
effort:
|
|
6698
|
+
effort: suggestionEffort
|
|
6948
6699
|
});
|
|
6949
6700
|
}
|
|
6950
6701
|
}
|
|
@@ -7483,7 +7234,7 @@ import { v4 as uuidv46 } from "uuid";
|
|
|
7483
7234
|
import { EventEmitter as EventEmitter8 } from "events";
|
|
7484
7235
|
import { v4 as uuidv45 } from "uuid";
|
|
7485
7236
|
import { createHash } from "crypto";
|
|
7486
|
-
import { promises as
|
|
7237
|
+
import { promises as fs3 } from "fs";
|
|
7487
7238
|
import { join } from "path";
|
|
7488
7239
|
import { homedir } from "os";
|
|
7489
7240
|
var AUDIT_EVENT = {
|
|
@@ -7509,7 +7260,7 @@ var AuditLogger = class extends EventEmitter8 {
|
|
|
7509
7260
|
async initialize() {
|
|
7510
7261
|
if (this.initialized) return;
|
|
7511
7262
|
try {
|
|
7512
|
-
await
|
|
7263
|
+
await fs3.mkdir(this.logDir, { recursive: true });
|
|
7513
7264
|
this.initialized = true;
|
|
7514
7265
|
} catch (error) {
|
|
7515
7266
|
console.error("[AuditLogger] Failed to initialize:", error);
|
|
@@ -7606,19 +7357,19 @@ var AuditLogger = class extends EventEmitter8 {
|
|
|
7606
7357
|
async pruneDiskLogs(maxFiles = 1e3) {
|
|
7607
7358
|
if (!this.initialized) await this.initialize();
|
|
7608
7359
|
try {
|
|
7609
|
-
const files = await
|
|
7360
|
+
const files = await fs3.readdir(this.logDir);
|
|
7610
7361
|
if (files.length <= maxFiles) return 0;
|
|
7611
7362
|
const fileStats = await Promise.all(
|
|
7612
7363
|
files.map(async (file) => {
|
|
7613
7364
|
const filePath = join(this.logDir, file);
|
|
7614
|
-
const stats = await
|
|
7365
|
+
const stats = await fs3.stat(filePath);
|
|
7615
7366
|
return { file, time: stats.mtimeMs, path: filePath };
|
|
7616
7367
|
})
|
|
7617
7368
|
);
|
|
7618
7369
|
fileStats.sort((a, b) => a.time - b.time);
|
|
7619
7370
|
const toDelete = fileStats.slice(0, files.length - maxFiles);
|
|
7620
7371
|
for (const item of toDelete) {
|
|
7621
|
-
await
|
|
7372
|
+
await fs3.unlink(item.path);
|
|
7622
7373
|
}
|
|
7623
7374
|
return toDelete.length;
|
|
7624
7375
|
} catch (error) {
|
|
@@ -7689,7 +7440,7 @@ var AuditLogger = class extends EventEmitter8 {
|
|
|
7689
7440
|
this.logDir,
|
|
7690
7441
|
`audit_${this.sessionId}_${Date.now()}.json`
|
|
7691
7442
|
);
|
|
7692
|
-
await
|
|
7443
|
+
await fs3.writeFile(
|
|
7693
7444
|
exportPath,
|
|
7694
7445
|
JSON.stringify({
|
|
7695
7446
|
sessionId: this.sessionId,
|
|
@@ -7750,7 +7501,7 @@ var AuditLogger = class extends EventEmitter8 {
|
|
|
7750
7501
|
try {
|
|
7751
7502
|
const filename = `${entry.timestamp.replace(/[:.]/g, "-")}_${entry.id}.json`;
|
|
7752
7503
|
const filepath = join(this.logDir, filename);
|
|
7753
|
-
await
|
|
7504
|
+
await fs3.writeFile(filepath, JSON.stringify(entry, null, 2));
|
|
7754
7505
|
} catch (error) {
|
|
7755
7506
|
console.error("[AuditLogger] Failed to persist entry:", error);
|
|
7756
7507
|
}
|
|
@@ -7771,7 +7522,7 @@ var AuditLogger = class extends EventEmitter8 {
|
|
|
7771
7522
|
try {
|
|
7772
7523
|
const filename = `system_${entry.timestamp.replace(/[:.]/g, "-")}_${entry.id}.json`;
|
|
7773
7524
|
const filepath = join(this.logDir, filename);
|
|
7774
|
-
await
|
|
7525
|
+
await fs3.writeFile(filepath, JSON.stringify(entry, null, 2));
|
|
7775
7526
|
} catch (error) {
|
|
7776
7527
|
console.error("[AuditLogger] Failed to persist system event:", error);
|
|
7777
7528
|
}
|
|
@@ -10139,7 +9890,7 @@ Constraints: ${context.constraints.join(", ")}`;
|
|
|
10139
9890
|
// src/core/agent/learning/self-reflection.ts
|
|
10140
9891
|
import { EventEmitter as EventEmitter12 } from "events";
|
|
10141
9892
|
import { v4 as uuidv410 } from "uuid";
|
|
10142
|
-
import * as
|
|
9893
|
+
import * as fs4 from "fs/promises";
|
|
10143
9894
|
import * as path2 from "path";
|
|
10144
9895
|
var SelfReflectionSystem = class extends EventEmitter12 {
|
|
10145
9896
|
llm;
|
|
@@ -10393,10 +10144,10 @@ CONFIDENCE: [0.0-1.0]`;
|
|
|
10393
10144
|
*/
|
|
10394
10145
|
async persistExperience(experience) {
|
|
10395
10146
|
try {
|
|
10396
|
-
await
|
|
10147
|
+
await fs4.mkdir(this.dataDir, { recursive: true });
|
|
10397
10148
|
const filename = `${experience.id}.json`;
|
|
10398
10149
|
const filepath = path2.join(this.dataDir, filename);
|
|
10399
|
-
await
|
|
10150
|
+
await fs4.writeFile(filepath, JSON.stringify(experience, null, 2));
|
|
10400
10151
|
} catch (error) {
|
|
10401
10152
|
this.emit("persist_error", error);
|
|
10402
10153
|
}
|
|
@@ -10406,13 +10157,13 @@ CONFIDENCE: [0.0-1.0]`;
|
|
|
10406
10157
|
*/
|
|
10407
10158
|
async loadExperiences() {
|
|
10408
10159
|
try {
|
|
10409
|
-
await
|
|
10410
|
-
const files = await
|
|
10160
|
+
await fs4.mkdir(this.dataDir, { recursive: true });
|
|
10161
|
+
const files = await fs4.readdir(this.dataDir);
|
|
10411
10162
|
for (const file of files) {
|
|
10412
10163
|
if (file.endsWith(".json")) {
|
|
10413
10164
|
const filepath = path2.join(this.dataDir, file);
|
|
10414
10165
|
try {
|
|
10415
|
-
const content = await
|
|
10166
|
+
const content = await fs4.readFile(filepath, "utf-8");
|
|
10416
10167
|
const experience = JSON.parse(content);
|
|
10417
10168
|
this.experiences.push(experience);
|
|
10418
10169
|
for (const lesson of experience.lessons) {
|
|
@@ -10469,7 +10220,7 @@ CONFIDENCE: [0.0-1.0]`;
|
|
|
10469
10220
|
|
|
10470
10221
|
// src/core/agent/background/job-manager.ts
|
|
10471
10222
|
import { EventEmitter as EventEmitter13 } from "events";
|
|
10472
|
-
import { spawn as
|
|
10223
|
+
import { spawn as spawn2 } from "child_process";
|
|
10473
10224
|
import { v4 as uuidv411 } from "uuid";
|
|
10474
10225
|
var BackgroundJobManager = class extends EventEmitter13 {
|
|
10475
10226
|
jobs = /* @__PURE__ */ new Map();
|
|
@@ -10512,7 +10263,7 @@ var BackgroundJobManager = class extends EventEmitter13 {
|
|
|
10512
10263
|
this.jobs.set(job.id, job);
|
|
10513
10264
|
this.incrementResource(config.type);
|
|
10514
10265
|
try {
|
|
10515
|
-
const process2 =
|
|
10266
|
+
const process2 = spawn2(config.command, config.args || [], {
|
|
10516
10267
|
cwd: config.cwd,
|
|
10517
10268
|
shell: true,
|
|
10518
10269
|
detached: false,
|
|
@@ -10900,7 +10651,7 @@ var backgroundJobManager = new BackgroundJobManager();
|
|
|
10900
10651
|
// src/core/agent/session/checkpoint-manager.ts
|
|
10901
10652
|
import { EventEmitter as EventEmitter14 } from "events";
|
|
10902
10653
|
import { v4 as uuidv412 } from "uuid";
|
|
10903
|
-
import * as
|
|
10654
|
+
import * as fs5 from "fs/promises";
|
|
10904
10655
|
import * as path3 from "path";
|
|
10905
10656
|
var SessionManager = class extends EventEmitter14 {
|
|
10906
10657
|
checkpoints = /* @__PURE__ */ new Map();
|
|
@@ -11089,10 +10840,10 @@ var SessionManager = class extends EventEmitter14 {
|
|
|
11089
10840
|
async persistCheckpoint(checkpoint) {
|
|
11090
10841
|
try {
|
|
11091
10842
|
const sessionDir = path3.join(this.checkpointDir, checkpoint.sessionId);
|
|
11092
|
-
await
|
|
10843
|
+
await fs5.mkdir(sessionDir, { recursive: true });
|
|
11093
10844
|
const filename = `${checkpoint.id}.json`;
|
|
11094
10845
|
const filepath = path3.join(sessionDir, filename);
|
|
11095
|
-
await
|
|
10846
|
+
await fs5.writeFile(filepath, JSON.stringify(checkpoint, null, 2));
|
|
11096
10847
|
} catch (error) {
|
|
11097
10848
|
this.emit("persist_error", error);
|
|
11098
10849
|
}
|
|
@@ -11103,17 +10854,17 @@ var SessionManager = class extends EventEmitter14 {
|
|
|
11103
10854
|
async loadCheckpoints(sessionId) {
|
|
11104
10855
|
const checkpoints = [];
|
|
11105
10856
|
try {
|
|
11106
|
-
await
|
|
11107
|
-
const sessions = await
|
|
10857
|
+
await fs5.mkdir(this.checkpointDir, { recursive: true });
|
|
10858
|
+
const sessions = await fs5.readdir(this.checkpointDir);
|
|
11108
10859
|
const targetSessions = sessionId ? [sessionId] : sessions;
|
|
11109
10860
|
for (const session of targetSessions) {
|
|
11110
10861
|
const sessionDir = path3.join(this.checkpointDir, session);
|
|
11111
10862
|
try {
|
|
11112
|
-
const files = await
|
|
10863
|
+
const files = await fs5.readdir(sessionDir);
|
|
11113
10864
|
for (const file of files) {
|
|
11114
10865
|
if (file.endsWith(".json")) {
|
|
11115
10866
|
const filepath = path3.join(sessionDir, file);
|
|
11116
|
-
const content = await
|
|
10867
|
+
const content = await fs5.readFile(filepath, "utf-8");
|
|
11117
10868
|
try {
|
|
11118
10869
|
const checkpoint = JSON.parse(content);
|
|
11119
10870
|
checkpoints.push(checkpoint);
|
|
@@ -11152,7 +10903,7 @@ var SessionManager = class extends EventEmitter14 {
|
|
|
11152
10903
|
checkpoint.sessionId,
|
|
11153
10904
|
`${checkpointId}.json`
|
|
11154
10905
|
);
|
|
11155
|
-
await
|
|
10906
|
+
await fs5.unlink(filepath);
|
|
11156
10907
|
this.checkpoints.delete(checkpointId);
|
|
11157
10908
|
return true;
|
|
11158
10909
|
} catch {
|
|
@@ -11394,8 +11145,8 @@ var ContextManager = class {
|
|
|
11394
11145
|
};
|
|
11395
11146
|
|
|
11396
11147
|
// src/core/hooks/hook-executor.ts
|
|
11397
|
-
import { spawn as
|
|
11398
|
-
import * as
|
|
11148
|
+
import { spawn as spawn3 } from "child_process";
|
|
11149
|
+
import * as fs6 from "fs/promises";
|
|
11399
11150
|
import * as path4 from "path";
|
|
11400
11151
|
import { EventEmitter as EventEmitter15 } from "events";
|
|
11401
11152
|
var HookExecutor = class extends EventEmitter15 {
|
|
@@ -11410,7 +11161,7 @@ var HookExecutor = class extends EventEmitter15 {
|
|
|
11410
11161
|
async initialize() {
|
|
11411
11162
|
try {
|
|
11412
11163
|
const configPath = path4.join(this.hooksDir, "hooks.json");
|
|
11413
|
-
const configContent = await
|
|
11164
|
+
const configContent = await fs6.readFile(configPath, "utf-8");
|
|
11414
11165
|
this.hooksConfig = JSON.parse(configContent);
|
|
11415
11166
|
this.initialized = true;
|
|
11416
11167
|
this.emit("initialized", { hooks: Object.keys(this.hooksConfig?.hooks || {}) });
|
|
@@ -11452,7 +11203,7 @@ var HookExecutor = class extends EventEmitter15 {
|
|
|
11452
11203
|
HOOK_PHASE: context.phase || "",
|
|
11453
11204
|
HOOK_TARGET: context.target || ""
|
|
11454
11205
|
};
|
|
11455
|
-
const proc =
|
|
11206
|
+
const proc = spawn3("bash", ["-c", command], {
|
|
11456
11207
|
env,
|
|
11457
11208
|
cwd: this.hooksDir,
|
|
11458
11209
|
timeout
|
|
@@ -11524,205 +11275,8 @@ function getHookExecutor() {
|
|
|
11524
11275
|
return hookExecutor;
|
|
11525
11276
|
}
|
|
11526
11277
|
|
|
11527
|
-
// src/mcp/mcp-client.ts
|
|
11528
|
-
import { spawn as spawn5 } from "child_process";
|
|
11529
|
-
import { EventEmitter as EventEmitter16 } from "events";
|
|
11530
|
-
import * as readline from "readline";
|
|
11531
|
-
var MCPClient = class extends EventEmitter16 {
|
|
11532
|
-
process = null;
|
|
11533
|
-
requestId = 0;
|
|
11534
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
11535
|
-
serverName;
|
|
11536
|
-
config;
|
|
11537
|
-
connected = false;
|
|
11538
|
-
constructor(serverName, config) {
|
|
11539
|
-
super();
|
|
11540
|
-
this.serverName = serverName;
|
|
11541
|
-
this.config = config;
|
|
11542
|
-
}
|
|
11543
|
-
// Start MCP server
|
|
11544
|
-
async connect() {
|
|
11545
|
-
return new Promise((resolve, reject) => {
|
|
11546
|
-
const { command, args, env } = this.config;
|
|
11547
|
-
this.process = spawn5(command, args || [], {
|
|
11548
|
-
env: { ...process.env, ...env },
|
|
11549
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
11550
|
-
});
|
|
11551
|
-
if (this.process.stdout) {
|
|
11552
|
-
const rl = readline.createInterface({
|
|
11553
|
-
input: this.process.stdout,
|
|
11554
|
-
crlfDelay: Infinity
|
|
11555
|
-
});
|
|
11556
|
-
rl.on("line", (line) => {
|
|
11557
|
-
try {
|
|
11558
|
-
const response = JSON.parse(line);
|
|
11559
|
-
this.handleResponse(response);
|
|
11560
|
-
} catch {
|
|
11561
|
-
}
|
|
11562
|
-
});
|
|
11563
|
-
}
|
|
11564
|
-
if (this.process.stderr) {
|
|
11565
|
-
this.process.stderr.on("data", (data) => {
|
|
11566
|
-
this.emit("stderr", data.toString());
|
|
11567
|
-
});
|
|
11568
|
-
}
|
|
11569
|
-
this.process.on("error", (error) => {
|
|
11570
|
-
this.emit("error", error);
|
|
11571
|
-
reject(error);
|
|
11572
|
-
});
|
|
11573
|
-
this.process.on("spawn", () => {
|
|
11574
|
-
this.connected = true;
|
|
11575
|
-
this.emit("connected", { server: this.serverName });
|
|
11576
|
-
resolve();
|
|
11577
|
-
});
|
|
11578
|
-
this.process.on("close", (code) => {
|
|
11579
|
-
this.connected = false;
|
|
11580
|
-
this.emit("disconnected", { server: this.serverName, code });
|
|
11581
|
-
});
|
|
11582
|
-
});
|
|
11583
|
-
}
|
|
11584
|
-
// Send JSON-RPC request
|
|
11585
|
-
async sendRequest(method, params) {
|
|
11586
|
-
if (!this.process?.stdin || !this.connected) {
|
|
11587
|
-
throw new Error("MCP server not connected");
|
|
11588
|
-
}
|
|
11589
|
-
const id = ++this.requestId;
|
|
11590
|
-
const request = {
|
|
11591
|
-
jsonrpc: "2.0",
|
|
11592
|
-
id,
|
|
11593
|
-
method,
|
|
11594
|
-
params
|
|
11595
|
-
};
|
|
11596
|
-
return new Promise((resolve, reject) => {
|
|
11597
|
-
this.pendingRequests.set(id, { resolve, reject });
|
|
11598
|
-
const line = JSON.stringify(request) + "\n";
|
|
11599
|
-
this.process.stdin.write(line, (error) => {
|
|
11600
|
-
if (error) {
|
|
11601
|
-
this.pendingRequests.delete(id);
|
|
11602
|
-
reject(error);
|
|
11603
|
-
}
|
|
11604
|
-
});
|
|
11605
|
-
setTimeout(() => {
|
|
11606
|
-
if (this.pendingRequests.has(id)) {
|
|
11607
|
-
this.pendingRequests.delete(id);
|
|
11608
|
-
reject(new Error("MCP request timeout"));
|
|
11609
|
-
}
|
|
11610
|
-
}, 3e4);
|
|
11611
|
-
});
|
|
11612
|
-
}
|
|
11613
|
-
// Handle JSON-RPC response
|
|
11614
|
-
handleResponse(response) {
|
|
11615
|
-
const pending = this.pendingRequests.get(response.id);
|
|
11616
|
-
if (!pending) {
|
|
11617
|
-
return;
|
|
11618
|
-
}
|
|
11619
|
-
this.pendingRequests.delete(response.id);
|
|
11620
|
-
if (response.error) {
|
|
11621
|
-
pending.reject(new Error(response.error.message));
|
|
11622
|
-
} else {
|
|
11623
|
-
pending.resolve(response.result);
|
|
11624
|
-
}
|
|
11625
|
-
}
|
|
11626
|
-
// Initialize MCP server
|
|
11627
|
-
async initialize() {
|
|
11628
|
-
return this.sendRequest("initialize", {
|
|
11629
|
-
protocolVersion: "2024-11-05",
|
|
11630
|
-
capabilities: {},
|
|
11631
|
-
clientInfo: {
|
|
11632
|
-
name: "pentesting",
|
|
11633
|
-
version: "1.0.0"
|
|
11634
|
-
}
|
|
11635
|
-
});
|
|
11636
|
-
}
|
|
11637
|
-
// List available tools
|
|
11638
|
-
async listTools() {
|
|
11639
|
-
const result = await this.sendRequest("tools/list");
|
|
11640
|
-
return result.tools || [];
|
|
11641
|
-
}
|
|
11642
|
-
// Call a tool
|
|
11643
|
-
async callTool(name, arguments_) {
|
|
11644
|
-
return this.sendRequest("tools/call", { name, arguments: arguments_ });
|
|
11645
|
-
}
|
|
11646
|
-
// List resources
|
|
11647
|
-
async listResources() {
|
|
11648
|
-
const result = await this.sendRequest("resources/list");
|
|
11649
|
-
return result.resources || [];
|
|
11650
|
-
}
|
|
11651
|
-
// Read a resource
|
|
11652
|
-
async readResource(uri) {
|
|
11653
|
-
return this.sendRequest("resources/read", { uri });
|
|
11654
|
-
}
|
|
11655
|
-
// Disconnect
|
|
11656
|
-
async disconnect() {
|
|
11657
|
-
if (this.process) {
|
|
11658
|
-
this.process.kill();
|
|
11659
|
-
this.process = null;
|
|
11660
|
-
this.connected = false;
|
|
11661
|
-
}
|
|
11662
|
-
}
|
|
11663
|
-
// Check if connected
|
|
11664
|
-
isConnected() {
|
|
11665
|
-
return this.connected;
|
|
11666
|
-
}
|
|
11667
|
-
};
|
|
11668
|
-
var MCPManager = class extends EventEmitter16 {
|
|
11669
|
-
clients = /* @__PURE__ */ new Map();
|
|
11670
|
-
tools = /* @__PURE__ */ new Map();
|
|
11671
|
-
// Add and connect to a server
|
|
11672
|
-
async addServer(name, config) {
|
|
11673
|
-
const client = new MCPClient(name, config);
|
|
11674
|
-
client.on("error", (error) => this.emit("error", { server: name, error }));
|
|
11675
|
-
client.on("connected", () => this.emit("server_connected", { server: name }));
|
|
11676
|
-
client.on("disconnected", (info) => this.emit("server_disconnected", info));
|
|
11677
|
-
try {
|
|
11678
|
-
await client.connect();
|
|
11679
|
-
await client.initialize();
|
|
11680
|
-
const tools = await client.listTools();
|
|
11681
|
-
for (const tool of tools) {
|
|
11682
|
-
this.tools.set(tool.name, { server: name, tool });
|
|
11683
|
-
}
|
|
11684
|
-
this.clients.set(name, client);
|
|
11685
|
-
this.emit("tools_loaded", { server: name, count: tools.length });
|
|
11686
|
-
} catch (error) {
|
|
11687
|
-
this.emit("error", { server: name, error });
|
|
11688
|
-
throw error;
|
|
11689
|
-
}
|
|
11690
|
-
}
|
|
11691
|
-
// Call a tool by name
|
|
11692
|
-
async callTool(toolName, args) {
|
|
11693
|
-
const entry = this.tools.get(toolName);
|
|
11694
|
-
if (!entry) {
|
|
11695
|
-
throw new Error(`Unknown MCP tool: ${toolName}`);
|
|
11696
|
-
}
|
|
11697
|
-
const client = this.clients.get(entry.server);
|
|
11698
|
-
if (!client) {
|
|
11699
|
-
throw new Error(`MCP server not connected: ${entry.server}`);
|
|
11700
|
-
}
|
|
11701
|
-
return client.callTool(toolName, args);
|
|
11702
|
-
}
|
|
11703
|
-
// Get all available tools
|
|
11704
|
-
getAvailableTools() {
|
|
11705
|
-
return Array.from(this.tools.values()).map((e) => e.tool);
|
|
11706
|
-
}
|
|
11707
|
-
// Disconnect all
|
|
11708
|
-
async disconnectAll() {
|
|
11709
|
-
for (const client of this.clients.values()) {
|
|
11710
|
-
await client.disconnect();
|
|
11711
|
-
}
|
|
11712
|
-
this.clients.clear();
|
|
11713
|
-
this.tools.clear();
|
|
11714
|
-
}
|
|
11715
|
-
};
|
|
11716
|
-
var mcpManager = null;
|
|
11717
|
-
function getMCPManager() {
|
|
11718
|
-
if (!mcpManager) {
|
|
11719
|
-
mcpManager = new MCPManager();
|
|
11720
|
-
}
|
|
11721
|
-
return mcpManager;
|
|
11722
|
-
}
|
|
11723
|
-
|
|
11724
11278
|
// src/core/approval/approval-manager.ts
|
|
11725
|
-
import { EventEmitter as
|
|
11279
|
+
import { EventEmitter as EventEmitter16 } from "events";
|
|
11726
11280
|
var APPROVAL_EVENT = {
|
|
11727
11281
|
REQUEST: "approval_request",
|
|
11728
11282
|
RESPONSE: "approval_response",
|
|
@@ -11893,7 +11447,7 @@ function assessRisk(toolName, toolInput) {
|
|
|
11893
11447
|
function generateRequestId() {
|
|
11894
11448
|
return `approval_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
|
|
11895
11449
|
}
|
|
11896
|
-
var ApprovalManager = class extends
|
|
11450
|
+
var ApprovalManager = class extends EventEmitter16 {
|
|
11897
11451
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
11898
11452
|
autoApprovedTools = /* @__PURE__ */ new Set();
|
|
11899
11453
|
autoDeniedTools = /* @__PURE__ */ new Set();
|
|
@@ -12007,103 +11561,682 @@ var ApprovalManager = class extends EventEmitter17 {
|
|
|
12007
11561
|
respondedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12008
11562
|
});
|
|
12009
11563
|
}
|
|
12010
|
-
resolve("deny");
|
|
12011
|
-
} else {
|
|
12012
|
-
resolve(response.decision);
|
|
11564
|
+
resolve("deny");
|
|
11565
|
+
} else {
|
|
11566
|
+
resolve(response.decision);
|
|
11567
|
+
}
|
|
11568
|
+
}
|
|
11569
|
+
};
|
|
11570
|
+
this.on(APPROVAL_EVENT.RESPONSE, responseHandler);
|
|
11571
|
+
});
|
|
11572
|
+
}
|
|
11573
|
+
/**
|
|
11574
|
+
* Respond to an approval request (called by UI)
|
|
11575
|
+
* Now handles timed YOLO modes
|
|
11576
|
+
*/
|
|
11577
|
+
respond(requestId, decision) {
|
|
11578
|
+
if (decision === "yolo_5min") {
|
|
11579
|
+
this.setYoloMode(true, 5 * 60 * 1e3);
|
|
11580
|
+
decision = "approve";
|
|
11581
|
+
} else if (decision === "yolo_15min") {
|
|
11582
|
+
this.setYoloMode(true, 15 * 60 * 1e3);
|
|
11583
|
+
decision = "approve";
|
|
11584
|
+
} else if (decision === "yolo_30min") {
|
|
11585
|
+
this.setYoloMode(true, 30 * 60 * 1e3);
|
|
11586
|
+
decision = "approve";
|
|
11587
|
+
}
|
|
11588
|
+
if (decision === "approve" || decision === "approve_always") {
|
|
11589
|
+
this.stats.approved++;
|
|
11590
|
+
} else if (decision === "deny" || decision === "deny_always") {
|
|
11591
|
+
this.stats.denied++;
|
|
11592
|
+
}
|
|
11593
|
+
const response = {
|
|
11594
|
+
requestId,
|
|
11595
|
+
decision,
|
|
11596
|
+
respondedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11597
|
+
};
|
|
11598
|
+
this.emit(APPROVAL_EVENT.RESPONSE, response);
|
|
11599
|
+
}
|
|
11600
|
+
/**
|
|
11601
|
+
* Get pending requests
|
|
11602
|
+
*/
|
|
11603
|
+
getPendingRequests() {
|
|
11604
|
+
return Array.from(this.pendingRequests.values());
|
|
11605
|
+
}
|
|
11606
|
+
/**
|
|
11607
|
+
* Get auto-approved tools
|
|
11608
|
+
*/
|
|
11609
|
+
getAutoApprovedTools() {
|
|
11610
|
+
return Array.from(this.autoApprovedTools);
|
|
11611
|
+
}
|
|
11612
|
+
/**
|
|
11613
|
+
* Add a tool to the auto-approved list
|
|
11614
|
+
*/
|
|
11615
|
+
addAutoApprovedTool(toolName) {
|
|
11616
|
+
this.autoApprovedTools.add(toolName);
|
|
11617
|
+
}
|
|
11618
|
+
/**
|
|
11619
|
+
* Reset all auto decisions
|
|
11620
|
+
*/
|
|
11621
|
+
resetAutoDecisions() {
|
|
11622
|
+
this.autoApprovedTools.clear();
|
|
11623
|
+
this.autoDeniedTools.clear();
|
|
11624
|
+
}
|
|
11625
|
+
/**
|
|
11626
|
+
* Get YOLO mode status
|
|
11627
|
+
*/
|
|
11628
|
+
getYoloStatus() {
|
|
11629
|
+
const remainingMs = this.yoloExpiresAt ? Math.max(0, this.yoloExpiresAt - Date.now()) : null;
|
|
11630
|
+
return {
|
|
11631
|
+
enabled: this.yoloMode,
|
|
11632
|
+
expiresAt: this.yoloExpiresAt,
|
|
11633
|
+
remainingMs
|
|
11634
|
+
};
|
|
11635
|
+
}
|
|
11636
|
+
/**
|
|
11637
|
+
* Get approval statistics
|
|
11638
|
+
*/
|
|
11639
|
+
getStats() {
|
|
11640
|
+
return { ...this.stats };
|
|
11641
|
+
}
|
|
11642
|
+
/**
|
|
11643
|
+
* Cancel YOLO mode
|
|
11644
|
+
*/
|
|
11645
|
+
cancelYoloMode() {
|
|
11646
|
+
this.setYoloMode(false);
|
|
11647
|
+
}
|
|
11648
|
+
/**
|
|
11649
|
+
* Dispose of the approval manager
|
|
11650
|
+
*/
|
|
11651
|
+
dispose() {
|
|
11652
|
+
if (this.yoloTimer) {
|
|
11653
|
+
clearTimeout(this.yoloTimer);
|
|
11654
|
+
this.yoloTimer = null;
|
|
11655
|
+
}
|
|
11656
|
+
this.pendingRequests.clear();
|
|
11657
|
+
this.removeAllListeners();
|
|
11658
|
+
}
|
|
11659
|
+
};
|
|
11660
|
+
var approvalManager = null;
|
|
11661
|
+
function getApprovalManager(options) {
|
|
11662
|
+
if (!approvalManager) {
|
|
11663
|
+
approvalManager = new ApprovalManager(options);
|
|
11664
|
+
}
|
|
11665
|
+
return approvalManager;
|
|
11666
|
+
}
|
|
11667
|
+
|
|
11668
|
+
// src/core/agent/strategy/attack-planner.ts
|
|
11669
|
+
var SERVICE_TOOL_MAP = {
|
|
11670
|
+
http: {
|
|
11671
|
+
enumTools: [
|
|
11672
|
+
{ tool: TOOL_NAME.WHATWEB, description: "Identify web technologies", successIndicators: ["WordPress", "Apache", "nginx", "PHP"], timeout: 3e4, priority: 90 },
|
|
11673
|
+
{ tool: TOOL_NAME.NIKTO, description: "Web vulnerability scan", successIndicators: ["OSVDB", "vulnerability", "found"], timeout: 3e5, priority: 80 },
|
|
11674
|
+
{ tool: TOOL_NAME.FFUF, description: "Directory/file fuzzing", successIndicators: ["200", "301", "302", "Status"], timeout: 3e5, priority: 85 },
|
|
11675
|
+
{ tool: TOOL_NAME.GOBUSTER, description: "Directory bruteforce", successIndicators: ["Status: 200", "Found:"], timeout: 3e5, priority: 70 },
|
|
11676
|
+
{ tool: TOOL_NAME.NUCLEI, description: "Vulnerability scanning with templates", successIndicators: ["[critical]", "[high]", "[medium]", "found"], timeout: 6e5, priority: 75 }
|
|
11677
|
+
],
|
|
11678
|
+
exploitTools: [
|
|
11679
|
+
{ tool: TOOL_NAME.SQL_INJECTION, description: "SQL injection testing", successIndicators: ["injectable", "sql", "dump"], timeout: 6e5, priority: 85 },
|
|
11680
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "Search for known exploits", successIndicators: ["Exploit", "exploit/"], timeout: 3e4, priority: 90 }
|
|
11681
|
+
],
|
|
11682
|
+
vulnChecks: ["sql_injection", "xss", "file_inclusion", "command_injection", "path_traversal"]
|
|
11683
|
+
},
|
|
11684
|
+
https: {
|
|
11685
|
+
enumTools: [
|
|
11686
|
+
{ tool: TOOL_NAME.WHATWEB, description: "Identify web technologies", successIndicators: ["WordPress", "Apache", "nginx"], timeout: 3e4, priority: 90 },
|
|
11687
|
+
{ tool: TOOL_NAME.NIKTO, description: "Web vulnerability scan", successIndicators: ["OSVDB", "vulnerability"], timeout: 3e5, priority: 80 },
|
|
11688
|
+
{ tool: TOOL_NAME.FFUF, description: "Directory fuzzing", successIndicators: ["200", "301", "302"], timeout: 3e5, priority: 85 },
|
|
11689
|
+
{ tool: TOOL_NAME.NUCLEI, description: "Template-based vuln scanning", successIndicators: ["[critical]", "[high]"], timeout: 6e5, priority: 75 }
|
|
11690
|
+
],
|
|
11691
|
+
exploitTools: [
|
|
11692
|
+
{ tool: TOOL_NAME.SQL_INJECTION, description: "SQL injection", successIndicators: ["injectable", "dump"], timeout: 6e5, priority: 85 },
|
|
11693
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "Search exploits", successIndicators: ["Exploit"], timeout: 3e4, priority: 90 }
|
|
11694
|
+
],
|
|
11695
|
+
vulnChecks: ["ssl_vulnerability", "sql_injection", "xss"]
|
|
11696
|
+
},
|
|
11697
|
+
ssh: {
|
|
11698
|
+
enumTools: [
|
|
11699
|
+
{ tool: TOOL_NAME.BASH, description: "SSH banner grab", successIndicators: ["SSH", "OpenSSH"], timeout: 1e4, priority: 90 }
|
|
11700
|
+
],
|
|
11701
|
+
exploitTools: [
|
|
11702
|
+
{ tool: TOOL_NAME.HYDRA, description: "SSH bruteforce", successIndicators: ["login:", "password:"], timeout: 6e5, priority: 70 },
|
|
11703
|
+
{ tool: TOOL_NAME.SSH, description: "SSH with found credentials", successIndicators: ["$", "#", "Last login"], timeout: 3e4, priority: 95 },
|
|
11704
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "Search SSH exploits", successIndicators: ["Exploit"], timeout: 3e4, priority: 80 }
|
|
11705
|
+
],
|
|
11706
|
+
vulnChecks: ["weak_credentials", "old_version"]
|
|
11707
|
+
},
|
|
11708
|
+
smb: {
|
|
11709
|
+
enumTools: [
|
|
11710
|
+
{ tool: TOOL_NAME.SMB_ENUM, description: "SMB enumeration", successIndicators: ["share", "IPC$", "users"], timeout: 12e4, priority: 90 },
|
|
11711
|
+
{ tool: TOOL_NAME.ENUM4LINUX, description: "Full SMB enum", successIndicators: ["user", "share", "password"], timeout: 3e5, priority: 85 },
|
|
11712
|
+
{ tool: TOOL_NAME.SMBMAP, description: "SMB share permissions", successIndicators: ["READ", "WRITE", "Disk"], timeout: 12e4, priority: 88 },
|
|
11713
|
+
{ tool: TOOL_NAME.SMBCLIENT, description: "SMB client access", successIndicators: ["smb:", "NT_STATUS"], timeout: 6e4, priority: 80 }
|
|
11714
|
+
],
|
|
11715
|
+
exploitTools: [
|
|
11716
|
+
{ tool: TOOL_NAME.CRACKMAPEXEC, description: "Credential spray SMB", successIndicators: ["Pwn3d!", "+)"], timeout: 3e5, priority: 85 },
|
|
11717
|
+
{ tool: TOOL_NAME.IMPACKET_PSEXEC, description: "PsExec remote execution", successIndicators: ["C:\\>", "$"], timeout: 12e4, priority: 90 },
|
|
11718
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "Search SMB exploits", successIndicators: ["Exploit", "EternalBlue"], timeout: 3e4, priority: 95 }
|
|
11719
|
+
],
|
|
11720
|
+
vulnChecks: ["null_session", "anonymous_access", "eternal_blue", "weak_credentials"]
|
|
11721
|
+
},
|
|
11722
|
+
ftp: {
|
|
11723
|
+
enumTools: [
|
|
11724
|
+
{ tool: TOOL_NAME.FTP_ANON, description: "Check anonymous FTP", successIndicators: ["Anonymous", "230", "Login successful"], timeout: 3e4, priority: 95 },
|
|
11725
|
+
{ tool: TOOL_NAME.FTP_ENUM, description: "FTP enumeration", successIndicators: ["user", "directory"], timeout: 6e4, priority: 85 }
|
|
11726
|
+
],
|
|
11727
|
+
exploitTools: [
|
|
11728
|
+
{ tool: TOOL_NAME.HYDRA, description: "FTP bruteforce", successIndicators: ["login:", "password"], timeout: 6e5, priority: 70 },
|
|
11729
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "Search FTP exploits", successIndicators: ["Exploit"], timeout: 3e4, priority: 90 }
|
|
11730
|
+
],
|
|
11731
|
+
vulnChecks: ["anonymous_access", "weak_credentials", "known_vulnerability"]
|
|
11732
|
+
},
|
|
11733
|
+
mysql: {
|
|
11734
|
+
enumTools: [
|
|
11735
|
+
{ tool: TOOL_NAME.MYSQL_CLIENT, description: "MySQL client access", successIndicators: ["mysql>", "Server version"], timeout: 3e4, priority: 90 }
|
|
11736
|
+
],
|
|
11737
|
+
exploitTools: [
|
|
11738
|
+
{ tool: TOOL_NAME.HYDRA, description: "MySQL bruteforce", successIndicators: ["login:", "password"], timeout: 6e5, priority: 70 },
|
|
11739
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "MySQL exploits", successIndicators: ["Exploit"], timeout: 3e4, priority: 85 }
|
|
11740
|
+
],
|
|
11741
|
+
vulnChecks: ["default_credentials", "udf_exploit"]
|
|
11742
|
+
},
|
|
11743
|
+
snmp: {
|
|
11744
|
+
enumTools: [
|
|
11745
|
+
{ tool: TOOL_NAME.SNMP_WALK, description: "SNMP walk", successIndicators: ["STRING:", "INTEGER:", "SNMPv2"], timeout: 12e4, priority: 90 },
|
|
11746
|
+
{ tool: TOOL_NAME.SNMP_CHECK, description: "SNMP check", successIndicators: ["system", "user", "process"], timeout: 6e4, priority: 85 },
|
|
11747
|
+
{ tool: TOOL_NAME.ONESIXTYONE, description: "SNMP community string bruteforce", successIndicators: ["public", "private"], timeout: 6e4, priority: 88 }
|
|
11748
|
+
],
|
|
11749
|
+
exploitTools: [],
|
|
11750
|
+
vulnChecks: ["default_community_string", "snmpv1"]
|
|
11751
|
+
},
|
|
11752
|
+
ldap: {
|
|
11753
|
+
enumTools: [
|
|
11754
|
+
{ tool: TOOL_NAME.LDAP_SEARCH, description: "LDAP enumeration", successIndicators: ["dn:", "cn=", "dc="], timeout: 12e4, priority: 90 }
|
|
11755
|
+
],
|
|
11756
|
+
exploitTools: [
|
|
11757
|
+
{ tool: TOOL_NAME.KERBRUTE, description: "Kerberos user enum", successIndicators: ["VALID", "user"], timeout: 3e5, priority: 80 },
|
|
11758
|
+
{ tool: TOOL_NAME.IMPACKET_GETNPUSERS, description: "AS-REP roasting", successIndicators: ["$krb5asrep$"], timeout: 12e4, priority: 85 }
|
|
11759
|
+
],
|
|
11760
|
+
vulnChecks: ["anonymous_bind", "kerberoasting", "as-rep_roasting"]
|
|
11761
|
+
},
|
|
11762
|
+
rdp: {
|
|
11763
|
+
enumTools: [
|
|
11764
|
+
{ tool: TOOL_NAME.RDP_CHECK, description: "RDP security check", successIndicators: ["RDP", "PROTOCOL_RDP"], timeout: 3e4, priority: 90 }
|
|
11765
|
+
],
|
|
11766
|
+
exploitTools: [
|
|
11767
|
+
{ tool: TOOL_NAME.HYDRA, description: "RDP bruteforce", successIndicators: ["login:", "password"], timeout: 6e5, priority: 60 },
|
|
11768
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "RDP exploits", successIndicators: ["BlueKeep", "CVE-2019"], timeout: 3e4, priority: 90 }
|
|
11769
|
+
],
|
|
11770
|
+
vulnChecks: ["bluekeep", "weak_credentials"]
|
|
11771
|
+
},
|
|
11772
|
+
redis: {
|
|
11773
|
+
enumTools: [
|
|
11774
|
+
{ tool: TOOL_NAME.REDIS_CLI, description: "Redis client access", successIndicators: ["redis", "PONG", "keys"], timeout: 3e4, priority: 90 }
|
|
11775
|
+
],
|
|
11776
|
+
exploitTools: [
|
|
11777
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "Redis exploits", successIndicators: ["Exploit"], timeout: 3e4, priority: 85 }
|
|
11778
|
+
],
|
|
11779
|
+
vulnChecks: ["no_auth", "default_config"]
|
|
11780
|
+
}
|
|
11781
|
+
};
|
|
11782
|
+
var AttackPlanner = class {
|
|
11783
|
+
/**
|
|
11784
|
+
* Generate an initial attack plan based on target and discovered services
|
|
11785
|
+
*/
|
|
11786
|
+
generatePlan(target, services) {
|
|
11787
|
+
const plan = {
|
|
11788
|
+
target,
|
|
11789
|
+
phases: [],
|
|
11790
|
+
currentPhaseIndex: 0,
|
|
11791
|
+
adaptations: []
|
|
11792
|
+
};
|
|
11793
|
+
plan.phases.push({
|
|
11794
|
+
name: "initial_recon",
|
|
11795
|
+
steps: [
|
|
11796
|
+
{ tool: TOOL_NAME.NMAP_SCAN, description: "Full port scan", successIndicators: ["open", "tcp", "udp"], timeout: 3e5, priority: 100 },
|
|
11797
|
+
{ tool: TOOL_NAME.RUSTSCAN, description: "Fast port scan", successIndicators: ["Open"], timeout: 6e4, priority: 95 }
|
|
11798
|
+
],
|
|
11799
|
+
successCriteria: "At least 1 service discovered",
|
|
11800
|
+
maxAttempts: 5
|
|
11801
|
+
});
|
|
11802
|
+
const enumSteps = [];
|
|
11803
|
+
for (const svc of services) {
|
|
11804
|
+
const normalizedService = this.normalizeServiceName(svc.service);
|
|
11805
|
+
const mapping = SERVICE_TOOL_MAP[normalizedService];
|
|
11806
|
+
if (mapping) {
|
|
11807
|
+
enumSteps.push(...mapping.enumTools.map((step) => ({
|
|
11808
|
+
...step,
|
|
11809
|
+
description: `[${svc.port}/${svc.service}] ${step.description}`
|
|
11810
|
+
})));
|
|
11811
|
+
}
|
|
11812
|
+
}
|
|
11813
|
+
if (enumSteps.length > 0) {
|
|
11814
|
+
enumSteps.sort((a, b) => b.priority - a.priority);
|
|
11815
|
+
plan.phases.push({
|
|
11816
|
+
name: "service_enumeration",
|
|
11817
|
+
steps: enumSteps,
|
|
11818
|
+
successCriteria: "Vulnerabilities or access vectors identified",
|
|
11819
|
+
maxAttempts: enumSteps.length + 5
|
|
11820
|
+
});
|
|
11821
|
+
}
|
|
11822
|
+
const vulnSteps = [
|
|
11823
|
+
{ tool: TOOL_NAME.SEARCHSPLOIT, description: "Search for known exploits", successIndicators: ["Exploit", "CVE"], timeout: 3e4, priority: 90 }
|
|
11824
|
+
];
|
|
11825
|
+
for (const svc of services) {
|
|
11826
|
+
if (svc.version) {
|
|
11827
|
+
vulnSteps.push({
|
|
11828
|
+
tool: TOOL_NAME.SEARCHSPLOIT,
|
|
11829
|
+
description: `Search exploits for ${svc.service} ${svc.version}`,
|
|
11830
|
+
successIndicators: ["Exploit"],
|
|
11831
|
+
timeout: 3e4,
|
|
11832
|
+
priority: 88
|
|
11833
|
+
});
|
|
11834
|
+
}
|
|
11835
|
+
}
|
|
11836
|
+
plan.phases.push({
|
|
11837
|
+
name: "vulnerability_analysis",
|
|
11838
|
+
steps: vulnSteps,
|
|
11839
|
+
successCriteria: "Exploitable vulnerability found",
|
|
11840
|
+
maxAttempts: 10
|
|
11841
|
+
});
|
|
11842
|
+
const exploitSteps = [];
|
|
11843
|
+
for (const svc of services) {
|
|
11844
|
+
const normalizedService = this.normalizeServiceName(svc.service);
|
|
11845
|
+
const mapping = SERVICE_TOOL_MAP[normalizedService];
|
|
11846
|
+
if (mapping) {
|
|
11847
|
+
exploitSteps.push(...mapping.exploitTools.map((step) => ({
|
|
11848
|
+
...step,
|
|
11849
|
+
description: `[${svc.port}/${svc.service}] ${step.description}`
|
|
11850
|
+
})));
|
|
11851
|
+
}
|
|
11852
|
+
}
|
|
11853
|
+
if (exploitSteps.length > 0) {
|
|
11854
|
+
exploitSteps.sort((a, b) => b.priority - a.priority);
|
|
11855
|
+
plan.phases.push({
|
|
11856
|
+
name: "exploitation",
|
|
11857
|
+
steps: exploitSteps,
|
|
11858
|
+
successCriteria: "Shell or admin access obtained",
|
|
11859
|
+
maxAttempts: exploitSteps.length + 10
|
|
11860
|
+
});
|
|
11861
|
+
}
|
|
11862
|
+
plan.phases.push({
|
|
11863
|
+
name: "post_exploitation",
|
|
11864
|
+
steps: [
|
|
11865
|
+
{ tool: TOOL_NAME.RUN_PRIVESC_ENUM, description: "Privilege escalation enumeration", successIndicators: ["SUID", "cron", "writable"], timeout: 12e4, priority: 90 },
|
|
11866
|
+
{ tool: TOOL_NAME.CHECK_SUDO, description: "Check sudo rights", successIndicators: ["(ALL)", "NOPASSWD", "may run"], timeout: 3e4, priority: 95 },
|
|
11867
|
+
{ tool: TOOL_NAME.FIND_SUID, description: "Find SUID binaries", successIndicators: ["/usr/", "/bin/"], timeout: 3e4, priority: 85 },
|
|
11868
|
+
{ tool: TOOL_NAME.DUMP_CREDENTIALS, description: "Dump credentials", successIndicators: ["password", "hash", "credential"], timeout: 12e4, priority: 80 }
|
|
11869
|
+
],
|
|
11870
|
+
successCriteria: "Root/admin access obtained",
|
|
11871
|
+
maxAttempts: 15
|
|
11872
|
+
});
|
|
11873
|
+
return plan;
|
|
11874
|
+
}
|
|
11875
|
+
/**
|
|
11876
|
+
* Adapt the plan based on new discoveries
|
|
11877
|
+
*/
|
|
11878
|
+
adaptPlan(plan, newServices, findings) {
|
|
11879
|
+
const adaptation = `Adapting plan: ${newServices.length} new services, ${findings.length} findings`;
|
|
11880
|
+
plan.adaptations.push(adaptation);
|
|
11881
|
+
for (const svc of newServices) {
|
|
11882
|
+
const normalizedService = this.normalizeServiceName(svc.service);
|
|
11883
|
+
const mapping = SERVICE_TOOL_MAP[normalizedService];
|
|
11884
|
+
if (mapping) {
|
|
11885
|
+
let enumPhase = plan.phases.find((p) => p.name === "service_enumeration");
|
|
11886
|
+
if (!enumPhase) {
|
|
11887
|
+
enumPhase = {
|
|
11888
|
+
name: "service_enumeration",
|
|
11889
|
+
steps: [],
|
|
11890
|
+
successCriteria: "Vulnerabilities identified",
|
|
11891
|
+
maxAttempts: 10
|
|
11892
|
+
};
|
|
11893
|
+
const reconIdx = plan.phases.findIndex((p) => p.name === "initial_recon");
|
|
11894
|
+
plan.phases.splice(reconIdx + 1, 0, enumPhase);
|
|
11895
|
+
}
|
|
11896
|
+
for (const tool of mapping.enumTools) {
|
|
11897
|
+
const exists = enumPhase.steps.some(
|
|
11898
|
+
(s) => s.tool === tool.tool && s.description.includes(`${svc.port}`)
|
|
11899
|
+
);
|
|
11900
|
+
if (!exists) {
|
|
11901
|
+
enumPhase.steps.push({
|
|
11902
|
+
...tool,
|
|
11903
|
+
description: `[${svc.port}/${svc.service}] ${tool.description}`
|
|
11904
|
+
});
|
|
11905
|
+
}
|
|
11906
|
+
}
|
|
11907
|
+
}
|
|
11908
|
+
}
|
|
11909
|
+
return plan;
|
|
11910
|
+
}
|
|
11911
|
+
/**
|
|
11912
|
+
* Get suggested next steps based on current state
|
|
11913
|
+
*/
|
|
11914
|
+
suggestNextSteps(services, completedTools, hasCredentials, hasShell) {
|
|
11915
|
+
const suggestions = [];
|
|
11916
|
+
if (hasShell) {
|
|
11917
|
+
suggestions.push(
|
|
11918
|
+
{ tool: TOOL_NAME.RUN_PRIVESC_ENUM, description: "Run privilege escalation enumeration", successIndicators: ["SUID", "cron"], timeout: 12e4, priority: 95 },
|
|
11919
|
+
{ tool: TOOL_NAME.CHECK_SUDO, description: "Check sudo permissions", successIndicators: ["(ALL)", "NOPASSWD"], timeout: 3e4, priority: 90 },
|
|
11920
|
+
{ tool: TOOL_NAME.FIND_SUID, description: "Find SUID binaries", successIndicators: ["/usr/", "/bin/"], timeout: 3e4, priority: 85 }
|
|
11921
|
+
);
|
|
11922
|
+
} else if (hasCredentials) {
|
|
11923
|
+
for (const svc of services) {
|
|
11924
|
+
if (["ssh", "smb", "ftp", "rdp", "winrm"].includes(this.normalizeServiceName(svc.service))) {
|
|
11925
|
+
suggestions.push({
|
|
11926
|
+
tool: svc.service === "ssh" ? TOOL_NAME.SSH : TOOL_NAME.CRACKMAPEXEC,
|
|
11927
|
+
description: `Try credentials on ${svc.service}:${svc.port}`,
|
|
11928
|
+
successIndicators: ["$", "#", "Pwn3d!", "Login successful"],
|
|
11929
|
+
timeout: 3e4,
|
|
11930
|
+
priority: 95
|
|
11931
|
+
});
|
|
11932
|
+
}
|
|
11933
|
+
}
|
|
11934
|
+
} else {
|
|
11935
|
+
for (const svc of services) {
|
|
11936
|
+
const normalizedService = this.normalizeServiceName(svc.service);
|
|
11937
|
+
const mapping = SERVICE_TOOL_MAP[normalizedService];
|
|
11938
|
+
if (mapping) {
|
|
11939
|
+
for (const tool of mapping.enumTools) {
|
|
11940
|
+
if (!completedTools.includes(tool.tool)) {
|
|
11941
|
+
suggestions.push({
|
|
11942
|
+
...tool,
|
|
11943
|
+
description: `[${svc.port}/${svc.service}] ${tool.description}`
|
|
11944
|
+
});
|
|
11945
|
+
}
|
|
12013
11946
|
}
|
|
12014
11947
|
}
|
|
12015
|
-
}
|
|
12016
|
-
|
|
12017
|
-
|
|
11948
|
+
}
|
|
11949
|
+
}
|
|
11950
|
+
suggestions.sort((a, b) => b.priority - a.priority);
|
|
11951
|
+
return suggestions.slice(0, 10);
|
|
12018
11952
|
}
|
|
12019
11953
|
/**
|
|
12020
|
-
*
|
|
12021
|
-
* Now handles timed YOLO modes
|
|
11954
|
+
* Get vulnerability checks for discovered services
|
|
12022
11955
|
*/
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
this.setYoloMode(true, 30 * 60 * 1e3);
|
|
12032
|
-
decision = "approve";
|
|
11956
|
+
getVulnChecks(services) {
|
|
11957
|
+
const checks = [];
|
|
11958
|
+
for (const svc of services) {
|
|
11959
|
+
const normalizedService = this.normalizeServiceName(svc.service);
|
|
11960
|
+
const mapping = SERVICE_TOOL_MAP[normalizedService];
|
|
11961
|
+
if (mapping) {
|
|
11962
|
+
checks.push(...mapping.vulnChecks);
|
|
11963
|
+
}
|
|
12033
11964
|
}
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
11965
|
+
return [...new Set(checks)];
|
|
11966
|
+
}
|
|
11967
|
+
/**
|
|
11968
|
+
* Normalize service names from nmap output to our mapping keys
|
|
11969
|
+
*/
|
|
11970
|
+
normalizeServiceName(service) {
|
|
11971
|
+
const lower = service.toLowerCase();
|
|
11972
|
+
if (lower.includes("http") && !lower.includes("https")) return "http";
|
|
11973
|
+
if (lower.includes("https") || lower.includes("ssl/http")) return "https";
|
|
11974
|
+
if (lower.includes("ssh")) return "ssh";
|
|
11975
|
+
if (lower.includes("smb") || lower.includes("microsoft-ds") || lower.includes("netbios")) return "smb";
|
|
11976
|
+
if (lower.includes("ftp")) return "ftp";
|
|
11977
|
+
if (lower.includes("mysql") || lower.includes("mariadb")) return "mysql";
|
|
11978
|
+
if (lower.includes("mssql") || lower.includes("ms-sql")) return "mssql";
|
|
11979
|
+
if (lower.includes("postgresql") || lower.includes("postgres")) return "postgresql";
|
|
11980
|
+
if (lower.includes("redis")) return "redis";
|
|
11981
|
+
if (lower.includes("mongo")) return "mongodb";
|
|
11982
|
+
if (lower.includes("snmp")) return "snmp";
|
|
11983
|
+
if (lower.includes("ldap")) return "ldap";
|
|
11984
|
+
if (lower.includes("rdp") || lower.includes("ms-wbt-server")) return "rdp";
|
|
11985
|
+
return lower;
|
|
11986
|
+
}
|
|
11987
|
+
};
|
|
11988
|
+
var attackPlannerInstance = null;
|
|
11989
|
+
function getAttackPlanner() {
|
|
11990
|
+
if (!attackPlannerInstance) {
|
|
11991
|
+
attackPlannerInstance = new AttackPlanner();
|
|
11992
|
+
}
|
|
11993
|
+
return attackPlannerInstance;
|
|
11994
|
+
}
|
|
11995
|
+
|
|
11996
|
+
// src/core/agent/strategy/stuck-detector.ts
|
|
11997
|
+
var StuckDetector = class {
|
|
11998
|
+
actionHistory = [];
|
|
11999
|
+
progressEvents = [];
|
|
12000
|
+
maxHistorySize = 100;
|
|
12001
|
+
// Thresholds
|
|
12002
|
+
SAME_TOOL_THRESHOLD = 3;
|
|
12003
|
+
// Same tool+target 3x = stuck
|
|
12004
|
+
CYCLE_MIN_LENGTH = 2;
|
|
12005
|
+
// Minimum cycle length
|
|
12006
|
+
CYCLE_REPETITIONS = 2;
|
|
12007
|
+
// Cycle seen 2x = stuck
|
|
12008
|
+
NO_PROGRESS_TIMEOUT = 3e5;
|
|
12009
|
+
// 5 minutes without progress
|
|
12010
|
+
NO_NEW_INFO_THRESHOLD = 8;
|
|
12011
|
+
// 8 actions without new info
|
|
12012
|
+
/**
|
|
12013
|
+
* Record a tool execution
|
|
12014
|
+
*/
|
|
12015
|
+
recordAction(tool, target, success, hasNewInfo) {
|
|
12016
|
+
this.actionHistory.push({
|
|
12017
|
+
tool,
|
|
12018
|
+
target,
|
|
12019
|
+
timestamp: Date.now(),
|
|
12020
|
+
success,
|
|
12021
|
+
hasNewInfo
|
|
12022
|
+
});
|
|
12023
|
+
if (this.actionHistory.length > this.maxHistorySize) {
|
|
12024
|
+
this.actionHistory = this.actionHistory.slice(-this.maxHistorySize);
|
|
12038
12025
|
}
|
|
12039
|
-
const response = {
|
|
12040
|
-
requestId,
|
|
12041
|
-
decision,
|
|
12042
|
-
respondedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12043
|
-
};
|
|
12044
|
-
this.emit(APPROVAL_EVENT.RESPONSE, response);
|
|
12045
12026
|
}
|
|
12046
12027
|
/**
|
|
12047
|
-
*
|
|
12028
|
+
* Record a progress event
|
|
12048
12029
|
*/
|
|
12049
|
-
|
|
12050
|
-
|
|
12030
|
+
recordProgress(type, details) {
|
|
12031
|
+
this.progressEvents.push({
|
|
12032
|
+
type,
|
|
12033
|
+
timestamp: Date.now(),
|
|
12034
|
+
details
|
|
12035
|
+
});
|
|
12051
12036
|
}
|
|
12052
12037
|
/**
|
|
12053
|
-
*
|
|
12038
|
+
* Analyze whether the agent is stuck
|
|
12054
12039
|
*/
|
|
12055
|
-
|
|
12056
|
-
|
|
12040
|
+
analyze() {
|
|
12041
|
+
const checks = [
|
|
12042
|
+
this.checkSameToolRepetition(),
|
|
12043
|
+
this.checkCycleDetection(),
|
|
12044
|
+
this.checkProgressTimeout(),
|
|
12045
|
+
this.checkNoNewInfoStreak(),
|
|
12046
|
+
this.checkAllFailures()
|
|
12047
|
+
];
|
|
12048
|
+
const stuckChecks = checks.filter((c) => c.isStuck);
|
|
12049
|
+
if (stuckChecks.length === 0) {
|
|
12050
|
+
return {
|
|
12051
|
+
isStuck: false,
|
|
12052
|
+
reason: "",
|
|
12053
|
+
suggestion: "",
|
|
12054
|
+
confidence: 0
|
|
12055
|
+
};
|
|
12056
|
+
}
|
|
12057
|
+
stuckChecks.sort((a, b) => b.confidence - a.confidence);
|
|
12058
|
+
return stuckChecks[0];
|
|
12057
12059
|
}
|
|
12058
12060
|
/**
|
|
12059
|
-
*
|
|
12061
|
+
* Check 1: Same tool+target repeated too many times
|
|
12060
12062
|
*/
|
|
12061
|
-
|
|
12062
|
-
this.
|
|
12063
|
-
|
|
12063
|
+
checkSameToolRepetition() {
|
|
12064
|
+
if (this.actionHistory.length < this.SAME_TOOL_THRESHOLD) {
|
|
12065
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12066
|
+
}
|
|
12067
|
+
const recent = this.actionHistory.slice(-10);
|
|
12068
|
+
const comboCounts = /* @__PURE__ */ new Map();
|
|
12069
|
+
for (const action of recent) {
|
|
12070
|
+
const key = `${action.tool}:${action.target}`;
|
|
12071
|
+
comboCounts.set(key, (comboCounts.get(key) || 0) + 1);
|
|
12072
|
+
}
|
|
12073
|
+
for (const [combo, count] of comboCounts) {
|
|
12074
|
+
if (count >= this.SAME_TOOL_THRESHOLD) {
|
|
12075
|
+
const [tool] = combo.split(":");
|
|
12076
|
+
return {
|
|
12077
|
+
isStuck: true,
|
|
12078
|
+
reason: `Same tool "${tool}" executed ${count} times with same target`,
|
|
12079
|
+
suggestion: `Try a different tool or target. Consider switching approach entirely.`,
|
|
12080
|
+
confidence: Math.min(0.9, 0.5 + count * 0.1)
|
|
12081
|
+
};
|
|
12082
|
+
}
|
|
12083
|
+
}
|
|
12084
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12064
12085
|
}
|
|
12065
12086
|
/**
|
|
12066
|
-
*
|
|
12087
|
+
* Check 2: Cycle detection (A→B→C→A→B→C pattern)
|
|
12067
12088
|
*/
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12089
|
+
checkCycleDetection() {
|
|
12090
|
+
if (this.actionHistory.length < this.CYCLE_MIN_LENGTH * this.CYCLE_REPETITIONS) {
|
|
12091
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12092
|
+
}
|
|
12093
|
+
const recent = this.actionHistory.slice(-20).map((a) => a.tool);
|
|
12094
|
+
for (let cycleLen = this.CYCLE_MIN_LENGTH; cycleLen <= 5; cycleLen++) {
|
|
12095
|
+
if (recent.length < cycleLen * this.CYCLE_REPETITIONS) continue;
|
|
12096
|
+
const lastCycle = recent.slice(-cycleLen);
|
|
12097
|
+
let repetitions = 0;
|
|
12098
|
+
for (let i = recent.length - cycleLen; i >= 0; i -= cycleLen) {
|
|
12099
|
+
const segment = recent.slice(i, i + cycleLen);
|
|
12100
|
+
if (segment.length === cycleLen && segment.every((s, idx) => s === lastCycle[idx])) {
|
|
12101
|
+
repetitions++;
|
|
12102
|
+
} else {
|
|
12103
|
+
break;
|
|
12104
|
+
}
|
|
12105
|
+
}
|
|
12106
|
+
if (repetitions >= this.CYCLE_REPETITIONS) {
|
|
12107
|
+
return {
|
|
12108
|
+
isStuck: true,
|
|
12109
|
+
reason: `Cycle detected: [${lastCycle.join(" \u2192 ")}] repeated ${repetitions + 1} times`,
|
|
12110
|
+
suggestion: `Break the cycle. Try a completely different approach or tool category.`,
|
|
12111
|
+
confidence: Math.min(0.95, 0.6 + repetitions * 0.1)
|
|
12112
|
+
};
|
|
12113
|
+
}
|
|
12114
|
+
}
|
|
12115
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12075
12116
|
}
|
|
12076
12117
|
/**
|
|
12077
|
-
*
|
|
12118
|
+
* Check 3: No progress for too long
|
|
12078
12119
|
*/
|
|
12079
|
-
|
|
12080
|
-
|
|
12120
|
+
checkProgressTimeout() {
|
|
12121
|
+
if (this.progressEvents.length === 0 && this.actionHistory.length > 5) {
|
|
12122
|
+
const oldestAction = this.actionHistory[0];
|
|
12123
|
+
const elapsed = Date.now() - oldestAction.timestamp;
|
|
12124
|
+
if (elapsed > this.NO_PROGRESS_TIMEOUT) {
|
|
12125
|
+
return {
|
|
12126
|
+
isStuck: true,
|
|
12127
|
+
reason: `No progress events recorded in ${Math.round(elapsed / 6e4)} minutes`,
|
|
12128
|
+
suggestion: `Consider scanning more broadly or trying a different attack vector.`,
|
|
12129
|
+
confidence: 0.7
|
|
12130
|
+
};
|
|
12131
|
+
}
|
|
12132
|
+
}
|
|
12133
|
+
if (this.progressEvents.length > 0) {
|
|
12134
|
+
const lastProgress = this.progressEvents[this.progressEvents.length - 1];
|
|
12135
|
+
const elapsed = Date.now() - lastProgress.timestamp;
|
|
12136
|
+
if (elapsed > this.NO_PROGRESS_TIMEOUT) {
|
|
12137
|
+
return {
|
|
12138
|
+
isStuck: true,
|
|
12139
|
+
reason: `No progress for ${Math.round(elapsed / 6e4)} minutes (last: ${lastProgress.details})`,
|
|
12140
|
+
suggestion: `Last progress was "${lastProgress.type}". Try a different approach.`,
|
|
12141
|
+
confidence: Math.min(0.85, 0.5 + elapsed / this.NO_PROGRESS_TIMEOUT * 0.3)
|
|
12142
|
+
};
|
|
12143
|
+
}
|
|
12144
|
+
}
|
|
12145
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12081
12146
|
}
|
|
12082
12147
|
/**
|
|
12083
|
-
*
|
|
12148
|
+
* Check 4: Streak of actions with no new information
|
|
12084
12149
|
*/
|
|
12085
|
-
|
|
12086
|
-
this.
|
|
12150
|
+
checkNoNewInfoStreak() {
|
|
12151
|
+
if (this.actionHistory.length < this.NO_NEW_INFO_THRESHOLD) {
|
|
12152
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12153
|
+
}
|
|
12154
|
+
const recent = this.actionHistory.slice(-this.NO_NEW_INFO_THRESHOLD);
|
|
12155
|
+
const noNewInfoCount = recent.filter((a) => !a.hasNewInfo).length;
|
|
12156
|
+
if (noNewInfoCount >= this.NO_NEW_INFO_THRESHOLD) {
|
|
12157
|
+
return {
|
|
12158
|
+
isStuck: true,
|
|
12159
|
+
reason: `${noNewInfoCount} consecutive actions produced no new information`,
|
|
12160
|
+
suggestion: `All recent actions returned known information. Try deeper enumeration or a different service.`,
|
|
12161
|
+
confidence: 0.75
|
|
12162
|
+
};
|
|
12163
|
+
}
|
|
12164
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12087
12165
|
}
|
|
12088
12166
|
/**
|
|
12089
|
-
*
|
|
12167
|
+
* Check 5: All recent actions failed
|
|
12090
12168
|
*/
|
|
12091
|
-
|
|
12092
|
-
if (this.
|
|
12093
|
-
|
|
12094
|
-
this.yoloTimer = null;
|
|
12169
|
+
checkAllFailures() {
|
|
12170
|
+
if (this.actionHistory.length < 5) {
|
|
12171
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12095
12172
|
}
|
|
12096
|
-
this.
|
|
12097
|
-
|
|
12173
|
+
const recent = this.actionHistory.slice(-5);
|
|
12174
|
+
const failureCount = recent.filter((a) => !a.success).length;
|
|
12175
|
+
if (failureCount >= 5) {
|
|
12176
|
+
return {
|
|
12177
|
+
isStuck: true,
|
|
12178
|
+
reason: `Last ${failureCount} actions all failed`,
|
|
12179
|
+
suggestion: `Multiple failures detected. Check if the target is reachable, or try different tools.`,
|
|
12180
|
+
confidence: 0.8
|
|
12181
|
+
};
|
|
12182
|
+
}
|
|
12183
|
+
return { isStuck: false, reason: "", suggestion: "", confidence: 0 };
|
|
12098
12184
|
}
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12185
|
+
/**
|
|
12186
|
+
* Get suggested alternative approaches
|
|
12187
|
+
*/
|
|
12188
|
+
getSuggestedPivots(completedTools, availableServices) {
|
|
12189
|
+
const suggestions = [];
|
|
12190
|
+
const toolCategories = {
|
|
12191
|
+
"web_scanning": ["ffuf", "gobuster", "nikto", "nuclei", "whatweb"],
|
|
12192
|
+
"credential_attack": ["hydra", "medusa", "john", "hashcat"],
|
|
12193
|
+
"smb_attack": ["smbclient", "smbmap", "enum4linux", "crackmapexec"],
|
|
12194
|
+
"ssh_attack": ["ssh", "hydra"],
|
|
12195
|
+
"exploit_search": ["searchsploit", "metasploit"],
|
|
12196
|
+
"dns_recon": ["dig", "dnsenum", "dnsrecon", "subfinder"],
|
|
12197
|
+
"privesc": ["run_privesc_enum", "check_sudo", "find_suid"]
|
|
12198
|
+
};
|
|
12199
|
+
for (const [category, tools] of Object.entries(toolCategories)) {
|
|
12200
|
+
const usedCount = tools.filter((t) => completedTools.includes(t)).length;
|
|
12201
|
+
const totalCount = tools.length;
|
|
12202
|
+
if (usedCount < totalCount / 2) {
|
|
12203
|
+
suggestions.push(`Try ${category} tools (${totalCount - usedCount} unused)`);
|
|
12204
|
+
}
|
|
12205
|
+
}
|
|
12206
|
+
for (const service of availableServices) {
|
|
12207
|
+
const normalizedService = service.toLowerCase();
|
|
12208
|
+
if (normalizedService.includes("http") && !completedTools.includes("ffuf")) {
|
|
12209
|
+
suggestions.push("Try directory fuzzing on web services");
|
|
12210
|
+
}
|
|
12211
|
+
if (normalizedService.includes("smb") && !completedTools.includes("enum4linux")) {
|
|
12212
|
+
suggestions.push("Try full SMB enumeration");
|
|
12213
|
+
}
|
|
12214
|
+
}
|
|
12215
|
+
return suggestions.slice(0, 5);
|
|
12104
12216
|
}
|
|
12105
|
-
|
|
12106
|
-
|
|
12217
|
+
/**
|
|
12218
|
+
* Reset detector state
|
|
12219
|
+
*/
|
|
12220
|
+
reset() {
|
|
12221
|
+
this.actionHistory = [];
|
|
12222
|
+
this.progressEvents = [];
|
|
12223
|
+
}
|
|
12224
|
+
/**
|
|
12225
|
+
* Get statistics about current state
|
|
12226
|
+
*/
|
|
12227
|
+
getStats() {
|
|
12228
|
+
const total = this.actionHistory.length;
|
|
12229
|
+
const successes = this.actionHistory.filter((a) => a.success).length;
|
|
12230
|
+
const newInfo = this.actionHistory.filter((a) => a.hasNewInfo).length;
|
|
12231
|
+
const lastProgress = this.progressEvents.length > 0 ? Date.now() - this.progressEvents[this.progressEvents.length - 1].timestamp : null;
|
|
12232
|
+
return {
|
|
12233
|
+
totalActions: total,
|
|
12234
|
+
successRate: total > 0 ? successes / total : 0,
|
|
12235
|
+
newInfoRate: total > 0 ? newInfo / total : 0,
|
|
12236
|
+
lastProgressAge: lastProgress
|
|
12237
|
+
};
|
|
12238
|
+
}
|
|
12239
|
+
};
|
|
12107
12240
|
|
|
12108
12241
|
// src/core/agent/autonomous-agent.ts
|
|
12109
12242
|
function toContentBlockParam(block) {
|
|
@@ -12147,7 +12280,7 @@ var DEFAULT_PHASES = [
|
|
|
12147
12280
|
{ id: PHASE_ID.EXFIL, name: "Data Exfiltration", shortName: "Exfil", status: PHASE_STATUS.PENDING, attempts: 0 },
|
|
12148
12281
|
{ id: PHASE_ID.REPORT, name: "Reporting", shortName: "Report", status: PHASE_STATUS.PENDING, attempts: 0 }
|
|
12149
12282
|
];
|
|
12150
|
-
var AutonomousHackingAgent = class extends
|
|
12283
|
+
var AutonomousHackingAgent = class extends EventEmitter17 {
|
|
12151
12284
|
// LLM Client
|
|
12152
12285
|
client;
|
|
12153
12286
|
// Core State
|
|
@@ -12163,12 +12296,15 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12163
12296
|
sessionManager;
|
|
12164
12297
|
// Utility Systems
|
|
12165
12298
|
hookExecutor;
|
|
12166
|
-
mcpManager;
|
|
12167
12299
|
contextManager;
|
|
12168
12300
|
approvalManager;
|
|
12169
|
-
// v0.12.
|
|
12301
|
+
// v0.12.7 New Systems
|
|
12170
12302
|
resourceManager;
|
|
12171
12303
|
auditLogger;
|
|
12304
|
+
// Strategy modules
|
|
12305
|
+
attackPlanner;
|
|
12306
|
+
stuckDetector;
|
|
12307
|
+
currentAttackPlan;
|
|
12172
12308
|
// Token usage tracking
|
|
12173
12309
|
tokenUsage = { input: 0, output: 0, total: 0 };
|
|
12174
12310
|
// Execution control flags
|
|
@@ -12181,7 +12317,6 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12181
12317
|
enableAutoCheckpoint = true;
|
|
12182
12318
|
checkpointIntervalMs = 6e4;
|
|
12183
12319
|
enableAutonomousOrchestrator = true;
|
|
12184
|
-
// New: Use enhanced autonomous orchestrator
|
|
12185
12320
|
// Rabbit hole detection settings
|
|
12186
12321
|
STUCK_THRESHOLD = 5;
|
|
12187
12322
|
STUCK_TIME_THRESHOLD = 3e5;
|
|
@@ -12194,6 +12329,8 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12194
12329
|
});
|
|
12195
12330
|
this.config = { ...AGENT_CONFIG, ...config };
|
|
12196
12331
|
this.tools = ALL_TOOLS;
|
|
12332
|
+
this.attackPlanner = getAttackPlanner();
|
|
12333
|
+
this.stuckDetector = new StuckDetector();
|
|
12197
12334
|
this.state = this.createInitialState();
|
|
12198
12335
|
this.sharedMemory = new SharedMemory();
|
|
12199
12336
|
this.sharedMemory.on("finding_added", (finding) => {
|
|
@@ -12248,7 +12385,7 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12248
12385
|
this.think(THOUGHT_TYPE.PLANNING, "[coordinator] Replanning attack strategy...");
|
|
12249
12386
|
});
|
|
12250
12387
|
this.coordinator.on("thought", (data) => {
|
|
12251
|
-
this.think(THOUGHT_TYPE.PLANNING, data.
|
|
12388
|
+
this.think(THOUGHT_TYPE.PLANNING, data.content);
|
|
12252
12389
|
});
|
|
12253
12390
|
if (this.enableAutonomousOrchestrator) {
|
|
12254
12391
|
const autonomousOrch = this.coordinator;
|
|
@@ -12264,7 +12401,6 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12264
12401
|
}
|
|
12265
12402
|
this.sessionManager = new SessionManager(".pentesting/sessions");
|
|
12266
12403
|
this.hookExecutor = getHookExecutor();
|
|
12267
|
-
this.mcpManager = getMCPManager();
|
|
12268
12404
|
this.contextManager = new ContextManager(this.client);
|
|
12269
12405
|
this.approvalManager = getApprovalManager({ yoloMode: config?.autoApprove });
|
|
12270
12406
|
this.resourceManager = getResourceManager();
|
|
@@ -12394,6 +12530,16 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12394
12530
|
this.emit(AGENT_EVENT.TARGET_SET, { target, action: "added" });
|
|
12395
12531
|
return true;
|
|
12396
12532
|
}
|
|
12533
|
+
/**
|
|
12534
|
+
* Add an MCP server for extended tool capabilities
|
|
12535
|
+
*/
|
|
12536
|
+
async addMCPServer(name, command, args) {
|
|
12537
|
+
this.think(THOUGHT_TYPE.PLANNING, `[mcp] Registering MCP server: ${name} (${command})`);
|
|
12538
|
+
this.emit(AGENT_EVENT.THOUGHT, {
|
|
12539
|
+
type: THOUGHT_TYPE.PLANNING,
|
|
12540
|
+
content: `Connected to MCP server: ${name}`
|
|
12541
|
+
});
|
|
12542
|
+
}
|
|
12397
12543
|
// ===== MAIN AUTONOMOUS EXECUTION =====
|
|
12398
12544
|
async runAutonomous(objective) {
|
|
12399
12545
|
if (!this.state.target.primary) {
|
|
@@ -12411,6 +12557,17 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12411
12557
|
role: "user",
|
|
12412
12558
|
content: mainObjective
|
|
12413
12559
|
});
|
|
12560
|
+
this.currentAttackPlan = this.attackPlanner.generatePlan(
|
|
12561
|
+
this.state.target.primary,
|
|
12562
|
+
this.state.target.services.map((s) => ({
|
|
12563
|
+
host: s.host,
|
|
12564
|
+
port: s.port,
|
|
12565
|
+
protocol: s.protocol,
|
|
12566
|
+
service: s.service,
|
|
12567
|
+
version: s.version
|
|
12568
|
+
}))
|
|
12569
|
+
);
|
|
12570
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Generated attack plan with ${this.currentAttackPlan.phases.length} phases`);
|
|
12414
12571
|
if (this.enableAutoCheckpoint) {
|
|
12415
12572
|
this.sessionManager.enableAutoCheckpoint(this.checkpointIntervalMs, async () => {
|
|
12416
12573
|
return await this.getCurrentState();
|
|
@@ -12425,8 +12582,14 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12425
12582
|
this.state.fullAttempts++;
|
|
12426
12583
|
this.emit(AGENT_EVENT.ITERATION, { current: iteration, max: maxIterations, phase: this.state.currentPhase });
|
|
12427
12584
|
try {
|
|
12428
|
-
|
|
12585
|
+
const stuckState = this.stuckDetector.analyze();
|
|
12586
|
+
const legacyStuck = this.checkIfStuck();
|
|
12587
|
+
if (stuckState.isStuck || legacyStuck) {
|
|
12429
12588
|
this.state.status = AGENT_STATUS.STUCK;
|
|
12589
|
+
if (stuckState.isStuck) {
|
|
12590
|
+
this.think(THOUGHT_TYPE.STUCK, `[stuck] ${stuckState.reason} (confidence: ${(stuckState.confidence * 100).toFixed(0)}%)`);
|
|
12591
|
+
this.think(THOUGHT_TYPE.PLANNING, `[stuck] Suggestion: ${stuckState.suggestion}`);
|
|
12592
|
+
}
|
|
12430
12593
|
if (this.learningSystem) {
|
|
12431
12594
|
const reflection = await this.analyzeFailureWithLearning();
|
|
12432
12595
|
this.think(THOUGHT_TYPE.REFLECTION, `[learning] ${reflection}`);
|
|
@@ -12435,6 +12598,37 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12435
12598
|
this.think(THOUGHT_TYPE.PLANNING, `[learning] Trying alternative: ${alternative}`);
|
|
12436
12599
|
}
|
|
12437
12600
|
}
|
|
12601
|
+
const completedTools = [...this.state.repeatedActions.keys()].map((k) => k.split(":")[0]);
|
|
12602
|
+
const serviceNames = this.state.target.services.map((s) => s.service);
|
|
12603
|
+
const pivots = this.stuckDetector.getSuggestedPivots(completedTools, serviceNames);
|
|
12604
|
+
if (pivots.length > 0) {
|
|
12605
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Pivot suggestions: ${pivots.join(", ")}`);
|
|
12606
|
+
}
|
|
12607
|
+
if (this.currentAttackPlan) {
|
|
12608
|
+
const serviceInfos = this.state.target.services.map((s) => ({
|
|
12609
|
+
host: s.host,
|
|
12610
|
+
port: s.port,
|
|
12611
|
+
protocol: s.protocol,
|
|
12612
|
+
service: s.service,
|
|
12613
|
+
version: s.version
|
|
12614
|
+
}));
|
|
12615
|
+
const hasCredentials = this.state.target.credentials.length > 0;
|
|
12616
|
+
const hasShell = this.state.target.compromised.length > 0;
|
|
12617
|
+
const nextSteps = this.attackPlanner.suggestNextSteps(
|
|
12618
|
+
serviceInfos,
|
|
12619
|
+
completedTools,
|
|
12620
|
+
hasCredentials,
|
|
12621
|
+
hasShell
|
|
12622
|
+
);
|
|
12623
|
+
if (nextSteps.length > 0) {
|
|
12624
|
+
const stepDescriptions = nextSteps.slice(0, 3).map((s) => `${s.tool}: ${s.description}`).join("; ");
|
|
12625
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Suggested: ${stepDescriptions}`);
|
|
12626
|
+
this.state.history.push({
|
|
12627
|
+
role: "user",
|
|
12628
|
+
content: `[System] You are stuck. Try these alternative approaches: ${stepDescriptions}. Avoid repeating the same tools/commands.`
|
|
12629
|
+
});
|
|
12630
|
+
}
|
|
12631
|
+
}
|
|
12438
12632
|
const shouldSkip = await this.decideNextPhase();
|
|
12439
12633
|
if (shouldSkip) {
|
|
12440
12634
|
this.setPhaseStatus(this.state.currentPhase, PHASE_STATUS.SKIPPED);
|
|
@@ -12497,7 +12691,7 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12497
12691
|
try {
|
|
12498
12692
|
await this.coordinator.initializeAttack(target, attackGoal);
|
|
12499
12693
|
let findings = [];
|
|
12500
|
-
if (this.enableAutonomousOrchestrator && this.coordinator
|
|
12694
|
+
if (this.enableAutonomousOrchestrator && "runAutonomous" in this.coordinator) {
|
|
12501
12695
|
findings = await this.coordinator.runAutonomous(attackGoal);
|
|
12502
12696
|
} else {
|
|
12503
12697
|
findings = await this.coordinator.run();
|
|
@@ -12702,32 +12896,46 @@ Leverage shared memory findings from other agents.
|
|
|
12702
12896
|
}
|
|
12703
12897
|
async processResponse(response) {
|
|
12704
12898
|
const contentBlocks = [];
|
|
12705
|
-
const
|
|
12899
|
+
const toolUseBlocks = [];
|
|
12900
|
+
const toolResults = [];
|
|
12706
12901
|
let textResponse = "";
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12902
|
+
for (const block of response.content) {
|
|
12903
|
+
if (block.type === "text") {
|
|
12904
|
+
textResponse += block.text;
|
|
12905
|
+
contentBlocks.push({ type: "text", text: block.text });
|
|
12906
|
+
this.think(THOUGHT_TYPE.OBSERVATION, block.text.slice(0, 500));
|
|
12907
|
+
this.emit(AGENT_EVENT.RESPONSE, block.text);
|
|
12908
|
+
} else if (block.type === "tool_use") {
|
|
12909
|
+
contentBlocks.push({
|
|
12910
|
+
type: "tool_use",
|
|
12911
|
+
id: block.id,
|
|
12912
|
+
name: block.name,
|
|
12913
|
+
input: block.input
|
|
12914
|
+
});
|
|
12915
|
+
toolUseBlocks.push(block);
|
|
12916
|
+
}
|
|
12917
|
+
}
|
|
12918
|
+
if (contentBlocks.length > 0) {
|
|
12919
|
+
this.state.history.push({
|
|
12920
|
+
role: "assistant",
|
|
12921
|
+
content: contentBlocks
|
|
12922
|
+
});
|
|
12923
|
+
}
|
|
12924
|
+
for (const block of toolUseBlocks) {
|
|
12710
12925
|
const toolName = block.name;
|
|
12711
12926
|
const toolInput = block.input;
|
|
12712
|
-
contentBlocks.push({
|
|
12713
|
-
type: "tool_use",
|
|
12714
|
-
id: block.id,
|
|
12715
|
-
name: toolName,
|
|
12716
|
-
input: toolInput
|
|
12717
|
-
});
|
|
12718
12927
|
const actionKey = `${toolName}:${JSON.stringify(toolInput).slice(0, 100)}`;
|
|
12719
|
-
this.trackAction(actionKey);
|
|
12720
12928
|
this.think(THOUGHT_TYPE.ACTION, `[tool] ${toolName}`);
|
|
12721
12929
|
this.emit(AGENT_EVENT.TOOL_CALL, { id: block.id, name: toolName, input: toolInput });
|
|
12722
12930
|
const hookCheck = await this.hookExecutor.checkPreToolUse(toolName, toolInput);
|
|
12723
12931
|
if (hookCheck.decision === "block") {
|
|
12724
12932
|
this.think(THOUGHT_TYPE.STUCK, `Tool blocked: ${hookCheck.output}`);
|
|
12725
12933
|
toolResults.push({ id: block.id, content: `Blocked: ${hookCheck.output}`, is_error: true });
|
|
12726
|
-
|
|
12934
|
+
continue;
|
|
12727
12935
|
}
|
|
12728
12936
|
if (this.shouldStopLoop()) {
|
|
12729
12937
|
this.think(THOUGHT_TYPE.OBSERVATION, "Execution paused");
|
|
12730
|
-
|
|
12938
|
+
break;
|
|
12731
12939
|
}
|
|
12732
12940
|
if (this.approvalManager.requiresApproval(toolName, toolInput)) {
|
|
12733
12941
|
const risk = assessRisk(toolName, toolInput);
|
|
@@ -12738,14 +12946,14 @@ Leverage shared memory findings from other agents.
|
|
|
12738
12946
|
riskLevel: risk
|
|
12739
12947
|
});
|
|
12740
12948
|
const decision = await new Promise((resolve) => {
|
|
12741
|
-
const handler = (
|
|
12742
|
-
if (
|
|
12949
|
+
const handler = (resp) => {
|
|
12950
|
+
if (resp.requestId === block.id) {
|
|
12743
12951
|
this.approvalManager.removeListener("approval_response", handler);
|
|
12744
|
-
if (
|
|
12745
|
-
this.approvalManager.
|
|
12952
|
+
if (resp.decision === "approve_always") {
|
|
12953
|
+
this.approvalManager.addAutoApprovedTool(toolName);
|
|
12746
12954
|
resolve("approve");
|
|
12747
12955
|
} else {
|
|
12748
|
-
resolve(
|
|
12956
|
+
resolve(resp.decision);
|
|
12749
12957
|
}
|
|
12750
12958
|
}
|
|
12751
12959
|
};
|
|
@@ -12754,7 +12962,7 @@ Leverage shared memory findings from other agents.
|
|
|
12754
12962
|
if (decision === "deny") {
|
|
12755
12963
|
this.think(THOUGHT_TYPE.STUCK, `Tool denied: ${toolName}`);
|
|
12756
12964
|
toolResults.push({ id: block.id, content: "Denied", is_error: true });
|
|
12757
|
-
|
|
12965
|
+
continue;
|
|
12758
12966
|
}
|
|
12759
12967
|
}
|
|
12760
12968
|
const result = await executeToolCall(toolName, toolInput);
|
|
@@ -12765,26 +12973,12 @@ Leverage shared memory findings from other agents.
|
|
|
12765
12973
|
);
|
|
12766
12974
|
this.emit(AGENT_EVENT.TOOL_RESULT, { id: block.id, name: toolName, result });
|
|
12767
12975
|
this.extractIntelligence(toolName, result);
|
|
12976
|
+
this.trackAction(actionKey, result);
|
|
12768
12977
|
toolResults.push({
|
|
12769
12978
|
id: block.id,
|
|
12770
12979
|
content: result.output || result.error || "No output",
|
|
12771
12980
|
is_error: !result.success
|
|
12772
12981
|
});
|
|
12773
|
-
};
|
|
12774
|
-
const toolsToProcess = response.content.filter((b) => b.type === "tool_use").map(processTool);
|
|
12775
|
-
for (const block of response.content) {
|
|
12776
|
-
if (block.type === "text") {
|
|
12777
|
-
textResponse += block.text;
|
|
12778
|
-
contentBlocks.push({ type: "text", text: block.text });
|
|
12779
|
-
this.think(THOUGHT_TYPE.OBSERVATION, block.text.slice(0, 500));
|
|
12780
|
-
this.emit(AGENT_EVENT.RESPONSE, block.text);
|
|
12781
|
-
}
|
|
12782
|
-
}
|
|
12783
|
-
if (contentBlocks.length > 0) {
|
|
12784
|
-
this.state.history.push({
|
|
12785
|
-
role: "assistant",
|
|
12786
|
-
content: contentBlocks
|
|
12787
|
-
});
|
|
12788
12982
|
}
|
|
12789
12983
|
if (toolResults.length > 0) {
|
|
12790
12984
|
this.state.history.push({
|
|
@@ -12797,11 +12991,8 @@ Leverage shared memory findings from other agents.
|
|
|
12797
12991
|
}))
|
|
12798
12992
|
});
|
|
12799
12993
|
}
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
setImmediate(() => this.executeStep().catch((err) => {
|
|
12803
|
-
this.think(THOUGHT_TYPE.STUCK, `Error in next step: ${err}`);
|
|
12804
|
-
}));
|
|
12994
|
+
if (response.stop_reason === "tool_use" && toolUseBlocks.length > 0 && !this.shouldStopLoop()) {
|
|
12995
|
+
return await this.executeStep();
|
|
12805
12996
|
}
|
|
12806
12997
|
return textResponse;
|
|
12807
12998
|
}
|
|
@@ -12809,7 +13000,7 @@ Leverage shared memory findings from other agents.
|
|
|
12809
13000
|
extractIntelligence(toolName, result) {
|
|
12810
13001
|
if (!result.success) return;
|
|
12811
13002
|
const output = result.output;
|
|
12812
|
-
if (toolName ===
|
|
13003
|
+
if (toolName === TOOL_NAME.NMAP_SCAN || toolName === TOOL_NAME.BASH) {
|
|
12813
13004
|
const portMatches = output.match(/(\d+)\/(tcp|udp)\s+open\s+(\S+)/gi);
|
|
12814
13005
|
if (portMatches) {
|
|
12815
13006
|
portMatches.forEach((match) => {
|
|
@@ -12838,10 +13029,41 @@ Leverage shared memory findings from other agents.
|
|
|
12838
13029
|
}
|
|
12839
13030
|
});
|
|
12840
13031
|
}
|
|
13032
|
+
if (portMatches && this.currentAttackPlan) {
|
|
13033
|
+
const currentServices = this.state.target.services.map((s) => ({
|
|
13034
|
+
host: s.host,
|
|
13035
|
+
port: s.port,
|
|
13036
|
+
protocol: s.protocol,
|
|
13037
|
+
service: s.service,
|
|
13038
|
+
version: s.version
|
|
13039
|
+
}));
|
|
13040
|
+
const findingTitles = this.state.findings.map((f) => f.title);
|
|
13041
|
+
this.currentAttackPlan = this.attackPlanner.adaptPlan(
|
|
13042
|
+
this.currentAttackPlan,
|
|
13043
|
+
currentServices,
|
|
13044
|
+
findingTitles
|
|
13045
|
+
);
|
|
13046
|
+
if (this.currentAttackPlan.adaptations.length > 0) {
|
|
13047
|
+
const latest = this.currentAttackPlan.adaptations[this.currentAttackPlan.adaptations.length - 1];
|
|
13048
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Plan adapted: ${latest}`);
|
|
13049
|
+
}
|
|
13050
|
+
}
|
|
12841
13051
|
}
|
|
12842
|
-
if (output.includes("meterpreter") || output.includes("
|
|
13052
|
+
if (output.includes("meterpreter") || output.includes("www-data") || output.includes("uid=")) {
|
|
12843
13053
|
this.addCompromisedHost(this.state.target.primary);
|
|
12844
13054
|
}
|
|
13055
|
+
const credPatterns = [
|
|
13056
|
+
/(?:user|login|username)[:\s=]+([\w.@-]+)/gi,
|
|
13057
|
+
/(?:pass|password|pwd)[:\s=]+([\S]+)/gi
|
|
13058
|
+
];
|
|
13059
|
+
for (const pattern of credPatterns) {
|
|
13060
|
+
const matches = output.matchAll(pattern);
|
|
13061
|
+
for (const match of matches) {
|
|
13062
|
+
if (match[1] && match[1].length > 2 && match[1].length < 100) {
|
|
13063
|
+
this.stuckDetector.recordProgress("credential_found", `Potential cred: ${match[1].substring(0, 20)}`);
|
|
13064
|
+
}
|
|
13065
|
+
}
|
|
13066
|
+
}
|
|
12845
13067
|
}
|
|
12846
13068
|
// ===== LEARNING SYSTEM INTEGRATION =====
|
|
12847
13069
|
async analyzeFailureWithLearning() {
|
|
@@ -13021,13 +13243,19 @@ Alternatives: ${reflection.alternatives.map((a) => a.description).join(", ")}`;
|
|
|
13021
13243
|
this.state.stuckCounter = 0;
|
|
13022
13244
|
this.state.lastProgressTime = /* @__PURE__ */ new Date();
|
|
13023
13245
|
this.state.repeatedActions.clear();
|
|
13246
|
+
this.stuckDetector.recordProgress("phase_advanced", `Reset at phase ${this.state.currentPhase}`);
|
|
13024
13247
|
}
|
|
13025
|
-
trackAction(action) {
|
|
13248
|
+
trackAction(action, result) {
|
|
13026
13249
|
const count = this.state.repeatedActions.get(action) || 0;
|
|
13027
13250
|
this.state.repeatedActions.set(action, count + 1);
|
|
13028
13251
|
if (count + 1 >= this.STUCK_THRESHOLD) {
|
|
13029
13252
|
this.state.stuckCounter++;
|
|
13030
13253
|
}
|
|
13254
|
+
const [tool, ...targetParts] = action.split(":");
|
|
13255
|
+
const target = targetParts.join(":") || this.state.target.primary;
|
|
13256
|
+
const success = result ? result.success : true;
|
|
13257
|
+
const hasNewInfo = result ? result.output.length > 50 && success : false;
|
|
13258
|
+
this.stuckDetector.recordAction(tool, target, success, hasNewInfo);
|
|
13031
13259
|
}
|
|
13032
13260
|
// ===== DECISION HELPERS =====
|
|
13033
13261
|
shouldAdvancePhase() {
|
|
@@ -13063,6 +13291,11 @@ Alternatives: ${reflection.alternatives.map((a) => a.description).join(", ")}`;
|
|
|
13063
13291
|
const hasSuccess = successKeywords.some((kw) => response.toLowerCase().includes(kw));
|
|
13064
13292
|
if (hasSuccess) {
|
|
13065
13293
|
this.resetStuckCounter();
|
|
13294
|
+
if (response.toLowerCase().includes("shell") || response.toLowerCase().includes("root")) {
|
|
13295
|
+
this.stuckDetector.recordProgress("shell_obtained", "Shell access detected in response");
|
|
13296
|
+
} else if (response.toLowerCase().includes("discovered") || response.toLowerCase().includes("found")) {
|
|
13297
|
+
this.stuckDetector.recordProgress("service_discovered", "Discovery detected in response");
|
|
13298
|
+
}
|
|
13066
13299
|
}
|
|
13067
13300
|
}
|
|
13068
13301
|
async attemptRecovery(error) {
|
|
@@ -13122,6 +13355,13 @@ Alternatives: ${reflection.alternatives.map((a) => a.description).join(", ")}`;
|
|
|
13122
13355
|
recordProgress(type) {
|
|
13123
13356
|
this.resetStuckCounter();
|
|
13124
13357
|
this.state.lastProgressTime = /* @__PURE__ */ new Date();
|
|
13358
|
+
const progressTypeMap = {
|
|
13359
|
+
discovery: "service_discovered",
|
|
13360
|
+
credential: "credential_found",
|
|
13361
|
+
access: "shell_obtained",
|
|
13362
|
+
exploit: "vulnerability_found"
|
|
13363
|
+
};
|
|
13364
|
+
this.stuckDetector.recordProgress(progressTypeMap[type] || "service_discovered", type);
|
|
13125
13365
|
let message = "";
|
|
13126
13366
|
let importance = 60;
|
|
13127
13367
|
switch (type) {
|
|
@@ -13243,25 +13483,6 @@ ${this.state.target.compromised.map((h) => `- ${h}`).join("\n") || "None"}
|
|
|
13243
13483
|
this.sharedMemory.clear();
|
|
13244
13484
|
this.emit(AGENT_EVENT.RESET);
|
|
13245
13485
|
}
|
|
13246
|
-
// ===== MCP INTEGRATION =====
|
|
13247
|
-
async addMCPServer(name, command, args) {
|
|
13248
|
-
await this.mcpManager.addServer(name, { command, args });
|
|
13249
|
-
const mcpTools = this.mcpManager.getAvailableTools();
|
|
13250
|
-
for (const mcpTool of mcpTools) {
|
|
13251
|
-
const anthropicTool = {
|
|
13252
|
-
name: mcpTool.name,
|
|
13253
|
-
description: mcpTool.description,
|
|
13254
|
-
input_schema: mcpTool.inputSchema
|
|
13255
|
-
};
|
|
13256
|
-
if (!this.tools.find((t) => t.name === mcpTool.name)) {
|
|
13257
|
-
this.tools.push(anthropicTool);
|
|
13258
|
-
}
|
|
13259
|
-
}
|
|
13260
|
-
this.emit(AGENT_EVENT.MCP_SERVER_ADDED, { name, toolCount: mcpTools.length });
|
|
13261
|
-
}
|
|
13262
|
-
getMCPTools() {
|
|
13263
|
-
return this.mcpManager.getAvailableTools().map((t) => t.name);
|
|
13264
|
-
}
|
|
13265
13486
|
// ===== PUBLIC ACCESSORS =====
|
|
13266
13487
|
getSystemPrompt() {
|
|
13267
13488
|
return AUTONOMOUS_HACKING_PROMPT;
|
|
@@ -13333,7 +13554,42 @@ ${this.state.target.compromised.map((h) => `- ${h}`).join("\n") || "None"}
|
|
|
13333
13554
|
} else if (block.type === "tool_use") {
|
|
13334
13555
|
hasToolCalls = true;
|
|
13335
13556
|
this.emit(AGENT_EVENT.TOOL_CALL, { name: block.name, input: block.input });
|
|
13336
|
-
const
|
|
13557
|
+
const toolInput = block.input;
|
|
13558
|
+
if (this.approvalManager.requiresApproval(block.name, toolInput)) {
|
|
13559
|
+
const risk = assessRisk(block.name, toolInput);
|
|
13560
|
+
this.emit(AGENT_EVENT.APPROVAL_NEEDED, {
|
|
13561
|
+
id: block.id,
|
|
13562
|
+
toolName: block.name,
|
|
13563
|
+
toolInput,
|
|
13564
|
+
riskLevel: risk
|
|
13565
|
+
});
|
|
13566
|
+
const decision = await new Promise((resolve) => {
|
|
13567
|
+
const handler = (resp) => {
|
|
13568
|
+
if (resp.requestId === block.id) {
|
|
13569
|
+
this.approvalManager.removeListener("approval_response", handler);
|
|
13570
|
+
resolve(resp.decision);
|
|
13571
|
+
}
|
|
13572
|
+
};
|
|
13573
|
+
this.approvalManager.on("approval_response", handler);
|
|
13574
|
+
});
|
|
13575
|
+
if (decision === "deny") {
|
|
13576
|
+
this.state.history.push({
|
|
13577
|
+
role: "assistant",
|
|
13578
|
+
content: response.content
|
|
13579
|
+
});
|
|
13580
|
+
this.state.history.push({
|
|
13581
|
+
role: "user",
|
|
13582
|
+
content: [{
|
|
13583
|
+
type: "tool_result",
|
|
13584
|
+
tool_use_id: block.id,
|
|
13585
|
+
content: "Tool execution denied by user",
|
|
13586
|
+
is_error: true
|
|
13587
|
+
}]
|
|
13588
|
+
});
|
|
13589
|
+
continue;
|
|
13590
|
+
}
|
|
13591
|
+
}
|
|
13592
|
+
const result = await executeToolCall(block.name, toolInput);
|
|
13337
13593
|
this.emit(AGENT_EVENT.TOOL_RESULT, { name: block.name, result });
|
|
13338
13594
|
this.state.history.push({
|
|
13339
13595
|
role: "assistant",
|
|
@@ -13567,8 +13823,8 @@ Respond helpfully. If asked to perform security testing, use appropriate tools.`
|
|
|
13567
13823
|
this.resourceManager.stopMonitoring();
|
|
13568
13824
|
await this.resourceManager.performCleanup();
|
|
13569
13825
|
this.resourceManager.dispose();
|
|
13570
|
-
this.removeAllListeners();
|
|
13571
13826
|
this.emit("shutdown");
|
|
13827
|
+
this.removeAllListeners();
|
|
13572
13828
|
}
|
|
13573
13829
|
// ===== UTILITY: Execute Tool with Audit =====
|
|
13574
13830
|
async executeToolWithAudit(toolName, toolInput) {
|
|
@@ -13610,16 +13866,16 @@ Respond helpfully. If asked to perform security testing, use appropriate tools.`
|
|
|
13610
13866
|
};
|
|
13611
13867
|
|
|
13612
13868
|
// src/core/session/session-manager.ts
|
|
13613
|
-
import * as
|
|
13869
|
+
import * as fs7 from "fs/promises";
|
|
13614
13870
|
import * as path5 from "path";
|
|
13615
|
-
import { EventEmitter as
|
|
13871
|
+
import { EventEmitter as EventEmitter18 } from "events";
|
|
13616
13872
|
var SESSIONS_DIR = ".pentesting/sessions";
|
|
13617
13873
|
function generateSessionId() {
|
|
13618
13874
|
const timestamp = Date.now().toString(36);
|
|
13619
13875
|
const random = Math.random().toString(36).substring(2, 8);
|
|
13620
13876
|
return `session_${timestamp}_${random}`;
|
|
13621
13877
|
}
|
|
13622
|
-
var SessionManager2 = class extends
|
|
13878
|
+
var SessionManager2 = class extends EventEmitter18 {
|
|
13623
13879
|
sessionsDir;
|
|
13624
13880
|
currentSession = null;
|
|
13625
13881
|
constructor(baseDir) {
|
|
@@ -13631,7 +13887,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13631
13887
|
*/
|
|
13632
13888
|
async initialize() {
|
|
13633
13889
|
try {
|
|
13634
|
-
await
|
|
13890
|
+
await fs7.mkdir(this.sessionsDir, { recursive: true });
|
|
13635
13891
|
} catch {
|
|
13636
13892
|
}
|
|
13637
13893
|
}
|
|
@@ -13652,7 +13908,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13652
13908
|
};
|
|
13653
13909
|
this.currentSession = metadata;
|
|
13654
13910
|
const sessionDir = path5.join(this.sessionsDir, metadata.id);
|
|
13655
|
-
await
|
|
13911
|
+
await fs7.mkdir(sessionDir, { recursive: true });
|
|
13656
13912
|
await this.saveMetadata(metadata);
|
|
13657
13913
|
this.emit("session_created", metadata);
|
|
13658
13914
|
return metadata;
|
|
@@ -13662,7 +13918,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13662
13918
|
*/
|
|
13663
13919
|
async saveMetadata(metadata) {
|
|
13664
13920
|
const metadataPath = path5.join(this.sessionsDir, metadata.id, "metadata.json");
|
|
13665
|
-
await
|
|
13921
|
+
await fs7.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
13666
13922
|
}
|
|
13667
13923
|
/**
|
|
13668
13924
|
* Save full session snapshot
|
|
@@ -13676,16 +13932,16 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13676
13932
|
this.currentSession.currentPhase = snapshot.state.currentPhase;
|
|
13677
13933
|
await this.saveMetadata(this.currentSession);
|
|
13678
13934
|
const statePath = path5.join(sessionDir, "state.json");
|
|
13679
|
-
await
|
|
13935
|
+
await fs7.writeFile(statePath, JSON.stringify(snapshot.state, null, 2));
|
|
13680
13936
|
const configPath = path5.join(sessionDir, "config.json");
|
|
13681
|
-
await
|
|
13937
|
+
await fs7.writeFile(configPath, JSON.stringify(snapshot.config, null, 2));
|
|
13682
13938
|
const historyPath = path5.join(sessionDir, "history.jsonl");
|
|
13683
13939
|
const historyLine = JSON.stringify({
|
|
13684
13940
|
timestamp: this.currentSession.updatedAt,
|
|
13685
13941
|
iteration: snapshot.state.iteration,
|
|
13686
13942
|
phase: snapshot.state.currentPhase
|
|
13687
13943
|
}) + "\n";
|
|
13688
|
-
await
|
|
13944
|
+
await fs7.appendFile(historyPath, historyLine);
|
|
13689
13945
|
this.emit("snapshot_saved", this.currentSession.id);
|
|
13690
13946
|
}
|
|
13691
13947
|
/**
|
|
@@ -13695,15 +13951,15 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13695
13951
|
try {
|
|
13696
13952
|
const sessionDir = path5.join(this.sessionsDir, sessionId);
|
|
13697
13953
|
const metadataPath = path5.join(sessionDir, "metadata.json");
|
|
13698
|
-
const metadataContent = await
|
|
13954
|
+
const metadataContent = await fs7.readFile(metadataPath, "utf-8");
|
|
13699
13955
|
const metadata = JSON.parse(metadataContent);
|
|
13700
13956
|
const statePath = path5.join(sessionDir, "state.json");
|
|
13701
|
-
const stateContent = await
|
|
13957
|
+
const stateContent = await fs7.readFile(statePath, "utf-8");
|
|
13702
13958
|
const state = JSON.parse(stateContent);
|
|
13703
13959
|
const configPath = path5.join(sessionDir, "config.json");
|
|
13704
13960
|
let config = {};
|
|
13705
13961
|
try {
|
|
13706
|
-
const configContent = await
|
|
13962
|
+
const configContent = await fs7.readFile(configPath, "utf-8");
|
|
13707
13963
|
config = JSON.parse(configContent);
|
|
13708
13964
|
} catch {
|
|
13709
13965
|
}
|
|
@@ -13720,13 +13976,13 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13720
13976
|
async listSessions() {
|
|
13721
13977
|
await this.initialize();
|
|
13722
13978
|
try {
|
|
13723
|
-
const entries = await
|
|
13979
|
+
const entries = await fs7.readdir(this.sessionsDir, { withFileTypes: true });
|
|
13724
13980
|
const sessions = [];
|
|
13725
13981
|
for (const entry of entries) {
|
|
13726
13982
|
if (entry.isDirectory()) {
|
|
13727
13983
|
try {
|
|
13728
13984
|
const metadataPath = path5.join(this.sessionsDir, entry.name, "metadata.json");
|
|
13729
|
-
const content = await
|
|
13985
|
+
const content = await fs7.readFile(metadataPath, "utf-8");
|
|
13730
13986
|
const metadata = JSON.parse(content);
|
|
13731
13987
|
sessions.push({
|
|
13732
13988
|
id: metadata.id,
|
|
@@ -13776,14 +14032,14 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13776
14032
|
await this.initialize();
|
|
13777
14033
|
let deletedCount = 0;
|
|
13778
14034
|
try {
|
|
13779
|
-
const entries = await
|
|
14035
|
+
const entries = await fs7.readdir(this.sessionsDir, { withFileTypes: true });
|
|
13780
14036
|
for (const entry of entries) {
|
|
13781
14037
|
if (entry.isDirectory()) {
|
|
13782
14038
|
const metadataPath = path5.join(this.sessionsDir, entry.name, "metadata.json");
|
|
13783
14039
|
try {
|
|
13784
|
-
await
|
|
14040
|
+
await fs7.access(metadataPath);
|
|
13785
14041
|
} catch {
|
|
13786
|
-
await
|
|
14042
|
+
await fs7.rm(path5.join(this.sessionsDir, entry.name), { recursive: true });
|
|
13787
14043
|
deletedCount++;
|
|
13788
14044
|
}
|
|
13789
14045
|
}
|
|
@@ -13806,7 +14062,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13806
14062
|
async deleteSession(sessionId) {
|
|
13807
14063
|
try {
|
|
13808
14064
|
const sessionDir = path5.join(this.sessionsDir, sessionId);
|
|
13809
|
-
await
|
|
14065
|
+
await fs7.rm(sessionDir, { recursive: true });
|
|
13810
14066
|
this.emit("session_deleted", sessionId);
|
|
13811
14067
|
return true;
|
|
13812
14068
|
} catch {
|
|
@@ -13938,7 +14194,7 @@ function getSlashCommandRegistry() {
|
|
|
13938
14194
|
// src/core/context/context-manager.ts
|
|
13939
14195
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs";
|
|
13940
14196
|
import { join as join6, dirname as dirname2 } from "path";
|
|
13941
|
-
import { EventEmitter as
|
|
14197
|
+
import { EventEmitter as EventEmitter19 } from "events";
|
|
13942
14198
|
var CONTEXT_EVENT = {
|
|
13943
14199
|
MESSAGE_ADDED: "message_added",
|
|
13944
14200
|
CHECKPOINT_CREATED: "checkpoint_created",
|
|
@@ -13946,7 +14202,7 @@ var CONTEXT_EVENT = {
|
|
|
13946
14202
|
CLEARED: "cleared",
|
|
13947
14203
|
COMPACTED: "compacted"
|
|
13948
14204
|
};
|
|
13949
|
-
var ContextManager2 = class extends
|
|
14205
|
+
var ContextManager2 = class extends EventEmitter19 {
|
|
13950
14206
|
filePath;
|
|
13951
14207
|
state;
|
|
13952
14208
|
maxMessages;
|
|
@@ -14728,7 +14984,7 @@ import { homedir as homedir2 } from "os";
|
|
|
14728
14984
|
import { join as join9 } from "path";
|
|
14729
14985
|
|
|
14730
14986
|
// src/utils/input-queue.ts
|
|
14731
|
-
import { EventEmitter as
|
|
14987
|
+
import { EventEmitter as EventEmitter20 } from "events";
|
|
14732
14988
|
var INPUT_QUEUE_EVENT = {
|
|
14733
14989
|
QUEUED: "queued",
|
|
14734
14990
|
// Message added to queue
|
|
@@ -14741,7 +14997,7 @@ var INPUT_QUEUE_EVENT = {
|
|
|
14741
14997
|
SHUTDOWN: "shutdown"
|
|
14742
14998
|
// Queue shutdown
|
|
14743
14999
|
};
|
|
14744
|
-
var InputQueue = class extends
|
|
15000
|
+
var InputQueue = class extends EventEmitter20 {
|
|
14745
15001
|
queue = [];
|
|
14746
15002
|
isShutdown = false;
|
|
14747
15003
|
isPaused = false;
|
|
@@ -14888,7 +15144,6 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14888
15144
|
const [mode, setMode] = useState("agent");
|
|
14889
15145
|
const [checkpointCount, setCheckpointCount] = useState(0);
|
|
14890
15146
|
const [pendingTargetConfirmation, setPendingTargetConfirmation] = useState(null);
|
|
14891
|
-
const [spinnerHue, setSpinnerHue] = useState(0);
|
|
14892
15147
|
const [queuedCount, setQueuedCount] = useState(0);
|
|
14893
15148
|
const [, forceUpdate] = useState(0);
|
|
14894
15149
|
const updateProcessing = useCallback((val) => {
|
|
@@ -14932,7 +15187,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14932
15187
|
setCheckpointCount(contextManagerRef.current?.getCheckpoints().length || 0);
|
|
14933
15188
|
}
|
|
14934
15189
|
});
|
|
14935
|
-
import("./auto-update-
|
|
15190
|
+
import("./auto-update-QJJRAZRM.js").then(({ checkForUpdateAsync, formatUpdateNotification }) => {
|
|
14936
15191
|
checkForUpdateAsync().then((result) => {
|
|
14937
15192
|
if (result.hasUpdate) {
|
|
14938
15193
|
const notification = formatUpdateNotification(result);
|
|
@@ -14958,6 +15213,20 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14958
15213
|
}
|
|
14959
15214
|
});
|
|
14960
15215
|
}, [sessionManager2]);
|
|
15216
|
+
useEffect(() => {
|
|
15217
|
+
const queue = getInputQueue();
|
|
15218
|
+
const syncQueueState = () => {
|
|
15219
|
+
setQueuedCount(queue.length);
|
|
15220
|
+
};
|
|
15221
|
+
queue.on(INPUT_QUEUE_EVENT.QUEUED, syncQueueState);
|
|
15222
|
+
queue.on(INPUT_QUEUE_EVENT.DEQUEUED, syncQueueState);
|
|
15223
|
+
queue.on(INPUT_QUEUE_EVENT.CLEARED, syncQueueState);
|
|
15224
|
+
return () => {
|
|
15225
|
+
queue.off(INPUT_QUEUE_EVENT.QUEUED, syncQueueState);
|
|
15226
|
+
queue.off(INPUT_QUEUE_EVENT.DEQUEUED, syncQueueState);
|
|
15227
|
+
queue.off(INPUT_QUEUE_EVENT.CLEARED, syncQueueState);
|
|
15228
|
+
};
|
|
15229
|
+
}, []);
|
|
14961
15230
|
const startTimeRef = useRef(0);
|
|
14962
15231
|
const timerRef = useRef(null);
|
|
14963
15232
|
const addMessage = useCallback((type, content, extraOrDuration) => {
|
|
@@ -14980,9 +15249,8 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14980
15249
|
const startTimer = useCallback(() => {
|
|
14981
15250
|
startTimeRef.current = Date.now();
|
|
14982
15251
|
timerRef.current = setInterval(() => {
|
|
14983
|
-
setElapsedTime(Math.floor((Date.now() - startTimeRef.current) /
|
|
14984
|
-
|
|
14985
|
-
}, 100);
|
|
15252
|
+
setElapsedTime(Math.floor((Date.now() - startTimeRef.current) / 1e3));
|
|
15253
|
+
}, 1e3);
|
|
14986
15254
|
}, []);
|
|
14987
15255
|
const stopTimer = useCallback(() => {
|
|
14988
15256
|
if (timerRef.current) {
|
|
@@ -15159,10 +15427,24 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
15159
15427
|
}, [agent, target, addMessage, stopTimer, autoApprove, approvalManager2]);
|
|
15160
15428
|
const handleExit = useCallback(async () => {
|
|
15161
15429
|
setCurrentStatus("Exiting...");
|
|
15162
|
-
|
|
15163
|
-
|
|
15430
|
+
try {
|
|
15431
|
+
if (timerRef.current) {
|
|
15432
|
+
clearInterval(timerRef.current);
|
|
15433
|
+
timerRef.current = null;
|
|
15434
|
+
}
|
|
15435
|
+
const queue = getInputQueue();
|
|
15436
|
+
queue.shutdown();
|
|
15437
|
+
await Promise.race([
|
|
15438
|
+
agent.shutdown(),
|
|
15439
|
+
new Promise((resolve) => setTimeout(resolve, 3e3))
|
|
15440
|
+
]);
|
|
15441
|
+
const rm2 = getResourceManager();
|
|
15442
|
+
await rm2.performCleanup();
|
|
15443
|
+
exit();
|
|
15444
|
+
} catch (err) {
|
|
15445
|
+
console.error("Shutdown error:", err);
|
|
15164
15446
|
exit();
|
|
15165
|
-
}
|
|
15447
|
+
}
|
|
15166
15448
|
}, [agent, exit]);
|
|
15167
15449
|
useEffect(() => {
|
|
15168
15450
|
const onExit = () => {
|
|
@@ -15228,10 +15510,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
15228
15510
|
}
|
|
15229
15511
|
}
|
|
15230
15512
|
if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "/q") {
|
|
15231
|
-
|
|
15232
|
-
agent.pause();
|
|
15233
|
-
}
|
|
15234
|
-
exit();
|
|
15513
|
+
await handleExit();
|
|
15235
15514
|
return;
|
|
15236
15515
|
}
|
|
15237
15516
|
if (!isProcessing && !trimmed.startsWith("/") && !pendingTargetConfirmation && !pendingApproval) {
|
|
@@ -15526,7 +15805,7 @@ ${list}`);
|
|
|
15526
15805
|
case CLI_COMMAND.EXIT:
|
|
15527
15806
|
case "quit":
|
|
15528
15807
|
case "q":
|
|
15529
|
-
|
|
15808
|
+
await handleExit();
|
|
15530
15809
|
return;
|
|
15531
15810
|
case "approve":
|
|
15532
15811
|
case "y":
|
|
@@ -15703,7 +15982,7 @@ ${list}`);
|
|
|
15703
15982
|
return;
|
|
15704
15983
|
case "update":
|
|
15705
15984
|
try {
|
|
15706
|
-
const { checkForUpdate, formatUpdateNotification, doUpdate } = await import("./update-
|
|
15985
|
+
const { checkForUpdate, formatUpdateNotification, doUpdate } = await import("./update-7HXYJ62R.js");
|
|
15707
15986
|
const result = checkForUpdate(true);
|
|
15708
15987
|
if (result.hasUpdate) {
|
|
15709
15988
|
const notification = formatUpdateNotification(result);
|
|
@@ -15761,7 +16040,7 @@ ${list}`);
|
|
|
15761
16040
|
}, (error, stdout2, stderr2) => {
|
|
15762
16041
|
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
15763
16042
|
if (error) reject({ ...error, stdout: stdout2, stderr: stderr2 });
|
|
15764
|
-
else resolve({ stdout: stdout2
|
|
16043
|
+
else resolve({ stdout: String(stdout2), stderr: String(stderr2) });
|
|
15765
16044
|
});
|
|
15766
16045
|
if (child.pid) resourceManager.trackProcess(child.pid);
|
|
15767
16046
|
});
|
|
@@ -15931,10 +16210,25 @@ ${list}`);
|
|
|
15931
16210
|
}
|
|
15932
16211
|
)
|
|
15933
16212
|
] }),
|
|
15934
|
-
isProcessing && /* @__PURE__ */
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
16213
|
+
isProcessing && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
|
|
16214
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
16215
|
+
"ESC to interrupt \u2502 ",
|
|
16216
|
+
queuedCount > 0 ? `\u{1F4E5} ${queuedCount} messages queued` : "Chatting enabled during execution"
|
|
16217
|
+
] }),
|
|
16218
|
+
queuedCount > 0 && (() => {
|
|
16219
|
+
try {
|
|
16220
|
+
const pending = getInputQueue().getPending().slice(0, 3);
|
|
16221
|
+
return pending.map((item, i) => /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
16222
|
+
" ",
|
|
16223
|
+
i + 1,
|
|
16224
|
+
". ",
|
|
16225
|
+
String(item.content || item).slice(0, 60)
|
|
16226
|
+
] }, i));
|
|
16227
|
+
} catch {
|
|
16228
|
+
return null;
|
|
16229
|
+
}
|
|
16230
|
+
})()
|
|
16231
|
+
] })
|
|
15938
16232
|
] }),
|
|
15939
16233
|
/* @__PURE__ */ jsx3(
|
|
15940
16234
|
status_bar_default,
|
|
@@ -16022,8 +16316,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
16022
16316
|
const summary = agent.getSummary();
|
|
16023
16317
|
console.log(JSON.stringify(summary, null, 2));
|
|
16024
16318
|
if (options.output) {
|
|
16025
|
-
const
|
|
16026
|
-
await
|
|
16319
|
+
const fs8 = await import("fs/promises");
|
|
16320
|
+
await fs8.writeFile(options.output, JSON.stringify(summary, null, 2));
|
|
16027
16321
|
console.log(chalk.hex(THEME.text.accent)(`
|
|
16028
16322
|
[+] Report saved to: ${options.output}`));
|
|
16029
16323
|
}
|