pentesting 0.16.7 → 0.16.8

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.
Files changed (3) hide show
  1. package/README.md +45 -0
  2. package/dist/main.js +329 -147
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,6 +30,51 @@ export PENTEST_MODEL="glm-5"
30
30
 
31
31
  pentesting
32
32
  ```
33
+
34
+ ---
35
+
36
+ ## Environment Variables
37
+
38
+ ### Required (LLM)
39
+
40
+ | Variable | Description | Example |
41
+ |----------|-------------|---------|
42
+ | `PENTEST_API_KEY` | API key for LLM provider | `your_api_key` |
43
+ | `PENTEST_BASE_URL` | API endpoint URL | `https://api.z.ai/api/anthropic` |
44
+ | `PENTEST_MODEL` | Model name | `glm-5` |
45
+
46
+ ### Optional (Web Search API)
47
+
48
+ | Variable | Description | Example |
49
+ |----------|-------------|---------|
50
+ | `SEARCH_API_KEY` | Search API key | `your_api_key` |
51
+ | `SEARCH_API_URL` | Search API endpoint | `https://open.bigmodel.cn/api/paas/v4/tools/web-search-pro` |
52
+ | `BROWSER_HEADLESS` | Set `false` to show browser for captcha | `false` |
53
+
54
+ **Supported Search APIs:**
55
+
56
+ | Provider | SEARCH_API_URL |
57
+ |----------|----------------|
58
+ | GLM (Zhipu) | `https://open.bigmodel.cn/api/paas/v4/tools/web-search-pro` |
59
+ | Brave | `https://api.search.brave.com/res/v1/web/search` |
60
+ | Serper | `https://google.serper.dev/search` |
61
+
62
+ ### Example `.env`
63
+
64
+ ```bash
65
+ # LLM Configuration
66
+ PENTEST_API_KEY=your_api_key_here
67
+ PENTEST_BASE_URL=https://api.z.ai/api/anthropic
68
+ PENTEST_MODEL=glm-5
69
+
70
+ # Web Search API
71
+ SEARCH_API_KEY=your_api_key
72
+ SEARCH_API_URL=https://open.bigmodel.cn/api/paas/v4/tools/web-search-pro
73
+
74
+ # Browser (optional - for captcha handling)
75
+ BROWSER_HEADLESS=false
76
+ ```
77
+
33
78
  ---
34
79
 
35
80
  ## Issue Report
package/dist/main.js CHANGED
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
2
8
 
3
9
  // src/platform/tui/main.tsx
4
10
  import { render } from "ink";
