pentesting 0.72.9 → 0.72.10

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 CHANGED
@@ -32,6 +32,15 @@
32
32
 
33
33
  Pentesting support tool. Can autonomously execute network penetration tests or assist with generic Capture The Flag (CTF) challenges (such as Reverse Engineering, Cryptography, and binary analysis) without requiring a specific network target.
34
34
 
35
+ ## Architecture Notes
36
+
37
+ - User input is preprocessed by a dedicated input processor LLM before the main loop acts on it.
38
+ - Durable engagement guidance, sensitive data handling rules, and reusable operator constraints are merged into `.pentesting/memory/policy.md`.
39
+ - Both the strategist and the main prompt builder read that policy document every turn.
40
+ - Each completed turn is compressed into `.pentesting/turns/{N}-memory.md`, with provenance metadata describing who wrote it and what sources were used.
41
+ - Automatically maintained LLM documents are intentionally small in number: bounded turn memories, one `policy.md`, one merged `persistent-knowledge.json`, and on-demand reports only.
42
+ - Interactive prompts are brokered through a single active input slot in the TUI. Additional prompts wait in a hidden queue and are promoted one at a time.
43
+
35
44
  ---
36
45
 
37
46
  ## Quick Start
@@ -126,13 +126,6 @@ function llmNodeSystemPrompt(nodeName) {
126
126
  `[pipeline.yaml] llm_nodes.${nodeName} must declare system_prompt_file, fallback_system_prompt_file, or system_prompt. Add one to pipeline.yaml.`
127
127
  );
128
128
  }
129
- function getUserInputQueueConfig() {
130
- return getPipelineConfig().user_input_queue ?? {
131
- format: "tagged",
132
- tag: "user-input",
133
- inject_position: "messages_end"
134
- };
135
- }
136
129
  function llmNodeOutputParsing(nodeName) {
137
130
  return getPipelineConfig().llm_nodes?.[nodeName]?.output_parsing;
138
131
  }
@@ -304,7 +297,6 @@ export {
304
297
  PROCESS_LIMITS,
305
298
  getPipelineConfig,
306
299
  llmNodeSystemPrompt,
307
- getUserInputQueueConfig,
308
300
  llmNodeOutputParsing,
309
301
  llmNodeCooldownPolicy,
310
302
  getPromptSources,
@@ -18,7 +18,7 @@ import {
18
18
  getProcessEventLog,
19
19
  logEvent,
20
20
  setProcess
21
- } from "./chunk-74KL4OOU.js";
21
+ } from "./chunk-GHJPYI4S.js";
22
22
 
23
23
  // src/shared/constants/time/conversions.ts
24
24
  var MS_PER_MINUTE = 6e4;
