pentesting 0.22.0 → 0.23.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
CHANGED
|
@@ -90,7 +90,21 @@ var DISPLAY_LIMITS = {
|
|
|
90
90
|
/** Max endpoints to sample */
|
|
91
91
|
ENDPOINT_SAMPLE: 5,
|
|
92
92
|
/** Purpose truncation length */
|
|
93
|
-
PURPOSE_TRUNCATE: 27
|
|
93
|
+
PURPOSE_TRUNCATE: 27,
|
|
94
|
+
/** Max characters for command description in background process */
|
|
95
|
+
COMMAND_DESCRIPTION_PREVIEW: 80,
|
|
96
|
+
/** Max characters for hash preview in tool output */
|
|
97
|
+
HASH_PREVIEW_LENGTH: 20,
|
|
98
|
+
/** Max characters for loot detail preview */
|
|
99
|
+
LOOT_DETAIL_PREVIEW: 30,
|
|
100
|
+
/** Max characters for link text preview in browser */
|
|
101
|
+
LINK_TEXT_PREVIEW: 100,
|
|
102
|
+
/** Prefix length for API key redaction in logs */
|
|
103
|
+
API_KEY_PREFIX: 8,
|
|
104
|
+
/** Prefix length for sensitive value redaction */
|
|
105
|
+
REDACT_PREFIX: 4,
|
|
106
|
+
/** Max characters for raw JSON error preview */
|
|
107
|
+
RAW_JSON_ERROR_PREVIEW: 500
|
|
94
108
|
};
|
|
95
109
|
var AGENT_LIMITS = {
|
|
96
110
|
/** Maximum agent loop iterations — generous to allow complex tasks.
|
|
@@ -123,7 +137,17 @@ var AGENT_LIMITS = {
|
|
|
123
137
|
/** Max chars of error text to include in web_search suggestion */
|
|
124
138
|
ERROR_SEARCH_PREVIEW_SLICE: 50,
|
|
125
139
|
/** How many consecutive identical blocks before warning + forcing alternative */
|
|
126
|
-
MAX_BLOCKED_BEFORE_WARN: 2
|
|
140
|
+
MAX_BLOCKED_BEFORE_WARN: 2,
|
|
141
|
+
/** Maximum action log entries to retain in memory.
|
|
142
|
+
* WHY: actionLog grows unboundedly during long engagements.
|
|
143
|
+
* Older entries are pruned to prevent memory bloat and oversized session files.
|
|
144
|
+
*/
|
|
145
|
+
MAX_ACTION_LOG: 200,
|
|
146
|
+
/** Maximum session files to keep on disk.
|
|
147
|
+
* WHY: Each save creates a timestamped .json in .pentesting/sessions/.
|
|
148
|
+
* Without rotation, disk usage grows unboundedly across engagements.
|
|
149
|
+
*/
|
|
150
|
+
MAX_SESSION_FILES: 10
|
|
127
151
|
};
|
|
128
152
|
|
|
129
153
|
// src/shared/constants/patterns.ts
|
|
@@ -150,14 +174,16 @@ var EXIT_CODES = {
|
|
|
150
174
|
/** Process killed by SIGINT (Ctrl+C) */
|
|
151
175
|
SIGINT: 130,
|
|
152
176
|
/** Process killed by SIGTERM */
|
|
153
|
-
SIGTERM: 143
|
|
177
|
+
SIGTERM: 143,
|
|
178
|
+
/** Process killed by SIGKILL */
|
|
179
|
+
SIGKILL: 137
|
|
154
180
|
};
|
|
155
181
|
|
|
156
182
|
// src/shared/constants/agent.ts
|
|
157
183
|
var ID_LENGTH = AGENT_LIMITS.ID_LENGTH;
|
|
158
184
|
var ID_RADIX = AGENT_LIMITS.ID_RADIX;
|
|
159
185
|
var APP_NAME = "Pentest AI";
|
|
160
|
-
var APP_VERSION = "0.
|
|
186
|
+
var APP_VERSION = "0.23.0";
|
|
161
187
|
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
162
188
|
var LLM_ROLES = {
|
|
163
189
|
SYSTEM: "system",
|
|
@@ -456,13 +482,13 @@ var DEFAULTS = {
|
|
|
456
482
|
};
|
|
457
483
|
|
|
458
484
|
// src/engine/process-manager.ts
|
|
459
|
-
import { spawn as
|
|
460
|
-
import { readFileSync as readFileSync2, existsSync as existsSync3, unlinkSync, writeFileSync as
|
|
485
|
+
import { spawn as spawn3, execSync as execSync2 } from "child_process";
|
|
486
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, unlinkSync, writeFileSync as writeFileSync3, appendFileSync as appendFileSync2 } from "fs";
|
|
461
487
|
|
|
462
488
|
// src/engine/tools-base.ts
|
|
463
|
-
import { spawn } from "child_process";
|
|
464
|
-
import { readFileSync, existsSync as existsSync2, writeFileSync } from "fs";
|
|
465
|
-
import { join, dirname } from "path";
|
|
489
|
+
import { spawn as spawn2 } from "child_process";
|
|
490
|
+
import { readFileSync, existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
491
|
+
import { join as join2, dirname } from "path";
|
|
466
492
|
import { tmpdir } from "os";
|
|
467
493
|
|
|
468
494
|
// src/shared/utils/file-utils.ts
|
|
@@ -571,7 +597,7 @@ var ORPHAN_PROCESS_NAMES = [
|
|
|
571
597
|
"python"
|
|
572
598
|
];
|
|
573
599
|
|
|
574
|
-
// src/shared/utils/command-
|
|
600
|
+
// src/shared/utils/command-security-lists.ts
|
|
575
601
|
var ALLOWED_BINARIES = /* @__PURE__ */ new Set([
|
|
576
602
|
// Network scanning
|
|
577
603
|
"nmap",
|
|
@@ -799,6 +825,131 @@ var BLOCKED_BINARIES = /* @__PURE__ */ new Set([
|
|
|
799
825
|
"nft",
|
|
800
826
|
"crontab"
|
|
801
827
|
]);
|
|
828
|
+
|
|
829
|
+
// src/shared/utils/debug-logger.ts
|
|
830
|
+
import { appendFileSync, writeFileSync } from "fs";
|
|
831
|
+
import { join } from "path";
|
|
832
|
+
|
|
833
|
+
// src/shared/constants/paths.ts
|
|
834
|
+
import path from "path";
|
|
835
|
+
import { fileURLToPath } from "url";
|
|
836
|
+
import { homedir } from "os";
|
|
837
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
838
|
+
var __dirname = path.dirname(__filename);
|
|
839
|
+
var PROJECT_ROOT = path.resolve(__dirname, "../../../");
|
|
840
|
+
var WORKSPACE_DIR_NAME = ".pentesting";
|
|
841
|
+
function getWorkspaceRoot() {
|
|
842
|
+
return path.join(homedir(), WORKSPACE_DIR_NAME);
|
|
843
|
+
}
|
|
844
|
+
var WORKSPACE = {
|
|
845
|
+
/** Root directory (resolved lazily via getWorkspaceRoot) */
|
|
846
|
+
get ROOT() {
|
|
847
|
+
return getWorkspaceRoot();
|
|
848
|
+
},
|
|
849
|
+
/** Per-session state snapshots */
|
|
850
|
+
get SESSIONS() {
|
|
851
|
+
return path.join(getWorkspaceRoot(), "sessions");
|
|
852
|
+
},
|
|
853
|
+
/** Debug logs */
|
|
854
|
+
get DEBUG() {
|
|
855
|
+
return path.join(getWorkspaceRoot(), "debug");
|
|
856
|
+
},
|
|
857
|
+
/** Generated reports */
|
|
858
|
+
get REPORTS() {
|
|
859
|
+
return path.join(getWorkspaceRoot(), "reports");
|
|
860
|
+
},
|
|
861
|
+
/** Downloaded loot, captured files */
|
|
862
|
+
get LOOT() {
|
|
863
|
+
return path.join(getWorkspaceRoot(), "loot");
|
|
864
|
+
},
|
|
865
|
+
/** Temporary files for active operations */
|
|
866
|
+
get TEMP() {
|
|
867
|
+
return path.join(getWorkspaceRoot(), "temp");
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
var PATHS = {
|
|
871
|
+
ROOT: PROJECT_ROOT,
|
|
872
|
+
SRC: path.join(PROJECT_ROOT, "src"),
|
|
873
|
+
DIST: path.join(PROJECT_ROOT, "dist")
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
// src/shared/utils/debug-logger.ts
|
|
877
|
+
var DebugLogger = class _DebugLogger {
|
|
878
|
+
static instance;
|
|
879
|
+
logPath;
|
|
880
|
+
initialized = false;
|
|
881
|
+
constructor(clearOnInit = false) {
|
|
882
|
+
const debugDir = WORKSPACE.DEBUG;
|
|
883
|
+
try {
|
|
884
|
+
ensureDirExists(debugDir);
|
|
885
|
+
this.logPath = join(debugDir, "debug.log");
|
|
886
|
+
if (clearOnInit) {
|
|
887
|
+
this.clear();
|
|
888
|
+
}
|
|
889
|
+
this.initialized = true;
|
|
890
|
+
this.log("general", "=== DEBUG LOGGER INITIALIZED ===");
|
|
891
|
+
this.log("general", `Log file: ${this.logPath}`);
|
|
892
|
+
} catch (e) {
|
|
893
|
+
console.error("[DebugLogger] Failed to initialize:", e);
|
|
894
|
+
this.logPath = "";
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
static getInstance(clearOnInit = false) {
|
|
898
|
+
if (!_DebugLogger.instance) {
|
|
899
|
+
_DebugLogger.instance = new _DebugLogger(clearOnInit);
|
|
900
|
+
}
|
|
901
|
+
return _DebugLogger.instance;
|
|
902
|
+
}
|
|
903
|
+
/** Reset the singleton instance (used by initDebugLogger) */
|
|
904
|
+
static resetInstance(clearOnInit = false) {
|
|
905
|
+
_DebugLogger.instance = new _DebugLogger(clearOnInit);
|
|
906
|
+
return _DebugLogger.instance;
|
|
907
|
+
}
|
|
908
|
+
log(category, message, data) {
|
|
909
|
+
if (!this.initialized || !this.logPath) return;
|
|
910
|
+
try {
|
|
911
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
912
|
+
let logLine = `[${timestamp}] [${category.toUpperCase()}] ${message}`;
|
|
913
|
+
if (data !== void 0) {
|
|
914
|
+
logLine += ` | ${JSON.stringify(data)}`;
|
|
915
|
+
}
|
|
916
|
+
logLine += "\n";
|
|
917
|
+
appendFileSync(this.logPath, logLine);
|
|
918
|
+
} catch (e) {
|
|
919
|
+
console.error("[DebugLogger] Write error:", e);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
logRaw(category, label, raw) {
|
|
923
|
+
if (!this.initialized || !this.logPath) return;
|
|
924
|
+
try {
|
|
925
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
926
|
+
const logLine = `[${timestamp}] [${category.toUpperCase()}] ${label}:
|
|
927
|
+
${raw}
|
|
928
|
+
---
|
|
929
|
+
`;
|
|
930
|
+
appendFileSync(this.logPath, logLine);
|
|
931
|
+
} catch (e) {
|
|
932
|
+
console.error("[DebugLogger] Write error:", e);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
clear() {
|
|
936
|
+
if (!this.logPath) return;
|
|
937
|
+
try {
|
|
938
|
+
writeFileSync(this.logPath, "");
|
|
939
|
+
} catch (e) {
|
|
940
|
+
console.error("[DebugLogger] Clear error:", e);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
var logger = DebugLogger.getInstance(false);
|
|
945
|
+
function initDebugLogger() {
|
|
946
|
+
DebugLogger.resetInstance(true);
|
|
947
|
+
}
|
|
948
|
+
function debugLog(category, message, data) {
|
|
949
|
+
logger.log(category, message, data);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/shared/utils/command-validator.ts
|
|
802
953
|
function validateCommand(command) {
|
|
803
954
|
if (!command || typeof command !== "string") {
|
|
804
955
|
return { safe: false, error: "Empty or invalid command" };
|
|
@@ -893,7 +1044,7 @@ function validateSingleCommand(command) {
|
|
|
893
1044
|
};
|
|
894
1045
|
}
|
|
895
1046
|
if (!ALLOWED_BINARIES.has(binary)) {
|
|
896
|
-
|
|
1047
|
+
debugLog("security", `Unknown binary '${binary}' - use with caution`);
|
|
897
1048
|
}
|
|
898
1049
|
}
|
|
899
1050
|
const redirectResult = validateRedirects(command);
|
|
@@ -992,7 +1143,8 @@ function extractBinary(command) {
|
|
|
992
1143
|
return binary.toLowerCase();
|
|
993
1144
|
}
|
|
994
1145
|
|
|
995
|
-
// src/engine/
|
|
1146
|
+
// src/engine/tool-auto-installer.ts
|
|
1147
|
+
import { spawn } from "child_process";
|
|
996
1148
|
var TOOL_PACKAGE_MAP = {
|
|
997
1149
|
"nmap": { apt: "nmap", brew: "nmap" },
|
|
998
1150
|
"masscan": { apt: "masscan", brew: "masscan" },
|
|
@@ -1046,15 +1198,7 @@ var TOOL_PACKAGE_MAP = {
|
|
|
1046
1198
|
"evil-winrm": { apt: "evil-winrm", brew: "evil-winrm" },
|
|
1047
1199
|
"netexec": { apt: "netexec", brew: "netexec" }
|
|
1048
1200
|
};
|
|
1049
|
-
var globalEventEmitter = null;
|
|
1050
|
-
function setCommandEventEmitter(emitter) {
|
|
1051
|
-
globalEventEmitter = emitter;
|
|
1052
|
-
}
|
|
1053
1201
|
var installedTools = /* @__PURE__ */ new Set();
|
|
1054
|
-
var globalInputHandler = null;
|
|
1055
|
-
function setInputHandler(handler) {
|
|
1056
|
-
globalInputHandler = handler;
|
|
1057
|
-
}
|
|
1058
1202
|
async function detectPackageManager() {
|
|
1059
1203
|
const managers = [
|
|
1060
1204
|
{ name: "apt", check: "which apt-get" },
|
|
@@ -1091,7 +1235,7 @@ function isCommandNotFound(stderr, stdout) {
|
|
|
1091
1235
|
const toolName = toolMatch ? toolMatch[1] : null;
|
|
1092
1236
|
return { missing: true, toolName };
|
|
1093
1237
|
}
|
|
1094
|
-
async function installTool(toolName) {
|
|
1238
|
+
async function installTool(toolName, eventEmitter, inputHandler) {
|
|
1095
1239
|
const pkgInfo = TOOL_PACKAGE_MAP[toolName];
|
|
1096
1240
|
if (!pkgInfo) {
|
|
1097
1241
|
return {
|
|
@@ -1114,7 +1258,7 @@ async function installTool(toolName) {
|
|
|
1114
1258
|
};
|
|
1115
1259
|
}
|
|
1116
1260
|
const packageName = pkgInfo[pkgManager] || pkgInfo.apt;
|
|
1117
|
-
|
|
1261
|
+
eventEmitter?.({
|
|
1118
1262
|
type: COMMAND_EVENT_TYPES.TOOL_INSTALL,
|
|
1119
1263
|
message: `Installing missing tool: ${toolName}`,
|
|
1120
1264
|
detail: `Using ${pkgManager} to install ${packageName}`
|
|
@@ -1134,10 +1278,10 @@ async function installTool(toolName) {
|
|
|
1134
1278
|
let stdout = "";
|
|
1135
1279
|
let stderr = "";
|
|
1136
1280
|
const checkForSudoPrompt = async (data) => {
|
|
1137
|
-
if (!
|
|
1281
|
+
if (!inputHandler) return;
|
|
1138
1282
|
for (const pattern of INPUT_PROMPT_PATTERNS) {
|
|
1139
1283
|
if (pattern.test(data)) {
|
|
1140
|
-
const userInput = await
|
|
1284
|
+
const userInput = await inputHandler(data.trim());
|
|
1141
1285
|
if (userInput !== null && child.stdin.writable) {
|
|
1142
1286
|
child.stdin.write(userInput + "\n");
|
|
1143
1287
|
}
|
|
@@ -1157,14 +1301,14 @@ async function installTool(toolName) {
|
|
|
1157
1301
|
});
|
|
1158
1302
|
child.on("close", (code) => {
|
|
1159
1303
|
if (code === 0) {
|
|
1160
|
-
|
|
1304
|
+
eventEmitter?.({
|
|
1161
1305
|
type: COMMAND_EVENT_TYPES.TOOL_INSTALLED,
|
|
1162
1306
|
message: `Successfully installed: ${toolName}`,
|
|
1163
1307
|
detail: `Package: ${packageName}`
|
|
1164
1308
|
});
|
|
1165
1309
|
resolve({ success: true, output: `Successfully installed ${toolName}` });
|
|
1166
1310
|
} else {
|
|
1167
|
-
|
|
1311
|
+
eventEmitter?.({
|
|
1168
1312
|
type: COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED,
|
|
1169
1313
|
message: `Failed to install: ${toolName}`,
|
|
1170
1314
|
detail: stderr.slice(0, SYSTEM_LIMITS.MAX_ERROR_DETAIL_SLICE)
|
|
@@ -1180,6 +1324,16 @@ async function installTool(toolName) {
|
|
|
1180
1324
|
});
|
|
1181
1325
|
});
|
|
1182
1326
|
}
|
|
1327
|
+
|
|
1328
|
+
// src/engine/tools-base.ts
|
|
1329
|
+
var globalEventEmitter = null;
|
|
1330
|
+
function setCommandEventEmitter(emitter) {
|
|
1331
|
+
globalEventEmitter = emitter;
|
|
1332
|
+
}
|
|
1333
|
+
var globalInputHandler = null;
|
|
1334
|
+
function setInputHandler(handler) {
|
|
1335
|
+
globalInputHandler = handler;
|
|
1336
|
+
}
|
|
1183
1337
|
async function runCommand(command, args = [], options = {}) {
|
|
1184
1338
|
const fullCommand = args.length > 0 ? `${command} ${args.join(" ")}` : command;
|
|
1185
1339
|
const validation = validateCommand(fullCommand);
|
|
@@ -1208,7 +1362,7 @@ async function runCommand(command, args = [], options = {}) {
|
|
|
1208
1362
|
message: `${STATUS_MARKERS.WARNING} Tool not found: ${toolName}`,
|
|
1209
1363
|
detail: `Attempting to install...`
|
|
1210
1364
|
});
|
|
1211
|
-
const installResult = await installTool(toolName);
|
|
1365
|
+
const installResult = await installTool(toolName, globalEventEmitter, globalInputHandler);
|
|
1212
1366
|
if (!installResult.success) {
|
|
1213
1367
|
return {
|
|
1214
1368
|
success: false,
|
|
@@ -1233,7 +1387,7 @@ async function executeCommandOnce(command, args = [], options = {}) {
|
|
|
1233
1387
|
type: COMMAND_EVENT_TYPES.COMMAND_START,
|
|
1234
1388
|
message: `Executing: ${command.slice(0, DISPLAY_LIMITS.COMMAND_PREVIEW)}${command.length > DISPLAY_LIMITS.COMMAND_PREVIEW ? "..." : ""}`
|
|
1235
1389
|
});
|
|
1236
|
-
const child =
|
|
1390
|
+
const child = spawn2("sh", ["-c", command], {
|
|
1237
1391
|
timeout,
|
|
1238
1392
|
env: { ...process.env, ...options.env },
|
|
1239
1393
|
cwd: options.cwd
|
|
@@ -1339,7 +1493,7 @@ async function writeFileContent(filePath, content) {
|
|
|
1339
1493
|
try {
|
|
1340
1494
|
const dir = dirname(filePath);
|
|
1341
1495
|
ensureDirExists(dir);
|
|
1342
|
-
|
|
1496
|
+
writeFileSync2(filePath, content, "utf-8");
|
|
1343
1497
|
return {
|
|
1344
1498
|
success: true,
|
|
1345
1499
|
output: `Written to ${filePath}`
|
|
@@ -1354,7 +1508,7 @@ async function writeFileContent(filePath, content) {
|
|
|
1354
1508
|
}
|
|
1355
1509
|
}
|
|
1356
1510
|
function createTempFile(suffix = "") {
|
|
1357
|
-
return
|
|
1511
|
+
return join2(tmpdir(), generateTempFilename(suffix));
|
|
1358
1512
|
}
|
|
1359
1513
|
|
|
1360
1514
|
// src/engine/process-detector.ts
|
|
@@ -1512,12 +1666,12 @@ function startBackgroundProcess(command, options = {}) {
|
|
|
1512
1666
|
const { tags, port, role, isInteractive } = detectProcessRole(command);
|
|
1513
1667
|
let wrappedCmd;
|
|
1514
1668
|
if (isInteractive) {
|
|
1515
|
-
|
|
1669
|
+
writeFileSync3(stdinFile, "", "utf-8");
|
|
1516
1670
|
wrappedCmd = `tail -f ${stdinFile} | ${command} > ${stdoutFile} 2> ${stderrFile}`;
|
|
1517
1671
|
} else {
|
|
1518
1672
|
wrappedCmd = `${command} > ${stdoutFile} 2> ${stderrFile}`;
|
|
1519
1673
|
}
|
|
1520
|
-
const child =
|
|
1674
|
+
const child = spawn3("sh", ["-c", wrappedCmd], {
|
|
1521
1675
|
detached: true,
|
|
1522
1676
|
stdio: "ignore",
|
|
1523
1677
|
cwd: options.cwd,
|
|
@@ -1571,7 +1725,7 @@ async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WA
|
|
|
1571
1725
|
} catch {
|
|
1572
1726
|
}
|
|
1573
1727
|
try {
|
|
1574
|
-
|
|
1728
|
+
appendFileSync2(proc.stdinFile, input + "\n", "utf-8");
|
|
1575
1729
|
} catch (error) {
|
|
1576
1730
|
return { success: false, output: `Failed to send input: ${error}`, newOutput: "" };
|
|
1577
1731
|
}
|
|
@@ -2140,6 +2294,9 @@ var SharedState = class {
|
|
|
2140
2294
|
timestamp: Date.now(),
|
|
2141
2295
|
...action
|
|
2142
2296
|
});
|
|
2297
|
+
if (this.data.actionLog.length > AGENT_LIMITS.MAX_ACTION_LOG) {
|
|
2298
|
+
this.data.actionLog.splice(0, this.data.actionLog.length - AGENT_LIMITS.MAX_ACTION_LOG);
|
|
2299
|
+
}
|
|
2143
2300
|
}
|
|
2144
2301
|
getRecentActions(count = DISPLAY_LIMITS.COMPACT_LIST_ITEMS) {
|
|
2145
2302
|
return this.data.actionLog.slice(-count);
|
|
@@ -2564,7 +2721,7 @@ Used ports: ${usedPorts.join(", ")}
|
|
|
2564
2721
|
}
|
|
2565
2722
|
try {
|
|
2566
2723
|
const proc = startBackgroundProcess(command, {
|
|
2567
|
-
description: command.slice(0,
|
|
2724
|
+
description: command.slice(0, DISPLAY_LIMITS.COMMAND_DESCRIPTION_PREVIEW),
|
|
2568
2725
|
purpose: params.purpose
|
|
2569
2726
|
});
|
|
2570
2727
|
const portInfo = proc.listeningPort ? `
|
|
@@ -2937,7 +3094,7 @@ Types: credential, hash, token, ssh_key, api_key, file, session, ticket, certifi
|
|
|
2937
3094
|
success: true,
|
|
2938
3095
|
output: `Loot recorded: [${lootType}] from ${p.host}
|
|
2939
3096
|
Detail: ${p.detail}
|
|
2940
|
-
` + (crackableTypes.includes(lootType) ? `This is crackable. Consider: hash_crack({ hashes: "${p.detail.slice(0,
|
|
3097
|
+
` + (crackableTypes.includes(lootType) ? `This is crackable. Consider: hash_crack({ hashes: "${p.detail.slice(0, DISPLAY_LIMITS.LOOT_DETAIL_PREVIEW)}..." })` : `Consider credential reuse / lateral movement with this loot.`)
|
|
2941
3098
|
};
|
|
2942
3099
|
}
|
|
2943
3100
|
},
|
|
@@ -2971,10 +3128,10 @@ Detail: ${p.detail}
|
|
|
2971
3128
|
import { execFileSync } from "child_process";
|
|
2972
3129
|
|
|
2973
3130
|
// src/shared/utils/config.ts
|
|
2974
|
-
import
|
|
2975
|
-
import { fileURLToPath } from "url";
|
|
2976
|
-
var
|
|
2977
|
-
var
|
|
3131
|
+
import path2 from "path";
|
|
3132
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3133
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
3134
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
2978
3135
|
var ENV_KEYS = {
|
|
2979
3136
|
API_KEY: "PENTEST_API_KEY",
|
|
2980
3137
|
BASE_URL: "PENTEST_BASE_URL",
|
|
@@ -3022,129 +3179,6 @@ var SEARCH_LIMIT = {
|
|
|
3022
3179
|
TIMEOUT_MS: 1e4
|
|
3023
3180
|
};
|
|
3024
3181
|
|
|
3025
|
-
// src/shared/utils/debug-logger.ts
|
|
3026
|
-
import { appendFileSync as appendFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
3027
|
-
import { join as join2 } from "path";
|
|
3028
|
-
|
|
3029
|
-
// src/shared/constants/paths.ts
|
|
3030
|
-
import path2 from "path";
|
|
3031
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3032
|
-
import { homedir } from "os";
|
|
3033
|
-
var __filename2 = fileURLToPath2(import.meta.url);
|
|
3034
|
-
var __dirname2 = path2.dirname(__filename2);
|
|
3035
|
-
var PROJECT_ROOT = path2.resolve(__dirname2, "../../../");
|
|
3036
|
-
var WORKSPACE_DIR_NAME = ".pentesting";
|
|
3037
|
-
function getWorkspaceRoot() {
|
|
3038
|
-
return path2.join(homedir(), WORKSPACE_DIR_NAME);
|
|
3039
|
-
}
|
|
3040
|
-
var WORKSPACE = {
|
|
3041
|
-
/** Root directory (resolved lazily via getWorkspaceRoot) */
|
|
3042
|
-
get ROOT() {
|
|
3043
|
-
return getWorkspaceRoot();
|
|
3044
|
-
},
|
|
3045
|
-
/** Per-session state snapshots */
|
|
3046
|
-
get SESSIONS() {
|
|
3047
|
-
return path2.join(getWorkspaceRoot(), "sessions");
|
|
3048
|
-
},
|
|
3049
|
-
/** Debug logs */
|
|
3050
|
-
get DEBUG() {
|
|
3051
|
-
return path2.join(getWorkspaceRoot(), "debug");
|
|
3052
|
-
},
|
|
3053
|
-
/** Generated reports */
|
|
3054
|
-
get REPORTS() {
|
|
3055
|
-
return path2.join(getWorkspaceRoot(), "reports");
|
|
3056
|
-
},
|
|
3057
|
-
/** Downloaded loot, captured files */
|
|
3058
|
-
get LOOT() {
|
|
3059
|
-
return path2.join(getWorkspaceRoot(), "loot");
|
|
3060
|
-
},
|
|
3061
|
-
/** Temporary files for active operations */
|
|
3062
|
-
get TEMP() {
|
|
3063
|
-
return path2.join(getWorkspaceRoot(), "temp");
|
|
3064
|
-
}
|
|
3065
|
-
};
|
|
3066
|
-
var PATHS = {
|
|
3067
|
-
ROOT: PROJECT_ROOT,
|
|
3068
|
-
SRC: path2.join(PROJECT_ROOT, "src"),
|
|
3069
|
-
DIST: path2.join(PROJECT_ROOT, "dist")
|
|
3070
|
-
};
|
|
3071
|
-
|
|
3072
|
-
// src/shared/utils/debug-logger.ts
|
|
3073
|
-
var DebugLogger = class _DebugLogger {
|
|
3074
|
-
static instance;
|
|
3075
|
-
logPath;
|
|
3076
|
-
initialized = false;
|
|
3077
|
-
constructor(clearOnInit = false) {
|
|
3078
|
-
const debugDir = WORKSPACE.DEBUG;
|
|
3079
|
-
try {
|
|
3080
|
-
ensureDirExists(debugDir);
|
|
3081
|
-
this.logPath = join2(debugDir, "debug.log");
|
|
3082
|
-
if (clearOnInit) {
|
|
3083
|
-
this.clear();
|
|
3084
|
-
}
|
|
3085
|
-
this.initialized = true;
|
|
3086
|
-
this.log("general", "=== DEBUG LOGGER INITIALIZED ===");
|
|
3087
|
-
this.log("general", `Log file: ${this.logPath}`);
|
|
3088
|
-
} catch (e) {
|
|
3089
|
-
console.error("[DebugLogger] Failed to initialize:", e);
|
|
3090
|
-
this.logPath = "";
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
static getInstance(clearOnInit = false) {
|
|
3094
|
-
if (!_DebugLogger.instance) {
|
|
3095
|
-
_DebugLogger.instance = new _DebugLogger(clearOnInit);
|
|
3096
|
-
}
|
|
3097
|
-
return _DebugLogger.instance;
|
|
3098
|
-
}
|
|
3099
|
-
/** Reset the singleton instance (used by initDebugLogger) */
|
|
3100
|
-
static resetInstance(clearOnInit = false) {
|
|
3101
|
-
_DebugLogger.instance = new _DebugLogger(clearOnInit);
|
|
3102
|
-
return _DebugLogger.instance;
|
|
3103
|
-
}
|
|
3104
|
-
log(category, message, data) {
|
|
3105
|
-
if (!this.initialized || !this.logPath) return;
|
|
3106
|
-
try {
|
|
3107
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3108
|
-
let logLine = `[${timestamp}] [${category.toUpperCase()}] ${message}`;
|
|
3109
|
-
if (data !== void 0) {
|
|
3110
|
-
logLine += ` | ${JSON.stringify(data)}`;
|
|
3111
|
-
}
|
|
3112
|
-
logLine += "\n";
|
|
3113
|
-
appendFileSync2(this.logPath, logLine);
|
|
3114
|
-
} catch (e) {
|
|
3115
|
-
console.error("[DebugLogger] Write error:", e);
|
|
3116
|
-
}
|
|
3117
|
-
}
|
|
3118
|
-
logRaw(category, label, raw) {
|
|
3119
|
-
if (!this.initialized || !this.logPath) return;
|
|
3120
|
-
try {
|
|
3121
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3122
|
-
const logLine = `[${timestamp}] [${category.toUpperCase()}] ${label}:
|
|
3123
|
-
${raw}
|
|
3124
|
-
---
|
|
3125
|
-
`;
|
|
3126
|
-
appendFileSync2(this.logPath, logLine);
|
|
3127
|
-
} catch (e) {
|
|
3128
|
-
console.error("[DebugLogger] Write error:", e);
|
|
3129
|
-
}
|
|
3130
|
-
}
|
|
3131
|
-
clear() {
|
|
3132
|
-
if (!this.logPath) return;
|
|
3133
|
-
try {
|
|
3134
|
-
writeFileSync3(this.logPath, "");
|
|
3135
|
-
} catch (e) {
|
|
3136
|
-
console.error("[DebugLogger] Clear error:", e);
|
|
3137
|
-
}
|
|
3138
|
-
}
|
|
3139
|
-
};
|
|
3140
|
-
var logger = DebugLogger.getInstance(false);
|
|
3141
|
-
function initDebugLogger() {
|
|
3142
|
-
DebugLogger.resetInstance(true);
|
|
3143
|
-
}
|
|
3144
|
-
function debugLog(category, message, data) {
|
|
3145
|
-
logger.log(category, message, data);
|
|
3146
|
-
}
|
|
3147
|
-
|
|
3148
3182
|
// src/engine/tools/web-browser.ts
|
|
3149
3183
|
import { join as join5 } from "path";
|
|
3150
3184
|
import { tmpdir as tmpdir3 } from "os";
|
|
@@ -3196,7 +3230,7 @@ var PLAYWRIGHT_SCRIPT = {
|
|
|
3196
3230
|
};
|
|
3197
3231
|
|
|
3198
3232
|
// src/engine/tools/web-browser-setup.ts
|
|
3199
|
-
import { spawn as
|
|
3233
|
+
import { spawn as spawn4 } from "child_process";
|
|
3200
3234
|
import { existsSync as existsSync4 } from "fs";
|
|
3201
3235
|
import { join as join3, dirname as dirname2 } from "path";
|
|
3202
3236
|
function getPlaywrightPath() {
|
|
@@ -3217,12 +3251,12 @@ function getPlaywrightPath() {
|
|
|
3217
3251
|
async function checkPlaywright() {
|
|
3218
3252
|
try {
|
|
3219
3253
|
const result2 = await new Promise((resolve) => {
|
|
3220
|
-
const child =
|
|
3254
|
+
const child = spawn4(PLAYWRIGHT_CMD.NPM, [PLAYWRIGHT_CMD.PLAYWRIGHT, PLAYWRIGHT_ACTION.VERSION], { shell: true });
|
|
3221
3255
|
let stdout = "";
|
|
3222
3256
|
child.stdout.on("data", (data) => stdout += data);
|
|
3223
3257
|
child.on("close", (code) => {
|
|
3224
3258
|
if (code === 0) {
|
|
3225
|
-
const browserCheck =
|
|
3259
|
+
const browserCheck = spawn4(PLAYWRIGHT_CMD.NPM, [PLAYWRIGHT_CMD.PLAYWRIGHT, PLAYWRIGHT_BROWSER.CHROMIUM, PLAYWRIGHT_ACTION.HELP], { shell: true });
|
|
3226
3260
|
browserCheck.on("close", (browserCode) => {
|
|
3227
3261
|
resolve({ installed: true, browserInstalled: browserCode === 0 });
|
|
3228
3262
|
});
|
|
@@ -3240,7 +3274,7 @@ async function checkPlaywright() {
|
|
|
3240
3274
|
}
|
|
3241
3275
|
async function installPlaywright() {
|
|
3242
3276
|
return new Promise((resolve) => {
|
|
3243
|
-
const child =
|
|
3277
|
+
const child = spawn4(PLAYWRIGHT_CMD.NPM, [PLAYWRIGHT_CMD.PLAYWRIGHT, PLAYWRIGHT_ACTION.INSTALL, PLAYWRIGHT_BROWSER.CHROMIUM], {
|
|
3244
3278
|
shell: true,
|
|
3245
3279
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3246
3280
|
});
|
|
@@ -3262,7 +3296,7 @@ async function installPlaywright() {
|
|
|
3262
3296
|
}
|
|
3263
3297
|
|
|
3264
3298
|
// src/engine/tools/web-browser-script.ts
|
|
3265
|
-
import { spawn as
|
|
3299
|
+
import { spawn as spawn5 } from "child_process";
|
|
3266
3300
|
import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
3267
3301
|
import { join as join4 } from "path";
|
|
3268
3302
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -3289,7 +3323,7 @@ function runPlaywrightScript(script, timeout, scriptPrefix) {
|
|
|
3289
3323
|
join4(process.cwd(), "node_modules"),
|
|
3290
3324
|
process.env.NODE_PATH || ""
|
|
3291
3325
|
].filter(Boolean).join(":");
|
|
3292
|
-
const child =
|
|
3326
|
+
const child = spawn5(PLAYWRIGHT_CMD.NODE, [scriptPath], {
|
|
3293
3327
|
timeout: timeout + PLAYWRIGHT_SCRIPT.SPAWN_TIMEOUT_BUFFER_MS,
|
|
3294
3328
|
env: { ...process.env, NODE_PATH: nodePathDirs }
|
|
3295
3329
|
});
|
|
@@ -3392,7 +3426,7 @@ const { chromium } = require(${safePlaywrightPath});
|
|
|
3392
3426
|
result.links = await page.evaluate(() => {
|
|
3393
3427
|
return Array.from(document.querySelectorAll('a[href]')).map(a => ({
|
|
3394
3428
|
href: a.href,
|
|
3395
|
-
text: a.textContent.trim().slice(0,
|
|
3429
|
+
text: a.textContent.trim().slice(0, ${DISPLAY_LIMITS.LINK_TEXT_PREVIEW})
|
|
3396
3430
|
})).slice(0, ${BROWSER_LIMITS.MAX_LINKS_EXTRACTION});
|
|
3397
3431
|
});` : ""}
|
|
3398
3432
|
|
|
@@ -3441,11 +3475,11 @@ function formatBrowserOutput(data, options) {
|
|
|
3441
3475
|
}
|
|
3442
3476
|
if (data.links && options.extractLinks && data.links.length > 0) {
|
|
3443
3477
|
lines.push(`## Links (${data.links.length} found)`);
|
|
3444
|
-
data.links.slice(0,
|
|
3478
|
+
data.links.slice(0, DISPLAY_LIMITS.LINKS_PREVIEW).forEach((link) => {
|
|
3445
3479
|
lines.push(`- [${link.text || "no text"}](${link.href})`);
|
|
3446
3480
|
});
|
|
3447
|
-
if (data.links.length >
|
|
3448
|
-
lines.push(`... and ${data.links.length -
|
|
3481
|
+
if (data.links.length > DISPLAY_LIMITS.LINKS_PREVIEW) {
|
|
3482
|
+
lines.push(`... and ${data.links.length - DISPLAY_LIMITS.LINKS_PREVIEW} more`);
|
|
3449
3483
|
}
|
|
3450
3484
|
lines.push("");
|
|
3451
3485
|
}
|
|
@@ -3600,146 +3634,10 @@ async function webSearchWithBrowser(query, engine = "google") {
|
|
|
3600
3634
|
});
|
|
3601
3635
|
}
|
|
3602
3636
|
|
|
3603
|
-
// src/engine/
|
|
3637
|
+
// src/engine/web-search-providers.ts
|
|
3604
3638
|
function getErrorMessage(error) {
|
|
3605
3639
|
return error instanceof Error ? error.message : String(error);
|
|
3606
3640
|
}
|
|
3607
|
-
async function parseNmap(xmlPath) {
|
|
3608
|
-
try {
|
|
3609
|
-
const fileResult = await readFileContent(xmlPath);
|
|
3610
|
-
if (!fileResult.success) {
|
|
3611
|
-
return fileResult;
|
|
3612
|
-
}
|
|
3613
|
-
const xmlContent = fileResult.output;
|
|
3614
|
-
const results = {
|
|
3615
|
-
targets: [],
|
|
3616
|
-
summary: { totalTargets: 0, openPorts: 0, servicesFound: 0 }
|
|
3617
|
-
};
|
|
3618
|
-
const hostRegex = /<host[^>]*>[\s\S]*?<\/host>/g;
|
|
3619
|
-
const hosts = xmlContent.match(hostRegex) || [];
|
|
3620
|
-
for (const hostBlock of hosts) {
|
|
3621
|
-
const ipMatch = hostBlock.match(/<address[^>]*addr="([^"]+)"/);
|
|
3622
|
-
const ip = ipMatch ? ipMatch[1] : "";
|
|
3623
|
-
const hostnameMatch = hostBlock.match(/<hostname[^>]*name="([^"]+)"/);
|
|
3624
|
-
const hostname = hostnameMatch ? hostnameMatch[1] : void 0;
|
|
3625
|
-
const ports = [];
|
|
3626
|
-
const portRegex = /<port[^>]*protocol="([^"]*)"[^>]*portid="(\d+)">[\s\S]*?<\/port>/g;
|
|
3627
|
-
let portMatch;
|
|
3628
|
-
while ((portMatch = portRegex.exec(hostBlock)) !== null) {
|
|
3629
|
-
const protocol = portMatch[1];
|
|
3630
|
-
const port = parseInt(portMatch[2]);
|
|
3631
|
-
const stateMatch = portMatch[0].match(/<state[^>]*state="([^"]+)"/);
|
|
3632
|
-
const state = stateMatch ? stateMatch[1] : "";
|
|
3633
|
-
const serviceMatch = portMatch[0].match(/<service[^>]*name="([^"]*)"(?:[^>]*version="([^"]*)")?/);
|
|
3634
|
-
const service = serviceMatch ? serviceMatch[1] : void 0;
|
|
3635
|
-
const version = serviceMatch && serviceMatch[2] ? serviceMatch[2] : void 0;
|
|
3636
|
-
if (state === "open") {
|
|
3637
|
-
ports.push({ port, protocol, state, service, version });
|
|
3638
|
-
results.summary.openPorts++;
|
|
3639
|
-
if (service) results.summary.servicesFound++;
|
|
3640
|
-
}
|
|
3641
|
-
}
|
|
3642
|
-
if (ip) {
|
|
3643
|
-
results.targets.push({ ip, hostname, ports });
|
|
3644
|
-
results.summary.totalTargets++;
|
|
3645
|
-
}
|
|
3646
|
-
}
|
|
3647
|
-
return {
|
|
3648
|
-
success: true,
|
|
3649
|
-
output: JSON.stringify(results, null, 2)
|
|
3650
|
-
};
|
|
3651
|
-
} catch (error) {
|
|
3652
|
-
return {
|
|
3653
|
-
success: false,
|
|
3654
|
-
output: "",
|
|
3655
|
-
error: getErrorMessage(error)
|
|
3656
|
-
};
|
|
3657
|
-
}
|
|
3658
|
-
}
|
|
3659
|
-
async function searchCVE(service, version) {
|
|
3660
|
-
try {
|
|
3661
|
-
return searchExploitDB(service, version);
|
|
3662
|
-
} catch (error) {
|
|
3663
|
-
return {
|
|
3664
|
-
success: false,
|
|
3665
|
-
output: "",
|
|
3666
|
-
error: getErrorMessage(error)
|
|
3667
|
-
};
|
|
3668
|
-
}
|
|
3669
|
-
}
|
|
3670
|
-
async function searchExploitDB(service, version) {
|
|
3671
|
-
try {
|
|
3672
|
-
const query = version ? `${service} ${version}` : service;
|
|
3673
|
-
try {
|
|
3674
|
-
const output = execFileSync("searchsploit", [query, "--color", "never"], {
|
|
3675
|
-
encoding: "utf-8",
|
|
3676
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3677
|
-
timeout: SEARCH_LIMIT.TIMEOUT_MS
|
|
3678
|
-
});
|
|
3679
|
-
const lines = output.trim().split("\n").slice(0, SEARCH_LIMIT.MAX_OUTPUT_LINES);
|
|
3680
|
-
return {
|
|
3681
|
-
success: true,
|
|
3682
|
-
output: lines.join("\n") || `No exploits found for ${query}`
|
|
3683
|
-
};
|
|
3684
|
-
} catch (e) {
|
|
3685
|
-
const execError = e;
|
|
3686
|
-
const stderr = String(execError.stderr || "");
|
|
3687
|
-
const stdout = String(execError.stdout || "");
|
|
3688
|
-
if (stderr.includes("No results")) {
|
|
3689
|
-
return {
|
|
3690
|
-
success: true,
|
|
3691
|
-
output: `No exploits found for ${query}`
|
|
3692
|
-
};
|
|
3693
|
-
}
|
|
3694
|
-
return {
|
|
3695
|
-
success: true,
|
|
3696
|
-
output: stdout || `No exploits found for ${query}`
|
|
3697
|
-
};
|
|
3698
|
-
}
|
|
3699
|
-
} catch (error) {
|
|
3700
|
-
return {
|
|
3701
|
-
success: false,
|
|
3702
|
-
output: "",
|
|
3703
|
-
error: getErrorMessage(error)
|
|
3704
|
-
};
|
|
3705
|
-
}
|
|
3706
|
-
}
|
|
3707
|
-
async function webSearch(query, _engine) {
|
|
3708
|
-
debugLog("search", "webSearch START", { query });
|
|
3709
|
-
try {
|
|
3710
|
-
const apiKey = getSearchApiKey();
|
|
3711
|
-
const apiUrl = getSearchApiUrl();
|
|
3712
|
-
debugLog("search", "Search API config", {
|
|
3713
|
-
hasApiKey: !!apiKey,
|
|
3714
|
-
apiUrl,
|
|
3715
|
-
apiKeyPrefix: apiKey ? apiKey.slice(0, 8) + "..." : null
|
|
3716
|
-
});
|
|
3717
|
-
if (apiKey) {
|
|
3718
|
-
if (apiUrl.includes(SEARCH_URL_PATTERN.GLM) || apiUrl.includes(SEARCH_URL_PATTERN.ZHIPU)) {
|
|
3719
|
-
debugLog("search", "Using GLM search");
|
|
3720
|
-
return await searchWithGLM(query, apiKey, apiUrl);
|
|
3721
|
-
} else if (apiUrl.includes(SEARCH_URL_PATTERN.BRAVE)) {
|
|
3722
|
-
debugLog("search", "Using Brave search");
|
|
3723
|
-
return await searchWithBrave(query, apiKey, apiUrl);
|
|
3724
|
-
} else if (apiUrl.includes(SEARCH_URL_PATTERN.SERPER)) {
|
|
3725
|
-
debugLog("search", "Using Serper search");
|
|
3726
|
-
return await searchWithSerper(query, apiKey, apiUrl);
|
|
3727
|
-
} else {
|
|
3728
|
-
debugLog("search", "Using generic search API");
|
|
3729
|
-
return await searchWithGenericApi(query, apiKey, apiUrl);
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
debugLog("search", "SEARCH_API_KEY not set \u2014 falling back to Playwright Google search");
|
|
3733
|
-
return await searchWithPlaywright(query);
|
|
3734
|
-
} catch (error) {
|
|
3735
|
-
debugLog("search", "webSearch ERROR", { error: getErrorMessage(error) });
|
|
3736
|
-
return {
|
|
3737
|
-
success: false,
|
|
3738
|
-
output: "",
|
|
3739
|
-
error: getErrorMessage(error)
|
|
3740
|
-
};
|
|
3741
|
-
}
|
|
3742
|
-
}
|
|
3743
3641
|
async function searchWithGLM(query, apiKey, apiUrl) {
|
|
3744
3642
|
debugLog("search", "GLM request START", { apiUrl, query });
|
|
3745
3643
|
const requestBody = {
|
|
@@ -3860,30 +3758,171 @@ async function searchWithGenericApi(query, apiKey, apiUrl) {
|
|
|
3860
3758
|
const errorText = await response.text();
|
|
3861
3759
|
throw new Error(`Search API error: ${response.status} - ${errorText}`);
|
|
3862
3760
|
}
|
|
3863
|
-
const data = await response.json();
|
|
3864
|
-
return { success: true, output: JSON.stringify(data, null, 2) };
|
|
3761
|
+
const data = await response.json();
|
|
3762
|
+
return { success: true, output: JSON.stringify(data, null, 2) };
|
|
3763
|
+
}
|
|
3764
|
+
async function searchWithPlaywright(query) {
|
|
3765
|
+
debugLog("search", "Playwright Google search START", { query });
|
|
3766
|
+
try {
|
|
3767
|
+
const result2 = await webSearchWithBrowser(query, "google");
|
|
3768
|
+
if (!result2.success) {
|
|
3769
|
+
return {
|
|
3770
|
+
success: false,
|
|
3771
|
+
output: "",
|
|
3772
|
+
error: `Playwright search failed: ${result2.error}. Set SEARCH_API_KEY for reliable search (Brave API by default).`
|
|
3773
|
+
};
|
|
3774
|
+
}
|
|
3775
|
+
debugLog("search", "Playwright Google search COMPLETE", { outputLength: result2.output.length });
|
|
3776
|
+
return {
|
|
3777
|
+
success: true,
|
|
3778
|
+
output: result2.output || `No results found for: ${query}`
|
|
3779
|
+
};
|
|
3780
|
+
} catch (error) {
|
|
3781
|
+
return {
|
|
3782
|
+
success: false,
|
|
3783
|
+
output: "",
|
|
3784
|
+
error: `Playwright search error: ${getErrorMessage(error)}. Set SEARCH_API_KEY for reliable search (Brave API by default).`
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
|
|
3789
|
+
// src/engine/tools-mid.ts
|
|
3790
|
+
function getErrorMessage2(error) {
|
|
3791
|
+
return error instanceof Error ? error.message : String(error);
|
|
3792
|
+
}
|
|
3793
|
+
async function parseNmap(xmlPath) {
|
|
3794
|
+
try {
|
|
3795
|
+
const fileResult = await readFileContent(xmlPath);
|
|
3796
|
+
if (!fileResult.success) {
|
|
3797
|
+
return fileResult;
|
|
3798
|
+
}
|
|
3799
|
+
const xmlContent = fileResult.output;
|
|
3800
|
+
const results = {
|
|
3801
|
+
targets: [],
|
|
3802
|
+
summary: { totalTargets: 0, openPorts: 0, servicesFound: 0 }
|
|
3803
|
+
};
|
|
3804
|
+
const hostRegex = /<host[^>]*>[\s\S]*?<\/host>/g;
|
|
3805
|
+
const hosts = xmlContent.match(hostRegex) || [];
|
|
3806
|
+
for (const hostBlock of hosts) {
|
|
3807
|
+
const ipMatch = hostBlock.match(/<address[^>]*addr="([^"]+)"/);
|
|
3808
|
+
const ip = ipMatch ? ipMatch[1] : "";
|
|
3809
|
+
const hostnameMatch = hostBlock.match(/<hostname[^>]*name="([^"]+)"/);
|
|
3810
|
+
const hostname = hostnameMatch ? hostnameMatch[1] : void 0;
|
|
3811
|
+
const ports = [];
|
|
3812
|
+
const portRegex = /<port[^>]*protocol="([^"]*)"[^>]*portid="(\d+)">[\s\S]*?<\/port>/g;
|
|
3813
|
+
let portMatch;
|
|
3814
|
+
while ((portMatch = portRegex.exec(hostBlock)) !== null) {
|
|
3815
|
+
const protocol = portMatch[1];
|
|
3816
|
+
const port = parseInt(portMatch[2]);
|
|
3817
|
+
const stateMatch = portMatch[0].match(/<state[^>]*state="([^"]+)"/);
|
|
3818
|
+
const state = stateMatch ? stateMatch[1] : "";
|
|
3819
|
+
const serviceMatch = portMatch[0].match(/<service[^>]*name="([^"]*)"(?:[^>]*version="([^"]*)")?/);
|
|
3820
|
+
const service = serviceMatch ? serviceMatch[1] : void 0;
|
|
3821
|
+
const version = serviceMatch && serviceMatch[2] ? serviceMatch[2] : void 0;
|
|
3822
|
+
if (state === "open") {
|
|
3823
|
+
ports.push({ port, protocol, state, service, version });
|
|
3824
|
+
results.summary.openPorts++;
|
|
3825
|
+
if (service) results.summary.servicesFound++;
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
if (ip) {
|
|
3829
|
+
results.targets.push({ ip, hostname, ports });
|
|
3830
|
+
results.summary.totalTargets++;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
return {
|
|
3834
|
+
success: true,
|
|
3835
|
+
output: JSON.stringify(results, null, 2)
|
|
3836
|
+
};
|
|
3837
|
+
} catch (error) {
|
|
3838
|
+
return {
|
|
3839
|
+
success: false,
|
|
3840
|
+
output: "",
|
|
3841
|
+
error: getErrorMessage2(error)
|
|
3842
|
+
};
|
|
3843
|
+
}
|
|
3865
3844
|
}
|
|
3866
|
-
async function
|
|
3867
|
-
debugLog("search", "Playwright Google search START", { query });
|
|
3845
|
+
async function searchCVE(service, version) {
|
|
3868
3846
|
try {
|
|
3869
|
-
|
|
3870
|
-
|
|
3847
|
+
return searchExploitDB(service, version);
|
|
3848
|
+
} catch (error) {
|
|
3849
|
+
return {
|
|
3850
|
+
success: false,
|
|
3851
|
+
output: "",
|
|
3852
|
+
error: getErrorMessage2(error)
|
|
3853
|
+
};
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
async function searchExploitDB(service, version) {
|
|
3857
|
+
try {
|
|
3858
|
+
const query = version ? `${service} ${version}` : service;
|
|
3859
|
+
try {
|
|
3860
|
+
const output = execFileSync("searchsploit", [query, "--color", "never"], {
|
|
3861
|
+
encoding: "utf-8",
|
|
3862
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3863
|
+
timeout: SEARCH_LIMIT.TIMEOUT_MS
|
|
3864
|
+
});
|
|
3865
|
+
const lines = output.trim().split("\n").slice(0, SEARCH_LIMIT.MAX_OUTPUT_LINES);
|
|
3871
3866
|
return {
|
|
3872
|
-
success:
|
|
3873
|
-
output: ""
|
|
3874
|
-
|
|
3867
|
+
success: true,
|
|
3868
|
+
output: lines.join("\n") || `No exploits found for ${query}`
|
|
3869
|
+
};
|
|
3870
|
+
} catch (e) {
|
|
3871
|
+
const execError = e;
|
|
3872
|
+
const stderr = String(execError.stderr || "");
|
|
3873
|
+
const stdout = String(execError.stdout || "");
|
|
3874
|
+
if (stderr.includes("No results")) {
|
|
3875
|
+
return {
|
|
3876
|
+
success: true,
|
|
3877
|
+
output: `No exploits found for ${query}`
|
|
3878
|
+
};
|
|
3879
|
+
}
|
|
3880
|
+
return {
|
|
3881
|
+
success: true,
|
|
3882
|
+
output: stdout || `No exploits found for ${query}`
|
|
3875
3883
|
};
|
|
3876
3884
|
}
|
|
3877
|
-
|
|
3885
|
+
} catch (error) {
|
|
3878
3886
|
return {
|
|
3879
|
-
success:
|
|
3880
|
-
output:
|
|
3887
|
+
success: false,
|
|
3888
|
+
output: "",
|
|
3889
|
+
error: getErrorMessage2(error)
|
|
3881
3890
|
};
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
async function webSearch(query, _engine) {
|
|
3894
|
+
debugLog("search", "webSearch START", { query });
|
|
3895
|
+
try {
|
|
3896
|
+
const apiKey = getSearchApiKey();
|
|
3897
|
+
const apiUrl = getSearchApiUrl();
|
|
3898
|
+
debugLog("search", "Search API config", {
|
|
3899
|
+
hasApiKey: !!apiKey,
|
|
3900
|
+
apiUrl,
|
|
3901
|
+
apiKeyPrefix: apiKey ? apiKey.slice(0, DISPLAY_LIMITS.API_KEY_PREFIX) + "..." : null
|
|
3902
|
+
});
|
|
3903
|
+
if (apiKey) {
|
|
3904
|
+
if (apiUrl.includes(SEARCH_URL_PATTERN.GLM) || apiUrl.includes(SEARCH_URL_PATTERN.ZHIPU)) {
|
|
3905
|
+
debugLog("search", "Using GLM search");
|
|
3906
|
+
return await searchWithGLM(query, apiKey, apiUrl);
|
|
3907
|
+
} else if (apiUrl.includes(SEARCH_URL_PATTERN.BRAVE)) {
|
|
3908
|
+
debugLog("search", "Using Brave search");
|
|
3909
|
+
return await searchWithBrave(query, apiKey, apiUrl);
|
|
3910
|
+
} else if (apiUrl.includes(SEARCH_URL_PATTERN.SERPER)) {
|
|
3911
|
+
debugLog("search", "Using Serper search");
|
|
3912
|
+
return await searchWithSerper(query, apiKey, apiUrl);
|
|
3913
|
+
} else {
|
|
3914
|
+
debugLog("search", "Using generic search API");
|
|
3915
|
+
return await searchWithGenericApi(query, apiKey, apiUrl);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
debugLog("search", "SEARCH_API_KEY not set \u2014 falling back to Playwright Google search");
|
|
3919
|
+
return await searchWithPlaywright(query);
|
|
3882
3920
|
} catch (error) {
|
|
3921
|
+
debugLog("search", "webSearch ERROR", { error: getErrorMessage2(error) });
|
|
3883
3922
|
return {
|
|
3884
3923
|
success: false,
|
|
3885
3924
|
output: "",
|
|
3886
|
-
error:
|
|
3925
|
+
error: getErrorMessage2(error)
|
|
3887
3926
|
};
|
|
3888
3927
|
}
|
|
3889
3928
|
}
|
|
@@ -4215,6 +4254,10 @@ Can extract forms and inputs for security testing.`,
|
|
|
4215
4254
|
{
|
|
4216
4255
|
name: TOOL_NAMES.GET_OWASP_KNOWLEDGE,
|
|
4217
4256
|
description: `Get OWASP Top 10 vulnerability knowledge (2017, 2021, 2025 editions).
|
|
4257
|
+
This is BOOTSTRAP knowledge for initial direction. Use it to quickly
|
|
4258
|
+
orient your attack approach, then try attacks based on what you learn.
|
|
4259
|
+
If built-in knowledge isn't enough, use web_search for version-specific details.
|
|
4260
|
+
|
|
4218
4261
|
Returns structured information about:
|
|
4219
4262
|
- OWASP Top 10 category details (detection methods, test payloads, tools)
|
|
4220
4263
|
- Common vulnerability patterns
|
|
@@ -4293,10 +4336,14 @@ Returns a step-by-step guide for:
|
|
|
4293
4336
|
},
|
|
4294
4337
|
{
|
|
4295
4338
|
name: TOOL_NAMES.GET_CVE_INFO,
|
|
4296
|
-
description: `Get information about known CVEs
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4339
|
+
description: `Get information about known CVEs from local bootstrap database.
|
|
4340
|
+
This is a STARTING POINT \u2014 use it first for quick lookups on major CVEs.
|
|
4341
|
+
If the CVE isn't found locally or you need more detail, then use web_search.
|
|
4342
|
+
|
|
4343
|
+
Use this to:
|
|
4344
|
+
- Quick-check if a well-known CVE exists locally
|
|
4345
|
+
- Get initial exploit hints for common vulnerabilities
|
|
4346
|
+
- If not found or insufficient, use web_search for full details and PoCs
|
|
4300
4347
|
|
|
4301
4348
|
Includes critical CVEs like Log4Shell, Spring4Shell, EternalBlue, XZ Backdoor, etc.
|
|
4302
4349
|
If CVE not found locally, use web_search to find it online.`,
|
|
@@ -4664,7 +4711,7 @@ Common wordlists are automatically searched in /usr/share/wordlists (rockyou.txt
|
|
|
4664
4711
|
const cmd = `hashcat -m ${format || HASHCAT_MODES.MD5} "${hashes}" "${wordlistPath}" --force`;
|
|
4665
4712
|
if (background) {
|
|
4666
4713
|
const proc = startBackgroundProcess(cmd, {
|
|
4667
|
-
description: `Cracking hashes: ${hashes.slice(0,
|
|
4714
|
+
description: `Cracking hashes: ${hashes.slice(0, DISPLAY_LIMITS.HASH_PREVIEW_LENGTH)}...`,
|
|
4668
4715
|
purpose: `Attempting to crack ${format || "unknown"} hashes using ${wordlist}`
|
|
4669
4716
|
});
|
|
4670
4717
|
return {
|
|
@@ -4771,72 +4818,61 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
|
|
|
4771
4818
|
"/usr/share/dirb/",
|
|
4772
4819
|
"/opt/wordlists/"
|
|
4773
4820
|
];
|
|
4774
|
-
const
|
|
4775
|
-
|
|
4821
|
+
const CATEGORY_KEYWORDS = {
|
|
4822
|
+
passwords: ["password", "rockyou"],
|
|
4823
|
+
usernames: ["user"],
|
|
4824
|
+
web: ["web", "content", "raft"],
|
|
4825
|
+
dns: ["dns", "subdomain"],
|
|
4826
|
+
fuzzing: ["fuzz", "injection"],
|
|
4827
|
+
api: ["api"]
|
|
4828
|
+
};
|
|
4829
|
+
const WORDLIST_EXTENSIONS = /* @__PURE__ */ new Set(["txt", "lst", "dict", "list", "wlist"]);
|
|
4830
|
+
const SKIP_DIRS = /* @__PURE__ */ new Set([".", "doc"]);
|
|
4831
|
+
const matchesCategory = (filePath) => {
|
|
4832
|
+
if (!category) return true;
|
|
4833
|
+
const pathLower = filePath.toLowerCase();
|
|
4834
|
+
const keywords = CATEGORY_KEYWORDS[category.toLowerCase()] || [category.toLowerCase()];
|
|
4835
|
+
return keywords.some((kw) => pathLower.includes(kw));
|
|
4836
|
+
};
|
|
4837
|
+
const matchesSearch = (filePath, fileName) => {
|
|
4838
|
+
if (!search) return true;
|
|
4839
|
+
const q = search.toLowerCase();
|
|
4840
|
+
return fileName.toLowerCase().includes(q) || filePath.toLowerCase().includes(q);
|
|
4841
|
+
};
|
|
4842
|
+
const results = ["# Available Wordlists on System\\n"];
|
|
4776
4843
|
let totalCount = 0;
|
|
4777
|
-
const
|
|
4778
|
-
|
|
4779
|
-
if (!
|
|
4844
|
+
const processFile = (fullPath, fileName) => {
|
|
4845
|
+
const ext = fileName.split(".").pop()?.toLowerCase();
|
|
4846
|
+
if (!WORDLIST_EXTENSIONS.has(ext || "")) return;
|
|
4847
|
+
const stats = statSync2(fullPath);
|
|
4848
|
+
if (stats.size < minSize) return;
|
|
4849
|
+
if (!matchesCategory(fullPath)) return;
|
|
4850
|
+
if (!matchesSearch(fullPath, fileName)) return;
|
|
4851
|
+
totalCount++;
|
|
4852
|
+
results.push(`### ${fullPath}`);
|
|
4853
|
+
results.push(`- Size: ${Math.round(stats.size / 1024)} KB (${stats.size.toLocaleString()} bytes)`);
|
|
4854
|
+
results.push("");
|
|
4855
|
+
};
|
|
4856
|
+
const scanDir = (dirPath, maxDepth = 3, depth = 0) => {
|
|
4857
|
+
if (depth > maxDepth || !existsSync7(dirPath)) return;
|
|
4858
|
+
let entries;
|
|
4780
4859
|
try {
|
|
4781
|
-
|
|
4782
|
-
for (const entry of entries) {
|
|
4783
|
-
const fullPath = join10(dirPath, entry.name);
|
|
4784
|
-
if (entry.isDirectory()) {
|
|
4785
|
-
if (entry.name.startsWith(".")) continue;
|
|
4786
|
-
if (entry.name === "doc") continue;
|
|
4787
|
-
scanDir(fullPath, maxDepth, currentDepth + 1);
|
|
4788
|
-
} else if (entry.isFile()) {
|
|
4789
|
-
const ext = entry.name.split(".").pop()?.toLowerCase();
|
|
4790
|
-
if (!["txt", "lst", "dict", "list", "wlist"].includes(ext || "")) {
|
|
4791
|
-
continue;
|
|
4792
|
-
}
|
|
4793
|
-
try {
|
|
4794
|
-
const stats = statSync2(fullPath);
|
|
4795
|
-
const sizeBytes = stats.size;
|
|
4796
|
-
if (sizeBytes < minSize) continue;
|
|
4797
|
-
const relPath = fullPath;
|
|
4798
|
-
const filename = entry.name;
|
|
4799
|
-
const sizeKB = Math.round(sizeBytes / 1024);
|
|
4800
|
-
if (category) {
|
|
4801
|
-
const pathLower = relPath.toLowerCase();
|
|
4802
|
-
let matchesCategory = false;
|
|
4803
|
-
switch (category.toLowerCase()) {
|
|
4804
|
-
case "passwords":
|
|
4805
|
-
matchesCategory = pathLower.includes("password") || pathLower.includes("rockyou");
|
|
4806
|
-
break;
|
|
4807
|
-
case "usernames":
|
|
4808
|
-
matchesCategory = pathLower.includes("user");
|
|
4809
|
-
break;
|
|
4810
|
-
case "web":
|
|
4811
|
-
matchesCategory = pathLower.includes("web") || pathLower.includes("content") || pathLower.includes("raft");
|
|
4812
|
-
break;
|
|
4813
|
-
case "dns":
|
|
4814
|
-
matchesCategory = pathLower.includes("dns") || pathLower.includes("subdomain");
|
|
4815
|
-
break;
|
|
4816
|
-
case "fuzzing":
|
|
4817
|
-
matchesCategory = pathLower.includes("fuzz") || pathLower.includes("injection");
|
|
4818
|
-
break;
|
|
4819
|
-
case "api":
|
|
4820
|
-
matchesCategory = pathLower.includes("api");
|
|
4821
|
-
break;
|
|
4822
|
-
default:
|
|
4823
|
-
matchesCategory = pathLower.includes(category.toLowerCase());
|
|
4824
|
-
}
|
|
4825
|
-
if (!matchesCategory) continue;
|
|
4826
|
-
}
|
|
4827
|
-
if (search && !filename.toLowerCase().includes(search.toLowerCase()) && !relPath.toLowerCase().includes(search.toLowerCase())) {
|
|
4828
|
-
continue;
|
|
4829
|
-
}
|
|
4830
|
-
totalCount++;
|
|
4831
|
-
results.push(`### ${relPath}`);
|
|
4832
|
-
results.push(`- Size: ${sizeKB} KB (${sizeBytes.toLocaleString()} bytes)`);
|
|
4833
|
-
results.push("");
|
|
4834
|
-
} catch {
|
|
4835
|
-
continue;
|
|
4836
|
-
}
|
|
4837
|
-
}
|
|
4838
|
-
}
|
|
4860
|
+
entries = readdirSync3(dirPath, { withFileTypes: true });
|
|
4839
4861
|
} catch {
|
|
4862
|
+
return;
|
|
4863
|
+
}
|
|
4864
|
+
for (const entry of entries) {
|
|
4865
|
+
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name)) continue;
|
|
4866
|
+
const fullPath = join10(dirPath, entry.name);
|
|
4867
|
+
if (entry.isDirectory()) {
|
|
4868
|
+
scanDir(fullPath, maxDepth, depth + 1);
|
|
4869
|
+
continue;
|
|
4870
|
+
}
|
|
4871
|
+
if (!entry.isFile()) continue;
|
|
4872
|
+
try {
|
|
4873
|
+
processFile(fullPath, entry.name);
|
|
4874
|
+
} catch {
|
|
4875
|
+
}
|
|
4840
4876
|
}
|
|
4841
4877
|
};
|
|
4842
4878
|
for (const basePath of scanPaths) {
|
|
@@ -6092,237 +6128,6 @@ var CategorizedToolRegistry = class extends ToolRegistry {
|
|
|
6092
6128
|
}
|
|
6093
6129
|
};
|
|
6094
6130
|
|
|
6095
|
-
// src/shared/constants/service-ports.ts
|
|
6096
|
-
var SERVICE_PORTS = {
|
|
6097
|
-
SSH: 22,
|
|
6098
|
-
FTP: 21,
|
|
6099
|
-
TELNET: 23,
|
|
6100
|
-
SMTP: 25,
|
|
6101
|
-
DNS: 53,
|
|
6102
|
-
HTTP: 80,
|
|
6103
|
-
POP3: 110,
|
|
6104
|
-
IMAP: 143,
|
|
6105
|
-
SMB_NETBIOS: 139,
|
|
6106
|
-
SMB: 445,
|
|
6107
|
-
HTTPS: 443,
|
|
6108
|
-
MSSQL: 1433,
|
|
6109
|
-
MYSQL: 3306,
|
|
6110
|
-
RDP: 3389,
|
|
6111
|
-
POSTGRESQL: 5432,
|
|
6112
|
-
REDIS: 6379,
|
|
6113
|
-
HTTP_ALT: 8080,
|
|
6114
|
-
HTTPS_ALT: 8443,
|
|
6115
|
-
MONGODB: 27017,
|
|
6116
|
-
ELASTICSEARCH: 9200,
|
|
6117
|
-
MEMCACHED: 11211,
|
|
6118
|
-
NODE_DEFAULT: 3e3,
|
|
6119
|
-
FLASK_DEFAULT: 5e3,
|
|
6120
|
-
DJANGO_DEFAULT: 8e3
|
|
6121
|
-
};
|
|
6122
|
-
var CRITICAL_SERVICE_PORTS = [
|
|
6123
|
-
SERVICE_PORTS.SSH,
|
|
6124
|
-
SERVICE_PORTS.RDP,
|
|
6125
|
-
SERVICE_PORTS.MYSQL,
|
|
6126
|
-
SERVICE_PORTS.POSTGRESQL,
|
|
6127
|
-
SERVICE_PORTS.REDIS,
|
|
6128
|
-
SERVICE_PORTS.MONGODB
|
|
6129
|
-
];
|
|
6130
|
-
var NO_AUTH_CRITICAL_PORTS = [
|
|
6131
|
-
SERVICE_PORTS.REDIS,
|
|
6132
|
-
SERVICE_PORTS.MONGODB,
|
|
6133
|
-
SERVICE_PORTS.ELASTICSEARCH,
|
|
6134
|
-
SERVICE_PORTS.MEMCACHED
|
|
6135
|
-
];
|
|
6136
|
-
var WEB_SERVICE_PORTS = [
|
|
6137
|
-
SERVICE_PORTS.HTTP,
|
|
6138
|
-
SERVICE_PORTS.HTTPS,
|
|
6139
|
-
SERVICE_PORTS.HTTP_ALT,
|
|
6140
|
-
SERVICE_PORTS.HTTPS_ALT,
|
|
6141
|
-
SERVICE_PORTS.NODE_DEFAULT,
|
|
6142
|
-
SERVICE_PORTS.FLASK_DEFAULT,
|
|
6143
|
-
SERVICE_PORTS.DJANGO_DEFAULT
|
|
6144
|
-
];
|
|
6145
|
-
var PLAINTEXT_HTTP_PORTS = [
|
|
6146
|
-
SERVICE_PORTS.HTTP,
|
|
6147
|
-
SERVICE_PORTS.HTTP_ALT,
|
|
6148
|
-
SERVICE_PORTS.NODE_DEFAULT
|
|
6149
|
-
];
|
|
6150
|
-
var DATABASE_PORTS = [
|
|
6151
|
-
SERVICE_PORTS.MYSQL,
|
|
6152
|
-
SERVICE_PORTS.POSTGRESQL,
|
|
6153
|
-
SERVICE_PORTS.MSSQL,
|
|
6154
|
-
SERVICE_PORTS.MONGODB,
|
|
6155
|
-
SERVICE_PORTS.REDIS
|
|
6156
|
-
];
|
|
6157
|
-
var SMB_PORTS = [
|
|
6158
|
-
SERVICE_PORTS.SMB,
|
|
6159
|
-
SERVICE_PORTS.SMB_NETBIOS
|
|
6160
|
-
];
|
|
6161
|
-
|
|
6162
|
-
// src/shared/utils/logger.ts
|
|
6163
|
-
var COLORS = {
|
|
6164
|
-
reset: "\x1B[0m",
|
|
6165
|
-
dim: "\x1B[2m",
|
|
6166
|
-
red: "\x1B[31m",
|
|
6167
|
-
yellow: "\x1B[33m",
|
|
6168
|
-
green: "\x1B[32m",
|
|
6169
|
-
blue: "\x1B[34m",
|
|
6170
|
-
magenta: "\x1B[35m",
|
|
6171
|
-
cyan: "\x1B[36m"
|
|
6172
|
-
};
|
|
6173
|
-
var levelColors = {
|
|
6174
|
-
[0 /* DEBUG */]: COLORS.dim,
|
|
6175
|
-
[1 /* INFO */]: COLORS.green,
|
|
6176
|
-
[2 /* WARN */]: COLORS.yellow,
|
|
6177
|
-
[3 /* ERROR */]: COLORS.red,
|
|
6178
|
-
[999 /* SILENT */]: COLORS.reset
|
|
6179
|
-
};
|
|
6180
|
-
var levelNames = {
|
|
6181
|
-
[0 /* DEBUG */]: "DEBUG",
|
|
6182
|
-
[1 /* INFO */]: "INFO",
|
|
6183
|
-
[2 /* WARN */]: "WARN",
|
|
6184
|
-
[3 /* ERROR */]: "ERROR",
|
|
6185
|
-
[999 /* SILENT */]: "SILENT"
|
|
6186
|
-
};
|
|
6187
|
-
var Logger = class {
|
|
6188
|
-
constructor(component, config = {}) {
|
|
6189
|
-
this.component = component;
|
|
6190
|
-
this.config = {
|
|
6191
|
-
minLevel: config.minLevel ?? 1 /* INFO */,
|
|
6192
|
-
includeTimestamp: config.includeTimestamp ?? true,
|
|
6193
|
-
includeComponent: config.includeComponent ?? true,
|
|
6194
|
-
colorOutput: config.colorOutput ?? true,
|
|
6195
|
-
outputToFile: config.outputToFile ?? false,
|
|
6196
|
-
logFilePath: config.logFilePath
|
|
6197
|
-
};
|
|
6198
|
-
}
|
|
6199
|
-
config;
|
|
6200
|
-
logBuffer = [];
|
|
6201
|
-
maxBufferSize = 1e3;
|
|
6202
|
-
/**
|
|
6203
|
-
* Set minimum log level
|
|
6204
|
-
*/
|
|
6205
|
-
setMinLevel(level) {
|
|
6206
|
-
this.config.minLevel = level;
|
|
6207
|
-
}
|
|
6208
|
-
/**
|
|
6209
|
-
* Log at a specific level
|
|
6210
|
-
*/
|
|
6211
|
-
log(level, message, data) {
|
|
6212
|
-
if (level < this.config.minLevel) {
|
|
6213
|
-
return;
|
|
6214
|
-
}
|
|
6215
|
-
const entry = {
|
|
6216
|
-
timestamp: Date.now(),
|
|
6217
|
-
level,
|
|
6218
|
-
component: this.component,
|
|
6219
|
-
message,
|
|
6220
|
-
data
|
|
6221
|
-
};
|
|
6222
|
-
this.logBuffer.push(entry);
|
|
6223
|
-
if (this.logBuffer.length > this.maxBufferSize) {
|
|
6224
|
-
this.logBuffer.shift();
|
|
6225
|
-
}
|
|
6226
|
-
const formatted = this.formatEntry(entry);
|
|
6227
|
-
console.log(formatted);
|
|
6228
|
-
}
|
|
6229
|
-
/**
|
|
6230
|
-
* Format a log entry for output
|
|
6231
|
-
*/
|
|
6232
|
-
formatEntry(entry) {
|
|
6233
|
-
const parts = [];
|
|
6234
|
-
if (this.config.includeTimestamp) {
|
|
6235
|
-
const date = new Date(entry.timestamp);
|
|
6236
|
-
const ts = date.toISOString().split("T")[1].slice(0, -1);
|
|
6237
|
-
parts.push(this.config.colorOutput ? COLORS.dim + ts + COLORS.reset : ts);
|
|
6238
|
-
}
|
|
6239
|
-
const levelName = levelNames[entry.level];
|
|
6240
|
-
const levelColor = this.config.colorOutput ? levelColors[entry.level] : "";
|
|
6241
|
-
parts.push(levelColor + `[${levelName}]` + (this.config.colorOutput ? COLORS.reset : ""));
|
|
6242
|
-
if (this.config.includeComponent) {
|
|
6243
|
-
const comp = this.config.colorOutput ? COLORS.cyan + entry.component + COLORS.reset : entry.component;
|
|
6244
|
-
parts.push(`[${comp}]`);
|
|
6245
|
-
}
|
|
6246
|
-
parts.push(entry.message);
|
|
6247
|
-
if (entry.data) {
|
|
6248
|
-
const dataStr = Object.entries(entry.data).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
|
|
6249
|
-
parts.push(this.config.colorOutput ? COLORS.dim + dataStr + COLORS.reset : dataStr);
|
|
6250
|
-
}
|
|
6251
|
-
return parts.join(" ");
|
|
6252
|
-
}
|
|
6253
|
-
/**
|
|
6254
|
-
* Debug level logging
|
|
6255
|
-
*/
|
|
6256
|
-
debug(message, data) {
|
|
6257
|
-
this.log(0 /* DEBUG */, message, data);
|
|
6258
|
-
}
|
|
6259
|
-
/**
|
|
6260
|
-
* Info level logging
|
|
6261
|
-
*/
|
|
6262
|
-
info(message, data) {
|
|
6263
|
-
this.log(1 /* INFO */, message, data);
|
|
6264
|
-
}
|
|
6265
|
-
/**
|
|
6266
|
-
* Warning level logging
|
|
6267
|
-
*/
|
|
6268
|
-
warn(message, data) {
|
|
6269
|
-
this.log(2 /* WARN */, message, data);
|
|
6270
|
-
}
|
|
6271
|
-
/**
|
|
6272
|
-
* Error level logging
|
|
6273
|
-
*/
|
|
6274
|
-
error(message, data) {
|
|
6275
|
-
this.log(3 /* ERROR */, message, data);
|
|
6276
|
-
}
|
|
6277
|
-
/**
|
|
6278
|
-
* Get all log entries
|
|
6279
|
-
*/
|
|
6280
|
-
getLogs() {
|
|
6281
|
-
return [...this.logBuffer];
|
|
6282
|
-
}
|
|
6283
|
-
/**
|
|
6284
|
-
* Get logs by level
|
|
6285
|
-
*/
|
|
6286
|
-
getLogsByLevel(minLevel) {
|
|
6287
|
-
return this.logBuffer.filter((entry) => entry.level >= minLevel);
|
|
6288
|
-
}
|
|
6289
|
-
/**
|
|
6290
|
-
* Clear log buffer
|
|
6291
|
-
*/
|
|
6292
|
-
clearLogs() {
|
|
6293
|
-
this.logBuffer = [];
|
|
6294
|
-
}
|
|
6295
|
-
/**
|
|
6296
|
-
* Export logs to string
|
|
6297
|
-
*/
|
|
6298
|
-
exportLogs() {
|
|
6299
|
-
return this.logBuffer.map((entry) => this.formatEntry(entry)).join("\n");
|
|
6300
|
-
}
|
|
6301
|
-
};
|
|
6302
|
-
var agentLogger = new Logger("Agent", {
|
|
6303
|
-
minLevel: 1 /* INFO */,
|
|
6304
|
-
colorOutput: true
|
|
6305
|
-
});
|
|
6306
|
-
|
|
6307
|
-
// src/shared/constants/_shared/http.const.ts
|
|
6308
|
-
var HTTP_STATUS = {
|
|
6309
|
-
// 2xx Success
|
|
6310
|
-
OK: 200,
|
|
6311
|
-
CREATED: 201,
|
|
6312
|
-
NO_CONTENT: 204,
|
|
6313
|
-
// 4xx Client Errors
|
|
6314
|
-
BAD_REQUEST: 400,
|
|
6315
|
-
UNAUTHORIZED: 401,
|
|
6316
|
-
FORBIDDEN: 403,
|
|
6317
|
-
NOT_FOUND: 404,
|
|
6318
|
-
RATE_LIMIT: 429,
|
|
6319
|
-
// 5xx Server Errors
|
|
6320
|
-
INTERNAL_ERROR: 500,
|
|
6321
|
-
BAD_GATEWAY: 502,
|
|
6322
|
-
SERVICE_UNAVAILABLE: 503,
|
|
6323
|
-
GATEWAY_TIMEOUT: 504
|
|
6324
|
-
};
|
|
6325
|
-
|
|
6326
6131
|
// src/shared/constants/llm/api.ts
|
|
6327
6132
|
var LLM_API = {
|
|
6328
6133
|
/** Default Anthropic API base URL */
|
|
@@ -6394,7 +6199,26 @@ var LLM_ERROR_TYPES = {
|
|
|
6394
6199
|
UNKNOWN: "unknown"
|
|
6395
6200
|
};
|
|
6396
6201
|
|
|
6397
|
-
// src/
|
|
6202
|
+
// src/shared/constants/_shared/http.const.ts
|
|
6203
|
+
var HTTP_STATUS = {
|
|
6204
|
+
// 2xx Success
|
|
6205
|
+
OK: 200,
|
|
6206
|
+
CREATED: 201,
|
|
6207
|
+
NO_CONTENT: 204,
|
|
6208
|
+
// 4xx Client Errors
|
|
6209
|
+
BAD_REQUEST: 400,
|
|
6210
|
+
UNAUTHORIZED: 401,
|
|
6211
|
+
FORBIDDEN: 403,
|
|
6212
|
+
NOT_FOUND: 404,
|
|
6213
|
+
RATE_LIMIT: 429,
|
|
6214
|
+
// 5xx Server Errors
|
|
6215
|
+
INTERNAL_ERROR: 500,
|
|
6216
|
+
BAD_GATEWAY: 502,
|
|
6217
|
+
SERVICE_UNAVAILABLE: 503,
|
|
6218
|
+
GATEWAY_TIMEOUT: 504
|
|
6219
|
+
};
|
|
6220
|
+
|
|
6221
|
+
// src/engine/llm-types.ts
|
|
6398
6222
|
var LLMError = class extends Error {
|
|
6399
6223
|
/** Structured error information */
|
|
6400
6224
|
errorInfo;
|
|
@@ -6425,6 +6249,8 @@ function classifyError(error) {
|
|
|
6425
6249
|
}
|
|
6426
6250
|
return { type: LLM_ERROR_TYPES.UNKNOWN, message: errorMessage, statusCode, isRetryable: false, suggestedAction: "Analyze error" };
|
|
6427
6251
|
}
|
|
6252
|
+
|
|
6253
|
+
// src/engine/llm.ts
|
|
6428
6254
|
var LLMClient = class {
|
|
6429
6255
|
apiKey;
|
|
6430
6256
|
baseUrl;
|
|
@@ -6434,7 +6260,7 @@ var LLMClient = class {
|
|
|
6434
6260
|
this.apiKey = getApiKey();
|
|
6435
6261
|
this.baseUrl = getBaseUrl() || LLM_API.DEFAULT_BASE_URL;
|
|
6436
6262
|
this.model = getModel();
|
|
6437
|
-
debugLog("llm", "LLMClient constructed", { model: this.model, baseUrl: this.baseUrl, apiKeyPrefix: this.apiKey.slice(0,
|
|
6263
|
+
debugLog("llm", "LLMClient constructed", { model: this.model, baseUrl: this.baseUrl, apiKeyPrefix: this.apiKey.slice(0, DISPLAY_LIMITS.API_KEY_PREFIX) + "..." });
|
|
6438
6264
|
}
|
|
6439
6265
|
/**
|
|
6440
6266
|
* Generate a non-streaming response
|
|
@@ -6614,7 +6440,7 @@ var LLMClient = class {
|
|
|
6614
6440
|
toolCall.input = parseResult.data;
|
|
6615
6441
|
debugLog("llm", `[${requestId}] Tool parsed OK`, { id, name: toolCall.name, input: toolCall.input });
|
|
6616
6442
|
} else {
|
|
6617
|
-
toolCall.input = { _parse_error: parseResult.error, _raw_json: toolCall._pendingJson.slice(0,
|
|
6443
|
+
toolCall.input = { _parse_error: parseResult.error, _raw_json: toolCall._pendingJson.slice(0, DISPLAY_LIMITS.RAW_JSON_ERROR_PREVIEW) };
|
|
6618
6444
|
debugLog("llm", `[${requestId}] Tool parse FAILED`, { id, name: toolCall.name, error: parseResult.error, raw: toolCall._pendingJson });
|
|
6619
6445
|
}
|
|
6620
6446
|
delete toolCall._pendingJson;
|
|
@@ -6764,7 +6590,7 @@ var __filename3 = fileURLToPath4(import.meta.url);
|
|
|
6764
6590
|
var __dirname4 = dirname4(__filename3);
|
|
6765
6591
|
|
|
6766
6592
|
// src/engine/state-persistence.ts
|
|
6767
|
-
import { writeFileSync as writeFileSync5, readFileSync as readFileSync3, existsSync as existsSync5, readdirSync, statSync } from "fs";
|
|
6593
|
+
import { writeFileSync as writeFileSync5, readFileSync as readFileSync3, existsSync as existsSync5, readdirSync, statSync, unlinkSync as unlinkSync3 } from "fs";
|
|
6768
6594
|
import { join as join8, basename } from "path";
|
|
6769
6595
|
function saveState(state) {
|
|
6770
6596
|
const sessionsDir = WORKSPACE.SESSIONS;
|
|
@@ -6777,7 +6603,7 @@ function saveState(state) {
|
|
|
6777
6603
|
findings: state.getFindings(),
|
|
6778
6604
|
loot: state.getLoot(),
|
|
6779
6605
|
todo: state.getTodo(),
|
|
6780
|
-
actionLog: state.getRecentActions(
|
|
6606
|
+
actionLog: state.getRecentActions(AGENT_LIMITS.MAX_ACTION_LOG),
|
|
6781
6607
|
currentPhase: state.getPhase(),
|
|
6782
6608
|
missionSummary: state.getMissionSummary(),
|
|
6783
6609
|
missionChecklist: state.getMissionChecklist()
|
|
@@ -6787,8 +6613,23 @@ function saveState(state) {
|
|
|
6787
6613
|
writeFileSync5(sessionFile, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
6788
6614
|
const latestFile = join8(sessionsDir, "latest.json");
|
|
6789
6615
|
writeFileSync5(latestFile, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
6616
|
+
pruneOldSessions(sessionsDir);
|
|
6790
6617
|
return sessionFile;
|
|
6791
6618
|
}
|
|
6619
|
+
function pruneOldSessions(sessionsDir) {
|
|
6620
|
+
try {
|
|
6621
|
+
const sessionFiles = readdirSync(sessionsDir).filter((f) => f.endsWith(".json") && f !== "latest.json").map((f) => ({
|
|
6622
|
+
name: f,
|
|
6623
|
+
path: join8(sessionsDir, f),
|
|
6624
|
+
mtime: statSync(join8(sessionsDir, f)).mtimeMs
|
|
6625
|
+
})).sort((a, b) => b.mtime - a.mtime);
|
|
6626
|
+
const toDelete = sessionFiles.slice(AGENT_LIMITS.MAX_SESSION_FILES);
|
|
6627
|
+
for (const file of toDelete) {
|
|
6628
|
+
unlinkSync3(file.path);
|
|
6629
|
+
}
|
|
6630
|
+
} catch {
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6792
6633
|
function loadState(state) {
|
|
6793
6634
|
const latestFile = join8(WORKSPACE.SESSIONS, "latest.json");
|
|
6794
6635
|
if (!existsSync5(latestFile)) {
|
|
@@ -6798,7 +6639,7 @@ function loadState(state) {
|
|
|
6798
6639
|
const raw = readFileSync3(latestFile, "utf-8");
|
|
6799
6640
|
const snapshot = JSON.parse(raw);
|
|
6800
6641
|
if (snapshot.version !== 1) {
|
|
6801
|
-
|
|
6642
|
+
debugLog("general", `Unknown snapshot version: ${snapshot.version}`);
|
|
6802
6643
|
return false;
|
|
6803
6644
|
}
|
|
6804
6645
|
if (snapshot.engagement) {
|
|
@@ -6816,7 +6657,9 @@ function loadState(state) {
|
|
|
6816
6657
|
for (const item of snapshot.todo) {
|
|
6817
6658
|
state.addTodo(item.content, item.priority);
|
|
6818
6659
|
}
|
|
6819
|
-
|
|
6660
|
+
const validPhases = new Set(Object.values(PHASES));
|
|
6661
|
+
const restoredPhase = validPhases.has(snapshot.currentPhase) ? snapshot.currentPhase : PHASES.RECON;
|
|
6662
|
+
state.setPhase(restoredPhase);
|
|
6820
6663
|
if (snapshot.missionSummary) {
|
|
6821
6664
|
state.setMissionSummary(snapshot.missionSummary);
|
|
6822
6665
|
}
|
|
@@ -6825,7 +6668,7 @@ function loadState(state) {
|
|
|
6825
6668
|
}
|
|
6826
6669
|
return true;
|
|
6827
6670
|
} catch (err) {
|
|
6828
|
-
|
|
6671
|
+
debugLog("general", `Failed to load state: ${err}`);
|
|
6829
6672
|
return false;
|
|
6830
6673
|
}
|
|
6831
6674
|
}
|
|
@@ -6835,11 +6678,24 @@ var FLAG_PATTERNS = {
|
|
|
6835
6678
|
// Generic CTF flag formats
|
|
6836
6679
|
generic: /flag\{[^}]+\}/gi,
|
|
6837
6680
|
curly_upper: /FLAG\{[^}]+\}/g,
|
|
6838
|
-
// Platform-specific
|
|
6681
|
+
// Platform-specific (major competitions)
|
|
6839
6682
|
htb: /HTB\{[^}]+\}/g,
|
|
6840
6683
|
thm: /THM\{[^}]+\}/g,
|
|
6841
6684
|
picoCTF: /picoCTF\{[^}]+\}/g,
|
|
6685
|
+
defcon_ooo: /OOO\{[^}]+\}/g,
|
|
6686
|
+
// DEF CON CTF (Order of the Overflow)
|
|
6687
|
+
csaw: /CSAW\{[^}]+\}/g,
|
|
6688
|
+
// CSAW CTF
|
|
6689
|
+
google: /CTF\{[^}]+\}/g,
|
|
6690
|
+
// Google CTF
|
|
6691
|
+
dragon: /DrgnS\{[^}]+\}/g,
|
|
6692
|
+
// Dragon Sector
|
|
6693
|
+
hxp: /hxp\{[^}]+\}/g,
|
|
6694
|
+
// hxp CTF
|
|
6695
|
+
zer0pts: /zer0pts\{[^}]+\}/g,
|
|
6696
|
+
// zer0pts CTF
|
|
6842
6697
|
ctfd_generic: /[A-Z]{2,10}\{[^}]+\}/g,
|
|
6698
|
+
// Generic CTFd format
|
|
6843
6699
|
// Hash-style flags (HTB user.txt / root.txt — 32-char hex)
|
|
6844
6700
|
hash_flag: /\b[a-f0-9]{32}\b/g,
|
|
6845
6701
|
// Base64-encoded flag detection (common in steganography/forensics)
|
|
@@ -6859,6 +6715,83 @@ function detectFlags(output) {
|
|
|
6859
6715
|
return Array.from(found);
|
|
6860
6716
|
}
|
|
6861
6717
|
|
|
6718
|
+
// src/agents/tool-error-enrichment.ts
|
|
6719
|
+
function enrichToolErrorContext(ctx) {
|
|
6720
|
+
const { toolName, input, error, originalOutput, progress } = ctx;
|
|
6721
|
+
const lines = [];
|
|
6722
|
+
if (originalOutput) lines.push(originalOutput);
|
|
6723
|
+
lines.push("");
|
|
6724
|
+
lines.push(`[TOOL ERROR ANALYSIS]`);
|
|
6725
|
+
lines.push(`Tool: ${toolName}`);
|
|
6726
|
+
lines.push(`Error: ${error}`);
|
|
6727
|
+
const errorLower = error.toLowerCase();
|
|
6728
|
+
if (isBlockedCommandError(errorLower)) {
|
|
6729
|
+
trackBlockedPattern(progress, toolName, errorLower);
|
|
6730
|
+
appendBlockedCommandHints(lines, errorLower);
|
|
6731
|
+
} else if (isMissingParamError(errorLower)) {
|
|
6732
|
+
const providedParams = Object.keys(input).join(", ") || "none";
|
|
6733
|
+
lines.push(`Provided parameters: ${providedParams}`);
|
|
6734
|
+
lines.push(`Fix: Check the tool's required parameters and provide ALL required values.`);
|
|
6735
|
+
lines.push(`Action: Retry this tool call with the missing parameter(s) added.`);
|
|
6736
|
+
} else if (isNotFoundError(errorLower)) {
|
|
6737
|
+
lines.push(`Fix: The tool or command may not be installed.`);
|
|
6738
|
+
lines.push(`Actions: (1) Try an alternative tool, (2) Use web_search to find installation instructions, or (3) Use a different approach entirely.`);
|
|
6739
|
+
} else if (isPermissionError(errorLower)) {
|
|
6740
|
+
lines.push(`Fix: Insufficient permissions for this operation.`);
|
|
6741
|
+
lines.push(`Actions: (1) Try with sudo if appropriate, (2) Use a different approach, or (3) Check if the target path/resource is correct.`);
|
|
6742
|
+
} else if (isConnectionError(errorLower)) {
|
|
6743
|
+
lines.push(`Fix: Cannot reach the target service.`);
|
|
6744
|
+
lines.push(`Actions: (1) Verify the target host/port is correct, (2) Check if the service is running, (3) Try a different port or protocol.`);
|
|
6745
|
+
} else if (isInvalidInputError(errorLower)) {
|
|
6746
|
+
lines.push(`Fix: The input format is incorrect.`);
|
|
6747
|
+
lines.push(`Actions: (1) Check the input format and fix it, (2) Use web_search to find the correct syntax, or (3) Try a simpler input.`);
|
|
6748
|
+
} else {
|
|
6749
|
+
lines.push(`Actions: (1) Analyze the error and modify your approach, (2) Use web_search("${toolName} ${errorLower.slice(0, AGENT_LIMITS.ERROR_SEARCH_PREVIEW_SLICE)}") to research the error, (3) Try an alternative tool or method.`);
|
|
6750
|
+
}
|
|
6751
|
+
return lines.join("\n");
|
|
6752
|
+
}
|
|
6753
|
+
function isBlockedCommandError(e) {
|
|
6754
|
+
return e.includes("command blocked") || e.includes("injection pattern") || e.includes("not in the allowed") || e.includes("not in allowed paths");
|
|
6755
|
+
}
|
|
6756
|
+
function isMissingParamError(e) {
|
|
6757
|
+
return e.includes("missing") && e.includes("parameter") || e.includes("required") && e.includes("missing") || e.includes("is required");
|
|
6758
|
+
}
|
|
6759
|
+
function isNotFoundError(e) {
|
|
6760
|
+
return e.includes("not found") || e.includes("command not found") || e.includes("no such file");
|
|
6761
|
+
}
|
|
6762
|
+
function isPermissionError(e) {
|
|
6763
|
+
return e.includes("permission denied") || e.includes("access denied") || e.includes("eacces");
|
|
6764
|
+
}
|
|
6765
|
+
function isConnectionError(e) {
|
|
6766
|
+
return e.includes("connection refused") || e.includes("econnrefused") || e.includes("connection reset") || e.includes("timeout");
|
|
6767
|
+
}
|
|
6768
|
+
function isInvalidInputError(e) {
|
|
6769
|
+
return e.includes("invalid") || e.includes("malformed") || e.includes("syntax error");
|
|
6770
|
+
}
|
|
6771
|
+
function trackBlockedPattern(progress, toolName, errorLower) {
|
|
6772
|
+
if (!progress) return;
|
|
6773
|
+
const patternKey = `${toolName}:${errorLower.slice(0, AGENT_LIMITS.BLOCKED_PATTERN_KEY_SLICE)}`;
|
|
6774
|
+
const count = (progress.blockedCommandPatterns.get(patternKey) || 0) + 1;
|
|
6775
|
+
progress.blockedCommandPatterns.set(patternKey, count);
|
|
6776
|
+
progress.totalBlockedCommands++;
|
|
6777
|
+
}
|
|
6778
|
+
function appendBlockedCommandHints(lines, errorLower) {
|
|
6779
|
+
if (errorLower.includes("pipe target") || errorLower.includes("injection")) {
|
|
6780
|
+
lines.push(`Fix: Use the tool's built-in output options instead of shell pipes.`);
|
|
6781
|
+
lines.push(`Alternative approaches:`);
|
|
6782
|
+
lines.push(` 1. Save output to file: command > /tmp/output.txt, then use read_file("/tmp/output.txt")`);
|
|
6783
|
+
lines.push(` 2. Use tool-specific flags: nmap -oN /tmp/scan.txt, curl -o /tmp/page.html`);
|
|
6784
|
+
lines.push(` 3. Use browse_url for web content instead of curl | grep`);
|
|
6785
|
+
lines.push(` 4. If filtering output: run the command first, then read_file + parse results`);
|
|
6786
|
+
} else if (errorLower.includes("redirect")) {
|
|
6787
|
+
lines.push(`Fix: Redirect to /tmp/ or /root/ paths only.`);
|
|
6788
|
+
lines.push(`Example: command > /tmp/output.txt or command 2>&1 > /tmp/errors.txt`);
|
|
6789
|
+
} else {
|
|
6790
|
+
lines.push(`Fix: Simplify the command. Avoid chaining complex operations.`);
|
|
6791
|
+
lines.push(`Break into multiple steps: run_cmd \u2192 read_file \u2192 run_cmd.`);
|
|
6792
|
+
}
|
|
6793
|
+
}
|
|
6794
|
+
|
|
6862
6795
|
// src/agents/core-agent.ts
|
|
6863
6796
|
var CoreAgent = class _CoreAgent {
|
|
6864
6797
|
llm;
|
|
@@ -6929,44 +6862,28 @@ var CoreAgent = class _CoreAgent {
|
|
|
6929
6862
|
const phase = this.state.getPhase();
|
|
6930
6863
|
const targets = this.state.getTargets().size;
|
|
6931
6864
|
const findings = this.state.getFindings().length;
|
|
6932
|
-
const
|
|
6933
|
-
[PHASES.RECON]: `RECON
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
[PHASES.
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
[PHASES.
|
|
6940
|
-
\u2192 Pick the highest-severity finding and write an exploit script.
|
|
6941
|
-
\u2192 web_search("{CVE} PoC github") \u2192 browse_url \u2192 write_file \u2192 run_cmd`,
|
|
6942
|
-
[PHASES.POST_EXPLOIT]: `POST-EXPLOIT: Escalate privileges and move laterally.
|
|
6943
|
-
\u2192 bg_process interact: "cat /etc/passwd && cat /etc/shadow && sudo -l"
|
|
6944
|
-
\u2192 web_search("{OS} privilege escalation hacktricks")`,
|
|
6945
|
-
[PHASES.PRIV_ESC]: `PRIVESC: Escalate NOW.
|
|
6946
|
-
\u2192 run_cmd({ command: "sudo -l && find / -perm -4000 -type f 2>/dev/null" })
|
|
6947
|
-
\u2192 web_search("{kernel_version} privilege escalation exploit")`,
|
|
6948
|
-
[PHASES.LATERAL]: `LATERAL MOVEMENT: Spread to other hosts.
|
|
6949
|
-
\u2192 Reuse discovered credentials on all known hosts
|
|
6950
|
-
\u2192 run_cmd({ command: "for ip in $(cat /tmp/hosts); do sshpass -p '<cred>' ssh user@$ip id; done" })`,
|
|
6951
|
-
[PHASES.WEB]: `WEB PHASE: Test injection points.
|
|
6952
|
-
\u2192 get_web_attack_surface with target URL
|
|
6953
|
-
\u2192 Test every parameter for SQLi, SSTI, XSS, command injection`
|
|
6865
|
+
const phaseDirection = {
|
|
6866
|
+
[PHASES.RECON]: `RECON: Scan targets. Enumerate services and versions.`,
|
|
6867
|
+
[PHASES.VULN_ANALYSIS]: `VULN ANALYSIS: ${targets} target(s) discovered. Search for CVEs and known exploits.`,
|
|
6868
|
+
[PHASES.EXPLOIT]: `EXPLOIT: ${findings} finding(s) available. Attack the highest-severity one.`,
|
|
6869
|
+
[PHASES.POST_EXPLOIT]: `POST-EXPLOIT: Escalate privileges. Search for escalation paths.`,
|
|
6870
|
+
[PHASES.PRIV_ESC]: `PRIVESC: Find and exploit privilege escalation vectors.`,
|
|
6871
|
+
[PHASES.LATERAL]: `LATERAL: Reuse discovered credentials on other hosts.`,
|
|
6872
|
+
[PHASES.WEB]: `WEB: Enumerate the attack surface. Test every input for injection.`
|
|
6954
6873
|
};
|
|
6955
|
-
const
|
|
6874
|
+
const direction = phaseDirection[phase] || phaseDirection[PHASES.RECON];
|
|
6956
6875
|
messages.push({
|
|
6957
6876
|
role: LLM_ROLES.USER,
|
|
6958
|
-
content:
|
|
6877
|
+
content: `\u26A1 DEADLOCK: ${AGENT_LIMITS.MAX_CONSECUTIVE_IDLE} turns with ZERO tool calls.
|
|
6959
6878
|
Phase: ${phase} | Targets: ${targets} | Findings: ${findings} | Tools executed: ${progress.totalToolsExecuted} (${progress.toolSuccesses}\u2713 ${progress.toolErrors}\u2717)
|
|
6960
6879
|
|
|
6961
|
-
${
|
|
6880
|
+
${direction}
|
|
6962
6881
|
|
|
6963
|
-
|
|
6964
|
-
-
|
|
6965
|
-
-
|
|
6966
|
-
-
|
|
6967
|
-
-
|
|
6968
|
-
- \u26A1 1 fail \u2260 stop: Try DIFFERENT approach immediately
|
|
6969
|
-
- \u26A1 NEVER output text without a tool call`
|
|
6882
|
+
RULES:
|
|
6883
|
+
- Every turn MUST have tool calls
|
|
6884
|
+
- If stuck: search for techniques (web_search)
|
|
6885
|
+
- If failed: try a DIFFERENT approach
|
|
6886
|
+
- ACT NOW \u2014 do not plan or explain`
|
|
6970
6887
|
});
|
|
6971
6888
|
}
|
|
6972
6889
|
} catch (error) {
|
|
@@ -7162,7 +7079,7 @@ Please decide how to handle this error and continue.`;
|
|
|
7162
7079
|
toolName,
|
|
7163
7080
|
success,
|
|
7164
7081
|
output,
|
|
7165
|
-
outputSummary: output.slice(0,
|
|
7082
|
+
outputSummary: output.slice(0, DISPLAY_LIMITS.OUTPUT_SUMMARY),
|
|
7166
7083
|
error,
|
|
7167
7084
|
duration
|
|
7168
7085
|
}
|
|
@@ -7273,7 +7190,7 @@ Please decide how to handle this error and continue.`;
|
|
|
7273
7190
|
\u{1F4A1} TIP: If you need to see the full output, use a tool to read the file directly or run the command with | head, | tail, or | grep.`;
|
|
7274
7191
|
}
|
|
7275
7192
|
if (result2.error) {
|
|
7276
|
-
outputText = this.
|
|
7193
|
+
outputText = this.enrichToolError({ toolName: call.name, input: call.input, error: result2.error, originalOutput: outputText, progress });
|
|
7277
7194
|
if (progress) progress.toolErrors++;
|
|
7278
7195
|
} else {
|
|
7279
7196
|
if (progress) {
|
|
@@ -7286,75 +7203,17 @@ Please decide how to handle this error and continue.`;
|
|
|
7286
7203
|
return { toolCallId: call.id, output: outputText, error: result2.error };
|
|
7287
7204
|
} catch (error) {
|
|
7288
7205
|
const errorMsg = String(error);
|
|
7289
|
-
const enrichedError = this.
|
|
7206
|
+
const enrichedError = this.enrichToolError({ toolName: call.name, input: call.input, error: errorMsg, originalOutput: "", progress });
|
|
7290
7207
|
if (progress) progress.toolErrors++;
|
|
7291
7208
|
this.emitToolResult(call.name, false, enrichedError, errorMsg, Date.now() - toolStartTime);
|
|
7292
7209
|
return { toolCallId: call.id, output: enrichedError, error: errorMsg };
|
|
7293
7210
|
}
|
|
7294
7211
|
}
|
|
7295
7212
|
/**
|
|
7296
|
-
* Enrich tool error
|
|
7297
|
-
* Instead of just showing the raw error, we add hints about what went wrong
|
|
7298
|
-
* and what the agent can do to fix it.
|
|
7213
|
+
* Enrich tool error — delegates to extracted module (§3-1)
|
|
7299
7214
|
*/
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
const lines = [];
|
|
7303
|
-
if (originalOutput) {
|
|
7304
|
-
lines.push(originalOutput);
|
|
7305
|
-
}
|
|
7306
|
-
lines.push("");
|
|
7307
|
-
lines.push(`[TOOL ERROR ANALYSIS]`);
|
|
7308
|
-
lines.push(`Tool: ${toolName}`);
|
|
7309
|
-
lines.push(`Error: ${error}`);
|
|
7310
|
-
const errorLower = error.toLowerCase();
|
|
7311
|
-
if (errorLower.includes("command blocked") || errorLower.includes("injection pattern") || errorLower.includes("not in the allowed") || errorLower.includes("not in allowed paths")) {
|
|
7312
|
-
if (progress) {
|
|
7313
|
-
const patternKey = `${toolName}:${errorLower.slice(0, AGENT_LIMITS.BLOCKED_PATTERN_KEY_SLICE)}`;
|
|
7314
|
-
const count = (progress.blockedCommandPatterns.get(patternKey) || 0) + 1;
|
|
7315
|
-
progress.blockedCommandPatterns.set(patternKey, count);
|
|
7316
|
-
progress.totalBlockedCommands++;
|
|
7317
|
-
if (count >= AGENT_LIMITS.MAX_BLOCKED_BEFORE_WARN) {
|
|
7318
|
-
lines.push(`
|
|
7319
|
-
\u26A0\uFE0F REPEAT BLOCK DETECTED (${count}x same pattern). STOP retrying this approach.`);
|
|
7320
|
-
lines.push(`You have been blocked ${progress.totalBlockedCommands} times total this session.`);
|
|
7321
|
-
}
|
|
7322
|
-
}
|
|
7323
|
-
if (errorLower.includes("pipe target") || errorLower.includes("injection")) {
|
|
7324
|
-
lines.push(`Fix: Use the tool's built-in output options instead of shell pipes.`);
|
|
7325
|
-
lines.push(`Alternative approaches:`);
|
|
7326
|
-
lines.push(` 1. Save output to file: command > /tmp/output.txt, then use read_file("/tmp/output.txt")`);
|
|
7327
|
-
lines.push(` 2. Use tool-specific flags: nmap -oN /tmp/scan.txt, curl -o /tmp/page.html`);
|
|
7328
|
-
lines.push(` 3. Use browse_url for web content instead of curl | grep`);
|
|
7329
|
-
lines.push(` 4. If filtering output: run the command first, then read_file + parse results`);
|
|
7330
|
-
} else if (errorLower.includes("redirect")) {
|
|
7331
|
-
lines.push(`Fix: Redirect to /tmp/ or /root/ paths only.`);
|
|
7332
|
-
lines.push(`Example: command > /tmp/output.txt or command 2>&1 > /tmp/errors.txt`);
|
|
7333
|
-
} else {
|
|
7334
|
-
lines.push(`Fix: Simplify the command. Avoid chaining complex operations.`);
|
|
7335
|
-
lines.push(`Break into multiple steps: run_cmd \u2192 read_file \u2192 run_cmd.`);
|
|
7336
|
-
}
|
|
7337
|
-
} else if (errorLower.includes("missing") && errorLower.includes("parameter") || errorLower.includes("required") && errorLower.includes("missing") || errorLower.includes("is required")) {
|
|
7338
|
-
const providedParams = Object.keys(input).join(", ") || "none";
|
|
7339
|
-
lines.push(`Provided parameters: ${providedParams}`);
|
|
7340
|
-
lines.push(`Fix: Check the tool's required parameters and provide ALL required values.`);
|
|
7341
|
-
lines.push(`Action: Retry this tool call with the missing parameter(s) added.`);
|
|
7342
|
-
} else if (errorLower.includes("not found") || errorLower.includes("command not found") || errorLower.includes("no such file")) {
|
|
7343
|
-
lines.push(`Fix: The tool or command may not be installed.`);
|
|
7344
|
-
lines.push(`Actions: (1) Try an alternative tool, (2) Use web_search to find installation instructions, or (3) Use a different approach entirely.`);
|
|
7345
|
-
} else if (errorLower.includes("permission denied") || errorLower.includes("access denied") || errorLower.includes("eacces")) {
|
|
7346
|
-
lines.push(`Fix: Insufficient permissions for this operation.`);
|
|
7347
|
-
lines.push(`Actions: (1) Try with sudo if appropriate, (2) Use a different approach, or (3) Check if the target path/resource is correct.`);
|
|
7348
|
-
} else if (errorLower.includes("connection refused") || errorLower.includes("econnrefused") || errorLower.includes("connection reset") || errorLower.includes("timeout")) {
|
|
7349
|
-
lines.push(`Fix: Cannot reach the target service.`);
|
|
7350
|
-
lines.push(`Actions: (1) Verify the target host/port is correct, (2) Check if the service is running, (3) Try a different port or protocol.`);
|
|
7351
|
-
} else if (errorLower.includes("invalid") || errorLower.includes("malformed") || errorLower.includes("syntax error")) {
|
|
7352
|
-
lines.push(`Fix: The input format is incorrect.`);
|
|
7353
|
-
lines.push(`Actions: (1) Check the input format and fix it, (2) Use web_search to find the correct syntax, or (3) Try a simpler input.`);
|
|
7354
|
-
} else {
|
|
7355
|
-
lines.push(`Actions: (1) Analyze the error and modify your approach, (2) Use web_search("${toolName} ${errorLower.slice(0, AGENT_LIMITS.ERROR_SEARCH_PREVIEW_SLICE)}") to research the error, (3) Try an alternative tool or method.`);
|
|
7356
|
-
}
|
|
7357
|
-
return lines.join("\n");
|
|
7215
|
+
enrichToolError(ctx) {
|
|
7216
|
+
return enrichToolErrorContext(ctx);
|
|
7358
7217
|
}
|
|
7359
7218
|
getToolSchemas() {
|
|
7360
7219
|
if (!this.toolRegistry) {
|
|
@@ -7416,7 +7275,7 @@ Please decide how to handle this error and continue.`;
|
|
|
7416
7275
|
};
|
|
7417
7276
|
|
|
7418
7277
|
// src/agents/prompt-builder.ts
|
|
7419
|
-
import { readFileSync as readFileSync4, existsSync as existsSync6 } from "fs";
|
|
7278
|
+
import { readFileSync as readFileSync4, existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
|
|
7420
7279
|
import { join as join9, dirname as dirname5 } from "path";
|
|
7421
7280
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7422
7281
|
|
|
@@ -7504,13 +7363,13 @@ var CORE_KNOWLEDGE_FILES = [
|
|
|
7504
7363
|
];
|
|
7505
7364
|
var PHASE_TECHNIQUE_MAP = {
|
|
7506
7365
|
[PHASES.RECON]: ["network-svc", "shells", "crypto"],
|
|
7507
|
-
[PHASES.VULN_ANALYSIS]: ["injection", "network-svc", "file-attacks", "crypto"],
|
|
7508
|
-
[PHASES.EXPLOIT]: ["injection", "shells", "file-attacks", "network-svc", "pwn", "container-escape"],
|
|
7509
|
-
[PHASES.POST_EXPLOIT]: ["privesc", "lateral", "auth-access", "shells", "container-escape"],
|
|
7366
|
+
[PHASES.VULN_ANALYSIS]: ["injection", "network-svc", "file-attacks", "crypto", "reversing"],
|
|
7367
|
+
[PHASES.EXPLOIT]: ["injection", "shells", "file-attacks", "network-svc", "pwn", "container-escape", "reversing"],
|
|
7368
|
+
[PHASES.POST_EXPLOIT]: ["privesc", "lateral", "auth-access", "shells", "container-escape", "forensics"],
|
|
7510
7369
|
[PHASES.PRIV_ESC]: ["privesc", "auth-access", "shells", "pwn", "container-escape"],
|
|
7511
7370
|
[PHASES.LATERAL]: ["lateral", "ad-attack", "auth-access", "container-escape"],
|
|
7512
7371
|
[PHASES.PERSISTENCE]: ["shells", "privesc"],
|
|
7513
|
-
[PHASES.EXFIL]: ["lateral", "network-svc"],
|
|
7372
|
+
[PHASES.EXFIL]: ["lateral", "network-svc", "forensics"],
|
|
7514
7373
|
[PHASES.WEB]: ["injection", "file-attacks", "auth-access", "crypto"],
|
|
7515
7374
|
[PHASES.REPORT]: []
|
|
7516
7375
|
// Report phase needs no attack techniques
|
|
@@ -7598,19 +7457,22 @@ ${content}
|
|
|
7598
7457
|
return fragments.join("\n\n");
|
|
7599
7458
|
}
|
|
7600
7459
|
/**
|
|
7601
|
-
* Load
|
|
7602
|
-
*
|
|
7460
|
+
* Load technique files relevant to the current phase.
|
|
7461
|
+
*
|
|
7462
|
+
* Loading strategy (Philosophy §11 — zero-code extension):
|
|
7463
|
+
* 1. PHASE_TECHNIQUE_MAP defines priority techniques per phase (loaded first)
|
|
7464
|
+
* 2. Any .md file in techniques/ NOT in the map is auto-discovered and loaded
|
|
7465
|
+
* as general reference — NO code change needed to add new techniques.
|
|
7603
7466
|
*
|
|
7604
|
-
*
|
|
7605
|
-
*
|
|
7606
|
-
* use web_search to look up specific attack techniques on demand.
|
|
7467
|
+
* The map is an optimization (priority ordering), not a gate.
|
|
7468
|
+
* "마크다운 파일 하나를 폴더에 넣으면, PromptBuilder가 자동으로 발견하고 로드한다."
|
|
7607
7469
|
*/
|
|
7608
7470
|
loadPhaseRelevantTechniques(phase) {
|
|
7609
7471
|
if (!existsSync6(TECHNIQUES_DIR)) return "";
|
|
7610
|
-
const
|
|
7611
|
-
|
|
7472
|
+
const priorityTechniques = PHASE_TECHNIQUE_MAP[phase] || [];
|
|
7473
|
+
const loadedSet = /* @__PURE__ */ new Set();
|
|
7612
7474
|
const fragments = [];
|
|
7613
|
-
for (const technique of
|
|
7475
|
+
for (const technique of priorityTechniques) {
|
|
7614
7476
|
const filePath = join9(TECHNIQUES_DIR, `${technique}.md`);
|
|
7615
7477
|
try {
|
|
7616
7478
|
if (!existsSync6(filePath)) continue;
|
|
@@ -7619,10 +7481,25 @@ ${content}
|
|
|
7619
7481
|
fragments.push(`<technique-reference category="${technique}">
|
|
7620
7482
|
${content}
|
|
7621
7483
|
</technique-reference>`);
|
|
7484
|
+
loadedSet.add(`${technique}.md`);
|
|
7622
7485
|
}
|
|
7623
7486
|
} catch {
|
|
7624
7487
|
}
|
|
7625
7488
|
}
|
|
7489
|
+
try {
|
|
7490
|
+
const allFiles = readdirSync2(TECHNIQUES_DIR).filter((f) => f.endsWith(".md") && f !== "README.md" && !loadedSet.has(f));
|
|
7491
|
+
for (const file of allFiles) {
|
|
7492
|
+
const filePath = join9(TECHNIQUES_DIR, file);
|
|
7493
|
+
const content = readFileSync4(filePath, PROMPT_CONFIG.ENCODING);
|
|
7494
|
+
if (content) {
|
|
7495
|
+
const category = file.replace(".md", "");
|
|
7496
|
+
fragments.push(`<technique-reference category="${category}">
|
|
7497
|
+
${content}
|
|
7498
|
+
</technique-reference>`);
|
|
7499
|
+
}
|
|
7500
|
+
}
|
|
7501
|
+
} catch {
|
|
7502
|
+
}
|
|
7626
7503
|
return fragments.join("\n\n");
|
|
7627
7504
|
}
|
|
7628
7505
|
getScopeFragment() {
|
|
@@ -7851,30 +7728,6 @@ var formatInlineStatus = () => {
|
|
|
7851
7728
|
// src/platform/tui/hooks/useAgentState.ts
|
|
7852
7729
|
import { useState, useRef, useCallback } from "react";
|
|
7853
7730
|
|
|
7854
|
-
// src/shared/constants/thought.ts
|
|
7855
|
-
var THOUGHT_TYPE = {
|
|
7856
|
-
THINKING: "thinking",
|
|
7857
|
-
// LLM text streaming
|
|
7858
|
-
REASONING: "reasoning",
|
|
7859
|
-
// LLM extended thinking
|
|
7860
|
-
PLANNING: "planning",
|
|
7861
|
-
// Strategic planning
|
|
7862
|
-
OBSERVATION: "observation",
|
|
7863
|
-
// Observing results
|
|
7864
|
-
HYPOTHESIS: "hypothesis",
|
|
7865
|
-
// Forming hypothesis
|
|
7866
|
-
REFLECTION: "reflection",
|
|
7867
|
-
// Self-reflection
|
|
7868
|
-
ACTION: "action",
|
|
7869
|
-
// Taking action
|
|
7870
|
-
RESULT: "result",
|
|
7871
|
-
// Action result
|
|
7872
|
-
STUCK: "stuck",
|
|
7873
|
-
// Detected stuck state
|
|
7874
|
-
BREAKTHROUGH: "breakthrough"
|
|
7875
|
-
// Found breakthrough
|
|
7876
|
-
};
|
|
7877
|
-
|
|
7878
7731
|
// src/shared/constants/theme.ts
|
|
7879
7732
|
var THEME = {
|
|
7880
7733
|
// Backgrounds (deep dark with blue tint)
|
|
@@ -8022,18 +7875,6 @@ var ICONS = {
|
|
|
8022
7875
|
pwned: "\u25C8"
|
|
8023
7876
|
// Compromised
|
|
8024
7877
|
};
|
|
8025
|
-
var THOUGHT_LABELS = {
|
|
8026
|
-
[THOUGHT_TYPE.THINKING]: "[think]",
|
|
8027
|
-
[THOUGHT_TYPE.REASONING]: "[reason]",
|
|
8028
|
-
[THOUGHT_TYPE.PLANNING]: "[plan]",
|
|
8029
|
-
[THOUGHT_TYPE.OBSERVATION]: "[observe]",
|
|
8030
|
-
[THOUGHT_TYPE.HYPOTHESIS]: "[hypothesis]",
|
|
8031
|
-
[THOUGHT_TYPE.REFLECTION]: "[reflect]",
|
|
8032
|
-
[THOUGHT_TYPE.ACTION]: "[action]",
|
|
8033
|
-
[THOUGHT_TYPE.RESULT]: "[result]",
|
|
8034
|
-
[THOUGHT_TYPE.STUCK]: "[stuck]",
|
|
8035
|
-
[THOUGHT_TYPE.BREAKTHROUGH]: "[!]"
|
|
8036
|
-
};
|
|
8037
7878
|
|
|
8038
7879
|
// src/platform/tui/constants/display.ts
|
|
8039
7880
|
var TUI_DISPLAY_LIMITS = {
|
|
@@ -8062,7 +7903,19 @@ var TUI_DISPLAY_LIMITS = {
|
|
|
8062
7903
|
/** Timer update interval in ms */
|
|
8063
7904
|
timerInterval: 1e3,
|
|
8064
7905
|
/** Exit delay in ms */
|
|
8065
|
-
exitDelay: 100
|
|
7906
|
+
exitDelay: 100,
|
|
7907
|
+
/** Purpose text max length before truncation */
|
|
7908
|
+
purposeMaxLength: 30,
|
|
7909
|
+
/** Purpose text truncated length */
|
|
7910
|
+
purposeTruncated: 27,
|
|
7911
|
+
/** Tool detail preview length in flow display */
|
|
7912
|
+
toolDetailPreview: 100,
|
|
7913
|
+
/** Observe detail preview length in flow display */
|
|
7914
|
+
observeDetailPreview: 80,
|
|
7915
|
+
/** Max flow nodes to display */
|
|
7916
|
+
maxFlowNodes: 50,
|
|
7917
|
+
/** Max stopped processes to show */
|
|
7918
|
+
maxStoppedProcesses: 3
|
|
8066
7919
|
};
|
|
8067
7920
|
var MESSAGE_STYLES = {
|
|
8068
7921
|
colors: {
|
|
@@ -8532,7 +8385,7 @@ function ProcessRow({ proc, compact }) {
|
|
|
8532
8385
|
const duration = formatDuration2(proc.durationMs);
|
|
8533
8386
|
const port = proc.listeningPort ? `:${proc.listeningPort}` : "";
|
|
8534
8387
|
const purpose = proc.purpose || proc.description || "";
|
|
8535
|
-
const truncatedPurpose = compact && purpose.length >
|
|
8388
|
+
const truncatedPurpose = compact && purpose.length > TUI_DISPLAY_LIMITS.purposeMaxLength ? purpose.slice(0, TUI_DISPLAY_LIMITS.purposeTruncated) + "..." : purpose;
|
|
8536
8389
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
8537
8390
|
/* @__PURE__ */ jsx(StatusIndicator, { running: proc.running, exitCode: proc.exitCode }),
|
|
8538
8391
|
/* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
|
|
@@ -8589,10 +8442,10 @@ var InlineStatus = ({
|
|
|
8589
8442
|
stopped.length,
|
|
8590
8443
|
")"
|
|
8591
8444
|
] }),
|
|
8592
|
-
stopped.slice(0,
|
|
8593
|
-
stopped.length >
|
|
8445
|
+
stopped.slice(0, TUI_DISPLAY_LIMITS.maxStoppedProcesses).map((proc) => /* @__PURE__ */ jsx(ProcessRow, { proc, compact }, proc.id)),
|
|
8446
|
+
stopped.length > TUI_DISPLAY_LIMITS.maxStoppedProcesses && /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
|
|
8594
8447
|
" ... and ",
|
|
8595
|
-
stopped.length -
|
|
8448
|
+
stopped.length - TUI_DISPLAY_LIMITS.maxStoppedProcesses,
|
|
8596
8449
|
" more"
|
|
8597
8450
|
] })
|
|
8598
8451
|
] }),
|
|
@@ -9029,19 +8882,6 @@ ${procData.stdout || "(no output)"}
|
|
|
9029
8882
|
};
|
|
9030
8883
|
var app_default = App;
|
|
9031
8884
|
|
|
9032
|
-
// src/shared/constants/_shared/signal.const.ts
|
|
9033
|
-
var EXIT_CODE = {
|
|
9034
|
-
SUCCESS: 0,
|
|
9035
|
-
ERROR: 1,
|
|
9036
|
-
// Unix convention: 128 + signal number
|
|
9037
|
-
SIGINT: 130,
|
|
9038
|
-
// 128 + 2
|
|
9039
|
-
SIGTERM: 143,
|
|
9040
|
-
// 128 + 15
|
|
9041
|
-
SIGKILL: 137
|
|
9042
|
-
// 128 + 9
|
|
9043
|
-
};
|
|
9044
|
-
|
|
9045
8885
|
// src/shared/constants/cli-defaults.const.ts
|
|
9046
8886
|
var CLI_DEFAULT = {
|
|
9047
8887
|
/** Default maximum steps for run command */
|
|
@@ -9112,8 +8952,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
9112
8952
|
const shutdown = async (exitCode = 0) => {
|
|
9113
8953
|
process.exit(exitCode);
|
|
9114
8954
|
};
|
|
9115
|
-
process.on("SIGINT", () => shutdown(
|
|
9116
|
-
process.on("SIGTERM", () => shutdown(
|
|
8955
|
+
process.on("SIGINT", () => shutdown(EXIT_CODES.SIGINT));
|
|
8956
|
+
process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
|
|
9117
8957
|
try {
|
|
9118
8958
|
const result2 = await agent.execute(objective);
|
|
9119
8959
|
console.log(chalk.hex(THEME.status.success)("\n[+] Assessment complete!\n"));
|
|
@@ -9145,8 +8985,8 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
|
|
|
9145
8985
|
const shutdown = async (exitCode = 0) => {
|
|
9146
8986
|
process.exit(exitCode);
|
|
9147
8987
|
};
|
|
9148
|
-
process.on("SIGINT", () => shutdown(
|
|
9149
|
-
process.on("SIGTERM", () => shutdown(
|
|
8988
|
+
process.on("SIGINT", () => shutdown(EXIT_CODES.SIGINT));
|
|
8989
|
+
process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
|
|
9150
8990
|
try {
|
|
9151
8991
|
await agent.execute(`Perform a ${options.scanType} scan on ${target}${options.ports ? ` focusing on ports ${options.ports}` : ""}. Analyze the results and identify potential vulnerabilities.`);
|
|
9152
8992
|
console.log(chalk.hex(THEME.status.success)("[+] Scan complete!"));
|