@pensar/apex 2.0.1 → 2.0.2-canary.9e4e15cf

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 (52) hide show
  1. package/build/{agent-x7n47c84.js → agent-1jp62njk.js} +9 -9
  2. package/build/{agent-6nhperp2.js → agent-1zfxfgfr.js} +9 -7
  3. package/build/agent-ssm6x6qs.js +19 -0
  4. package/build/{apps-2ac4vt09.js → apps-2qd57k2z.js} +20 -15
  5. package/build/{auth-bmt98hz0.js → auth-bwb51krx.js} +15 -15
  6. package/build/authentication-3p84dzrr.js +19 -0
  7. package/build/blackboxAgent-dn68r23n.js +19 -0
  8. package/build/{blackboxPentest-xngbtdxb.js → blackboxPentest-txe2h6h0.js} +13 -13
  9. package/build/{cli-0yptvbbm.js → cli-0t1j4jh4.js} +9 -8
  10. package/build/{cli-fxtbkw2f.js → cli-3n78e7hs.js} +3 -3
  11. package/build/{cli-hk03x6fq.js → cli-8pw332mm.js} +2 -1
  12. package/build/{cli-z1dapp7v.js → cli-dk63pna5.js} +48 -11
  13. package/build/{cli-mfzkhttr.js → cli-e450tvx3.js} +11 -9
  14. package/build/{cli-f93g10xk.js → cli-jd0bfde5.js} +2 -2
  15. package/build/{cli-w2st266h.js → cli-jh6k2m9z.js} +31 -2
  16. package/build/{cli-88bhxzr1.js → cli-kj5vjbnd.js} +1 -1
  17. package/build/{cli-eptabm2j.js → cli-kxpba6jy.js} +1 -1
  18. package/build/{cli-1f5zzrxj.js → cli-nc2yvszh.js} +1 -1
  19. package/build/{cli-8g5cwvbm.js → cli-p904s2yj.js} +1 -1
  20. package/build/{cli-fa7nrded.js → cli-ppq4kh08.js} +3 -3
  21. package/build/{cli-zpdmnz8c.js → cli-qdh225p7.js} +353 -227
  22. package/build/{cli-cc13ydyx.js → cli-w1nb1y6h.js} +1 -1
  23. package/build/{cli-pyzw545d.js → cli-xcr4rax4.js} +23 -5
  24. package/build/{cli-ddtmgbqv.js → cli-za4t23gh.js} +2 -2
  25. package/build/cli.js +44 -39
  26. package/build/{config-j0gfjhrm.js → config-6s6jsd2a.js} +3 -3
  27. package/build/{doctor-zn8ms7gs.js → doctor-yh7zdmgt.js} +6 -6
  28. package/build/{fixes-d8ytvyzn.js → fixes-0xebnr67.js} +15 -15
  29. package/build/{index-528cyewc.js → index-k3f1hxpe.js} +17 -17
  30. package/build/{index-3cbcjqw1.js → index-kjky8ack.js} +9 -9
  31. package/build/{index-hjvqqkem.js → index-qfveae0v.js} +4 -4
  32. package/build/{index-a1sy2zak.js → index-ss31xhpj.js} +2 -2
  33. package/build/{index-9d2es97h.js → index-t3rffyf8.js} +3 -3
  34. package/build/{index-2t2cg8x0.js → index-w81kvhhz.js} +8 -8
  35. package/build/{index-k6ttkac6.js → index-zgr0dv9e.js} +6 -6
  36. package/build/{issues-17kdjtdg.js → issues-zjybm91e.js} +15 -15
  37. package/build/{logs-r4rjar4m.js → logs-t0vtg96a.js} +15 -15
  38. package/build/{offesecAgent-azd8ahkm.js → offesecAgent-kfjetqzw.js} +8 -8
  39. package/build/pentest-043y2x03.js +28 -0
  40. package/build/{pentests-npjb5q1h.js → pentests-h2mk21aq.js} +15 -15
  41. package/build/{targetedPentest-m24wvscc.js → targetedPentest-m6k9nde8.js} +9 -9
  42. package/build/targets-pknexde1.js +113 -0
  43. package/build/threatModel-mb9340cz.js +26 -0
  44. package/build/{uninstall-7pm6zcah.js → uninstall-bnamn9pn.js} +1 -1
  45. package/build/{upload-wg0vxmk0.js → upload-xrbxx4hz.js} +6 -6
  46. package/build/{utils-gd1y4t26.js → utils-6e1wgez0.js} +5 -5
  47. package/package.json +9 -8
  48. package/build/agent-4g69jwmq.js +0 -19
  49. package/build/authentication-c0aj9zaz.js +0 -19
  50. package/build/blackboxAgent-sgph70e4.js +0 -19
  51. package/build/pentest-2vsjf0j8.js +0 -28
  52. package/build/threatModel-7akmfzzm.js +0 -26
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  detectOSAndEnhancePrompt
3
- } from "./cli-cc13ydyx.js";
3
+ } from "./cli-w1nb1y6h.js";
4
4
  import {
5
5
  parseTargetUrl
6
6
  } from "./cli-c8131c4q.js";
@@ -26,14 +26,14 @@ import {
26
26
  scopedLogger,
27
27
  streamResponse,
28
28
  write
29
- } from "./cli-z1dapp7v.js";
29
+ } from "./cli-dk63pna5.js";
30
30
  import {
31
31
  ensureValidToken,
32
32
  getPensarApiUrl,
33
33
  init_auth,
34
34
  init_constants,
35
35
  signGatewayRequest
36
- } from "./cli-8g5cwvbm.js";
36
+ } from "./cli-p904s2yj.js";
37
37
  import {
38
38
  _enum,
39
39
  _null,
@@ -64,7 +64,7 @@ import {
64
64
  import {
65
65
  config,
66
66
  init_config
67
- } from "./cli-eptabm2j.js";
67
+ } from "./cli-kxpba6jy.js";
68
68
  import {
69
69
  __commonJS,
70
70
  __require,
@@ -42486,8 +42486,8 @@ var require_core3 = __commonJS((exports) => {
42486
42486
  function many1(p) {
42487
42487
  return ab(p, many(p), (head, tail) => [head, ...tail]);
42488
42488
  }
42489
- function ab(pa, pb, join13) {
42490
- return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join13(ma.value, vb, data, i, j)));
42489
+ function ab(pa, pb, join14) {
42490
+ return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join14(ma.value, vb, data, i, j)));
42491
42491
  }
42492
42492
  function left(pa, pb) {
42493
42493
  return ab(pa, pb, (va) => va);
@@ -42495,8 +42495,8 @@ var require_core3 = __commonJS((exports) => {
42495
42495
  function right(pa, pb) {
42496
42496
  return ab(pa, pb, (va, vb) => vb);
42497
42497
  }
42498
- function abc(pa, pb, pc, join13) {
42499
- return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join13(ma.value, mb.value, vc, data, i, j))));
42498
+ function abc(pa, pb, pc, join14) {
42499
+ return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join14(ma.value, mb.value, vc, data, i, j))));
42500
42500
  }