@@ -235,7 +235,7 @@ var INPUT_PROMPT_PATTERNS = [
235
235
 
236
236
  // src/shared/constants/agent.ts
237
237
  var APP_NAME = "Pentest AI";
238
- var APP_VERSION = "0.72.9";
238
+ var APP_VERSION = "0.72.10";
239
239
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
240
240
  var LLM_ROLES = {
241
241
  SYSTEM: "system",
@@ -682,6 +682,7 @@ var EVENT_TYPES = {
682
682
  NOTIFICATION: "notification",
683
683
  AUXILIARY_WORK_START: "auxiliary_work_start",
684
684
  AUXILIARY_WORK_END: "auxiliary_work_end",
685
+ QUEUE_UPDATED: "queue_updated",
685
686
  QUEUE_DRAINED: "queue_drained"
686
687
  };
687
688
  var COMMAND_EVENT_TYPES = {
@@ -869,30 +870,142 @@ function detectConnection(stdout) {
869
870
  return DETECTION_PATTERNS.CONNECTION.some((p) => p.test(stdout));
870
871
  }
871
872
 
872
- // src/engine/tools-base/event-emitter.ts
873
- var globalEventEmitter = null;
874
- var globalInputHandler = null;
875
- function setCommandEventEmitter(emitter) {
876
- globalEventEmitter = emitter;
873
+ // src/engine/session-runtime.ts
874
+ var SessionRuntime = class {
875
+ commandEventEmitter = null;
876
+ inputHandler = null;
877
+ credentialHandler = null;
878
+ setCommandEventEmitter(emitter) {
879
+ this.commandEventEmitter = emitter;
880
+ }
881
+ clearCommandEventEmitter() {
882
+ this.commandEventEmitter = null;
883
+ }
884
+ getCommandEventEmitter() {
885
+ return this.commandEventEmitter;
886
+ }
887
+ setInputHandler(handler) {
888
+ this.inputHandler = handler;
889
+ }
890
+ clearInputHandler() {
891
+ this.inputHandler = null;
892
+ }
893
+ getInputHandler() {
894
+ return this.inputHandler;
895
+ }
896
+ setCredentialHandler(handler) {
897
+ this.credentialHandler = handler;
898
+ }
899
+ clearCredentialHandler() {
900
+ this.credentialHandler = null;
901
+ }
902
+ getCredentialHandler() {
903
+ return this.credentialHandler;
904
+ }
905
+ clearAll() {
906
+ this.clearCommandEventEmitter();
907
+ this.clearInputHandler();
908
+ this.clearCredentialHandler();
909
+ }
910
+ };
911
+ var activeSessionRuntime = null;
912
+ function createSessionRuntime() {
913
+ return new SessionRuntime();
877
914
  }
878
- function setInputHandler(handler) {
879
- globalInputHandler = handler;
915
+ function setActiveSessionRuntime(runtime) {
916
+ activeSessionRuntime = runtime;
880
917
  }
881
- function clearInputHandler() {
882
- globalInputHandler = null;
918
+ function clearActiveSessionRuntime(runtime) {
919
+ if (!runtime || activeSessionRuntime === runtime) {
920
+ activeSessionRuntime = null;
921
+ }
883
922
  }
884
- function clearCommandEventEmitter() {
885
- globalEventEmitter = null;
923
+ function getActiveSessionRuntime() {
924
+ return activeSessionRuntime;
886
925
  }
926
+
927
+ // src/engine/tools-base/event-emitter.ts
887
928
  function getEventEmitter() {
888
- return globalEventEmitter;
929
+ return getActiveSessionRuntime()?.getCommandEventEmitter() || null;
889
930
  }
890
931
  function getInputHandler() {
891
- return globalInputHandler;
932
+ return getActiveSessionRuntime()?.getInputHandler() || null;
892
933
  }
893
934
 
894
935
  // src/shared/constants/paths.ts
895
936
  import path from "path";
937
+
938
+ // src/shared/constants/files/extensions.ts
939
+ var FILE_EXTENSIONS = {
940
+ // Data formats
941
+ JSON: ".json",
942
+ MARKDOWN: ".md",
943
+ TXT: ".txt",
944
+ XML: ".xml",
945
+ // Network capture
946
+ PCAP: ".pcap",
947
+ HOSTS: ".hosts",
948
+ MITM: ".mitm",
949
+ // Process I/O
950
+ STDOUT: ".stdout",
951
+ STDERR: ".stderr",
952
+ STDIN: ".stdin",
953
+ // Scripts
954
+ SH: ".sh",
955
+ PY: ".py",
956
+ CJS: ".cjs",
957
+ // Config
958
+ ENV: ".env",
959
+ BAK: ".bak"
960
+ };
961
+
962
+ // src/shared/constants/files/special.ts
963
+ var SPECIAL_FILES = {
964
+ /** Latest state snapshot */
965
+ LATEST_STATE: "latest.json",
966
+ /** README */
967
+ README: "README.md",
968
+ /** Persistent knowledge store */
969
+ PERSISTENT_KNOWLEDGE: "persistent-knowledge.json",
970
+ /** Persistent policy memory document */
971
+ POLICY: "policy.md",
972
+ /** Debug log file */
973
+ DEBUG_LOG: "debug.log",
974
+ /**
975
+ * Session snapshot for Insane-level long session resumption (3차).
976
+ * Stores current phase, achieved foothold, next steps, credentials.
977
+ * Written by PlaybookSynthesizer on flag capture / manual update_mission.
978
+ */
979
+ SESSION_SNAPSHOT: "session-snapshot.json"
980
+ };
981
+
982
+ // src/shared/utils/file-ops/file-utils.ts
983
+ import { existsSync, mkdirSync } from "fs";
984
+ function ensureDirExists(dirPath) {
985
+ if (!existsSync(dirPath)) {
986
+ mkdirSync(dirPath, { recursive: true });
987
+ }
988
+ }
989
+ function fileTimestamp() {
990
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
991
+ }
992
+ function sanitizeFilename(name, maxLength = 30) {
993
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, maxLength);
994
+ }
995
+
996
+ // src/shared/constants/files/patterns.ts
997
+ var FILE_PATTERNS = {
998
+ /** Tool output filename: nmap.txt, gobuster.txt (sanitized) */
999
+ toolOutput(toolName) {
1000
+ return `${sanitizeFilename(toolName)}.txt`;
1001
+ },
1002
+ /** Generate session snapshot filename: 2026-02-21T08-30-15.json */
1003
+ session() {
1004
+ return `${fileTimestamp()}.json`;
1005
+ }
1006
+ };
1007
+
1008
+ // src/shared/constants/paths.ts
896
1009
  var _dirs = null;
897
1010
  var _root = null;
898
1011
  function getRoot() {
@@ -925,6 +1038,9 @@ var WORKSPACE = {
925
1038
  get MEMORY() {
926
1039
  return path.resolve(dir("memory", `${getRoot()}/memory`));
927
1040
  },
1041
+ get POLICY() {
1042
+ return path.resolve(dir("memory", `${getRoot()}/memory`), SPECIAL_FILES.POLICY);
1043
+ },
928
1044
  get REPORTS() {
929
1045
  return path.resolve(dir("reports", `${getRoot()}/reports`));
930
1046
  },
@@ -937,28 +1053,16 @@ var WORKSPACE = {
937
1053
  get DEBUG() {
938
1054
  return path.resolve(dir("debug", `${getRoot()}/debug`));
939
1055
  },
940
- /** DEPRECATED — kept for clearWorkspace cleanup only */
941
- get OUTPUTS() {
942
- return path.resolve(`${getRoot()}/outputs`);
943
- },
944
- /** DEPRECATED — kept for clearWorkspace cleanup only */
945
- get JOURNAL() {
946
- return path.resolve(`${getRoot()}/journal`);
947
- },
948
- /** DEPRECATED — kept for clearWorkspace cleanup only */
949
- get ARCHIVE() {
950
- return path.resolve(dir("archive", `${getRoot()}/archive`));
951
- },
952
1056
  /** Turn insight files root: .pentesting/turns/ */
953
1057
  get TURNS() {
954
1058
  return path.resolve(dir("turns", `${getRoot()}/turns`));
955
1059
  },
956
1060
  /**
957
- * Resolve the insight file for a specific turn: .pentesting/turns/{N}.md
1061
+ * Resolve the turn memory file for a specific turn: .pentesting/turns/{N}-memory.md
958
1062
  * Pipeline.yaml: workspace.turn_structure → single file per turn
959
1063
  */
960
1064
  turnPath(turn) {
961
- return path.resolve(dir("turns", `${getRoot()}/turns`), `${turn}.md`);
1065
+ return path.resolve(dir("turns", `${getRoot()}/turns`), `${turn}-memory.md`);
962
1066
  }
963
1067
  };
964
1068
 
@@ -1193,78 +1297,6 @@ var BLOCKED_BINARIES = /* @__PURE__ */ new Set([
1193
1297
  // src/shared/utils/debug/debug-logger.ts
1194
1298
  import { appendFileSync, writeFileSync, statSync, readFileSync } from "fs";
1195
1299
  import { join } from "path";
1196
-
1197
- // src/shared/utils/file-ops/file-utils.ts
1198
- import { existsSync, mkdirSync } from "fs";
1199
- function ensureDirExists(dirPath) {
1200
- if (!existsSync(dirPath)) {
1201
- mkdirSync(dirPath, { recursive: true });
1202
- }
1203
- }
1204
- function fileTimestamp() {
1205
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
1206
- }
1207
- function sanitizeFilename(name, maxLength = 30) {
1208
- return name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, maxLength);
1209
- }
1210
-
1211
- // src/shared/constants/files/extensions.ts
1212
- var FILE_EXTENSIONS = {
1213
- // Data formats
1214
- JSON: ".json",
1215
- MARKDOWN: ".md",
1216
- TXT: ".txt",
1217
- XML: ".xml",
1218
- // Network capture
1219
- PCAP: ".pcap",
1220
- HOSTS: ".hosts",
1221
- MITM: ".mitm",
1222
- // Process I/O
1223
- STDOUT: ".stdout",
1224
- STDERR: ".stderr",
1225
- STDIN: ".stdin",
1226
- // Scripts
1227
- SH: ".sh",
1228
- PY: ".py",
1229
- CJS: ".cjs",
1230
- // Config
1231
- ENV: ".env",
1232
- BAK: ".bak"
1233
- };
1234
-
1235
- // src/shared/constants/files/special.ts
1236
- var SPECIAL_FILES = {
1237
- /** Latest state snapshot */
1238
- LATEST_STATE: "latest.json",
1239
- /** README */
1240
- README: "README.md",
1241
- /** Session summary (single source — LLM primary, deterministic fallback) */
1242
- SUMMARY: "summary.md",
1243
- /** Persistent knowledge store */
1244
- PERSISTENT_KNOWLEDGE: "persistent-knowledge.json",
1245
- /** Debug log file */
1246
- DEBUG_LOG: "debug.log",
1247
- /**
1248
- * Session snapshot for Insane-level long session resumption (3차).
1249
- * Stores current phase, achieved foothold, next steps, credentials.
1250
- * Written by PlaybookSynthesizer on flag capture / manual update_mission.
1251
- */
1252
- SESSION_SNAPSHOT: "session-snapshot.json"
1253
- };
1254
-
1255
- // src/shared/constants/files/patterns.ts
1256
- var FILE_PATTERNS = {
1257
- /** Tool output filename: nmap.txt, gobuster.txt (sanitized) */
1258
- toolOutput(toolName) {
1259
- return `${sanitizeFilename(toolName)}.txt`;
1260
- },
1261
- /** Generate session snapshot filename: 2026-02-21T08-30-15.json */
1262
- session() {
1263
- return `${fileTimestamp()}.json`;
1264
- }
1265
- };
1266
-
1267
- // src/shared/utils/debug/debug-logger.ts
1268
1300
  var ROTATE_BYTES = 5 * 1024 * 1024;
1269
1301
  var WIPE_BYTES = 20 * 1024 * 1024;
1270
1302
  var ROTATE_CHECK_INTERVAL = 500;
@@ -1896,6 +1928,8 @@ var ENV_KEYS = {
1896
1928
  SEARCH_API_URL: "SEARCH_API_URL",
1897
1929
  THINKING: "PENTEST_THINKING",
1898
1930
  THINKING_BUDGET: "PENTEST_THINKING_BUDGET",
1931
+ SCOPE_MODE: "PENTEST_SCOPE_MODE",
1932
+ APPROVAL_MODE: "PENTEST_APPROVAL_MODE",
1899
1933
  APP_VERSION: "PENTESTING_VERSION"
1900
1934
  };
1901
1935
  var DEFAULT_SEARCH_API_URL = "https://api.search.brave.com/res/v1/web/search";
@@ -1929,6 +1963,12 @@ function getThinkingBudget() {
1929
1963
  const val = parseInt(process.env[ENV_KEYS.THINKING_BUDGET] || "", 10);
1930
1964
  return isNaN(val) ? 8e3 : Math.max(1024, val);
1931
1965
  }
1966
+ function getScopeMode() {
1967
+ return process.env[ENV_KEYS.SCOPE_MODE] === "enforce" ? "enforce" : "advisory";
1968
+ }
1969
+ function getApprovalMode() {
1970
+ return process.env[ENV_KEYS.APPROVAL_MODE] === "require_auto_approve" ? "require_auto_approve" : "advisory";
1971
+ }
1932
1972
 
1933
1973
  // src/shared/utils/config/tor/core.ts
1934
1974
  var TOR_PROXY = {
@@ -2607,7 +2647,7 @@ async function cleanupAllProcesses() {
2607
2647
  cleanupDone = false;
2608
2648
  return;
2609
2649
  }
2610
- const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-BDTYM4MC.js");
2650
+ const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-CCAQVJ4Y.js");
2611
2651
  const backgroundProcesses = getBackgroundProcessesMap2();
2612
2652
  terminateAllNatively(backgroundProcesses, "SIGTERM");
2613
2653
  await new Promise((r) => setTimeout(r, SYSTEM_LIMITS.CLEANUP_BATCH_WAIT_MS));
@@ -2801,6 +2841,9 @@ var ACTIONABLE_LOOT_TYPES = /* @__PURE__ */ new Set([
2801
2841
  LOOT_TYPES.TICKET,
2802
2842
  LOOT_TYPES.SESSION
2803
2843
  ]);
2844
+ function safeList(values) {
2845
+ return Array.isArray(values) ? values.filter((value) => typeof value === "string") : [];
2846
+ }
2804
2847
  var StateSerializer = class {
2805
2848
  /**
2806
2849
  * Convert full state to a compact prompt summary
@@ -2837,7 +2880,9 @@ var StateSerializer = class {
2837
2880
  lines.push(`Engagement: ${engagement.name} (${engagement.client})`);
2838
2881
  }
2839
2882
  if (scope) {
2840
- lines.push(`Scope: CIDR=[${scope.allowedCidrs.join(", ")}] Domains=[${scope.allowedDomains.join(", ")}]`);
2883
+ const allowedCidrs = safeList(scope.allowedCidrs);
2884
+ const allowedDomains = safeList(scope.allowedDomains);
2885
+ lines.push(`Scope: CIDR=[${allowedCidrs.join(", ")}] Domains=[${allowedDomains.join(", ")}]`);
2841
2886
  }
2842
2887
  const mission = state3.getMissionSummary();
2843
2888
  if (mission) {
@@ -3014,11 +3059,11 @@ function loadState(state3) {
3014
3059
  state3.addTarget(value);
3015
3060
  }
3016
3061
  for (const finding of snapshot.findings) {
3017
- const legacyFinding = finding;
3018
- if (typeof legacyFinding.confidence !== "number") {
3019
- legacyFinding.confidence = legacyFinding.isVerified === true ? 80 : 25;
3062
+ const migratedFinding = finding;
3063
+ if (typeof migratedFinding.confidence !== "number") {
3064
+ migratedFinding.confidence = migratedFinding.isVerified === true ? 80 : 25;
3020
3065
  }
3021
- state3.addFinding(legacyFinding);
3066
+ state3.addFinding(migratedFinding);
3022
3067
  }
3023
3068
  for (const loot of snapshot.loot) {
3024
3069
  state3.addLoot(loot);
@@ -3044,18 +3089,20 @@ function loadState(state3) {
3044
3089
 
3045
3090
  // src/engine/state/persistence/janitor.ts
3046
3091
  import { existsSync as existsSync7, rmSync } from "fs";
3092
+ import path2 from "path";
3047
3093
  function clearWorkspace() {
3048
3094
  const cleared = [];
3049
3095
  const errors = [];
3096
+ const root = WORKSPACE.ROOT;
3050
3097
  const dirsToClean = [
3051
3098
  { path: WORKSPACE.TURNS, label: "turn insights" },
3052
- { path: WORKSPACE.ARCHIVE, label: "archive (legacy)" },
3053
3099
  { path: WORKSPACE.DEBUG, label: "debug logs" },
3054
3100
  { path: WORKSPACE.TMP, label: "workspace/temp" },
3055
- { path: WORKSPACE.OUTPUTS, label: "outputs (legacy)" },
3056
- { path: WORKSPACE.JOURNAL, label: "journal (legacy)" },
3057
3101
  { path: WORKSPACE.LOOT, label: "loot" },
3058
- { path: WORKSPACE.REPORTS, label: "reports" }
3102
+ { path: WORKSPACE.REPORTS, label: "reports" },
3103
+ { path: path2.join(root, "archive"), label: "archive (historical cleanup)" },
3104
+ { path: path2.join(root, "outputs"), label: "outputs (historical cleanup)" },
3105
+ { path: path2.join(root, "journal"), label: "journal (historical cleanup)" }
3059
3106
  ];
3060
3107
  for (const dir2 of dirsToClean) {
3061
3108
  try {
@@ -3073,11 +3120,11 @@ function clearWorkspace() {
3073
3120
 
3074
3121
  export {
3075
3122
  ensureDirExists,
3076
- WORK_DIR,
3077
- WORKSPACE,
3078
3123
  FILE_EXTENSIONS,
3079
3124
  SPECIAL_FILES,
3080
3125
  FILE_PATTERNS,
3126
+ WORK_DIR,
3127
+ WORKSPACE,
3081
3128
  initDebugLogger,
3082
3129
  debugLog,
3083
3130
  flowLog,
@@ -3090,6 +3137,8 @@ export {
3090
3137
  isZaiProvider,
3091
3138
  isThinkingEnabled,
3092
3139
  getThinkingBudget,
3140
+ getScopeMode,
3141
+ getApprovalMode,
3093
3142
  isTorEnabled,
3094
3143
  setTorEnabled,
3095
3144
  getTorBrowserArgs,
@@ -3136,10 +3185,10 @@ export {
3136
3185
  UI_COMMANDS,
3137
3186
  generateId,
3138
3187
  generatePrefixedId,
3139
- setCommandEventEmitter,
3140
- setInputHandler,
3141
- clearInputHandler,
3142
- clearCommandEventEmitter,
3188
+ createSessionRuntime,
3189
+ setActiveSessionRuntime,
3190
+ clearActiveSessionRuntime,
3191
+ getActiveSessionRuntime,
3143
3192
  runCommand,
3144
3193
  getErrorMessage,
3145
3194
  readFileContent,