pentesting 0.46.11 → 0.47.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
@@ -311,7 +311,7 @@ var ORPHAN_PROCESS_NAMES = [
311
311
 
312
312
  // src/shared/constants/agent.ts
313
313
  var APP_NAME = "Pentest AI";
314
- var APP_VERSION = "0.46.11";
314
+ var APP_VERSION = "0.47.0";
315
315
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
316
316
  var LLM_ROLES = {
317
317
  SYSTEM: "system",
@@ -7093,7 +7093,7 @@ You should either: (1) try a different approach that doesn't need this input, or
7093
7093
  if (isSensitive) {
7094
7094
  return {
7095
7095
  success: true,
7096
- output: `\u2713 Received ${request.type} input from user.
7096
+ output: `Received ${request.type} input from user.
7097
7097
 
7098
7098
  The value has been stored. You can now use it in your next command.
7099
7099
  For sudo: include 'echo "<the_value>" | sudo -S <command>'
@@ -7103,7 +7103,7 @@ For SSH: use sshpass or expect with the provided password.`,
7103
7103
  }
7104
7104
  return {
7105
7105
  success: true,
7106
- output: `\u2713 User provided: ${result2.value}
7106
+ output: `User provided: ${result2.value}
7107
7107
 
7108
7108
  Use this value in your next action.`,
7109
7109
  value: result2.value
@@ -10110,11 +10110,17 @@ Phase: ${phase} | Targets: ${targets} | Findings: ${findings} | Tools executed:
10110
10110
 
10111
10111
  ${direction}
10112
10112
 
10113
+ ESCALATION CHAIN \u2014 follow this order:
10114
+ 1. web_search: Search for techniques, bypasses, default creds, CVEs, HackTricks
10115
+ 2. BYPASS: Try alternative approaches \u2014 different protocols, ports, encodings, methods
10116
+ 3. ZERO-DAY EXPLORATION: Probe for unknown vulns \u2014 fuzz parameters, test edge cases, analyze error responses for leaks
10117
+ 4. BRUTE-FORCE: Wordlists, credential stuffing, common passwords, custom password lists from context
10118
+ 5. ask_user: ONLY as last resort \u2014 ask the user for hints, wordlists, or guidance
10119
+
10113
10120
  RULES:
10114
10121
  - Every turn MUST have tool calls
10115
- - If stuck: search for techniques (web_search)
10116
- - If failed: try a DIFFERENT approach
10117
- - ACT NOW \u2014 do not plan or explain`
10122
+ - NEVER silently give up \u2014 exhaust ALL 5 steps above first
10123
+ - ACT NOW \u2014 do not plan, do not explain, do not summarize. EXECUTE.`
10118
10124
  });
10119
10125
  }
10120
10126
  } catch (error) {
@@ -10232,11 +10238,6 @@ Please decide how to handle this error and continue.`;
10232
10238
  const stepDuration = Date.now() - stepStartTime;
10233
10239
  const tokens = response.usage ? { input: response.usage.input_tokens, output: response.usage.output_tokens } : void 0;
10234
10240
  if (!response.toolCalls?.length) {
10235
- const hasDoneMeaningfulWork = (progress?.totalToolsExecuted ?? 0) > 0;
10236
- if (hasDoneMeaningfulWork) {
10237
- this.emitComplete(response.content, iteration, 0, stepDuration, tokens);
10238
- return { output: response.content, toolsExecuted: 0, isCompleted: true };
10239
- }
10240
10241
  return { output: response.content, toolsExecuted: 0, isCompleted: false };
10241
10242
  }
10242
10243
  const results = await this.processToolCalls(response.toolCalls, progress);
@@ -11972,14 +11973,14 @@ var useAgentEvents = (agent, eventsRef, state) => {
11972
11973
  };
11973
11974
  const onReasoningStart = () => {
11974
11975
  reasoningBufferRef.current = "";
11975
- setCurrentStatus("Thinking\u2026");
11976
+ setCurrentStatus("Reasoning\u2026");
11976
11977
  };
11977
11978
  const onReasoningDelta = (e) => {
11978
11979
  reasoningBufferRef.current += e.data.content;
11979
11980
  const chars = reasoningBufferRef.current.length;
11980
- const firstLine = reasoningBufferRef.current.split("\n")[0]?.slice(0, TUI_DISPLAY_LIMITS.reasoningPreviewChars) || "";
11981
- setCurrentStatus(`Thinking\u2026 ${chars} chars
11982
- ${firstLine}`);
11981
+ const estTokens = Math.round(chars / 4);
11982
+ setCurrentStatus(`Reasoning (~${estTokens} tokens)
11983
+ ${reasoningBufferRef.current}`);
11983
11984
  };
11984
11985
  const onReasoningEnd = () => {
11985
11986
  const text = reasoningBufferRef.current.trim();
@@ -12438,22 +12439,16 @@ var MessageList = memo(({ messages }) => {
12438
12439
  if (msg.type === "thinking") {
12439
12440
  const lines = msg.content.split("\n");
12440
12441
  const charCount = msg.content.length;
12441
- const firstLine = lines[0]?.slice(0, TUI_DISPLAY_LIMITS.thinkingSummaryChars) || "";
12442
- const isMultiLine = lines.length > 1 || charCount > TUI_DISPLAY_LIMITS.thinkingSummaryChars;
12442
+ const estTokens = Math.round(charCount / 4);
12443
12443
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 0, marginBottom: 1, children: [
12444
12444
  /* @__PURE__ */ jsxs2(Box2, { children: [
12445
- /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, children: "\u25D0 " }),
12446
- /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, bold: true, children: "Thinking" }),
12447
- /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: ` (${lines.length} line${lines.length !== 1 ? "s" : ""}, ${charCount} chars)` })
12445
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, bold: true, children: "Reasoning" }),
12446
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: ` (~${estTokens} tokens)` })
12448
12447
  ] }),
12449
12448
  lines.map((line, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
12450
12449
  /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, children: "\u2502 " }),
12451
12450
  /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: line })
12452
- ] }, i)),
12453
- /* @__PURE__ */ jsxs2(Box2, { children: [
12454
- /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, children: "\u2570\u2500" }),
12455
- isMultiLine && /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: ` ${firstLine}\u2026` })
12456
- ] })
12451
+ ] }, i))
12457
12452
  ] }, msg.id);
12458
12453
  }
12459
12454
  if (msg.type === "tool") {
@@ -12529,6 +12524,7 @@ var MusicSpinner = memo2(({ color }) => {
12529
12524
 
12530
12525
  // src/platform/tui/components/StatusDisplay.tsx
12531
12526
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
12527
+ var MAX_THINKING_LINES = 15;
12532
12528
  var StatusDisplay = memo3(({
12533
12529
  retryState,
12534
12530
  isProcessing,
@@ -12540,10 +12536,9 @@ var StatusDisplay = memo3(({
12540
12536
  return err.length > DISPLAY_LIMITS.RETRY_ERROR_PREVIEW ? err.substring(0, DISPLAY_LIMITS.RETRY_ERROR_TRUNCATED) + "..." : err;
12541
12537
  };
12542
12538
  const meta = formatMeta(elapsedTime * 1e3, currentTokens);
12539
+ const isThinkingStatus = currentStatus.startsWith("Reasoning");
12543
12540
  const statusLines = currentStatus ? currentStatus.split("\n").filter(Boolean) : [];
12544
12541
  const statusMain = statusLines[0] || "Processing...";
12545
- const statusSub = statusLines.slice(1).join(" ");
12546
- const isThinkingStatus = statusMain.startsWith("Thinking");
12547
12542
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 0, children: [
12548
12543
  retryState.status === "retrying" && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12549
12544
  /* @__PURE__ */ jsx4(Text4, { color: THEME.yellow, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.yellow }) }),
@@ -12572,10 +12567,14 @@ var StatusDisplay = memo3(({
12572
12567
  meta
12573
12568
  ] })
12574
12569
  ] }),
12575
- statusSub ? /* @__PURE__ */ jsx4(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text4, { color: isThinkingStatus ? THEME.cyan : THEME.gray, children: [
12570
+ isThinkingStatus && statusLines.length > 1 && statusLines.slice(1).slice(-MAX_THINKING_LINES).map((line, i) => /* @__PURE__ */ jsxs3(Box3, { children: [
12571
+ /* @__PURE__ */ jsx4(Text4, { color: THEME.cyan, children: "\u2502 " }),
12572
+ /* @__PURE__ */ jsx4(Text4, { color: THEME.gray, children: line })
12573
+ ] }, i)),
12574
+ !isThinkingStatus && statusLines.length > 1 && /* @__PURE__ */ jsx4(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12576
12575
  "\u2502 ",
12577
- statusSub
12578
- ] }) }) : null
12576
+ statusLines.slice(1).join(" ")
12577
+ ] }) })
12579
12578
  ] })
12580
12579
  ] });
12581
12580
  });
@@ -12938,22 +12937,43 @@ ${procData.stdout || "(no output)"}
12938
12937
  setInputRequest({ status: "inactive" });
12939
12938
  setSecretInput("");
12940
12939
  }, [addMessage, setInputRequest]);
12940
+ const ctrlCTimerRef = useRef5(null);
12941
+ const ctrlCPressedRef = useRef5(false);
12942
+ const handleCtrlC = useCallback4(() => {
12943
+ if (ctrlCPressedRef.current) {
12944
+ if (ctrlCTimerRef.current) clearTimeout(ctrlCTimerRef.current);
12945
+ handleExit();
12946
+ return;
12947
+ }
12948
+ ctrlCPressedRef.current = true;
12949
+ addMessage("system", "\u26A0\uFE0F Press Ctrl+C again within 3 seconds to exit.");
12950
+ if (isProcessingRef.current) abort();
12951
+ ctrlCTimerRef.current = setTimeout(() => {
12952
+ ctrlCPressedRef.current = false;
12953
+ ctrlCTimerRef.current = null;
12954
+ }, 3e3);
12955
+ }, [handleExit, addMessage, abort]);
12956
+ useEffect4(() => {
12957
+ return () => {
12958
+ if (ctrlCTimerRef.current) clearTimeout(ctrlCTimerRef.current);
12959
+ };
12960
+ }, []);
12941
12961
  useInput2(useCallback4((ch, key) => {
12942
12962
  if (key.escape) {
12943
12963
  if (inputRequestRef.current.status === "active") cancelInputRequest();
12944
12964
  else if (isProcessingRef.current) abort();
12945
12965
  }
12946
- if (key.ctrl && ch === "c") handleExit();
12947
- }, [cancelInputRequest, abort, handleExit]));
12966
+ if (key.ctrl && ch === "c") handleCtrlC();
12967
+ }, [cancelInputRequest, abort, handleCtrlC]));
12948
12968
  useEffect4(() => {
12949
- const onSignal = () => handleExit();
12969
+ const onSignal = () => handleCtrlC();
12950
12970
  process.on("SIGINT", onSignal);
12951
12971
  process.on("SIGTERM", onSignal);
12952
12972
  return () => {
12953
12973
  process.off("SIGINT", onSignal);
12954
12974
  process.off("SIGTERM", onSignal);
12955
12975
  };
12956
- }, [handleExit]);
12976
+ }, [handleCtrlC]);
12957
12977
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
12958
12978
  /* @__PURE__ */ jsx7(Box6, { flexDirection: "column", marginBottom: 1, flexGrow: 1, children: /* @__PURE__ */ jsx7(MessageList, { messages }) }),
12959
12979
  /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
@@ -78,7 +78,8 @@ What target would you like me to attack? (IP, domain, or CTF challenge)
78
78
  - **Update objectives**: Use `update_mission` to keep the operation summary and checklist current when needed
79
79
  - Is it time to move to the next step, or dig deeper at the current one?
80
80
 
81
- This loop **repeats continuously** until the task is complete. **Never stop.**
81
+ This loop **repeats continuously** until the task is complete. **Never stop on your own.**
82
+ If you believe you have exhausted all approaches → use `ask_user` to confirm with the user before stopping.
82
83
 
83
84
  ## Absolute Rules
84
85
 
@@ -87,12 +88,13 @@ This loop **repeats continuously** until the task is complete. **Never stop.**
87
88
  - Record findings immediately with add_finding
88
89
  - **Execute tasks immediately without unnecessary confirmations/questions**
89
90
  - If no results → **try a different approach** (never repeat the same method)
90
- - ask_user is **only for physically unobtainable information** (passwords, SSH keys, API tokens)
91
+ - ask_user is for: (1) physically unobtainable information (passwords, SSH keys, API tokens), (2) **confirming you're truly done** when all vectors are exhausted
91
92
 
92
93
  ### 2. ask_user Rules
93
94
  - Use received values **immediately in the next command** — receiving and not using is forbidden
94
95
  - Once received → **reuse** — never ask for the same thing again
95
96
  - Confirmation requests like "Can I do this?" are forbidden
97
+ - **WHEN TO ASK**: If you believe all attack vectors are exhausted and want to stop, you MUST `ask_user` to confirm. The user may have hints, custom wordlists, or additional context. **Never silently give up.**
96
98
 
97
99
  ### 3. Self-Correction on Errors (MANDATORY)
98
100
  When an error occurs, read the `[TOOL ERROR ANALYSIS]` section and fix immediately:
@@ -223,16 +225,21 @@ Don't agonize. **The world's best methodologies are already on the web.** Search
223
225
  When you find a PoC → verify code with `browse_url` → save with `write_file` → modify for environment → execute with `run_cmd`.
224
226
  **Searching is not a waste of time — it's a prerequisite for accurate attacks.**
225
227
 
226
- ### When Stuck — Autonomous Breakthrough Principles
228
+ ### When Stuck — Escalation Chain (follow in order)
227
229
 
228
- 1. **Same method fails twice → immediately switch approaches** (don't wait for 3)
229
- 2. **Search when you don't know** — the answer is in HackTricks, PayloadsAllTheThings, GTFOBins
230
- 3. **Install the tool or write code yourself if unavailable** — tool absence is not a reason to stop attacking
231
- 4. **Approach from a completely different angle** — web fails network, network fails different service, service fails → different target
232
- 5. **Errors are information** — extract version, path, and configuration hints from error messages
233
- 6. **Use your judgment** — "Can I do this?" is forbidden. Act and see the results
234
- 7. **When you find a PoC read save execute** you must know how to modify code
235
- 8. **If you have a shell, use it for everything** tool download, script execution, additional recon all possible
230
+ **Same method fails twice → immediately switch approaches** (don't wait for 3).
231
+ **Errors are information** — extract version, path, and configuration hints from error messages.
232
+
233
+ 1. **🔍 SEARCH** — `web_search` for techniques, bypasses, default creds, CVEs, HackTricks, PayloadsAllTheThings, GTFOBins
234
+ 2. **🔄 BYPASS** — Try completely different angles: different protocol, port, encoding, different service, different target. Install missing tools or write your own code
235
+ 3. **🧬 ZERO-DAY EXPLORATION** — Probe for unknown vulns: fuzz parameters, test edge cases, analyze error responses for information leaks, try unconventional inputs
236
+ 4. **🔨 BRUTE-FORCE** Wordlists, credential stuffing, common passwords, custom password lists built from discovered context (usernames, company names, service names)
237
+ 5. **❓ ask_user** ONLY as absolute last resort. Ask the user for hints, custom wordlists, or guidance. **Never silently give up.**
238
+
239
+ Additional principles:
240
+ - **If you have a shell, use it for everything** — tool download, script execution, additional recon
241
+ - **When you find a PoC → read → save → execute** — modify code for the environment
242
+ - **Tool absence is not a reason to stop** — write equivalent scripts yourself
236
243
 
237
244
  ### PoC Acquisition and Execution Protocol
238
245
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pentesting",
3
- "version": "0.46.11",
3
+ "version": "0.47.0",
4
4
  "description": "Autonomous Penetration Testing AI Agent",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -28,7 +28,7 @@
28
28
  "release:patch": "npm version patch && npm run build && npm run publish:token",
29
29
  "release:minor": "npm version minor && npm run build && npm run publish:token",
30
30
  "release:major": "npm version major && npm run build && npm run publish:token",
31
- "release:docker": "docker buildx build --platform linux/amd64,linux/arm64 -t agnusdei1207/pentesting:latest --push .",
31
+ "release:docker": "docker buildx build --platform linux/amd64,linux/arm64 -t agnusdei1207/pentesting:latest --push . && docker system prune -af",
32
32
  "check": "TMPDIR=/tmp npm run test && npm run build && npm run release:docker && bash test.sh"
33
33
  },
34
34
  "repository": {