@@ -2173,6 +2179,107 @@ All ports freed. All children killed.`
2173
2179
 
2174
2180
  // src/engine/tools-mid.ts
2175
2181
  import { execFileSync } from "child_process";
2182
+
2183
+ // src/shared/utils/config.ts
2184
+ import path from "path";
2185
+ import { fileURLToPath } from "url";
2186
+ var __filename = fileURLToPath(import.meta.url);
2187
+ var __dirname = path.dirname(__filename);
2188
+ function getApiKey() {
2189
+ return process.env.PENTEST_API_KEY || "";
2190
+ }
2191
+ function getBaseUrl() {
2192
+ return process.env.PENTEST_BASE_URL || void 0;
2193
+ }
2194
+ function getModel() {
2195
+ return process.env.PENTEST_MODEL || "";
2196
+ }
2197
+ function getSearchApiKey() {
2198
+ return process.env.SEARCH_API_KEY;
2199
+ }
2200
+ function getSearchApiUrl() {
2201
+ return process.env.SEARCH_API_URL;
2202
+ }
2203
+ function isBrowserHeadless() {
2204
+ return process.env.BROWSER_HEADLESS !== "false";
2205
+ }
2206
+
2207
+ // src/shared/utils/debug-logger.ts
2208
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync3, writeFileSync as writeFileSync3 } from "fs";
2209
+ import { join as join2 } from "path";
2210
+ var DebugLogger = class _DebugLogger {
2211
+ static instance;
2212
+ logPath;
2213
+ initialized = false;
2214
+ constructor(clearOnInit = false) {
2215
+ const debugDir = join2(process.cwd(), ".pentesting", "debug");
2216
+ try {
2217
+ if (!existsSync3(debugDir)) {
2218
+ mkdirSync2(debugDir, { recursive: true });
2219
+ }
2220
+ this.logPath = join2(debugDir, "debug.log");
2221
+ if (clearOnInit) {
2222
+ this.clear();
2223
+ }
2224
+ this.initialized = true;
2225
+ this.log("general", "=== DEBUG LOGGER INITIALIZED ===");
2226
+ this.log("general", `Log file: ${this.logPath}`);
2227
+ } catch (e) {
2228
+ console.error("[DebugLogger] Failed to initialize:", e);
2229
+ this.logPath = "";
2230
+ }
2231
+ }
2232
+ static getInstance(clearOnInit = false) {
2233
+ if (!_DebugLogger.instance) {
2234
+ _DebugLogger.instance = new _DebugLogger(clearOnInit);
2235
+ }
2236
+ return _DebugLogger.instance;
2237
+ }
2238
+ log(category, message, data) {
2239
+ if (!this.initialized || !this.logPath) return;
2240
+ try {
2241
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2242
+ let logLine = `[${timestamp}] [${category.toUpperCase()}] ${message}`;
2243
+ if (data !== void 0) {
2244
+ logLine += ` | ${JSON.stringify(data)}`;
2245
+ }
2246
+ logLine += "\n";
2247
+ appendFileSync2(this.logPath, logLine);
2248
+ } catch (e) {
2249
+ console.error("[DebugLogger] Write error:", e);
2250
+ }
2251
+ }
2252
+ logRaw(category, label, raw) {
2253
+ if (!this.initialized || !this.logPath) return;
2254
+ try {
2255
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2256
+ const logLine = `[${timestamp}] [${category.toUpperCase()}] ${label}:
2257
+ ${raw}
2258
+ ---
2259
+ `;
2260
+ appendFileSync2(this.logPath, logLine);
2261
+ } catch (e) {
2262
+ console.error("[DebugLogger] Write error:", e);
2263
+ }
2264
+ }
2265
+ clear() {
2266
+ if (!this.logPath) return;
2267
+ try {
2268
+ writeFileSync3(this.logPath, "");
2269
+ } catch (e) {
2270
+ console.error("[DebugLogger] Clear error:", e);
2271
+ }
2272
+ }
2273
+ };
2274
+ var logger = DebugLogger.getInstance(false);
2275
+ function initDebugLogger() {
2276
+ DebugLogger.instance = new DebugLogger(true);
2277
+ }
2278
+ function debugLog(category, message, data) {
2279
+ logger.log(category, message, data);
2280
+ }
2281
+
2282
+ // src/engine/tools-mid.ts
2176
2283
  function getErrorMessage(error) {
2177
2284
  return error instanceof Error ? error.message : String(error);
2178
2285
  }
@@ -2276,32 +2383,39 @@ async function searchExploitDB(service, version) {
2276
2383
  };
2277
2384
  }
2278
2385
  }
2279
- async function webSearch(query, engine = "duckduckgo") {
2386
+ async function webSearch(query, _engine) {
2387
+ debugLog("search", "webSearch START", { query });
2280
2388
  try {
2281
- let results = [];
2282
- if (engine === "duckduckgo") {
2283
- const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
2284
- try {
2285
- const html = execFileSync("curl", ["-s", "-L", "-A", "Mozilla/5.0", url], {
2286
- encoding: "utf-8",
2287
- stdio: ["ignore", "pipe", "pipe"],
2288
- timeout: 15e3
2289
- });
2290
- const titleRegex = /class="result__a"[^>]*>([^<]+)</g;
2291
- const matches = html.match(titleRegex) || [];
2292
- results = matches.map((m) => m.replace(/class="result__a"[^>]*>/, "")).slice(0, 10);
2293
- } catch (e) {
2294
- results = [`Web search failed: ${getErrorMessage(e)}`];
2295
- }
2389
+ const apiKey = getSearchApiKey();
2390
+ const apiUrl = getSearchApiUrl();
2391
+ debugLog("search", "Search API config", {
2392
+ hasApiKey: !!apiKey,
2393
+ apiUrl,
2394
+ apiKeyPrefix: apiKey ? apiKey.slice(0, 8) + "..." : null
2395
+ });
2396
+ if (!apiKey || !apiUrl) {
2397
+ debugLog("search", "Search API NOT CONFIGURED");
2398
+ return {
2399
+ success: false,
2400
+ output: "",
2401
+ error: "Search API not configured. Set SEARCH_API_KEY and SEARCH_API_URL environment variables."
2402
+ };
2403
+ }
2404
+ if (apiUrl.includes("bigmodel.cn") || apiUrl.includes("zhipuai")) {
2405
+ debugLog("search", "Using GLM search");
2406
+ return await searchWithGLM(query, apiKey, apiUrl);
2407
+ } else if (apiUrl.includes("brave.com")) {
2408
+ debugLog("search", "Using Brave search");
2409
+ return await searchWithBrave(query, apiKey, apiUrl);
2410
+ } else if (apiUrl.includes("serper.dev")) {
2411
+ debugLog("search", "Using Serper search");
2412
+ return await searchWithSerper(query, apiKey, apiUrl);
2296
2413
  } else {
2297
- results = ["Search engine not supported. Use: duckduckgo"];
2414
+ debugLog("search", "Using generic search API");
2415
+ return await searchWithGenericApi(query, apiKey, apiUrl);
2298
2416
  }
2299
- const formatted = results.map((r, i) => `${i + 1}. ${r}`).join("\n");
2300
- return {
2301
- success: true,
2302
- output: formatted || `No results found for: ${query}`
2303
- };
2304
2417
  } catch (error) {
2418
+ debugLog("search", "webSearch ERROR", { error: getErrorMessage(error) });
2305
2419
  return {
2306
2420
  success: false,
2307
2421
  output: "",
@@ -2309,11 +2423,134 @@ async function webSearch(query, engine = "duckduckgo") {
2309
2423
  };
2310
2424
  }
2311
2425
  }
2426
+ async function searchWithGLM(query, apiKey, apiUrl) {
2427
+ debugLog("search", "GLM request START", { apiUrl, query });
2428
+ const requestBody = {
2429
+ model: "web-search-pro",
2430
+ messages: [{ role: "user", content: query }],
2431
+ stream: false
2432
+ };
2433
+ debugLog("search", "GLM request body", requestBody);
2434
+ const response = await fetch(apiUrl, {
2435
+ method: "POST",
2436
+ headers: {
2437
+ "Content-Type": "application/json",
2438
+ "Authorization": `Bearer ${apiKey}`
2439
+ },
2440
+ body: JSON.stringify(requestBody)
2441
+ });
2442
+ debugLog("search", "GLM response status", { status: response.status, ok: response.ok });
2443
+ if (!response.ok) {
2444
+ const errorText = await response.text();
2445
+ debugLog("search", "GLM response ERROR", { status: response.status, error: errorText });
2446
+ throw new Error(`GLM Search API error: ${response.status} - ${errorText}`);
2447
+ }
2448
+ const data = await response.json();
2449
+ debugLog("search", "GLM response data", { hasChoices: !!data.choices, choicesCount: data.choices?.length });
2450
+ let results = "";
2451
+ if (data.choices?.[0]?.message?.content) {
2452
+ results = data.choices[0].message.content;
2453
+ debugLog("search", "GLM extracted from content", { contentLength: results.length });
2454
+ } else if (data.choices?.[0]?.message?.tool_calls) {
2455
+ debugLog("search", "GLM has tool_calls", { count: data.choices[0].message.tool_calls.length });
2456
+ for (const tc of data.choices[0].message.tool_calls) {
2457
+ if (tc.function?.arguments) {
2458
+ try {
2459
+ const args = JSON.parse(tc.function.arguments);
2460
+ results += JSON.stringify(args, null, 2) + "\n";
2461
+ } catch {
2462
+ }
2463
+ }
2464
+ }
2465
+ }
2466
+ debugLog("search", "GLM search COMPLETE", { resultsLength: results.length });
2467
+ return {
2468
+ success: true,
2469
+ output: results || `No results found for: ${query}`
2470
+ };
2471
+ }
2472
+ async function searchWithBrave(query, apiKey, apiUrl) {
2473
+ debugLog("search", "Brave request START", { apiUrl, query });
2474
+ const url = `${apiUrl}?q=${encodeURIComponent(query)}&count=10`;
2475
+ debugLog("search", "Brave request URL", { url });
2476
+ const response = await fetch(url, {
2477
+ headers: {
2478
+ "Accept": "application/json",
2479
+ "X-Subscription-Token": apiKey
2480
+ }
2481
+ });
2482
+ debugLog("search", "Brave response status", { status: response.status, ok: response.ok });
2483
+ if (!response.ok) {
2484
+ const errorText = await response.text();
2485
+ debugLog("search", "Brave response ERROR", { status: response.status, error: errorText });
2486
+ throw new Error(`Brave API error: ${response.status}`);
2487
+ }
2488
+ const data = await response.json();
2489
+ const results = data.web?.results || [];
2490
+ debugLog("search", "Brave results count", { count: results.length });
2491
+ if (results.length === 0) {
2492
+ return { success: true, output: `No results found for: ${query}` };
2493
+ }
2494
+ const formatted = results.map(
2495
+ (r, i) => `${i + 1}. ${r.title || "No title"}
2496
+ ${r.url || ""}
2497
+ ${r.description || ""}`
2498
+ ).join("\n\n");
2499
+ debugLog("search", "Brave search COMPLETE", { resultsLength: formatted.length });
2500
+ return { success: true, output: formatted };
2501
+ }
2502
+ async function searchWithSerper(query, apiKey, apiUrl) {
2503
+ debugLog("search", "Serper request START", { apiUrl, query });
2504
+ const response = await fetch(apiUrl, {
2505
+ method: "POST",
2506
+ headers: {
2507
+ "Content-Type": "application/json",
2508
+ "X-API-KEY": apiKey
2509
+ },
2510
+ body: JSON.stringify({ q: query })
2511
+ });
2512
+ debugLog("search", "Serper response status", { status: response.status, ok: response.ok });
2513
+ if (!response.ok) {
2514
+ const errorText = await response.text();
2515
+ debugLog("search", "Serper response ERROR", { status: response.status, error: errorText });
2516
+ throw new Error(`Serper API error: ${response.status}`);
2517
+ }
2518
+ const data = await response.json();
2519
+ const results = data.organic || [];
2520
+ debugLog("search", "Serper results count", { count: results.length });
2521
+ if (results.length === 0) {
2522
+ return { success: true, output: `No results found for: ${query}` };
2523
+ }
2524
+ const formatted = results.map(
2525
+ (r, i) => `${i + 1}. ${r.title || "No title"}
2526
+ ${r.link || ""}
2527
+ ${r.snippet || ""}`
2528
+ ).join("\n\n");
2529
+ debugLog("search", "Serper search COMPLETE", { resultsLength: formatted.length });
2530
+ return { success: true, output: formatted };
2531
+ }
2532
+ async function searchWithGenericApi(query, apiKey, apiUrl) {
2533
+ const response = await fetch(apiUrl, {
2534
+ method: "POST",
2535
+ headers: {
2536
+ "Content-Type": "application/json",
2537
+ "Authorization": `Bearer ${apiKey}`,
2538
+ "X-API-KEY": apiKey
2539
+ },
2540
+ body: JSON.stringify({ query, q: query })
2541
+ });
2542
+ if (!response.ok) {
2543
+ const errorText = await response.text();
2544
+ throw new Error(`Search API error: ${response.status} - ${errorText}`);
2545
+ }
2546
+ const data = await response.json();
2547
+ return { success: true, output: JSON.stringify(data, null, 2) };
2548
+ }
2312
2549
 
2313
2550
  // src/engine/tools/web-browser.ts
2314
2551
  import { spawn as spawn3 } from "child_process";
2315
- import { writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
2316
- import { join as join2 } from "path";
2552
+ import { writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
2553
+ import { join as join3, dirname } from "path";
2317
2554
  import { tmpdir as tmpdir2 } from "os";
2318
2555
 
2319
2556
  // src/shared/constants/browser.ts
@@ -2336,6 +2573,14 @@ var BROWSER_PATHS = {
2336
2573
  };
2337
2574
 
2338
2575
  // src/engine/tools/web-browser.ts
2576
+ function getPlaywrightPath() {
2577
+ try {
2578
+ const mainPath = __require.resolve("playwright");
2579
+ return dirname(mainPath);
2580
+ } catch {
2581
+ return join3(process.cwd(), "node_modules", "playwright");
2582
+ }
2583
+ }
2339
2584
  var DEFAULT_OPTIONS = {
2340
2585
  timeout: BROWSER_CONFIG.DEFAULT_TIMEOUT,
2341
2586
  userAgent: BROWSER_CONFIG.DEFAULT_USER_AGENT,
@@ -2405,15 +2650,15 @@ async function browseUrl(url, options = {}) {
2405
2650
  };
2406
2651
  }
2407
2652
  }
2408
- const tempDir = join2(tmpdir2(), BROWSER_PATHS.TEMP_DIR_NAME);
2409
- if (!existsSync3(tempDir)) {
2410
- mkdirSync2(tempDir, { recursive: true });
2653
+ const tempDir = join3(tmpdir2(), BROWSER_PATHS.TEMP_DIR_NAME);
2654
+ if (!existsSync4(tempDir)) {
2655
+ mkdirSync3(tempDir, { recursive: true });
2411
2656
  }
2412
- const screenshotPath = opts.screenshot ? join2(tempDir, `screenshot-${Date.now()}.png`) : void 0;
2657
+ const screenshotPath = opts.screenshot ? join3(tempDir, `screenshot-${Date.now()}.png`) : void 0;
2413
2658
  const script = buildBrowseScript(url, opts, screenshotPath);
2414
- const scriptPath = join2(tempDir, `browse-${Date.now()}.cjs`);
2659
+ const scriptPath = join3(tempDir, `browse-${Date.now()}.cjs`);
2415
2660
  try {
2416
- writeFileSync3(scriptPath, script, "utf-8");
2661
+ writeFileSync4(scriptPath, script, "utf-8");
2417
2662
  } catch (err) {
2418
2663
  return {
2419
2664
  success: false,
@@ -2422,9 +2667,10 @@ async function browseUrl(url, options = {}) {
2422
2667
  };
2423
2668
  }
2424
2669
  return new Promise((resolve) => {
2670
+ const nodePath = join3(process.cwd(), "node_modules");
2425
2671
  const child = spawn3("node", [scriptPath], {
2426
2672
  timeout: opts.timeout + 1e4,
2427
- env: { ...process.env, NODE_PATH: process.cwd() }
2673
+ env: { ...process.env, NODE_PATH: nodePath }
2428
2674
  });
2429
2675
  let stdout = "";
2430
2676
  let stderr = "";
@@ -2481,12 +2727,15 @@ function buildBrowseScript(url, options, screenshotPath) {
2481
2727
  const safeUserAgent = safeJsString(options.userAgent || BROWSER_CONFIG.DEFAULT_USER_AGENT);
2482
2728
  const safeScreenshotPath = screenshotPath ? safeJsString(screenshotPath) : "null";
2483
2729
  const safeExtraHeaders = JSON.stringify(options.extraHeaders || {});
2730
+ const playwrightPath = getPlaywrightPath();
2731
+ const safePlaywrightPath = safeJsString(playwrightPath);
2732
+ const headlessMode = isBrowserHeadless();
2484
2733
  return `
