pentesting 0.12.7 → 0.12.13
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-6CLBRLE3.js} +2 -2
- package/dist/{chunk-M5JWJSPW.js → chunk-5IKQY4A4.js} +1 -1
- package/dist/chunk-6IXHQS2A.js +525 -0
- package/dist/chunk-AOJBE232.js +457 -0
- package/dist/index.js +1267 -939
- package/dist/{update-UMRID565.js → update-34NDFWS3.js} +2 -2
- package/dist/web-search-XQYEM24B.js +43 -0
- package/package.json +6 -6
- 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-6IXHQS2A.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,206 +3701,50 @@ 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
|
|
3815
|
-
result = await executeSearchWindowsPrivesc(input);
|
|
3816
|
-
break;
|
|
3817
|
-
case
|
|
3818
|
-
result = await executeCtfResearch(input);
|
|
3819
|
-
break;
|
|
3820
|
-
case
|
|
3821
|
-
result = await executeSecurityResearch(input);
|
|
3822
|
-
break;
|
|
3823
|
-
default:
|
|
3824
|
-
result = {
|
|
3825
|
-
success: false,
|
|
3826
|
-
output: "",
|
|
3827
|
-
error: `Unknown tool: ${toolName}`,
|
|
3828
|
-
duration: Date.now() - startTime
|
|
3829
|
-
};
|
|
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
|
-
};
|
|
3950
|
-
} catch (error) {
|
|
3951
|
-
return {
|
|
3952
|
-
success: false,
|
|
3953
|
-
output: "",
|
|
3954
|
-
error: error.message || String(error),
|
|
3955
|
-
duration: 0
|
|
3956
|
-
};
|
|
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
|
-
};
|
|
3719
|
+
case TOOL_NAME.SEARCH_WINDOWS_PRIVESC:
|
|
3720
|
+
result = await executeSearchWindowsPrivesc(input);
|
|
3721
|
+
break;
|
|
3722
|
+
case TOOL_NAME.CTF_RESEARCH:
|
|
3723
|
+
result = await executeCtfResearch(input);
|
|
3724
|
+
break;
|
|
3725
|
+
case TOOL_NAME.SECURITY_RESEARCH:
|
|
3726
|
+
result = await executeSecurityResearch(input);
|
|
3727
|
+
break;
|
|
3728
|
+
default:
|
|
3729
|
+
result = {
|
|
3730
|
+
success: false,
|
|
3731
|
+
output: "",
|
|
3732
|
+
error: `Unknown tool: ${toolName}`,
|
|
3733
|
+
duration: Date.now() - startTime
|
|
3734
|
+
};
|
|
3984
3735
|
}
|
|
3985
|
-
|
|
3986
|
-
|
|
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 };
|
|
3736
|
+
result.duration = Date.now() - startTime;
|
|
3737
|
+
return result;
|
|
3997
3738
|
} catch (error) {
|
|
3998
|
-
|
|
3739
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
3740
|
+
return {
|
|
3741
|
+
success: false,
|
|
3742
|
+
output: "",
|
|
3743
|
+
error: errMsg,
|
|
3744
|
+
duration: Date.now() - startTime
|
|
3745
|
+
};
|
|
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();
|
|
@@ -12017,93 +11571,672 @@ var ApprovalManager = class extends EventEmitter17 {
|
|
|
12017
11571
|
});
|
|
12018
11572
|
}
|
|
12019
11573
|
/**
|
|
12020
|
-
* Respond to an approval request (called by UI)
|
|
12021
|
-
* Now handles timed YOLO modes
|
|
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
|
+
}
|
|
11946
|
+
}
|
|
11947
|
+
}
|
|
11948
|
+
}
|
|
11949
|
+
}
|
|
11950
|
+
suggestions.sort((a, b) => b.priority - a.priority);
|
|
11951
|
+
return suggestions.slice(0, 10);
|
|
11952
|
+
}
|
|
11953
|
+
/**
|
|
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,11 +12317,12 @@ 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;
|
|
12188
12323
|
MAX_PHASE_ATTEMPTS = 20;
|
|
12324
|
+
// Stream control
|
|
12325
|
+
currentStream = null;
|
|
12189
12326
|
constructor(apiKey, config) {
|
|
12190
12327
|
super();
|
|
12191
12328
|
this.client = new Anthropic({
|
|
@@ -12194,6 +12331,8 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12194
12331
|
});
|
|
12195
12332
|
this.config = { ...AGENT_CONFIG, ...config };
|
|
12196
12333
|
this.tools = ALL_TOOLS;
|
|
12334
|
+
this.attackPlanner = getAttackPlanner();
|
|
12335
|
+
this.stuckDetector = new StuckDetector();
|
|
12197
12336
|
this.state = this.createInitialState();
|
|
12198
12337
|
this.sharedMemory = new SharedMemory();
|
|
12199
12338
|
this.sharedMemory.on("finding_added", (finding) => {
|
|
@@ -12248,7 +12387,7 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12248
12387
|
this.think(THOUGHT_TYPE.PLANNING, "[coordinator] Replanning attack strategy...");
|
|
12249
12388
|
});
|
|
12250
12389
|
this.coordinator.on("thought", (data) => {
|
|
12251
|
-
this.think(THOUGHT_TYPE.PLANNING, data.
|
|
12390
|
+
this.think(THOUGHT_TYPE.PLANNING, data.content);
|
|
12252
12391
|
});
|
|
12253
12392
|
if (this.enableAutonomousOrchestrator) {
|
|
12254
12393
|
const autonomousOrch = this.coordinator;
|
|
@@ -12264,7 +12403,6 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12264
12403
|
}
|
|
12265
12404
|
this.sessionManager = new SessionManager(".pentesting/sessions");
|
|
12266
12405
|
this.hookExecutor = getHookExecutor();
|
|
12267
|
-
this.mcpManager = getMCPManager();
|
|
12268
12406
|
this.contextManager = new ContextManager(this.client);
|
|
12269
12407
|
this.approvalManager = getApprovalManager({ yoloMode: config?.autoApprove });
|
|
12270
12408
|
this.resourceManager = getResourceManager();
|
|
@@ -12394,6 +12532,16 @@ var AutonomousHackingAgent = class extends EventEmitter18 {
|
|
|
12394
12532
|
this.emit(AGENT_EVENT.TARGET_SET, { target, action: "added" });
|
|
12395
12533
|
return true;
|
|
12396
12534
|
}
|
|
12535
|
+
/**
|
|
12536
|
+
* Add an MCP server for extended tool capabilities
|
|
12537
|
+
*/
|
|
12538
|
+
async addMCPServer(name, command, args) {
|
|
12539
|
+
this.think(THOUGHT_TYPE.PLANNING, `[mcp] Registering MCP server: ${name} (${command})`);
|
|
12540
|
+
this.emit(AGENT_EVENT.THOUGHT, {
|
|
12541
|
+
type: THOUGHT_TYPE.PLANNING,
|
|
12542
|
+
content: `Connected to MCP server: ${name}`
|
|
12543
|
+
});
|
|
12544
|
+
}
|
|
12397
12545
|
// ===== MAIN AUTONOMOUS EXECUTION =====
|
|
12398
12546
|
async runAutonomous(objective) {
|
|
12399
12547
|
if (!this.state.target.primary) {
|
|
@@ -12411,6 +12559,17 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12411
12559
|
role: "user",
|
|
12412
12560
|
content: mainObjective
|
|
12413
12561
|
});
|
|
12562
|
+
this.currentAttackPlan = this.attackPlanner.generatePlan(
|
|
12563
|
+
this.state.target.primary,
|
|
12564
|
+
this.state.target.services.map((s) => ({
|
|
12565
|
+
host: s.host,
|
|
12566
|
+
port: s.port,
|
|
12567
|
+
protocol: s.protocol,
|
|
12568
|
+
service: s.service,
|
|
12569
|
+
version: s.version
|
|
12570
|
+
}))
|
|
12571
|
+
);
|
|
12572
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Generated attack plan with ${this.currentAttackPlan.phases.length} phases`);
|
|
12414
12573
|
if (this.enableAutoCheckpoint) {
|
|
12415
12574
|
this.sessionManager.enableAutoCheckpoint(this.checkpointIntervalMs, async () => {
|
|
12416
12575
|
return await this.getCurrentState();
|
|
@@ -12425,8 +12584,14 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12425
12584
|
this.state.fullAttempts++;
|
|
12426
12585
|
this.emit(AGENT_EVENT.ITERATION, { current: iteration, max: maxIterations, phase: this.state.currentPhase });
|
|
12427
12586
|
try {
|
|
12428
|
-
|
|
12587
|
+
const stuckState = this.stuckDetector.analyze();
|
|
12588
|
+
const legacyStuck = this.checkIfStuck();
|
|
12589
|
+
if (stuckState.isStuck || legacyStuck) {
|
|
12429
12590
|
this.state.status = AGENT_STATUS.STUCK;
|
|
12591
|
+
if (stuckState.isStuck) {
|
|
12592
|
+
this.think(THOUGHT_TYPE.STUCK, `[stuck] ${stuckState.reason} (confidence: ${(stuckState.confidence * 100).toFixed(0)}%)`);
|
|
12593
|
+
this.think(THOUGHT_TYPE.PLANNING, `[stuck] Suggestion: ${stuckState.suggestion}`);
|
|
12594
|
+
}
|
|
12430
12595
|
if (this.learningSystem) {
|
|
12431
12596
|
const reflection = await this.analyzeFailureWithLearning();
|
|
12432
12597
|
this.think(THOUGHT_TYPE.REFLECTION, `[learning] ${reflection}`);
|
|
@@ -12435,6 +12600,37 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12435
12600
|
this.think(THOUGHT_TYPE.PLANNING, `[learning] Trying alternative: ${alternative}`);
|
|
12436
12601
|
}
|
|
12437
12602
|
}
|
|
12603
|
+
const completedTools = [...this.state.repeatedActions.keys()].map((k) => k.split(":")[0]);
|
|
12604
|
+
const serviceNames = this.state.target.services.map((s) => s.service);
|
|
12605
|
+
const pivots = this.stuckDetector.getSuggestedPivots(completedTools, serviceNames);
|
|
12606
|
+
if (pivots.length > 0) {
|
|
12607
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Pivot suggestions: ${pivots.join(", ")}`);
|
|
12608
|
+
}
|
|
12609
|
+
if (this.currentAttackPlan) {
|
|
12610
|
+
const serviceInfos = this.state.target.services.map((s) => ({
|
|
12611
|
+
host: s.host,
|
|
12612
|
+
port: s.port,
|
|
12613
|
+
protocol: s.protocol,
|
|
12614
|
+
service: s.service,
|
|
12615
|
+
version: s.version
|
|
12616
|
+
}));
|
|
12617
|
+
const hasCredentials = this.state.target.credentials.length > 0;
|
|
12618
|
+
const hasShell = this.state.target.compromised.length > 0;
|
|
12619
|
+
const nextSteps = this.attackPlanner.suggestNextSteps(
|
|
12620
|
+
serviceInfos,
|
|
12621
|
+
completedTools,
|
|
12622
|
+
hasCredentials,
|
|
12623
|
+
hasShell
|
|
12624
|
+
);
|
|
12625
|
+
if (nextSteps.length > 0) {
|
|
12626
|
+
const stepDescriptions = nextSteps.slice(0, 3).map((s) => `${s.tool}: ${s.description}`).join("; ");
|
|
12627
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Suggested: ${stepDescriptions}`);
|
|
12628
|
+
this.state.history.push({
|
|
12629
|
+
role: "user",
|
|
12630
|
+
content: `[System] You are stuck. Try these alternative approaches: ${stepDescriptions}. Avoid repeating the same tools/commands.`
|
|
12631
|
+
});
|
|
12632
|
+
}
|
|
12633
|
+
}
|
|
12438
12634
|
const shouldSkip = await this.decideNextPhase();
|
|
12439
12635
|
if (shouldSkip) {
|
|
12440
12636
|
this.setPhaseStatus(this.state.currentPhase, PHASE_STATUS.SKIPPED);
|
|
@@ -12497,7 +12693,7 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12497
12693
|
try {
|
|
12498
12694
|
await this.coordinator.initializeAttack(target, attackGoal);
|
|
12499
12695
|
let findings = [];
|
|
12500
|
-
if (this.enableAutonomousOrchestrator && this.coordinator
|
|
12696
|
+
if (this.enableAutonomousOrchestrator && "runAutonomous" in this.coordinator) {
|
|
12501
12697
|
findings = await this.coordinator.runAutonomous(attackGoal);
|
|
12502
12698
|
} else {
|
|
12503
12699
|
findings = await this.coordinator.run();
|
|
@@ -12622,7 +12818,7 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12622
12818
|
this.emit(AGENT_EVENT.LLM_START, { model: LLM_MODEL });
|
|
12623
12819
|
let response;
|
|
12624
12820
|
try {
|
|
12625
|
-
|
|
12821
|
+
this.currentStream = this.client.messages.stream({
|
|
12626
12822
|
model: LLM_MODEL,
|
|
12627
12823
|
max_tokens: LLM_MAX_TOKENS,
|
|
12628
12824
|
system: systemPrompt,
|
|
@@ -12631,7 +12827,7 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12631
12827
|
thinking: { type: "enabled", budget_tokens: 16e3 }
|
|
12632
12828
|
});
|
|
12633
12829
|
let thinkingBuffer = "";
|
|
12634
|
-
|
|
12830
|
+
this.currentStream.on("contentBlock", (block) => {
|
|
12635
12831
|
if (block.type === "thinking" && block.thinking) {
|
|
12636
12832
|
thinkingBuffer += block.thinking;
|
|
12637
12833
|
this.emit(AGENT_EVENT.THOUGHT, {
|
|
@@ -12645,7 +12841,8 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
|
|
|
12645
12841
|
});
|
|
12646
12842
|
}
|
|
12647
12843
|
});
|
|
12648
|
-
response = await
|
|
12844
|
+
response = await this.currentStream.finalMessage();
|
|
12845
|
+
this.currentStream = null;
|
|
12649
12846
|
} catch (error) {
|
|
12650
12847
|
response = await withRetry(
|
|
12651
12848
|
() => this.client.messages.create({
|
|
@@ -12702,32 +12899,46 @@ Leverage shared memory findings from other agents.
|
|
|
12702
12899
|
}
|
|
12703
12900
|
async processResponse(response) {
|
|
12704
12901
|
const contentBlocks = [];
|
|
12705
|
-
const
|
|
12902
|
+
const toolUseBlocks = [];
|
|
12903
|
+
const toolResults = [];
|
|
12706
12904
|
let textResponse = "";
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12905
|
+
for (const block of response.content) {
|
|
12906
|
+
if (block.type === "text") {
|
|
12907
|
+
textResponse += block.text;
|
|
12908
|
+
contentBlocks.push({ type: "text", text: block.text });
|
|
12909
|
+
this.think(THOUGHT_TYPE.OBSERVATION, block.text.slice(0, 500));
|
|
12910
|
+
this.emit(AGENT_EVENT.RESPONSE, block.text);
|
|
12911
|
+
} else if (block.type === "tool_use") {
|
|
12912
|
+
contentBlocks.push({
|
|
12913
|
+
type: "tool_use",
|
|
12914
|
+
id: block.id,
|
|
12915
|
+
name: block.name,
|
|
12916
|
+
input: block.input
|
|
12917
|
+
});
|
|
12918
|
+
toolUseBlocks.push(block);
|
|
12919
|
+
}
|
|
12920
|
+
}
|
|
12921
|
+
if (contentBlocks.length > 0) {
|
|
12922
|
+
this.state.history.push({
|
|
12923
|
+
role: "assistant",
|
|
12924
|
+
content: contentBlocks
|
|
12925
|
+
});
|
|
12926
|
+
}
|
|
12927
|
+
for (const block of toolUseBlocks) {
|
|
12710
12928
|
const toolName = block.name;
|
|
12711
12929
|
const toolInput = block.input;
|
|
12712
|
-
contentBlocks.push({
|
|
12713
|
-
type: "tool_use",
|
|
12714
|
-
id: block.id,
|
|
12715
|
-
name: toolName,
|
|
12716
|
-
input: toolInput
|
|
12717
|
-
});
|
|
12718
12930
|
const actionKey = `${toolName}:${JSON.stringify(toolInput).slice(0, 100)}`;
|
|
12719
|
-
this.trackAction(actionKey);
|
|
12720
12931
|
this.think(THOUGHT_TYPE.ACTION, `[tool] ${toolName}`);
|
|
12721
12932
|
this.emit(AGENT_EVENT.TOOL_CALL, { id: block.id, name: toolName, input: toolInput });
|
|
12722
12933
|
const hookCheck = await this.hookExecutor.checkPreToolUse(toolName, toolInput);
|
|
12723
12934
|
if (hookCheck.decision === "block") {
|
|
12724
12935
|
this.think(THOUGHT_TYPE.STUCK, `Tool blocked: ${hookCheck.output}`);
|
|
12725
12936
|
toolResults.push({ id: block.id, content: `Blocked: ${hookCheck.output}`, is_error: true });
|
|
12726
|
-
|
|
12937
|
+
continue;
|
|
12727
12938
|
}
|
|
12728
12939
|
if (this.shouldStopLoop()) {
|
|
12729
12940
|
this.think(THOUGHT_TYPE.OBSERVATION, "Execution paused");
|
|
12730
|
-
|
|
12941
|
+
break;
|
|
12731
12942
|
}
|
|
12732
12943
|
if (this.approvalManager.requiresApproval(toolName, toolInput)) {
|
|
12733
12944
|
const risk = assessRisk(toolName, toolInput);
|
|
@@ -12738,14 +12949,14 @@ Leverage shared memory findings from other agents.
|
|
|
12738
12949
|
riskLevel: risk
|
|
12739
12950
|
});
|
|
12740
12951
|
const decision = await new Promise((resolve) => {
|
|
12741
|
-
const handler = (
|
|
12742
|
-
if (
|
|
12952
|
+
const handler = (resp) => {
|
|
12953
|
+
if (resp.requestId === block.id) {
|
|
12743
12954
|
this.approvalManager.removeListener("approval_response", handler);
|
|
12744
|
-
if (
|
|
12745
|
-
this.approvalManager.
|
|
12955
|
+
if (resp.decision === "approve_always") {
|
|
12956
|
+
this.approvalManager.addAutoApprovedTool(toolName);
|
|
12746
12957
|
resolve("approve");
|
|
12747
12958
|
} else {
|
|
12748
|
-
resolve(
|
|
12959
|
+
resolve(resp.decision);
|
|
12749
12960
|
}
|
|
12750
12961
|
}
|
|
12751
12962
|
};
|
|
@@ -12754,7 +12965,7 @@ Leverage shared memory findings from other agents.
|
|
|
12754
12965
|
if (decision === "deny") {
|
|
12755
12966
|
this.think(THOUGHT_TYPE.STUCK, `Tool denied: ${toolName}`);
|
|
12756
12967
|
toolResults.push({ id: block.id, content: "Denied", is_error: true });
|
|
12757
|
-
|
|
12968
|
+
continue;
|
|
12758
12969
|
}
|
|
12759
12970
|
}
|
|
12760
12971
|
const result = await executeToolCall(toolName, toolInput);
|
|
@@ -12765,26 +12976,12 @@ Leverage shared memory findings from other agents.
|
|
|
12765
12976
|
);
|
|
12766
12977
|
this.emit(AGENT_EVENT.TOOL_RESULT, { id: block.id, name: toolName, result });
|
|
12767
12978
|
this.extractIntelligence(toolName, result);
|
|
12979
|
+
this.trackAction(actionKey, result);
|
|
12768
12980
|
toolResults.push({
|
|
12769
12981
|
id: block.id,
|
|
12770
12982
|
content: result.output || result.error || "No output",
|
|
12771
12983
|
is_error: !result.success
|
|
12772
12984
|
});
|
|
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
12985
|
}
|
|
12789
12986
|
if (toolResults.length > 0) {
|
|
12790
12987
|
this.state.history.push({
|
|
@@ -12797,11 +12994,8 @@ Leverage shared memory findings from other agents.
|
|
|
12797
12994
|
}))
|
|
12798
12995
|
});
|
|
12799
12996
|
}
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
setImmediate(() => this.executeStep().catch((err) => {
|
|
12803
|
-
this.think(THOUGHT_TYPE.STUCK, `Error in next step: ${err}`);
|
|
12804
|
-
}));
|
|
12997
|
+
if (response.stop_reason === "tool_use" && toolUseBlocks.length > 0 && !this.shouldStopLoop()) {
|
|
12998
|
+
return await this.executeStep();
|
|
12805
12999
|
}
|
|
12806
13000
|
return textResponse;
|
|
12807
13001
|
}
|
|
@@ -12809,7 +13003,7 @@ Leverage shared memory findings from other agents.
|
|
|
12809
13003
|
extractIntelligence(toolName, result) {
|
|
12810
13004
|
if (!result.success) return;
|
|
12811
13005
|
const output = result.output;
|
|
12812
|
-
if (toolName ===
|
|
13006
|
+
if (toolName === TOOL_NAME.NMAP_SCAN || toolName === TOOL_NAME.BASH) {
|
|
12813
13007
|
const portMatches = output.match(/(\d+)\/(tcp|udp)\s+open\s+(\S+)/gi);
|
|
12814
13008
|
if (portMatches) {
|
|
12815
13009
|
portMatches.forEach((match) => {
|
|
@@ -12838,10 +13032,41 @@ Leverage shared memory findings from other agents.
|
|
|
12838
13032
|
}
|
|
12839
13033
|
});
|
|
12840
13034
|
}
|
|
13035
|
+
if (portMatches && this.currentAttackPlan) {
|
|
13036
|
+
const currentServices = this.state.target.services.map((s) => ({
|
|
13037
|
+
host: s.host,
|
|
13038
|
+
port: s.port,
|
|
13039
|
+
protocol: s.protocol,
|
|
13040
|
+
service: s.service,
|
|
13041
|
+
version: s.version
|
|
13042
|
+
}));
|
|
13043
|
+
const findingTitles = this.state.findings.map((f) => f.title);
|
|
13044
|
+
this.currentAttackPlan = this.attackPlanner.adaptPlan(
|
|
13045
|
+
this.currentAttackPlan,
|
|
13046
|
+
currentServices,
|
|
13047
|
+
findingTitles
|
|
13048
|
+
);
|
|
13049
|
+
if (this.currentAttackPlan.adaptations.length > 0) {
|
|
13050
|
+
const latest = this.currentAttackPlan.adaptations[this.currentAttackPlan.adaptations.length - 1];
|
|
13051
|
+
this.think(THOUGHT_TYPE.PLANNING, `[planner] Plan adapted: ${latest}`);
|
|
13052
|
+
}
|
|
13053
|
+
}
|
|
12841
13054
|
}
|
|
12842
|
-
if (output.includes("meterpreter") || output.includes("
|
|
13055
|
+
if (output.includes("meterpreter") || output.includes("www-data") || output.includes("uid=")) {
|
|
12843
13056
|
this.addCompromisedHost(this.state.target.primary);
|
|
12844
13057
|
}
|
|
13058
|
+
const credPatterns = [
|
|
13059
|
+
/(?:user|login|username)[:\s=]+([\w.@-]+)/gi,
|
|
13060
|
+
/(?:pass|password|pwd)[:\s=]+([\S]+)/gi
|
|
13061
|
+
];
|
|
13062
|
+
for (const pattern of credPatterns) {
|
|
13063
|
+
const matches = output.matchAll(pattern);
|
|
13064
|
+
for (const match of matches) {
|
|
13065
|
+
if (match[1] && match[1].length > 2 && match[1].length < 100) {
|
|
13066
|
+
this.stuckDetector.recordProgress("credential_found", `Potential cred: ${match[1].substring(0, 20)}`);
|
|
13067
|
+
}
|
|
13068
|
+
}
|
|
13069
|
+
}
|
|
12845
13070
|
}
|
|
12846
13071
|
// ===== LEARNING SYSTEM INTEGRATION =====
|
|
12847
13072
|
async analyzeFailureWithLearning() {
|
|
@@ -13021,13 +13246,19 @@ Alternatives: ${reflection.alternatives.map((a) => a.description).join(", ")}`;
|
|
|
13021
13246
|
this.state.stuckCounter = 0;
|
|
13022
13247
|
this.state.lastProgressTime = /* @__PURE__ */ new Date();
|
|
13023
13248
|
this.state.repeatedActions.clear();
|
|
13249
|
+
this.stuckDetector.recordProgress("phase_advanced", `Reset at phase ${this.state.currentPhase}`);
|
|
13024
13250
|
}
|
|
13025
|
-
trackAction(action) {
|
|
13251
|
+
trackAction(action, result) {
|
|
13026
13252
|
const count = this.state.repeatedActions.get(action) || 0;
|
|
13027
13253
|
this.state.repeatedActions.set(action, count + 1);
|
|
13028
13254
|
if (count + 1 >= this.STUCK_THRESHOLD) {
|
|
13029
13255
|
this.state.stuckCounter++;
|
|
13030
13256
|
}
|
|
13257
|
+
const [tool, ...targetParts] = action.split(":");
|
|
13258
|
+
const target = targetParts.join(":") || this.state.target.primary;
|
|
13259
|
+
const success = result ? result.success : true;
|
|
13260
|
+
const hasNewInfo = result ? result.output.length > 50 && success : false;
|
|
13261
|
+
this.stuckDetector.recordAction(tool, target, success, hasNewInfo);
|
|
13031
13262
|
}
|
|
13032
13263
|
// ===== DECISION HELPERS =====
|
|
13033
13264
|
shouldAdvancePhase() {
|
|
@@ -13063,6 +13294,11 @@ Alternatives: ${reflection.alternatives.map((a) => a.description).join(", ")}`;
|
|
|
13063
13294
|
const hasSuccess = successKeywords.some((kw) => response.toLowerCase().includes(kw));
|
|
13064
13295
|
if (hasSuccess) {
|
|
13065
13296
|
this.resetStuckCounter();
|
|
13297
|
+
if (response.toLowerCase().includes("shell") || response.toLowerCase().includes("root")) {
|
|
13298
|
+
this.stuckDetector.recordProgress("shell_obtained", "Shell access detected in response");
|
|
13299
|
+
} else if (response.toLowerCase().includes("discovered") || response.toLowerCase().includes("found")) {
|
|
13300
|
+
this.stuckDetector.recordProgress("service_discovered", "Discovery detected in response");
|
|
13301
|
+
}
|
|
13066
13302
|
}
|
|
13067
13303
|
}
|
|
13068
13304
|
async attemptRecovery(error) {
|
|
@@ -13122,6 +13358,13 @@ Alternatives: ${reflection.alternatives.map((a) => a.description).join(", ")}`;
|
|
|
13122
13358
|
recordProgress(type) {
|
|
13123
13359
|
this.resetStuckCounter();
|
|
13124
13360
|
this.state.lastProgressTime = /* @__PURE__ */ new Date();
|
|
13361
|
+
const progressTypeMap = {
|
|
13362
|
+
discovery: "service_discovered",
|
|
13363
|
+
credential: "credential_found",
|
|
13364
|
+
access: "shell_obtained",
|
|
13365
|
+
exploit: "vulnerability_found"
|
|
13366
|
+
};
|
|
13367
|
+
this.stuckDetector.recordProgress(progressTypeMap[type] || "service_discovered", type);
|
|
13125
13368
|
let message = "";
|
|
13126
13369
|
let importance = 60;
|
|
13127
13370
|
switch (type) {
|
|
@@ -13220,6 +13463,13 @@ ${this.state.target.compromised.map((h) => `- ${h}`).join("\n") || "None"}
|
|
|
13220
13463
|
pause() {
|
|
13221
13464
|
this.isPaused = true;
|
|
13222
13465
|
this.state.status = AGENT_STATUS.PAUSED;
|
|
13466
|
+
if (this.currentStream) {
|
|
13467
|
+
try {
|
|
13468
|
+
this.currentStream.abort();
|
|
13469
|
+
} catch (e) {
|
|
13470
|
+
}
|
|
13471
|
+
this.currentStream = null;
|
|
13472
|
+
}
|
|
13223
13473
|
this.emit(AGENT_EVENT.PAUSED);
|
|
13224
13474
|
}
|
|
13225
13475
|
resume() {
|
|
@@ -13234,6 +13484,13 @@ ${this.state.target.compromised.map((h) => `- ${h}`).join("\n") || "None"}
|
|
|
13234
13484
|
this.isPaused = true;
|
|
13235
13485
|
this.isAborted = true;
|
|
13236
13486
|
this.state.status = AGENT_STATUS.IDLE;
|
|
13487
|
+
if (this.currentStream) {
|
|
13488
|
+
try {
|
|
13489
|
+
this.currentStream.abort();
|
|
13490
|
+
} catch (e) {
|
|
13491
|
+
}
|
|
13492
|
+
this.currentStream = null;
|
|
13493
|
+
}
|
|
13237
13494
|
this.emit(AGENT_EVENT.PAUSED);
|
|
13238
13495
|
}
|
|
13239
13496
|
reset() {
|
|
@@ -13243,25 +13500,6 @@ ${this.state.target.compromised.map((h) => `- ${h}`).join("\n") || "None"}
|
|
|
13243
13500
|
this.sharedMemory.clear();
|
|
13244
13501
|
this.emit(AGENT_EVENT.RESET);
|
|
13245
13502
|
}
|
|
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
13503
|
// ===== PUBLIC ACCESSORS =====
|
|
13266
13504
|
getSystemPrompt() {
|
|
13267
13505
|
return AUTONOMOUS_HACKING_PROMPT;
|
|
@@ -13333,7 +13571,42 @@ ${this.state.target.compromised.map((h) => `- ${h}`).join("\n") || "None"}
|
|
|
13333
13571
|
} else if (block.type === "tool_use") {
|
|
13334
13572
|
hasToolCalls = true;
|
|
13335
13573
|
this.emit(AGENT_EVENT.TOOL_CALL, { name: block.name, input: block.input });
|
|
13336
|
-
const
|
|
13574
|
+
const toolInput = block.input;
|
|
13575
|
+
if (this.approvalManager.requiresApproval(block.name, toolInput)) {
|
|
13576
|
+
const risk = assessRisk(block.name, toolInput);
|
|
13577
|
+
this.emit(AGENT_EVENT.APPROVAL_NEEDED, {
|
|
13578
|
+
id: block.id,
|
|
13579
|
+
toolName: block.name,
|
|
13580
|
+
toolInput,
|
|
13581
|
+
riskLevel: risk
|
|
13582
|
+
});
|
|
13583
|
+
const decision = await new Promise((resolve) => {
|
|
13584
|
+
const handler = (resp) => {
|
|
13585
|
+
if (resp.requestId === block.id) {
|
|
13586
|
+
this.approvalManager.removeListener("approval_response", handler);
|
|
13587
|
+
resolve(resp.decision);
|
|
13588
|
+
}
|
|
13589
|
+
};
|
|
13590
|
+
this.approvalManager.on("approval_response", handler);
|
|
13591
|
+
});
|
|
13592
|
+
if (decision === "deny") {
|
|
13593
|
+
this.state.history.push({
|
|
13594
|
+
role: "assistant",
|
|
13595
|
+
content: response.content
|
|
13596
|
+
});
|
|
13597
|
+
this.state.history.push({
|
|
13598
|
+
role: "user",
|
|
13599
|
+
content: [{
|
|
13600
|
+
type: "tool_result",
|
|
13601
|
+
tool_use_id: block.id,
|
|
13602
|
+
content: "Tool execution denied by user",
|
|
13603
|
+
is_error: true
|
|
13604
|
+
}]
|
|
13605
|
+
});
|
|
13606
|
+
continue;
|
|
13607
|
+
}
|
|
13608
|
+
}
|
|
13609
|
+
const result = await executeToolCall(block.name, toolInput);
|
|
13337
13610
|
this.emit(AGENT_EVENT.TOOL_RESULT, { name: block.name, result });
|
|
13338
13611
|
this.state.history.push({
|
|
13339
13612
|
role: "assistant",
|
|
@@ -13567,8 +13840,8 @@ Respond helpfully. If asked to perform security testing, use appropriate tools.`
|
|
|
13567
13840
|
this.resourceManager.stopMonitoring();
|
|
13568
13841
|
await this.resourceManager.performCleanup();
|
|
13569
13842
|
this.resourceManager.dispose();
|
|
13570
|
-
this.removeAllListeners();
|
|
13571
13843
|
this.emit("shutdown");
|
|
13844
|
+
this.removeAllListeners();
|
|
13572
13845
|
}
|
|
13573
13846
|
// ===== UTILITY: Execute Tool with Audit =====
|
|
13574
13847
|
async executeToolWithAudit(toolName, toolInput) {
|
|
@@ -13610,16 +13883,16 @@ Respond helpfully. If asked to perform security testing, use appropriate tools.`
|
|
|
13610
13883
|
};
|
|
13611
13884
|
|
|
13612
13885
|
// src/core/session/session-manager.ts
|
|
13613
|
-
import * as
|
|
13886
|
+
import * as fs7 from "fs/promises";
|
|
13614
13887
|
import * as path5 from "path";
|
|
13615
|
-
import { EventEmitter as
|
|
13888
|
+
import { EventEmitter as EventEmitter18 } from "events";
|
|
13616
13889
|
var SESSIONS_DIR = ".pentesting/sessions";
|
|
13617
13890
|
function generateSessionId() {
|
|
13618
13891
|
const timestamp = Date.now().toString(36);
|
|
13619
13892
|
const random = Math.random().toString(36).substring(2, 8);
|
|
13620
13893
|
return `session_${timestamp}_${random}`;
|
|
13621
13894
|
}
|
|
13622
|
-
var SessionManager2 = class extends
|
|
13895
|
+
var SessionManager2 = class extends EventEmitter18 {
|
|
13623
13896
|
sessionsDir;
|
|
13624
13897
|
currentSession = null;
|
|
13625
13898
|
constructor(baseDir) {
|
|
@@ -13631,7 +13904,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13631
13904
|
*/
|
|
13632
13905
|
async initialize() {
|
|
13633
13906
|
try {
|
|
13634
|
-
await
|
|
13907
|
+
await fs7.mkdir(this.sessionsDir, { recursive: true });
|
|
13635
13908
|
} catch {
|
|
13636
13909
|
}
|
|
13637
13910
|
}
|
|
@@ -13652,7 +13925,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13652
13925
|
};
|
|
13653
13926
|
this.currentSession = metadata;
|
|
13654
13927
|
const sessionDir = path5.join(this.sessionsDir, metadata.id);
|
|
13655
|
-
await
|
|
13928
|
+
await fs7.mkdir(sessionDir, { recursive: true });
|
|
13656
13929
|
await this.saveMetadata(metadata);
|
|
13657
13930
|
this.emit("session_created", metadata);
|
|
13658
13931
|
return metadata;
|
|
@@ -13662,7 +13935,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13662
13935
|
*/
|
|
13663
13936
|
async saveMetadata(metadata) {
|
|
13664
13937
|
const metadataPath = path5.join(this.sessionsDir, metadata.id, "metadata.json");
|
|
13665
|
-
await
|
|
13938
|
+
await fs7.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
13666
13939
|
}
|
|
13667
13940
|
/**
|
|
13668
13941
|
* Save full session snapshot
|
|
@@ -13676,16 +13949,16 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13676
13949
|
this.currentSession.currentPhase = snapshot.state.currentPhase;
|
|
13677
13950
|
await this.saveMetadata(this.currentSession);
|
|
13678
13951
|
const statePath = path5.join(sessionDir, "state.json");
|
|
13679
|
-
await
|
|
13952
|
+
await fs7.writeFile(statePath, JSON.stringify(snapshot.state, null, 2));
|
|
13680
13953
|
const configPath = path5.join(sessionDir, "config.json");
|
|
13681
|
-
await
|
|
13954
|
+
await fs7.writeFile(configPath, JSON.stringify(snapshot.config, null, 2));
|
|
13682
13955
|
const historyPath = path5.join(sessionDir, "history.jsonl");
|
|
13683
13956
|
const historyLine = JSON.stringify({
|
|
13684
13957
|
timestamp: this.currentSession.updatedAt,
|
|
13685
13958
|
iteration: snapshot.state.iteration,
|
|
13686
13959
|
phase: snapshot.state.currentPhase
|
|
13687
13960
|
}) + "\n";
|
|
13688
|
-
await
|
|
13961
|
+
await fs7.appendFile(historyPath, historyLine);
|
|
13689
13962
|
this.emit("snapshot_saved", this.currentSession.id);
|
|
13690
13963
|
}
|
|
13691
13964
|
/**
|
|
@@ -13695,15 +13968,15 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13695
13968
|
try {
|
|
13696
13969
|
const sessionDir = path5.join(this.sessionsDir, sessionId);
|
|
13697
13970
|
const metadataPath = path5.join(sessionDir, "metadata.json");
|
|
13698
|
-
const metadataContent = await
|
|
13971
|
+
const metadataContent = await fs7.readFile(metadataPath, "utf-8");
|
|
13699
13972
|
const metadata = JSON.parse(metadataContent);
|
|
13700
13973
|
const statePath = path5.join(sessionDir, "state.json");
|
|
13701
|
-
const stateContent = await
|
|
13974
|
+
const stateContent = await fs7.readFile(statePath, "utf-8");
|
|
13702
13975
|
const state = JSON.parse(stateContent);
|
|
13703
13976
|
const configPath = path5.join(sessionDir, "config.json");
|
|
13704
13977
|
let config = {};
|
|
13705
13978
|
try {
|
|
13706
|
-
const configContent = await
|
|
13979
|
+
const configContent = await fs7.readFile(configPath, "utf-8");
|
|
13707
13980
|
config = JSON.parse(configContent);
|
|
13708
13981
|
} catch {
|
|
13709
13982
|
}
|
|
@@ -13720,13 +13993,13 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13720
13993
|
async listSessions() {
|
|
13721
13994
|
await this.initialize();
|
|
13722
13995
|
try {
|
|
13723
|
-
const entries = await
|
|
13996
|
+
const entries = await fs7.readdir(this.sessionsDir, { withFileTypes: true });
|
|
13724
13997
|
const sessions = [];
|
|
13725
13998
|
for (const entry of entries) {
|
|
13726
13999
|
if (entry.isDirectory()) {
|
|
13727
14000
|
try {
|
|
13728
14001
|
const metadataPath = path5.join(this.sessionsDir, entry.name, "metadata.json");
|
|
13729
|
-
const content = await
|
|
14002
|
+
const content = await fs7.readFile(metadataPath, "utf-8");
|
|
13730
14003
|
const metadata = JSON.parse(content);
|
|
13731
14004
|
sessions.push({
|
|
13732
14005
|
id: metadata.id,
|
|
@@ -13776,14 +14049,14 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13776
14049
|
await this.initialize();
|
|
13777
14050
|
let deletedCount = 0;
|
|
13778
14051
|
try {
|
|
13779
|
-
const entries = await
|
|
14052
|
+
const entries = await fs7.readdir(this.sessionsDir, { withFileTypes: true });
|
|
13780
14053
|
for (const entry of entries) {
|
|
13781
14054
|
if (entry.isDirectory()) {
|
|
13782
14055
|
const metadataPath = path5.join(this.sessionsDir, entry.name, "metadata.json");
|
|
13783
14056
|
try {
|
|
13784
|
-
await
|
|
14057
|
+
await fs7.access(metadataPath);
|
|
13785
14058
|
} catch {
|
|
13786
|
-
await
|
|
14059
|
+
await fs7.rm(path5.join(this.sessionsDir, entry.name), { recursive: true });
|
|
13787
14060
|
deletedCount++;
|
|
13788
14061
|
}
|
|
13789
14062
|
}
|
|
@@ -13806,7 +14079,7 @@ var SessionManager2 = class extends EventEmitter19 {
|
|
|
13806
14079
|
async deleteSession(sessionId) {
|
|
13807
14080
|
try {
|
|
13808
14081
|
const sessionDir = path5.join(this.sessionsDir, sessionId);
|
|
13809
|
-
await
|
|
14082
|
+
await fs7.rm(sessionDir, { recursive: true });
|
|
13810
14083
|
this.emit("session_deleted", sessionId);
|
|
13811
14084
|
return true;
|
|
13812
14085
|
} catch {
|
|
@@ -13938,7 +14211,7 @@ function getSlashCommandRegistry() {
|
|
|
13938
14211
|
// src/core/context/context-manager.ts
|
|
13939
14212
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs";
|
|
13940
14213
|
import { join as join6, dirname as dirname2 } from "path";
|
|
13941
|
-
import { EventEmitter as
|
|
14214
|
+
import { EventEmitter as EventEmitter19 } from "events";
|
|
13942
14215
|
var CONTEXT_EVENT = {
|
|
13943
14216
|
MESSAGE_ADDED: "message_added",
|
|
13944
14217
|
CHECKPOINT_CREATED: "checkpoint_created",
|
|
@@ -13946,7 +14219,7 @@ var CONTEXT_EVENT = {
|
|
|
13946
14219
|
CLEARED: "cleared",
|
|
13947
14220
|
COMPACTED: "compacted"
|
|
13948
14221
|
};
|
|
13949
|
-
var ContextManager2 = class extends
|
|
14222
|
+
var ContextManager2 = class extends EventEmitter19 {
|
|
13950
14223
|
filePath;
|
|
13951
14224
|
state;
|
|
13952
14225
|
maxMessages;
|
|
@@ -14728,7 +15001,7 @@ import { homedir as homedir2 } from "os";
|
|
|
14728
15001
|
import { join as join9 } from "path";
|
|
14729
15002
|
|
|
14730
15003
|
// src/utils/input-queue.ts
|
|
14731
|
-
import { EventEmitter as
|
|
15004
|
+
import { EventEmitter as EventEmitter20 } from "events";
|
|
14732
15005
|
var INPUT_QUEUE_EVENT = {
|
|
14733
15006
|
QUEUED: "queued",
|
|
14734
15007
|
// Message added to queue
|
|
@@ -14741,7 +15014,7 @@ var INPUT_QUEUE_EVENT = {
|
|
|
14741
15014
|
SHUTDOWN: "shutdown"
|
|
14742
15015
|
// Queue shutdown
|
|
14743
15016
|
};
|
|
14744
|
-
var InputQueue = class extends
|
|
15017
|
+
var InputQueue = class extends EventEmitter20 {
|
|
14745
15018
|
queue = [];
|
|
14746
15019
|
isShutdown = false;
|
|
14747
15020
|
isPaused = false;
|
|
@@ -14888,9 +15161,10 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14888
15161
|
const [mode, setMode] = useState("agent");
|
|
14889
15162
|
const [checkpointCount, setCheckpointCount] = useState(0);
|
|
14890
15163
|
const [pendingTargetConfirmation, setPendingTargetConfirmation] = useState(null);
|
|
14891
|
-
const [spinnerHue, setSpinnerHue] = useState(0);
|
|
14892
15164
|
const [queuedCount, setQueuedCount] = useState(0);
|
|
14893
15165
|
const [, forceUpdate] = useState(0);
|
|
15166
|
+
const [ctrlCCount, setCtrlCCount] = useState(0);
|
|
15167
|
+
const ctrlCTimerRef = useRef(null);
|
|
14894
15168
|
const updateProcessing = useCallback((val) => {
|
|
14895
15169
|
isProcessingRef.current = val;
|
|
14896
15170
|
setIsProcessing(val);
|
|
@@ -14932,7 +15206,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14932
15206
|
setCheckpointCount(contextManagerRef.current?.getCheckpoints().length || 0);
|
|
14933
15207
|
}
|
|
14934
15208
|
});
|
|
14935
|
-
import("./auto-update-
|
|
15209
|
+
import("./auto-update-6CLBRLE3.js").then(({ checkForUpdateAsync, formatUpdateNotification }) => {
|
|
14936
15210
|
checkForUpdateAsync().then((result) => {
|
|
14937
15211
|
if (result.hasUpdate) {
|
|
14938
15212
|
const notification = formatUpdateNotification(result);
|
|
@@ -14958,6 +15232,20 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14958
15232
|
}
|
|
14959
15233
|
});
|
|
14960
15234
|
}, [sessionManager2]);
|
|
15235
|
+
useEffect(() => {
|
|
15236
|
+
const queue = getInputQueue();
|
|
15237
|
+
const syncQueueState = () => {
|
|
15238
|
+
setQueuedCount(queue.length);
|
|
15239
|
+
};
|
|
15240
|
+
queue.on(INPUT_QUEUE_EVENT.QUEUED, syncQueueState);
|
|
15241
|
+
queue.on(INPUT_QUEUE_EVENT.DEQUEUED, syncQueueState);
|
|
15242
|
+
queue.on(INPUT_QUEUE_EVENT.CLEARED, syncQueueState);
|
|
15243
|
+
return () => {
|
|
15244
|
+
queue.off(INPUT_QUEUE_EVENT.QUEUED, syncQueueState);
|
|
15245
|
+
queue.off(INPUT_QUEUE_EVENT.DEQUEUED, syncQueueState);
|
|
15246
|
+
queue.off(INPUT_QUEUE_EVENT.CLEARED, syncQueueState);
|
|
15247
|
+
};
|
|
15248
|
+
}, []);
|
|
14961
15249
|
const startTimeRef = useRef(0);
|
|
14962
15250
|
const timerRef = useRef(null);
|
|
14963
15251
|
const addMessage = useCallback((type, content, extraOrDuration) => {
|
|
@@ -14980,9 +15268,8 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
14980
15268
|
const startTimer = useCallback(() => {
|
|
14981
15269
|
startTimeRef.current = Date.now();
|
|
14982
15270
|
timerRef.current = setInterval(() => {
|
|
14983
|
-
setElapsedTime(Math.floor((Date.now() - startTimeRef.current) /
|
|
14984
|
-
|
|
14985
|
-
}, 100);
|
|
15271
|
+
setElapsedTime(Math.floor((Date.now() - startTimeRef.current) / 1e3));
|
|
15272
|
+
}, 1e3);
|
|
14986
15273
|
}, []);
|
|
14987
15274
|
const stopTimer = useCallback(() => {
|
|
14988
15275
|
if (timerRef.current) {
|
|
@@ -15159,10 +15446,24 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
15159
15446
|
}, [agent, target, addMessage, stopTimer, autoApprove, approvalManager2]);
|
|
15160
15447
|
const handleExit = useCallback(async () => {
|
|
15161
15448
|
setCurrentStatus("Exiting...");
|
|
15162
|
-
|
|
15163
|
-
|
|
15449
|
+
try {
|
|
15450
|
+
if (timerRef.current) {
|
|
15451
|
+
clearInterval(timerRef.current);
|
|
15452
|
+
timerRef.current = null;
|
|
15453
|
+
}
|
|
15454
|
+
const queue = getInputQueue();
|
|
15455
|
+
queue.shutdown();
|
|
15456
|
+
await Promise.race([
|
|
15457
|
+
agent.shutdown(),
|
|
15458
|
+
new Promise((resolve) => setTimeout(resolve, 3e3))
|
|
15459
|
+
]);
|
|
15460
|
+
const rm2 = getResourceManager();
|
|
15461
|
+
await rm2.performCleanup();
|
|
15462
|
+
exit();
|
|
15463
|
+
} catch (err) {
|
|
15464
|
+
console.error("Shutdown error:", err);
|
|
15164
15465
|
exit();
|
|
15165
|
-
}
|
|
15466
|
+
}
|
|
15166
15467
|
}, [agent, exit]);
|
|
15167
15468
|
useEffect(() => {
|
|
15168
15469
|
const onExit = () => {
|
|
@@ -15228,10 +15529,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
15228
15529
|
}
|
|
15229
15530
|
}
|
|
15230
15531
|
if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "/q") {
|
|
15231
|
-
|
|
15232
|
-
agent.pause();
|
|
15233
|
-
}
|
|
15234
|
-
exit();
|
|
15532
|
+
await handleExit();
|
|
15235
15533
|
return;
|
|
15236
15534
|
}
|
|
15237
15535
|
if (!isProcessing && !trimmed.startsWith("/") && !pendingTargetConfirmation && !pendingApproval) {
|
|
@@ -15526,7 +15824,7 @@ ${list}`);
|
|
|
15526
15824
|
case CLI_COMMAND.EXIT:
|
|
15527
15825
|
case "quit":
|
|
15528
15826
|
case "q":
|
|
15529
|
-
|
|
15827
|
+
await handleExit();
|
|
15530
15828
|
return;
|
|
15531
15829
|
case "approve":
|
|
15532
15830
|
case "y":
|
|
@@ -15703,7 +16001,7 @@ ${list}`);
|
|
|
15703
16001
|
return;
|
|
15704
16002
|
case "update":
|
|
15705
16003
|
try {
|
|
15706
|
-
const { checkForUpdate, formatUpdateNotification, doUpdate } = await import("./update-
|
|
16004
|
+
const { checkForUpdate, formatUpdateNotification, doUpdate } = await import("./update-34NDFWS3.js");
|
|
15707
16005
|
const result = checkForUpdate(true);
|
|
15708
16006
|
if (result.hasUpdate) {
|
|
15709
16007
|
const notification = formatUpdateNotification(result);
|
|
@@ -15761,7 +16059,7 @@ ${list}`);
|
|
|
15761
16059
|
}, (error, stdout2, stderr2) => {
|
|
15762
16060
|
if (child.pid) resourceManager.untrackProcess(child.pid);
|
|
15763
16061
|
if (error) reject({ ...error, stdout: stdout2, stderr: stderr2 });
|
|
15764
|
-
else resolve({ stdout: stdout2
|
|
16062
|
+
else resolve({ stdout: String(stdout2), stderr: String(stderr2) });
|
|
15765
16063
|
});
|
|
15766
16064
|
if (child.pid) resourceManager.trackProcess(child.pid);
|
|
15767
16065
|
});
|
|
@@ -15821,29 +16119,44 @@ ${list}`);
|
|
|
15821
16119
|
}
|
|
15822
16120
|
if (key.ctrl && input2 === "c") {
|
|
15823
16121
|
if (isProcessing) {
|
|
15824
|
-
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
|
|
16122
|
+
if (ctrlCCount === 0) {
|
|
16123
|
+
setCtrlCCount(1);
|
|
16124
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "\u26A0\uFE0F Really stop? Press Ctrl+C again within 10 seconds to confirm.");
|
|
16125
|
+
if (ctrlCTimerRef.current) clearTimeout(ctrlCTimerRef.current);
|
|
16126
|
+
ctrlCTimerRef.current = setTimeout(() => {
|
|
16127
|
+
setCtrlCCount(0);
|
|
16128
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "Continuing operation...");
|
|
16129
|
+
}, 1e4);
|
|
16130
|
+
} else {
|
|
16131
|
+
if (ctrlCTimerRef.current) clearTimeout(ctrlCTimerRef.current);
|
|
16132
|
+
setCtrlCCount(0);
|
|
16133
|
+
agent.pause();
|
|
16134
|
+
stopTimer();
|
|
16135
|
+
updateProcessing(false);
|
|
16136
|
+
setCurrentStatus("");
|
|
16137
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "\u23F9 Interrupted by user.");
|
|
16138
|
+
}
|
|
15829
16139
|
} else {
|
|
15830
16140
|
exit();
|
|
15831
16141
|
}
|
|
16142
|
+
return;
|
|
15832
16143
|
}
|
|
15833
|
-
if (key.escape) {
|
|
16144
|
+
if (key.escape || input2 === "\x1B") {
|
|
15834
16145
|
if (isProcessing) {
|
|
15835
16146
|
agent.pause();
|
|
15836
16147
|
stopTimer();
|
|
15837
16148
|
updateProcessing(false);
|
|
15838
16149
|
setCurrentStatus("");
|
|
15839
16150
|
updatePendingApproval(null);
|
|
15840
|
-
addMessage(MESSAGE_TYPE.SYSTEM, "
|
|
16151
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "\u23F9 Process halted via ESC. Ready for next command.");
|
|
15841
16152
|
}
|
|
16153
|
+
return;
|
|
15842
16154
|
}
|
|
15843
16155
|
if (key.tab) {
|
|
15844
16156
|
const newMode = mode === "agent" ? "shell" : "agent";
|
|
15845
16157
|
setMode(newMode);
|
|
15846
16158
|
addMessage(MESSAGE_TYPE.SYSTEM, `Mode: ${newMode === "agent" ? "Agent" : "Shell"}`);
|
|
16159
|
+
return;
|
|
15847
16160
|
}
|
|
15848
16161
|
});
|
|
15849
16162
|
const getStyle = (type) => {
|
|
@@ -15931,10 +16244,25 @@ ${list}`);
|
|
|
15931
16244
|
}
|
|
15932
16245
|
)
|
|
15933
16246
|
] }),
|
|
15934
|
-
isProcessing && /* @__PURE__ */
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
16247
|
+
isProcessing && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
|
|
16248
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
16249
|
+
"ESC to interrupt \u2502 ",
|
|
16250
|
+
queuedCount > 0 ? `\u{1F4E5} ${queuedCount} messages queued` : "Chatting enabled during execution"
|
|
16251
|
+
] }),
|
|
16252
|
+
queuedCount > 0 && (() => {
|
|
16253
|
+
try {
|
|
16254
|
+
const pending = getInputQueue().getPending().slice(0, 3);
|
|
16255
|
+
return pending.map((item, i) => /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
16256
|
+
" ",
|
|
16257
|
+
i + 1,
|
|
16258
|
+
". ",
|
|
16259
|
+
String(item.content || item).slice(0, 60)
|
|
16260
|
+
] }, i));
|
|
16261
|
+
} catch {
|
|
16262
|
+
return null;
|
|
16263
|
+
}
|
|
16264
|
+
})()
|
|
16265
|
+
] })
|
|
15938
16266
|
] }),
|
|
15939
16267
|
/* @__PURE__ */ jsx3(
|
|
15940
16268
|
status_bar_default,
|
|
@@ -16022,8 +16350,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
16022
16350
|
const summary = agent.getSummary();
|
|
16023
16351
|
console.log(JSON.stringify(summary, null, 2));
|
|
16024
16352
|
if (options.output) {
|
|
16025
|
-
const
|
|
16026
|
-
await
|
|
16353
|
+
const fs8 = await import("fs/promises");
|
|
16354
|
+
await fs8.writeFile(options.output, JSON.stringify(summary, null, 2));
|
|
16027
16355
|
console.log(chalk.hex(THEME.text.accent)(`
|
|
16028
16356
|
[+] Report saved to: ${options.output}`));
|
|
16029
16357
|
}
|