42501
42501
  function middle(pa, pb, pc) {
42502
42502
  return abc(pa, pb, pc, (ra, rb) => rb);
@@ -71179,7 +71179,7 @@ var require_thread_stream = __commonJS((exports, module) => {
71179
71179
  var { version } = require_package5();
71180
71180
  var { EventEmitter: EventEmitter2 } = __require("events");
71181
71181
  var { Worker } = __require("worker_threads");
71182
- var { join: join13 } = __require("path");
71182
+ var { join: join14 } = __require("path");
71183
71183
  var { pathToFileURL } = __require("url");
71184
71184
  var { wait } = require_wait();
71185
71185
  var {
@@ -71215,7 +71215,7 @@ var require_thread_stream = __commonJS((exports, module) => {
71215
71215
  function createWorker(stream, opts) {
71216
71216
  const { filename, workerData } = opts;
71217
71217
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
71218
- const toExecute = bundlerOverrides["thread-stream-worker"] || join13(__dirname, "lib", "worker.js");
71218
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join14(__dirname, "lib", "worker.js");
71219
71219
  const worker = new Worker(toExecute, {
71220
71220
  ...opts.workerOpts,
71221
71221
  trackUnmanagedFds: false,
@@ -71601,10 +71601,10 @@ var require_thread_stream = __commonJS((exports, module) => {
71601
71601
  // node_modules/pino/lib/transport.js
71602
71602
  var require_transport = __commonJS((exports, module) => {
71603
71603
  var __dirname = "/home/runner/work/apex/apex/node_modules/pino/lib";
71604
- var { createRequire: createRequire2 } = __require("module");
71604
+ var { createRequire: createRequire3 } = __require("module");
71605
71605
  var { existsSync: existsSync9 } = __require("node:fs");
71606
71606
  var getCallers = require_caller();
71607
- var { join: join13, isAbsolute: isAbsolute2, sep } = __require("node:path");
71607
+ var { join: join14, isAbsolute: isAbsolute2, sep } = __require("node:path");
71608
71608
  var { fileURLToPath } = __require("node:url");
71609
71609
  var sleep = require_atomic_sleep();
71610
71610
  var onExit = require_on_exit_leak_free();
@@ -71757,7 +71757,7 @@ var require_transport = __commonJS((exports, module) => {
71757
71757
  throw new Error("only one of target or targets can be specified");
71758
71758
  }
71759
71759
  if (targets) {
71760
- target = bundlerOverrides["pino-worker"] || join13(__dirname, "worker.js");
71760
+ target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
71761
71761
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
71762
71762
  return {
71763
71763
  ...dest,
@@ -71774,7 +71774,7 @@ var require_transport = __commonJS((exports, module) => {
71774
71774
  });
71775
71775
  });
71776
71776
  } else if (pipeline2) {
71777
- target = bundlerOverrides["pino-worker"] || join13(__dirname, "worker.js");
71777
+ target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
71778
71778
  options.pipelines = [pipeline2.map((dest) => {
71779
71779
  return {
71780
71780
  ...dest,
@@ -71797,13 +71797,13 @@ var require_transport = __commonJS((exports, module) => {
71797
71797
  return origin;
71798
71798
  }
71799
71799
  if (origin === "pino/file") {
71800
- return join13(__dirname, "..", "file.js");
71800
+ return join14(__dirname, "..", "file.js");
71801
71801
  }
71802
71802
  let fixTarget2;
71803
71803
  for (const filePath of callers) {
71804
71804
  try {
71805
71805
  const context = filePath === "node:repl" ? process.cwd() + sep : filePath;
71806
- fixTarget2 = createRequire2(context).resolve(origin);
71806
+ fixTarget2 = createRequire3(context).resolve(origin);
71807
71807
  break;
71808
71808
  } catch (err) {
71809
71809
  continue;
@@ -72726,7 +72726,7 @@ var require_safe_stable_stringify = __commonJS((exports, module) => {
72726
72726
  return circularValue;
72727
72727
  }
72728
72728
  let res = "";
72729
- let join13 = ",";
72729
+ let join14 = ",";
72730
72730
  const originalIndentation = indentation;
72731
72731
  if (Array.isArray(value)) {
72732
72732
  if (value.length === 0) {
@@ -72740,7 +72740,7 @@ var require_safe_stable_stringify = __commonJS((exports, module) => {
72740
72740
  indentation += spacer;
72741
72741
  res += `
72742
72742
  ${indentation}`;
72743
- join13 = `,
72743
+ join14 = `,
72744
72744
  ${indentation}`;
72745
72745
  }
72746
72746
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -72748,13 +72748,13 @@ ${indentation}`;
72748
72748
  for (;i < maximumValuesToStringify - 1; i++) {
72749
72749
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
72750
72750
  res += tmp2 !== undefined ? tmp2 : "null";
72751
- res += join13;
72751
+ res += join14;
72752
72752
  }
72753
72753
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
72754
72754
  res += tmp !== undefined ? tmp : "null";
72755
72755
  if (value.length - 1 > maximumBreadth) {
72756
72756
  const removedKeys = value.length - maximumBreadth - 1;
72757
- res += `${join13}"... ${getItemCount(removedKeys)} not stringified"`;
72757
+ res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
72758
72758
  }
72759
72759
  if (spacer !== "") {
72760
72760
  res += `
@@ -72775,7 +72775,7 @@ ${originalIndentation}`;
72775
72775
  let separator = "";
72776
72776
  if (spacer !== "") {
72777
72777
  indentation += spacer;
72778
- join13 = `,
72778
+ join14 = `,
72779
72779
  ${indentation}`;
72780
72780
  whitespace = " ";
72781
72781
  }
@@ -72789,13 +72789,13 @@ ${indentation}`;
72789
72789
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
72790
72790
  if (tmp !== undefined) {
72791
72791
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
72792
- separator = join13;
72792
+ separator = join14;
72793
72793
  }
72794
72794
  }
72795
72795
  if (keyLength > maximumBreadth) {
72796
72796
  const removedKeys = keyLength - maximumBreadth;
72797
72797
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
72798
- separator = join13;
72798
+ separator = join14;
72799
72799
  }
72800
72800
  if (spacer !== "" && separator.length > 1) {
72801
72801
  res = `
@@ -72835,7 +72835,7 @@ ${originalIndentation}`;
72835
72835
  }
72836
72836
  const originalIndentation = indentation;
72837
72837
  let res = "";
72838
- let join13 = ",";
72838
+ let join14 = ",";
72839
72839
  if (Array.isArray(value)) {
72840
72840
  if (value.length === 0) {
72841
72841
  return "[]";
@@ -72848,7 +72848,7 @@ ${originalIndentation}`;
72848
72848
  indentation += spacer;
72849
72849
  res += `
72850
72850
  ${indentation}`;
72851
- join13 = `,
72851
+ join14 = `,
72852
72852
  ${indentation}`;
72853
72853
  }
72854
72854
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -72856,13 +72856,13 @@ ${indentation}`;
72856
72856
  for (;i < maximumValuesToStringify - 1; i++) {
72857
72857
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
72858
72858
  res += tmp2 !== undefined ? tmp2 : "null";
72859
- res += join13;
72859
+ res += join14;
72860
72860
  }
72861
72861
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
72862
72862
  res += tmp !== undefined ? tmp : "null";
72863
72863
  if (value.length - 1 > maximumBreadth) {
72864
72864
  const removedKeys = value.length - maximumBreadth - 1;
72865
- res += `${join13}"... ${getItemCount(removedKeys)} not stringified"`;
72865
+ res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
72866
72866
  }
72867
72867
  if (spacer !== "") {
72868
72868
  res += `
@@ -72875,7 +72875,7 @@ ${originalIndentation}`;
72875
72875
  let whitespace = "";
72876
72876
  if (spacer !== "") {
72877
72877
  indentation += spacer;
72878
- join13 = `,
72878
+ join14 = `,
72879
72879
  ${indentation}`;
72880
72880
  whitespace = " ";
72881
72881
  }
@@ -72884,7 +72884,7 @@ ${indentation}`;
72884
72884
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
72885
72885
  if (tmp !== undefined) {
72886
72886
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
72887
- separator = join13;
72887
+ separator = join14;
72888
72888
  }
72889
72889
  }
72890
72890
  if (spacer !== "" && separator.length > 1) {
@@ -72941,20 +72941,20 @@ ${originalIndentation}`;
72941
72941
  indentation += spacer;
72942
72942
  let res2 = `
72943
72943
  ${indentation}`;
72944
- const join14 = `,
72944
+ const join15 = `,
72945
72945
  ${indentation}`;
72946
72946
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
72947
72947
  let i = 0;
72948
72948
  for (;i < maximumValuesToStringify - 1; i++) {
72949
72949
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
72950
72950
  res2 += tmp2 !== undefined ? tmp2 : "null";
72951
- res2 += join14;
72951
+ res2 += join15;
72952
72952
  }
72953
72953
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
72954
72954
  res2 += tmp !== undefined ? tmp : "null";
72955
72955
  if (value.length - 1 > maximumBreadth) {
72956
72956
  const removedKeys = value.length - maximumBreadth - 1;
72957
- res2 += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
72957
+ res2 += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
72958
72958
  }
72959
72959
  res2 += `
72960
72960
  ${originalIndentation}`;
@@ -72970,16 +72970,16 @@ ${originalIndentation}`;
72970
72970
  return '"[Object]"';
72971
72971
  }
72972
72972
  indentation += spacer;
72973
- const join13 = `,
72973
+ const join14 = `,
72974
72974
  ${indentation}`;
72975
72975
  let res = "";
72976
72976
  let separator = "";
72977
72977
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
72978
72978
  if (isTypedArrayWithEntries(value)) {
72979
- res += stringifyTypedArray(value, join13, maximumBreadth);
72979
+ res += stringifyTypedArray(value, join14, maximumBreadth);
72980
72980
  keys = keys.slice(value.length);
72981
72981
  maximumPropertiesToStringify -= value.length;
72982
- separator = join13;
72982
+ separator = join14;
72983
72983
  }
72984
72984
  if (deterministic) {
72985
72985
  keys = sort(keys, comparator);
@@ -72990,13 +72990,13 @@ ${indentation}`;
72990
72990
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
72991
72991
  if (tmp !== undefined) {
72992
72992
  res += `${separator}${strEscape(key2)}: ${tmp}`;
72993
- separator = join13;
72993
+ separator = join14;
72994
72994
  }
72995
72995
  }
72996
72996
  if (keyLength > maximumBreadth) {
72997
72997
  const removedKeys = keyLength - maximumBreadth;
72998
72998
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
72999
- separator = join13;
72999
+ separator = join14;
73000
73000
  }
73001
73001
  if (separator !== "") {
73002
73002
  res = `
@@ -109378,8 +109378,46 @@ function isElectron() {
109378
109378
  init_dist();
109379
109379
  init_zod();
109380
109380
  import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
109381
- import { createRequire } from "module";
109382
- import { dirname, join as join3 } from "path";
109381
+ import { createRequire as createRequire2 } from "module";
109382
+ import { dirname as dirname2, join as join4 } from "path";
109383
+
109384
+ // src/core/agents/offSecAgent/tools/camoufox.ts
109385
+ import { execFile } from "node:child_process";
109386
+ import { createRequire } from "node:module";
109387
+ import { dirname, join as join3 } from "node:path";
109388
+ import { promisify } from "node:util";
109389
+ var execFileAsync = promisify(execFile);
109390
+ var CAMOUFOX_OPTIONS = {
109391
+ humanize: true,
109392
+ block_webrtc: true
109393
+ };
109394
+ async function resolveCamoufoxLaunchOptions(headless) {
109395
+ const { launchOptions: camoufoxLaunchOptions } = await import("camoufox-js");
109396
+ return await camoufoxLaunchOptions({
109397
+ ...CAMOUFOX_OPTIONS,
109398
+ headless
109399
+ });
109400
+ }
109401
+ var ensurePromise = null;
109402
+ function ensureCamoufox(log) {
109403
+ if (!ensurePromise) {
109404
+ ensurePromise = (async () => {
109405
+ log?.("Provisioning Camoufox browser (first run downloads ~150MB; cached after)…");
109406
+ const require_ = createRequire(import.meta.url);
109407
+ const pkgPath = require_.resolve("camoufox-js/package.json");
109408
+ const fetchBin = join3(dirname(pkgPath), "dist", "__main__.js");
109409
+ await execFileAsync("node", [fetchBin, "fetch"], {
109410
+ timeout: 300000
109411
+ });
109412
+ })().catch((err) => {
109413
+ ensurePromise = null;
109414
+ throw err;
109415
+ });
109416
+ }
109417
+ return ensurePromise;
109418
+ }
109419
+
109420
+ // src/core/agents/offSecAgent/tools/playwrightMcp.ts
109383
109421
  var BrowserNavigateInput = exports_external.object({
109384
109422
  url: exports_external.string().describe("Full URL to navigate to"),
109385
109423
  toolCallDescription: exports_external.string().describe("Why you are navigating to this URL")
@@ -109495,6 +109533,7 @@ class PlaywrightMcpSession {
109495
109533
  viewportSize;
109496
109534
  extraHttpHeaders;
109497
109535
  mcpConfigPath = null;
109536
+ cachedCamouOptions = null;
109498
109537
  constructor(options = {}) {
109499
109538
  const { headless, userAgent, viewportSize, extraHttpHeaders } = options;
109500
109539
  this.headless = headless ?? defaultHeadless;
@@ -109504,14 +109543,26 @@ class PlaywrightMcpSession {
109504
109543
  this.extraHttpHeaders = headerSource && Object.keys(headerSource).length > 0 ? { ...headerSource } : undefined;
109505
109544
  }
109506
109545
  resetState() {
109546
+ this.cleanupConfigFile();
109507
109547
  this.mcpClient = null;
109508
109548
  this.mcpTransport = null;
109509
109549
  this.mcpProcess = null;
109510
109550
  this.connectionPromise = null;
109511
109551
  this.disconnecting = false;
109512
109552
  }
109513
- forceKillProcess() {
109514
- const proc = this.mcpProcess;
109553
+ cleanupConfigFile() {
109554
+ const configPath = this.mcpConfigPath;
109555
+ if (!configPath)
109556
+ return;
109557
+ this.mcpConfigPath = null;
109558
+ (async () => {
109559
+ try {
109560
+ const fsp = await import("fs/promises");
109561
+ await fsp.unlink(configPath);
109562
+ } catch {}
109563
+ })();
109564
+ }
109565
+ forceKillProcess(proc = this.mcpProcess) {
109515
109566
  if (!proc || proc.exitCode !== null)
109516
109567
  return;
109517
109568
  try {
@@ -109539,41 +109590,40 @@ class PlaywrightMcpSession {
109539
109590
  }
109540
109591
  this.connectionPromise = (async () => {
109541
109592
  try {
109542
- const require_ = createRequire(import.meta.url);
109593
+ const require_ = createRequire2(import.meta.url);
109543
109594
  const mcpPkgJsonPath = require_.resolve("@playwright/mcp/package.json");
109544
- const cliPath = join3(dirname(mcpPkgJsonPath), "cli.js");
109595
+ const cliPath = join4(dirname2(mcpPkgJsonPath), "cli.js");
109545
109596
  const args = [cliPath, "--isolated"];
109546
- if (this.headless) {
109547
- args.push("--headless");
109548
- }
109549
- if (this.userAgent) {
109550
- args.push("--user-agent", this.userAgent);
109551
- }
109552
- if (this.viewportSize) {
109553
- args.push(`--viewport-size=${this.viewportSize}`);
109554
- }
109555
- if (this.extraHttpHeaders) {
109556
- const os = await import("os");
109557
- const fsp = await import("fs/promises");
109558
- const cfg = {
109559
- browser: {
109560
- contextOptions: { extraHTTPHeaders: this.extraHttpHeaders }
109561
- }
109562
- };
109563
- this.mcpConfigPath = join3(os.tmpdir(), `pensar-mcp-${process.pid}-${Date.now()}.json`);
109564
- await fsp.writeFile(this.mcpConfigPath, JSON.stringify(cfg), {
109565
- encoding: "utf-8",
109566
- mode: 384
109567
- });
109568
- args.push("--config", this.mcpConfigPath);
109569
- }
109570
- if (process.getuid?.() === 0) {
109571
- args.push("--no-sandbox");
109572
- }
109597
+ await ensureCamoufox();
109598
+ if (!this.cachedCamouOptions) {
109599
+ this.cachedCamouOptions = await resolveCamoufoxLaunchOptions(this.headless);
109600
+ }
109601
+ const camou = this.cachedCamouOptions;
109602
+ const os = await import("os");
109603
+ const fsp = await import("fs/promises");
109604
+ const cfg = {
109605
+ browser: {
109606
+ browserName: "firefox",
109607
+ launchOptions: {
109608
+ executablePath: camou.executablePath,
109609
+ args: camou.args,
109610
+ firefoxUserPrefs: camou.firefoxUserPrefs,
109611
+ headless: camou.headless
109612
+ },
109613
+ ...this.extraHttpHeaders ? { contextOptions: { extraHTTPHeaders: this.extraHttpHeaders } } : {}
109614
+ }
109615
+ };
109616
+ this.mcpConfigPath = join4(os.tmpdir(), `pensar-mcp-${process.pid}-${Date.now()}.json`);
109617
+ await fsp.writeFile(this.mcpConfigPath, JSON.stringify(cfg), {
109618
+ encoding: "utf-8",
109619
+ mode: 384
109620
+ });
109621
+ args.push("--config", this.mcpConfigPath);
109573
109622
  const transport = new StdioClientTransport({
109574
109623
  command: "node",
109575
109624
  args,
109576
- stderr: "pipe"
109625
+ stderr: "pipe",
109626
+ env: Object.fromEntries(Object.entries(camou.env).map(([k, v]) => [k, String(v)]))
109577
109627
  });
109578
109628
  const client = new Client({
109579
109629
  name: "apex-browser-agent",
@@ -109607,29 +109657,28 @@ class PlaywrightMcpSession {
109607
109657
  this.disconnecting = true;
109608
109658
  const transport = this.mcpTransport;
109609
109659
  const client = this.mcpClient;
109660
+ const proc = this.mcpProcess;
109610
109661
  this.resetState();
109611
109662
  this.disconnecting = true;
109612
- if (client) {
109613
- try {
109614
- await client.close();
109615
- } catch {}
109616
- }
109617
- if (transport) {
109618
- try {
109619
- await transport.close();
109620
- } catch {}
109621
- }
109622
- this.forceKillProcess();
109623
- if (this.mcpConfigPath) {
109624
- const configPath = this.mcpConfigPath;
109625
- this.mcpConfigPath = null;
109663
+ this.forceKillProcess(proc);
109664
+ const CLOSE_TIMEOUT_MS = 5000;
109665
+ let closeTimer;
109666
+ await Promise.race([
109626
109667
  (async () => {
109627
109668
  try {
109628
- const fsp = await import("fs/promises");
109629
- await fsp.unlink(configPath);
109669
+ await client?.close();
109630
109670
  } catch {}
109631
- })();
109632
- }
109671
+ try {
109672
+ await transport?.close();
109673
+ } catch {}
109674
+ })(),
109675
+ new Promise((resolve) => {
109676
+ closeTimer = setTimeout(resolve, CLOSE_TIMEOUT_MS);
109677
+ })
109678
+ ]);
109679
+ if (closeTimer)
109680
+ clearTimeout(closeTimer);
109681
+ this.cleanupConfigFile();
109633
109682
  this.disconnecting = false;
109634
109683
  }
109635
109684
  isConnected() {
@@ -109953,8 +110002,8 @@ Target base URL: ${targetUrl}`,
109953
110002
  if (base64Data) {
109954
110003
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
109955
110004
  const screenshotFilename = `${filename}_${timestamp}.png`;
109956
- const screenshotPath = join3(evidenceDir, screenshotFilename);
109957
- const dir = dirname(screenshotPath);
110005
+ const screenshotPath = join4(evidenceDir, screenshotFilename);
110006
+ const dir = dirname2(screenshotPath);
109958
110007
  if (!existsSync2(dir)) {
109959
110008
  mkdirSync(dir, { recursive: true });
109960
110009
  }
@@ -110168,17 +110217,19 @@ The returned cookies can be formatted as a Cookie header for use with http_reque
110168
110217
  init_dist();
110169
110218
  init_zod();
110170
110219
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
110171
- import { dirname as dirname2, join as join4 } from "path";
110220
+ import { dirname as dirname3, join as join5 } from "path";
110172
110221
  var SANDBOX_PW_DIR = "/opt/sandbox-playwright";
110173
110222
  var SANDBOX_EVIDENCE_DIR = "/tmp/evidence";
110174
110223
  var SANDBOX_REFS_FILE = "/tmp/pw-refs.json";
110175
110224
  var SANDBOX_URL_FILE = "/tmp/pw-current-url";
110176
110225
  var SANDBOX_CONSOLE_FILE = "/tmp/pw-console-log.json";
110226
+ var SANDBOX_CAMOU_CACHE = "/tmp/pw-camou-opts.json";
110177
110227
  var RESULT_START = "__PW_RESULT__";
110178
110228
  var RESULT_END = "__PW_END__";
110179
110229
  var installationCache = new WeakMap;
110230
+ var browserSetupCache = new WeakMap;
110180
110231
  async function checkSandboxPlaywright(sandbox) {
110181
- const script = `try{require("playwright");console.log("OK")}catch(e){process.exit(1)}`;
110232
+ const script = `(async()=>{try{const{launchPath}=await import("camoufox-js/dist/pkgman.js");require("playwright-core");if(!require("fs").existsSync(launchPath()))process.exit(1);console.log("OK")}catch(e){process.exit(1)}})()`;
110182
110233
  const b64 = Buffer.from(script).toString("base64");
110183
110234
  await sandbox.execute(`mkdir -p ${SANDBOX_PW_DIR} && echo "${b64}" | base64 -d > ${SANDBOX_PW_DIR}/pw_check.js`, { timeout: 10 });
110184
110235
  const result = await sandbox.execute(`node ${SANDBOX_PW_DIR}/pw_check.js`, {
@@ -110189,35 +110240,33 @@ async function checkSandboxPlaywright(sandbox) {
110189
110240
  async function installSandboxPlaywright(sandbox) {
110190
110241
  await sandbox.execute(`mkdir -p ${SANDBOX_PW_DIR}`, { timeout: 10 });
110191
110242
  const isAlpine = (await sandbox.execute("which apk", { timeout: 5 })).success;
110243
+ if (isAlpine) {
110244
+ throw new Error("Camoufox requires a glibc-based sandbox image (Debian/Ubuntu); Alpine/musl is unsupported.");
110245
+ }
110192
110246
  const initResult = await sandbox.execute(`cd ${SANDBOX_PW_DIR} && npm init -y --silent 2>/dev/null`, { timeout: 30 });
110193
110247
  if (!initResult.success) {
110194
110248
  throw new Error(`Failed to init npm project in sandbox: ${initResult.stderr || initResult.stdout}`);
110195
110249
  }
110196
- const npmInstallCmd = isAlpine ? `cd ${SANDBOX_PW_DIR} && PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install playwright 2>&1` : `cd ${SANDBOX_PW_DIR} && npm install playwright 2>&1`;
110197
- const installResult = await sandbox.execute(npmInstallCmd, { timeout: 300 });
110250
+ const installResult = await sandbox.execute(`cd ${SANDBOX_PW_DIR} && npm install camoufox-js@0.11.1 playwright-core@1.53.1 2>&1`, { timeout: 300 });
110198
110251
  if (!installResult.success) {
110199
- throw new Error(`Failed to install Playwright in sandbox: ${installResult.stderr || installResult.stdout}`);
110252
+ throw new Error(`Failed to install camoufox-js in sandbox: ${installResult.stderr || installResult.stdout}`);
110200
110253
  }
110201
- if (isAlpine) {
110202
- const apkResult = await sandbox.execute("apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont 2>&1", { timeout: 120 });
110203
- if (!apkResult.success) {
110204
- throw new Error(`Failed to install Chromium via apk: ${apkResult.stderr || apkResult.stdout}`);
110205
- }
110206
- } else {
110207
- await sandbox.execute(`(sudo rm -f /etc/apt/sources.list.d/yarn.list /etc/apt/sources.list.d/yarn.sources 2>/dev/null || rm -f /etc/apt/sources.list.d/yarn.list /etc/apt/sources.list.d/yarn.sources 2>/dev/null || true)`, { timeout: 10 });
110208
- let browserResult;
110209
- for (let attempt = 1;attempt <= 3; attempt++) {
110210
- browserResult = await sandbox.execute(`cd ${SANDBOX_PW_DIR} && npx playwright install chromium --with-deps 2>&1`, { timeout: 300 });
110211
- if (browserResult.success)
110212
- break;
110213
- if (attempt < 3) {
110214
- await new Promise((r) => setTimeout(r, 3000));
110215
- }
110216
- }
110217
- if (!browserResult.success) {
110218
- throw new Error(`Failed to install Chromium in sandbox: ${browserResult.stderr || browserResult.stdout}`);
110254
+ const depsResult = await sandbox.execute(`rm -f /etc/apt/sources.list.d/yarn.list /etc/apt/sources.list.d/yarn.sources 2>/dev/null; cd ${SANDBOX_PW_DIR} && npx playwright@1.53.1 install-deps firefox 2>&1`, { timeout: 300 });
110255
+ if (!depsResult.success) {
110256
+ throw new Error(`Failed to install Firefox system deps in sandbox: ${depsResult.stderr || depsResult.stdout}`);
110257
+ }
110258
+ let fetchResult;
110259
+ for (let attempt = 1;attempt <= 3; attempt++) {
110260
+ fetchResult = await sandbox.execute(`cd ${SANDBOX_PW_DIR} && node ./node_modules/camoufox-js/dist/__main__.js fetch 2>&1`, { timeout: 300 });
110261
+ if (fetchResult.success)
110262
+ break;
110263
+ if (attempt < 3) {
110264
+ await new Promise((r) => setTimeout(r, 3000));
110219
110265
  }
110220
110266
  }
110267
+ if (!fetchResult.success) {
110268
+ throw new Error(`Failed to fetch Camoufox in sandbox: ${fetchResult.stderr || fetchResult.stdout}`);
110269
+ }
110221
110270
  }
110222
110271
  async function ensureSandboxPlaywright(sandbox) {
110223
110272
  let cached = installationCache.get(sandbox);
@@ -110238,42 +110287,59 @@ async function ensureSandboxPlaywright(sandbox) {
110238
110287
  }
110239
110288
  }
110240
110289
  async function ensureSandboxBrowser(sandbox) {
110241
- await sandbox.execute(`mkdir -p ${SANDBOX_EVIDENCE_DIR} /tmp/pw-user-data`, {
110242
- timeout: 5
110243
- });
110290
+ await sandbox.execute(`rm -rf /tmp/pw-user-data ${SANDBOX_CAMOU_CACHE}; mkdir -p ${SANDBOX_EVIDENCE_DIR} /tmp/pw-user-data`, { timeout: 5 });
110244
110291
  }
110245
110292
  async function ensureSandboxReady(sandbox) {
110246
110293
  await ensureSandboxPlaywright(sandbox);
110247
- await ensureSandboxBrowser(sandbox);
110294
+ let cached = browserSetupCache.get(sandbox);
110295
+ if (!cached) {
110296
+ cached = ensureSandboxBrowser(sandbox);
110297
+ browserSetupCache.set(sandbox, cached);
110298
+ try {
110299
+ await cached;
110300
+ } catch (error) {
110301
+ browserSetupCache.delete(sandbox);
110302
+ throw error;
110303
+ }
110304
+ } else {
110305
+ await cached;
110306
+ }
110248
110307
  }
110249
110308
  async function runPlaywrightScript(sandbox, body, timeout = 60, extraHttpHeaders) {
110250
110309
  const headersJson = extraHttpHeaders && Object.keys(extraHttpHeaders).length > 0 ? JSON.stringify(extraHttpHeaders) : "null";
110251
110310
  const script = `
110252
- const { chromium } = require('playwright');
110311
+ const { firefox } = require('playwright-core');
110253
110312
  const fs = require('fs');
110254
110313
 
110255
110314
  (async () => {
110315
+ const { launchOptions } = await import('camoufox-js');
110256
110316
  function resolve(value) {
110257
110317
  process.stdout.write('${RESULT_START}' + JSON.stringify(value) + '${RESULT_END}');
110258
110318
  }
110259
110319
 
110260
- // Prefer system Chromium (Alpine apk install) over Playwright's bundled binary.
110261
- let executablePath;
110262
- for (const p of ['/usr/bin/chromium-browser', '/usr/bin/chromium']) {
110263
- try { fs.accessSync(p, fs.constants.X_OK); executablePath = p; break; } catch {}
110264
- }
110265
-
110266
110320
  // Resolved per script invocation so /headers mutations take effect
110267
110321
  // on the next browser tool call.
110268
110322
  const __extraHeaders = ${headersJson};
110269
110323
 
110324
+ // Use the sandbox's virtual display if one is present — Camoufox is far less
110325
+ // detectable headful. Falls back to plain headless, which is still fully
110326
+ // fingerprint-spoofed (just a weaker stealth posture, never vanilla Chromium).
110327
+ const __headless = process.env.DISPLAY ? false : true;
110328
+ // Resolve Camoufox options once per sandbox session and cache to disk so
110329
+ // every tool call presents the same fingerprint on the shared profile dir.
110330
+ let __camou;
110331
+ try {
110332
+ __camou = JSON.parse(fs.readFileSync('${SANDBOX_CAMOU_CACHE}', 'utf-8'));
110333
+ } catch {
110334
+ __camou = await launchOptions({ ...${JSON.stringify(CAMOUFOX_OPTIONS)}, headless: __headless });
110335
+ fs.writeFileSync('${SANDBOX_CAMOU_CACHE}', JSON.stringify(__camou));
110336
+ }
110337
+
110270
110338
  let context;
110271
110339
  const __consoleMessages = [];
110272
110340
  try {
110273
- context = await chromium.launchPersistentContext('/tmp/pw-user-data', {
110274
- headless: true,
110275
- ...(executablePath ? { executablePath } : {}),
110276
- args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu'],
110341
+ context = await firefox.launchPersistentContext('/tmp/pw-user-data', {
110342
+ ...__camou,
110277
110343
  ...(__extraHeaders ? { extraHTTPHeaders: __extraHeaders } : {}),
110278
110344
  });
110279
110345
  const pages = context.pages();
@@ -110363,7 +110429,7 @@ var BrowserGetCookiesInput2 = exports_external.object({
110363
110429
  });
110364
110430
  function createSandboxBrowserTools(ctx) {
110365
110431
  const sandbox = ctx.sandbox;
110366
- const evidenceDir = join4(ctx.session.rootPath, "evidence");
110432
+ const evidenceDir = join5(ctx.session.rootPath, "evidence");
110367
110433
  const targetUrl = ctx.target ?? "";
110368
110434
  if (!existsSync3(evidenceDir)) {
110369
110435
  mkdirSync2(evidenceDir, { recursive: true });
@@ -110375,9 +110441,13 @@ function createSandboxBrowserTools(ctx) {
110375
110441
  }
110376
110442
  return setupPromise;
110377
110443
  }
110444
+ let scriptQueue = Promise.resolve();
110378
110445
  function runScript(body, timeout = 60) {
110379
110446
  const resolved = targetUrl ? resolveEffectiveHeaders(resolverSessionFromCtx(ctx), targetUrl) : ctx.session.config?.headers;
110380
- return runPlaywrightScript(sandbox, body, timeout, stripBrowserManagedHeaders(resolved));
110447
+ const headers = stripBrowserManagedHeaders(resolved);
110448
+ const next = scriptQueue.then(() => runPlaywrightScript(sandbox, body, timeout, headers));
110449
+ scriptQueue = next.then(() => {}, () => {});
110450
+ return next;
110381
110451
  }
110382
110452
  const browser_navigate = tool({
110383
110453
  description: `Navigate the browser to a URL to load and render a page.
@@ -110428,8 +110498,8 @@ Use this to document:
110428
110498
  resolve({ success: true, data: b64, sandboxPath: ${JSON.stringify(sandboxPath)} });
110429
110499
  `, 30);
110430
110500
  if (result.success && result.data) {
110431
- const localPath = join4(evidenceDir, screenshotFilename);
110432
- const dir = dirname2(localPath);
110501
+ const localPath = join5(evidenceDir, screenshotFilename);
110502
+ const dir = dirname3(localPath);
110433
110503
  if (!existsSync3(dir)) {
110434
110504
  mkdirSync2(dir, { recursive: true });
110435
110505
  }
@@ -110806,7 +110876,7 @@ The returned cookies can be formatted as a Cookie header for use with http_reque
110806
110876
  // src/core/agents/offSecAgent/tools/browserTools.ts
110807
110877
  init_dist();
110808
110878
  init_zod();
110809
- import { join as join5 } from "path";
110879
+ import { join as join6 } from "path";
110810
110880
  var BROWSER_TOOL_NAMES = [
110811
110881
  "browser_navigate",
110812
110882
  "browser_snapshot",
@@ -110818,7 +110888,7 @@ var BROWSER_TOOL_NAMES = [
110818
110888
  "browser_get_cookies"
110819
110889
  ];
110820
110890
  function createBrowserToolset(ctx) {
110821
- const tools = ctx.sandbox ? createSandboxBrowserTools(ctx) : createBrowserTools(ctx.target ?? "", join5(ctx.session.rootPath, "evidence"), "operator", undefined, ctx.abortSignal, undefined, undefined, undefined, ctx.browserSession);
110891
+ const tools = ctx.sandbox ? createSandboxBrowserTools(ctx) : createBrowserTools(ctx.target ?? "", join6(ctx.session.rootPath, "evidence"), "operator", undefined, ctx.abortSignal, undefined, undefined, undefined, ctx.browserSession);
110822
110892
  if (!ctx.credentialManager) {
110823
110893
  return tools;
110824
110894
  }
@@ -110944,7 +111014,7 @@ init_zod();
110944
111014
  init_structured();
110945
111015
  init_lazyLogger();
110946
111016
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
110947
- import { join as join6 } from "path";
111017
+ import { join as join7 } from "path";
110948
111018
  var log = scopedLogger(() => createLogger("complete_authentication"));
110949
111019
  var AUTH_DIR = "auth";
110950
111020
  var AUTH_DATA_FILENAME = "auth-data.json";
@@ -110988,11 +111058,11 @@ This tool marks the end of the authentication flow.`,
110988
111058
  log.info(`Authentication complete: ${result.success ? "SUCCESS" : "FAILED"}`);
110989
111059
  let authDataPath;
110990
111060
  try {
110991
- const authDir = join6(ctx.session.rootPath, AUTH_DIR);
111061
+ const authDir = join7(ctx.session.rootPath, AUTH_DIR);
110992
111062
  if (!existsSync4(authDir)) {
110993
111063
  mkdirSync3(authDir, { recursive: true });
110994
111064
  }
110995
- authDataPath = join6(authDir, AUTH_DATA_FILENAME);
111065
+ authDataPath = join7(authDir, AUTH_DATA_FILENAME);
110996
111066
  const authData = {
110997
111067
  authenticated: result.success,
110998
111068
  strategy: result.strategy || "unknown",
@@ -111209,7 +111279,7 @@ function crawlAuthenticated(ctx) {
111209
111279
  init_dist();
111210
111280
  init_zod();
111211
111281
  import { writeFileSync as writeFileSync5 } from "fs";
111212
- import { join as join7 } from "path";
111282
+ import { join as join8 } from "path";
111213
111283
  function createAttackSurfaceReport(ctx) {
111214
111284
  return tool({
111215
111285
  description: `Provide attack surface analysis results to the orchestrator agent.
@@ -111235,7 +111305,7 @@ Call this at the END of your analysis with:
111235
111305
  toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
111236
111306
  }),
111237
111307
  execute: async (results) => {
111238
- const resultsPath = join7(ctx.session.rootPath, "attack-surface-results.json");
111308
+ const resultsPath = join8(ctx.session.rootPath, "attack-surface-results.json");
111239
111309
  writeFileSync5(resultsPath, JSON.stringify(results, null, 2));
111240
111310
  return {
111241
111311
  success: true,
@@ -111254,7 +111324,7 @@ init_structured();
111254
111324
  init_lazyLogger();
111255
111325
  import { existsSync as existsSync5 } from "fs";
111256
111326
  import { mkdir, writeFile } from "fs/promises";
111257
- import { dirname as dirname3, isAbsolute, resolve } from "path";
111327
+ import { dirname as dirname4, isAbsolute, resolve } from "path";
111258
111328
  var log3 = scopedLogger(() => createLogger("create_file"));
111259
111329
  var createFileInputSchema = exports_external.object({
111260
111330
  path: exports_external.string().describe("Absolute or relative path for the new file"),
@@ -111298,7 +111368,7 @@ async function executeLocalCreate(filePath, content, overwrite) {
111298
111368
  path: filePath
111299
111369
  };
111300
111370
  }
111301
- const dir = dirname3(filePath);
111371
+ const dir = dirname4(filePath);
111302
111372
  log3.debug(`local: mkdir ${dir}`);
111303
111373
  await mkdir(dir, { recursive: true });
111304
111374
  log3.debug(`local: mkdir done, writing ${content.length} bytes`);
@@ -111332,7 +111402,7 @@ async function executeSandboxCreate(ctx, filePath, content, overwrite) {
111332
111402
  };
111333
111403
  }
111334
111404
  }
111335
- const dirPath = dirname3(filePath);
111405
+ const dirPath = dirname4(filePath);
111336
111406
  await ctx.sandbox.execute(`mkdir -p "${dirPath}"`);
111337
111407
  const base64Content = Buffer.from(content).toString("base64");
111338
111408
  const writeResult = await ctx.sandbox.execute(`echo "${base64Content}" | base64 -d > "${filePath}"`);
@@ -111363,16 +111433,16 @@ init_zod();
111363
111433
 
111364
111434
  // src/core/tasks/index.ts
111365
111435
  import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync, writeFileSync as writeFileSync6 } from "fs";
111366
- import { join as join8 } from "path";
111436
+ import { join as join9 } from "path";
111367
111437
  var HWM_FILENAME = "_hwm.json";
111368
111438
  function ensureDir(dir) {
111369
111439
  mkdirSync4(dir, { recursive: true });
111370
111440
  }
111371
111441
  function taskPath(tasksDir, id) {
111372
- return join8(tasksDir, `${id}.json`);
111442
+ return join9(tasksDir, `${id}.json`);
111373
111443
  }
111374
111444
  function hwmPath(tasksDir) {
111375
- return join8(tasksDir, HWM_FILENAME);
111445
+ return join9(tasksDir, HWM_FILENAME);
111376
111446
  }
111377
111447
  function readHighWaterMark(tasksDir) {
111378
111448
  try {
@@ -111445,7 +111515,7 @@ function listTasks(tasksDir, filter) {
111445
111515
  if (!file.endsWith(".json") || file === HWM_FILENAME)
111446
111516
  continue;
111447
111517
  try {
111448
- const raw = readFileSync(join8(tasksDir, file), "utf-8");
111518
+ const raw = readFileSync(join9(tasksDir, file), "utf-8");
111449
111519
  const task = JSON.parse(raw);
111450
111520
  if (filter?.status && task.status !== filter.status)
111451
111521
  continue;
@@ -111543,7 +111613,7 @@ init_dist();
111543
111613
  init_zod();
111544
111614
  init_credentials();
111545
111615
  import { writeFileSync as writeFileSync7 } from "fs";
111546
- import { join as join9 } from "path";
111616
+ import { join as join10 } from "path";
111547
111617
  init_structured();
111548
111618
  init_lazyLogger();
111549
111619
  var log4 = scopedLogger(() => createLogger("delegate_auth"));
@@ -111709,7 +111779,7 @@ When to use delegate_to_auth_subagent vs authenticate_session:
111709
111779
  if (credentials) {
111710
111780
  ctx.session.credentialManager.addFromAuthCredentials(credentials);
111711
111781
  }
111712
- const { runAuthenticationAgent } = await import("./authentication-c0aj9zaz.js");
111782
+ const { runAuthenticationAgent } = await import("./authentication-3p84dzrr.js");
111713
111783
  const localBus = new AgentEventBus;
111714
111784
  AgentEventBus.attachChild(localBus, ctx.eventBus, subagentId);
111715
111785
  const result = await runAuthenticationAgent({
@@ -111728,7 +111798,7 @@ When to use delegate_to_auth_subagent vs authenticate_session:
111728
111798
  parentSubagentId: ctx.subagentId
111729
111799
  });
111730
111800
  if (result.success) {
111731
- const sessionInfoPath = join9(ctx.session.rootPath, "session-info.json");
111801
+ const sessionInfoPath = join10(ctx.session.rootPath, "session-info.json");
111732
111802
  const sessionInfo = {
111733
111803
  authenticated: true,
111734
111804
  username: username || "via_subagent",
@@ -111923,7 +111993,7 @@ function detectBarrier(bodyLower, statusCode) {
111923
111993
  init_dist();
111924
111994
  init_zod();
111925
111995
  import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "fs";
111926
- import { join as join10 } from "path";
111996
+ import { join as join11 } from "path";
111927
111997
  function sanitizeName(name) {
111928
111998
  return name.toLowerCase().replace(/[^a-z0-9-_.]/g, "_");
111929
111999
  }
@@ -111962,7 +112032,7 @@ function validateDomainUrl(value) {
111962
112032
  }
111963
112033
  }
111964
112034
  function documentApp(ctx) {
111965
- const baseAppsPath = join10(ctx.session.rootPath, "apps");
112035
+ const baseAppsPath = join11(ctx.session.rootPath, "apps");
111966
112036
  return tool({
111967
112037
  description: `Document a discovered application during attack surface analysis.
111968
112038
 
@@ -112015,7 +112085,7 @@ Each application creates a JSON file in the apps directory for tracking and anal
112015
112085
  const sanitizedName = sanitizeName(input.appName);
112016
112086
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
112017
112087
  const filename = `app_${sanitizedName}_${timestamp}.json`;
112018
- const filepath = join10(baseAppsPath, filename);
112088
+ const filepath = join11(baseAppsPath, filename);
112019
112089
  const appRecord = {
112020
112090
  ...input,
112021
112091
  discoveredAt: new Date().toISOString(),
@@ -112039,7 +112109,7 @@ Each application creates a JSON file in the apps directory for tracking and anal
112039
112109
  init_dist();
112040
112110
  init_zod();
112041
112111
  import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "fs";
112042
- import { join as join11 } from "path";
112112
+ import { join as join12 } from "path";
112043
112113
 
112044
112114
  // src/core/agents/specialized/attackSurface/blackboxRiskScoring.ts
112045
112115
  var RISK_LEVEL_BASE_SCORE = {
@@ -112389,7 +112459,7 @@ async function generateThreatModelForEndpoint(ctx, input) {
112389
112459
  return threatModelLimiter(async () => {
112390
112460
  if (ctx.abortSignal?.aborted)
112391
112461
  return null;
112392
- const { CodeAgent } = await import("./agent-4g69jwmq.js");
112462
+ const { CodeAgent } = await import("./agent-ssm6x6qs.js");
112393
112463
  const subagentId = `threat-model-${sanitize(input.appName)}-${sanitize(input.routePath)}`;
112394
112464
  ctx.eventBus?.emit("subagent-spawn", {
112395
112465
  subagentId,
@@ -112697,7 +112767,7 @@ var documentEndpointInputSchema = exports_external.object({
112697
112767
  toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
112698
112768
  });
112699
112769
  function documentEndpoint(ctx) {
112700
- const baseAssetsPath = join11(ctx.session.rootPath, "assets");
112770
+ const baseAssetsPath = join12(ctx.session.rootPath, "assets");
112701
112771
  return tool({
112702
112772
  description: `Document a discovered endpoint during attack surface analysis.
112703
112773
 
@@ -112743,14 +112813,14 @@ Each endpoint creates a JSON file in the assets directory for tracking and analy
112743
112813
  message: `routePath "${input.routePath}" is a full URL. The domain is already stored on the parent application. ` + `Use a path or access pattern instead (e.g. "/api/users", "/{objectKey}?X-Amz-Signature={sig}", "arn:aws:s3:::bucket-name").`
112744
112814
  };
112745
112815
  }
112746
- const targetDir = join11(baseAssetsPath, sanitizeName2(input.appName));
112816
+ const targetDir = join12(baseAssetsPath, sanitizeName2(input.appName));
112747
112817
  if (!existsSync7(targetDir)) {
112748
112818
  mkdirSync6(targetDir, { recursive: true });
112749
112819
  }
112750
112820
  const sanitizedPath = sanitizeName2(input.routePath);
112751
112821
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
112752
112822
  const filename = `asset_${sanitizedPath}_${timestamp}.json`;
112753
- const filepath = join11(targetDir, filename);
112823
+ const filepath = join12(targetDir, filename);
112754
112824
  const heuristicRiskScore = computeBlackboxRiskScore(input.riskLevel, "endpoint", {
112755
112825
  url: input.routePath,
112756
112826
  method: input.method,
@@ -112828,7 +112898,7 @@ import {
112828
112898
  unlinkSync,
112829
112899
  writeFileSync as writeFileSync10
112830
112900
  } from "fs";
112831
- import { join as join12 } from "path";
112901
+ import { join as join13 } from "path";
112832
112902
 
112833
112903
  // src/lib/cwe/types.ts
112834
112904
  init_zod();
@@ -114063,6 +114133,30 @@ var CVSS_SCORER_SYSTEM_PROMPT = `You are a CVSS 4.0 scoring specialist. Your tas
114063
114133
  - If production-like or confirmed unintentional: **E:A**
114064
114134
  - Severity: typically **HIGH-CRITICAL** for real bypasses, **MEDIUM** for intentional test credentials in sandbox
114065
114135
 
114136
+ ### Denial of Service / Resource Exhaustion (unbounded input, algorithmic complexity, amplification)
114137
+ - Typically: **AV:N, AC:L, AT:N, PR** varies (often **L** for authenticated endpoints), **UI:N**
114138
+ - **VC:N, VI:N** — DoS has no confidentiality/integrity impact unless separately demonstrated
114139
+ - **VA** must reflect what the evidence actually proves, not the worst case it implies:
114140
+ - **VA:L** when a single request degrades performance or ties up one worker temporarily (the typical demonstrated case). A slow response (e.g. several seconds) for one request is **VA:L**, not **VA:H**.
114141
+ - **VA:H** ONLY when the POC demonstrates a *sustained, service-wide* outage — e.g. proven worker-pool exhaustion at realistic concurrency, or the service staying unavailable to other users. Extrapolating "5-10 concurrent requests would exhaust the pool" is theoretical and does NOT justify **VA:H** unless the POC actually showed it.
114142
+ - **E:P** when only the resource-consumption gap was demonstrated; **E:A** only if an actual outage was produced and observed during testing.
114143
+ - Severity range: typically **LOW-MEDIUM**. Reserve **HIGH** for a demonstrated full outage with no easy mitigation.
114144
+
114145
+ ### OAuth Flow Weaknesses (missing PKCE, weak/predictable state, state integrity)
114146
+ - These findings describe a *missing or weak control* in an auth flow. The impact (account/session takeover) is only realizable when chained with a *separate* capability: an authorization-code interception path (open redirect, referrer leakage, network MITM) or a forgeable/guessable state.
114147
+ - **Do NOT credit full VC:H/VI:H on the vulnerable system unless the interception/forgery chain was actually demonstrated end to end.** If only the absence of PKCE (or a missing signature) was shown, the direct impact on the vulnerable system is limited → prefer **VC:L/VI:L or N** and **E:P**, with **AC:H** and/or **AT:P** to reflect the required preconditions.
114148
+ - If a state parameter already binds to the initiating user (e.g. a server-side userId check on return), missing PKCE/signing is largely a **defense-in-depth / best-practice** gap → **LOW** (or informational when state is also unguessable and validated).
114149
+ - Severity range: typically **LOW** for "missing PKCE / unsigned state" in isolation; elevate only when a working interception or CSRF-via-state-forgery chain is demonstrated.
114150
+
114151
+ ## Severity Calibration Principles
114152
+
114153
+ Apply these across every vulnerability class. They override the worst-case instinct.
114154
+
114155
+ - **Score what was demonstrated, not what could theoretically follow.** The CVSS reflects the impact the POC actually proved on the target, not the maximal impact the weakness might enable in a different configuration or when chained with an undiscovered bug.
114156
+ - **Chained / conditional exploitation:** when realizing impact depends on a *separate, undemonstrated* vulnerability or attacker-favorable precondition (interception position, a second bug, a guessed identifier), reflect that in the exploitability metrics (**AC:H**, **AT:P**, exploit maturity **E:P/U**) and do NOT assign full **VC:H/VI:H/VA:H** to the vulnerable system. A finding whose impact is entirely contingent on a missing precondition trends **LOW**.
114157
+ - **Security-boundary bypass without proven sensitive exposure:** when a test shows an access-control/authorization boundary was bypassed but the data actually returned is non-sensitive or same-tenant/expected-visible, score the *demonstrated* confidentiality impact (often **VC:L** or **VC:N**), not the boundary's theoretical worst case. Note explicitly in the reasoning what data was (and was not) exposed.
114158
+ - **Cross-tenant vs same-tenant matters:** confirmed cross-tenant/cross-user exposure of private data justifies **VC:H**; same-tenant data the identity could plausibly already see does not. If tenancy is unproven, say so and score conservatively.
114159
+
114066
114160
  ## Analysis Instructions
114067
114161
 
114068
114162
  1. Read the finding description and evidence carefully
@@ -114070,8 +114164,8 @@ var CVSS_SCORER_SYSTEM_PROMPT = `You are a CVSS 4.0 scoring specialist. Your tas
114070
114164
  3. Assess complexity based on whether special conditions were needed
114071
114165
  4. Determine privileges based on authentication requirements
114072
114166
  5. Evaluate user interaction based on exploit mechanics
114073
- 6. Assess impact on both the vulnerable system AND potential subsequent systems
114074
- 7. Since a POC exists and confirmed the vulnerability, E should typically be 'A'
114167
+ 6. Assess impact on both the vulnerable system AND potential subsequent systems — score what the evidence proves, not the worst case it implies (see Severity Calibration Principles)
114168
+ 7. Exploit Maturity (E): use **A (Attacked)** only when the POC demonstrated the actual security impact (data accessed, outage produced, session taken over). When the POC only proved the *gap* exists — a missing control, a slow response, a boundary bypass without confirmed sensitive exposure — use **P (POC)**. Do not default to **A** merely because a script ran and exited 0.
114075
114169
 
114076
114170
  Always provide brief reasoning explaining your key decisions.
114077
114171
 
@@ -114103,6 +114197,8 @@ In addition to CVSS metrics, assign one or more CWE identifiers to the finding.
114103
114197
  | Hardcoded Credentials | CWE-798 | CWE-259 (Hard-coded Password), CWE-321 (Hard-coded Crypto Key) |
114104
114198
  | Captcha Bypass | CWE-804 | CWE-837 (Improper Enforcement of Single Access) |
114105
114199
  | Missing Security Headers | CWE-1021 | CWE-693 (Protection Mechanism Failure), CWE-16 (Configuration) |
114200
+ | Denial of Service / Resource Exhaustion | CWE-400 | CWE-770 (Allocation Without Limits), CWE-1333 (Inefficient Regex) |
114201
+ | OAuth Flow Weakness (missing PKCE, weak state) | CWE-352 | CWE-345 (Insufficient Verification of Authenticity), CWE-1275 (Sensitive Cookie Weak Attributes) |
114106
114202
 
114107
114203
  ### CWE Assignment Rules
114108
114204
 
@@ -114239,7 +114335,7 @@ init_lazyLogger();
114239
114335
  var log6 = scopedLogger(() => createLogger("FindingJudge"));
114240
114336
  async function judgeFinding(input, ctx) {
114241
114337
  try {
114242
- const { FindingJudgeAgent } = await import("./agent-6nhperp2.js");
114338
+ const { FindingJudgeAgent } = await import("./agent-1zfxfgfr.js");
114243
114339
  const agent = new FindingJudgeAgent({
114244
114340
  finding: input,
114245
114341
  model: ctx.model,
@@ -114529,14 +114625,14 @@ CRITICAL RULES — READ BEFORE CALLING:
114529
114625
  description: `POC execution output for ${filename}`
114530
114626
  });
114531
114627
  const timestamp = new Date().toISOString();
114532
- const outputDir = isVulnerability ? session.findingsPath : join12(session.rootPath, "informational");
114628
+ const outputDir = isVulnerability ? session.findingsPath : join13(session.rootPath, "informational");
114533
114629
  if (!isVulnerability) {
114534
114630
  mkdirSync7(outputDir, { recursive: true });
114535
114631
  }
114536
114632
  let evidenceForPrompt = materializedEvidence;
114537
114633
  if (materializedEvidence.length > EVIDENCE_FILE_THRESHOLD) {
114538
114634
  const evidenceFilename = `${timestamp.split("T")[0]}-${slugify2(input.title, 40)}-evidence.txt`;
114539
- const evidenceFilePath = join12(outputDir, evidenceFilename);
114635
+ const evidenceFilePath = join13(outputDir, evidenceFilename);
114540
114636
  writeFileSync10(evidenceFilePath, materializedEvidence);
114541
114637
  evidenceForPrompt = materializedEvidence.substring(0, EVIDENCE_FILE_THRESHOLD) + `
114542
114638
  ... [truncated — full output saved to ${evidenceFilename}]`;
@@ -114678,8 +114774,8 @@ CRITICAL RULES — READ BEFORE CALLING:
114678
114774
  const findingId = `${timestamp.split("T")[0]}-${slugify2(finding.title, 50)}`;
114679
114775
  const jsonFilename = `${findingId}.json`;
114680
114776
  const mdFilename = `${findingId}.md`;
114681
- const jsonPath = join12(outputDir, jsonFilename);
114682
- const mdPath = join12(outputDir, mdFilename);
114777
+ const jsonPath = join13(outputDir, jsonFilename);
114778
+ const mdPath = join13(outputDir, mdFilename);
114683
114779
  try {
114684
114780
  writeFileSync10(jsonPath, JSON.stringify(findingWithMeta, null, 2));
114685
114781
  const cvssSection = cvssWarning ? `## CVSS 4.0 Assessment
@@ -114762,7 +114858,7 @@ ${finding.references}` : ""}
114762
114858
  `;
114763
114859
  writeFileSync10(mdPath, markdown);
114764
114860
  if (isVulnerability) {
114765
- const summaryPath = join12(session.rootPath, "findings-summary.md");
114861
+ const summaryPath = join13(session.rootPath, "findings-summary.md");
114766
114862
  const cweTag = cvssResult.cwes?.length ? ` (${cvssResult.cwes.map((c) => c.id).join(", ")})` : "";
114767
114863
  const cvssTag = cvssWarning ? "(estimated)" : `(CVSS ${cvssResult.score})`;
114768
114864
  const summaryEntry = `- [${finding.severity}] ${cvssTag}${cweTag} ${finding.title} - \`findings/${mdFilename}\`
@@ -114820,7 +114916,7 @@ async function executeLocalPoc(ctx, input) {
114820
114916
  mkdirSync7(pocsPath, { recursive: true });
114821
114917
  }
114822
114918
  const { filename, pocContent } = preparePoc(input);
114823
- const pocPath = join12(pocsPath, filename);
114919
+ const pocPath = join13(pocsPath, filename);
114824
114920
  writeFileSync10(pocPath, pocContent);
114825
114921
  chmodSync(pocPath, 493);
114826
114922
  const { stdout, stderr, exitCode } = await runScript(POC_RUNNERS[input.pocType], pocPath, 60000, ctx.abortSignal);
@@ -114838,7 +114934,7 @@ async function executeSandboxPoc(ctx, input) {
114838
114934
  if (!existsSync8(localPocsPath)) {
114839
114935
  mkdirSync7(localPocsPath, { recursive: true });
114840
114936
  }
114841
- const localPocPath = join12(localPocsPath, filename);
114937
+ const localPocPath = join13(localPocsPath, filename);
114842
114938
  writeFileSync10(localPocPath, pocContent);
114843
114939
  const sandboxPocPath = `/tmp/pocs/${filename}`;
114844
114940
  const base64Content = Buffer.from(pocContent).toString("base64");
@@ -114871,10 +114967,10 @@ POC file has been deleted.`,
114871
114967
  }
114872
114968
  function cleanupPocFiles(ctx, filename) {
114873
114969
  try {
114874
- unlinkSync(join12(ctx.session.pocsPath, filename));
114970
+ unlinkSync(join13(ctx.session.pocsPath, filename));
114875
114971
  } catch {}
114876
114972
  try {
114877
- unlinkSync(join12(ctx.session.pocsPath, `${filename}.output.json`));
114973
+ unlinkSync(join13(ctx.session.pocsPath, `${filename}.output.json`));
114878
114974
  } catch {}
114879
114975
  }
114880
114976
  function sanitizeFilename(str) {
@@ -114935,7 +115031,7 @@ ${commentChar} Created: ${new Date().toISOString()}
114935
115031
  }
114936
115032
  function writePocOutputSidecar(pocsPath, filename, stdout, stderr, exitCode, description) {
114937
115033
  try {
114938
- const outputPath = join12(pocsPath, `${filename}.output.json`);
115034
+ const outputPath = join13(pocsPath, `${filename}.output.json`);
114939
115035
  writeFileSync10(outputPath, JSON.stringify({
114940
115036
  stdout,
114941
115037
  stderr,
@@ -118250,7 +118346,7 @@ var SEND_EMAIL_TOOL_NAME = "send_email";
118250
118346
  init_dist();
118251
118347
  init_zod();
118252
118348
  import { existsSync as existsSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync11 } from "fs";
118253
- import { join as join13 } from "path";
118349
+ import { join as join14 } from "path";
118254
118350
  var MAX_INLINE = 50000;
118255
118351
  var MS_TIMEOUT_THRESHOLD = 1e4;
118256
118352
  var executeCommandInputSchema = exports_external.object({
@@ -118272,13 +118368,13 @@ function maybeSaveFullOutput(raw, ctx) {
118272
118368
  if (raw.length <= MAX_INLINE) {
118273
118369
  return { text: raw || "(no output)" };
118274
118370
  }
118275
- const outputDir = join13(ctx.session.logsPath, "cmd-output");
118371
+ const outputDir = join14(ctx.session.logsPath, "cmd-output");
118276
118372
  if (!existsSync9(outputDir)) {
118277
118373
  mkdirSync8(outputDir, { recursive: true });
118278
118374
  }
118279
118375
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
118280
118376
  const filename = `output-${ts}.txt`;
118281
- const filePath = join13(outputDir, filename);
118377
+ const filePath = join14(outputDir, filename);
118282
118378
  try {
118283
118379
  writeFileSync11(filePath, raw);
118284
118380
  } catch {
@@ -118765,7 +118861,7 @@ search with flags or a more specific directory if results are truncated.`,
118765
118861
  init_dist();
118766
118862
  init_zod();
118767
118863
  import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync12 } from "fs";
118768
- import { join as join14 } from "path";
118864
+ import { join as join15 } from "path";
118769
118865
  var MAX_INLINE_BODY = 5000;
118770
118866
  var httpRequestInputSchema = exports_external.object({
118771
118867
  url: exports_external.string().describe("The URL to request"),
@@ -118779,13 +118875,13 @@ var httpRequestInputSchema = exports_external.object({
118779
118875
  function maybeSaveBody(body, ctx) {
118780
118876
  if (body.length <= MAX_INLINE_BODY)
118781
118877
  return { text: body };
118782
- const outputDir = join14(ctx.session.logsPath, "http-responses");
118878
+ const outputDir = join15(ctx.session.logsPath, "http-responses");
118783
118879
  if (!existsSync10(outputDir)) {
118784
118880
  mkdirSync9(outputDir, { recursive: true });
118785
118881
  }
118786
118882
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
118787
118883
  const filename = `response-${ts}.txt`;
118788
- const filePath = join14(outputDir, filename);
118884
+ const filePath = join15(outputDir, filename);
118789
118885
  try {
118790
118886
  writeFileSync12(filePath, body);
118791
118887
  } catch {
@@ -119024,7 +119120,7 @@ async function executeSandboxHttpRequest(ctx, opts) {
119024
119120
  init_dist();
119025
119121
  init_zod();
119026
119122
  import { readdir, stat } from "fs/promises";
119027
- import { isAbsolute as isAbsolute2, join as join15, relative, resolve as resolve2 } from "path";
119123
+ import { isAbsolute as isAbsolute2, join as join16, relative, resolve as resolve2 } from "path";
119028
119124
  var listFilesInputSchema = exports_external.object({
119029
119125
  directory: exports_external.string().optional().describe("Absolute or relative path to the directory to list (defaults to current working directory)"),
119030
119126
  recursive: exports_external.boolean().optional().describe("If true, list files recursively (default: false)"),
@@ -119044,7 +119140,7 @@ async function listRecursive(dir, maxEntries) {
119044
119140
  }
119045
119141
  for (const entry of entries) {
119046
119142
  total++;
119047
- const fullPath = join15(current, entry.name);
119143
+ const fullPath = join16(current, entry.name);
119048
119144
  if (entry.isDirectory()) {
119049
119145
  if (results.length < maxEntries)
119050
119146
  results.push(`${fullPath}/`);
@@ -119107,7 +119203,7 @@ Each directory entry is suffixed with "/" for easy identification.`,
119107
119203
  }
119108
119204
  const entries = await readdir(dir, { withFileTypes: true });
119109
119205
  const fullPaths = entries.map((e) => {
119110
- const name = join15(dir, e.name);
119206
+ const name = join16(dir, e.name);
119111
119207
  return e.isDirectory() ? `${name}/` : name;
119112
119208
  });
119113
119209
  const capped = fullPaths.slice(0, MAX_NON_RECURSIVE);
@@ -119249,9 +119345,9 @@ import {
119249
119345
  unlinkSync as unlinkSync2
119250
119346
  } from "fs";
119251
119347
  import { tmpdir } from "os";
119252
- import { join as join16 } from "path";
119348
+ import { join as join17 } from "path";
119253
119349
  var MAX_BUFFER = 5000000;
119254
- var APEX_TMP_ROOT = join16(tmpdir(), `apex-shell-${process.pid}`);
119350
+ var APEX_TMP_ROOT = join17(tmpdir(), `apex-shell-${process.pid}`);
119255
119351
  try {
119256
119352
  mkdirSync10(APEX_TMP_ROOT, { recursive: true });
119257
119353
  } catch {}
@@ -119503,8 +119599,8 @@ class PersistentShell {
119503
119599
  const marker = `__APEX_${nonceHex}__`;
119504
119600
  const exitMarkerPrefix = `${marker}_EXIT_`;
119505
119601
  const cutoverMarker = `${marker}_CUTOVER`;
119506
- const outPath = join16(APEX_TMP_ROOT, `${nonceHex}.out`);
119507
- const errPath = join16(APEX_TMP_ROOT, `${nonceHex}.err`);
119602
+ const outPath = join17(APEX_TMP_ROOT, `${nonceHex}.out`);
119603
+ const errPath = join17(APEX_TMP_ROOT, `${nonceHex}.err`);
119508
119604
  const pending = {
119509
119605
  streamedStdout: "",
119510
119606
  authoritativeStdout: "",
@@ -119923,7 +120019,7 @@ Returns discovered endpoints and recommended login approach.`,
119923
120019
  init_dist();
119924
120020
  init_zod();
119925
120021
  import { writeFileSync as writeFileSync13 } from "fs";
119926
- import { join as join17 } from "path";
120022
+ import { join as join18 } from "path";
119927
120023
  function provideComparisonResults(ctx) {
119928
120024
  return tool({
119929
120025
  description: `Provide the final comparison results with matched, missed, and extra findings.
@@ -119969,7 +120065,7 @@ Results will be saved to: comparison-results.json in the session directory.`,
119969
120065
  recall,
119970
120066
  precision
119971
120067
  };
119972
- const resultsPath = join17(ctx.session.rootPath, "comparison-results.json");
120068
+ const resultsPath = join18(ctx.session.rootPath, "comparison-results.json");
119973
120069
  writeFileSync13(resultsPath, JSON.stringify(result, null, 2));
119974
120070
  return {
119975
120071
  success: true,
@@ -120123,7 +120219,7 @@ should be passed directly to spawn_pentest_swarm for deep testing.`,
120123
120219
  });
120124
120220
  if (cwd) {
120125
120221
  try {
120126
- const { WhiteboxAttackSurfaceAgent } = await import("./index-3cbcjqw1.js");
120222
+ const { WhiteboxAttackSurfaceAgent } = await import("./index-kjky8ack.js");
120127
120223
  const localBus = new AgentEventBus;
120128
120224
  AgentEventBus.attachChild(localBus, ctx.eventBus, subagentId);
120129
120225
  const agent = new WhiteboxAttackSurfaceAgent({
@@ -120173,7 +120269,7 @@ should be passed directly to spawn_pentest_swarm for deep testing.`,
120173
120269
  }
120174
120270
  }
120175
120271
  try {
120176
- const { BlackboxAttackSurfaceAgent } = await import("./blackboxAgent-sgph70e4.js");
120272
+ const { BlackboxAttackSurfaceAgent } = await import("./blackboxAgent-dn68r23n.js");
120177
120273
  const localBus = new AgentEventBus;
120178
120274
  AgentEventBus.attachChild(localBus, ctx.eventBus, subagentId);
120179
120275
  const agent = new BlackboxAttackSurfaceAgent({
@@ -120251,7 +120347,7 @@ Omit \`cwd\` for blackbox mode (live target probing only).`,
120251
120347
  toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
120252
120348
  }),
120253
120349
  execute: async ({ target, cwd }) => {
120254
- const { runPentestWorkflow: workflow } = await import("./pentest-2vsjf0j8.js");
120350
+ const { runPentestWorkflow: workflow } = await import("./pentest-043y2x03.js");
120255
120351
  if (!ctx.model) {
120256
120352
  return {
120257
120353
  success: false,
@@ -120356,11 +120452,11 @@ init_zod();
120356
120452
 
120357
120453
  // src/core/whitebox/adapters.ts
120358
120454
  import { existsSync as existsSync12 } from "node:fs";
120359
- import { join as join19 } from "node:path";
120455
+ import { join as join20 } from "node:path";
120360
120456
 
120361
120457
  // src/core/whitebox/artifacts.ts
120362
120458
  import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
120363
- import { basename as basename2, join as join18 } from "node:path";
120459
+ import { basename as basename2, join as join19 } from "node:path";
120364
120460
 
120365
120461
  // src/core/whitebox/paths.ts
120366
120462
  import { existsSync as existsSync11, realpathSync } from "node:fs";
@@ -120421,10 +120517,10 @@ function safeName(value) {
120421
120517
  return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/\.{2,}/g, ".").replace(/^-+|-+$/g, "").slice(0, 80);
120422
120518
  }
120423
120519
  function getWhiteboxLogsDir(session) {
120424
- return join18(session.logsPath, "whitebox");
120520
+ return join19(session.logsPath, "whitebox");
120425
120521
  }
120426
120522
  function getWhiteboxScratchDir(session) {
120427
- return join18(session.scratchpadPath, "whitebox");
120523
+ return join19(session.scratchpadPath, "whitebox");
120428
120524
  }
120429
120525
  async function ensureWhiteboxDirs(session) {
120430
120526
  await Promise.all([
@@ -120437,7 +120533,7 @@ async function writeWhiteboxArtifact(input) {
120437
120533
  const baseDir = input.area === "scratchpad" ? getWhiteboxScratchDir(input.session) : getWhiteboxLogsDir(input.session);
120438
120534
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
120439
120535
  const filename = `${timestamp}-${safeName(input.name)}${input.extension ?? ".txt"}`;
120440
- const absolutePath = join18(baseDir, filename);
120536
+ const absolutePath = join19(baseDir, filename);
120441
120537
  await writeFile2(absolutePath, input.content, "utf-8");
120442
120538
  const relativeRoot = input.area === "scratchpad" ? "scratchpad/whitebox" : "logs/whitebox";
120443
120539
  return {
@@ -120679,7 +120775,7 @@ var WHITEBOX_SCAN_ADAPTERS = [
120679
120775
  id: "npm-audit",
120680
120776
  kind: "dependencies",
120681
120777
  tool: "npm",
120682
- detect: (profile) => hasTool(profile, "npm") && (profile.packageManagers.includes("npm") || existsSync12(join19(profile.rootPath, "package-lock.json"))),
120778
+ detect: (profile) => hasTool(profile, "npm") && (profile.packageManagers.includes("npm") || existsSync12(join20(profile.rootPath, "package-lock.json"))),
120683
120779
  buildCommand: () => ["npm", "audit", "--json"],
120684
120780
  parseSummary: (raw) => simpleLineFindings("npm audit", raw)
120685
120781
  },
@@ -120788,7 +120884,7 @@ ${raw.stderr}` : "");
120788
120884
  // src/core/whitebox/candidates.ts
120789
120885
  import { existsSync as existsSync13 } from "node:fs";
120790
120886
  import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
120791
- import { join as join20 } from "node:path";
120887
+ import { join as join21 } from "node:path";
120792
120888
  var CANDIDATES_FILE = "candidates.json";
120793
120889
  var writeLocks = new Map;
120794
120890
  function withCandidateLock(session, fn) {
@@ -120815,10 +120911,10 @@ var ALLOWED_TRANSITIONS = {
120815
120911
  deferred: ["hypothesis", "investigating"]
120816
120912
  };
120817
120913
  function candidatesDir(session) {
120818
- return join20(session.scratchpadPath, "whitebox");
120914
+ return join21(session.scratchpadPath, "whitebox");
120819
120915
  }
120820
120916
  function candidatesPath(session) {
120821
- return join20(candidatesDir(session), CANDIDATES_FILE);
120917
+ return join21(candidatesDir(session), CANDIDATES_FILE);
120822
120918
  }
120823
120919
  function makeId2(title) {
120824
120920
  const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 48);
@@ -121288,7 +121384,7 @@ import {
121288
121384
  readSync as readSync2,
121289
121385
  statSync as statSync3
121290
121386
  } from "node:fs";
121291
- import { join as join21 } from "node:path";
121387
+ import { join as join22 } from "node:path";
121292
121388
  var MAX_JOB_LOG_INLINE = 40000;
121293
121389
  var MAX_JOB_LOG_BYTES = 10 * 1024 * 1024;
121294
121390
  var PRUNE_AFTER_MS = 60000;
@@ -121363,7 +121459,7 @@ function startWhiteboxJob(input) {
121363
121459
  const logsDir = getWhiteboxLogsDir(input.session);
121364
121460
  mkdirSync11(logsDir, { recursive: true });
121365
121461
  const safeName2 = (input.name ?? "job").replace(/[^a-z0-9._-]+/gi, "-").replace(/\.{2,}/g, ".");
121366
- const logPath = join21(logsDir, `${id}-${safeName2}.log`);
121462
+ const logPath = join22(logsDir, `${id}-${safeName2}.log`);
121367
121463
  const now = new Date().toISOString();
121368
121464
  writeLog(logPath, `$ ${input.command}
121369
121465
 
@@ -121489,12 +121585,12 @@ function readWhiteboxJobLog(id, sessionId) {
121489
121585
  };
121490
121586
  }
121491
121587
  // src/core/whitebox/repoProfile.ts
121492
- import { execFile } from "node:child_process";
121588
+ import { execFile as execFile2 } from "node:child_process";
121493
121589
  import { existsSync as existsSync15, realpathSync as realpathSync2 } from "node:fs";
121494
121590
  import { readdir as readdir2, readFile as readFile5, stat as stat2 } from "node:fs/promises";
121495
- import { basename as basename3, extname as extname2, join as join22, relative as relative3, resolve as resolve5 } from "node:path";
121496
- import { promisify } from "node:util";
121497
- var execFileAsync = promisify(execFile);
121591
+ import { basename as basename3, extname as extname2, join as join23, relative as relative3, resolve as resolve5 } from "node:path";
121592
+ import { promisify as promisify2 } from "node:util";
121593
+ var execFileAsync2 = promisify2(execFile2);
121498
121594
  var MAX_PROFILE_FILES = 5000;
121499
121595
  var LANGUAGE_BY_EXTENSION = {
121500
121596
  ".ts": "typescript",
@@ -121601,7 +121697,7 @@ async function walkFiles(rootPath) {
121601
121697
  for (const entry of entries) {
121602
121698
  if (files.length >= MAX_PROFILE_FILES)
121603
121699
  return;
121604
- const fullPath = join22(current, entry.name);
121700
+ const fullPath = join23(current, entry.name);
121605
121701
  if (entry.isDirectory()) {
121606
121702
  if (shouldSkipDir(entry.name))
121607
121703
  continue;
@@ -121663,7 +121759,7 @@ function detectEntryHints(files) {
121663
121759
  return hints.slice(0, 100);
121664
121760
  }
121665
121761
  async function readPackageScripts(rootPath) {
121666
- const packageJsonPath = join22(rootPath, "package.json");
121762
+ const packageJsonPath = join23(rootPath, "package.json");
121667
121763
  if (!existsSync15(packageJsonPath)) {
121668
121764
  return { buildCommands: [], testCommands: [], runCommands: [] };
121669
121765
  }
@@ -121682,7 +121778,7 @@ async function readPackageScripts(rootPath) {
121682
121778
  }
121683
121779
  async function runGit(rootPath, args) {
121684
121780
  try {
121685
- const { stdout } = await execFileAsync("git", args, {
121781
+ const { stdout } = await execFileAsync2("git", args, {
121686
121782
  cwd: rootPath,
121687
121783
  timeout: 5000,
121688
121784
  maxBuffer: 1024 * 1024
@@ -121695,7 +121791,7 @@ async function runGit(rootPath, args) {
121695
121791
  async function detectTool(name) {
121696
121792
  const cmd = process.platform === "win32" ? "where" : "which";
121697
121793
  try {
121698
- const { stdout } = await execFileAsync(cmd, [name], {
121794
+ const { stdout } = await execFileAsync2(cmd, [name], {
121699
121795
  timeout: 2000,
121700
121796
  maxBuffer: 1024 * 64
121701
121797
  });
@@ -121827,7 +121923,7 @@ Returns an array of results with the text output from each agent.`,
121827
121923
  });
121828
121924
  }
121829
121925
  async function runSingleCodingAgent(ctx, codebasePath, objective, agentIndex, name) {
121830
- const { CodeAgent } = await import("./agent-4g69jwmq.js");
121926
+ const { CodeAgent } = await import("./agent-ssm6x6qs.js");
121831
121927
  const subagentId = `coding-agent-${agentIndex}`;
121832
121928
  ctx.eventBus?.emit("subagent-spawn", {
121833
121929
  subagentId,
@@ -121877,7 +121973,7 @@ init_ai();
121877
121973
  init_structured();
121878
121974
  init_lazyLogger();
121879
121975
  import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync4 } from "fs";
121880
- import { join as join23 } from "path";
121976
+ import { join as join24 } from "path";
121881
121977
  var log9 = scopedLogger(() => createLogger("findings:registry"));
121882
121978
  var VULN_CLASS_PATTERNS = [
121883
121979
  [/sql\s*injection/i, "sql-injection"],
@@ -122000,6 +122096,15 @@ ${existingList}
122000
122096
 
122001
122097
  Is the new finding a duplicate of any existing finding?`;
122002
122098
  }
122099
+ var SEVERITY_RANK = {
122100
+ CRITICAL: 4,
122101
+ HIGH: 3,
122102
+ MEDIUM: 2,
122103
+ LOW: 1
122104
+ };
122105
+ function maxSeverity(severities) {
122106
+ return severities.reduce((acc, s) => SEVERITY_RANK[s] > SEVERITY_RANK[acc] ? s : acc, "LOW");
122107
+ }
122003
122108
  var RootCauseGroupResultSchema = exports_external.object({
122004
122109
  groups: exports_external.array(exports_external.object({
122005
122110
  groupId: exports_external.string().describe("Short kebab-case identifier for the group"),
@@ -122060,7 +122165,7 @@ class FindingsRegistry {
122060
122165
  log9.debug(`fromDirectory: path=${findingsPath}, jsonFiles=${files.length}`);
122061
122166
  for (const file of files) {
122062
122167
  try {
122063
- const raw = readFileSync4(join23(findingsPath, file), "utf-8");
122168
+ const raw = readFileSync4(join24(findingsPath, file), "utf-8");
122064
122169
  const finding = JSON.parse(raw);
122065
122170
  if (finding && typeof finding.title === "string" && typeof finding.endpoint === "string") {
122066
122171
  registry.indexFinding(finding);
@@ -122204,18 +122309,32 @@ class FindingsRegistry {
122204
122309
  const sanitised = [];
122205
122310
  await new Promise((resolve6) => {
122206
122311
  this.mutex = this.mutex.then(() => {
122312
+ const baseSeverities = snapshot.map((f) => f.severity);
122207
122313
  for (const group of result.groups) {
122208
122314
  const validIndices = group.findingIndices.filter((i) => i >= 0 && i < snapshot.length);
122209
122315
  if (validIndices.length < 2)
122210
122316
  continue;
122317
+ const normalizedSeverity = maxSeverity(validIndices.map((i) => baseSeverities[i]));
122318
+ const leadFindingIndex = validIndices.reduce((best, i) => {
122319
+ const aRank = SEVERITY_RANK[baseSeverities[i]];
122320
+ const bRank = SEVERITY_RANK[baseSeverities[best]];
122321
+ if (aRank !== bRank) {
122322
+ return aRank > bRank ? i : best;
122323
+ }
122324
+ return (snapshot[i].description?.length ?? 0) > (snapshot[best].description?.length ?? 0) ? i : best;
122325
+ }, validIndices[0]);
122211
122326
  sanitised.push({
122212
122327
  groupId: group.groupId,
122213
122328
  findingIndices: validIndices,
122214
- rootCause: group.rootCause
122329
+ rootCause: group.rootCause,
122330
+ normalizedSeverity,
122331
+ leadFindingIndex
122215
122332
  });
122216
122333
  for (const idx of validIndices) {
122217
122334
  const finding = snapshot[idx];
122218
122335
  finding.rootCauseGroup = group.groupId;
122336
+ finding.severity = normalizedSeverity;
122337
+ finding.rootCauseLead = idx === leadFindingIndex;
122219
122338
  finding.relatedFindings = validIndices.filter((i) => i !== idx).map((i) => snapshot[i].title);
122220
122339
  }
122221
122340
  }
@@ -122251,13 +122370,13 @@ class FindingsRegistry {
122251
122370
 
122252
122371
  // src/core/plan/index.ts
122253
122372
  import { existsSync as existsSync17, readFileSync as readFileSync5 } from "fs";
122254
- import { join as join24 } from "path";
122373
+ import { join as join25 } from "path";
122255
122374
  var PLAN_FILENAME = "plan.md";
122256
122375
  function planFilePath(sessionRootPath, subagentId) {
122257
122376
  if (subagentId) {
122258
- return join24(sessionRootPath, "subagents", `${subagentId}-plan.md`);
122377
+ return join25(sessionRootPath, "subagents", `${subagentId}-plan.md`);
122259
122378
  }
122260
- return join24(sessionRootPath, PLAN_FILENAME);
122379
+ return join25(sessionRootPath, PLAN_FILENAME);
122261
122380
  }
122262
122381
  function readPlan(sessionRootPath, subagentId) {
122263
122382
  const path = planFilePath(sessionRootPath, subagentId);
@@ -122310,7 +122429,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
122310
122429
  message: "spawn_pentest_agent requires a model in the tool context."
122311
122430
  };
122312
122431
  }
122313
- const { TargetedPentestAgent } = await import("./agent-x7n47c84.js");
122432
+ const { TargetedPentestAgent } = await import("./agent-1jp62njk.js");
122314
122433
  const findingsRegistry = ctx.findingsRegistry ?? FindingsRegistry.fromDirectory(ctx.session.findingsPath, {
122315
122434
  model: ctx.model,
122316
122435
  authConfig: ctx.authConfig,
@@ -122464,7 +122583,7 @@ Pass every target you want tested — the swarm handles concurrency automaticall
122464
122583
  toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
122465
122584
  }),
122466
122585
  execute: async ({ targets }) => {
122467
- const { runPentestSwarm, DEFAULT_CONCURRENCY: DEFAULT_CONCURRENCY2 } = await import("./pentest-2vsjf0j8.js");
122586
+ const { runPentestSwarm, DEFAULT_CONCURRENCY: DEFAULT_CONCURRENCY2 } = await import("./pentest-043y2x03.js");
122468
122587
  if (!ctx.model) {
122469
122588
  return {
122470
122589
  success: false,
@@ -123101,7 +123220,7 @@ init_zod();
123101
123220
  init_structured();
123102
123221
  import { mkdirSync as mkdirSync12 } from "fs";
123103
123222
  import { writeFile as writeFile5 } from "fs/promises";
123104
- import { dirname as dirname4 } from "path";
123223
+ import { dirname as dirname5 } from "path";
123105
123224
  init_lazyLogger();
123106
123225
  var log12 = scopedLogger(() => createLogger("write_plan"));
123107
123226
  var writePlanInputSchema = exports_external.object({
@@ -123127,7 +123246,7 @@ Required plan sections:
123127
123246
  execute: async ({ content }) => {
123128
123247
  const scopeId = ctx.planSubagentId ?? ctx.subagentId;
123129
123248
  const planPath = planFilePath(ctx.session.rootPath, scopeId);
123130
- mkdirSync12(dirname4(planPath), { recursive: true });
123249
+ mkdirSync12(dirname5(planPath), { recursive: true });
123131
123250
  log12.debug(`enter: contentLen=${content.length}, path=${planPath}`);
123132
123251
  try {
123133
123252
  await writeFile5(planPath, content, "utf-8");
@@ -124156,7 +124275,7 @@ var SKILL_TOOL_NAMES = ["read_skill"];
124156
124275
  // src/core/agents/offSecAgent/trace.ts
124157
124276
  import { createHash } from "crypto";
124158
124277
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync13 } from "fs";
124159
- import { dirname as dirname5 } from "path";
124278
+ import { dirname as dirname6 } from "path";
124160
124279
  var OUTPUT_PREVIEW_LIMIT = 2000;
124161
124280
  var INPUT_PREVIEW_LIMIT = 1000;
124162
124281
  var SYSTEM_PROMPT_HASH_PREFIX_LENGTH = 12;
@@ -124217,7 +124336,7 @@ class StepTraceWriter {
124217
124336
  const now = Date.now();
124218
124337
  this.agentStartTime = now;
124219
124338
  this.lastStepTime = now;
124220
- mkdirSync13(dirname5(this.tracePath), { recursive: true });
124339
+ mkdirSync13(dirname6(this.tracePath), { recursive: true });
124221
124340
  }
124222
124341
  writeInit(data) {
124223
124342
  const { systemPrompt, ...rest } = data;
@@ -124358,7 +124477,7 @@ init_dist();
124358
124477
  init_ai();
124359
124478
  import { existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
124360
124479
  import { writeFile as writeFile6 } from "fs/promises";
124361
- import { join as join25 } from "path";
124480
+ import { join as join26 } from "path";
124362
124481
  init_structured();
124363
124482
  init_observability();
124364
124483
 
@@ -124648,6 +124767,7 @@ class OffensiveSecurityAgent {
124648
124767
  subagentId;
124649
124768
  persistentShell;
124650
124769
  browserSession;
124770
+ ownsBrowserSession;
124651
124771
  abortSignal;
124652
124772
  userPrompt;
124653
124773
  _session;
@@ -124687,19 +124807,22 @@ class OffensiveSecurityAgent {
124687
124807
  }
124688
124808
  if (!input.sandbox) {
124689
124809
  const sessionHeaders = input.target ? resolveEffectiveHeaders(input.session, input.target) : input.session.config?.headers;
124810
+ this.ownsBrowserSession = !input.browserSession;
124690
124811
  this.browserSession = input.browserSession ?? new PlaywrightMcpSession({
124691
124812
  extraHttpHeaders: stripBrowserManagedHeaders(sessionHeaders)
124692
124813
  });
124814
+ } else {
124815
+ this.ownsBrowserSession = false;
124693
124816
  }
124694
- const messagesDir = input.messagesDir ?? (input.subagentId ? join25(input.session.rootPath, "subagents", input.subagentId) : input.session.rootPath);
124695
- const tracePath = input.subagentId ? join25(input.session.rootPath, "subagents", `${input.subagentId}.trace.jsonl`) : join25(messagesDir, "trace.jsonl");
124817
+ const messagesDir = input.messagesDir ?? (input.subagentId ? join26(input.session.rootPath, "subagents", input.subagentId) : input.session.rootPath);
124818
+ const tracePath = input.subagentId ? join26(input.session.rootPath, "subagents", `${input.subagentId}.trace.jsonl`) : join26(messagesDir, "trace.jsonl");
124696
124819
  const traceWriter = new StepTraceWriter({
124697
124820
  tracePath,
124698
124821
  agentId: input.subagentId ?? null,
124699
124822
  eventBus: this.eventBus
124700
124823
  });
124701
124824
  const taskDriven = input.session.config?.taskDriven ?? false;
124702
- const tasksDir = input.tasksDir ?? (taskDriven ? input.subagentId ? join25(input.session.rootPath, "subagents", `${input.subagentId}-tasks`) : join25(input.session.rootPath, "tasks") : undefined);
124825
+ const tasksDir = input.tasksDir ?? (taskDriven ? input.subagentId ? join26(input.session.rootPath, "subagents", `${input.subagentId}-tasks`) : join26(input.session.rootPath, "tasks") : undefined);
124703
124826
  const credentialManager = input.credentialManager ?? input.session.credentialManager;
124704
124827
  const builtinTools = createAllTools({
124705
124828
  session: input.session,
@@ -124775,7 +124898,7 @@ class OffensiveSecurityAgent {
124775
124898
  if (!existsSync18(messagesDir)) {
124776
124899
  mkdirSync14(messagesDir, { recursive: true });
124777
124900
  }
124778
- const messagesPath = join25(messagesDir, "messages.json");
124901
+ const messagesPath = join26(messagesDir, "messages.json");
124779
124902
  const initialMessagesRef = {
124780
124903
  current: input.messages ? [...input.messages] : [
124781
124904
  {
@@ -124893,6 +125016,9 @@ class OffensiveSecurityAgent {
124893
125016
  }
124894
125017
  } finally {
124895
125018
  this.persistentShell?.dispose();
125019
+ if (this.ownsBrowserSession && this.browserSession) {
125020
+ await this.browserSession.disconnect().catch(() => {});
125021
+ }
124896
125022
  }
124897
125023
  if (this.abortSignal?.aborted) {
124898
125024
  throw new DOMException("Agent aborted by user", "AbortError");