pentesting 0.40.7 → 0.41.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 +852 -237
- package/dist/prompts/recon.md +130 -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.41.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);
|
|
2356
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;
|
|
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");
|
|
@@ -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) {
|
|
@@ -10554,6 +11039,17 @@ var MainAgent = class extends CoreAgent {
|
|
|
10554
11039
|
isCtfMode() {
|
|
10555
11040
|
return this.state.isCtfMode();
|
|
10556
11041
|
}
|
|
11042
|
+
/**
|
|
11043
|
+
* Full session reset — clears state, workspace files, and background processes.
|
|
11044
|
+
* Used by /clear command for a complete fresh start.
|
|
11045
|
+
*/
|
|
11046
|
+
async resetSession() {
|
|
11047
|
+
await cleanupAllProcesses().catch(() => {
|
|
11048
|
+
});
|
|
11049
|
+
this.state.reset();
|
|
11050
|
+
this.userInput = "";
|
|
11051
|
+
return clearWorkspace();
|
|
11052
|
+
}
|
|
10557
11053
|
setScope(allowed, exclusions = []) {
|
|
10558
11054
|
this.state.setScope({
|
|
10559
11055
|
allowedCidrs: allowed.filter((a) => a.includes("/")),
|
|
@@ -10682,10 +11178,10 @@ var THEME = {
|
|
|
10682
11178
|
// Amber
|
|
10683
11179
|
error: "#ef4444",
|
|
10684
11180
|
// Red
|
|
10685
|
-
info: "#
|
|
10686
|
-
//
|
|
10687
|
-
running: "#
|
|
10688
|
-
//
|
|
11181
|
+
info: "#3b82f6",
|
|
11182
|
+
// Blue 500 (distinct from user sky)
|
|
11183
|
+
running: "#60a5fa",
|
|
11184
|
+
// Blue 400 (AI activity — distinct from user sky)
|
|
10689
11185
|
pending: "#64748b"
|
|
10690
11186
|
// Slate
|
|
10691
11187
|
},
|
|
@@ -10705,14 +11201,16 @@ var THEME = {
|
|
|
10705
11201
|
// Border colors
|
|
10706
11202
|
border: {
|
|
10707
11203
|
default: "#1e293b",
|
|
10708
|
-
focus: "#
|
|
11204
|
+
focus: "#7dd3fc",
|
|
11205
|
+
// Sky 300 (softer, UI decorative)
|
|
10709
11206
|
error: "#ef4444",
|
|
10710
11207
|
success: "#22c55e"
|
|
10711
11208
|
},
|
|
10712
11209
|
// Phase colors
|
|
10713
11210
|
phase: {
|
|
10714
11211
|
recon: "#94a3b8",
|
|
10715
|
-
enum: "#
|
|
11212
|
+
enum: "#60a5fa",
|
|
11213
|
+
// Blue 400 (phase indicator)
|
|
10716
11214
|
vuln: "#f59e0b",
|
|
10717
11215
|
exploit: "#ef4444",
|
|
10718
11216
|
privesc: "#8b5cf6",
|
|
@@ -10762,10 +11260,12 @@ var THEME = {
|
|
|
10762
11260
|
gold: ["#f59e0b", "#78350f"],
|
|
10763
11261
|
royal: ["#818cf8", "#312e81"]
|
|
10764
11262
|
},
|
|
10765
|
-
// Spinner color (
|
|
10766
|
-
spinner: "#
|
|
10767
|
-
//
|
|
10768
|
-
|
|
11263
|
+
// Spinner color (soft sky — UI feedback, not user input)
|
|
11264
|
+
spinner: "#7dd3fc",
|
|
11265
|
+
// Sky 300
|
|
11266
|
+
// Identity color (branded accent)
|
|
11267
|
+
identity: "#60a5fa"
|
|
11268
|
+
// Blue 400
|
|
10769
11269
|
};
|
|
10770
11270
|
var ASCII_BANNER = `
|
|
10771
11271
|
____ __ __ _
|
|
@@ -10871,30 +11371,46 @@ var MESSAGE_STYLES = {
|
|
|
10871
11371
|
status: "\u25C8"
|
|
10872
11372
|
}
|
|
10873
11373
|
};
|
|
11374
|
+
var COMMAND_DEFINITIONS = [
|
|
11375
|
+
{ name: "target", alias: "t", args: "<ip>", description: "Set target IP or domain" },
|
|
11376
|
+
{ name: "start", alias: "s", args: "[goal]", description: "Start autonomous pentest" },
|
|
11377
|
+
{ name: "findings", alias: "f", description: "Show discovered vulnerabilities" },
|
|
11378
|
+
{ name: "graph", alias: "g", description: "Visualize the attack graph" },
|
|
11379
|
+
{ name: "paths", alias: "p", description: "Show ranked attack paths" },
|
|
11380
|
+
{ name: "assets", alias: "a", description: "List background processes" },
|
|
11381
|
+
{ name: "logs", alias: "l", args: "<id>", description: "Show logs for an asset" },
|
|
11382
|
+
{ name: "ctf", description: "Toggle CTF mode" },
|
|
11383
|
+
{ name: "auto", description: "Toggle auto-approve mode" },
|
|
11384
|
+
{ name: "clear", alias: "c", description: "Reset session & clean workspace" },
|
|
11385
|
+
{ name: "help", alias: "h", description: "Show detailed help" },
|
|
11386
|
+
{ name: "exit", alias: "q", description: "Exit the application" }
|
|
11387
|
+
];
|
|
11388
|
+
function getMatchingCommands(partial) {
|
|
11389
|
+
const lower = partial.toLowerCase();
|
|
11390
|
+
if (!lower) return COMMAND_DEFINITIONS;
|
|
11391
|
+
return COMMAND_DEFINITIONS.filter(
|
|
11392
|
+
(cmd) => cmd.name.startsWith(lower) || cmd.alias && cmd.alias.startsWith(lower)
|
|
11393
|
+
);
|
|
11394
|
+
}
|
|
10874
11395
|
var HELP_TEXT = `
|
|
10875
11396
|
\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
|
|
11397
|
+
${COMMAND_DEFINITIONS.map((cmd) => {
|
|
11398
|
+
const usage = `/${cmd.name}${cmd.args ? " " + cmd.args : ""}`;
|
|
11399
|
+
const alias = cmd.alias ? ` (/${cmd.alias})` : "";
|
|
11400
|
+
return `${usage.padEnd(18)}${alias.padEnd(8)} ${cmd.description}`;
|
|
11401
|
+
}).join("\n")}
|
|
10885
11402
|
|
|
10886
11403
|
\u2500\u2500 Features \u2500\u2500
|
|
10887
11404
|
\u2022 Auto-install missing tools (apt/brew)
|
|
10888
11405
|
\u2022 Transparent command execution
|
|
10889
11406
|
\u2022 Interactive sudo password input
|
|
10890
11407
|
\u2022 CTF mode: Auto flag detection & CTF-specific prompts
|
|
11408
|
+
\u2022 Attack graph: Tracks discovered paths & prevents repeating failures
|
|
10891
11409
|
|
|
10892
|
-
\u2500\u2500
|
|
10893
|
-
/
|
|
10894
|
-
/
|
|
10895
|
-
/
|
|
10896
|
-
/ctf (toggle CTF/standard mode)
|
|
10897
|
-
/auto (toggle approval mode)
|
|
11410
|
+
\u2500\u2500 Tips \u2500\u2500
|
|
11411
|
+
\u2022 Type / and press Tab to autocomplete commands
|
|
11412
|
+
\u2022 /clear resets the entire session (messages, state, .pentesting data)
|
|
11413
|
+
\u2022 /start automatically enables auto-approve for hands-free operation
|
|
10898
11414
|
`;
|
|
10899
11415
|
|
|
10900
11416
|
// src/platform/tui/hooks/useAgentState.ts
|
|
@@ -11113,6 +11629,9 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
11113
11629
|
return () => {
|
|
11114
11630
|
events.removeAllListeners();
|
|
11115
11631
|
clearAllTimers();
|
|
11632
|
+
clearInputHandler();
|
|
11633
|
+
clearCredentialHandler();
|
|
11634
|
+
clearCommandEventEmitter();
|
|
11116
11635
|
};
|
|
11117
11636
|
}, [
|
|
11118
11637
|
agent,
|
|
@@ -11218,6 +11737,7 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
11218
11737
|
inputRequest,
|
|
11219
11738
|
setInputRequest,
|
|
11220
11739
|
stats,
|
|
11740
|
+
setStats,
|
|
11221
11741
|
lastResponseMetaRef,
|
|
11222
11742
|
addMessage,
|
|
11223
11743
|
manageTimer,
|
|
@@ -11265,6 +11785,17 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
11265
11785
|
addMessage("system", "Input cancelled");
|
|
11266
11786
|
}
|
|
11267
11787
|
}, [setInputRequest, addMessage]);
|
|
11788
|
+
const refreshStats = useCallback2(() => {
|
|
11789
|
+
const s = agent.getState();
|
|
11790
|
+
setStats({
|
|
11791
|
+
phase: s.getPhase() || PHASES.RECON,
|
|
11792
|
+
targets: s.getTargets().size,
|
|
11793
|
+
findings: s.getFindings().length,
|
|
11794
|
+
todo: s.getTodo().length
|
|
11795
|
+
});
|
|
11796
|
+
resetCumulativeCounters();
|
|
11797
|
+
setCurrentStatus("");
|
|
11798
|
+
}, [agent, setStats, resetCumulativeCounters, setCurrentStatus]);
|
|
11268
11799
|
return {
|
|
11269
11800
|
agent,
|
|
11270
11801
|
messages,
|
|
@@ -11280,7 +11811,8 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
11280
11811
|
executeTask,
|
|
11281
11812
|
abort,
|
|
11282
11813
|
cancelInputRequest,
|
|
11283
|
-
addMessage
|
|
11814
|
+
addMessage,
|
|
11815
|
+
refreshStats
|
|
11284
11816
|
};
|
|
11285
11817
|
};
|
|
11286
11818
|
|
|
@@ -11535,9 +12067,11 @@ var StatusDisplay = ({
|
|
|
11535
12067
|
};
|
|
11536
12068
|
|
|
11537
12069
|
// src/platform/tui/components/ChatInput.tsx
|
|
11538
|
-
import {
|
|
12070
|
+
import { useMemo, useCallback as useCallback3, useRef as useRef3 } from "react";
|
|
12071
|
+
import { Box as Box4, Text as Text5, useInput } from "ink";
|
|
11539
12072
|
import TextInput from "ink-text-input";
|
|
11540
12073
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
12074
|
+
var MAX_SUGGESTIONS = 6;
|
|
11541
12075
|
var ChatInput = ({
|
|
11542
12076
|
value,
|
|
11543
12077
|
onChange,
|
|
@@ -11548,43 +12082,100 @@ var ChatInput = ({
|
|
|
11548
12082
|
setSecretInput,
|
|
11549
12083
|
onSecretSubmit
|
|
11550
12084
|
}) => {
|
|
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
|
-
|
|
12085
|
+
const isSlashMode = value.startsWith("/");
|
|
12086
|
+
const partialCmd = isSlashMode ? value.slice(1).split(" ")[0] : "";
|
|
12087
|
+
const hasArgs = isSlashMode && value.includes(" ");
|
|
12088
|
+
const suggestions = useMemo(() => {
|
|
12089
|
+
if (!isSlashMode || hasArgs) return [];
|
|
12090
|
+
return getMatchingCommands(partialCmd).slice(0, MAX_SUGGESTIONS);
|
|
12091
|
+
}, [isSlashMode, partialCmd, hasArgs]);
|
|
12092
|
+
const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
|
|
12093
|
+
const suggestionsRef = useRef3(suggestions);
|
|
12094
|
+
suggestionsRef.current = suggestions;
|
|
12095
|
+
const isSlashModeRef = useRef3(isSlashMode);
|
|
12096
|
+
isSlashModeRef.current = isSlashMode;
|
|
12097
|
+
const hasArgsRef = useRef3(hasArgs);
|
|
12098
|
+
hasArgsRef.current = hasArgs;
|
|
12099
|
+
const inputRequestRef = useRef3(inputRequest);
|
|
12100
|
+
inputRequestRef.current = inputRequest;
|
|
12101
|
+
const onChangeRef = useRef3(onChange);
|
|
12102
|
+
onChangeRef.current = onChange;
|
|
12103
|
+
useInput(useCallback3((_input, key) => {
|
|
12104
|
+
if (inputRequestRef.current.isActive) return;
|
|
12105
|
+
if (key.tab && isSlashModeRef.current && !hasArgsRef.current && suggestionsRef.current.length > 0) {
|
|
12106
|
+
const best = suggestionsRef.current[0];
|
|
12107
|
+
const argsHint = best.args ? " " : "";
|
|
12108
|
+
onChangeRef.current(`/${best.name}${argsHint}`);
|
|
12109
|
+
}
|
|
12110
|
+
}, []));
|
|
12111
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
12112
|
+
showPreview && /* @__PURE__ */ jsx5(
|
|
12113
|
+
Box4,
|
|
12114
|
+
{
|
|
12115
|
+
flexDirection: "column",
|
|
12116
|
+
borderStyle: "single",
|
|
12117
|
+
borderColor: THEME.border.default,
|
|
12118
|
+
paddingX: 1,
|
|
12119
|
+
marginBottom: 0,
|
|
12120
|
+
children: suggestions.map((cmd, i) => {
|
|
12121
|
+
const isFirst = i === 0;
|
|
12122
|
+
const nameColor = isFirst ? THEME.text.accent : THEME.text.secondary;
|
|
12123
|
+
const aliasText = cmd.alias ? ` /${cmd.alias}` : "";
|
|
12124
|
+
const argsText = cmd.args ? ` ${cmd.args}` : "";
|
|
12125
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
12126
|
+
/* @__PURE__ */ jsxs4(Text5, { color: nameColor, bold: isFirst, children: [
|
|
12127
|
+
"/",
|
|
12128
|
+
cmd.name
|
|
12129
|
+
] }),
|
|
12130
|
+
/* @__PURE__ */ jsx5(Text5, { color: THEME.text.muted, children: argsText }),
|
|
12131
|
+
aliasText && /* @__PURE__ */ jsx5(Text5, { color: THEME.text.muted, children: aliasText }),
|
|
12132
|
+
/* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
|
|
12133
|
+
" \u2014 ",
|
|
12134
|
+
cmd.description
|
|
12135
|
+
] }),
|
|
12136
|
+
isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.accent.cyan, children: " \u21E5 Tab" })
|
|
12137
|
+
] }, cmd.name);
|
|
12138
|
+
})
|
|
12139
|
+
}
|
|
12140
|
+
),
|
|
12141
|
+
/* @__PURE__ */ jsx5(
|
|
12142
|
+
Box4,
|
|
12143
|
+
{
|
|
12144
|
+
borderStyle: "single",
|
|
12145
|
+
borderColor: inputRequest.isActive ? THEME.status.warning : THEME.border.default,
|
|
12146
|
+
paddingX: 1,
|
|
12147
|
+
children: inputRequest.isActive ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
12148
|
+
/* @__PURE__ */ jsx5(Text5, { color: THEME.status.warning, children: "\u{1F512}" }),
|
|
12149
|
+
/* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
|
|
12150
|
+
" ",
|
|
12151
|
+
inputRequest.prompt
|
|
12152
|
+
] }),
|
|
12153
|
+
/* @__PURE__ */ jsx5(
|
|
12154
|
+
TextInput,
|
|
12155
|
+
{
|
|
12156
|
+
value: secretInput,
|
|
12157
|
+
onChange: setSecretInput,
|
|
12158
|
+
onSubmit: onSecretSubmit,
|
|
12159
|
+
placeholder: "...",
|
|
12160
|
+
mask: inputRequest.isPassword ? "\u2022" : void 0
|
|
12161
|
+
}
|
|
12162
|
+
)
|
|
12163
|
+
] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
12164
|
+
/* @__PURE__ */ jsx5(Text5, { color: THEME.text.secondary, children: "\u25B8" }),
|
|
12165
|
+
/* @__PURE__ */ jsx5(Text5, { children: " " }),
|
|
12166
|
+
/* @__PURE__ */ jsx5(
|
|
12167
|
+
TextInput,
|
|
12168
|
+
{
|
|
12169
|
+
value,
|
|
12170
|
+
onChange,
|
|
12171
|
+
onSubmit,
|
|
12172
|
+
placeholder
|
|
12173
|
+
}
|
|
12174
|
+
)
|
|
12175
|
+
] })
|
|
12176
|
+
}
|
|
12177
|
+
)
|
|
12178
|
+
] });
|
|
11588
12179
|
};
|
|
11589
12180
|
|
|
11590
12181
|
// src/platform/tui/components/footer.tsx
|
|
@@ -11658,15 +12249,16 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
11658
12249
|
executeTask,
|
|
11659
12250
|
abort,
|
|
11660
12251
|
cancelInputRequest,
|
|
11661
|
-
addMessage
|
|
12252
|
+
addMessage,
|
|
12253
|
+
refreshStats
|
|
11662
12254
|
} = useAgent(autoApproveMode, target);
|
|
11663
|
-
const isProcessingRef =
|
|
12255
|
+
const isProcessingRef = useRef4(isProcessing);
|
|
11664
12256
|
isProcessingRef.current = isProcessing;
|
|
11665
|
-
const autoApproveModeRef =
|
|
12257
|
+
const autoApproveModeRef = useRef4(autoApproveMode);
|
|
11666
12258
|
autoApproveModeRef.current = autoApproveMode;
|
|
11667
|
-
const inputRequestRef =
|
|
12259
|
+
const inputRequestRef = useRef4(inputRequest);
|
|
11668
12260
|
inputRequestRef.current = inputRequest;
|
|
11669
|
-
const handleExit =
|
|
12261
|
+
const handleExit = useCallback4(() => {
|
|
11670
12262
|
const ir = inputRequestRef.current;
|
|
11671
12263
|
if (ir.isActive && ir.resolve) ir.resolve(null);
|
|
11672
12264
|
cleanupAllProcesses().catch(() => {
|
|
@@ -11674,16 +12266,31 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
11674
12266
|
exit();
|
|
11675
12267
|
setTimeout(() => process.exit(0), DISPLAY_LIMITS.EXIT_DELAY);
|
|
11676
12268
|
}, [exit]);
|
|
11677
|
-
const handleCommand =
|
|
12269
|
+
const handleCommand = useCallback4(async (cmd, args) => {
|
|
11678
12270
|
switch (cmd) {
|
|
11679
12271
|
case UI_COMMANDS.HELP:
|
|
11680
12272
|
case UI_COMMANDS.HELP_SHORT:
|
|
11681
12273
|
addMessage("system", HELP_TEXT);
|
|
11682
12274
|
break;
|
|
11683
12275
|
case UI_COMMANDS.CLEAR:
|
|
11684
|
-
case UI_COMMANDS.CLEAR_SHORT:
|
|
12276
|
+
case UI_COMMANDS.CLEAR_SHORT: {
|
|
12277
|
+
if (isProcessingRef.current) {
|
|
12278
|
+
addMessage("error", "Cannot /clear while agent is running. Press Esc to abort first.");
|
|
12279
|
+
break;
|
|
12280
|
+
}
|
|
11685
12281
|
setMessages([]);
|
|
12282
|
+
const result2 = await agent.resetSession();
|
|
12283
|
+
if (result2.cleared.length > 0) {
|
|
12284
|
+
addMessage("system", `\u{1F9F9} Session reset \u2014 cleared: ${result2.cleared.join(", ")}`);
|
|
12285
|
+
} else {
|
|
12286
|
+
addMessage("system", "\u{1F9F9} Session reset \u2014 all clean");
|
|
12287
|
+
}
|
|
12288
|
+
if (result2.errors.length > 0) {
|
|
12289
|
+
addMessage("error", `Cleanup errors: ${result2.errors.join("; ")}`);
|
|
12290
|
+
}
|
|
12291
|
+
refreshStats();
|
|
11686
12292
|
break;
|
|
12293
|
+
}
|
|
11687
12294
|
case UI_COMMANDS.TARGET:
|
|
11688
12295
|
case UI_COMMANDS.TARGET_SHORT:
|
|
11689
12296
|
if (!args[0]) {
|
|
@@ -11743,7 +12350,15 @@ ${procData.stdout || "(no output)"}
|
|
|
11743
12350
|
const ctfEnabled = agent.toggleCtfMode();
|
|
11744
12351
|
addMessage("system", ctfEnabled ? "\u{1F3F4} CTF mode ON \u2014 flag detection active, CTF prompts loaded" : "\u{1F512} CTF mode OFF \u2014 standard pentest mode");
|
|
11745
12352
|
break;
|
|
11746
|
-
case
|
|
12353
|
+
case UI_COMMANDS.GRAPH:
|
|
12354
|
+
case UI_COMMANDS.GRAPH_SHORT:
|
|
12355
|
+
addMessage("system", agent.getState().attackGraph.toASCII());
|
|
12356
|
+
break;
|
|
12357
|
+
case UI_COMMANDS.PATHS:
|
|
12358
|
+
case UI_COMMANDS.PATHS_SHORT:
|
|
12359
|
+
addMessage("system", agent.getState().attackGraph.toPathsList());
|
|
12360
|
+
break;
|
|
12361
|
+
case UI_COMMANDS.AUTO:
|
|
11747
12362
|
setAutoApproveMode((prev) => {
|
|
11748
12363
|
const newVal = !prev;
|
|
11749
12364
|
agent.setAutoApprove(newVal);
|
|
@@ -11759,8 +12374,8 @@ ${procData.stdout || "(no output)"}
|
|
|
11759
12374
|
default:
|
|
11760
12375
|
addMessage("error", `Unknown command: /${cmd}`);
|
|
11761
12376
|
}
|
|
11762
|
-
}, [agent, addMessage, executeTask, setMessages, handleExit]);
|
|
11763
|
-
const handleSubmit =
|
|
12377
|
+
}, [agent, addMessage, executeTask, setMessages, handleExit, refreshStats]);
|
|
12378
|
+
const handleSubmit = useCallback4(async (value) => {
|
|
11764
12379
|
const trimmed = value.trim();
|
|
11765
12380
|
if (!trimmed) return;
|
|
11766
12381
|
setInput("");
|
|
@@ -11772,7 +12387,7 @@ ${procData.stdout || "(no output)"}
|
|
|
11772
12387
|
await executeTask(trimmed);
|
|
11773
12388
|
}
|
|
11774
12389
|
}, [addMessage, executeTask, handleCommand]);
|
|
11775
|
-
const handleSecretSubmit =
|
|
12390
|
+
const handleSecretSubmit = useCallback4((value) => {
|
|
11776
12391
|
const ir = inputRequestRef.current;
|
|
11777
12392
|
if (!ir.isActive || !ir.resolve) return;
|
|
11778
12393
|
const displayText = ir.isPassword ? "\u2022".repeat(value.length) : value;
|
|
@@ -11782,7 +12397,7 @@ ${procData.stdout || "(no output)"}
|
|
|
11782
12397
|
setInputRequest({ isActive: false, prompt: "", isPassword: false, resolve: null });
|
|
11783
12398
|
setSecretInput("");
|
|
11784
12399
|
}, [addMessage, setInputRequest]);
|
|
11785
|
-
|
|
12400
|
+
useInput2(useCallback4((ch, key) => {
|
|
11786
12401
|
if (key.escape) {
|
|
11787
12402
|
if (inputRequestRef.current.isActive) cancelInputRequest();
|
|
11788
12403
|
else if (isProcessingRef.current) abort();
|
|
@@ -11873,7 +12488,7 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
|
|
|
11873
12488
|
console.clear();
|
|
11874
12489
|
console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
|
|
11875
12490
|
console.log(
|
|
11876
|
-
" " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.
|
|
12491
|
+
" " + 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
12492
|
);
|
|
11878
12493
|
if (skipPermissions) {
|
|
11879
12494
|
console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions"));
|
|
@@ -11897,7 +12512,7 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
11897
12512
|
if (skipPermissions) {
|
|
11898
12513
|
console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
|
|
11899
12514
|
}
|
|
11900
|
-
console.log(chalk.hex(THEME.
|
|
12515
|
+
console.log(chalk.hex(THEME.accent.blue)(`[target] Objective: ${objective}
|
|
11901
12516
|
`));
|
|
11902
12517
|
const agent = AgentFactory.createMainAgent(skipPermissions);
|
|
11903
12518
|
if (skipPermissions) {
|
|
@@ -11919,7 +12534,7 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
11919
12534
|
if (options.output) {
|
|
11920
12535
|
const fs = await import("fs/promises");
|
|
11921
12536
|
await fs.writeFile(options.output, JSON.stringify({ result: result2 }, null, 2));
|
|
11922
|
-
console.log(chalk.hex(THEME.
|
|
12537
|
+
console.log(chalk.hex(THEME.accent.blue)(`
|
|
11923
12538
|
[+] Report saved to: ${options.output}`));
|
|
11924
12539
|
}
|
|
11925
12540
|
await shutdown(0);
|
|
@@ -11934,7 +12549,7 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
|
|
|
11934
12549
|
const opts = program.opts();
|
|
11935
12550
|
const skipPermissions = opts.dangerouslySkipPermissions || false;
|
|
11936
12551
|
console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
|
|
11937
|
-
console.log(chalk.hex(THEME.
|
|
12552
|
+
console.log(chalk.hex(THEME.accent.blue)(`
|
|
11938
12553
|
[scan] Target: ${target} (${options.scanType})
|
|
11939
12554
|
`));
|
|
11940
12555
|
const agent = AgentFactory.createMainAgent(skipPermissions);
|
|
@@ -11958,7 +12573,7 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
|
|
|
11958
12573
|
program.command("help-extended").description("Show extended help with examples").action(() => {
|
|
11959
12574
|
console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
|
|
11960
12575
|
console.log(`
|
|
11961
|
-
${chalk.hex(THEME.
|
|
12576
|
+
${chalk.hex(THEME.accent.blue)(APP_NAME + " - Autonomous Penetration Testing AI")}
|
|
11962
12577
|
|
|
11963
12578
|
${chalk.hex(THEME.status.warning)("Usage:")}
|
|
11964
12579
|
|
|
@@ -11968,24 +12583,24 @@ ${chalk.hex(THEME.status.warning)("Usage:")}
|
|
|
11968
12583
|
|
|
11969
12584
|
${chalk.hex(THEME.status.warning)("Commands:")}
|
|
11970
12585
|
|
|
11971
|
-
${chalk.hex(THEME.
|
|
11972
|
-
${chalk.hex(THEME.
|
|
11973
|
-
${chalk.hex(THEME.
|
|
12586
|
+
${chalk.hex(THEME.accent.blue)("pentesting")} Interactive TUI mode
|
|
12587
|
+
${chalk.hex(THEME.accent.blue)("pentesting run <objective>")} Run single objective
|
|
12588
|
+
${chalk.hex(THEME.accent.blue)("pentesting scan <target>")} Quick scan target
|
|
11974
12589
|
|
|
11975
12590
|
${chalk.hex(THEME.status.warning)("Options:")}
|
|
11976
12591
|
|
|
11977
|
-
${chalk.hex(THEME.
|
|
11978
|
-
${chalk.hex(THEME.
|
|
11979
|
-
${chalk.hex(THEME.
|
|
12592
|
+
${chalk.hex(THEME.accent.blue)("--dangerously-skip-permissions")} Skip all permission prompts
|
|
12593
|
+
${chalk.hex(THEME.accent.blue)("-t, --target <ip>")} Set target
|
|
12594
|
+
${chalk.hex(THEME.accent.blue)("-o, --output <file>")} Save results to file
|
|
11980
12595
|
|
|
11981
12596
|
${chalk.hex(THEME.status.warning)("Interactive Commands:")}
|
|
11982
12597
|
|
|
11983
|
-
${chalk.hex(THEME.
|
|
11984
|
-
${chalk.hex(THEME.
|
|
11985
|
-
${chalk.hex(THEME.
|
|
11986
|
-
${chalk.hex(THEME.
|
|
11987
|
-
${chalk.hex(THEME.
|
|
11988
|
-
${chalk.hex(THEME.
|
|
12598
|
+
${chalk.hex(THEME.accent.blue)("/target <ip>")} Set target
|
|
12599
|
+
${chalk.hex(THEME.accent.blue)("/start")} Start autonomous mode
|
|
12600
|
+
${chalk.hex(THEME.accent.blue)("/config")} Manage configuration
|
|
12601
|
+
${chalk.hex(THEME.accent.blue)("/hint <text>")} Provide hint
|
|
12602
|
+
${chalk.hex(THEME.accent.blue)("/findings")} Show findings
|
|
12603
|
+
${chalk.hex(THEME.accent.blue)("/reset")} Reset session
|
|
11989
12604
|
|
|
11990
12605
|
${chalk.hex(THEME.status.warning)("Examples:")}
|
|
11991
12606
|
|
|
@@ -12000,9 +12615,9 @@ ${chalk.hex(THEME.status.warning)("Examples:")}
|
|
|
12000
12615
|
|
|
12001
12616
|
${chalk.hex(THEME.status.warning)("Environment:")}
|
|
12002
12617
|
|
|
12003
|
-
${chalk.hex(THEME.
|
|
12004
|
-
${chalk.hex(THEME.
|
|
12005
|
-
${chalk.hex(THEME.
|
|
12618
|
+
${chalk.hex(THEME.accent.blue)("PENTEST_API_KEY")} Required - LLM API key
|
|
12619
|
+
${chalk.hex(THEME.accent.blue)("PENTEST_BASE_URL")} Optional - AI API base URL
|
|
12620
|
+
${chalk.hex(THEME.accent.blue)("PENTEST_MODEL")} Optional - Model override
|
|
12006
12621
|
`);
|
|
12007
12622
|
});
|
|
12008
12623
|
program.parse();
|