pentesting 0.40.7 → 0.43.0
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/dist/main.js +1164 -324
- package/dist/prompts/recon.md +130 -0
- package/dist/prompts/strategist-system.md +39 -0
- package/dist/prompts/strategy.md +15 -1
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -13,8 +13,8 @@ import chalk from "chalk";
|
|
|
13
13
|
import gradient from "gradient-string";
|
|
14
14
|
|
|
15
15
|
// src/platform/tui/app.tsx
|
|
16
|
-
import { useState as useState4, useCallback as
|
|
17
|
-
import { Box as Box6, useInput, useApp } from "ink";
|
|
16
|
+
import { useState as useState4, useCallback as useCallback4, useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
17
|
+
import { Box as Box6, useInput as useInput2, useApp } from "ink";
|
|
18
18
|
|
|
19
19
|
// src/platform/tui/hooks/useAgent.ts
|
|
20
20
|
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
@@ -199,6 +199,14 @@ var EXIT_CODES = {
|
|
|
199
199
|
/** Process killed by SIGKILL */
|
|
200
200
|
SIGKILL: 137
|
|
201
201
|
};
|
|
202
|
+
var PROCESS_ACTIONS = {
|
|
203
|
+
LIST: "list",
|
|
204
|
+
STATUS: "status",
|
|
205
|
+
INTERACT: "interact",
|
|
206
|
+
PROMOTE: "promote",
|
|
207
|
+
STOP: "stop",
|
|
208
|
+
STOP_ALL: "stop_all"
|
|
209
|
+
};
|
|
202
210
|
var PROCESS_ROLES = {
|
|
203
211
|
LISTENER: "listener",
|
|
204
212
|
ACTIVE_SHELL: "active_shell",
|
|
@@ -306,7 +314,7 @@ var ORPHAN_PROCESS_NAMES = [
|
|
|
306
314
|
|
|
307
315
|
// src/shared/constants/agent.ts
|
|
308
316
|
var APP_NAME = "Pentest AI";
|
|
309
|
-
var APP_VERSION = "0.
|
|
317
|
+
var APP_VERSION = "0.43.0";
|
|
310
318
|
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
311
319
|
var LLM_ROLES = {
|
|
312
320
|
SYSTEM: "system",
|
|
@@ -565,6 +573,11 @@ var UI_COMMANDS = {
|
|
|
565
573
|
LOGS: "logs",
|
|
566
574
|
LOGS_SHORT: "l",
|
|
567
575
|
CTF: "ctf",
|
|
576
|
+
AUTO: "auto",
|
|
577
|
+
GRAPH: "graph",
|
|
578
|
+
GRAPH_SHORT: "g",
|
|
579
|
+
PATHS: "paths",
|
|
580
|
+
PATHS_SHORT: "p",
|
|
568
581
|
EXIT: "exit",
|
|
569
582
|
QUIT: "quit",
|
|
570
583
|
EXIT_SHORT: "q"
|
|
@@ -958,6 +971,18 @@ function debugLog(category, message, data) {
|
|
|
958
971
|
}
|
|
959
972
|
|
|
960
973
|
// src/shared/utils/command-validator.ts
|
|
974
|
+
var SHELL_OPERATORS = {
|
|
975
|
+
AND: "&&",
|
|
976
|
+
OR: "||",
|
|
977
|
+
SEQUENCE: ";",
|
|
978
|
+
PIPE: "|",
|
|
979
|
+
BACKGROUND: "&"
|
|
980
|
+
};
|
|
981
|
+
var SHELL_CHARS = {
|
|
982
|
+
SINGLE_QUOTE: "'",
|
|
983
|
+
DOUBLE_QUOTE: '"',
|
|
984
|
+
BACKTICK: "`"
|
|
985
|
+
};
|
|
961
986
|
function validateCommand(command) {
|
|
962
987
|
if (!command || typeof command !== "string") {
|
|
963
988
|
return { safe: false, error: "Empty or invalid command" };
|
|
@@ -997,13 +1022,13 @@ function splitChainedCommands(command) {
|
|
|
997
1022
|
let i = 0;
|
|
998
1023
|
while (i < command.length) {
|
|
999
1024
|
const ch = command[i];
|
|
1000
|
-
if (ch ===
|
|
1025
|
+
if (ch === SHELL_CHARS.SINGLE_QUOTE && !inDouble) {
|
|
1001
1026
|
inSingle = !inSingle;
|
|
1002
1027
|
current += ch;
|
|
1003
1028
|
i++;
|
|
1004
1029
|
continue;
|
|
1005
1030
|
}
|
|
1006
|
-
if (ch ===
|
|
1031
|
+
if (ch === SHELL_CHARS.DOUBLE_QUOTE && !inSingle) {
|
|
1007
1032
|
inDouble = !inDouble;
|
|
1008
1033
|
current += ch;
|
|
1009
1034
|
i++;
|
|
@@ -1016,7 +1041,7 @@ function splitChainedCommands(command) {
|
|
|
1016
1041
|
i += 2;
|
|
1017
1042
|
continue;
|
|
1018
1043
|
}
|
|
1019
|
-
if (ch ===
|
|
1044
|
+
if (ch === SHELL_OPERATORS.SEQUENCE) {
|
|
1020
1045
|
if (current.trim()) parts.push(current.trim());
|
|
1021
1046
|
current = "";
|
|
1022
1047
|
i++;
|
|
@@ -1342,6 +1367,12 @@ var globalInputHandler = null;
|
|
|
1342
1367
|
function setInputHandler(handler) {
|
|
1343
1368
|
globalInputHandler = handler;
|
|
1344
1369
|
}
|
|
1370
|
+
function clearInputHandler() {
|
|
1371
|
+
globalInputHandler = null;
|
|
1372
|
+
}
|
|
1373
|
+
function clearCommandEventEmitter() {
|
|
1374
|
+
globalEventEmitter = null;
|
|
1375
|
+
}
|
|
1345
1376
|
async function runCommand(command, args = [], options = {}) {
|
|
1346
1377
|
const fullCommand = args.length > 0 ? `${command} ${args.join(" ")}` : command;
|
|
1347
1378
|
const validation = validateCommand(fullCommand);
|
|
@@ -1935,7 +1966,10 @@ async function cleanupAllProcesses() {
|
|
|
1935
1966
|
if (cleanupDone) return;
|
|
1936
1967
|
cleanupDone = true;
|
|
1937
1968
|
const ids = Array.from(backgroundProcesses.keys());
|
|
1938
|
-
if (ids.length === 0)
|
|
1969
|
+
if (ids.length === 0) {
|
|
1970
|
+
cleanupDone = false;
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1939
1973
|
for (const [_id, proc] of backgroundProcesses) {
|
|
1940
1974
|
if (!proc.hasExited) {
|
|
1941
1975
|
proc.childPids = discoverAllDescendants(proc.pid);
|
|
@@ -1997,6 +2031,7 @@ async function cleanupAllProcesses() {
|
|
|
1997
2031
|
} catch {
|
|
1998
2032
|
}
|
|
1999
2033
|
}
|
|
2034
|
+
cleanupDone = false;
|
|
2000
2035
|
}
|
|
2001
2036
|
function getResourceSummary() {
|
|
2002
2037
|
const procs = listBackgroundProcesses();
|
|
@@ -2175,11 +2210,54 @@ var GRAPH_LIMITS = {
|
|
|
2175
2210
|
/** Maximum chains to show in prompt */
|
|
2176
2211
|
PROMPT_CHAINS: 5,
|
|
2177
2212
|
/** Maximum unexploited vulns to show in prompt */
|
|
2178
|
-
PROMPT_VULNS: 5
|
|
2213
|
+
PROMPT_VULNS: 5,
|
|
2214
|
+
/** Maximum failed paths to show in prompt */
|
|
2215
|
+
PROMPT_FAILED: 5,
|
|
2216
|
+
/** Maximum DFS depth for chain discovery */
|
|
2217
|
+
MAX_CHAIN_DEPTH: 6,
|
|
2218
|
+
/** Maximum nodes to display in ASCII graph */
|
|
2219
|
+
ASCII_MAX_NODES: 30
|
|
2220
|
+
};
|
|
2221
|
+
var NODE_TYPE = {
|
|
2222
|
+
HOST: "host",
|
|
2223
|
+
SERVICE: "service",
|
|
2224
|
+
CREDENTIAL: "credential",
|
|
2225
|
+
VULNERABILITY: "vulnerability",
|
|
2226
|
+
ACCESS: "access",
|
|
2227
|
+
LOOT: "loot",
|
|
2228
|
+
OSINT: "osint"
|
|
2229
|
+
};
|
|
2230
|
+
var NODE_STATUS = {
|
|
2231
|
+
DISCOVERED: "discovered",
|
|
2232
|
+
ATTEMPTED: "attempted",
|
|
2233
|
+
SUCCEEDED: "succeeded",
|
|
2234
|
+
FAILED: "failed"
|
|
2235
|
+
};
|
|
2236
|
+
var EDGE_STATUS = {
|
|
2237
|
+
UNTESTED: "untested",
|
|
2238
|
+
TESTING: "testing",
|
|
2239
|
+
SUCCEEDED: "succeeded",
|
|
2240
|
+
FAILED: "failed"
|
|
2241
|
+
};
|
|
2242
|
+
var SEVERITY = {
|
|
2243
|
+
CRITICAL: "critical",
|
|
2244
|
+
HIGH: "high",
|
|
2245
|
+
MEDIUM: "medium",
|
|
2246
|
+
LOW: "low",
|
|
2247
|
+
INFO: "info"
|
|
2179
2248
|
};
|
|
2249
|
+
var IMPACT_WEIGHT = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
2180
2250
|
var AttackGraph = class {
|
|
2181
2251
|
nodes = /* @__PURE__ */ new Map();
|
|
2182
2252
|
edges = [];
|
|
2253
|
+
failedPaths = [];
|
|
2254
|
+
// ─── Core Graph Operations ──────────────────────────────────
|
|
2255
|
+
/** Reset the graph to an empty state. */
|
|
2256
|
+
reset() {
|
|
2257
|
+
this.nodes.clear();
|
|
2258
|
+
this.edges = [];
|
|
2259
|
+
this.failedPaths = [];
|
|
2260
|
+
}
|
|
2183
2261
|
/**
|
|
2184
2262
|
* Add a node to the attack graph.
|
|
2185
2263
|
*/
|
|
@@ -2191,7 +2269,7 @@ var AttackGraph = class {
|
|
|
2191
2269
|
type,
|
|
2192
2270
|
label,
|
|
2193
2271
|
data,
|
|
2194
|
-
|
|
2272
|
+
status: NODE_STATUS.DISCOVERED,
|
|
2195
2273
|
discoveredAt: Date.now()
|
|
2196
2274
|
});
|
|
2197
2275
|
}
|
|
@@ -2203,34 +2281,106 @@ var AttackGraph = class {
|
|
|
2203
2281
|
addEdge(fromId, toId, relation, confidence = 0.5) {
|
|
2204
2282
|
const exists = this.edges.some((e) => e.from === fromId && e.to === toId && e.relation === relation);
|
|
2205
2283
|
if (!exists) {
|
|
2206
|
-
this.edges.push({ from: fromId, to: toId, relation, confidence });
|
|
2284
|
+
this.edges.push({ from: fromId, to: toId, relation, confidence, status: EDGE_STATUS.UNTESTED });
|
|
2207
2285
|
}
|
|
2208
2286
|
}
|
|
2209
2287
|
/**
|
|
2210
|
-
*
|
|
2288
|
+
* Get node by ID.
|
|
2211
2289
|
*/
|
|
2212
|
-
|
|
2290
|
+
getNode(nodeId) {
|
|
2291
|
+
return this.nodes.get(nodeId);
|
|
2292
|
+
}
|
|
2293
|
+
// ─── Status Management ──────────────────────────────────────
|
|
2294
|
+
/**
|
|
2295
|
+
* Mark a node as being attempted (in-progress attack).
|
|
2296
|
+
*/
|
|
2297
|
+
markAttempted(nodeId) {
|
|
2213
2298
|
const node = this.nodes.get(nodeId);
|
|
2214
|
-
if (node
|
|
2299
|
+
if (node && node.status === NODE_STATUS.DISCOVERED) {
|
|
2300
|
+
node.status = NODE_STATUS.ATTEMPTED;
|
|
2301
|
+
node.attemptedAt = Date.now();
|
|
2302
|
+
}
|
|
2215
2303
|
}
|
|
2216
2304
|
/**
|
|
2217
|
-
*
|
|
2305
|
+
* Mark a node as successfully exploited/achieved.
|
|
2218
2306
|
*/
|
|
2219
|
-
|
|
2220
|
-
|
|
2307
|
+
markSucceeded(nodeId) {
|
|
2308
|
+
const node = this.nodes.get(nodeId);
|
|
2309
|
+
if (node) {
|
|
2310
|
+
node.status = NODE_STATUS.SUCCEEDED;
|
|
2311
|
+
node.resolvedAt = Date.now();
|
|
2312
|
+
}
|
|
2313
|
+
for (const edge of this.edges) {
|
|
2314
|
+
if (edge.to === nodeId && edge.status === EDGE_STATUS.TESTING) {
|
|
2315
|
+
edge.status = EDGE_STATUS.SUCCEEDED;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Mark a node as failed (attack didn't work).
|
|
2321
|
+
*/
|
|
2322
|
+
markFailed(nodeId, reason) {
|
|
2323
|
+
const node = this.nodes.get(nodeId);
|
|
2324
|
+
if (node) {
|
|
2325
|
+
node.status = NODE_STATUS.FAILED;
|
|
2326
|
+
node.resolvedAt = Date.now();
|
|
2327
|
+
node.failReason = reason;
|
|
2328
|
+
this.failedPaths.push(`${node.label}${reason ? ` (${reason})` : ""}`);
|
|
2329
|
+
}
|
|
2330
|
+
for (const edge of this.edges) {
|
|
2331
|
+
if (edge.to === nodeId && (edge.status === EDGE_STATUS.TESTING || edge.status === EDGE_STATUS.UNTESTED)) {
|
|
2332
|
+
edge.status = EDGE_STATUS.FAILED;
|
|
2333
|
+
edge.failReason = reason;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Mark an edge as being tested.
|
|
2339
|
+
*/
|
|
2340
|
+
markEdgeTesting(fromId, toId) {
|
|
2341
|
+
for (const edge of this.edges) {
|
|
2342
|
+
if (edge.from === fromId && edge.to === toId) {
|
|
2343
|
+
edge.status = EDGE_STATUS.TESTING;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
/**
|
|
2348
|
+
* Mark an edge as failed.
|
|
2349
|
+
*/
|
|
2350
|
+
markEdgeFailed(fromId, toId, reason) {
|
|
2351
|
+
for (const edge of this.edges) {
|
|
2352
|
+
if (edge.from === fromId && edge.to === toId) {
|
|
2353
|
+
edge.status = EDGE_STATUS.FAILED;
|
|
2354
|
+
edge.failReason = reason;
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
this.failedPaths.push(`${fromId} \u2192 ${toId}${reason ? ` (${reason})` : ""}`);
|
|
2358
|
+
}
|
|
2359
|
+
// Backward-compatible alias
|
|
2360
|
+
markExploited(nodeId) {
|
|
2361
|
+
this.markSucceeded(nodeId);
|
|
2362
|
+
}
|
|
2363
|
+
// ─── Domain-Specific Registration ───────────────────────────
|
|
2364
|
+
/**
|
|
2365
|
+
* Record a host discovery.
|
|
2366
|
+
*/
|
|
2367
|
+
addHost(ip, hostname) {
|
|
2368
|
+
return this.addNode(NODE_TYPE.HOST, ip, { ip, hostname });
|
|
2221
2369
|
}
|
|
2222
2370
|
/**
|
|
2223
2371
|
* Record a service discovery and auto-create edges.
|
|
2224
2372
|
*/
|
|
2225
2373
|
addService(host, port, service, version) {
|
|
2226
|
-
const
|
|
2374
|
+
const hostId = this.addHost(host);
|
|
2375
|
+
const serviceId = this.addNode(NODE_TYPE.SERVICE, `${host}:${port}`, {
|
|
2227
2376
|
host,
|
|
2228
2377
|
port,
|
|
2229
2378
|
service,
|
|
2230
2379
|
version
|
|
2231
2380
|
});
|
|
2381
|
+
this.addEdge(hostId, serviceId, "has_service", 0.95);
|
|
2232
2382
|
if (version) {
|
|
2233
|
-
const vulnId = this.addNode(
|
|
2383
|
+
const vulnId = this.addNode(NODE_TYPE.VULNERABILITY, `CVE search: ${service} ${version}`, {
|
|
2234
2384
|
service,
|
|
2235
2385
|
version,
|
|
2236
2386
|
status: GRAPH_STATUS.NEEDS_SEARCH
|
|
@@ -2251,7 +2401,7 @@ var AttackGraph = class {
|
|
|
2251
2401
|
for (const [id, node] of this.nodes) {
|
|
2252
2402
|
if (node.type === "service") {
|
|
2253
2403
|
const svc = String(node.data.service || "");
|
|
2254
|
-
if (["ssh", "ftp", "rdp", "smb", "http", "mysql", "postgresql", "mssql", "winrm"].some((s) => svc.includes(s))) {
|
|
2404
|
+
if (["ssh", "ftp", "rdp", "smb", "http", "mysql", "postgresql", "mssql", "winrm", "vnc", "telnet"].some((s) => svc.includes(s))) {
|
|
2255
2405
|
this.addEdge(credId, id, "can_try_on", 0.6);
|
|
2256
2406
|
}
|
|
2257
2407
|
}
|
|
@@ -2290,7 +2440,7 @@ var AttackGraph = class {
|
|
|
2290
2440
|
level,
|
|
2291
2441
|
via
|
|
2292
2442
|
});
|
|
2293
|
-
this.
|
|
2443
|
+
this.markSucceeded(accessId);
|
|
2294
2444
|
if (["root", "admin", "SYSTEM", "Administrator"].includes(level)) {
|
|
2295
2445
|
const lootId = this.addNode("loot", `flags on ${host}`, {
|
|
2296
2446
|
host,
|
|
@@ -2301,104 +2451,302 @@ var AttackGraph = class {
|
|
|
2301
2451
|
return accessId;
|
|
2302
2452
|
}
|
|
2303
2453
|
/**
|
|
2304
|
-
*
|
|
2305
|
-
* Returns chains sorted by probability (highest first).
|
|
2454
|
+
* Record OSINT discovery (Docker image, GitHub repo, company info, etc.)
|
|
2306
2455
|
*/
|
|
2307
|
-
|
|
2308
|
-
const
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
});
|
|
2456
|
+
addOSINT(category, detail, data = {}) {
|
|
2457
|
+
const osintId = this.addNode("osint", `${category}: ${detail}`, {
|
|
2458
|
+
category,
|
|
2459
|
+
detail,
|
|
2460
|
+
...data
|
|
2461
|
+
});
|
|
2462
|
+
for (const [id, node] of this.nodes) {
|
|
2463
|
+
if (node.type === "host" || node.type === "service") {
|
|
2464
|
+
const hostIp = String(node.data.ip || node.data.host || "");
|
|
2465
|
+
const hostname = String(node.data.hostname || "");
|
|
2466
|
+
if (hostIp && detail.includes(hostIp) || hostname && detail.includes(hostname)) {
|
|
2467
|
+
this.addEdge(osintId, id, "relates_to", 0.5);
|
|
2320
2468
|
}
|
|
2321
2469
|
}
|
|
2322
2470
|
}
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2471
|
+
return osintId;
|
|
2472
|
+
}
|
|
2473
|
+
// ─── Chain Discovery (DFS) ──────────────────────────────────
|
|
2474
|
+
/**
|
|
2475
|
+
* Find all viable attack paths using DFS.
|
|
2476
|
+
* Only follows edges that are untested or succeeded.
|
|
2477
|
+
* Prioritizes by (probability × impact).
|
|
2478
|
+
*/
|
|
2479
|
+
recommendChains() {
|
|
2480
|
+
const chains = [];
|
|
2481
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2482
|
+
const goalTypes = [NODE_TYPE.ACCESS, NODE_TYPE.LOOT];
|
|
2483
|
+
const entryNodes = Array.from(this.nodes.values()).filter(
|
|
2484
|
+
(n) => n.type === NODE_TYPE.HOST || n.type === NODE_TYPE.SERVICE || n.type === NODE_TYPE.CREDENTIAL || n.type === NODE_TYPE.OSINT
|
|
2485
|
+
);
|
|
2486
|
+
for (const entry of entryNodes) {
|
|
2487
|
+
visited.clear();
|
|
2488
|
+
this.dfsChains(entry.id, [], [], visited, chains, goalTypes);
|
|
2336
2489
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2490
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2491
|
+
const unique = chains.filter((c) => {
|
|
2492
|
+
if (seen.has(c.description)) return false;
|
|
2493
|
+
seen.add(c.description);
|
|
2494
|
+
return true;
|
|
2495
|
+
});
|
|
2496
|
+
unique.sort(
|
|
2497
|
+
(a, b) => b.probability * (IMPACT_WEIGHT[b.impact] || 1) - a.probability * (IMPACT_WEIGHT[a.impact] || 1)
|
|
2498
|
+
);
|
|
2499
|
+
return unique.slice(0, GRAPH_LIMITS.MAX_CHAINS);
|
|
2500
|
+
}
|
|
2501
|
+
dfsChains(nodeId, path2, pathEdges, visited, results, goalTypes) {
|
|
2502
|
+
if (visited.has(nodeId)) return;
|
|
2503
|
+
if (path2.length >= GRAPH_LIMITS.MAX_CHAIN_DEPTH) return;
|
|
2504
|
+
const node = this.nodes.get(nodeId);
|
|
2505
|
+
if (!node) return;
|
|
2506
|
+
if (node.status === NODE_STATUS.FAILED) return;
|
|
2507
|
+
visited.add(nodeId);
|
|
2508
|
+
path2.push(node);
|
|
2509
|
+
if (goalTypes.includes(node.type) && node.status !== NODE_STATUS.SUCCEEDED && path2.length > 1) {
|
|
2510
|
+
const prob = pathEdges.reduce((acc, e) => acc * e.confidence, 1);
|
|
2511
|
+
const impact = this.estimateImpact(node);
|
|
2512
|
+
results.push({
|
|
2513
|
+
steps: [...path2],
|
|
2514
|
+
edges: [...pathEdges],
|
|
2515
|
+
description: path2.map((n) => n.label).join(" \u2192 "),
|
|
2516
|
+
probability: prob,
|
|
2517
|
+
impact,
|
|
2518
|
+
length: path2.length
|
|
2519
|
+
});
|
|
2350
2520
|
}
|
|
2351
|
-
const
|
|
2352
|
-
|
|
2353
|
-
(a, b) => b.probability * (impactWeight[b.impact] || 1) - a.probability * (impactWeight[a.impact] || 1)
|
|
2521
|
+
const outEdges = this.edges.filter(
|
|
2522
|
+
(e) => e.from === nodeId && e.status !== EDGE_STATUS.FAILED
|
|
2354
2523
|
);
|
|
2355
|
-
|
|
2524
|
+
for (const edge of outEdges) {
|
|
2525
|
+
this.dfsChains(edge.to, path2, [...pathEdges, edge], visited, results, goalTypes);
|
|
2526
|
+
}
|
|
2527
|
+
path2.pop();
|
|
2528
|
+
visited.delete(nodeId);
|
|
2529
|
+
}
|
|
2530
|
+
estimateImpact(node) {
|
|
2531
|
+
if (node.type === NODE_TYPE.LOOT) return SEVERITY.CRITICAL;
|
|
2532
|
+
if (node.type === NODE_TYPE.ACCESS) {
|
|
2533
|
+
const level = String(node.data.level || "");
|
|
2534
|
+
if (["root", "SYSTEM", "Administrator", "admin"].includes(level)) return SEVERITY.CRITICAL;
|
|
2535
|
+
return SEVERITY.HIGH;
|
|
2536
|
+
}
|
|
2537
|
+
if (node.type === NODE_TYPE.VULNERABILITY) {
|
|
2538
|
+
const sev = String(node.data.severity || "");
|
|
2539
|
+
if (sev === SEVERITY.CRITICAL) return SEVERITY.CRITICAL;
|
|
2540
|
+
if (sev === SEVERITY.HIGH) return SEVERITY.HIGH;
|
|
2541
|
+
if (sev === SEVERITY.MEDIUM) return SEVERITY.MEDIUM;
|
|
2542
|
+
}
|
|
2543
|
+
return SEVERITY.LOW;
|
|
2356
2544
|
}
|
|
2545
|
+
// ─── Prompt Generation ──────────────────────────────────────
|
|
2357
2546
|
/**
|
|
2358
2547
|
* Format attack graph status for prompt injection.
|
|
2548
|
+
* Provides LLM with:
|
|
2549
|
+
* - Available paths (sorted by probability)
|
|
2550
|
+
* - Failed paths (DO NOT retry)
|
|
2551
|
+
* - Unexploited vulnerabilities
|
|
2359
2552
|
*/
|
|
2360
2553
|
toPrompt() {
|
|
2361
2554
|
if (this.nodes.size === 0) return "";
|
|
2362
2555
|
const lines = ["<attack-graph>"];
|
|
2363
2556
|
const nodesByType = {};
|
|
2557
|
+
const nodesByStatus = {};
|
|
2364
2558
|
for (const node of this.nodes.values()) {
|
|
2365
2559
|
nodesByType[node.type] = (nodesByType[node.type] || 0) + 1;
|
|
2560
|
+
nodesByStatus[node.status] = (nodesByStatus[node.status] || 0) + 1;
|
|
2366
2561
|
}
|
|
2367
2562
|
lines.push(`Nodes: ${Object.entries(nodesByType).map(([t, c]) => `${c} ${t}s`).join(", ")}`);
|
|
2368
|
-
lines.push(`
|
|
2563
|
+
lines.push(`Status: ${Object.entries(nodesByStatus).map(([s, c]) => `${c} ${s}`).join(", ")}`);
|
|
2564
|
+
lines.push(`Edges: ${this.edges.length} (${this.edges.filter((e) => e.status === EDGE_STATUS.FAILED).length} failed)`);
|
|
2369
2565
|
const chains = this.recommendChains();
|
|
2370
2566
|
if (chains.length > 0) {
|
|
2371
2567
|
lines.push("");
|
|
2372
|
-
lines.push("RECOMMENDED
|
|
2568
|
+
lines.push("RECOMMENDED ATTACK PATHS (try these):");
|
|
2373
2569
|
for (let i = 0; i < Math.min(GRAPH_LIMITS.PROMPT_CHAINS, chains.length); i++) {
|
|
2374
2570
|
const c = chains[i];
|
|
2375
2571
|
const prob = (c.probability * 100).toFixed(0);
|
|
2376
|
-
lines.push(` ${i + 1}
|
|
2572
|
+
lines.push(` PATH ${i + 1} [${c.impact.toUpperCase()} | ${prob}%]: ${c.description}`);
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
if (this.failedPaths.length > 0) {
|
|
2576
|
+
lines.push("");
|
|
2577
|
+
lines.push("FAILED PATHS (DO NOT RETRY):");
|
|
2578
|
+
for (const fp of this.failedPaths.slice(-GRAPH_LIMITS.PROMPT_FAILED)) {
|
|
2579
|
+
lines.push(` \u2717 ${fp}`);
|
|
2377
2580
|
}
|
|
2378
2581
|
}
|
|
2379
|
-
const unexploitedVulns = Array.from(this.nodes.values()).filter((n) => n.type ===
|
|
2582
|
+
const unexploitedVulns = Array.from(this.nodes.values()).filter((n) => n.type === NODE_TYPE.VULNERABILITY && n.status !== NODE_STATUS.SUCCEEDED && n.status !== NODE_STATUS.FAILED);
|
|
2380
2583
|
if (unexploitedVulns.length > 0) {
|
|
2381
2584
|
lines.push("");
|
|
2382
2585
|
lines.push(`UNEXPLOITED VULNERABILITIES (${unexploitedVulns.length}):`);
|
|
2383
2586
|
for (const v of unexploitedVulns.slice(0, GRAPH_LIMITS.PROMPT_VULNS)) {
|
|
2384
|
-
|
|
2587
|
+
const sev = v.data.severity || "unknown";
|
|
2588
|
+
const status = v.status === NODE_STATUS.ATTEMPTED ? " \u23F3 testing" : "";
|
|
2589
|
+
lines.push(` - ${v.label} (${sev})${status}`);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
const accessNodes = Array.from(this.nodes.values()).filter((n) => n.type === NODE_TYPE.ACCESS && n.status === NODE_STATUS.SUCCEEDED);
|
|
2593
|
+
if (accessNodes.length > 0) {
|
|
2594
|
+
lines.push("");
|
|
2595
|
+
lines.push("ACHIEVED ACCESS:");
|
|
2596
|
+
for (const a of accessNodes) {
|
|
2597
|
+
lines.push(` \u2713 ${a.label}`);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
const credNodes = Array.from(this.nodes.values()).filter((n) => n.type === NODE_TYPE.CREDENTIAL);
|
|
2601
|
+
if (credNodes.length > 0) {
|
|
2602
|
+
lines.push("");
|
|
2603
|
+
lines.push(`CREDENTIALS (${credNodes.length} \u2014 try on all services):`);
|
|
2604
|
+
for (const c of credNodes) {
|
|
2605
|
+
const source = c.data.source || "unknown";
|
|
2606
|
+
const sprayEdges = this.edges.filter((e) => e.from === c.id && e.relation === "can_try_on");
|
|
2607
|
+
const testedCount = sprayEdges.filter((e) => e.status !== EDGE_STATUS.UNTESTED).length;
|
|
2608
|
+
lines.push(` \u{1F511} ${c.label} (from: ${source}) [sprayed: ${testedCount}/${sprayEdges.length}]`);
|
|
2385
2609
|
}
|
|
2386
2610
|
}
|
|
2387
2611
|
lines.push("</attack-graph>");
|
|
2388
2612
|
return lines.join("\n");
|
|
2389
2613
|
}
|
|
2614
|
+
// ─── TUI Visualization ──────────────────────────────────────
|
|
2615
|
+
/**
|
|
2616
|
+
* Generate ASCII visualization for TUI /graph command.
|
|
2617
|
+
*/
|
|
2618
|
+
toASCII() {
|
|
2619
|
+
if (this.nodes.size === 0) return "(Empty attack graph \u2014 no discoveries yet)";
|
|
2620
|
+
const lines = [];
|
|
2621
|
+
lines.push("\u250C\u2500\u2500\u2500 Attack Graph \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
2622
|
+
const groups = {};
|
|
2623
|
+
for (const node of this.nodes.values()) {
|
|
2624
|
+
if (!groups[node.type]) groups[node.type] = [];
|
|
2625
|
+
groups[node.type].push(node);
|
|
2626
|
+
}
|
|
2627
|
+
const typeIcons = {
|
|
2628
|
+
[NODE_TYPE.HOST]: "\u{1F5A5}",
|
|
2629
|
+
[NODE_TYPE.SERVICE]: "\u2699",
|
|
2630
|
+
[NODE_TYPE.VULNERABILITY]: "\u26A0",
|
|
2631
|
+
[NODE_TYPE.CREDENTIAL]: "\u{1F511}",
|
|
2632
|
+
[NODE_TYPE.ACCESS]: "\u{1F513}",
|
|
2633
|
+
[NODE_TYPE.LOOT]: "\u{1F3F4}",
|
|
2634
|
+
[NODE_TYPE.OSINT]: "\u{1F50D}"
|
|
2635
|
+
};
|
|
2636
|
+
const statusIcons = {
|
|
2637
|
+
[NODE_STATUS.DISCOVERED]: "\u25CB",
|
|
2638
|
+
[NODE_STATUS.ATTEMPTED]: "\u25D0",
|
|
2639
|
+
[NODE_STATUS.SUCCEEDED]: "\u25CF",
|
|
2640
|
+
[NODE_STATUS.FAILED]: "\u2717"
|
|
2641
|
+
};
|
|
2642
|
+
let nodeCount = 0;
|
|
2643
|
+
for (const [type, nodes] of Object.entries(groups)) {
|
|
2644
|
+
if (nodeCount >= GRAPH_LIMITS.ASCII_MAX_NODES) {
|
|
2645
|
+
lines.push(`\u2502 ... and ${this.nodes.size - nodeCount} more nodes`);
|
|
2646
|
+
break;
|
|
2647
|
+
}
|
|
2648
|
+
const icon = typeIcons[type] || "\xB7";
|
|
2649
|
+
lines.push(`\u2502`);
|
|
2650
|
+
lines.push(`\u2502 ${icon} ${type.toUpperCase()} (${nodes.length})`);
|
|
2651
|
+
for (const node of nodes) {
|
|
2652
|
+
if (nodeCount >= GRAPH_LIMITS.ASCII_MAX_NODES) break;
|
|
2653
|
+
const sIcon = statusIcons[node.status] || "?";
|
|
2654
|
+
const fail = node.status === NODE_STATUS.FAILED && node.failReason ? ` \u2014 ${node.failReason}` : "";
|
|
2655
|
+
const outEdges = this.edges.filter((e) => e.from === node.id);
|
|
2656
|
+
const edgeStr = outEdges.length > 0 ? ` \u2192 ${outEdges.map((e) => {
|
|
2657
|
+
const target = this.nodes.get(e.to);
|
|
2658
|
+
const eName = target ? target.label : e.to;
|
|
2659
|
+
const eStatus = e.status === EDGE_STATUS.FAILED ? " \u2717" : e.status === EDGE_STATUS.SUCCEEDED ? " \u2713" : "";
|
|
2660
|
+
return `${eName}${eStatus}`;
|
|
2661
|
+
}).join(", ")}` : "";
|
|
2662
|
+
lines.push(`\u2502 ${sIcon} ${node.label}${fail}${edgeStr}`);
|
|
2663
|
+
nodeCount++;
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
const stats = this.getStats();
|
|
2667
|
+
lines.push(`\u2502`);
|
|
2668
|
+
lines.push(`\u251C\u2500\u2500\u2500 Summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524`);
|
|
2669
|
+
lines.push(`\u2502 Nodes: ${stats.nodes} | Edges: ${stats.edges} | Succeeded: ${stats.succeeded} | Failed: ${stats.failed} | Chains: ${stats.chains}`);
|
|
2670
|
+
lines.push(`\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`);
|
|
2671
|
+
return lines.join("\n");
|
|
2672
|
+
}
|
|
2673
|
+
/**
|
|
2674
|
+
* Generate path listing for TUI /paths command.
|
|
2675
|
+
*/
|
|
2676
|
+
toPathsList() {
|
|
2677
|
+
const chains = this.recommendChains();
|
|
2678
|
+
if (chains.length === 0) {
|
|
2679
|
+
if (this.nodes.size === 0) return "No attack graph data yet. Start reconnaissance first.";
|
|
2680
|
+
return "No viable attack paths found. All paths may be exhausted or failed.";
|
|
2681
|
+
}
|
|
2682
|
+
const lines = [];
|
|
2683
|
+
lines.push(`\u2500\u2500\u2500 ${chains.length} Attack Paths (sorted by priority) \u2500\u2500\u2500`);
|
|
2684
|
+
lines.push("");
|
|
2685
|
+
for (let i = 0; i < chains.length; i++) {
|
|
2686
|
+
const c = chains[i];
|
|
2687
|
+
const prob = (c.probability * 100).toFixed(0);
|
|
2688
|
+
const impactColors = {
|
|
2689
|
+
[SEVERITY.CRITICAL]: "\u{1F534}",
|
|
2690
|
+
[SEVERITY.HIGH]: "\u{1F7E0}",
|
|
2691
|
+
[SEVERITY.MEDIUM]: "\u{1F7E1}",
|
|
2692
|
+
[SEVERITY.LOW]: "\u{1F7E2}"
|
|
2693
|
+
};
|
|
2694
|
+
const impactIcon = impactColors[c.impact] || "\u26AA";
|
|
2695
|
+
lines.push(`${impactIcon} PATH ${i + 1} [${c.impact.toUpperCase()} | prob: ${prob}% | steps: ${c.length}]`);
|
|
2696
|
+
lines.push(` ${c.description}`);
|
|
2697
|
+
for (const edge of c.edges) {
|
|
2698
|
+
const statusTag = edge.status === EDGE_STATUS.UNTESTED ? " ?" : edge.status === EDGE_STATUS.TESTING ? " \u23F3" : edge.status === EDGE_STATUS.SUCCEEDED ? " \u2713" : " \u2717";
|
|
2699
|
+
lines.push(` ${statusTag} ${edge.from.split(":").pop()} \u2014[${edge.relation}]\u2192 ${edge.to.split(":").pop()}`);
|
|
2700
|
+
}
|
|
2701
|
+
lines.push("");
|
|
2702
|
+
}
|
|
2703
|
+
if (this.failedPaths.length > 0) {
|
|
2704
|
+
lines.push(`\u2500\u2500\u2500 Failed Paths (${this.failedPaths.length}) \u2500\u2500\u2500`);
|
|
2705
|
+
for (const fp of this.failedPaths) {
|
|
2706
|
+
lines.push(` \u2717 ${fp}`);
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
return lines.join("\n");
|
|
2710
|
+
}
|
|
2711
|
+
// ─── Stats ──────────────────────────────────────────────────
|
|
2390
2712
|
/**
|
|
2391
2713
|
* Get graph stats for metrics display.
|
|
2392
2714
|
*/
|
|
2393
2715
|
getStats() {
|
|
2394
|
-
const
|
|
2716
|
+
const succeeded = Array.from(this.nodes.values()).filter((n) => n.status === NODE_STATUS.SUCCEEDED).length;
|
|
2717
|
+
const failed = Array.from(this.nodes.values()).filter((n) => n.status === NODE_STATUS.FAILED).length;
|
|
2395
2718
|
return {
|
|
2396
2719
|
nodes: this.nodes.size,
|
|
2397
2720
|
edges: this.edges.length,
|
|
2398
|
-
|
|
2721
|
+
succeeded,
|
|
2722
|
+
failed,
|
|
2399
2723
|
chains: this.recommendChains().length
|
|
2400
2724
|
};
|
|
2401
2725
|
}
|
|
2726
|
+
/**
|
|
2727
|
+
* Get all nodes (for serialization or external access).
|
|
2728
|
+
*/
|
|
2729
|
+
getAllNodes() {
|
|
2730
|
+
return Array.from(this.nodes.values());
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* Get all edges (for serialization or external access).
|
|
2734
|
+
*/
|
|
2735
|
+
getAllEdges() {
|
|
2736
|
+
return [...this.edges];
|
|
2737
|
+
}
|
|
2738
|
+
/**
|
|
2739
|
+
* Get failed paths list.
|
|
2740
|
+
*/
|
|
2741
|
+
getFailedPaths() {
|
|
2742
|
+
return [...this.failedPaths];
|
|
2743
|
+
}
|
|
2744
|
+
/**
|
|
2745
|
+
* Check if the graph has any data.
|
|
2746
|
+
*/
|
|
2747
|
+
isEmpty() {
|
|
2748
|
+
return this.nodes.size === 0;
|
|
2749
|
+
}
|
|
2402
2750
|
};
|
|
2403
2751
|
|
|
2404
2752
|
// src/shared/utils/agent-memory.ts
|
|
@@ -2494,6 +2842,10 @@ var WorkingMemory = class {
|
|
|
2494
2842
|
getEntries() {
|
|
2495
2843
|
return [...this.entries];
|
|
2496
2844
|
}
|
|
2845
|
+
/** Clear all working memory entries. */
|
|
2846
|
+
clear() {
|
|
2847
|
+
this.entries = [];
|
|
2848
|
+
}
|
|
2497
2849
|
};
|
|
2498
2850
|
var EpisodicMemory = class {
|
|
2499
2851
|
events = [];
|
|
@@ -2549,6 +2901,10 @@ var EpisodicMemory = class {
|
|
|
2549
2901
|
lines.push("</session-timeline>");
|
|
2550
2902
|
return lines.join("\n");
|
|
2551
2903
|
}
|
|
2904
|
+
/** Clear all episodic events. */
|
|
2905
|
+
clear() {
|
|
2906
|
+
this.events = [];
|
|
2907
|
+
}
|
|
2552
2908
|
};
|
|
2553
2909
|
var MEMORY_DIR = "/tmp/pentesting-memory";
|
|
2554
2910
|
var MEMORY_FILE = join3(MEMORY_DIR, "persistent-knowledge.json");
|
|
@@ -2733,6 +3089,10 @@ var DynamicTechniqueLibrary = class {
|
|
|
2733
3089
|
getAll() {
|
|
2734
3090
|
return [...this.techniques];
|
|
2735
3091
|
}
|
|
3092
|
+
/** Clear all learned techniques. */
|
|
3093
|
+
clear() {
|
|
3094
|
+
this.techniques = [];
|
|
3095
|
+
}
|
|
2736
3096
|
/**
|
|
2737
3097
|
* Format for prompt injection.
|
|
2738
3098
|
*/
|
|
@@ -2841,6 +3201,32 @@ var SharedState = class {
|
|
|
2841
3201
|
challengeAnalysis: null
|
|
2842
3202
|
};
|
|
2843
3203
|
}
|
|
3204
|
+
/**
|
|
3205
|
+
* Reset all state to initial defaults.
|
|
3206
|
+
* Used by /clear command to start a fresh session without recreating the agent.
|
|
3207
|
+
*/
|
|
3208
|
+
reset() {
|
|
3209
|
+
this.data = {
|
|
3210
|
+
engagement: null,
|
|
3211
|
+
targets: /* @__PURE__ */ new Map(),
|
|
3212
|
+
findings: [],
|
|
3213
|
+
loot: [],
|
|
3214
|
+
todo: [],
|
|
3215
|
+
actionLog: [],
|
|
3216
|
+
currentPhase: PHASES.RECON,
|
|
3217
|
+
missionSummary: "",
|
|
3218
|
+
missionChecklist: [],
|
|
3219
|
+
ctfMode: true,
|
|
3220
|
+
flags: [],
|
|
3221
|
+
startedAt: Date.now(),
|
|
3222
|
+
deadlineAt: 0,
|
|
3223
|
+
challengeAnalysis: null
|
|
3224
|
+
};
|
|
3225
|
+
this.attackGraph.reset();
|
|
3226
|
+
this.workingMemory.clear();
|
|
3227
|
+
this.episodicMemory.clear();
|
|
3228
|
+
this.dynamicTechniques.clear();
|
|
3229
|
+
}
|
|
2844
3230
|
// --- Mission & Persistent Context ---
|
|
2845
3231
|
setMissionSummary(summary) {
|
|
2846
3232
|
this.data.missionSummary = summary;
|
|
@@ -3351,6 +3737,11 @@ var NOISE_CLASSIFICATION = {
|
|
|
3351
3737
|
};
|
|
3352
3738
|
|
|
3353
3739
|
// src/shared/utils/binary-analysis.ts
|
|
3740
|
+
var RELRO_STATUS = {
|
|
3741
|
+
NO: "no",
|
|
3742
|
+
PARTIAL: "partial",
|
|
3743
|
+
FULL: "full"
|
|
3744
|
+
};
|
|
3354
3745
|
function parseChecksec(output) {
|
|
3355
3746
|
const info = {};
|
|
3356
3747
|
if (/x86-64|amd64/i.test(output)) {
|
|
@@ -3404,10 +3795,10 @@ function suggestExploitTechniques(info) {
|
|
|
3404
3795
|
if (info.pie === true && info.canary === false) {
|
|
3405
3796
|
suggestions.push("PIE enabled \u2192 Need info leak first (partial overwrite, format string)");
|
|
3406
3797
|
}
|
|
3407
|
-
if (info.relro ===
|
|
3798
|
+
if (info.relro === RELRO_STATUS.PARTIAL) {
|
|
3408
3799
|
suggestions.push("Partial RELRO \u2192 GOT overwrite possible");
|
|
3409
3800
|
}
|
|
3410
|
-
if (info.relro ===
|
|
3801
|
+
if (info.relro === RELRO_STATUS.FULL) {
|
|
3411
3802
|
suggestions.push("Full RELRO \u2192 GOT is read-only, try __malloc_hook/__free_hook or stack-based attacks");
|
|
3412
3803
|
}
|
|
3413
3804
|
if (info.canary === true) {
|
|
@@ -3442,6 +3833,11 @@ function formatBinaryAnalysis(info) {
|
|
|
3442
3833
|
}
|
|
3443
3834
|
|
|
3444
3835
|
// src/shared/utils/structured-output.ts
|
|
3836
|
+
var PORT_STATE = {
|
|
3837
|
+
OPEN: "open",
|
|
3838
|
+
CLOSED: "closed",
|
|
3839
|
+
FILTERED: "filtered"
|
|
3840
|
+
};
|
|
3445
3841
|
function extractNmapStructured(output) {
|
|
3446
3842
|
const ports = [];
|
|
3447
3843
|
const hosts = [];
|
|
@@ -3459,8 +3855,8 @@ function extractNmapStructured(output) {
|
|
|
3459
3855
|
while ((match = hostRegex.exec(output)) !== null) {
|
|
3460
3856
|
hosts.push(match[1]);
|
|
3461
3857
|
}
|
|
3462
|
-
const openCount = ports.filter((p) => p.state ===
|
|
3463
|
-
const summary = `${hosts.length} host(s), ${openCount} open port(s): ${ports.filter((p) => p.state ===
|
|
3858
|
+
const openCount = ports.filter((p) => p.state === PORT_STATE.OPEN).length;
|
|
3859
|
+
const summary = `${hosts.length} host(s), ${openCount} open port(s): ${ports.filter((p) => p.state === PORT_STATE.OPEN).map((p) => `${p.port}/${p.service}`).join(", ")}`;
|
|
3464
3860
|
return {
|
|
3465
3861
|
summary,
|
|
3466
3862
|
structured: { openPorts: ports, hosts }
|
|
@@ -4032,7 +4428,7 @@ Resource Safety:
|
|
|
4032
4428
|
const action = params.action;
|
|
4033
4429
|
const processId = params.process_id;
|
|
4034
4430
|
switch (action) {
|
|
4035
|
-
case
|
|
4431
|
+
case PROCESS_ACTIONS.LIST: {
|
|
4036
4432
|
const procs = listBackgroundProcesses();
|
|
4037
4433
|
if (procs.length === 0) {
|
|
4038
4434
|
return { success: true, output: "No background processes running.\nNo ports in use." };
|
|
@@ -4063,7 +4459,7 @@ Ports in use: ${usedPorts.join(", ")}` : "\nNo ports in use.";
|
|
|
4063
4459
|
${lines.join("\n\n")}${portLine}${eventLines}`
|
|
4064
4460
|
};
|
|
4065
4461
|
}
|
|
4066
|
-
case
|
|
4462
|
+
case PROCESS_ACTIONS.STATUS: {
|
|
4067
4463
|
if (!processId) return { success: false, output: "", error: "Missing required parameter: process_id" };
|
|
4068
4464
|
const output = getProcessOutput(processId);
|
|
4069
4465
|
if (!output) return { success: false, output: "", error: `Process ${processId} not found.` };
|
|
@@ -4103,7 +4499,7 @@ ${output.stdout.slice(-SYSTEM_LIMITS.MAX_STDOUT_SLICE) || "(empty)"}
|
|
|
4103
4499
|
${output.stderr.slice(-SYSTEM_LIMITS.MAX_STDERR_SLICE) || "(empty)"}` + connectionHint + interactHint
|
|
4104
4500
|
};
|
|
4105
4501
|
}
|
|
4106
|
-
case
|
|
4502
|
+
case PROCESS_ACTIONS.INTERACT: {
|
|
4107
4503
|
if (!processId) return { success: false, output: "", error: "Missing process_id for interact" };
|
|
4108
4504
|
const cmd = params.command;
|
|
4109
4505
|
if (!cmd) return { success: false, output: "", error: "Missing command for interact. Provide the command to execute on the target." };
|
|
@@ -4121,7 +4517,7 @@ ${result2.newOutput}
|
|
|
4121
4517
|
${result2.output}`
|
|
4122
4518
|
};
|
|
4123
4519
|
}
|
|
4124
|
-
case
|
|
4520
|
+
case PROCESS_ACTIONS.PROMOTE: {
|
|
4125
4521
|
if (!processId) return { success: false, output: "", error: "Missing process_id for promote" };
|
|
4126
4522
|
const desc = params.description;
|
|
4127
4523
|
const success = promoteToShell(processId, desc);
|
|
@@ -4142,11 +4538,11 @@ Next steps:
|
|
|
4142
4538
|
5. Begin post-exploitation enumeration: whoami, sudo -l, etc.`
|
|
4143
4539
|
};
|
|
4144
4540
|
}
|
|
4145
|
-
case
|
|
4541
|
+
case PROCESS_ACTIONS.STOP: {
|
|
4146
4542
|
if (!processId) return { success: false, output: "", error: "Missing process_id" };
|
|
4147
4543
|
return stopBackgroundProcess(processId);
|
|
4148
4544
|
}
|
|
4149
|
-
case
|
|
4545
|
+
case PROCESS_ACTIONS.STOP_ALL: {
|
|
4150
4546
|
const procs = listBackgroundProcesses();
|
|
4151
4547
|
if (procs.length === 0) return { success: true, output: "No background processes to stop." };
|
|
4152
4548
|
const activeShells = procs.filter((p) => p.role === PROCESS_ROLES.ACTIVE_SHELL && p.isRunning);
|
|
@@ -5217,6 +5613,11 @@ async function searchWithPlaywright(query) {
|
|
|
5217
5613
|
}
|
|
5218
5614
|
|
|
5219
5615
|
// src/engine/tools-mid.ts
|
|
5616
|
+
var PORT_STATE2 = {
|
|
5617
|
+
OPEN: "open",
|
|
5618
|
+
CLOSED: "closed",
|
|
5619
|
+
FILTERED: "filtered"
|
|
5620
|
+
};
|
|
5220
5621
|
function getErrorMessage2(error) {
|
|
5221
5622
|
return error instanceof Error ? error.message : String(error);
|
|
5222
5623
|
}
|
|
@@ -5249,7 +5650,7 @@ async function parseNmap(xmlPath) {
|
|
|
5249
5650
|
const serviceMatch = portMatch[0].match(/<service[^>]*name="([^"]*)"(?:[^>]*version="([^"]*)")?/);
|
|
5250
5651
|
const service = serviceMatch ? serviceMatch[1] : void 0;
|
|
5251
5652
|
const version = serviceMatch && serviceMatch[2] ? serviceMatch[2] : void 0;
|
|
5252
|
-
if (state ===
|
|
5653
|
+
if (state === PORT_STATE2.OPEN) {
|
|
5253
5654
|
ports.push({ port, protocol, state, service, version });
|
|
5254
5655
|
results.summary.openPorts++;
|
|
5255
5656
|
if (service) results.summary.servicesFound++;
|
|
@@ -5819,6 +6220,54 @@ For CVEs not in this list, use web_search to find them.`
|
|
|
5819
6220
|
];
|
|
5820
6221
|
|
|
5821
6222
|
// src/shared/utils/payload-mutator.ts
|
|
6223
|
+
var TRANSFORM_TYPE = {
|
|
6224
|
+
URL: "url",
|
|
6225
|
+
DOUBLE_URL: "double_url",
|
|
6226
|
+
TRIPLE_URL: "triple_url",
|
|
6227
|
+
UNICODE: "unicode",
|
|
6228
|
+
HTML_ENTITY_DEC: "html_entity_dec",
|
|
6229
|
+
HTML_ENTITY_HEX: "html_entity_hex",
|
|
6230
|
+
HEX: "hex",
|
|
6231
|
+
OCTAL: "octal",
|
|
6232
|
+
BASE64: "base64",
|
|
6233
|
+
CASE_SWAP: "case_swap",
|
|
6234
|
+
COMMENT_INSERT: "comment_insert",
|
|
6235
|
+
WHITESPACE_ALT: "whitespace_alt",
|
|
6236
|
+
CHAR_FUNCTION: "char_function",
|
|
6237
|
+
CONCAT_SPLIT: "concat_split",
|
|
6238
|
+
NULL_BYTE: "null_byte",
|
|
6239
|
+
REVERSE: "reverse",
|
|
6240
|
+
UTF8_OVERLONG: "utf8_overlong",
|
|
6241
|
+
MIXED_ENCODING: "mixed_encoding",
|
|
6242
|
+
TAG_ALTERNATIVE: "tag_alternative",
|
|
6243
|
+
EVENT_HANDLER: "event_handler",
|
|
6244
|
+
KEYWORD_BYPASS: "keyword_bypass",
|
|
6245
|
+
SPACE_BYPASS: "space_bypass",
|
|
6246
|
+
NO_QUOTES: "no_quotes",
|
|
6247
|
+
JS_ALTERNATIVE: "js_alternative"
|
|
6248
|
+
};
|
|
6249
|
+
var PAYLOAD_CONTEXT = {
|
|
6250
|
+
URL_PATH: "url_path",
|
|
6251
|
+
URL_PARAM: "url_param",
|
|
6252
|
+
POST_BODY: "post_body",
|
|
6253
|
+
JSON_BODY: "json_body",
|
|
6254
|
+
XML_BODY: "xml_body",
|
|
6255
|
+
HTML_BODY: "html_body",
|
|
6256
|
+
HTML_ATTR: "html_attr",
|
|
6257
|
+
JS_STRING: "js_string",
|
|
6258
|
+
SQL_STRING: "sql_string",
|
|
6259
|
+
SQL_NUMERIC: "sql_numeric",
|
|
6260
|
+
SHELL_CMD: "shell_cmd",
|
|
6261
|
+
HTTP_HEADER: "http_header",
|
|
6262
|
+
COOKIE: "cookie",
|
|
6263
|
+
GENERIC: "generic"
|
|
6264
|
+
};
|
|
6265
|
+
var DATABASE_TYPE = {
|
|
6266
|
+
MYSQL: "mysql",
|
|
6267
|
+
MSSQL: "mssql",
|
|
6268
|
+
POSTGRESQL: "pg",
|
|
6269
|
+
ORACLE: "oracle"
|
|
6270
|
+
};
|
|
5822
6271
|
function urlEncode(s) {
|
|
5823
6272
|
return [...s].map((c) => {
|
|
5824
6273
|
const code = c.charCodeAt(0);
|
|
@@ -5879,12 +6328,12 @@ function whitespaceAlt(s) {
|
|
|
5879
6328
|
const alt = alts[Math.floor(Math.random() * alts.length)];
|
|
5880
6329
|
return s.replace(/ /g, alt);
|
|
5881
6330
|
}
|
|
5882
|
-
function charFunction(s, dbType =
|
|
6331
|
+
function charFunction(s, dbType = DATABASE_TYPE.MYSQL) {
|
|
5883
6332
|
const chars = [...s].map((c) => c.charCodeAt(0));
|
|
5884
|
-
if (dbType ===
|
|
6333
|
+
if (dbType === DATABASE_TYPE.MYSQL) {
|
|
5885
6334
|
return `CHAR(${chars.join(",")})`;
|
|
5886
6335
|
}
|
|
5887
|
-
if (dbType ===
|
|
6336
|
+
if (dbType === DATABASE_TYPE.MSSQL) {
|
|
5888
6337
|
return chars.map((c) => `CHAR(${c})`).join("+");
|
|
5889
6338
|
}
|
|
5890
6339
|
return chars.map((c) => `CHR(${c})`).join("||");
|
|
@@ -5969,78 +6418,78 @@ function mutatePayload(request) {
|
|
|
5969
6418
|
const activeTransforms = transforms || getDefaultTransforms(context);
|
|
5970
6419
|
for (const transform of activeTransforms) {
|
|
5971
6420
|
switch (transform) {
|
|
5972
|
-
case
|
|
6421
|
+
case TRANSFORM_TYPE.URL:
|
|
5973
6422
|
variants.push({ payload: urlEncode(payload), transform: "url", description: "URL encoded (special chars only)" });
|
|
5974
6423
|
variants.push({ payload: urlEncodeAll(payload), transform: "url_full", description: "URL encoded (all chars)" });
|
|
5975
6424
|
break;
|
|
5976
|
-
case
|
|
6425
|
+
case TRANSFORM_TYPE.DOUBLE_URL:
|
|
5977
6426
|
variants.push({ payload: doubleUrlEncode(payload), transform: "double_url", description: "Double URL encoded" });
|
|
5978
6427
|
break;
|
|
5979
|
-
case
|
|
6428
|
+
case TRANSFORM_TYPE.TRIPLE_URL:
|
|
5980
6429
|
variants.push({ payload: tripleUrlEncode(payload), transform: "triple_url", description: "Triple URL encoded" });
|
|
5981
6430
|
break;
|
|
5982
|
-
case
|
|
6431
|
+
case TRANSFORM_TYPE.UNICODE:
|
|
5983
6432
|
variants.push({ payload: unicodeEncode(payload), transform: "unicode", description: "Unicode escape sequences" });
|
|
5984
6433
|
break;
|
|
5985
|
-
case
|
|
6434
|
+
case TRANSFORM_TYPE.HTML_ENTITY_DEC:
|
|
5986
6435
|
variants.push({ payload: htmlEntityDec(payload), transform: "html_entity_dec", description: "HTML decimal entities" });
|
|
5987
6436
|
break;
|
|
5988
|
-
case
|
|
6437
|
+
case TRANSFORM_TYPE.HTML_ENTITY_HEX:
|
|
5989
6438
|
variants.push({ payload: htmlEntityHex(payload), transform: "html_entity_hex", description: "HTML hex entities" });
|
|
5990
6439
|
break;
|
|
5991
|
-
case
|
|
6440
|
+
case TRANSFORM_TYPE.HEX:
|
|
5992
6441
|
variants.push({ payload: hexEncode(payload), transform: "hex", description: "Hex encoded" });
|
|
5993
6442
|
break;
|
|
5994
|
-
case
|
|
6443
|
+
case TRANSFORM_TYPE.OCTAL:
|
|
5995
6444
|
variants.push({ payload: octalEncode(payload), transform: "octal", description: "Octal encoded" });
|
|
5996
6445
|
break;
|
|
5997
|
-
case
|
|
6446
|
+
case TRANSFORM_TYPE.BASE64:
|
|
5998
6447
|
variants.push({ payload: base64Encode(payload), transform: "base64", description: "Base64 encoded" });
|
|
5999
6448
|
break;
|
|
6000
|
-
case
|
|
6449
|
+
case TRANSFORM_TYPE.CASE_SWAP:
|
|
6001
6450
|
for (let i = 0; i < 3; i++) {
|
|
6002
6451
|
variants.push({ payload: caseSwap(payload), transform: "case_swap", description: `Case variation ${i + 1}` });
|
|
6003
6452
|
}
|
|
6004
6453
|
break;
|
|
6005
|
-
case
|
|
6454
|
+
case TRANSFORM_TYPE.COMMENT_INSERT:
|
|
6006
6455
|
variants.push({ payload: commentInsert(payload), transform: "comment_insert", description: "SQL comments in keywords" });
|
|
6007
6456
|
break;
|
|
6008
|
-
case
|
|
6457
|
+
case TRANSFORM_TYPE.WHITESPACE_ALT:
|
|
6009
6458
|
variants.push({ payload: whitespaceAlt(payload), transform: "whitespace_alt", description: "Alternative whitespace chars" });
|
|
6010
6459
|
variants.push({ payload: payload.replace(/ /g, "/**/"), transform: "whitespace_comment", description: "Comment as whitespace" });
|
|
6011
6460
|
variants.push({ payload: payload.replace(/ /g, "+"), transform: "whitespace_plus", description: "Plus as whitespace" });
|
|
6012
6461
|
break;
|
|
6013
|
-
case
|
|
6462
|
+
case TRANSFORM_TYPE.CHAR_FUNCTION:
|
|
6014
6463
|
variants.push({ payload: charFunction(payload, "mysql"), transform: "char_mysql", description: "MySQL CHAR() encoding" });
|
|
6015
6464
|
variants.push({ payload: charFunction(payload, "mssql"), transform: "char_mssql", description: "MSSQL CHAR() encoding" });
|
|
6016
6465
|
variants.push({ payload: charFunction(payload, "pg"), transform: "char_pg", description: "PostgreSQL CHR() encoding" });
|
|
6017
6466
|
break;
|
|
6018
|
-
case
|
|
6467
|
+
case TRANSFORM_TYPE.CONCAT_SPLIT:
|
|
6019
6468
|
variants.push({ payload: concatSplit(payload), transform: "concat_split", description: "String split via CONCAT" });
|
|
6020
6469
|
break;
|
|
6021
|
-
case
|
|
6470
|
+
case TRANSFORM_TYPE.NULL_BYTE:
|
|
6022
6471
|
variants.push({ payload: nullByteAppend(payload), transform: "null_byte", description: "Null byte appended" });
|
|
6023
6472
|
variants.push({ payload: payload + "%00.jpg", transform: "null_byte_ext", description: "Null byte + fake extension" });
|
|
6024
6473
|
break;
|
|
6025
|
-
case
|
|
6474
|
+
case TRANSFORM_TYPE.REVERSE:
|
|
6026
6475
|
variants.push({ payload: reversePayload(payload), transform: "reverse", description: "Reversed (use with rev | sh)" });
|
|
6027
6476
|
break;
|
|
6028
|
-
case
|
|
6477
|
+
case TRANSFORM_TYPE.UTF8_OVERLONG:
|
|
6029
6478
|
variants.push({ payload: utf8Overlong(payload), transform: "utf8_overlong", description: "UTF-8 overlong sequences" });
|
|
6030
6479
|
break;
|
|
6031
|
-
case
|
|
6480
|
+
case TRANSFORM_TYPE.MIXED_ENCODING:
|
|
6032
6481
|
variants.push({ payload: mixedEncoding(payload), transform: "mixed_encoding", description: "Mixed encoding (partial)" });
|
|
6033
6482
|
break;
|
|
6034
|
-
case
|
|
6035
|
-
case
|
|
6036
|
-
case
|
|
6483
|
+
case TRANSFORM_TYPE.TAG_ALTERNATIVE:
|
|
6484
|
+
case TRANSFORM_TYPE.EVENT_HANDLER:
|
|
6485
|
+
case TRANSFORM_TYPE.JS_ALTERNATIVE:
|
|
6037
6486
|
variants.push(...generateXssAlternatives(payload));
|
|
6038
6487
|
break;
|
|
6039
|
-
case
|
|
6488
|
+
case TRANSFORM_TYPE.KEYWORD_BYPASS:
|
|
6040
6489
|
variants.push({ payload: keywordBypass(payload), transform: "keyword_bypass", description: "Quote-inserted keyword bypass" });
|
|
6041
6490
|
variants.push({ payload: spaceBypass(payload), transform: "space_bypass", description: "$IFS space bypass" });
|
|
6042
6491
|
break;
|
|
6043
|
-
case
|
|
6492
|
+
case TRANSFORM_TYPE.SPACE_BYPASS:
|
|
6044
6493
|
variants.push({ payload: payload.replace(/ /g, "${IFS}"), transform: "ifs", description: "$IFS space bypass" });
|
|
6045
6494
|
variants.push({ payload: payload.replace(/ /g, "%09"), transform: "tab", description: "Tab space bypass" });
|
|
6046
6495
|
variants.push({ payload: payload.replace(/ /g, "<"), transform: "redirect", description: "Redirect as separator" });
|
|
@@ -6062,52 +6511,52 @@ function mutatePayload(request) {
|
|
|
6062
6511
|
}
|
|
6063
6512
|
function getDefaultTransforms(context) {
|
|
6064
6513
|
switch (context) {
|
|
6065
|
-
case
|
|
6066
|
-
case
|
|
6067
|
-
return [
|
|
6068
|
-
case
|
|
6069
|
-
return [
|
|
6070
|
-
case
|
|
6071
|
-
return [
|
|
6072
|
-
case
|
|
6073
|
-
return [
|
|
6074
|
-
case
|
|
6075
|
-
case
|
|
6076
|
-
return [
|
|
6077
|
-
case
|
|
6078
|
-
return [
|
|
6079
|
-
case
|
|
6080
|
-
return [
|
|
6081
|
-
case
|
|
6082
|
-
return [
|
|
6083
|
-
case
|
|
6084
|
-
case
|
|
6085
|
-
return [
|
|
6514
|
+
case PAYLOAD_CONTEXT.URL_PATH:
|
|
6515
|
+
case PAYLOAD_CONTEXT.URL_PARAM:
|
|
6516
|
+
return [TRANSFORM_TYPE.URL, TRANSFORM_TYPE.DOUBLE_URL, TRANSFORM_TYPE.UNICODE, TRANSFORM_TYPE.UTF8_OVERLONG, TRANSFORM_TYPE.MIXED_ENCODING, TRANSFORM_TYPE.NULL_BYTE];
|
|
6517
|
+
case PAYLOAD_CONTEXT.HTML_BODY:
|
|
6518
|
+
return [TRANSFORM_TYPE.HTML_ENTITY_DEC, TRANSFORM_TYPE.HTML_ENTITY_HEX, TRANSFORM_TYPE.UNICODE, TRANSFORM_TYPE.TAG_ALTERNATIVE, TRANSFORM_TYPE.EVENT_HANDLER, TRANSFORM_TYPE.JS_ALTERNATIVE];
|
|
6519
|
+
case PAYLOAD_CONTEXT.HTML_ATTR:
|
|
6520
|
+
return [TRANSFORM_TYPE.HTML_ENTITY_DEC, TRANSFORM_TYPE.HTML_ENTITY_HEX, TRANSFORM_TYPE.EVENT_HANDLER];
|
|
6521
|
+
case PAYLOAD_CONTEXT.JS_STRING:
|
|
6522
|
+
return [TRANSFORM_TYPE.UNICODE, TRANSFORM_TYPE.HEX, TRANSFORM_TYPE.BASE64, TRANSFORM_TYPE.JS_ALTERNATIVE];
|
|
6523
|
+
case PAYLOAD_CONTEXT.SQL_STRING:
|
|
6524
|
+
case PAYLOAD_CONTEXT.SQL_NUMERIC:
|
|
6525
|
+
return [TRANSFORM_TYPE.CASE_SWAP, TRANSFORM_TYPE.COMMENT_INSERT, TRANSFORM_TYPE.WHITESPACE_ALT, TRANSFORM_TYPE.CHAR_FUNCTION, TRANSFORM_TYPE.CONCAT_SPLIT, TRANSFORM_TYPE.URL, TRANSFORM_TYPE.DOUBLE_URL];
|
|
6526
|
+
case PAYLOAD_CONTEXT.SHELL_CMD:
|
|
6527
|
+
return [TRANSFORM_TYPE.KEYWORD_BYPASS, TRANSFORM_TYPE.SPACE_BYPASS, TRANSFORM_TYPE.BASE64, TRANSFORM_TYPE.REVERSE, TRANSFORM_TYPE.HEX, TRANSFORM_TYPE.OCTAL];
|
|
6528
|
+
case PAYLOAD_CONTEXT.XML_BODY:
|
|
6529
|
+
return [TRANSFORM_TYPE.HTML_ENTITY_DEC, TRANSFORM_TYPE.HTML_ENTITY_HEX, TRANSFORM_TYPE.UNICODE];
|
|
6530
|
+
case PAYLOAD_CONTEXT.JSON_BODY:
|
|
6531
|
+
return [TRANSFORM_TYPE.UNICODE];
|
|
6532
|
+
case PAYLOAD_CONTEXT.HTTP_HEADER:
|
|
6533
|
+
case PAYLOAD_CONTEXT.COOKIE:
|
|
6534
|
+
return [TRANSFORM_TYPE.URL, TRANSFORM_TYPE.DOUBLE_URL, TRANSFORM_TYPE.BASE64];
|
|
6086
6535
|
default:
|
|
6087
|
-
return [
|
|
6536
|
+
return [TRANSFORM_TYPE.URL, TRANSFORM_TYPE.DOUBLE_URL, TRANSFORM_TYPE.HTML_ENTITY_DEC, TRANSFORM_TYPE.UNICODE, TRANSFORM_TYPE.BASE64, TRANSFORM_TYPE.CASE_SWAP];
|
|
6088
6537
|
}
|
|
6089
6538
|
}
|
|
6090
6539
|
function getContextRecommendations(context, variantCount) {
|
|
6091
6540
|
const recs = [];
|
|
6092
6541
|
recs.push(`Generated ${variantCount} variants for context: ${context}`);
|
|
6093
6542
|
switch (context) {
|
|
6094
|
-
case
|
|
6543
|
+
case PAYLOAD_CONTEXT.URL_PARAM:
|
|
6095
6544
|
recs.push("If all URL encoding fails: try HTTP Parameter Pollution (send same param twice)");
|
|
6096
6545
|
recs.push("Try switching GET \u2192 POST or changing Content-Type");
|
|
6097
6546
|
recs.push("Check if WebSocket endpoint exists (often unfiltered)");
|
|
6098
6547
|
break;
|
|
6099
|
-
case
|
|
6100
|
-
case
|
|
6548
|
+
case PAYLOAD_CONTEXT.SQL_STRING:
|
|
6549
|
+
case PAYLOAD_CONTEXT.SQL_NUMERIC:
|
|
6101
6550
|
recs.push("If inline comments fail: try MySQL version comments /*!50000SELECT*/");
|
|
6102
6551
|
recs.push("Try time-based blind if error/union payloads are blocked");
|
|
6103
6552
|
recs.push("Use sqlmap with --tamper scripts for automated bypass");
|
|
6104
6553
|
break;
|
|
6105
|
-
case
|
|
6554
|
+
case PAYLOAD_CONTEXT.HTML_BODY:
|
|
6106
6555
|
recs.push("If common XSS tags blocked: try SVG, MathML, mutation XSS");
|
|
6107
6556
|
recs.push("Check CSP header \u2014 it determines what JS execution is possible");
|
|
6108
6557
|
recs.push("Try DOM-based XSS via document.location, innerHTML, postMessage");
|
|
6109
6558
|
break;
|
|
6110
|
-
case
|
|
6559
|
+
case PAYLOAD_CONTEXT.SHELL_CMD:
|
|
6111
6560
|
recs.push("Use ${IFS} for space, quotes for keyword bypass, base64 for full obfuscation");
|
|
6112
6561
|
recs.push("Try: echo PAYLOAD_BASE64 | base64 -d | sh");
|
|
6113
6562
|
recs.push("Variable concatenation: a=c;b=at;$a$b /etc/passwd");
|
|
@@ -6237,8 +6686,8 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
|
|
|
6237
6686
|
}
|
|
6238
6687
|
},
|
|
6239
6688
|
execute: async (p) => {
|
|
6240
|
-
const { existsSync:
|
|
6241
|
-
const { join:
|
|
6689
|
+
const { existsSync: existsSync10, statSync: statSync2, readdirSync: readdirSync3 } = await import("fs");
|
|
6690
|
+
const { join: join12 } = await import("path");
|
|
6242
6691
|
const category = p.category || "";
|
|
6243
6692
|
const search = p.search || "";
|
|
6244
6693
|
const minSize = p.min_size || 0;
|
|
@@ -6284,7 +6733,7 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
|
|
|
6284
6733
|
results.push("");
|
|
6285
6734
|
};
|
|
6286
6735
|
const scanDir = (dirPath, maxDepth = 3, depth = 0) => {
|
|
6287
|
-
if (depth > maxDepth || !
|
|
6736
|
+
if (depth > maxDepth || !existsSync10(dirPath)) return;
|
|
6288
6737
|
let entries;
|
|
6289
6738
|
try {
|
|
6290
6739
|
entries = readdirSync3(dirPath, { withFileTypes: true });
|
|
@@ -6293,7 +6742,7 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
|
|
|
6293
6742
|
}
|
|
6294
6743
|
for (const entry of entries) {
|
|
6295
6744
|
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name)) continue;
|
|
6296
|
-
const fullPath =
|
|
6745
|
+
const fullPath = join12(dirPath, entry.name);
|
|
6297
6746
|
if (entry.isDirectory()) {
|
|
6298
6747
|
scanDir(fullPath, maxDepth, depth + 1);
|
|
6299
6748
|
continue;
|
|
@@ -6345,6 +6794,9 @@ var globalCredentialHandler = null;
|
|
|
6345
6794
|
function setCredentialHandler(handler) {
|
|
6346
6795
|
globalCredentialHandler = handler;
|
|
6347
6796
|
}
|
|
6797
|
+
function clearCredentialHandler() {
|
|
6798
|
+
globalCredentialHandler = null;
|
|
6799
|
+
}
|
|
6348
6800
|
async function requestCredential(request) {
|
|
6349
6801
|
if (!globalCredentialHandler) {
|
|
6350
6802
|
return {
|
|
@@ -7542,6 +7994,11 @@ var FILE_SHARING_CONFIG = {
|
|
|
7542
7994
|
};
|
|
7543
7995
|
|
|
7544
7996
|
// src/domains/cloud/tools.ts
|
|
7997
|
+
var CLOUD_PROVIDER = {
|
|
7998
|
+
AWS: "aws",
|
|
7999
|
+
AZURE: "azure",
|
|
8000
|
+
GCP: "gcp"
|
|
8001
|
+
};
|
|
7545
8002
|
var CLOUD_TOOLS = [
|
|
7546
8003
|
{
|
|
7547
8004
|
name: TOOL_NAMES.AWS_S3_CHECK,
|
|
@@ -7564,8 +8021,8 @@ var CLOUD_TOOLS = [
|
|
|
7564
8021
|
required: ["provider"],
|
|
7565
8022
|
execute: async (params) => {
|
|
7566
8023
|
const provider = params.provider;
|
|
7567
|
-
if (provider ===
|
|
7568
|
-
if (provider ===
|
|
8024
|
+
if (provider === CLOUD_PROVIDER.AWS) return await runCommand("curl", ["http://169.254.169.254/latest/meta-data/"]);
|
|
8025
|
+
if (provider === CLOUD_PROVIDER.AZURE) return await runCommand("curl", ["-H", "Metadata:true", "http://169.254.169.254/metadata/instance?api-version=2021-02-01"]);
|
|
7569
8026
|
return await runCommand("curl", ["-H", "Metadata-Flavor: Google", "http://metadata.google.internal/computeMetadata/v1/"]);
|
|
7570
8027
|
}
|
|
7571
8028
|
}
|
|
@@ -8223,6 +8680,12 @@ var LLM_ERROR_TYPES = {
|
|
|
8223
8680
|
|
|
8224
8681
|
// src/engine/llm-types.ts
|
|
8225
8682
|
var HTTP_STATUS = { BAD_REQUEST: 400, UNAUTHORIZED: 401, FORBIDDEN: 403, RATE_LIMIT: 429 };
|
|
8683
|
+
var NETWORK_ERROR_CODES = {
|
|
8684
|
+
ECONNRESET: "ECONNRESET",
|
|
8685
|
+
ETIMEDOUT: "ETIMEDOUT",
|
|
8686
|
+
ENOTFOUND: "ENOTFOUND",
|
|
8687
|
+
CONNECT_TIMEOUT: "UND_ERR_CONNECT_TIMEOUT"
|
|
8688
|
+
};
|
|
8226
8689
|
var LLMError = class extends Error {
|
|
8227
8690
|
/** Structured error information */
|
|
8228
8691
|
errorInfo;
|
|
@@ -8245,10 +8708,10 @@ function classifyError(error) {
|
|
|
8245
8708
|
if (statusCode === HTTP_STATUS.BAD_REQUEST) {
|
|
8246
8709
|
return { type: LLM_ERROR_TYPES.INVALID_REQUEST, message: errorMessage, statusCode, isRetryable: false, suggestedAction: "Modify request" };
|
|
8247
8710
|
}
|
|
8248
|
-
if (e.code ===
|
|
8711
|
+
if (e.code === NETWORK_ERROR_CODES.ECONNRESET || e.code === NETWORK_ERROR_CODES.ETIMEDOUT || e.code === NETWORK_ERROR_CODES.ENOTFOUND) {
|
|
8249
8712
|
return { type: LLM_ERROR_TYPES.NETWORK_ERROR, message: errorMessage, isRetryable: true, suggestedAction: "Check network" };
|
|
8250
8713
|
}
|
|
8251
|
-
if (errorMessage.toLowerCase().includes("timeout") || e.code ===
|
|
8714
|
+
if (errorMessage.toLowerCase().includes("timeout") || e.code === NETWORK_ERROR_CODES.CONNECT_TIMEOUT) {
|
|
8252
8715
|
return { type: LLM_ERROR_TYPES.TIMEOUT, message: errorMessage, isRetryable: true, suggestedAction: "Retry" };
|
|
8253
8716
|
}
|
|
8254
8717
|
return { type: LLM_ERROR_TYPES.UNKNOWN, message: errorMessage, statusCode, isRetryable: false, suggestedAction: "Analyze error" };
|
|
@@ -8594,7 +9057,7 @@ var __filename2 = fileURLToPath3(import.meta.url);
|
|
|
8594
9057
|
var __dirname3 = dirname4(__filename2);
|
|
8595
9058
|
|
|
8596
9059
|
// src/engine/state-persistence.ts
|
|
8597
|
-
import { writeFileSync as writeFileSync6, readFileSync as readFileSync4, existsSync as existsSync6, readdirSync, statSync, unlinkSync as unlinkSync4 } from "fs";
|
|
9060
|
+
import { writeFileSync as writeFileSync6, readFileSync as readFileSync4, existsSync as existsSync6, readdirSync, statSync, unlinkSync as unlinkSync4, rmSync } from "fs";
|
|
8598
9061
|
import { join as join9 } from "path";
|
|
8599
9062
|
function saveState(state) {
|
|
8600
9063
|
const sessionsDir = WORKSPACE.SESSIONS;
|
|
@@ -8676,6 +9139,28 @@ function loadState(state) {
|
|
|
8676
9139
|
return false;
|
|
8677
9140
|
}
|
|
8678
9141
|
}
|
|
9142
|
+
function clearWorkspace() {
|
|
9143
|
+
const cleared = [];
|
|
9144
|
+
const errors = [];
|
|
9145
|
+
const dirsToClean = [
|
|
9146
|
+
{ path: WORKSPACE.SESSIONS, label: "sessions" },
|
|
9147
|
+
{ path: WORKSPACE.DEBUG, label: "debug logs" },
|
|
9148
|
+
{ path: WORKSPACE.TEMP, label: "temp files" },
|
|
9149
|
+
{ path: WORKSPACE.OUTPUTS, label: "outputs" }
|
|
9150
|
+
];
|
|
9151
|
+
for (const dir of dirsToClean) {
|
|
9152
|
+
try {
|
|
9153
|
+
if (existsSync6(dir.path)) {
|
|
9154
|
+
rmSync(dir.path, { recursive: true, force: true });
|
|
9155
|
+
ensureDirExists(dir.path);
|
|
9156
|
+
cleared.push(dir.label);
|
|
9157
|
+
}
|
|
9158
|
+
} catch (err) {
|
|
9159
|
+
errors.push(`${dir.label}: ${err instanceof Error ? err.message : String(err)}`);
|
|
9160
|
+
}
|
|
9161
|
+
}
|
|
9162
|
+
return { cleared, errors };
|
|
9163
|
+
}
|
|
8679
9164
|
|
|
8680
9165
|
// src/agents/tool-error-enrichment.ts
|
|
8681
9166
|
function enrichToolErrorContext(ctx) {
|
|
@@ -10199,13 +10684,21 @@ var PHASE_TECHNIQUE_MAP = {
|
|
|
10199
10684
|
};
|
|
10200
10685
|
var PromptBuilder = class {
|
|
10201
10686
|
state;
|
|
10687
|
+
strategist = null;
|
|
10202
10688
|
constructor(state) {
|
|
10203
10689
|
this.state = state;
|
|
10204
10690
|
}
|
|
10205
10691
|
/**
|
|
10206
|
-
*
|
|
10692
|
+
* Attach a Strategist instance for meta-prompting.
|
|
10693
|
+
* Called by MainAgent after construction.
|
|
10694
|
+
*/
|
|
10695
|
+
setStrategist(strategist) {
|
|
10696
|
+
this.strategist = strategist;
|
|
10697
|
+
}
|
|
10698
|
+
/**
|
|
10699
|
+
* Build complete prompt for LLM iteration (async).
|
|
10207
10700
|
*
|
|
10208
|
-
* Layers (phase-aware, enhanced with
|
|
10701
|
+
* Layers (phase-aware, enhanced with D-CIPHER meta-prompting):
|
|
10209
10702
|
* 1. base.md — Core identity, rules, autonomy, shell management (~7.6K tok)
|
|
10210
10703
|
* 2. Phase-specific prompt — current phase's full specialist knowledge (~2K tok)
|
|
10211
10704
|
* 3. Core methodology — strategy, orchestrator, evasion (always loaded, ~12K tok)
|
|
@@ -10221,9 +10714,10 @@ var PromptBuilder = class {
|
|
|
10221
10714
|
* 12. Session timeline (#12: episodic memory)
|
|
10222
10715
|
* 13. Learned techniques (#7: dynamic technique library)
|
|
10223
10716
|
* 14. Persistent memory (#12: cross-session knowledge)
|
|
10224
|
-
* 15.
|
|
10717
|
+
* ★ 15. STRATEGIC DIRECTIVE — LLM-generated tactical instructions (D-CIPHER)
|
|
10718
|
+
* 16. User context
|
|
10225
10719
|
*/
|
|
10226
|
-
build(userInput, phase) {
|
|
10720
|
+
async build(userInput, phase) {
|
|
10227
10721
|
const fragments = [
|
|
10228
10722
|
this.loadPromptFile(PROMPT_PATHS.BASE),
|
|
10229
10723
|
this.loadCtfModePrompt(),
|
|
@@ -10246,10 +10740,14 @@ var PromptBuilder = class {
|
|
|
10246
10740
|
// #12
|
|
10247
10741
|
this.getDynamicTechniquesFragment(),
|
|
10248
10742
|
// #7
|
|
10249
|
-
this.getPersistentMemoryFragment()
|
|
10743
|
+
this.getPersistentMemoryFragment()
|
|
10250
10744
|
// #12
|
|
10251
|
-
PROMPT_DEFAULTS.USER_CONTEXT(userInput)
|
|
10252
10745
|
];
|
|
10746
|
+
const strategistDirective = await this.getStrategistFragment();
|
|
10747
|
+
if (strategistDirective) {
|
|
10748
|
+
fragments.push(strategistDirective);
|
|
10749
|
+
}
|
|
10750
|
+
fragments.push(PROMPT_DEFAULTS.USER_CONTEXT(userInput));
|
|
10253
10751
|
return fragments.filter((f) => !!f).join("\n\n");
|
|
10254
10752
|
}
|
|
10255
10753
|
/**
|
|
@@ -10439,11 +10937,213 @@ ${lines.join("\n")}
|
|
|
10439
10937
|
}
|
|
10440
10938
|
return this.state.persistentMemory.toPrompt(services);
|
|
10441
10939
|
}
|
|
10940
|
+
// --- D-CIPHER: Strategist Meta-Prompting ---
|
|
10941
|
+
/**
|
|
10942
|
+
* Generate strategic directive via Strategist LLM.
|
|
10943
|
+
* Called every turn. Returns '' if no strategist is attached or call fails.
|
|
10944
|
+
*/
|
|
10945
|
+
async getStrategistFragment() {
|
|
10946
|
+
if (!this.strategist) return "";
|
|
10947
|
+
return this.strategist.generateDirective();
|
|
10948
|
+
}
|
|
10949
|
+
};
|
|
10950
|
+
|
|
10951
|
+
// src/agents/strategist.ts
|
|
10952
|
+
import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
|
|
10953
|
+
import { join as join11, dirname as dirname6 } from "path";
|
|
10954
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10955
|
+
|
|
10956
|
+
// src/shared/constants/strategist.ts
|
|
10957
|
+
var STRATEGIST_LIMITS = {
|
|
10958
|
+
/** Maximum characters of state context sent to Strategist LLM.
|
|
10959
|
+
* WHY: Keeps Strategist input focused and affordable (~3-5K tokens).
|
|
10960
|
+
* Full state can be 20K+; Strategist needs summary, not everything. */
|
|
10961
|
+
MAX_INPUT_CHARS: 15e3,
|
|
10962
|
+
/** Maximum characters for the Strategist's response.
|
|
10963
|
+
* WHY: Directives should be terse and actionable (~500-800 tokens).
|
|
10964
|
+
* Longer = more cost, less focus. */
|
|
10965
|
+
MAX_OUTPUT_CHARS: 3e3,
|
|
10966
|
+
/** Maximum lines in the directive output.
|
|
10967
|
+
* WHY: Forces concise, prioritized directives. */
|
|
10968
|
+
MAX_DIRECTIVE_LINES: 40
|
|
10969
|
+
};
|
|
10970
|
+
|
|
10971
|
+
// src/agents/strategist.ts
|
|
10972
|
+
var __dirname5 = dirname6(fileURLToPath5(import.meta.url));
|
|
10973
|
+
var STRATEGIST_PROMPT_PATH = join11(__dirname5, "prompts", "strategist-system.md");
|
|
10974
|
+
var Strategist = class {
|
|
10975
|
+
llm;
|
|
10976
|
+
state;
|
|
10977
|
+
systemPrompt;
|
|
10978
|
+
// Metrics
|
|
10979
|
+
totalTokenCost = 0;
|
|
10980
|
+
totalCalls = 0;
|
|
10981
|
+
lastDirective = null;
|
|
10982
|
+
constructor(llm, state) {
|
|
10983
|
+
this.llm = llm;
|
|
10984
|
+
this.state = state;
|
|
10985
|
+
this.systemPrompt = this.loadSystemPrompt();
|
|
10986
|
+
}
|
|
10987
|
+
/**
|
|
10988
|
+
* Generate a fresh strategic directive for this turn.
|
|
10989
|
+
* Called every iteration by PromptBuilder.
|
|
10990
|
+
*
|
|
10991
|
+
* @returns Formatted directive string for prompt injection, or '' on failure
|
|
10992
|
+
*/
|
|
10993
|
+
async generateDirective() {
|
|
10994
|
+
try {
|
|
10995
|
+
const input = this.buildInput();
|
|
10996
|
+
const directive = await this.callLLM(input);
|
|
10997
|
+
this.lastDirective = directive;
|
|
10998
|
+
this.totalCalls++;
|
|
10999
|
+
debugLog("general", "Strategist directive generated", {
|
|
11000
|
+
tokens: directive.tokenCost,
|
|
11001
|
+
totalCalls: this.totalCalls,
|
|
11002
|
+
contentLength: directive.content.length
|
|
11003
|
+
});
|
|
11004
|
+
return this.formatForPrompt(directive);
|
|
11005
|
+
} catch (err) {
|
|
11006
|
+
debugLog("general", "Strategist failed \u2014 agent will proceed without directive", {
|
|
11007
|
+
error: String(err)
|
|
11008
|
+
});
|
|
11009
|
+
if (this.lastDirective?.content) {
|
|
11010
|
+
return this.formatForPrompt(this.lastDirective, true);
|
|
11011
|
+
}
|
|
11012
|
+
return "";
|
|
11013
|
+
}
|
|
11014
|
+
}
|
|
11015
|
+
// ─── Input Construction ─────────────────────────────────────
|
|
11016
|
+
/**
|
|
11017
|
+
* Build the user message for the Strategist LLM.
|
|
11018
|
+
* Assembles state, memory, attack graph, and timeline into a focused context.
|
|
11019
|
+
*/
|
|
11020
|
+
buildInput() {
|
|
11021
|
+
const sections = [];
|
|
11022
|
+
sections.push("## Engagement State");
|
|
11023
|
+
sections.push(this.state.toPrompt());
|
|
11024
|
+
const timeline = this.state.episodicMemory.toPrompt();
|
|
11025
|
+
if (timeline) {
|
|
11026
|
+
sections.push("");
|
|
11027
|
+
sections.push("## Recent Actions");
|
|
11028
|
+
sections.push(timeline);
|
|
11029
|
+
}
|
|
11030
|
+
const failures = this.state.workingMemory.toPrompt();
|
|
11031
|
+
if (failures) {
|
|
11032
|
+
sections.push("");
|
|
11033
|
+
sections.push("## Failed Attempts (DO NOT REPEAT THESE)");
|
|
11034
|
+
sections.push(failures);
|
|
11035
|
+
}
|
|
11036
|
+
const graph = this.state.attackGraph.toPrompt();
|
|
11037
|
+
if (graph) {
|
|
11038
|
+
sections.push("");
|
|
11039
|
+
sections.push("## Attack Graph");
|
|
11040
|
+
sections.push(graph);
|
|
11041
|
+
}
|
|
11042
|
+
const techniques = this.state.dynamicTechniques.toPrompt();
|
|
11043
|
+
if (techniques) {
|
|
11044
|
+
sections.push("");
|
|
11045
|
+
sections.push("## Learned Techniques");
|
|
11046
|
+
sections.push(techniques);
|
|
11047
|
+
}
|
|
11048
|
+
sections.push("");
|
|
11049
|
+
sections.push("## Time");
|
|
11050
|
+
sections.push(this.state.getTimeStatus());
|
|
11051
|
+
const analysis = this.state.getChallengeAnalysis();
|
|
11052
|
+
if (analysis && analysis.primaryType !== "unknown") {
|
|
11053
|
+
sections.push("");
|
|
11054
|
+
sections.push(`## Challenge Type: ${analysis.primaryType.toUpperCase()} (${(analysis.confidence * 100).toFixed(0)}%)`);
|
|
11055
|
+
sections.push(analysis.strategySuggestion);
|
|
11056
|
+
}
|
|
11057
|
+
let input = sections.join("\n");
|
|
11058
|
+
if (input.length > STRATEGIST_LIMITS.MAX_INPUT_CHARS) {
|
|
11059
|
+
input = input.slice(0, STRATEGIST_LIMITS.MAX_INPUT_CHARS) + "\n\n... [state truncated for Strategist context]";
|
|
11060
|
+
}
|
|
11061
|
+
return input;
|
|
11062
|
+
}
|
|
11063
|
+
// ─── LLM Call ───────────────────────────────────────────────
|
|
11064
|
+
async callLLM(input) {
|
|
11065
|
+
const messages = [{
|
|
11066
|
+
role: "user",
|
|
11067
|
+
content: `Analyze this penetration test situation and write a tactical directive for the attack agent.
|
|
11068
|
+
|
|
11069
|
+
${input}`
|
|
11070
|
+
}];
|
|
11071
|
+
const response = await this.llm.generateResponse(
|
|
11072
|
+
messages,
|
|
11073
|
+
void 0,
|
|
11074
|
+
// No tools — strategy generation only
|
|
11075
|
+
this.systemPrompt
|
|
11076
|
+
);
|
|
11077
|
+
let content = response.content || "";
|
|
11078
|
+
if (content.length > STRATEGIST_LIMITS.MAX_OUTPUT_CHARS) {
|
|
11079
|
+
content = content.slice(0, STRATEGIST_LIMITS.MAX_OUTPUT_CHARS) + "\n... [directive truncated]";
|
|
11080
|
+
}
|
|
11081
|
+
const cost = response.usage ? response.usage.input_tokens + response.usage.output_tokens : 0;
|
|
11082
|
+
this.totalTokenCost += cost;
|
|
11083
|
+
return {
|
|
11084
|
+
content,
|
|
11085
|
+
generatedAt: Date.now(),
|
|
11086
|
+
tokenCost: cost
|
|
11087
|
+
};
|
|
11088
|
+
}
|
|
11089
|
+
// ─── Formatting ─────────────────────────────────────────────
|
|
11090
|
+
/**
|
|
11091
|
+
* Format directive for injection into the attack agent's system prompt.
|
|
11092
|
+
*/
|
|
11093
|
+
formatForPrompt(directive, isStale = false) {
|
|
11094
|
+
if (!directive.content) return "";
|
|
11095
|
+
const age = Math.floor((Date.now() - directive.generatedAt) / 6e4);
|
|
11096
|
+
const staleWarning = isStale ? `
|
|
11097
|
+
NOTE: This directive is from ${age}min ago (Strategist call failed this turn). Verify assumptions are still valid.` : "";
|
|
11098
|
+
return [
|
|
11099
|
+
"<strategic-directive>",
|
|
11100
|
+
"TACTICAL DIRECTIVE (generated by Strategist LLM \u2014 follow these priorities):",
|
|
11101
|
+
"",
|
|
11102
|
+
directive.content,
|
|
11103
|
+
staleWarning,
|
|
11104
|
+
"</strategic-directive>"
|
|
11105
|
+
].filter(Boolean).join("\n");
|
|
11106
|
+
}
|
|
11107
|
+
// ─── System Prompt Loading ──────────────────────────────────
|
|
11108
|
+
loadSystemPrompt() {
|
|
11109
|
+
try {
|
|
11110
|
+
if (existsSync9(STRATEGIST_PROMPT_PATH)) {
|
|
11111
|
+
return readFileSync6(STRATEGIST_PROMPT_PATH, "utf-8");
|
|
11112
|
+
}
|
|
11113
|
+
} catch {
|
|
11114
|
+
}
|
|
11115
|
+
return FALLBACK_SYSTEM_PROMPT;
|
|
11116
|
+
}
|
|
11117
|
+
// ─── Public API ─────────────────────────────────────────────
|
|
11118
|
+
/** Get total token cost of all Strategist calls this session. */
|
|
11119
|
+
getTotalTokenCost() {
|
|
11120
|
+
return this.totalTokenCost;
|
|
11121
|
+
}
|
|
11122
|
+
/** Get number of Strategist calls this session. */
|
|
11123
|
+
getTotalCalls() {
|
|
11124
|
+
return this.totalCalls;
|
|
11125
|
+
}
|
|
11126
|
+
/** Get the most recent directive (for TUI display). */
|
|
11127
|
+
getLastDirective() {
|
|
11128
|
+
return this.lastDirective;
|
|
11129
|
+
}
|
|
11130
|
+
/** Reset strategist state (for /clear command). */
|
|
11131
|
+
reset() {
|
|
11132
|
+
this.lastDirective = null;
|
|
11133
|
+
this.totalTokenCost = 0;
|
|
11134
|
+
this.totalCalls = 0;
|
|
11135
|
+
}
|
|
10442
11136
|
};
|
|
11137
|
+
var FALLBACK_SYSTEM_PROMPT = `You are a penetration testing strategist.
|
|
11138
|
+
Analyze the situation and write a precise tactical directive for the attack agent.
|
|
11139
|
+
Be specific: name exact tools, commands, and parameters.
|
|
11140
|
+
Maximum 30 lines. Prioritize by probability of success.
|
|
11141
|
+
NEVER suggest approaches listed in the failed attempts section.`;
|
|
10443
11142
|
|
|
10444
11143
|
// src/agents/main-agent.ts
|
|
10445
11144
|
var MainAgent = class extends CoreAgent {
|
|
10446
11145
|
promptBuilder;
|
|
11146
|
+
strategist;
|
|
10447
11147
|
approvalGate;
|
|
10448
11148
|
scopeGuard;
|
|
10449
11149
|
userInput = "";
|
|
@@ -10452,6 +11152,8 @@ var MainAgent = class extends CoreAgent {
|
|
|
10452
11152
|
this.approvalGate = approvalGate;
|
|
10453
11153
|
this.scopeGuard = scopeGuard;
|
|
10454
11154
|
this.promptBuilder = new PromptBuilder(state);
|
|
11155
|
+
this.strategist = new Strategist(this.llm, state);
|
|
11156
|
+
this.promptBuilder.setStrategist(this.strategist);
|
|
10455
11157
|
}
|
|
10456
11158
|
/**
|
|
10457
11159
|
* Public entry point for running the agent.
|
|
@@ -10462,7 +11164,8 @@ var MainAgent = class extends CoreAgent {
|
|
|
10462
11164
|
this.emitStart(userInput);
|
|
10463
11165
|
this.initializeTask();
|
|
10464
11166
|
try {
|
|
10465
|
-
const
|
|
11167
|
+
const initialPrompt = await this.getCurrentPrompt();
|
|
11168
|
+
const result2 = await this.run(userInput, initialPrompt);
|
|
10466
11169
|
return result2.output;
|
|
10467
11170
|
} finally {
|
|
10468
11171
|
try {
|
|
@@ -10478,9 +11181,10 @@ var MainAgent = class extends CoreAgent {
|
|
|
10478
11181
|
/**
|
|
10479
11182
|
* Override step to rebuild prompt dynamically each iteration.
|
|
10480
11183
|
* This ensures the agent always sees the latest state, phase, and active processes.
|
|
11184
|
+
* The Strategist LLM generates a fresh tactical directive every turn.
|
|
10481
11185
|
*/
|
|
10482
11186
|
async step(iteration, messages, _unusedPrompt, progress) {
|
|
10483
|
-
const dynamicPrompt = this.getCurrentPrompt();
|
|
11187
|
+
const dynamicPrompt = await this.getCurrentPrompt();
|
|
10484
11188
|
const result2 = await super.step(iteration, messages, dynamicPrompt, progress);
|
|
10485
11189
|
this.emitStateChange();
|
|
10486
11190
|
return result2;
|
|
@@ -10509,7 +11213,7 @@ var MainAgent = class extends CoreAgent {
|
|
|
10509
11213
|
saveCurrentState() {
|
|
10510
11214
|
return saveState(this.state);
|
|
10511
11215
|
}
|
|
10512
|
-
getCurrentPrompt() {
|
|
11216
|
+
async getCurrentPrompt() {
|
|
10513
11217
|
const phase = this.state.getPhase() || PHASES.RECON;
|
|
10514
11218
|
return this.promptBuilder.build(this.userInput, phase);
|
|
10515
11219
|
}
|
|
@@ -10554,6 +11258,18 @@ var MainAgent = class extends CoreAgent {
|
|
|
10554
11258
|
isCtfMode() {
|
|
10555
11259
|
return this.state.isCtfMode();
|
|
10556
11260
|
}
|
|
11261
|
+
/**
|
|
11262
|
+
* Full session reset — clears state, workspace files, and background processes.
|
|
11263
|
+
* Used by /clear command for a complete fresh start.
|
|
11264
|
+
*/
|
|
11265
|
+
async resetSession() {
|
|
11266
|
+
await cleanupAllProcesses().catch(() => {
|
|
11267
|
+
});
|
|
11268
|
+
this.state.reset();
|
|
11269
|
+
this.userInput = "";
|
|
11270
|
+
this.strategist.reset();
|
|
11271
|
+
return clearWorkspace();
|
|
11272
|
+
}
|
|
10557
11273
|
setScope(allowed, exclusions = []) {
|
|
10558
11274
|
this.state.setScope({
|
|
10559
11275
|
allowedCidrs: allowed.filter((a) => a.includes("/")),
|
|
@@ -10573,6 +11289,10 @@ var MainAgent = class extends CoreAgent {
|
|
|
10573
11289
|
});
|
|
10574
11290
|
this.emitStateChange();
|
|
10575
11291
|
}
|
|
11292
|
+
/** Get the Strategist instance (for TUI metrics display). */
|
|
11293
|
+
getStrategist() {
|
|
11294
|
+
return this.strategist;
|
|
11295
|
+
}
|
|
10576
11296
|
};
|
|
10577
11297
|
|
|
10578
11298
|
// src/agents/factory.ts
|
|
@@ -10668,13 +11388,13 @@ var THEME = {
|
|
|
10668
11388
|
secondary: "#cbd5e1",
|
|
10669
11389
|
// Brighter slate
|
|
10670
11390
|
muted: "#94a3b8",
|
|
10671
|
-
// Lighter blue-gray
|
|
10672
|
-
accent: "#
|
|
10673
|
-
//
|
|
11391
|
+
// Lighter blue-gray
|
|
11392
|
+
accent: "#1d4ed8",
|
|
11393
|
+
// Blue 700 - deep blue
|
|
10674
11394
|
highlight: "#ffffff"
|
|
10675
11395
|
// Pure white
|
|
10676
11396
|
},
|
|
10677
|
-
// Status colors
|
|
11397
|
+
// Status colors (deep blue-focused)
|
|
10678
11398
|
status: {
|
|
10679
11399
|
success: "#10b981",
|
|
10680
11400
|
// Emerald
|
|
@@ -10682,10 +11402,10 @@ var THEME = {
|
|
|
10682
11402
|
// Amber
|
|
10683
11403
|
error: "#ef4444",
|
|
10684
11404
|
// Red
|
|
10685
|
-
info: "#
|
|
10686
|
-
//
|
|
10687
|
-
running: "#
|
|
10688
|
-
//
|
|
11405
|
+
info: "#1d4ed8",
|
|
11406
|
+
// Blue 700
|
|
11407
|
+
running: "#1e40af",
|
|
11408
|
+
// Blue 800 (AI activity)
|
|
10689
11409
|
pending: "#64748b"
|
|
10690
11410
|
// Slate
|
|
10691
11411
|
},
|
|
@@ -10699,27 +11419,29 @@ var THEME = {
|
|
|
10699
11419
|
// Orange
|
|
10700
11420
|
low: "#eab308",
|
|
10701
11421
|
// Yellow
|
|
10702
|
-
info: "#
|
|
10703
|
-
// Blue
|
|
11422
|
+
info: "#1d4ed8"
|
|
11423
|
+
// Blue 700
|
|
10704
11424
|
},
|
|
10705
11425
|
// Border colors
|
|
10706
11426
|
border: {
|
|
10707
11427
|
default: "#1e293b",
|
|
10708
|
-
focus: "#
|
|
11428
|
+
focus: "#3b82f6",
|
|
11429
|
+
// Blue 500 (UI decorative)
|
|
10709
11430
|
error: "#ef4444",
|
|
10710
11431
|
success: "#22c55e"
|
|
10711
11432
|
},
|
|
10712
|
-
// Phase colors
|
|
11433
|
+
// Phase colors (deep blue-focused)
|
|
10713
11434
|
phase: {
|
|
10714
11435
|
recon: "#94a3b8",
|
|
10715
|
-
enum: "#
|
|
11436
|
+
enum: "#1d4ed8",
|
|
11437
|
+
// Blue 700 (phase indicator)
|
|
10716
11438
|
vuln: "#f59e0b",
|
|
10717
11439
|
exploit: "#ef4444",
|
|
10718
11440
|
privesc: "#8b5cf6",
|
|
10719
11441
|
persist: "#22c55e",
|
|
10720
11442
|
report: "#64748b"
|
|
10721
11443
|
},
|
|
10722
|
-
// Accent colors
|
|
11444
|
+
// Accent colors (NO cyan/teal - pure blue palette)
|
|
10723
11445
|
accent: {
|
|
10724
11446
|
pink: "#f472b6",
|
|
10725
11447
|
rose: "#fb7185",
|
|
@@ -10727,9 +11449,8 @@ var THEME = {
|
|
|
10727
11449
|
purple: "#a78bfa",
|
|
10728
11450
|
violet: "#8b5cf6",
|
|
10729
11451
|
indigo: "#818cf8",
|
|
10730
|
-
blue: "#
|
|
10731
|
-
|
|
10732
|
-
teal: "#2dd4bf",
|
|
11452
|
+
blue: "#1d4ed8",
|
|
11453
|
+
// Blue 700 - primary accent
|
|
10733
11454
|
emerald: "#34d399",
|
|
10734
11455
|
green: "#4ade80",
|
|
10735
11456
|
lime: "#a3e635",
|
|
@@ -10738,34 +11459,35 @@ var THEME = {
|
|
|
10738
11459
|
orange: "#fb923c",
|
|
10739
11460
|
red: "#f87171"
|
|
10740
11461
|
},
|
|
10741
|
-
// Gradients
|
|
11462
|
+
// Gradients (deep blue-focused)
|
|
10742
11463
|
gradient: {
|
|
10743
11464
|
cyber: [
|
|
10744
|
-
"#
|
|
10745
|
-
//
|
|
10746
|
-
"#
|
|
10747
|
-
"#
|
|
10748
|
-
"#
|
|
10749
|
-
"#
|
|
10750
|
-
"#
|
|
10751
|
-
//
|
|
10752
|
-
"#
|
|
10753
|
-
"#
|
|
10754
|
-
"#
|
|
10755
|
-
|
|
10756
|
-
"#
|
|
10757
|
-
|
|
10758
|
-
// Emerald 400
|
|
11465
|
+
"#3b82f6",
|
|
11466
|
+
// Blue 500
|
|
11467
|
+
"#3584f4",
|
|
11468
|
+
"#2f86f2",
|
|
11469
|
+
"#2988f0",
|
|
11470
|
+
"#238aee",
|
|
11471
|
+
"#1d8cec",
|
|
11472
|
+
// Mid blue
|
|
11473
|
+
"#1d7ad8",
|
|
11474
|
+
"#1d78c6",
|
|
11475
|
+
"#1d76b4",
|
|
11476
|
+
"#1d74a2",
|
|
11477
|
+
"#1e40af"
|
|
11478
|
+
// Blue 800
|
|
10759
11479
|
],
|
|
10760
11480
|
danger: ["#ef4444", "#7f1d1d"],
|
|
10761
11481
|
success: ["#22c55e", "#14532d"],
|
|
10762
11482
|
gold: ["#f59e0b", "#78350f"],
|
|
10763
11483
|
royal: ["#818cf8", "#312e81"]
|
|
10764
11484
|
},
|
|
10765
|
-
// Spinner color (
|
|
10766
|
-
spinner: "#
|
|
10767
|
-
//
|
|
10768
|
-
|
|
11485
|
+
// Spinner color (deep blue — UI feedback)
|
|
11486
|
+
spinner: "#3b82f6",
|
|
11487
|
+
// Blue 500
|
|
11488
|
+
// Identity color (branded accent - deep blue)
|
|
11489
|
+
identity: "#1d4ed8"
|
|
11490
|
+
// Blue 700
|
|
10769
11491
|
};
|
|
10770
11492
|
var ASCII_BANNER = `
|
|
10771
11493
|
____ __ __ _
|
|
@@ -10811,39 +11533,39 @@ var ICONS = {
|
|
|
10811
11533
|
// src/platform/tui/constants/display.ts
|
|
10812
11534
|
var TUI_DISPLAY_LIMITS = {
|
|
10813
11535
|
/** Reasoning buffer size for display */
|
|
10814
|
-
reasoningBuffer:
|
|
11536
|
+
reasoningBuffer: 2e3,
|
|
10815
11537
|
/** Reasoning preview length */
|
|
10816
|
-
reasoningPreview:
|
|
11538
|
+
reasoningPreview: 500,
|
|
10817
11539
|
/** Reasoning history slice for display */
|
|
10818
|
-
reasoningHistorySlice:
|
|
11540
|
+
reasoningHistorySlice: 1e3,
|
|
10819
11541
|
/** Tool input preview length (for command display) */
|
|
10820
|
-
toolInputPreview:
|
|
11542
|
+
toolInputPreview: 200,
|
|
10821
11543
|
/** Tool input truncated length */
|
|
10822
|
-
toolInputTruncated:
|
|
10823
|
-
/** Tool output preview length */
|
|
10824
|
-
toolOutputPreview:
|
|
11544
|
+
toolInputTruncated: 197,
|
|
11545
|
+
/** Tool output preview length - increased to show full output */
|
|
11546
|
+
toolOutputPreview: 2e3,
|
|
10825
11547
|
/** Error message preview length */
|
|
10826
|
-
errorPreview:
|
|
11548
|
+
errorPreview: 500,
|
|
10827
11549
|
/** Status thought preview length */
|
|
10828
|
-
statusThoughtPreview:
|
|
11550
|
+
statusThoughtPreview: 150,
|
|
10829
11551
|
/** Status thought truncated length */
|
|
10830
|
-
statusThoughtTruncated:
|
|
11552
|
+
statusThoughtTruncated: 147,
|
|
10831
11553
|
/** Retry error preview length */
|
|
10832
|
-
retryErrorPreview:
|
|
11554
|
+
retryErrorPreview: 100,
|
|
10833
11555
|
/** Retry error truncated length */
|
|
10834
|
-
retryErrorTruncated:
|
|
11556
|
+
retryErrorTruncated: 97,
|
|
10835
11557
|
/** Timer update interval in ms */
|
|
10836
11558
|
timerInterval: 1e3,
|
|
10837
11559
|
/** Exit delay in ms */
|
|
10838
11560
|
exitDelay: 100,
|
|
10839
11561
|
/** Purpose text max length before truncation */
|
|
10840
|
-
purposeMaxLength:
|
|
11562
|
+
purposeMaxLength: 50,
|
|
10841
11563
|
/** Purpose text truncated length */
|
|
10842
|
-
purposeTruncated:
|
|
11564
|
+
purposeTruncated: 47,
|
|
10843
11565
|
/** Tool detail preview length in flow display */
|
|
10844
|
-
toolDetailPreview:
|
|
11566
|
+
toolDetailPreview: 200,
|
|
10845
11567
|
/** Observe detail preview length in flow display */
|
|
10846
|
-
observeDetailPreview:
|
|
11568
|
+
observeDetailPreview: 150,
|
|
10847
11569
|
/** Max flow nodes to display */
|
|
10848
11570
|
maxFlowNodes: 50,
|
|
10849
11571
|
/** Max stopped processes to show */
|
|
@@ -10858,7 +11580,7 @@ var MESSAGE_STYLES = {
|
|
|
10858
11580
|
error: THEME.status.error,
|
|
10859
11581
|
tool: THEME.status.running,
|
|
10860
11582
|
result: THEME.text.muted,
|
|
10861
|
-
status: THEME.accent.
|
|
11583
|
+
status: THEME.accent.blue
|
|
10862
11584
|
},
|
|
10863
11585
|
prefixes: {
|
|
10864
11586
|
user: "\u276F",
|
|
@@ -10871,30 +11593,46 @@ var MESSAGE_STYLES = {
|
|
|
10871
11593
|
status: "\u25C8"
|
|
10872
11594
|
}
|
|
10873
11595
|
};
|
|
11596
|
+
var COMMAND_DEFINITIONS = [
|
|
11597
|
+
{ name: "target", alias: "t", args: "<ip>", description: "Set target IP or domain" },
|
|
11598
|
+
{ name: "start", alias: "s", args: "[goal]", description: "Start autonomous pentest" },
|
|
11599
|
+
{ name: "findings", alias: "f", description: "Show discovered vulnerabilities" },
|
|
11600
|
+
{ name: "graph", alias: "g", description: "Visualize the attack graph" },
|
|
11601
|
+
{ name: "paths", alias: "p", description: "Show ranked attack paths" },
|
|
11602
|
+
{ name: "assets", alias: "a", description: "List background processes" },
|
|
11603
|
+
{ name: "logs", alias: "l", args: "<id>", description: "Show logs for an asset" },
|
|
11604
|
+
{ name: "ctf", description: "Toggle CTF mode" },
|
|
11605
|
+
{ name: "auto", description: "Toggle auto-approve mode" },
|
|
11606
|
+
{ name: "clear", alias: "c", description: "Reset session & clean workspace" },
|
|
11607
|
+
{ name: "help", alias: "h", description: "Show detailed help" },
|
|
11608
|
+
{ name: "exit", alias: "q", description: "Exit the application" }
|
|
11609
|
+
];
|
|
11610
|
+
function getMatchingCommands(partial) {
|
|
11611
|
+
const lower = partial.toLowerCase();
|
|
11612
|
+
if (!lower) return COMMAND_DEFINITIONS;
|
|
11613
|
+
return COMMAND_DEFINITIONS.filter(
|
|
11614
|
+
(cmd) => cmd.name.startsWith(lower) || cmd.alias && cmd.alias.startsWith(lower)
|
|
11615
|
+
);
|
|
11616
|
+
}
|
|
10874
11617
|
var HELP_TEXT = `
|
|
10875
11618
|
\u2500\u2500 Commands \u2500\u2500
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
/ctf Toggle CTF mode (default: ON)
|
|
10882
|
-
/auto Toggle auto-approve mode
|
|
10883
|
-
/clear Clear screen
|
|
10884
|
-
/exit Exit
|
|
11619
|
+
${COMMAND_DEFINITIONS.map((cmd) => {
|
|
11620
|
+
const usage = `/${cmd.name}${cmd.args ? " " + cmd.args : ""}`;
|
|
11621
|
+
const alias = cmd.alias ? ` (/${cmd.alias})` : "";
|
|
11622
|
+
return `${usage.padEnd(18)}${alias.padEnd(8)} ${cmd.description}`;
|
|
11623
|
+
}).join("\n")}
|
|
10885
11624
|
|
|
10886
11625
|
\u2500\u2500 Features \u2500\u2500
|
|
10887
11626
|
\u2022 Auto-install missing tools (apt/brew)
|
|
10888
11627
|
\u2022 Transparent command execution
|
|
10889
11628
|
\u2022 Interactive sudo password input
|
|
10890
11629
|
\u2022 CTF mode: Auto flag detection & CTF-specific prompts
|
|
11630
|
+
\u2022 Attack graph: Tracks discovered paths & prevents repeating failures
|
|
10891
11631
|
|
|
10892
|
-
\u2500\u2500
|
|
10893
|
-
/
|
|
10894
|
-
/
|
|
10895
|
-
/
|
|
10896
|
-
/ctf (toggle CTF/standard mode)
|
|
10897
|
-
/auto (toggle approval mode)
|
|
11632
|
+
\u2500\u2500 Tips \u2500\u2500
|
|
11633
|
+
\u2022 Type / and press Tab to autocomplete commands
|
|
11634
|
+
\u2022 /clear resets the entire session (messages, state, .pentesting data)
|
|
11635
|
+
\u2022 /start automatically enables auto-approve for hands-free operation
|
|
10898
11636
|
`;
|
|
10899
11637
|
|
|
10900
11638
|
// src/platform/tui/hooks/useAgentState.ts
|
|
@@ -11041,10 +11779,10 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
11041
11779
|
setCurrentTokens(tokenAccumRef.current + stepTokens);
|
|
11042
11780
|
};
|
|
11043
11781
|
const onFlagFound = (e) => {
|
|
11044
|
-
addMessage("system",
|
|
11782
|
+
addMessage("system", `[FLAG] ${e.data.flag} (total: ${e.data.totalFlags})`);
|
|
11045
11783
|
};
|
|
11046
11784
|
const onPhaseChange = (e) => {
|
|
11047
|
-
addMessage("system",
|
|
11785
|
+
addMessage("system", `[Phase] ${e.data.fromPhase} -> ${e.data.toPhase} (${e.data.reason})`);
|
|
11048
11786
|
const s = agent.getState();
|
|
11049
11787
|
setStats({
|
|
11050
11788
|
phase: e.data.toPhase,
|
|
@@ -11113,6 +11851,9 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
11113
11851
|
return () => {
|
|
11114
11852
|
events.removeAllListeners();
|
|
11115
11853
|
clearAllTimers();
|
|
11854
|
+
clearInputHandler();
|
|
11855
|
+
clearCredentialHandler();
|
|
11856
|
+
clearCommandEventEmitter();
|
|
11116
11857
|
};
|
|
11117
11858
|
}, [
|
|
11118
11859
|
agent,
|
|
@@ -11186,18 +11927,18 @@ Options: ${request.options.join(", ")}`;
|
|
|
11186
11927
|
}
|
|
11187
11928
|
function getCommandEventIcon(eventType) {
|
|
11188
11929
|
const icons = {
|
|
11189
|
-
[COMMAND_EVENT_TYPES.TOOL_MISSING]: "
|
|
11190
|
-
[COMMAND_EVENT_TYPES.TOOL_INSTALL]: "
|
|
11191
|
-
[COMMAND_EVENT_TYPES.TOOL_INSTALLED]: "
|
|
11192
|
-
[COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED]: "
|
|
11193
|
-
[COMMAND_EVENT_TYPES.TOOL_RETRY]: "
|
|
11194
|
-
[COMMAND_EVENT_TYPES.COMMAND_START]: "
|
|
11195
|
-
[COMMAND_EVENT_TYPES.COMMAND_SUCCESS]: "
|
|
11196
|
-
[COMMAND_EVENT_TYPES.COMMAND_FAILED]: "
|
|
11197
|
-
[COMMAND_EVENT_TYPES.COMMAND_ERROR]: "
|
|
11198
|
-
[COMMAND_EVENT_TYPES.INPUT_REQUIRED]: "
|
|
11930
|
+
[COMMAND_EVENT_TYPES.TOOL_MISSING]: "[!]",
|
|
11931
|
+
[COMMAND_EVENT_TYPES.TOOL_INSTALL]: "[install]",
|
|
11932
|
+
[COMMAND_EVENT_TYPES.TOOL_INSTALLED]: "[ok]",
|
|
11933
|
+
[COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED]: "[fail]",
|
|
11934
|
+
[COMMAND_EVENT_TYPES.TOOL_RETRY]: "[retry]",
|
|
11935
|
+
[COMMAND_EVENT_TYPES.COMMAND_START]: "[>]",
|
|
11936
|
+
[COMMAND_EVENT_TYPES.COMMAND_SUCCESS]: "[ok]",
|
|
11937
|
+
[COMMAND_EVENT_TYPES.COMMAND_FAILED]: "[x]",
|
|
11938
|
+
[COMMAND_EVENT_TYPES.COMMAND_ERROR]: "[err]",
|
|
11939
|
+
[COMMAND_EVENT_TYPES.INPUT_REQUIRED]: "[auth]"
|
|
11199
11940
|
};
|
|
11200
|
-
return icons[eventType] || "
|
|
11941
|
+
return icons[eventType] || "[-]";
|
|
11201
11942
|
}
|
|
11202
11943
|
|
|
11203
11944
|
// src/platform/tui/hooks/useAgent.ts
|
|
@@ -11218,6 +11959,7 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
11218
11959
|
inputRequest,
|
|
11219
11960
|
setInputRequest,
|
|
11220
11961
|
stats,
|
|
11962
|
+
setStats,
|
|
11221
11963
|
lastResponseMetaRef,
|
|
11222
11964
|
addMessage,
|
|
11223
11965
|
manageTimer,
|
|
@@ -11265,6 +12007,17 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
11265
12007
|
addMessage("system", "Input cancelled");
|
|
11266
12008
|
}
|
|
11267
12009
|
}, [setInputRequest, addMessage]);
|
|
12010
|
+
const refreshStats = useCallback2(() => {
|
|
12011
|
+
const s = agent.getState();
|
|
12012
|
+
setStats({
|
|
12013
|
+
phase: s.getPhase() || PHASES.RECON,
|
|
12014
|
+
targets: s.getTargets().size,
|
|
12015
|
+
findings: s.getFindings().length,
|
|
12016
|
+
todo: s.getTodo().length
|
|
12017
|
+
});
|
|
12018
|
+
resetCumulativeCounters();
|
|
12019
|
+
setCurrentStatus("");
|
|
12020
|
+
}, [agent, setStats, resetCumulativeCounters, setCurrentStatus]);
|
|
11268
12021
|
return {
|
|
11269
12022
|
agent,
|
|
11270
12023
|
messages,
|
|
@@ -11280,11 +12033,13 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
11280
12033
|
executeTask,
|
|
11281
12034
|
abort,
|
|
11282
12035
|
cancelInputRequest,
|
|
11283
|
-
addMessage
|
|
12036
|
+
addMessage,
|
|
12037
|
+
refreshStats
|
|
11284
12038
|
};
|
|
11285
12039
|
};
|
|
11286
12040
|
|
|
11287
12041
|
// src/platform/tui/components/MessageList.tsx
|
|
12042
|
+
import { memo } from "react";
|
|
11288
12043
|
import { Box as Box2, Text as Text2, Static } from "ink";
|
|
11289
12044
|
|
|
11290
12045
|
// src/platform/tui/components/inline-status.tsx
|
|
@@ -11302,7 +12057,7 @@ function formatDuration2(ms) {
|
|
|
11302
12057
|
}
|
|
11303
12058
|
function getRoleColor(role) {
|
|
11304
12059
|
const roleColors = {
|
|
11305
|
-
listener: THEME.accent.
|
|
12060
|
+
listener: THEME.accent.blue,
|
|
11306
12061
|
active_shell: THEME.accent.green,
|
|
11307
12062
|
server: THEME.accent.blue,
|
|
11308
12063
|
sniffer: THEME.accent.amber,
|
|
@@ -11447,7 +12202,7 @@ function parseStatusContent(content) {
|
|
|
11447
12202
|
}
|
|
11448
12203
|
return null;
|
|
11449
12204
|
}
|
|
11450
|
-
var MessageList = ({ messages }) => {
|
|
12205
|
+
var MessageList = memo(({ messages }) => {
|
|
11451
12206
|
return /* @__PURE__ */ jsx2(Static, { items: messages, children: (msg) => {
|
|
11452
12207
|
if (msg.type === "status") {
|
|
11453
12208
|
const statusData = parseStatusContent(msg.content);
|
|
@@ -11468,18 +12223,19 @@ var MessageList = ({ messages }) => {
|
|
|
11468
12223
|
msg.content
|
|
11469
12224
|
] }) }, msg.id);
|
|
11470
12225
|
} });
|
|
11471
|
-
};
|
|
12226
|
+
});
|
|
11472
12227
|
|
|
11473
12228
|
// src/platform/tui/components/StatusDisplay.tsx
|
|
12229
|
+
import { memo as memo3 } from "react";
|
|
11474
12230
|
import { Box as Box3, Text as Text4 } from "ink";
|
|
11475
12231
|
|
|
11476
12232
|
// src/platform/tui/components/MusicSpinner.tsx
|
|
11477
|
-
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
12233
|
+
import { useState as useState3, useEffect as useEffect3, memo as memo2 } from "react";
|
|
11478
12234
|
import { Text as Text3 } from "ink";
|
|
11479
12235
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
11480
12236
|
var FRAMES = ["\u2669", "\u266A", "\u266B", "\u266C", "\u266B", "\u266A"];
|
|
11481
|
-
var INTERVAL =
|
|
11482
|
-
var MusicSpinner = ({ color }) => {
|
|
12237
|
+
var INTERVAL = 600;
|
|
12238
|
+
var MusicSpinner = memo2(({ color }) => {
|
|
11483
12239
|
const [index, setIndex] = useState3(0);
|
|
11484
12240
|
useEffect3(() => {
|
|
11485
12241
|
const timer = setInterval(() => {
|
|
@@ -11488,11 +12244,11 @@ var MusicSpinner = ({ color }) => {
|
|
|
11488
12244
|
return () => clearInterval(timer);
|
|
11489
12245
|
}, []);
|
|
11490
12246
|
return /* @__PURE__ */ jsx3(Text3, { color, children: FRAMES[index] });
|
|
11491
|
-
};
|
|
12247
|
+
});
|
|
11492
12248
|
|
|
11493
12249
|
// src/platform/tui/components/StatusDisplay.tsx
|
|
11494
12250
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
11495
|
-
var StatusDisplay = ({
|
|
12251
|
+
var StatusDisplay = memo3(({
|
|
11496
12252
|
retryState,
|
|
11497
12253
|
isProcessing,
|
|
11498
12254
|
currentStatus,
|
|
@@ -11514,7 +12270,7 @@ var StatusDisplay = ({
|
|
|
11514
12270
|
" \xB7 ",
|
|
11515
12271
|
truncateError(retryState.error)
|
|
11516
12272
|
] }),
|
|
11517
|
-
/* @__PURE__ */ jsxs3(Text4, { color: THEME.accent.
|
|
12273
|
+
/* @__PURE__ */ jsxs3(Text4, { color: THEME.accent.blue, bold: true, children: [
|
|
11518
12274
|
" \xB7 ",
|
|
11519
12275
|
retryState.countdown,
|
|
11520
12276
|
"s"
|
|
@@ -11532,13 +12288,15 @@ var StatusDisplay = ({
|
|
|
11532
12288
|
] })
|
|
11533
12289
|
] })
|
|
11534
12290
|
] });
|
|
11535
|
-
};
|
|
12291
|
+
});
|
|
11536
12292
|
|
|
11537
12293
|
// src/platform/tui/components/ChatInput.tsx
|
|
11538
|
-
import {
|
|
12294
|
+
import { useMemo, useCallback as useCallback3, useRef as useRef3, memo as memo4 } from "react";
|
|
12295
|
+
import { Box as Box4, Text as Text5, useInput } from "ink";
|
|
11539
12296
|
import TextInput from "ink-text-input";
|
|
11540
12297
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
11541
|
-
var
|
|
12298
|
+
var MAX_SUGGESTIONS = 6;
|
|
12299
|
+
var ChatInput = memo4(({
|
|
11542
12300
|
value,
|
|
11543
12301
|
onChange,
|
|
11544
12302
|
onSubmit,
|
|
@@ -11548,46 +12306,104 @@ var ChatInput = ({
|
|
|
11548
12306
|
setSecretInput,
|
|
11549
12307
|
onSecretSubmit
|
|
11550
12308
|
}) => {
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
12309
|
+
const isSlashMode = value.startsWith("/");
|
|
12310
|
+
const partialCmd = isSlashMode ? value.slice(1).split(" ")[0] : "";
|
|
12311
|
+
const hasArgs = isSlashMode && value.includes(" ");
|
|
12312
|
+
const suggestions = useMemo(() => {
|
|
12313
|
+
if (!isSlashMode || hasArgs) return [];
|
|
12314
|
+
return getMatchingCommands(partialCmd).slice(0, MAX_SUGGESTIONS);
|
|
12315
|
+
}, [isSlashMode, partialCmd, hasArgs]);
|
|
12316
|
+
const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
|
|
12317
|
+
const suggestionsRef = useRef3(suggestions);
|
|
12318
|
+
suggestionsRef.current = suggestions;
|
|
12319
|
+
const isSlashModeRef = useRef3(isSlashMode);
|
|
12320
|
+
isSlashModeRef.current = isSlashMode;
|
|
12321
|
+
const hasArgsRef = useRef3(hasArgs);
|
|
12322
|
+
hasArgsRef.current = hasArgs;
|
|
12323
|
+
const inputRequestRef = useRef3(inputRequest);
|
|
12324
|
+
inputRequestRef.current = inputRequest;
|
|
12325
|
+
const onChangeRef = useRef3(onChange);
|
|
12326
|
+
onChangeRef.current = onChange;
|
|
12327
|
+
useInput(useCallback3((_input, key) => {
|
|
12328
|
+
if (inputRequestRef.current.isActive) return;
|
|
12329
|
+
if (key.tab && isSlashModeRef.current && !hasArgsRef.current && suggestionsRef.current.length > 0) {
|
|
12330
|
+
const best = suggestionsRef.current[0];
|
|
12331
|
+
const argsHint = best.args ? " " : "";
|
|
12332
|
+
onChangeRef.current(`/${best.name}${argsHint}`);
|
|
12333
|
+
}
|
|
12334
|
+
}, []));
|
|
12335
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
12336
|
+
showPreview && /* @__PURE__ */ jsx5(
|
|
12337
|
+
Box4,
|
|
12338
|
+
{
|
|
12339
|
+
flexDirection: "column",
|
|
12340
|
+
borderStyle: "single",
|
|
12341
|
+
borderColor: THEME.border.default,
|
|
12342
|
+
paddingX: 1,
|
|
12343
|
+
marginBottom: 0,
|
|
12344
|
+
children: suggestions.map((cmd, i) => {
|
|
12345
|
+
const isFirst = i === 0;
|
|
12346
|
+
const nameColor = isFirst ? THEME.text.accent : THEME.text.secondary;
|
|
12347
|
+
const aliasText = cmd.alias ? ` /${cmd.alias}` : "";
|
|
12348
|
+
const argsText = cmd.args ? ` ${cmd.args}` : "";
|
|
12349
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
12350
|
+
/* @__PURE__ */ jsxs4(Text5, { color: nameColor, bold: isFirst, children: [
|
|
12351
|
+
"/",
|
|
12352
|
+
cmd.name
|
|
12353
|
+
] }),
|
|
12354
|
+
/* @__PURE__ */ jsx5(Text5, { color: THEME.text.muted, children: argsText }),
|
|
12355
|
+
aliasText && /* @__PURE__ */ jsx5(Text5, { color: THEME.text.muted, children: aliasText }),
|
|
12356
|
+
/* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
|
|
12357
|
+
" \u2014 ",
|
|
12358
|
+
cmd.description
|
|
12359
|
+
] }),
|
|
12360
|
+
isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.accent.blue, children: " [Tab]" })
|
|
12361
|
+
] }, cmd.name);
|
|
12362
|
+
})
|
|
12363
|
+
}
|
|
12364
|
+
),
|
|
12365
|
+
/* @__PURE__ */ jsx5(
|
|
12366
|
+
Box4,
|
|
12367
|
+
{
|
|
12368
|
+
borderStyle: "single",
|
|
12369
|
+
borderColor: inputRequest.isActive ? THEME.status.warning : THEME.border.default,
|
|
12370
|
+
paddingX: 1,
|
|
12371
|
+
children: inputRequest.isActive ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
12372
|
+
/* @__PURE__ */ jsx5(Text5, { color: THEME.status.warning, children: "[auth]" }),
|
|
12373
|
+
/* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
|
|
12374
|
+
" ",
|
|
12375
|
+
inputRequest.prompt
|
|
12376
|
+
] }),
|
|
12377
|
+
/* @__PURE__ */ jsx5(
|
|
12378
|
+
TextInput,
|
|
12379
|
+
{
|
|
12380
|
+
value: secretInput,
|
|
12381
|
+
onChange: setSecretInput,
|
|
12382
|
+
onSubmit: onSecretSubmit,
|
|
12383
|
+
placeholder: "...",
|
|
12384
|
+
mask: inputRequest.isPassword ? "\u2022" : void 0
|
|
12385
|
+
}
|
|
12386
|
+
)
|
|
12387
|
+
] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
12388
|
+
/* @__PURE__ */ jsx5(Text5, { color: THEME.text.secondary, children: "\u25B8" }),
|
|
12389
|
+
/* @__PURE__ */ jsx5(Text5, { children: " " }),
|
|
12390
|
+
/* @__PURE__ */ jsx5(
|
|
12391
|
+
TextInput,
|
|
12392
|
+
{
|
|
12393
|
+
value,
|
|
12394
|
+
onChange,
|
|
12395
|
+
onSubmit,
|
|
12396
|
+
placeholder
|
|
12397
|
+
}
|
|
12398
|
+
)
|
|
12399
|
+
] })
|
|
12400
|
+
}
|
|
12401
|
+
)
|
|
12402
|
+
] });
|
|
12403
|
+
});
|
|
11589
12404
|
|
|
11590
12405
|
// src/platform/tui/components/footer.tsx
|
|
12406
|
+
import { memo as memo5 } from "react";
|
|
11591
12407
|
import { Box as Box5, Text as Text6 } from "ink";
|
|
11592
12408
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
11593
12409
|
var formatElapsed = (totalSeconds) => {
|
|
@@ -11600,7 +12416,7 @@ var formatElapsed = (totalSeconds) => {
|
|
|
11600
12416
|
}
|
|
11601
12417
|
return `${minutes}:${pad(seconds)}`;
|
|
11602
12418
|
};
|
|
11603
|
-
var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) => {
|
|
12419
|
+
var Footer = memo5(({ phase, targets, findings, todo, elapsedTime, isProcessing }) => {
|
|
11604
12420
|
return /* @__PURE__ */ jsxs5(
|
|
11605
12421
|
Box5,
|
|
11606
12422
|
{
|
|
@@ -11611,7 +12427,7 @@ var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) =>
|
|
|
11611
12427
|
/* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
|
|
11612
12428
|
/* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
|
|
11613
12429
|
"Phase: ",
|
|
11614
|
-
/* @__PURE__ */ jsx6(Text6, { color: THEME.accent.
|
|
12430
|
+
/* @__PURE__ */ jsx6(Text6, { color: THEME.accent.blue, children: phase })
|
|
11615
12431
|
] }),
|
|
11616
12432
|
/* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
|
|
11617
12433
|
"Targets: ",
|
|
@@ -11633,7 +12449,7 @@ var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) =>
|
|
|
11633
12449
|
]
|
|
11634
12450
|
}
|
|
11635
12451
|
);
|
|
11636
|
-
};
|
|
12452
|
+
});
|
|
11637
12453
|
var footer_default = Footer;
|
|
11638
12454
|
|
|
11639
12455
|
// src/platform/tui/app.tsx
|
|
@@ -11658,15 +12474,16 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
11658
12474
|
executeTask,
|
|
11659
12475
|
abort,
|
|
11660
12476
|
cancelInputRequest,
|
|
11661
|
-
addMessage
|
|
12477
|
+
addMessage,
|
|
12478
|
+
refreshStats
|
|
11662
12479
|
} = useAgent(autoApproveMode, target);
|
|
11663
|
-
const isProcessingRef =
|
|
12480
|
+
const isProcessingRef = useRef4(isProcessing);
|
|
11664
12481
|
isProcessingRef.current = isProcessing;
|
|
11665
|
-
const autoApproveModeRef =
|
|
12482
|
+
const autoApproveModeRef = useRef4(autoApproveMode);
|
|
11666
12483
|
autoApproveModeRef.current = autoApproveMode;
|
|
11667
|
-
const inputRequestRef =
|
|
12484
|
+
const inputRequestRef = useRef4(inputRequest);
|
|
11668
12485
|
inputRequestRef.current = inputRequest;
|
|
11669
|
-
const handleExit =
|
|
12486
|
+
const handleExit = useCallback4(() => {
|
|
11670
12487
|
const ir = inputRequestRef.current;
|
|
11671
12488
|
if (ir.isActive && ir.resolve) ir.resolve(null);
|
|
11672
12489
|
cleanupAllProcesses().catch(() => {
|
|
@@ -11674,16 +12491,31 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
11674
12491
|
exit();
|
|
11675
12492
|
setTimeout(() => process.exit(0), DISPLAY_LIMITS.EXIT_DELAY);
|
|
11676
12493
|
}, [exit]);
|
|
11677
|
-
const handleCommand =
|
|
12494
|
+
const handleCommand = useCallback4(async (cmd, args) => {
|
|
11678
12495
|
switch (cmd) {
|
|
11679
12496
|
case UI_COMMANDS.HELP:
|
|
11680
12497
|
case UI_COMMANDS.HELP_SHORT:
|
|
11681
12498
|
addMessage("system", HELP_TEXT);
|
|
11682
12499
|
break;
|
|
11683
12500
|
case UI_COMMANDS.CLEAR:
|
|
11684
|
-
case UI_COMMANDS.CLEAR_SHORT:
|
|
12501
|
+
case UI_COMMANDS.CLEAR_SHORT: {
|
|
12502
|
+
if (isProcessingRef.current) {
|
|
12503
|
+
addMessage("error", "Cannot /clear while agent is running. Press Esc to abort first.");
|
|
12504
|
+
break;
|
|
12505
|
+
}
|
|
11685
12506
|
setMessages([]);
|
|
12507
|
+
const result2 = await agent.resetSession();
|
|
12508
|
+
if (result2.cleared.length > 0) {
|
|
12509
|
+
addMessage("system", `[reset] Session cleared: ${result2.cleared.join(", ")}`);
|
|
12510
|
+
} else {
|
|
12511
|
+
addMessage("system", "[reset] Session clean");
|
|
12512
|
+
}
|
|
12513
|
+
if (result2.errors.length > 0) {
|
|
12514
|
+
addMessage("error", `Cleanup errors: ${result2.errors.join("; ")}`);
|
|
12515
|
+
}
|
|
12516
|
+
refreshStats();
|
|
11686
12517
|
break;
|
|
12518
|
+
}
|
|
11687
12519
|
case UI_COMMANDS.TARGET:
|
|
11688
12520
|
case UI_COMMANDS.TARGET_SHORT:
|
|
11689
12521
|
if (!args[0]) {
|
|
@@ -11703,7 +12535,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
11703
12535
|
if (!autoApproveModeRef.current) {
|
|
11704
12536
|
setAutoApproveMode(true);
|
|
11705
12537
|
agent.setAutoApprove(true);
|
|
11706
|
-
addMessage("system", "
|
|
12538
|
+
addMessage("system", "[auto] Autonomous mode enabled");
|
|
11707
12539
|
}
|
|
11708
12540
|
addMessage("system", "Starting penetration test...");
|
|
11709
12541
|
const targets = Array.from(agent.getState().getTargets().keys());
|
|
@@ -11741,9 +12573,17 @@ ${procData.stdout || "(no output)"}
|
|
|
11741
12573
|
break;
|
|
11742
12574
|
case UI_COMMANDS.CTF:
|
|
11743
12575
|
const ctfEnabled = agent.toggleCtfMode();
|
|
11744
|
-
addMessage("system", ctfEnabled ? "
|
|
12576
|
+
addMessage("system", ctfEnabled ? "[CTF] Mode ON - flag detection active" : "[CTF] Mode OFF - standard pentest");
|
|
12577
|
+
break;
|
|
12578
|
+
case UI_COMMANDS.GRAPH:
|
|
12579
|
+
case UI_COMMANDS.GRAPH_SHORT:
|
|
12580
|
+
addMessage("system", agent.getState().attackGraph.toASCII());
|
|
12581
|
+
break;
|
|
12582
|
+
case UI_COMMANDS.PATHS:
|
|
12583
|
+
case UI_COMMANDS.PATHS_SHORT:
|
|
12584
|
+
addMessage("system", agent.getState().attackGraph.toPathsList());
|
|
11745
12585
|
break;
|
|
11746
|
-
case
|
|
12586
|
+
case UI_COMMANDS.AUTO:
|
|
11747
12587
|
setAutoApproveMode((prev) => {
|
|
11748
12588
|
const newVal = !prev;
|
|
11749
12589
|
agent.setAutoApprove(newVal);
|
|
@@ -11759,8 +12599,8 @@ ${procData.stdout || "(no output)"}
|
|
|
11759
12599
|
default:
|
|
11760
12600
|
addMessage("error", `Unknown command: /${cmd}`);
|
|
11761
12601
|
}
|
|
11762
|
-
}, [agent, addMessage, executeTask, setMessages, handleExit]);
|
|
11763
|
-
const handleSubmit =
|
|
12602
|
+
}, [agent, addMessage, executeTask, setMessages, handleExit, refreshStats]);
|
|
12603
|
+
const handleSubmit = useCallback4(async (value) => {
|
|
11764
12604
|
const trimmed = value.trim();
|
|
11765
12605
|
if (!trimmed) return;
|
|
11766
12606
|
setInput("");
|
|
@@ -11772,7 +12612,7 @@ ${procData.stdout || "(no output)"}
|
|
|
11772
12612
|
await executeTask(trimmed);
|
|
11773
12613
|
}
|
|
11774
12614
|
}, [addMessage, executeTask, handleCommand]);
|
|
11775
|
-
const handleSecretSubmit =
|
|
12615
|
+
const handleSecretSubmit = useCallback4((value) => {
|
|
11776
12616
|
const ir = inputRequestRef.current;
|
|
11777
12617
|
if (!ir.isActive || !ir.resolve) return;
|
|
11778
12618
|
const displayText = ir.isPassword ? "\u2022".repeat(value.length) : value;
|
|
@@ -11782,7 +12622,7 @@ ${procData.stdout || "(no output)"}
|
|
|
11782
12622
|
setInputRequest({ isActive: false, prompt: "", isPassword: false, resolve: null });
|
|
11783
12623
|
setSecretInput("");
|
|
11784
12624
|
}, [addMessage, setInputRequest]);
|
|
11785
|
-
|
|
12625
|
+
useInput2(useCallback4((ch, key) => {
|
|
11786
12626
|
if (key.escape) {
|
|
11787
12627
|
if (inputRequestRef.current.isActive) cancelInputRequest();
|
|
11788
12628
|
else if (isProcessingRef.current) abort();
|
|
@@ -11873,7 +12713,7 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
|
|
|
11873
12713
|
console.clear();
|
|
11874
12714
|
console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
|
|
11875
12715
|
console.log(
|
|
11876
|
-
" " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.
|
|
12716
|
+
" " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.accent.blue)("Type /help for commands") + "\n"
|
|
11877
12717
|
);
|
|
11878
12718
|
if (skipPermissions) {
|
|
11879
12719
|
console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions"));
|
|
@@ -11897,7 +12737,7 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
11897
12737
|
if (skipPermissions) {
|
|
11898
12738
|
console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
|
|
11899
12739
|
}
|
|
11900
|
-
console.log(chalk.hex(THEME.
|
|
12740
|
+
console.log(chalk.hex(THEME.accent.blue)(`[target] Objective: ${objective}
|
|
11901
12741
|
`));
|
|
11902
12742
|
const agent = AgentFactory.createMainAgent(skipPermissions);
|
|
11903
12743
|
if (skipPermissions) {
|
|
@@ -11919,7 +12759,7 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
11919
12759
|
if (options.output) {
|
|
11920
12760
|
const fs = await import("fs/promises");
|
|
11921
12761
|
await fs.writeFile(options.output, JSON.stringify({ result: result2 }, null, 2));
|
|
11922
|
-
console.log(chalk.hex(THEME.
|
|
12762
|
+
console.log(chalk.hex(THEME.accent.blue)(`
|
|
11923
12763
|
[+] Report saved to: ${options.output}`));
|
|
11924
12764
|
}
|
|
11925
12765
|
await shutdown(0);
|
|
@@ -11934,7 +12774,7 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
|
|
|
11934
12774
|
const opts = program.opts();
|
|
11935
12775
|
const skipPermissions = opts.dangerouslySkipPermissions || false;
|
|
11936
12776
|
console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
|
|
11937
|
-
console.log(chalk.hex(THEME.
|
|
12777
|
+
console.log(chalk.hex(THEME.accent.blue)(`
|
|
11938
12778
|
[scan] Target: ${target} (${options.scanType})
|
|
11939
12779
|
`));
|
|
11940
12780
|
const agent = AgentFactory.createMainAgent(skipPermissions);
|
|
@@ -11958,7 +12798,7 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
|
|
|
11958
12798
|
program.command("help-extended").description("Show extended help with examples").action(() => {
|
|
11959
12799
|
console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
|
|
11960
12800
|
console.log(`
|
|
11961
|
-
${chalk.hex(THEME.
|
|
12801
|
+
${chalk.hex(THEME.accent.blue)(APP_NAME + " - Autonomous Penetration Testing AI")}
|
|
11962
12802
|
|
|
11963
12803
|
${chalk.hex(THEME.status.warning)("Usage:")}
|
|
11964
12804
|
|
|
@@ -11968,24 +12808,24 @@ ${chalk.hex(THEME.status.warning)("Usage:")}
|
|
|
11968
12808
|
|
|
11969
12809
|
${chalk.hex(THEME.status.warning)("Commands:")}
|
|
11970
12810
|
|
|
11971
|
-
${chalk.hex(THEME.
|
|
11972
|
-
${chalk.hex(THEME.
|
|
11973
|
-
${chalk.hex(THEME.
|
|
12811
|
+
${chalk.hex(THEME.accent.blue)("pentesting")} Interactive TUI mode
|
|
12812
|
+
${chalk.hex(THEME.accent.blue)("pentesting run <objective>")} Run single objective
|
|
12813
|
+
${chalk.hex(THEME.accent.blue)("pentesting scan <target>")} Quick scan target
|
|
11974
12814
|
|
|
11975
12815
|
${chalk.hex(THEME.status.warning)("Options:")}
|
|
11976
12816
|
|
|
11977
|
-
${chalk.hex(THEME.
|
|
11978
|
-
${chalk.hex(THEME.
|
|
11979
|
-
${chalk.hex(THEME.
|
|
12817
|
+
${chalk.hex(THEME.accent.blue)("--dangerously-skip-permissions")} Skip all permission prompts
|
|
12818
|
+
${chalk.hex(THEME.accent.blue)("-t, --target <ip>")} Set target
|
|
12819
|
+
${chalk.hex(THEME.accent.blue)("-o, --output <file>")} Save results to file
|
|
11980
12820
|
|
|
11981
12821
|
${chalk.hex(THEME.status.warning)("Interactive Commands:")}
|
|
11982
12822
|
|
|
11983
|
-
${chalk.hex(THEME.
|
|
11984
|
-
${chalk.hex(THEME.
|
|
11985
|
-
${chalk.hex(THEME.
|
|
11986
|
-
${chalk.hex(THEME.
|
|
11987
|
-
${chalk.hex(THEME.
|
|
11988
|
-
${chalk.hex(THEME.
|
|
12823
|
+
${chalk.hex(THEME.accent.blue)("/target <ip>")} Set target
|
|
12824
|
+
${chalk.hex(THEME.accent.blue)("/start")} Start autonomous mode
|
|
12825
|
+
${chalk.hex(THEME.accent.blue)("/config")} Manage configuration
|
|
12826
|
+
${chalk.hex(THEME.accent.blue)("/hint <text>")} Provide hint
|
|
12827
|
+
${chalk.hex(THEME.accent.blue)("/findings")} Show findings
|
|
12828
|
+
${chalk.hex(THEME.accent.blue)("/reset")} Reset session
|
|
11989
12829
|
|
|
11990
12830
|
${chalk.hex(THEME.status.warning)("Examples:")}
|
|
11991
12831
|
|
|
@@ -12000,9 +12840,9 @@ ${chalk.hex(THEME.status.warning)("Examples:")}
|
|
|
12000
12840
|
|
|
12001
12841
|
${chalk.hex(THEME.status.warning)("Environment:")}
|
|
12002
12842
|
|
|
12003
|
-
${chalk.hex(THEME.
|
|
12004
|
-
${chalk.hex(THEME.
|
|
12005
|
-
${chalk.hex(THEME.
|
|
12843
|
+
${chalk.hex(THEME.accent.blue)("PENTEST_API_KEY")} Required - LLM API key
|
|
12844
|
+
${chalk.hex(THEME.accent.blue)("PENTEST_BASE_URL")} Optional - AI API base URL
|
|
12845
|
+
${chalk.hex(THEME.accent.blue)("PENTEST_MODEL")} Optional - Model override
|
|
12006
12846
|
`);
|
|
12007
12847
|
});
|
|
12008
12848
|
program.parse();
|