pentesting 0.73.2 → 0.73.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -0
- package/dist/agent-tool-MP274HWD.js +989 -0
- package/dist/{chunk-BGEXGHPB.js → chunk-3KWJPPYB.js} +917 -430
- package/dist/{chunk-KBJPZDIL.js → chunk-7E2VUIFU.js} +456 -211
- package/dist/chunk-I52SWXYV.js +1122 -0
- package/dist/main.js +2285 -474
- package/dist/{persistence-VFIOGTRC.js → persistence-BNVN3WW6.js} +2 -2
- package/dist/{process-registry-GSHEX2LT.js → process-registry-BI7BKPHN.js} +1 -1
- package/dist/prompts/main-agent.md +35 -1
- package/dist/prompts/strategist-system.md +34 -0
- package/package.json +3 -4
- package/dist/agent-tool-HYQGTZC4.js +0 -256
- package/dist/chunk-YFDJI3GO.js +0 -331
|
@@ -13,12 +13,13 @@ import {
|
|
|
13
13
|
getAllProcesses,
|
|
14
14
|
getBackgroundProcessesMap,
|
|
15
15
|
getLimits,
|
|
16
|
-
getPipelineConfig,
|
|
17
16
|
getProcess,
|
|
18
17
|
getProcessEventLog,
|
|
18
|
+
getRequiredRuntimeSection,
|
|
19
|
+
getRuntimeSectionOr,
|
|
19
20
|
logEvent,
|
|
20
21
|
setProcess
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-I52SWXYV.js";
|
|
22
23
|
|
|
23
24
|
// src/shared/constants/time/conversions.ts
|
|
24
25
|
var MS_PER_MINUTE = 6e4;
|
|
@@ -138,6 +139,119 @@ var DISPLAY_LIMITS = {
|
|
|
138
139
|
SESSION_VALUE_PREVIEW: 300
|
|
139
140
|
};
|
|
140
141
|
|
|
142
|
+
// src/shared/utils/file-ops/runtime-asset-resolver.ts
|
|
143
|
+
import { existsSync, readFileSync } from "fs";
|
|
144
|
+
import path from "path";
|
|
145
|
+
import { fileURLToPath } from "url";
|
|
146
|
+
var RUNTIME_FILE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
147
|
+
var DIST_DIR = path.basename(RUNTIME_FILE_DIR) === "dist" ? RUNTIME_FILE_DIR : path.resolve(process.cwd(), "dist");
|
|
148
|
+
function mapSourceAssetToDist(declaredPath) {
|
|
149
|
+
if (declaredPath.startsWith("src/agents/prompts/")) {
|
|
150
|
+
return path.join(DIST_DIR, "prompts", declaredPath.slice("src/agents/prompts/".length));
|
|
151
|
+
}
|
|
152
|
+
if (/^src\/domains\/[^/]+\/prompt\.md$/.test(declaredPath)) {
|
|
153
|
+
const [, domain] = declaredPath.split("/");
|
|
154
|
+
return path.join(DIST_DIR, domain, "prompt.md");
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
function getRuntimeAssetCandidates(declaredPath) {
|
|
159
|
+
const normalized = declaredPath.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
160
|
+
const candidates = [path.resolve(process.cwd(), normalized)];
|
|
161
|
+
const distCandidate = mapSourceAssetToDist(normalized);
|
|
162
|
+
if (distCandidate) {
|
|
163
|
+
candidates.push(distCandidate);
|
|
164
|
+
}
|
|
165
|
+
return [...new Set(candidates)];
|
|
166
|
+
}
|
|
167
|
+
function resolveRuntimeAssetPath(declaredPath) {
|
|
168
|
+
for (const candidate of getRuntimeAssetCandidates(declaredPath)) {
|
|
169
|
+
if (existsSync(candidate)) {
|
|
170
|
+
return candidate;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
function readRuntimeAssetFile(declaredPath, encoding = "utf-8") {
|
|
176
|
+
const resolved = resolveRuntimeAssetPath(declaredPath);
|
|
177
|
+
if (!resolved) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return readFileSync(resolved, encoding);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/agents/llm-node-config.ts
|
|
184
|
+
var RUNTIME_CONFIG_PATH = "src/agents/runtime-config.ts";
|
|
185
|
+
function buildMissingNodeError(nodeName) {
|
|
186
|
+
return new Error(
|
|
187
|
+
`[runtime-config] llm_nodes.${nodeName} is not declared. Add it to ${RUNTIME_CONFIG_PATH}.`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
function buildMissingPromptError(nodeName) {
|
|
191
|
+
return new Error(
|
|
192
|
+
`[runtime-config] llm_nodes.${nodeName} must declare system_prompt_file, fallback_system_prompt_file, or system_prompt. Add one to ${RUNTIME_CONFIG_PATH}.`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
function buildMissingCooldownPolicyError(nodeName) {
|
|
196
|
+
return new Error(
|
|
197
|
+
`[runtime-config] llm_nodes.${nodeName}.cooldown_policy is required but not set. Add it to ${RUNTIME_CONFIG_PATH}.`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
function resolvePromptFile(declaredPath, context) {
|
|
201
|
+
const content = readRuntimeAssetFile(declaredPath, "utf-8");
|
|
202
|
+
if (content === null) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`[runtime-config] ${context} points to "${declaredPath}" but no runtime asset was found. Tried: ${getRuntimeAssetCandidates(declaredPath).join(", ")}`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return content.trim();
|
|
208
|
+
}
|
|
209
|
+
function resolveInlinePrompt(prompt2) {
|
|
210
|
+
return prompt2 ? prompt2.trim() : void 0;
|
|
211
|
+
}
|
|
212
|
+
function resolveConfiguredPrompt(nodeName, nodeConfig) {
|
|
213
|
+
if (nodeConfig.system_prompt_file) {
|
|
214
|
+
return resolvePromptFile(
|
|
215
|
+
nodeConfig.system_prompt_file,
|
|
216
|
+
`llm_nodes.${nodeName}.system_prompt_file`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (nodeConfig.fallback_system_prompt_file) {
|
|
220
|
+
return resolvePromptFile(
|
|
221
|
+
nodeConfig.fallback_system_prompt_file,
|
|
222
|
+
`llm_nodes.${nodeName}.fallback_system_prompt_file`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return resolveInlinePrompt(nodeConfig.system_prompt) ?? resolveInlinePrompt(nodeConfig.fallback_system_prompt);
|
|
226
|
+
}
|
|
227
|
+
function getLlmNodeConfig(nodeName) {
|
|
228
|
+
const nodeConfig = getRequiredRuntimeSection("llm_nodes")[nodeName];
|
|
229
|
+
if (!nodeConfig) {
|
|
230
|
+
throw buildMissingNodeError(nodeName);
|
|
231
|
+
}
|
|
232
|
+
return nodeConfig;
|
|
233
|
+
}
|
|
234
|
+
function llmNodeSystemPrompt(nodeName) {
|
|
235
|
+
const prompt2 = resolveConfiguredPrompt(nodeName, getLlmNodeConfig(nodeName));
|
|
236
|
+
if (!prompt2) {
|
|
237
|
+
throw buildMissingPromptError(nodeName);
|
|
238
|
+
}
|
|
239
|
+
return prompt2;
|
|
240
|
+
}
|
|
241
|
+
function llmNodeOutputParsing(nodeName) {
|
|
242
|
+
return getLlmNodeConfig(nodeName).output_parsing;
|
|
243
|
+
}
|
|
244
|
+
function llmNodeCooldownPolicy(nodeName) {
|
|
245
|
+
const policy = getLlmNodeConfig(nodeName).cooldown_policy;
|
|
246
|
+
if (!policy) {
|
|
247
|
+
throw buildMissingCooldownPolicyError(nodeName);
|
|
248
|
+
}
|
|
249
|
+
return policy;
|
|
250
|
+
}
|
|
251
|
+
function llmNodeMaxTokens(nodeName, fallback) {
|
|
252
|
+
return getLlmNodeConfig(nodeName).limits?.max_tokens ?? fallback;
|
|
253
|
+
}
|
|
254
|
+
|
|
141
255
|
// src/shared/constants/llm/config.ts
|
|
142
256
|
var RETRY_CONFIG = {
|
|
143
257
|
baseDelayMs: 2e3,
|
|
@@ -150,9 +264,8 @@ var RETRY_CONFIG = {
|
|
|
150
264
|
autoRetryDelayMs: 1e4
|
|
151
265
|
// 10s fixed interval for 429/network errors
|
|
152
266
|
};
|
|
153
|
-
var
|
|
154
|
-
var
|
|
155
|
-
var strategistTokens = config.llm_nodes?.["strategist"]?.limits?.max_tokens ?? 262144;
|
|
267
|
+
var mainLlmTokens = llmNodeMaxTokens("main_llm", 128e3);
|
|
268
|
+
var strategistTokens = llmNodeMaxTokens("strategist", 262144);
|
|
156
269
|
var LLM_LIMITS = {
|
|
157
270
|
/** WHY 64K: non-streaming calls (orchestrator, summaries) benefit from
|
|
158
271
|
* generous output budgets. Don't force premature truncation. */
|
|
@@ -194,7 +307,7 @@ var AGENT_LIMITS = {
|
|
|
194
307
|
MAX_FAILED_PATHS: state.max_failed_paths,
|
|
195
308
|
MAX_ATTACK_NODES: state.max_attack_nodes,
|
|
196
309
|
MAX_ATTACK_EDGES: state.max_attack_edges,
|
|
197
|
-
// ─── Fixed Infrastructure Constants (not tunable via
|
|
310
|
+
// ─── Fixed Infrastructure Constants (not tunable via runtime config) ───
|
|
198
311
|
/** Infinite retry on LLM errors — rate limit recovery */
|
|
199
312
|
MAX_CONSECUTIVE_LLM_ERRORS: agent_loop.max_consecutive_llm_errors === "infinity" || agent_loop.max_consecutive_llm_errors === Infinity ? Infinity : Number(agent_loop.max_consecutive_llm_errors ?? Infinity),
|
|
200
313
|
MAX_RETRIES: agent_loop.max_retries ?? 3,
|
|
@@ -218,7 +331,7 @@ var MEMORY_LIMITS = {
|
|
|
218
331
|
TECHNIQUE_FAILURE_DECAY: state2.technique_failure_decay,
|
|
219
332
|
TECHNIQUE_PRUNE_THRESHOLD: state2.technique_prune_threshold,
|
|
220
333
|
MAX_TURN_ENTRIES: archive.max_turn_entries,
|
|
221
|
-
/** Maximum unverified techniques to show in prompt (display-only, not in
|
|
334
|
+
/** Maximum unverified techniques to show in prompt (display-only, not in runtime config) */
|
|
222
335
|
PROMPT_UNVERIFIED_TECHNIQUES: 10
|
|
223
336
|
};
|
|
224
337
|
|
|
@@ -235,7 +348,7 @@ var INPUT_PROMPT_PATTERNS = [
|
|
|
235
348
|
|
|
236
349
|
// src/shared/constants/agent.ts
|
|
237
350
|
var APP_NAME = "Pentest AI";
|
|
238
|
-
var APP_VERSION = "0.73.
|
|
351
|
+
var APP_VERSION = "0.73.4";
|
|
239
352
|
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
240
353
|
var LLM_ROLES = {
|
|
241
354
|
SYSTEM: "system",
|
|
@@ -509,6 +622,19 @@ var TOOL_NAMES = {
|
|
|
509
622
|
READ_FILE: "read_file",
|
|
510
623
|
WRITE_FILE: "write_file",
|
|
511
624
|
BG_PROCESS: "bg_process",
|
|
625
|
+
LISTENER_START: "listener_start",
|
|
626
|
+
LISTENER_STATUS: "listener_status",
|
|
627
|
+
SHELL_PROMOTE: "shell_promote",
|
|
628
|
+
SHELL_EXEC: "shell_exec",
|
|
629
|
+
SHELL_CHECK: "shell_check",
|
|
630
|
+
SHELL_UPGRADE: "shell_upgrade",
|
|
631
|
+
EXPLOIT_CREDENTIAL_CHECK: "exploit_credential_check",
|
|
632
|
+
EXPLOIT_ARTIFACT_CHECK: "exploit_artifact_check",
|
|
633
|
+
EXPLOIT_FOOTHOLD_CHECK: "exploit_foothold_check",
|
|
634
|
+
EXPLOIT_VECTOR_CHECK: "exploit_vector_check",
|
|
635
|
+
PWN_CRASH_REPRO: "pwn_crash_repro",
|
|
636
|
+
PWN_OFFSET_CHECK: "pwn_offset_check",
|
|
637
|
+
PWN_PAYLOAD_SMOKE: "pwn_payload_smoke",
|
|
512
638
|
// Intelligence & Research
|
|
513
639
|
PARSE_NMAP: "parse_nmap",
|
|
514
640
|
SEARCH_CVE: "search_cve",
|
|
@@ -936,7 +1062,20 @@ function getInputHandler() {
|
|
|
936
1062
|
}
|
|
937
1063
|
|
|
938
1064
|
// src/shared/constants/paths.ts
|
|
939
|
-
import
|
|
1065
|
+
import path2 from "path";
|
|
1066
|
+
|
|
1067
|
+
// src/agents/workspace-config.ts
|
|
1068
|
+
var DEFAULT_WORKSPACE_ROOT = ".pentesting";
|
|
1069
|
+
var DEFAULT_WORKSPACE_DIRECTORIES = {};
|
|
1070
|
+
function getWorkspaceConfig() {
|
|
1071
|
+
return getRuntimeSectionOr("workspace", {});
|
|
1072
|
+
}
|
|
1073
|
+
function getWorkspaceRoot() {
|
|
1074
|
+
return getWorkspaceConfig().root ?? DEFAULT_WORKSPACE_ROOT;
|
|
1075
|
+
}
|
|
1076
|
+
function getWorkspaceDirectories() {
|
|
1077
|
+
return getWorkspaceConfig().directories ?? DEFAULT_WORKSPACE_DIRECTORIES;
|
|
1078
|
+
}
|
|
940
1079
|
|
|
941
1080
|
// src/shared/constants/files/extensions.ts
|
|
942
1081
|
var FILE_EXTENSIONS = {
|
|
@@ -983,9 +1122,9 @@ var SPECIAL_FILES = {
|
|
|
983
1122
|
};
|
|
984
1123
|
|
|
985
1124
|
// src/shared/utils/file-ops/file-utils.ts
|
|
986
|
-
import { existsSync, mkdirSync } from "fs";
|
|
1125
|
+
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
987
1126
|
function ensureDirExists(dirPath) {
|
|
988
|
-
if (!
|
|
1127
|
+
if (!existsSync2(dirPath)) {
|
|
989
1128
|
mkdirSync(dirPath, { recursive: true });
|
|
990
1129
|
}
|
|
991
1130
|
}
|
|
@@ -1013,15 +1152,13 @@ var _dirs = null;
|
|
|
1013
1152
|
var _root = null;
|
|
1014
1153
|
function getRoot() {
|
|
1015
1154
|
if (_root === null) {
|
|
1016
|
-
|
|
1017
|
-
_root = ws?.root ?? ".pentesting";
|
|
1155
|
+
_root = getWorkspaceRoot();
|
|
1018
1156
|
}
|
|
1019
1157
|
return _root;
|
|
1020
1158
|
}
|
|
1021
1159
|
function getDirs() {
|
|
1022
1160
|
if (_dirs === null) {
|
|
1023
|
-
|
|
1024
|
-
_dirs = ws?.directories ?? {};
|
|
1161
|
+
_dirs = getWorkspaceDirectories();
|
|
1025
1162
|
}
|
|
1026
1163
|
return _dirs;
|
|
1027
1164
|
}
|
|
@@ -1033,39 +1170,39 @@ var WORK_DIR = dir("workspace", `${getRoot()}/workspace`);
|
|
|
1033
1170
|
var MEMORY_DIR = dir("memory", `${getRoot()}/memory`);
|
|
1034
1171
|
var WORKSPACE = {
|
|
1035
1172
|
get ROOT() {
|
|
1036
|
-
return
|
|
1173
|
+
return path2.resolve(getRoot());
|
|
1037
1174
|
},
|
|
1038
1175
|
get TMP() {
|
|
1039
|
-
return
|
|
1176
|
+
return path2.resolve(dir("workspace", `${getRoot()}/workspace`));
|
|
1040
1177
|
},
|
|
1041
1178
|
get MEMORY() {
|
|
1042
|
-
return
|
|
1179
|
+
return path2.resolve(dir("memory", `${getRoot()}/memory`));
|
|
1043
1180
|
},
|
|
1044
1181
|
get POLICY() {
|
|
1045
|
-
return
|
|
1182
|
+
return path2.resolve(dir("memory", `${getRoot()}/memory`), SPECIAL_FILES.POLICY);
|
|
1046
1183
|
},
|
|
1047
1184
|
get REPORTS() {
|
|
1048
|
-
return
|
|
1185
|
+
return path2.resolve(dir("reports", `${getRoot()}/reports`));
|
|
1049
1186
|
},
|
|
1050
1187
|
get SESSIONS() {
|
|
1051
|
-
return
|
|
1188
|
+
return path2.resolve(dir("sessions", `${getRoot()}/sessions`));
|
|
1052
1189
|
},
|
|
1053
1190
|
get LOOT() {
|
|
1054
|
-
return
|
|
1191
|
+
return path2.resolve(dir("loot", `${getRoot()}/loot`));
|
|
1055
1192
|
},
|
|
1056
1193
|
get DEBUG() {
|
|
1057
|
-
return
|
|
1194
|
+
return path2.resolve(dir("debug", `${getRoot()}/debug`));
|
|
1058
1195
|
},
|
|
1059
1196
|
/** Turn insight files root: .pentesting/turns/ */
|
|
1060
1197
|
get TURNS() {
|
|
1061
|
-
return
|
|
1198
|
+
return path2.resolve(dir("turns", `${getRoot()}/turns`));
|
|
1062
1199
|
},
|
|
1063
1200
|
/**
|
|
1064
1201
|
* Resolve the turn memory file for a specific turn: .pentesting/turns/{N}-memory.md
|
|
1065
|
-
*
|
|
1202
|
+
* Runtime config turn storage policy → single file per turn
|
|
1066
1203
|
*/
|
|
1067
1204
|
turnPath(turn) {
|
|
1068
|
-
return
|
|
1205
|
+
return path2.resolve(dir("turns", `${getRoot()}/turns`), `${turn}-memory.md`);
|
|
1069
1206
|
}
|
|
1070
1207
|
};
|
|
1071
1208
|
|
|
@@ -1298,7 +1435,7 @@ var BLOCKED_BINARIES = /* @__PURE__ */ new Set([
|
|
|
1298
1435
|
]);
|
|
1299
1436
|
|
|
1300
1437
|
// src/shared/utils/debug/debug-logger.ts
|
|
1301
|
-
import { appendFileSync, writeFileSync, statSync, readFileSync } from "fs";
|
|
1438
|
+
import { appendFileSync, writeFileSync, statSync, readFileSync as readFileSync2 } from "fs";
|
|
1302
1439
|
import { join } from "path";
|
|
1303
1440
|
var ROTATE_BYTES = 5 * 1024 * 1024;
|
|
1304
1441
|
var WIPE_BYTES = 20 * 1024 * 1024;
|
|
@@ -1350,7 +1487,7 @@ var DebugLogger = class _DebugLogger {
|
|
|
1350
1487
|
writeFileSync(this.logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [GENERAL] === LOG WIPED (exceeded ${Math.round(WIPE_BYTES / 1024 / 1024)}MB) ===
|
|
1351
1488
|
`);
|
|
1352
1489
|
} else if (size > ROTATE_BYTES) {
|
|
1353
|
-
const content =
|
|
1490
|
+
const content = readFileSync2(this.logPath, "utf-8");
|
|
1354
1491
|
const half = content.slice(Math.floor(content.length / 2));
|
|
1355
1492
|
const firstNewline = half.indexOf("\n");
|
|
1356
1493
|
const trimmed = firstNewline >= 0 ? half.slice(firstNewline + 1) : half;
|
|
@@ -1507,6 +1644,57 @@ function splitByPipe(command) {
|
|
|
1507
1644
|
}
|
|
1508
1645
|
|
|
1509
1646
|
// src/shared/utils/command-validator/redirect.ts
|
|
1647
|
+
function isAllowedRedirectPath(target) {
|
|
1648
|
+
return SAFE_REDIRECT_PATHS.some((pathPrefix) => target.startsWith(pathPrefix));
|
|
1649
|
+
}
|
|
1650
|
+
function updateQuoteState(ch, state3) {
|
|
1651
|
+
if (ch === "'" && !state3.inDouble) {
|
|
1652
|
+
state3.inSingle = !state3.inSingle;
|
|
1653
|
+
return true;
|
|
1654
|
+
}
|
|
1655
|
+
if (ch === '"' && !state3.inSingle) {
|
|
1656
|
+
state3.inDouble = !state3.inDouble;
|
|
1657
|
+
return true;
|
|
1658
|
+
}
|
|
1659
|
+
return false;
|
|
1660
|
+
}
|
|
1661
|
+
function extractFileDescriptor(command, operatorIndex) {
|
|
1662
|
+
let fd = "";
|
|
1663
|
+
let b = operatorIndex - 1;
|
|
1664
|
+
while (b >= 0 && /\d/.test(command[b])) {
|
|
1665
|
+
fd = command[b] + fd;
|
|
1666
|
+
b--;
|
|
1667
|
+
}
|
|
1668
|
+
return fd;
|
|
1669
|
+
}
|
|
1670
|
+
function skipSpaces(command, start) {
|
|
1671
|
+
let index = start;
|
|
1672
|
+
while (index < command.length && /\s/.test(command[index])) index++;
|
|
1673
|
+
return index;
|
|
1674
|
+
}
|
|
1675
|
+
function readRedirectTarget(command, start) {
|
|
1676
|
+
let i = start;
|
|
1677
|
+
let target = "";
|
|
1678
|
+
let targetInSingle = false;
|
|
1679
|
+
let targetInDouble = false;
|
|
1680
|
+
while (i < command.length) {
|
|
1681
|
+
const ch = command[i];
|
|
1682
|
+
if (ch === "'" && !targetInDouble) {
|
|
1683
|
+
targetInSingle = !targetInSingle;
|
|
1684
|
+
i++;
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
if (ch === '"' && !targetInSingle) {
|
|
1688
|
+
targetInDouble = !targetInDouble;
|
|
1689
|
+
i++;
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
if (!targetInSingle && !targetInDouble && /[\s|;&<>]/.test(ch)) break;
|
|
1693
|
+
target += ch;
|
|
1694
|
+
i++;
|
|
1695
|
+
}
|
|
1696
|
+
return { target, nextIndex: i };
|
|
1697
|
+
}
|
|
1510
1698
|
function stripRedirects(segment) {
|
|
1511
1699
|
return segment.replace(/\d*>\s*&\d+/g, "").replace(/\d*>>\s*\S+/g, "").replace(/\d*>\s*\S+/g, "").replace(/<\s*\S+/g, "").trim();
|
|
1512
1700
|
}
|
|
@@ -1515,8 +1703,7 @@ function validateRedirects(command) {
|
|
|
1515
1703
|
for (const { type, target } of redirects) {
|
|
1516
1704
|
if (type === ">" || type === ">>") {
|
|
1517
1705
|
if (target.startsWith("&") || target === "/dev/null") continue;
|
|
1518
|
-
|
|
1519
|
-
if (!isAllowed) {
|
|
1706
|
+
if (!isAllowedRedirectPath(target)) {
|
|
1520
1707
|
return {
|
|
1521
1708
|
isSafe: false,
|
|
1522
1709
|
error: `Redirect to '${target}' is not in allowed paths`,
|
|
@@ -1524,8 +1711,7 @@ function validateRedirects(command) {
|
|
|
1524
1711
|
};
|
|
1525
1712
|
}
|
|
1526
1713
|
} else if (type === "<") {
|
|
1527
|
-
|
|
1528
|
-
if (!isAllowed) {
|
|
1714
|
+
if (!isAllowedRedirectPath(target)) {
|
|
1529
1715
|
return {
|
|
1530
1716
|
isSafe: false,
|
|
1531
1717
|
error: `Input redirect from '${target}' is not in allowed paths`,
|
|
@@ -1538,55 +1724,23 @@ function validateRedirects(command) {
|
|
|
1538
1724
|
}
|
|
1539
1725
|
function extractRedirectTargets(command) {
|
|
1540
1726
|
const redirects = [];
|
|
1541
|
-
|
|
1542
|
-
let inDouble = false;
|
|
1727
|
+
const quoteState = { inSingle: false, inDouble: false };
|
|
1543
1728
|
let i = 0;
|
|
1544
1729
|
while (i < command.length) {
|
|
1545
1730
|
const ch = command[i];
|
|
1546
|
-
if (ch
|
|
1547
|
-
inSingle = !inSingle;
|
|
1548
|
-
i++;
|
|
1549
|
-
continue;
|
|
1550
|
-
}
|
|
1551
|
-
if (ch === '"' && !inSingle) {
|
|
1552
|
-
inDouble = !inDouble;
|
|
1731
|
+
if (updateQuoteState(ch, quoteState)) {
|
|
1553
1732
|
i++;
|
|
1554
1733
|
continue;
|
|
1555
1734
|
}
|
|
1556
|
-
if (!inSingle && !inDouble) {
|
|
1735
|
+
if (!quoteState.inSingle && !quoteState.inDouble) {
|
|
1557
1736
|
if (ch === ">" || ch === "<") {
|
|
1558
1737
|
const isAppend = ch === ">" && command[i + 1] === ">";
|
|
1559
1738
|
const type = isAppend ? ">>" : ch;
|
|
1560
1739
|
const jump = isAppend ? 2 : 1;
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
fd = command[b] + fd;
|
|
1566
|
-
b--;
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
i += jump;
|
|
1570
|
-
while (i < command.length && /\s/.test(command[i])) i++;
|
|
1571
|
-
let target = "";
|
|
1572
|
-
let targetInSingle = false;
|
|
1573
|
-
let targetInDouble = false;
|
|
1574
|
-
while (i < command.length) {
|
|
1575
|
-
const tc = command[i];
|
|
1576
|
-
if (tc === "'" && !targetInDouble) {
|
|
1577
|
-
targetInSingle = !targetInSingle;
|
|
1578
|
-
i++;
|
|
1579
|
-
continue;
|
|
1580
|
-
}
|
|
1581
|
-
if (tc === '"' && !targetInSingle) {
|
|
1582
|
-
targetInDouble = !targetInDouble;
|
|
1583
|
-
i++;
|
|
1584
|
-
continue;
|
|
1585
|
-
}
|
|
1586
|
-
if (!targetInSingle && !targetInDouble && /[\s|;&<>]/.test(tc)) break;
|
|
1587
|
-
target += tc;
|
|
1588
|
-
i++;
|
|
1589
|
-
}
|
|
1740
|
+
const fd = ch === ">" ? extractFileDescriptor(command, i) : "";
|
|
1741
|
+
const targetStart = skipSpaces(command, i + jump);
|
|
1742
|
+
const { target, nextIndex } = readRedirectTarget(command, targetStart);
|
|
1743
|
+
i = nextIndex;
|
|
1590
1744
|
if (target) {
|
|
1591
1745
|
redirects.push({ type, fd, target });
|
|
1592
1746
|
}
|
|
@@ -1946,76 +2100,115 @@ function getTorBrowserArgs() {
|
|
|
1946
2100
|
}
|
|
1947
2101
|
|
|
1948
2102
|
// src/shared/utils/config/tor/wrapper.ts
|
|
2103
|
+
function wrapCurl(command, socks) {
|
|
2104
|
+
if (!/\bcurl\b/.test(command)) return null;
|
|
2105
|
+
if (/--socks5|--proxy\b|-x\s/.test(command)) return command;
|
|
2106
|
+
return command.replace(/\bcurl\b/, `curl --socks5-hostname ${socks}`);
|
|
2107
|
+
}
|
|
2108
|
+
function wrapWget(command, socks) {
|
|
2109
|
+
if (!/\bwget\b/.test(command)) return null;
|
|
2110
|
+
if (/--execute.*proxy|http_proxy|https_proxy/i.test(command)) return command;
|
|
2111
|
+
return command.replace(
|
|
2112
|
+
/\bwget\b/,
|
|
2113
|
+
`wget -e use_proxy=yes -e http_proxy=socks5h://${socks} -e https_proxy=socks5h://${socks}`
|
|
2114
|
+
);
|
|
2115
|
+
}
|
|
2116
|
+
function wrapSqlmap(command) {
|
|
2117
|
+
if (!/\bsqlmap\b/.test(command)) return null;
|
|
2118
|
+
if (/--tor\b|--proxy\b/.test(command)) return command;
|
|
2119
|
+
return command.replace(
|
|
2120
|
+
/\bsqlmap\b/,
|
|
2121
|
+
`sqlmap --tor --tor-type=SOCKS5 --tor-port=${TOR_PROXY.SOCKS_PORT}`
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
function wrapGobuster(command, socks) {
|
|
2125
|
+
if (!/\bgobuster\b/.test(command)) return null;
|
|
2126
|
+
if (/--proxy\b/.test(command)) return command;
|
|
2127
|
+
return command.replace(/\bgobuster\b/, `gobuster --proxy socks5://${socks}`);
|
|
2128
|
+
}
|
|
2129
|
+
function wrapFfuf(command, socks) {
|
|
2130
|
+
if (!/\bffuf\b/.test(command)) return null;
|
|
2131
|
+
if (/-x\s/.test(command)) return command;
|
|
2132
|
+
return command.replace(/\bffuf\b/, `ffuf -x socks5://${socks}`);
|
|
2133
|
+
}
|
|
2134
|
+
function wrapNmap(command, wrapperPrefix) {
|
|
2135
|
+
if (!/\bnmap\b/.test(command)) return null;
|
|
2136
|
+
let nmapCmd = command;
|
|
2137
|
+
nmapCmd = nmapCmd.replace(/\s-s[SAXFN]\b/g, " -sT");
|
|
2138
|
+
if (!/\s-Pn\b/.test(nmapCmd)) {
|
|
2139
|
+
nmapCmd = nmapCmd.replace(/\bnmap\b/, "nmap -Pn");
|
|
2140
|
+
}
|
|
2141
|
+
return `${wrapperPrefix} ${nmapCmd}`;
|
|
2142
|
+
}
|
|
1949
2143
|
function wrapCommandForTor(command) {
|
|
1950
2144
|
if (!isTorEnabled()) return command;
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
return command.replace(
|
|
1966
|
-
/\bsqlmap\b/,
|
|
1967
|
-
`sqlmap --tor --tor-type=SOCKS5 --tor-port=${TOR_PROXY.SOCKS_PORT}`
|
|
1968
|
-
);
|
|
1969
|
-
}
|
|
1970
|
-
if (/\bgobuster\b/.test(command)) {
|
|
1971
|
-
if (/--proxy\b/.test(command)) return command;
|
|
1972
|
-
return command.replace(/\bgobuster\b/, `gobuster --proxy socks5://${SOCKS}`);
|
|
1973
|
-
}
|
|
1974
|
-
if (/\bffuf\b/.test(command)) {
|
|
1975
|
-
if (/-x\s/.test(command)) return command;
|
|
1976
|
-
return command.replace(/\bffuf\b/, `ffuf -x socks5://${SOCKS}`);
|
|
1977
|
-
}
|
|
1978
|
-
if (/\bnmap\b/.test(command)) {
|
|
1979
|
-
let nmapCmd = command;
|
|
1980
|
-
nmapCmd = nmapCmd.replace(/\s-s[SAXFN]\b/g, " -sT");
|
|
1981
|
-
if (!/\s-Pn\b/.test(nmapCmd)) {
|
|
1982
|
-
nmapCmd = nmapCmd.replace(/\bnmap\b/, "nmap -Pn");
|
|
2145
|
+
const socks = `${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT}`;
|
|
2146
|
+
const wrapperPrefix = `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS}`;
|
|
2147
|
+
const handlers = [
|
|
2148
|
+
() => wrapCurl(command, socks),
|
|
2149
|
+
() => wrapWget(command, socks),
|
|
2150
|
+
() => wrapSqlmap(command),
|
|
2151
|
+
() => wrapGobuster(command, socks),
|
|
2152
|
+
() => wrapFfuf(command, socks),
|
|
2153
|
+
() => wrapNmap(command, wrapperPrefix)
|
|
2154
|
+
];
|
|
2155
|
+
for (const handler of handlers) {
|
|
2156
|
+
const wrapped = handler();
|
|
2157
|
+
if (wrapped !== null) {
|
|
2158
|
+
return wrapped;
|
|
1983
2159
|
}
|
|
1984
|
-
return `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} ${nmapCmd}`;
|
|
1985
2160
|
}
|
|
1986
|
-
return `${
|
|
2161
|
+
return `${wrapperPrefix} ${command}`;
|
|
1987
2162
|
}
|
|
1988
2163
|
|
|
1989
2164
|
// src/shared/utils/config/tor/leak-detector.ts
|
|
2165
|
+
function checkPingLeakRisk(command) {
|
|
2166
|
+
if (!/\bping\b/.test(command)) return null;
|
|
2167
|
+
return {
|
|
2168
|
+
safe: false,
|
|
2169
|
+
reason: "ping uses ICMP \u2014 bypasses Tor, real IP exposed to target",
|
|
2170
|
+
suggestion: "Use TCP probe instead: curl --socks5-hostname 127.0.0.1:9050 -s --connect-timeout 5 http://<target>:<port>"
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
function checkTracerouteLeakRisk(command) {
|
|
2174
|
+
if (!/\btraceroute\b|\btracepath\b|\bmtr\b/.test(command)) return null;
|
|
2175
|
+
return {
|
|
2176
|
+
safe: false,
|
|
2177
|
+
reason: "traceroute/tracepath/mtr uses ICMP/UDP \u2014 bypasses Tor, real IP exposed",
|
|
2178
|
+
suggestion: "Skip traceroute when Tor is enabled (reveals all hops including your IP)"
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
function checkDnsLeakRisk(command) {
|
|
2182
|
+
if (!/\bdig\b/.test(command) && !/\bnslookup\b/.test(command) && !/(?:^|[;&|\s])host(?:\s|$)/.test(command)) {
|
|
2183
|
+
return null;
|
|
2184
|
+
}
|
|
2185
|
+
return {
|
|
2186
|
+
safe: false,
|
|
2187
|
+
reason: "dig/host/nslookup use UDP \u2014 DNS query bypasses Tor, real IP visible to DNS server",
|
|
2188
|
+
suggestion: `Use DNS-over-HTTPS: curl --socks5-hostname ${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT} "https://dns.google/resolve?name=<host>&type=A"`
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
function checkNmapUdpLeakRisk(command) {
|
|
2192
|
+
if (!/\bnmap\b/.test(command) || !/\s-sU\b/.test(command)) return null;
|
|
2193
|
+
return {
|
|
2194
|
+
safe: false,
|
|
2195
|
+
reason: "nmap -sU (UDP scan) bypasses Tor \u2014 UDP is not routed through SOCKS5",
|
|
2196
|
+
suggestion: `Skip UDP scan with Tor ON. Use TCP scan: ${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} nmap -sT -Pn`
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
1990
2199
|
function checkTorLeakRisk(command) {
|
|
1991
2200
|
if (!isTorEnabled()) return { safe: true };
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
suggestion: "Skip traceroute when Tor is enabled (reveals all hops including your IP)"
|
|
2004
|
-
};
|
|
2005
|
-
}
|
|
2006
|
-
if (/\bdig\b/.test(command) || /\bnslookup\b/.test(command) || /(?:^|[;&|\s])host(?:\s|$)/.test(command)) {
|
|
2007
|
-
return {
|
|
2008
|
-
safe: false,
|
|
2009
|
-
reason: "dig/host/nslookup use UDP \u2014 DNS query bypasses Tor, real IP visible to DNS server",
|
|
2010
|
-
suggestion: `Use DNS-over-HTTPS: curl --socks5-hostname ${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT} "https://dns.google/resolve?name=<host>&type=A"`
|
|
2011
|
-
};
|
|
2012
|
-
}
|
|
2013
|
-
if (/\bnmap\b/.test(command) && /\s-sU\b/.test(command)) {
|
|
2014
|
-
return {
|
|
2015
|
-
safe: false,
|
|
2016
|
-
reason: "nmap -sU (UDP scan) bypasses Tor \u2014 UDP is not routed through SOCKS5",
|
|
2017
|
-
suggestion: `Skip UDP scan with Tor ON. Use TCP scan: ${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} nmap -sT -Pn`
|
|
2018
|
-
};
|
|
2201
|
+
const checks = [
|
|
2202
|
+
checkPingLeakRisk,
|
|
2203
|
+
checkTracerouteLeakRisk,
|
|
2204
|
+
checkDnsLeakRisk,
|
|
2205
|
+
checkNmapUdpLeakRisk
|
|
2206
|
+
];
|
|
2207
|
+
for (const check of checks) {
|
|
2208
|
+
const risk = check(command);
|
|
2209
|
+
if (risk) {
|
|
2210
|
+
return risk;
|
|
2211
|
+
}
|
|
2019
2212
|
}
|
|
2020
2213
|
return { safe: true };
|
|
2021
2214
|
}
|
|
@@ -2276,7 +2469,7 @@ Suggestion: ${torLeak.suggestion}`
|
|
|
2276
2469
|
}
|
|
2277
2470
|
|
|
2278
2471
|
// src/engine/tools-base/file-operations.ts
|
|
2279
|
-
import { readFileSync as
|
|
2472
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
2280
2473
|
import { dirname } from "path";
|
|
2281
2474
|
import { join as join2 } from "path";
|
|
2282
2475
|
import { tmpdir } from "os";
|
|
@@ -2296,14 +2489,14 @@ function getErrorMessage(error) {
|
|
|
2296
2489
|
// src/engine/tools-base/file-operations.ts
|
|
2297
2490
|
async function readFileContent(filePath) {
|
|
2298
2491
|
try {
|
|
2299
|
-
if (!
|
|
2492
|
+
if (!existsSync3(filePath)) {
|
|
2300
2493
|
return {
|
|
2301
2494
|
success: false,
|
|
2302
2495
|
output: "",
|
|
2303
2496
|
error: `File not found: ${filePath}`
|
|
2304
2497
|
};
|
|
2305
2498
|
}
|
|
2306
|
-
const content =
|
|
2499
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
2307
2500
|
return {
|
|
2308
2501
|
success: true,
|
|
2309
2502
|
output: content
|
|
@@ -2414,7 +2607,7 @@ function startBackgroundProcess(command, options = {}) {
|
|
|
2414
2607
|
}
|
|
2415
2608
|
|
|
2416
2609
|
// src/engine/process/process-interaction.ts
|
|
2417
|
-
import { existsSync as
|
|
2610
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, appendFileSync as appendFileSync2 } from "fs";
|
|
2418
2611
|
async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WAIT_MS_INTERACT) {
|
|
2419
2612
|
const proc = getProcess(processId);
|
|
2420
2613
|
if (!proc) return { success: false, output: `Process ${processId} not found`, newOutput: "" };
|
|
@@ -2422,8 +2615,8 @@ async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WA
|
|
|
2422
2615
|
if (proc.hasExited) return { success: false, output: `Process ${processId} has exited`, newOutput: "" };
|
|
2423
2616
|
let currentLen = 0;
|
|
2424
2617
|
try {
|
|
2425
|
-
if (
|
|
2426
|
-
currentLen =
|
|
2618
|
+
if (existsSync4(proc.stdoutFile)) {
|
|
2619
|
+
currentLen = readFileSync4(proc.stdoutFile, "utf-8").length;
|
|
2427
2620
|
}
|
|
2428
2621
|
} catch {
|
|
2429
2622
|
}
|
|
@@ -2436,8 +2629,8 @@ async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WA
|
|
|
2436
2629
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
2437
2630
|
let fullStdout = "";
|
|
2438
2631
|
try {
|
|
2439
|
-
if (
|
|
2440
|
-
fullStdout =
|
|
2632
|
+
if (existsSync4(proc.stdoutFile)) {
|
|
2633
|
+
fullStdout = readFileSync4(proc.stdoutFile, "utf-8");
|
|
2441
2634
|
}
|
|
2442
2635
|
} catch {
|
|
2443
2636
|
}
|
|
@@ -2459,7 +2652,7 @@ function promoteToShell(processId, description) {
|
|
|
2459
2652
|
}
|
|
2460
2653
|
|
|
2461
2654
|
// src/engine/process/process-monitor.ts
|
|
2462
|
-
import { existsSync as
|
|
2655
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
2463
2656
|
function isProcessRunning(processId) {
|
|
2464
2657
|
const proc = getProcess(processId);
|
|
2465
2658
|
if (!proc) return false;
|
|
@@ -2472,8 +2665,8 @@ function isProcessRunning(processId) {
|
|
|
2472
2665
|
proc.childPids = discoverAllDescendants(proc.pid);
|
|
2473
2666
|
if (proc.role === PROCESS_ROLES.LISTENER) {
|
|
2474
2667
|
try {
|
|
2475
|
-
if (
|
|
2476
|
-
const stdout =
|
|
2668
|
+
if (existsSync5(proc.stdoutFile)) {
|
|
2669
|
+
const stdout = readFileSync5(proc.stdoutFile, "utf-8");
|
|
2477
2670
|
if (detectConnection(stdout)) {
|
|
2478
2671
|
promoteToShell(processId, "Reverse shell connected (auto-detected)");
|
|
2479
2672
|
logEvent(processId, PROCESS_EVENTS.CONNECTION_DETECTED, `Connection detected on port ${proc.listeningPort}`);
|
|
@@ -2491,16 +2684,16 @@ function getProcessOutput(processId) {
|
|
|
2491
2684
|
let stdout = "";
|
|
2492
2685
|
let stderr = "";
|
|
2493
2686
|
try {
|
|
2494
|
-
if (
|
|
2495
|
-
const content =
|
|
2687
|
+
if (existsSync5(proc.stdoutFile)) {
|
|
2688
|
+
const content = readFileSync5(proc.stdoutFile, "utf-8");
|
|
2496
2689
|
stdout = content.length > SYSTEM_LIMITS.MAX_STDOUT_SLICE ? `... [truncated ${content.length - SYSTEM_LIMITS.MAX_STDOUT_SLICE} chars] ...
|
|
2497
2690
|
` + content.slice(-SYSTEM_LIMITS.MAX_STDOUT_SLICE) : content;
|
|
2498
2691
|
}
|
|
2499
2692
|
} catch {
|
|
2500
2693
|
}
|
|
2501
2694
|
try {
|
|
2502
|
-
if (
|
|
2503
|
-
const content =
|
|
2695
|
+
if (existsSync5(proc.stderrFile)) {
|
|
2696
|
+
const content = readFileSync5(proc.stderrFile, "utf-8");
|
|
2504
2697
|
stderr = content.length > SYSTEM_LIMITS.MAX_STDERR_SLICE ? `... [truncated ${content.length - SYSTEM_LIMITS.MAX_STDERR_SLICE} chars] ...
|
|
2505
2698
|
` + content.slice(-SYSTEM_LIMITS.MAX_STDERR_SLICE) : content;
|
|
2506
2699
|
}
|
|
@@ -2606,7 +2799,7 @@ async function cleanupAllProcesses() {
|
|
|
2606
2799
|
cleanupDone = false;
|
|
2607
2800
|
return;
|
|
2608
2801
|
}
|
|
2609
|
-
const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-
|
|
2802
|
+
const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-BI7BKPHN.js");
|
|
2610
2803
|
const backgroundProcesses = getBackgroundProcessesMap2();
|
|
2611
2804
|
terminateAllNatively(backgroundProcesses, "SIGTERM");
|
|
2612
2805
|
await new Promise((r) => setTimeout(r, SYSTEM_LIMITS.CLEANUP_BATCH_WAIT_MS));
|
|
@@ -2673,44 +2866,36 @@ function cleanupOrphanProcesses() {
|
|
|
2673
2866
|
}
|
|
2674
2867
|
|
|
2675
2868
|
// src/engine/process/resource-summary.ts
|
|
2676
|
-
import { existsSync as
|
|
2677
|
-
function
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
const
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
const roleIcon = PROCESS_ICONS[p.role] || PROCESS_ICONS[PROCESS_ROLES.BACKGROUND];
|
|
2693
|
-
let lastOutput = "";
|
|
2694
|
-
try {
|
|
2695
|
-
if (p.stdoutFile && existsSync5(p.stdoutFile)) {
|
|
2696
|
-
const content = readFileSync5(p.stdoutFile, "utf-8");
|
|
2697
|
-
const outputLines = content.trim().split("\n");
|
|
2698
|
-
lastOutput = outputLines.slice(-SYSTEM_LIMITS.RECENT_OUTPUT_LINES).join(" | ").replace(/\n/g, " ");
|
|
2699
|
-
}
|
|
2700
|
-
} catch {
|
|
2869
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
2870
|
+
function formatActiveProcesses(running, lines) {
|
|
2871
|
+
if (running.length === 0) return;
|
|
2872
|
+
lines.push(`Active Assets (${running.length}):`);
|
|
2873
|
+
for (const p of running) {
|
|
2874
|
+
const dur = Math.round(p.durationMs / 1e3);
|
|
2875
|
+
const portInfo = p.listeningPort ? ` port:${p.listeningPort}` : "";
|
|
2876
|
+
const childInfo = p.childPids.length > 0 ? ` children:${p.childPids.join(",")}` : "";
|
|
2877
|
+
const interactiveFlag = p.isInteractive ? ` ${STATUS_MARKERS.INTERACTIVE}` : "";
|
|
2878
|
+
const roleIcon = PROCESS_ICONS[p.role] || PROCESS_ICONS[PROCESS_ROLES.BACKGROUND];
|
|
2879
|
+
let lastOutput = "";
|
|
2880
|
+
try {
|
|
2881
|
+
if (p.stdoutFile && existsSync6(p.stdoutFile)) {
|
|
2882
|
+
const content = readFileSync6(p.stdoutFile, "utf-8");
|
|
2883
|
+
const outputLines = content.trim().split("\n");
|
|
2884
|
+
lastOutput = outputLines.slice(-SYSTEM_LIMITS.RECENT_OUTPUT_LINES).join(" | ").replace(/\n/g, " ");
|
|
2701
2885
|
}
|
|
2702
|
-
|
|
2703
|
-
> Log: ${lastOutput}` : "";
|
|
2704
|
-
lines.push(` ${roleIcon} [${p.id}] PID:${p.pid}${portInfo}${childInfo}${interactiveFlag} [${p.role}] ${p.purpose} (${dur}s)${outputSnippet}`);
|
|
2886
|
+
} catch {
|
|
2705
2887
|
}
|
|
2888
|
+
const outputSnippet = lastOutput ? `
|
|
2889
|
+
> Log: ${lastOutput}` : "";
|
|
2890
|
+
lines.push(` ${roleIcon} [${p.id}] PID:${p.pid}${portInfo}${childInfo}${interactiveFlag} [${p.role}] ${p.purpose} (${dur}s)${outputSnippet}`);
|
|
2706
2891
|
}
|
|
2892
|
+
}
|
|
2893
|
+
function formatZombies(exited, lines) {
|
|
2707
2894
|
const orphans = [];
|
|
2708
2895
|
for (const p of exited) {
|
|
2709
2896
|
if (p.childPids.length > 0) {
|
|
2710
2897
|
const aliveChildren = p.childPids.filter((pid) => isPidAlive(pid));
|
|
2711
|
-
if (aliveChildren.length > 0) {
|
|
2712
|
-
orphans.push({ id: p.id, childPids: aliveChildren });
|
|
2713
|
-
}
|
|
2898
|
+
if (aliveChildren.length > 0) orphans.push({ id: p.id, childPids: aliveChildren });
|
|
2714
2899
|
}
|
|
2715
2900
|
}
|
|
2716
2901
|
if (orphans.length > 0) {
|
|
@@ -2721,10 +2906,8 @@ ${STATUS_MARKERS.WARNING} SUSPECTED ZOMBIES (Orphaned Children):`);
|
|
|
2721
2906
|
}
|
|
2722
2907
|
lines.push(` \u2192 Recommendation: Run bg_process({ action: "stop", process_id: "ID" }) to clean up.`);
|
|
2723
2908
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
Ports In Use: ${ports.join(", ")}`);
|
|
2727
|
-
}
|
|
2909
|
+
}
|
|
2910
|
+
function formatActiveShells(running, lines) {
|
|
2728
2911
|
const activeShells = running.filter((p) => p.role === PROCESS_ROLES.ACTIVE_SHELL);
|
|
2729
2912
|
if (activeShells.length > 0) {
|
|
2730
2913
|
lines.push(`
|
|
@@ -2733,19 +2916,39 @@ Ports In Use: ${ports.join(", ")}`);
|
|
|
2733
2916
|
lines.push(` \u2192 [${s.id}] Use interact to execute commands. Upgrade if unstable.`);
|
|
2734
2917
|
}
|
|
2735
2918
|
}
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2919
|
+
}
|
|
2920
|
+
function formatExitedProcesses(exited, lines) {
|
|
2921
|
+
if (exited.length === 0) return;
|
|
2922
|
+
lines.push(`Exited Processes (${exited.length}):`);
|
|
2923
|
+
for (const p of exited) {
|
|
2924
|
+
lines.push(` ${STATUS_MARKERS.EXITED} [${p.id}] exit:${p.exitCode} [${p.role}] ${p.description}`);
|
|
2741
2925
|
}
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2926
|
+
}
|
|
2927
|
+
function formatRecentEvents(recentEvents, lines) {
|
|
2928
|
+
if (recentEvents.length === 0) return;
|
|
2929
|
+
lines.push(`Recent Process Events:`);
|
|
2930
|
+
for (const e of recentEvents.slice(-SYSTEM_LIMITS.RECENT_EVENTS_DISPLAY)) {
|
|
2931
|
+
const ago = Math.round((Date.now() - e.timestamp) / 1e3);
|
|
2932
|
+
lines.push(` ${ago}s ago: [${e.event}] ${e.detail}`);
|
|
2748
2933
|
}
|
|
2934
|
+
}
|
|
2935
|
+
function getResourceSummary() {
|
|
2936
|
+
const procs = listBackgroundProcesses();
|
|
2937
|
+
const running = procs.filter((p) => p.isRunning);
|
|
2938
|
+
const exited = procs.filter((p) => !p.isRunning);
|
|
2939
|
+
const ports = getUsedPorts();
|
|
2940
|
+
const recentEvents = getProcessEventLog().slice(-SYSTEM_LIMITS.RECENT_EVENTS_IN_SUMMARY);
|
|
2941
|
+
if (running.length === 0 && exited.length === 0 && recentEvents.length === 0) return "";
|
|
2942
|
+
const lines = [];
|
|
2943
|
+
formatActiveProcesses(running, lines);
|
|
2944
|
+
formatZombies(exited, lines);
|
|
2945
|
+
if (ports.length > 0) {
|
|
2946
|
+
lines.push(`
|
|
2947
|
+
Ports In Use: ${ports.join(", ")}`);
|
|
2948
|
+
}
|
|
2949
|
+
formatActiveShells(running, lines);
|
|
2950
|
+
formatExitedProcesses(exited, lines);
|
|
2951
|
+
formatRecentEvents(recentEvents, lines);
|
|
2749
2952
|
return lines.join("\n");
|
|
2750
2953
|
}
|
|
2751
2954
|
|
|
@@ -3150,7 +3353,7 @@ var EpisodicMemory = class {
|
|
|
3150
3353
|
};
|
|
3151
3354
|
|
|
3152
3355
|
// src/shared/utils/agent-memory/persistent-memory.ts
|
|
3153
|
-
import { existsSync as
|
|
3356
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
3154
3357
|
import { join as join4 } from "path";
|
|
3155
3358
|
|
|
3156
3359
|
// src/shared/utils/agent-memory/similarity.ts
|
|
@@ -3179,7 +3382,7 @@ function matchesServiceProfile(serviceProfile, services) {
|
|
|
3179
3382
|
}
|
|
3180
3383
|
|
|
3181
3384
|
// src/shared/utils/agent-memory/session-snapshot.ts
|
|
3182
|
-
import { existsSync as
|
|
3385
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
3183
3386
|
import { join as join3 } from "path";
|
|
3184
3387
|
var SNAPSHOT_FILE = join3(WORKSPACE.MEMORY, SPECIAL_FILES.SESSION_SNAPSHOT);
|
|
3185
3388
|
function saveSessionSnapshot(snapshot) {
|
|
@@ -3191,8 +3394,8 @@ function saveSessionSnapshot(snapshot) {
|
|
|
3191
3394
|
}
|
|
3192
3395
|
function loadSessionSnapshot() {
|
|
3193
3396
|
try {
|
|
3194
|
-
if (
|
|
3195
|
-
return JSON.parse(
|
|
3397
|
+
if (existsSync7(SNAPSHOT_FILE)) {
|
|
3398
|
+
return JSON.parse(readFileSync7(SNAPSHOT_FILE, "utf-8"));
|
|
3196
3399
|
}
|
|
3197
3400
|
} catch {
|
|
3198
3401
|
}
|
|
@@ -3223,7 +3426,7 @@ function snapshotToPrompt() {
|
|
|
3223
3426
|
}
|
|
3224
3427
|
function clearSessionSnapshot() {
|
|
3225
3428
|
try {
|
|
3226
|
-
if (
|
|
3429
|
+
if (existsSync7(SNAPSHOT_FILE)) {
|
|
3227
3430
|
unlinkSync4(SNAPSHOT_FILE);
|
|
3228
3431
|
}
|
|
3229
3432
|
} catch {
|
|
@@ -3373,8 +3576,8 @@ var PersistentMemory = class {
|
|
|
3373
3576
|
}
|
|
3374
3577
|
load() {
|
|
3375
3578
|
try {
|
|
3376
|
-
if (
|
|
3377
|
-
const data = JSON.parse(
|
|
3579
|
+
if (existsSync8(MEMORY_FILE)) {
|
|
3580
|
+
const data = JSON.parse(readFileSync8(MEMORY_FILE, "utf-8"));
|
|
3378
3581
|
return {
|
|
3379
3582
|
...data,
|
|
3380
3583
|
exploitChains: data.exploitChains ?? []
|
|
@@ -3604,6 +3807,7 @@ var StateSerializer = class {
|
|
|
3604
3807
|
const lines = [];
|
|
3605
3808
|
this.formatContextAndMission(state3, lines);
|
|
3606
3809
|
this.formatArtifactsAndObjectives(state3, lines);
|
|
3810
|
+
this.formatDelegatedTasks(state3, lines);
|
|
3607
3811
|
this.formatTargets(state3, lines);
|
|
3608
3812
|
this.formatFindings(state3, lines);
|
|
3609
3813
|
this.formatLoot(state3, lines);
|
|
@@ -3625,6 +3829,34 @@ var StateSerializer = class {
|
|
|
3625
3829
|
}
|
|
3626
3830
|
}
|
|
3627
3831
|
}
|
|
3832
|
+
static formatDelegatedTasks(state3, lines) {
|
|
3833
|
+
const activeTasks = state3.getActiveDelegatedTasks();
|
|
3834
|
+
if (activeTasks.length === 0) return;
|
|
3835
|
+
lines.push(`Delegated Tasks (${activeTasks.length} active):`);
|
|
3836
|
+
for (const task of activeTasks.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
|
|
3837
|
+
const waiting = task.waitingOn ? ` waiting:${task.waitingOn}` : "";
|
|
3838
|
+
const workerType = task.nextWorkerType || task.workerType;
|
|
3839
|
+
const worker = workerType ? ` worker:${workerType}` : "";
|
|
3840
|
+
lines.push(` [${task.status}] ${task.task}${worker}${waiting}`);
|
|
3841
|
+
lines.push(` summary: ${task.summary}`);
|
|
3842
|
+
if (task.parentTaskId || task.rootTaskId) {
|
|
3843
|
+
const chainParts = [
|
|
3844
|
+
task.parentTaskId ? `parent:${task.parentTaskId}` : "",
|
|
3845
|
+
task.rootTaskId ? `root:${task.rootTaskId}` : ""
|
|
3846
|
+
].filter(Boolean);
|
|
3847
|
+
lines.push(` chain: ${chainParts.join(" ")}`);
|
|
3848
|
+
}
|
|
3849
|
+
if (task.assets.length > 0) {
|
|
3850
|
+
lines.push(` assets: ${task.assets.join(", ")}`);
|
|
3851
|
+
}
|
|
3852
|
+
if (task.sessions.length > 0) {
|
|
3853
|
+
lines.push(` sessions: ${task.sessions.join(", ")}`);
|
|
3854
|
+
}
|
|
3855
|
+
if (task.resumeHint) {
|
|
3856
|
+
lines.push(` resume: ${task.resumeHint}`);
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3628
3860
|
static formatContextAndMission(state3, lines) {
|
|
3629
3861
|
const engagement = state3.getEngagement();
|
|
3630
3862
|
const scope = state3.getScope();
|
|
@@ -3761,7 +3993,8 @@ function saveState(state3) {
|
|
|
3761
3993
|
actionLog: state3.getRecentActions(AGENT_LIMITS.MAX_ACTION_LOG),
|
|
3762
3994
|
currentPhase: state3.getPhase(),
|
|
3763
3995
|
missionSummary: state3.getMissionSummary(),
|
|
3764
|
-
missionChecklist: state3.getMissionChecklist()
|
|
3996
|
+
missionChecklist: state3.getMissionChecklist(),
|
|
3997
|
+
delegatedTasks: state3.getDelegatedTasks()
|
|
3765
3998
|
};
|
|
3766
3999
|
const sessionFile = join5(sessionsDir, FILE_PATTERNS.session());
|
|
3767
4000
|
const json = JSON.stringify(snapshot, null, 2);
|
|
@@ -3790,15 +4023,15 @@ function pruneOldSessions(sessionsDir) {
|
|
|
3790
4023
|
}
|
|
3791
4024
|
|
|
3792
4025
|
// src/engine/state/persistence/loader.ts
|
|
3793
|
-
import { readFileSync as
|
|
4026
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
3794
4027
|
import { join as join6 } from "path";
|
|
3795
4028
|
function loadState(state3) {
|
|
3796
4029
|
const latestFile = join6(WORKSPACE.SESSIONS, "latest.json");
|
|
3797
|
-
if (!
|
|
4030
|
+
if (!existsSync9(latestFile)) {
|
|
3798
4031
|
return false;
|
|
3799
4032
|
}
|
|
3800
4033
|
try {
|
|
3801
|
-
const raw =
|
|
4034
|
+
const raw = readFileSync9(latestFile, "utf-8");
|
|
3802
4035
|
const snapshot = JSON.parse(raw);
|
|
3803
4036
|
if (snapshot.version !== 1) {
|
|
3804
4037
|
debugLog("general", `Unknown snapshot version: ${snapshot.version}`);
|
|
@@ -3832,6 +4065,11 @@ function loadState(state3) {
|
|
|
3832
4065
|
if (snapshot.missionChecklist?.length > 0) {
|
|
3833
4066
|
state3.restoreMissionChecklist(snapshot.missionChecklist);
|
|
3834
4067
|
}
|
|
4068
|
+
if (Array.isArray(snapshot.delegatedTasks)) {
|
|
4069
|
+
for (const task of snapshot.delegatedTasks) {
|
|
4070
|
+
state3.restoreDelegatedTask(task);
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
3835
4073
|
return true;
|
|
3836
4074
|
} catch (err) {
|
|
3837
4075
|
debugLog("general", `Failed to load state: ${err}`);
|
|
@@ -3840,8 +4078,8 @@ function loadState(state3) {
|
|
|
3840
4078
|
}
|
|
3841
4079
|
|
|
3842
4080
|
// src/engine/state/persistence/janitor.ts
|
|
3843
|
-
import { existsSync as
|
|
3844
|
-
import
|
|
4081
|
+
import { existsSync as existsSync10, rmSync } from "fs";
|
|
4082
|
+
import path3 from "path";
|
|
3845
4083
|
function clearWorkspace() {
|
|
3846
4084
|
const cleared = [];
|
|
3847
4085
|
const errors = [];
|
|
@@ -3852,13 +4090,13 @@ function clearWorkspace() {
|
|
|
3852
4090
|
{ path: WORKSPACE.TMP, label: "workspace/temp" },
|
|
3853
4091
|
{ path: WORKSPACE.LOOT, label: "loot" },
|
|
3854
4092
|
{ path: WORKSPACE.REPORTS, label: "reports" },
|
|
3855
|
-
{ path:
|
|
3856
|
-
{ path:
|
|
3857
|
-
{ path:
|
|
4093
|
+
{ path: path3.join(root, "archive"), label: "archive (historical cleanup)" },
|
|
4094
|
+
{ path: path3.join(root, "outputs"), label: "outputs (historical cleanup)" },
|
|
4095
|
+
{ path: path3.join(root, "journal"), label: "journal (historical cleanup)" }
|
|
3858
4096
|
];
|
|
3859
4097
|
for (const dir2 of dirsToClean) {
|
|
3860
4098
|
try {
|
|
3861
|
-
if (
|
|
4099
|
+
if (existsSync10(dir2.path)) {
|
|
3862
4100
|
rmSync(dir2.path, { recursive: true, force: true });
|
|
3863
4101
|
ensureDirExists(dir2.path);
|
|
3864
4102
|
cleared.push(dir2.label);
|
|
@@ -3872,6 +4110,7 @@ function clearWorkspace() {
|
|
|
3872
4110
|
|
|
3873
4111
|
export {
|
|
3874
4112
|
ensureDirExists,
|
|
4113
|
+
getWorkspaceConfig,
|
|
3875
4114
|
FILE_EXTENSIONS,
|
|
3876
4115
|
FILE_PATTERNS,
|
|
3877
4116
|
WORK_DIR,
|
|
@@ -3883,6 +4122,11 @@ export {
|
|
|
3883
4122
|
getTorBrowserArgs,
|
|
3884
4123
|
MS_PER_MINUTE,
|
|
3885
4124
|
DISPLAY_LIMITS,
|
|
4125
|
+
resolveRuntimeAssetPath,
|
|
4126
|
+
readRuntimeAssetFile,
|
|
4127
|
+
llmNodeSystemPrompt,
|
|
4128
|
+
llmNodeOutputParsing,
|
|
4129
|
+
llmNodeCooldownPolicy,
|
|
3886
4130
|
RETRY_CONFIG,
|
|
3887
4131
|
LLM_LIMITS,
|
|
3888
4132
|
LLM_ERROR_TYPES,
|
|
@@ -3922,6 +4166,7 @@ export {
|
|
|
3922
4166
|
COMMAND_EVENT_TYPES,
|
|
3923
4167
|
UI_COMMANDS,
|
|
3924
4168
|
generateId,
|
|
4169
|
+
generatePrefixedId,
|
|
3925
4170
|
WorkingMemory,
|
|
3926
4171
|
EpisodicMemory,
|
|
3927
4172
|
saveSessionSnapshot,
|