2485
- const { chromium } = require('playwright');
2734
+ const { chromium } = require(${safePlaywrightPath});
2486
2735
 
2487
2736
  (async () => {
2488
2737
  const browser = await chromium.launch({
2489
- headless: true,
2738
+ headless: ${headlessMode},
2490
2739
  args: ['--no-sandbox', '--disable-setuid-sandbox']
2491
2740
  });
2492
2741
 
@@ -2606,11 +2855,14 @@ async function fillAndSubmitForm(url, formData, options = {}) {
2606
2855
  const opts = { ...DEFAULT_OPTIONS, ...options };
2607
2856
  const safeUrl = safeJsString(url);
2608
2857
  const safeFormData = JSON.stringify(formData);
2858
+ const playwrightPath = getPlaywrightPath();
2859
+ const safePlaywrightPath = safeJsString(playwrightPath);
2860
+ const headlessMode = isBrowserHeadless();
2609
2861
  const script = `
2610
- const { chromium } = require('playwright');
2862
+ const { chromium } = require(${safePlaywrightPath});
2611
2863
 
2612
2864
  (async () => {
2613
- const browser = await chromium.launch({ headless: true });
2865
+ const browser = await chromium.launch({ headless: ${headlessMode} });
2614
2866
  const page = await browser.newPage();
2615
2867
 
2616
2868
  try {
@@ -2646,13 +2898,13 @@ const { chromium } = require('playwright');
2646
2898
  }
2647
2899
  })();
2648
2900
  `;
2649
- const tempDir = join2(tmpdir2(), BROWSER_PATHS.TEMP_DIR_NAME);
2650
- if (!existsSync3(tempDir)) {
2651
- mkdirSync2(tempDir, { recursive: true });
2901
+ const tempDir = join3(tmpdir2(), BROWSER_PATHS.TEMP_DIR_NAME);
2902
+ if (!existsSync4(tempDir)) {
2903
+ mkdirSync3(tempDir, { recursive: true });
2652
2904
  }
2653
- const scriptPath = join2(tempDir, `form-${Date.now()}.cjs`);
2905
+ const scriptPath = join3(tempDir, `form-${Date.now()}.cjs`);
2654
2906
  try {
2655
- writeFileSync3(scriptPath, script, "utf-8");
2907
+ writeFileSync4(scriptPath, script, "utf-8");
2656
2908
  } catch (err) {
2657
2909
  return {
2658
2910
  success: false,
@@ -2661,8 +2913,10 @@ const { chromium } = require('playwright');
2661
2913
  };
2662
2914
  }
2663
2915
  return new Promise((resolve) => {
2916
+ const nodePath = join3(process.cwd(), "node_modules");
2664
2917
  const child = spawn3("node", [scriptPath], {
2665
- timeout: opts.timeout + 1e4
2918
+ timeout: opts.timeout + 1e4,
2919
+ env: { ...process.env, NODE_PATH: nodePath }
2666
2920
  });
2667
2921
  let stdout = "";
2668
2922
  let stderr = "";
@@ -3993,81 +4247,81 @@ var ServiceParser = class {
3993
4247
  };
3994
4248
 
3995
4249
  // src/domains/registry.ts
3996
- import { join as join3, dirname } from "path";
3997
- import { fileURLToPath } from "url";
3998
- var __dirname = dirname(fileURLToPath(import.meta.url));
4250
+ import { join as join4, dirname as dirname2 } from "path";
4251
+ import { fileURLToPath as fileURLToPath2 } from "url";
4252
+ var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
3999
4253
  var DOMAINS = {
4000
4254
  [SERVICE_CATEGORIES.NETWORK]: {
4001
4255
  id: SERVICE_CATEGORIES.NETWORK,
4002
4256
  name: "Network Infrastructure",
4003
4257
  description: "Vulnerability scanning, port mapping, and network service exploitation.",
4004
- promptPath: join3(__dirname, "network/prompt.md")
4258
+ promptPath: join4(__dirname2, "network/prompt.md")
4005
4259
  },
4006
4260
  [SERVICE_CATEGORIES.WEB]: {
4007
4261
  id: SERVICE_CATEGORIES.WEB,
4008
4262
  name: "Web Application",
4009
4263
  description: "Web app security testing, injection attacks, and auth bypass.",
4010
- promptPath: join3(__dirname, "web/prompt.md")
4264
+ promptPath: join4(__dirname2, "web/prompt.md")
4011
4265
  },
4012
4266
  [SERVICE_CATEGORIES.DATABASE]: {
4013
4267
  id: SERVICE_CATEGORIES.DATABASE,
4014
4268
  name: "Database Security",
4015
4269
  description: "SQL injection, database enumeration, and data extraction.",
4016
- promptPath: join3(__dirname, "database/prompt.md")
4270
+ promptPath: join4(__dirname2, "database/prompt.md")
4017
4271
  },
4018
4272
  [SERVICE_CATEGORIES.AD]: {
4019
4273
  id: SERVICE_CATEGORIES.AD,
4020
4274
  name: "Active Directory",
4021
4275
  description: "Kerberos, LDAP, and Windows domain privilege escalation.",
4022
- promptPath: join3(__dirname, "ad/prompt.md")
4276
+ promptPath: join4(__dirname2, "ad/prompt.md")
4023
4277
  },
4024
4278
  [SERVICE_CATEGORIES.EMAIL]: {
4025
4279
  id: SERVICE_CATEGORIES.EMAIL,
4026
4280
  name: "Email Services",
4027
4281
  description: "SMTP, IMAP, POP3 security and user enumeration.",
4028
- promptPath: join3(__dirname, "email/prompt.md")
4282
+ promptPath: join4(__dirname2, "email/prompt.md")
4029
4283
  },
4030
4284
  [SERVICE_CATEGORIES.REMOTE_ACCESS]: {
4031
4285
  id: SERVICE_CATEGORIES.REMOTE_ACCESS,
4032
4286
  name: "Remote Access",
4033
4287
  description: "SSH, RDP, VNC and other remote control protocols.",
4034
- promptPath: join3(__dirname, "remote-access/prompt.md")
4288
+ promptPath: join4(__dirname2, "remote-access/prompt.md")
4035
4289
  },
4036
4290
  [SERVICE_CATEGORIES.FILE_SHARING]: {
4037
4291
  id: SERVICE_CATEGORIES.FILE_SHARING,
4038
4292
  name: "File Sharing",
4039
4293
  description: "SMB, NFS, FTP and shared resource security.",
4040
- promptPath: join3(__dirname, "file-sharing/prompt.md")
4294
+ promptPath: join4(__dirname2, "file-sharing/prompt.md")
4041
4295
  },
4042
4296
  [SERVICE_CATEGORIES.CLOUD]: {
4043
4297
  id: SERVICE_CATEGORIES.CLOUD,
4044
4298
  name: "Cloud Infrastructure",
4045
4299
  description: "AWS, Azure, and GCP security and misconfiguration.",
4046
- promptPath: join3(__dirname, "cloud/prompt.md")
4300
+ promptPath: join4(__dirname2, "cloud/prompt.md")
4047
4301
  },
4048
4302
  [SERVICE_CATEGORIES.CONTAINER]: {
4049
4303
  id: SERVICE_CATEGORIES.CONTAINER,
4050
4304
  name: "Container Systems",
4051
4305
  description: "Docker and Kubernetes security testing.",
4052
- promptPath: join3(__dirname, "container/prompt.md")
4306
+ promptPath: join4(__dirname2, "container/prompt.md")
4053
4307
  },
4054
4308
  [SERVICE_CATEGORIES.API]: {
4055
4309
  id: SERVICE_CATEGORIES.API,
4056
4310
  name: "API Security",
4057
4311
  description: "REST, GraphQL, and SOAP API security testing.",
4058
- promptPath: join3(__dirname, "api/prompt.md")
4312
+ promptPath: join4(__dirname2, "api/prompt.md")
4059
4313
  },
4060
4314
  [SERVICE_CATEGORIES.WIRELESS]: {
4061
4315
  id: SERVICE_CATEGORIES.WIRELESS,
4062
4316
  name: "Wireless Networks",
4063
4317
  description: "WiFi and Bluetooth security testing.",
4064
- promptPath: join3(__dirname, "wireless/prompt.md")
4318
+ promptPath: join4(__dirname2, "wireless/prompt.md")
4065
4319
  },
4066
4320
  [SERVICE_CATEGORIES.ICS]: {
4067
4321
  id: SERVICE_CATEGORIES.ICS,
4068
4322
  name: "Industrial Systems",
4069
4323
  description: "Critical infrastructure - Modbus, DNP3, ENIP.",
4070
- promptPath: join3(__dirname, "ics/prompt.md")
4324
+ promptPath: join4(__dirname2, "ics/prompt.md")
4071
4325
  }
4072
4326
  };
4073
4327
 
@@ -4316,21 +4570,6 @@ var agentLogger = new Logger("Agent", {
4316
4570
  colorOutput: true
4317
4571
  });
4318
4572
 
4319
- // src/shared/utils/config.ts
4320
- import path from "path";
4321
- import { fileURLToPath as fileURLToPath2 } from "url";
4322
- var __filename = fileURLToPath2(import.meta.url);
4323
- var __dirname2 = path.dirname(__filename);
4324
- function getApiKey() {
4325
- return process.env.PENTEST_API_KEY || "";
4326
- }
4327
- function getBaseUrl() {
4328
- return process.env.PENTEST_BASE_URL || void 0;
4329
- }
4330
- function getModel() {
4331
- return process.env.PENTEST_MODEL || "";
4332
- }
4333
-
4334
4573
  // src/shared/constants/llm.ts
4335
4574
  var RETRY_CONFIG = {
4336
4575
  baseDelayMs: 2e3,
@@ -4360,64 +4599,6 @@ var LLM_ERROR_TYPES = {
4360
4599
  };
4361
4600
 
4362
4601
  // src/engine/llm.ts
4363
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
4364
- import { join as join4 } from "path";
4365
- import { homedir } from "os";
4366
- var LLMLogger = class _LLMLogger {
4367
- static instance;
4368
- logPath;
4369
- initialized = false;
4370
- constructor() {
4371
- const debugDir = join4(homedir(), ".pentesting", "debug");
4372
- try {
4373
- if (!existsSync4(debugDir)) {
4374
- mkdirSync3(debugDir, { recursive: true });
4375
- }
4376
- this.logPath = join4(debugDir, "llm-debug.log");
4377
- this.initialized = true;
4378
- this.log("=== LLM LOGGER INITIALIZED ===");
4379
- this.log(`Log file: ${this.logPath}`);
4380
- } catch (e) {
4381
- console.error("[LLMLogger] Failed to initialize:", e);
4382
- this.logPath = "";
4383
- }
4384
- }
4385
- static getInstance() {
4386
- if (!_LLMLogger.instance) {
4387
- _LLMLogger.instance = new _LLMLogger();
4388
- }
4389
- return _LLMLogger.instance;
4390
- }
4391
- log(message, data) {
4392
- if (!this.initialized || !this.logPath) return;
4393
- try {
4394
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4395
- let logLine = `[${timestamp}] ${message}`;
4396
- if (data !== void 0) {
4397
- logLine += ` | ${JSON.stringify(data)}`;
4398
- }
4399
- logLine += "\n";
4400
- appendFileSync2(this.logPath, logLine);
4401
- } catch (e) {
4402
- console.error("[LLMLogger] Write error:", e);
4403
- }
4404
- }
4405
- logRaw(label, raw) {
4406
- if (!this.initialized || !this.logPath) return;
4407
- try {
4408
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4409
- const logLine = `[${timestamp}] ${label}:
4410
- ${raw}
4411
- ---
4412
- `;
4413
- appendFileSync2(this.logPath, logLine);
4414
- } catch (e) {
4415
- console.error("[LLMLogger] Write error:", e);
4416
- }
4417
- }
4418
- };
4419
- var logger = LLMLogger.getInstance();
4420
- logger.log("=== LLM MODULE LOADED ===");
4421
4602
  var LLMError = class extends Error {
4422
4603
  errorInfo;
4423
4604
  constructor(info) {
@@ -4456,7 +4637,7 @@ var LLMClient = class {
4456
4637
  this.apiKey = getApiKey();
4457
4638
  this.baseUrl = getBaseUrl() || "https://api.anthropic.com";
4458
4639
  this.model = getModel();
4459
- logger.log("LLMClient constructed", { model: this.model, baseUrl: this.baseUrl, apiKeyPrefix: this.apiKey.slice(0, 8) + "..." });
4640
+ debugLog("llm", "LLMClient constructed", { model: this.model, baseUrl: this.baseUrl, apiKeyPrefix: this.apiKey.slice(0, 8) + "..." });
4460
4641
  }
4461
4642
  async generateResponse(messages, tools, systemPrompt, callbacks) {
4462
4643
  return this.executeWithRetry(() => this.executeNonStream(messages, tools, systemPrompt), callbacks);
@@ -4516,12 +4697,12 @@ var LLMClient = class {
4516
4697
  messages: this.convertMessages(messages),
4517
4698
  tools
4518
4699
  };
4519
- logger.log("Non-stream request", { model: this.model, toolCount: tools?.length });
4700
+ debugLog("llm", "Non-stream request", { model: this.model, toolCount: tools?.length });
4520
4701
  const response = await this.makeRequest(requestBody);
4521
4702
  const data = await response.json();
4522
4703
  const textBlock = data.content.find((b) => b.type === "text");
4523
4704
  const toolBlocks = data.content.filter((b) => b.type === "tool_use");
4524
- logger.log("Non-stream response", { toolCount: toolBlocks.length, tools: toolBlocks.map((t) => ({ id: t.id, name: t.name, input: t.input })) });
4705
+ debugLog("llm", "Non-stream response", { toolCount: toolBlocks.length, tools: toolBlocks.map((t) => ({ id: t.id, name: t.name, input: t.input })) });
4525
4706
  return {
4526
4707
  content: textBlock?.text || "",
4527
4708
  toolCalls: toolBlocks.map((b) => ({ id: b.id || "", name: b.name || "", input: b.input || {} })),
@@ -4540,7 +4721,7 @@ var LLMClient = class {
4540
4721
  tools,
4541
4722
  stream: true
4542
4723
  };
4543
- logger.log(`[${requestId2}] Stream request START`, { model: this.model, toolCount: tools?.length, toolNames: tools?.map((t) => t.name) });
4724
+ debugLog("llm", `[${requestId2}] Stream request START`, { model: this.model, toolCount: tools?.length, toolNames: tools?.map((t) => t.name) });
4544
4725
  const response = await this.makeRequest(requestBody, callbacks?.abortSignal);
4545
4726
  let fullContent = "";
4546
4727
  let fullReasoning = "";
@@ -4569,12 +4750,12 @@ var LLMClient = class {
4569
4750
  if (data.trim() === "[DONE]") continue;
4570
4751
  try {
4571
4752
  const event = JSON.parse(data);
4572
- logger.log(`[${requestId2}] SSE event`, { type: event.type, index: event.index, deltaType: event.delta?.type });
4753
+ debugLog("llm", `[${requestId2}] SSE event`, { type: event.type, index: event.index, deltaType: event.delta?.type });
4573
4754
  if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
4574
- logger.log(`[${requestId2}] TOOL BLOCK START`, { id: event.content_block.id, name: event.content_block.name });
4755
+ debugLog("llm", `[${requestId2}] TOOL BLOCK START`, { id: event.content_block.id, name: event.content_block.name });
4575
4756
  }
4576
4757
  if (event.type === "content_block_delta" && event.delta?.type === "input_json_delta") {
4577
- logger.log(`[${requestId2}] JSON DELTA`, { index: event.index, partialJson: event.delta.partial_json });
4758
+ debugLog("llm", `[${requestId2}] JSON DELTA`, { index: event.index, partialJson: event.delta.partial_json });
4578
4759
  }
4579
4760
  this.processStreamEvent(event, {
4580
4761
  toolCallsMap,
@@ -4609,25 +4790,25 @@ var LLMClient = class {
4609
4790
  reader.releaseLock();
4610
4791
  }
4611
4792
  const toolCalls = [];
4612
- logger.log(`[${requestId2}] PARSING ${toolCallsMap.size} TOOL CALLS`);
4793
+ debugLog("llm", `[${requestId2}] PARSING ${toolCallsMap.size} TOOL CALLS`);
4613
4794
  for (const [id, toolCall] of toolCallsMap) {
4614
- logger.log(`[${requestId2}] Tool before parse`, { id, name: toolCall.name, pendingJsonLen: toolCall._pendingJson?.length, pendingJson: toolCall._pendingJson });
4795
+ debugLog("llm", `[${requestId2}] Tool before parse`, { id, name: toolCall.name, pendingJsonLen: toolCall._pendingJson?.length, pendingJson: toolCall._pendingJson });
4615
4796
  if (toolCall._pendingJson) {
4616
4797
  const parseResult = this.safeParseJson(toolCall._pendingJson);
4617
4798
  if (parseResult.success) {
4618
4799
  toolCall.input = parseResult.data;
4619
- logger.log(`[${requestId2}] Tool parsed OK`, { id, name: toolCall.name, input: toolCall.input });
4800
+ debugLog("llm", `[${requestId2}] Tool parsed OK`, { id, name: toolCall.name, input: toolCall.input });
4620
4801
  } else {
4621
4802
  toolCall.input = { _parse_error: parseResult.error, _raw_json: toolCall._pendingJson.slice(0, 500) };
4622
- logger.log(`[${requestId2}] Tool parse FAILED`, { id, name: toolCall.name, error: parseResult.error, raw: toolCall._pendingJson });
4803
+ debugLog("llm", `[${requestId2}] Tool parse FAILED`, { id, name: toolCall.name, error: parseResult.error, raw: toolCall._pendingJson });
4623
4804
  }
4624
4805
  delete toolCall._pendingJson;
4625
4806
  } else {
4626
- logger.log(`[${requestId2}] Tool NO JSON RECEIVED`, { id, name: toolCall.name });
4807
+ debugLog("llm", `[${requestId2}] Tool NO JSON RECEIVED`, { id, name: toolCall.name });
4627
4808
  }
4628
4809
  toolCalls.push({ id: toolCall.id, name: toolCall.name, input: toolCall.input });
4629
4810
  }
4630
- logger.log(`[${requestId2}] FINAL toolCalls`, { count: toolCalls.length, tools: toolCalls.map((t) => ({ id: t.id, name: t.name, input: t.input })) });
4811
+ debugLog("llm", `[${requestId2}] FINAL toolCalls`, { count: toolCalls.length, tools: toolCalls.map((t) => ({ id: t.id, name: t.name, input: t.input })) });
4631
4812
  return {
4632
4813
  content: fullContent,
4633
4814
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
@@ -4674,9 +4855,9 @@ var LLMClient = class {
4674
4855
  const toolCall = Array.from(toolCallsMap.values()).find((t) => t._index === index);
4675
4856
  if (toolCall) {
4676
4857
  toolCall._pendingJson = (toolCall._pendingJson || "") + event.delta.partial_json;
4677
- logger.log(`[${requestId}] JSON DELTA applied`, { index, toolId: toolCall.id, json: event.delta.partial_json });
4858
+ debugLog("llm", `[${requestId}] JSON DELTA applied`, { index, toolId: toolCall.id, json: event.delta.partial_json });
4678
4859
  } else {
4679
- logger.log(`[${requestId}] JSON DELTA no tool found for index`, { index });
4860
+ debugLog("llm", `[${requestId}] JSON DELTA no tool found for index`, { index });
4680
4861
  }
4681
4862
  }
4682
4863
  }
@@ -4758,7 +4939,7 @@ function getLLMClient() {
4758
4939
  return llmInstance;
4759
4940
  }
4760
4941
  function logLLM(message, data) {
4761
- logger.log(message, data);
4942
+ debugLog("llm", message, data);
4762
4943
  }
4763
4944
 
4764
4945
  // src/agents/core-agent.ts
@@ -5193,7 +5374,7 @@ Please decide how to handle this error and continue.`;
5193
5374
 
5194
5375
  // src/agents/prompt-builder.ts
5195
5376
  import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
5196
- import { join as join5, dirname as dirname2 } from "path";
5377
+ import { join as join5, dirname as dirname3 } from "path";
5197
5378
  import { fileURLToPath as fileURLToPath3 } from "url";
5198
5379
 
5199
5380
  // src/shared/constants/prompts.ts
@@ -5227,7 +5408,7 @@ var INITIAL_TASKS = {
5227
5408
  };
5228
5409
 
5229
5410
  // src/agents/prompt-builder.ts
5230
- var __dirname3 = dirname2(fileURLToPath3(import.meta.url));
5411
+ var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
5231
5412
  var PROMPTS_DIR = join5(__dirname3, "prompts");
5232
5413
  var PHASE_PROMPT_MAP = {
5233
5414
  recon: "recon.md",
@@ -6326,6 +6507,7 @@ var EXIT_CODE = {
6326
6507
 
6327
6508
  // src/platform/tui/main.tsx
6328
6509
  import { jsx as jsx7 } from "react/jsx-runtime";
6510
+ initDebugLogger();
6329
6511
  var program = new Command();
6330
6512
  program.name("pentesting").version(APP_VERSION).description(APP_DESCRIPTION).option("--dangerously-skip-permissions", "Skip all permission prompts (dangerous!)").option("-t, --target <target>", "Set initial target");
6331
6513
  program.command("interactive", { isDefault: true }).alias("i").description("Start interactive TUI mode").action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pentesting",
3
- "version": "0.16.7",
3
+ "version": "0.16.8",
4
4
  "description": "Autonomous Penetration Testing AI Agent",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",