@pensar/apex 2.0.1 → 2.0.2-canary.09dbe74f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/{agent-x7n47c84.js → agent-067x1n67.js} +9 -9
- package/build/{agent-6nhperp2.js → agent-3n8msbqb.js} +10 -8
- package/build/agent-m3bsrs09.js +19 -0
- package/build/{apps-2ac4vt09.js → apps-7fhwknrj.js} +20 -15
- package/build/{auth-bmt98hz0.js → auth-a1z7ew5p.js} +15 -15
- package/build/authentication-69q16waz.js +19 -0
- package/build/blackboxAgent-80y8j499.js +19 -0
- package/build/{blackboxPentest-xngbtdxb.js → blackboxPentest-aay3kx94.js} +13 -13
- package/build/{cli-hk03x6fq.js → cli-117ypqqj.js} +2 -1
- package/build/{cli-fa7nrded.js → cli-2hw7jen3.js} +5 -5
- package/build/{cli-zpdmnz8c.js → cli-2p56h4nq.js} +881 -373
- package/build/{cli-mfzkhttr.js → cli-5e5gq617.js} +22 -20
- package/build/{cli-ddtmgbqv.js → cli-8q9kr0x9.js} +2 -2
- package/build/{cli-z1dapp7v.js → cli-a3pzv462.js} +71 -33
- package/build/{cli-eptabm2j.js → cli-eg35gebv.js} +1 -1
- package/build/{cli-0yptvbbm.js → cli-hh404jw6.js} +10 -9
- package/build/{cli-1f5zzrxj.js → cli-k8zpadm5.js} +1 -1
- package/build/{cli-f93g10xk.js → cli-mcn1ar96.js} +2 -2
- package/build/{cli-pyzw545d.js → cli-ncecs1a9.js} +76 -11
- package/build/{cli-w2st266h.js → cli-pppvjz87.js} +33 -4
- package/build/{cli-fxtbkw2f.js → cli-pyz8tnrp.js} +6 -6
- package/build/{cli-8xknm7d9.js → cli-q24twygk.js} +1 -1
- package/build/{cli-8g5cwvbm.js → cli-r9yh5mka.js} +2 -2
- package/build/{cli-cc13ydyx.js → cli-sgrt7t9v.js} +9 -9
- package/build/{cli-88bhxzr1.js → cli-tt7ps59g.js} +4 -4
- package/build/cli.js +52 -47
- package/build/{config-j0gfjhrm.js → config-8rnbn2d0.js} +4 -4
- package/build/{doctor-zn8ms7gs.js → doctor-jddjv64a.js} +8 -8
- package/build/{fixes-d8ytvyzn.js → fixes-rfrer4t8.js} +15 -15
- package/build/{index-hjvqqkem.js → index-0nz7y997.js} +4 -4
- package/build/{index-2t2cg8x0.js → index-mk5mxrjd.js} +8 -8
- package/build/{index-k6ttkac6.js → index-nw5vbcg9.js} +13 -12
- package/build/{index-528cyewc.js → index-pr80jx2t.js} +116 -70
- package/build/{index-a1sy2zak.js → index-rg2czyx7.js} +2 -2
- package/build/{index-3cbcjqw1.js → index-speg9ebc.js} +9 -9
- package/build/{index-9d2es97h.js → index-syz3srk7.js} +3 -3
- package/build/{issues-17kdjtdg.js → issues-484xb633.js} +15 -15
- package/build/{logs-r4rjar4m.js → logs-kmw11amx.js} +15 -15
- package/build/{offesecAgent-azd8ahkm.js → offesecAgent-ykx4cmb1.js} +8 -8
- package/build/{parse-15kqmy2v.js → parse-7djk9jyf.js} +1 -1
- package/build/pentest-4v06edvf.js +28 -0
- package/build/{pentests-npjb5q1h.js → pentests-jpc8hjdm.js} +15 -15
- package/build/{targetedPentest-m24wvscc.js → targetedPentest-zkezvsdt.js} +9 -9
- package/build/targets-p4d186xd.js +113 -0
- package/build/threatModel-k41x41eg.js +26 -0
- package/build/{uninstall-7pm6zcah.js → uninstall-s75nx85e.js} +9 -9
- package/build/{upload-wg0vxmk0.js → upload-57y9k8gs.js} +6 -6
- package/build/{utils-gd1y4t26.js → utils-dwkm0fb2.js} +5 -5
- package/package.json +9 -8
- package/build/agent-4g69jwmq.js +0 -19
- package/build/authentication-c0aj9zaz.js +0 -19
- package/build/blackboxAgent-sgph70e4.js +0 -19
- package/build/pentest-2vsjf0j8.js +0 -28
- package/build/threatModel-7akmfzzm.js +0 -26
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectOSAndEnhancePrompt
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-sgrt7t9v.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-
|
|
29
|
+
} from "./cli-a3pzv462.js";
|
|
30
30
|
import {
|
|
31
31
|
ensureValidToken,
|
|
32
32
|
getPensarApiUrl,
|
|
33
33
|
init_auth,
|
|
34
34
|
init_constants,
|
|
35
35
|
signGatewayRequest
|
|
36
|
-
} from "./cli-
|
|
36
|
+
} from "./cli-r9yh5mka.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-
|
|
67
|
+
} from "./cli-eg35gebv.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,
|
|
42490
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, 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,
|
|
42499
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, 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:
|
|
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"] ||
|
|
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:
|
|
71604
|
+
var { createRequire: createRequire3 } = __require("module");
|
|
71605
71605
|
var { existsSync: existsSync9 } = __require("node:fs");
|
|
71606
71606
|
var getCallers = require_caller();
|
|
71607
|
-
var { join:
|
|
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"] ||
|
|
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"] ||
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 +=
|
|
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 += `${
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 +=
|
|
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 += `${
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 +=
|
|
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 += `${
|
|
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
|
|
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,
|
|
72979
|
+
res += stringifyTypedArray(value, join14, maximumBreadth);
|
|
72980
72980
|
keys = keys.slice(value.length);
|
|
72981
72981
|
maximumPropertiesToStringify -= value.length;
|
|
72982
|
-
separator =
|
|
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 =
|
|
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 =
|
|
72999
|
+
separator = join14;
|
|
73000
73000
|
}
|
|
73001
73001
|
if (separator !== "") {
|
|
73002
73002
|
res = `
|
|
@@ -105309,7 +105309,7 @@ Invalid`)
|
|
|
105309
105309
|
});
|
|
105310
105310
|
|
|
105311
105311
|
// src/core/eventBus.ts
|
|
105312
|
-
import { EventEmitter } from "events";
|
|
105312
|
+
import { EventEmitter } from "node:events";
|
|
105313
105313
|
var CHILD_BUS_FORWARD_POLICY = {
|
|
105314
105314
|
"text-delta": "forward",
|
|
105315
105315
|
"tool-call-start": "forward",
|
|
@@ -106392,8 +106392,8 @@ function assertCommandInScope(command, ctx) {
|
|
|
106392
106392
|
// src/core/agents/offSecAgent/tools/authenticateSession.ts
|
|
106393
106393
|
init_dist();
|
|
106394
106394
|
init_zod();
|
|
106395
|
-
import { writeFileSync } from "fs";
|
|
106396
|
-
import { join as join2 } from "path";
|
|
106395
|
+
import { writeFileSync } from "node:fs";
|
|
106396
|
+
import { join as join2 } from "node:path";
|
|
106397
106397
|
|
|
106398
106398
|
// src/core/http/targetHeaders.ts
|
|
106399
106399
|
function getRegistrableDomain2(hostname) {
|
|
@@ -106834,6 +106834,11 @@ or provide username/password directly.`,
|
|
|
106834
106834
|
});
|
|
106835
106835
|
}
|
|
106836
106836
|
|
|
106837
|
+
// src/core/agents/offSecAgent/tools/playwrightMcp.ts
|
|
106838
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
106839
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
106840
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
106841
|
+
|
|
106837
106842
|
// node_modules/zod/v4/mini/parse.js
|
|
106838
106843
|
init_core();
|
|
106839
106844
|
|
|
@@ -109377,9 +109382,44 @@ function isElectron() {
|
|
|
109377
109382
|
// src/core/agents/offSecAgent/tools/playwrightMcp.ts
|
|
109378
109383
|
init_dist();
|
|
109379
109384
|
init_zod();
|
|
109380
|
-
|
|
109381
|
-
|
|
109382
|
-
import {
|
|
109385
|
+
|
|
109386
|
+
// src/core/agents/offSecAgent/tools/camoufox.ts
|
|
109387
|
+
import { execFile } from "node:child_process";
|
|
109388
|
+
import { createRequire } from "node:module";
|
|
109389
|
+
import { dirname, join as join3 } from "node:path";
|
|
109390
|
+
import { promisify } from "node:util";
|
|
109391
|
+
var execFileAsync = promisify(execFile);
|
|
109392
|
+
var CAMOUFOX_OPTIONS = {
|
|
109393
|
+
humanize: true,
|
|
109394
|
+
block_webrtc: true
|
|
109395
|
+
};
|
|
109396
|
+
async function resolveCamoufoxLaunchOptions(headless) {
|
|
109397
|
+
const { launchOptions: camoufoxLaunchOptions } = await import("camoufox-js");
|
|
109398
|
+
return await camoufoxLaunchOptions({
|
|
109399
|
+
...CAMOUFOX_OPTIONS,
|
|
109400
|
+
headless
|
|
109401
|
+
});
|
|
109402
|
+
}
|
|
109403
|
+
var ensurePromise = null;
|
|
109404
|
+
function ensureCamoufox(log) {
|
|
109405
|
+
if (!ensurePromise) {
|
|
109406
|
+
ensurePromise = (async () => {
|
|
109407
|
+
log?.("Provisioning Camoufox browser (first run downloads ~150MB; cached after)…");
|
|
109408
|
+
const require_ = createRequire(import.meta.url);
|
|
109409
|
+
const pkgPath = require_.resolve("camoufox-js/package.json");
|
|
109410
|
+
const fetchBin = join3(dirname(pkgPath), "dist", "__main__.js");
|
|
109411
|
+
await execFileAsync("node", [fetchBin, "fetch"], {
|
|
109412
|
+
timeout: 300000
|
|
109413
|
+
});
|
|
109414
|
+
})().catch((err) => {
|
|
109415
|
+
ensurePromise = null;
|
|
109416
|
+
throw err;
|
|
109417
|
+
});
|
|
109418
|
+
}
|
|
109419
|
+
return ensurePromise;
|
|
109420
|
+
}
|
|
109421
|
+
|
|
109422
|
+
// src/core/agents/offSecAgent/tools/playwrightMcp.ts
|
|
109383
109423
|
var BrowserNavigateInput = exports_external.object({
|
|
109384
109424
|
url: exports_external.string().describe("Full URL to navigate to"),
|
|
109385
109425
|
toolCallDescription: exports_external.string().describe("Why you are navigating to this URL")
|
|
@@ -109495,6 +109535,7 @@ class PlaywrightMcpSession {
|
|
|
109495
109535
|
viewportSize;
|
|
109496
109536
|
extraHttpHeaders;
|
|
109497
109537
|
mcpConfigPath = null;
|
|
109538
|
+
cachedCamouOptions = null;
|
|
109498
109539
|
constructor(options = {}) {
|
|
109499
109540
|
const { headless, userAgent, viewportSize, extraHttpHeaders } = options;
|
|
109500
109541
|
this.headless = headless ?? defaultHeadless;
|
|
@@ -109504,14 +109545,26 @@ class PlaywrightMcpSession {
|
|
|
109504
109545
|
this.extraHttpHeaders = headerSource && Object.keys(headerSource).length > 0 ? { ...headerSource } : undefined;
|
|
109505
109546
|
}
|
|
109506
109547
|
resetState() {
|
|
109548
|
+
this.cleanupConfigFile();
|
|
109507
109549
|
this.mcpClient = null;
|
|
109508
109550
|
this.mcpTransport = null;
|
|
109509
109551
|
this.mcpProcess = null;
|
|
109510
109552
|
this.connectionPromise = null;
|
|
109511
109553
|
this.disconnecting = false;
|
|
109512
109554
|
}
|
|
109513
|
-
|
|
109514
|
-
const
|
|
109555
|
+
cleanupConfigFile() {
|
|
109556
|
+
const configPath = this.mcpConfigPath;
|
|
109557
|
+
if (!configPath)
|
|
109558
|
+
return;
|
|
109559
|
+
this.mcpConfigPath = null;
|
|
109560
|
+
(async () => {
|
|
109561
|
+
try {
|
|
109562
|
+
const fsp = await import("fs/promises");
|
|
109563
|
+
await fsp.unlink(configPath);
|
|
109564
|
+
} catch {}
|
|
109565
|
+
})();
|
|
109566
|
+
}
|
|
109567
|
+
forceKillProcess(proc = this.mcpProcess) {
|
|
109515
109568
|
if (!proc || proc.exitCode !== null)
|
|
109516
109569
|
return;
|
|
109517
109570
|
try {
|
|
@@ -109539,41 +109592,40 @@ class PlaywrightMcpSession {
|
|
|
109539
109592
|
}
|
|
109540
109593
|
this.connectionPromise = (async () => {
|
|
109541
109594
|
try {
|
|
109542
|
-
const require_ =
|
|
109595
|
+
const require_ = createRequire2(import.meta.url);
|
|
109543
109596
|
const mcpPkgJsonPath = require_.resolve("@playwright/mcp/package.json");
|
|
109544
|
-
const cliPath =
|
|
109597
|
+
const cliPath = join4(dirname2(mcpPkgJsonPath), "cli.js");
|
|
109545
109598
|
const args = [cliPath, "--isolated"];
|
|
109546
|
-
|
|
109547
|
-
|
|
109548
|
-
|
|
109549
|
-
|
|
109550
|
-
|
|
109551
|
-
|
|
109552
|
-
|
|
109553
|
-
|
|
109554
|
-
|
|
109555
|
-
|
|
109556
|
-
|
|
109557
|
-
|
|
109558
|
-
|
|
109559
|
-
|
|
109560
|
-
|
|
109561
|
-
}
|
|
109562
|
-
|
|
109563
|
-
|
|
109564
|
-
|
|
109565
|
-
|
|
109566
|
-
|
|
109567
|
-
|
|
109568
|
-
|
|
109569
|
-
}
|
|
109570
|
-
|
|
109571
|
-
args.push("--no-sandbox");
|
|
109572
|
-
}
|
|
109599
|
+
await ensureCamoufox();
|
|
109600
|
+
if (!this.cachedCamouOptions) {
|
|
109601
|
+
this.cachedCamouOptions = await resolveCamoufoxLaunchOptions(this.headless);
|
|
109602
|
+
}
|
|
109603
|
+
const camou = this.cachedCamouOptions;
|
|
109604
|
+
const os = await import("node:os");
|
|
109605
|
+
const fsp = await import("node:fs/promises");
|
|
109606
|
+
const cfg = {
|
|
109607
|
+
browser: {
|
|
109608
|
+
browserName: "firefox",
|
|
109609
|
+
launchOptions: {
|
|
109610
|
+
executablePath: camou.executablePath,
|
|
109611
|
+
args: camou.args,
|
|
109612
|
+
firefoxUserPrefs: camou.firefoxUserPrefs,
|
|
109613
|
+
headless: camou.headless
|
|
109614
|
+
},
|
|
109615
|
+
...this.extraHttpHeaders ? { contextOptions: { extraHTTPHeaders: this.extraHttpHeaders } } : {}
|
|
109616
|
+
}
|
|
109617
|
+
};
|
|
109618
|
+
this.mcpConfigPath = join4(os.tmpdir(), `pensar-mcp-${process.pid}-${Date.now()}.json`);
|
|
109619
|
+
await fsp.writeFile(this.mcpConfigPath, JSON.stringify(cfg), {
|
|
109620
|
+
encoding: "utf-8",
|
|
109621
|
+
mode: 384
|
|
109622
|
+
});
|
|
109623
|
+
args.push("--config", this.mcpConfigPath);
|
|
109573
109624
|
const transport = new StdioClientTransport({
|
|
109574
109625
|
command: "node",
|
|
109575
109626
|
args,
|
|
109576
|
-
stderr: "pipe"
|
|
109627
|
+
stderr: "pipe",
|
|
109628
|
+
env: Object.fromEntries(Object.entries(camou.env).map(([k, v]) => [k, String(v)]))
|
|
109577
109629
|
});
|
|
109578
109630
|
const client = new Client({
|
|
109579
109631
|
name: "apex-browser-agent",
|
|
@@ -109607,29 +109659,28 @@ class PlaywrightMcpSession {
|
|
|
109607
109659
|
this.disconnecting = true;
|
|
109608
109660
|
const transport = this.mcpTransport;
|
|
109609
109661
|
const client = this.mcpClient;
|
|
109662
|
+
const proc = this.mcpProcess;
|
|
109610
109663
|
this.resetState();
|
|
109611
109664
|
this.disconnecting = true;
|
|
109612
|
-
|
|
109613
|
-
|
|
109614
|
-
|
|
109615
|
-
|
|
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;
|
|
109665
|
+
this.forceKillProcess(proc);
|
|
109666
|
+
const CLOSE_TIMEOUT_MS = 5000;
|
|
109667
|
+
let closeTimer;
|
|
109668
|
+
await Promise.race([
|
|
109626
109669
|
(async () => {
|
|
109627
109670
|
try {
|
|
109628
|
-
|
|
109629
|
-
await fsp.unlink(configPath);
|
|
109671
|
+
await client?.close();
|
|
109630
109672
|
} catch {}
|
|
109631
|
-
|
|
109632
|
-
|
|
109673
|
+
try {
|
|
109674
|
+
await transport?.close();
|
|
109675
|
+
} catch {}
|
|
109676
|
+
})(),
|
|
109677
|
+
new Promise((resolve) => {
|
|
109678
|
+
closeTimer = setTimeout(resolve, CLOSE_TIMEOUT_MS);
|
|
109679
|
+
})
|
|
109680
|
+
]);
|
|
109681
|
+
if (closeTimer)
|
|
109682
|
+
clearTimeout(closeTimer);
|
|
109683
|
+
this.cleanupConfigFile();
|
|
109633
109684
|
this.disconnecting = false;
|
|
109634
109685
|
}
|
|
109635
109686
|
isConnected() {
|
|
@@ -109953,8 +110004,8 @@ Target base URL: ${targetUrl}`,
|
|
|
109953
110004
|
if (base64Data) {
|
|
109954
110005
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
109955
110006
|
const screenshotFilename = `${filename}_${timestamp}.png`;
|
|
109956
|
-
const screenshotPath =
|
|
109957
|
-
const dir =
|
|
110007
|
+
const screenshotPath = join4(evidenceDir, screenshotFilename);
|
|
110008
|
+
const dir = dirname2(screenshotPath);
|
|
109958
110009
|
if (!existsSync2(dir)) {
|
|
109959
110010
|
mkdirSync(dir, { recursive: true });
|
|
109960
110011
|
}
|
|
@@ -110167,18 +110218,20 @@ The returned cookies can be formatted as a Cookie header for use with http_reque
|
|
|
110167
110218
|
// src/core/agents/offSecAgent/tools/sandboxPlaywright.ts
|
|
110168
110219
|
init_dist();
|
|
110169
110220
|
init_zod();
|
|
110170
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
110171
|
-
import { dirname as
|
|
110221
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
110222
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
110172
110223
|
var SANDBOX_PW_DIR = "/opt/sandbox-playwright";
|
|
110173
110224
|
var SANDBOX_EVIDENCE_DIR = "/tmp/evidence";
|
|
110174
110225
|
var SANDBOX_REFS_FILE = "/tmp/pw-refs.json";
|
|
110175
110226
|
var SANDBOX_URL_FILE = "/tmp/pw-current-url";
|
|
110176
110227
|
var SANDBOX_CONSOLE_FILE = "/tmp/pw-console-log.json";
|
|
110228
|
+
var SANDBOX_CAMOU_CACHE = "/tmp/pw-camou-opts.json";
|
|
110177
110229
|
var RESULT_START = "__PW_RESULT__";
|
|
110178
110230
|
var RESULT_END = "__PW_END__";
|
|
110179
110231
|
var installationCache = new WeakMap;
|
|
110232
|
+
var browserSetupCache = new WeakMap;
|
|
110180
110233
|
async function checkSandboxPlaywright(sandbox) {
|
|
110181
|
-
const script = `try{require("playwright");console.log("OK")}catch(e){process.exit(1)}`;
|
|
110234
|
+
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
110235
|
const b64 = Buffer.from(script).toString("base64");
|
|
110183
110236
|
await sandbox.execute(`mkdir -p ${SANDBOX_PW_DIR} && echo "${b64}" | base64 -d > ${SANDBOX_PW_DIR}/pw_check.js`, { timeout: 10 });
|
|
110184
110237
|
const result = await sandbox.execute(`node ${SANDBOX_PW_DIR}/pw_check.js`, {
|
|
@@ -110189,35 +110242,33 @@ async function checkSandboxPlaywright(sandbox) {
|
|
|
110189
110242
|
async function installSandboxPlaywright(sandbox) {
|
|
110190
110243
|
await sandbox.execute(`mkdir -p ${SANDBOX_PW_DIR}`, { timeout: 10 });
|
|
110191
110244
|
const isAlpine = (await sandbox.execute("which apk", { timeout: 5 })).success;
|
|
110245
|
+
if (isAlpine) {
|
|
110246
|
+
throw new Error("Camoufox requires a glibc-based sandbox image (Debian/Ubuntu); Alpine/musl is unsupported.");
|
|
110247
|
+
}
|
|
110192
110248
|
const initResult = await sandbox.execute(`cd ${SANDBOX_PW_DIR} && npm init -y --silent 2>/dev/null`, { timeout: 30 });
|
|
110193
110249
|
if (!initResult.success) {
|
|
110194
110250
|
throw new Error(`Failed to init npm project in sandbox: ${initResult.stderr || initResult.stdout}`);
|
|
110195
110251
|
}
|
|
110196
|
-
const
|
|
110197
|
-
const installResult = await sandbox.execute(npmInstallCmd, { timeout: 300 });
|
|
110252
|
+
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
110253
|
if (!installResult.success) {
|
|
110199
|
-
throw new Error(`Failed to install
|
|
110254
|
+
throw new Error(`Failed to install camoufox-js in sandbox: ${installResult.stderr || installResult.stdout}`);
|
|
110200
110255
|
}
|
|
110201
|
-
|
|
110202
|
-
|
|
110203
|
-
|
|
110204
|
-
|
|
110205
|
-
|
|
110206
|
-
|
|
110207
|
-
await sandbox.execute(`
|
|
110208
|
-
|
|
110209
|
-
|
|
110210
|
-
|
|
110211
|
-
|
|
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}`);
|
|
110256
|
+
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 });
|
|
110257
|
+
if (!depsResult.success) {
|
|
110258
|
+
throw new Error(`Failed to install Firefox system deps in sandbox: ${depsResult.stderr || depsResult.stdout}`);
|
|
110259
|
+
}
|
|
110260
|
+
let fetchResult;
|
|
110261
|
+
for (let attempt = 1;attempt <= 3; attempt++) {
|
|
110262
|
+
fetchResult = await sandbox.execute(`cd ${SANDBOX_PW_DIR} && node ./node_modules/camoufox-js/dist/__main__.js fetch 2>&1`, { timeout: 300 });
|
|
110263
|
+
if (fetchResult.success)
|
|
110264
|
+
break;
|
|
110265
|
+
if (attempt < 3) {
|
|
110266
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
110219
110267
|
}
|
|
110220
110268
|
}
|
|
110269
|
+
if (!fetchResult.success) {
|
|
110270
|
+
throw new Error(`Failed to fetch Camoufox in sandbox: ${fetchResult.stderr || fetchResult.stdout}`);
|
|
110271
|
+
}
|
|
110221
110272
|
}
|
|
110222
110273
|
async function ensureSandboxPlaywright(sandbox) {
|
|
110223
110274
|
let cached = installationCache.get(sandbox);
|
|
@@ -110238,42 +110289,59 @@ async function ensureSandboxPlaywright(sandbox) {
|
|
|
110238
110289
|
}
|
|
110239
110290
|
}
|
|
110240
110291
|
async function ensureSandboxBrowser(sandbox) {
|
|
110241
|
-
await sandbox.execute(`mkdir -p ${SANDBOX_EVIDENCE_DIR} /tmp/pw-user-data`, {
|
|
110242
|
-
timeout: 5
|
|
110243
|
-
});
|
|
110292
|
+
await sandbox.execute(`rm -rf /tmp/pw-user-data ${SANDBOX_CAMOU_CACHE}; mkdir -p ${SANDBOX_EVIDENCE_DIR} /tmp/pw-user-data`, { timeout: 5 });
|
|
110244
110293
|
}
|
|
110245
110294
|
async function ensureSandboxReady(sandbox) {
|
|
110246
110295
|
await ensureSandboxPlaywright(sandbox);
|
|
110247
|
-
|
|
110296
|
+
let cached = browserSetupCache.get(sandbox);
|
|
110297
|
+
if (!cached) {
|
|
110298
|
+
cached = ensureSandboxBrowser(sandbox);
|
|
110299
|
+
browserSetupCache.set(sandbox, cached);
|
|
110300
|
+
try {
|
|
110301
|
+
await cached;
|
|
110302
|
+
} catch (error) {
|
|
110303
|
+
browserSetupCache.delete(sandbox);
|
|
110304
|
+
throw error;
|
|
110305
|
+
}
|
|
110306
|
+
} else {
|
|
110307
|
+
await cached;
|
|
110308
|
+
}
|
|
110248
110309
|
}
|
|
110249
110310
|
async function runPlaywrightScript(sandbox, body, timeout = 60, extraHttpHeaders) {
|
|
110250
110311
|
const headersJson = extraHttpHeaders && Object.keys(extraHttpHeaders).length > 0 ? JSON.stringify(extraHttpHeaders) : "null";
|
|
110251
110312
|
const script = `
|
|
110252
|
-
const {
|
|
110313
|
+
const { firefox } = require('playwright-core');
|
|
110253
110314
|
const fs = require('fs');
|
|
110254
110315
|
|
|
110255
110316
|
(async () => {
|
|
110317
|
+
const { launchOptions } = await import('camoufox-js');
|
|
110256
110318
|
function resolve(value) {
|
|
110257
110319
|
process.stdout.write('${RESULT_START}' + JSON.stringify(value) + '${RESULT_END}');
|
|
110258
110320
|
}
|
|
110259
110321
|
|
|
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
110322
|
// Resolved per script invocation so /headers mutations take effect
|
|
110267
110323
|
// on the next browser tool call.
|
|
110268
110324
|
const __extraHeaders = ${headersJson};
|
|
110269
110325
|
|
|
110326
|
+
// Use the sandbox's virtual display if one is present — Camoufox is far less
|
|
110327
|
+
// detectable headful. Falls back to plain headless, which is still fully
|
|
110328
|
+
// fingerprint-spoofed (just a weaker stealth posture, never vanilla Chromium).
|
|
110329
|
+
const __headless = process.env.DISPLAY ? false : true;
|
|
110330
|
+
// Resolve Camoufox options once per sandbox session and cache to disk so
|
|
110331
|
+
// every tool call presents the same fingerprint on the shared profile dir.
|
|
110332
|
+
let __camou;
|
|
110333
|
+
try {
|
|
110334
|
+
__camou = JSON.parse(fs.readFileSync('${SANDBOX_CAMOU_CACHE}', 'utf-8'));
|
|
110335
|
+
} catch {
|
|
110336
|
+
__camou = await launchOptions({ ...${JSON.stringify(CAMOUFOX_OPTIONS)}, headless: __headless });
|
|
110337
|
+
fs.writeFileSync('${SANDBOX_CAMOU_CACHE}', JSON.stringify(__camou));
|
|
110338
|
+
}
|
|
110339
|
+
|
|
110270
110340
|
let context;
|
|
110271
110341
|
const __consoleMessages = [];
|
|
110272
110342
|
try {
|
|
110273
|
-
context = await
|
|
110274
|
-
|
|
110275
|
-
...(executablePath ? { executablePath } : {}),
|
|
110276
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu'],
|
|
110343
|
+
context = await firefox.launchPersistentContext('/tmp/pw-user-data', {
|
|
110344
|
+
...__camou,
|
|
110277
110345
|
...(__extraHeaders ? { extraHTTPHeaders: __extraHeaders } : {}),
|
|
110278
110346
|
});
|
|
110279
110347
|
const pages = context.pages();
|
|
@@ -110363,7 +110431,7 @@ var BrowserGetCookiesInput2 = exports_external.object({
|
|
|
110363
110431
|
});
|
|
110364
110432
|
function createSandboxBrowserTools(ctx) {
|
|
110365
110433
|
const sandbox = ctx.sandbox;
|
|
110366
|
-
const evidenceDir =
|
|
110434
|
+
const evidenceDir = join5(ctx.session.rootPath, "evidence");
|
|
110367
110435
|
const targetUrl = ctx.target ?? "";
|
|
110368
110436
|
if (!existsSync3(evidenceDir)) {
|
|
110369
110437
|
mkdirSync2(evidenceDir, { recursive: true });
|
|
@@ -110375,9 +110443,13 @@ function createSandboxBrowserTools(ctx) {
|
|
|
110375
110443
|
}
|
|
110376
110444
|
return setupPromise;
|
|
110377
110445
|
}
|
|
110446
|
+
let scriptQueue = Promise.resolve();
|
|
110378
110447
|
function runScript(body, timeout = 60) {
|
|
110379
110448
|
const resolved = targetUrl ? resolveEffectiveHeaders(resolverSessionFromCtx(ctx), targetUrl) : ctx.session.config?.headers;
|
|
110380
|
-
|
|
110449
|
+
const headers = stripBrowserManagedHeaders(resolved);
|
|
110450
|
+
const next = scriptQueue.then(() => runPlaywrightScript(sandbox, body, timeout, headers));
|
|
110451
|
+
scriptQueue = next.then(() => {}, () => {});
|
|
110452
|
+
return next;
|
|
110381
110453
|
}
|
|
110382
110454
|
const browser_navigate = tool({
|
|
110383
110455
|
description: `Navigate the browser to a URL to load and render a page.
|
|
@@ -110428,8 +110500,8 @@ Use this to document:
|
|
|
110428
110500
|
resolve({ success: true, data: b64, sandboxPath: ${JSON.stringify(sandboxPath)} });
|
|
110429
110501
|
`, 30);
|
|
110430
110502
|
if (result.success && result.data) {
|
|
110431
|
-
const localPath =
|
|
110432
|
-
const dir =
|
|
110503
|
+
const localPath = join5(evidenceDir, screenshotFilename);
|
|
110504
|
+
const dir = dirname3(localPath);
|
|
110433
110505
|
if (!existsSync3(dir)) {
|
|
110434
110506
|
mkdirSync2(dir, { recursive: true });
|
|
110435
110507
|
}
|
|
@@ -110806,7 +110878,7 @@ The returned cookies can be formatted as a Cookie header for use with http_reque
|
|
|
110806
110878
|
// src/core/agents/offSecAgent/tools/browserTools.ts
|
|
110807
110879
|
init_dist();
|
|
110808
110880
|
init_zod();
|
|
110809
|
-
import { join as
|
|
110881
|
+
import { join as join6 } from "node:path";
|
|
110810
110882
|
var BROWSER_TOOL_NAMES = [
|
|
110811
110883
|
"browser_navigate",
|
|
110812
110884
|
"browser_snapshot",
|
|
@@ -110818,7 +110890,7 @@ var BROWSER_TOOL_NAMES = [
|
|
|
110818
110890
|
"browser_get_cookies"
|
|
110819
110891
|
];
|
|
110820
110892
|
function createBrowserToolset(ctx) {
|
|
110821
|
-
const tools = ctx.sandbox ? createSandboxBrowserTools(ctx) : createBrowserTools(ctx.target ?? "",
|
|
110893
|
+
const tools = ctx.sandbox ? createSandboxBrowserTools(ctx) : createBrowserTools(ctx.target ?? "", join6(ctx.session.rootPath, "evidence"), "operator", undefined, ctx.abortSignal, undefined, undefined, undefined, ctx.browserSession);
|
|
110822
110894
|
if (!ctx.credentialManager) {
|
|
110823
110895
|
return tools;
|
|
110824
110896
|
}
|
|
@@ -110878,7 +110950,7 @@ Credential mode: Instead of passing a raw secret as "value", you can pass ` + `"
|
|
|
110878
110950
|
error: "Either value or credentialId + credentialField must be provided"
|
|
110879
110951
|
};
|
|
110880
110952
|
}
|
|
110881
|
-
return originalFill.execute({ element, ref, value, toolCallDescription }, { toolCallId: "", messages: [], abortSignal: undefined });
|
|
110953
|
+
return originalFill.execute?.({ element, ref, value, toolCallDescription }, { toolCallId: "", messages: [], abortSignal: undefined });
|
|
110882
110954
|
}
|
|
110883
110955
|
});
|
|
110884
110956
|
return {
|
|
@@ -110943,8 +111015,8 @@ init_dist();
|
|
|
110943
111015
|
init_zod();
|
|
110944
111016
|
init_structured();
|
|
110945
111017
|
init_lazyLogger();
|
|
110946
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
110947
|
-
import { join as
|
|
111018
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
111019
|
+
import { join as join7 } from "node:path";
|
|
110948
111020
|
var log = scopedLogger(() => createLogger("complete_authentication"));
|
|
110949
111021
|
var AUTH_DIR = "auth";
|
|
110950
111022
|
var AUTH_DATA_FILENAME = "auth-data.json";
|
|
@@ -110988,11 +111060,11 @@ This tool marks the end of the authentication flow.`,
|
|
|
110988
111060
|
log.info(`Authentication complete: ${result.success ? "SUCCESS" : "FAILED"}`);
|
|
110989
111061
|
let authDataPath;
|
|
110990
111062
|
try {
|
|
110991
|
-
const authDir =
|
|
111063
|
+
const authDir = join7(ctx.session.rootPath, AUTH_DIR);
|
|
110992
111064
|
if (!existsSync4(authDir)) {
|
|
110993
111065
|
mkdirSync3(authDir, { recursive: true });
|
|
110994
111066
|
}
|
|
110995
|
-
authDataPath =
|
|
111067
|
+
authDataPath = join7(authDir, AUTH_DATA_FILENAME);
|
|
110996
111068
|
const authData = {
|
|
110997
111069
|
authenticated: result.success,
|
|
110998
111070
|
strategy: result.strategy || "unknown",
|
|
@@ -111061,10 +111133,10 @@ async function extractJavascriptEndpoints(params) {
|
|
|
111061
111133
|
const patternCopy = new RegExp(pattern.source, pattern.flags);
|
|
111062
111134
|
for (let match = patternCopy.exec(scriptContent);match !== null; match = patternCopy.exec(scriptContent)) {
|
|
111063
111135
|
const endpoint = match[1] || match[2];
|
|
111064
|
-
if (endpoint
|
|
111136
|
+
if (endpoint?.startsWith("/")) {
|
|
111065
111137
|
endpoints.push({
|
|
111066
111138
|
endpoint,
|
|
111067
|
-
pattern: pattern.source.substring(0, 30)
|
|
111139
|
+
pattern: `${pattern.source.substring(0, 30)}...`,
|
|
111068
111140
|
source: "inline-script"
|
|
111069
111141
|
});
|
|
111070
111142
|
}
|
|
@@ -111208,8 +111280,8 @@ function crawlAuthenticated(ctx) {
|
|
|
111208
111280
|
// src/core/agents/offSecAgent/tools/createAttackSurfaceReport.ts
|
|
111209
111281
|
init_dist();
|
|
111210
111282
|
init_zod();
|
|
111211
|
-
import { writeFileSync as writeFileSync5 } from "fs";
|
|
111212
|
-
import { join as
|
|
111283
|
+
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
111284
|
+
import { join as join8 } from "node:path";
|
|
111213
111285
|
function createAttackSurfaceReport(ctx) {
|
|
111214
111286
|
return tool({
|
|
111215
111287
|
description: `Provide attack surface analysis results to the orchestrator agent.
|
|
@@ -111235,7 +111307,7 @@ Call this at the END of your analysis with:
|
|
|
111235
111307
|
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
|
|
111236
111308
|
}),
|
|
111237
111309
|
execute: async (results) => {
|
|
111238
|
-
const resultsPath =
|
|
111310
|
+
const resultsPath = join8(ctx.session.rootPath, "attack-surface-results.json");
|
|
111239
111311
|
writeFileSync5(resultsPath, JSON.stringify(results, null, 2));
|
|
111240
111312
|
return {
|
|
111241
111313
|
success: true,
|
|
@@ -111252,9 +111324,9 @@ init_dist();
|
|
|
111252
111324
|
init_zod();
|
|
111253
111325
|
init_structured();
|
|
111254
111326
|
init_lazyLogger();
|
|
111255
|
-
import { existsSync as existsSync5 } from "fs";
|
|
111256
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
111257
|
-
import { dirname as
|
|
111327
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
111328
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
111329
|
+
import { dirname as dirname4, isAbsolute, resolve } from "node:path";
|
|
111258
111330
|
var log3 = scopedLogger(() => createLogger("create_file"));
|
|
111259
111331
|
var createFileInputSchema = exports_external.object({
|
|
111260
111332
|
path: exports_external.string().describe("Absolute or relative path for the new file"),
|
|
@@ -111298,7 +111370,7 @@ async function executeLocalCreate(filePath, content, overwrite) {
|
|
|
111298
111370
|
path: filePath
|
|
111299
111371
|
};
|
|
111300
111372
|
}
|
|
111301
|
-
const dir =
|
|
111373
|
+
const dir = dirname4(filePath);
|
|
111302
111374
|
log3.debug(`local: mkdir ${dir}`);
|
|
111303
111375
|
await mkdir(dir, { recursive: true });
|
|
111304
111376
|
log3.debug(`local: mkdir done, writing ${content.length} bytes`);
|
|
@@ -111332,7 +111404,7 @@ async function executeSandboxCreate(ctx, filePath, content, overwrite) {
|
|
|
111332
111404
|
};
|
|
111333
111405
|
}
|
|
111334
111406
|
}
|
|
111335
|
-
const dirPath =
|
|
111407
|
+
const dirPath = dirname4(filePath);
|
|
111336
111408
|
await ctx.sandbox.execute(`mkdir -p "${dirPath}"`);
|
|
111337
111409
|
const base64Content = Buffer.from(content).toString("base64");
|
|
111338
111410
|
const writeResult = await ctx.sandbox.execute(`echo "${base64Content}" | base64 -d > "${filePath}"`);
|
|
@@ -111362,17 +111434,17 @@ init_dist();
|
|
|
111362
111434
|
init_zod();
|
|
111363
111435
|
|
|
111364
111436
|
// src/core/tasks/index.ts
|
|
111365
|
-
import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync, writeFileSync as writeFileSync6 } from "fs";
|
|
111366
|
-
import { join as
|
|
111437
|
+
import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
111438
|
+
import { join as join9 } from "node:path";
|
|
111367
111439
|
var HWM_FILENAME = "_hwm.json";
|
|
111368
111440
|
function ensureDir(dir) {
|
|
111369
111441
|
mkdirSync4(dir, { recursive: true });
|
|
111370
111442
|
}
|
|
111371
111443
|
function taskPath(tasksDir, id) {
|
|
111372
|
-
return
|
|
111444
|
+
return join9(tasksDir, `${id}.json`);
|
|
111373
111445
|
}
|
|
111374
111446
|
function hwmPath(tasksDir) {
|
|
111375
|
-
return
|
|
111447
|
+
return join9(tasksDir, HWM_FILENAME);
|
|
111376
111448
|
}
|
|
111377
111449
|
function readHighWaterMark(tasksDir) {
|
|
111378
111450
|
try {
|
|
@@ -111445,7 +111517,7 @@ function listTasks(tasksDir, filter) {
|
|
|
111445
111517
|
if (!file.endsWith(".json") || file === HWM_FILENAME)
|
|
111446
111518
|
continue;
|
|
111447
111519
|
try {
|
|
111448
|
-
const raw = readFileSync(
|
|
111520
|
+
const raw = readFileSync(join9(tasksDir, file), "utf-8");
|
|
111449
111521
|
const task = JSON.parse(raw);
|
|
111450
111522
|
if (filter?.status && task.status !== filter.status)
|
|
111451
111523
|
continue;
|
|
@@ -111542,8 +111614,8 @@ Examples of good tasks:
|
|
|
111542
111614
|
init_dist();
|
|
111543
111615
|
init_zod();
|
|
111544
111616
|
init_credentials();
|
|
111545
|
-
import { writeFileSync as writeFileSync7 } from "fs";
|
|
111546
|
-
import { join as
|
|
111617
|
+
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
111618
|
+
import { join as join10 } from "node:path";
|
|
111547
111619
|
init_structured();
|
|
111548
111620
|
init_lazyLogger();
|
|
111549
111621
|
var log4 = scopedLogger(() => createLogger("delegate_auth"));
|
|
@@ -111709,7 +111781,7 @@ When to use delegate_to_auth_subagent vs authenticate_session:
|
|
|
111709
111781
|
if (credentials) {
|
|
111710
111782
|
ctx.session.credentialManager.addFromAuthCredentials(credentials);
|
|
111711
111783
|
}
|
|
111712
|
-
const { runAuthenticationAgent } = await import("./authentication-
|
|
111784
|
+
const { runAuthenticationAgent } = await import("./authentication-69q16waz.js");
|
|
111713
111785
|
const localBus = new AgentEventBus;
|
|
111714
111786
|
AgentEventBus.attachChild(localBus, ctx.eventBus, subagentId);
|
|
111715
111787
|
const result = await runAuthenticationAgent({
|
|
@@ -111728,7 +111800,7 @@ When to use delegate_to_auth_subagent vs authenticate_session:
|
|
|
111728
111800
|
parentSubagentId: ctx.subagentId
|
|
111729
111801
|
});
|
|
111730
111802
|
if (result.success) {
|
|
111731
|
-
const sessionInfoPath =
|
|
111803
|
+
const sessionInfoPath = join10(ctx.session.rootPath, "session-info.json");
|
|
111732
111804
|
const sessionInfo = {
|
|
111733
111805
|
authenticated: true,
|
|
111734
111806
|
username: username || "via_subagent",
|
|
@@ -111922,8 +111994,8 @@ function detectBarrier(bodyLower, statusCode) {
|
|
|
111922
111994
|
// src/core/agents/offSecAgent/tools/documentApp.ts
|
|
111923
111995
|
init_dist();
|
|
111924
111996
|
init_zod();
|
|
111925
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
111926
|
-
import { join as
|
|
111997
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
111998
|
+
import { join as join11 } from "node:path";
|
|
111927
111999
|
function sanitizeName(name) {
|
|
111928
112000
|
return name.toLowerCase().replace(/[^a-z0-9-_.]/g, "_");
|
|
111929
112001
|
}
|
|
@@ -111962,7 +112034,7 @@ function validateDomainUrl(value) {
|
|
|
111962
112034
|
}
|
|
111963
112035
|
}
|
|
111964
112036
|
function documentApp(ctx) {
|
|
111965
|
-
const baseAppsPath =
|
|
112037
|
+
const baseAppsPath = join11(ctx.session.rootPath, "apps");
|
|
111966
112038
|
return tool({
|
|
111967
112039
|
description: `Document a discovered application during attack surface analysis.
|
|
111968
112040
|
|
|
@@ -112015,7 +112087,7 @@ Each application creates a JSON file in the apps directory for tracking and anal
|
|
|
112015
112087
|
const sanitizedName = sanitizeName(input.appName);
|
|
112016
112088
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
112017
112089
|
const filename = `app_${sanitizedName}_${timestamp}.json`;
|
|
112018
|
-
const filepath =
|
|
112090
|
+
const filepath = join11(baseAppsPath, filename);
|
|
112019
112091
|
const appRecord = {
|
|
112020
112092
|
...input,
|
|
112021
112093
|
discoveredAt: new Date().toISOString(),
|
|
@@ -112038,8 +112110,8 @@ Each application creates a JSON file in the apps directory for tracking and anal
|
|
|
112038
112110
|
// src/core/agents/offSecAgent/tools/documentEndpoint.ts
|
|
112039
112111
|
init_dist();
|
|
112040
112112
|
init_zod();
|
|
112041
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "fs";
|
|
112042
|
-
import { join as
|
|
112113
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "node:fs";
|
|
112114
|
+
import { join as join12 } from "node:path";
|
|
112043
112115
|
|
|
112044
112116
|
// src/core/agents/specialized/attackSurface/blackboxRiskScoring.ts
|
|
112045
112117
|
var RISK_LEVEL_BASE_SCORE = {
|
|
@@ -112091,7 +112163,7 @@ function computeExposure(riskLevel, details) {
|
|
|
112091
112163
|
}
|
|
112092
112164
|
return Math.min(3, Math.max(0, base));
|
|
112093
112165
|
}
|
|
112094
|
-
function computeDataSensitivity(riskLevel,
|
|
112166
|
+
function computeDataSensitivity(riskLevel, _assetType, details) {
|
|
112095
112167
|
if (riskLevel === "CRITICAL")
|
|
112096
112168
|
return 3;
|
|
112097
112169
|
let score = riskLevel === "HIGH" ? 2 : riskLevel === "MEDIUM" ? 1 : 0;
|
|
@@ -112389,7 +112461,7 @@ async function generateThreatModelForEndpoint(ctx, input) {
|
|
|
112389
112461
|
return threatModelLimiter(async () => {
|
|
112390
112462
|
if (ctx.abortSignal?.aborted)
|
|
112391
112463
|
return null;
|
|
112392
|
-
const { CodeAgent } = await import("./agent-
|
|
112464
|
+
const { CodeAgent } = await import("./agent-m3bsrs09.js");
|
|
112393
112465
|
const subagentId = `threat-model-${sanitize(input.appName)}-${sanitize(input.routePath)}`;
|
|
112394
112466
|
ctx.eventBus?.emit("subagent-spawn", {
|
|
112395
112467
|
subagentId,
|
|
@@ -112697,7 +112769,7 @@ var documentEndpointInputSchema = exports_external.object({
|
|
|
112697
112769
|
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
|
|
112698
112770
|
});
|
|
112699
112771
|
function documentEndpoint(ctx) {
|
|
112700
|
-
const baseAssetsPath =
|
|
112772
|
+
const baseAssetsPath = join12(ctx.session.rootPath, "assets");
|
|
112701
112773
|
return tool({
|
|
112702
112774
|
description: `Document a discovered endpoint during attack surface analysis.
|
|
112703
112775
|
|
|
@@ -112743,14 +112815,14 @@ Each endpoint creates a JSON file in the assets directory for tracking and analy
|
|
|
112743
112815
|
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
112816
|
};
|
|
112745
112817
|
}
|
|
112746
|
-
const targetDir =
|
|
112818
|
+
const targetDir = join12(baseAssetsPath, sanitizeName2(input.appName));
|
|
112747
112819
|
if (!existsSync7(targetDir)) {
|
|
112748
112820
|
mkdirSync6(targetDir, { recursive: true });
|
|
112749
112821
|
}
|
|
112750
112822
|
const sanitizedPath = sanitizeName2(input.routePath);
|
|
112751
112823
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
112752
112824
|
const filename = `asset_${sanitizedPath}_${timestamp}.json`;
|
|
112753
|
-
const filepath =
|
|
112825
|
+
const filepath = join12(targetDir, filename);
|
|
112754
112826
|
const heuristicRiskScore = computeBlackboxRiskScore(input.riskLevel, "endpoint", {
|
|
112755
112827
|
url: input.routePath,
|
|
112756
112828
|
method: input.method,
|
|
@@ -112819,7 +112891,7 @@ Each endpoint creates a JSON file in the assets directory for tracking and analy
|
|
|
112819
112891
|
// src/core/agents/offSecAgent/tools/documentFinding.ts
|
|
112820
112892
|
init_dist();
|
|
112821
112893
|
init_zod();
|
|
112822
|
-
import { spawn as spawn2 } from "child_process";
|
|
112894
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
112823
112895
|
import {
|
|
112824
112896
|
appendFileSync,
|
|
112825
112897
|
chmodSync,
|
|
@@ -112827,8 +112899,8 @@ import {
|
|
|
112827
112899
|
mkdirSync as mkdirSync7,
|
|
112828
112900
|
unlinkSync,
|
|
112829
112901
|
writeFileSync as writeFileSync10
|
|
112830
|
-
} from "fs";
|
|
112831
|
-
import { join as
|
|
112902
|
+
} from "node:fs";
|
|
112903
|
+
import { join as join13 } from "node:path";
|
|
112832
112904
|
|
|
112833
112905
|
// src/lib/cwe/types.ts
|
|
112834
112906
|
init_zod();
|
|
@@ -113327,7 +113399,7 @@ function getMetricDistance(metrics, metric) {
|
|
|
113327
113399
|
if (!levels)
|
|
113328
113400
|
return 0;
|
|
113329
113401
|
let value;
|
|
113330
|
-
const modifiedMetric =
|
|
113402
|
+
const modifiedMetric = `M${metric}`;
|
|
113331
113403
|
const metricsRecord = metrics;
|
|
113332
113404
|
if (metricsRecord[modifiedMetric] && metricsRecord[modifiedMetric] !== "X") {
|
|
113333
113405
|
value = metricsRecord[modifiedMetric] ?? "";
|
|
@@ -113349,12 +113421,12 @@ function getNextLowerScore(macroVector, position) {
|
|
|
113349
113421
|
return MACROVECTOR_LOOKUP[nextMacroVector] ?? null;
|
|
113350
113422
|
}
|
|
113351
113423
|
function interpolateScore(metrics, macroVector, baseScore) {
|
|
113352
|
-
const eq1 = parseInt(macroVector[0]);
|
|
113353
|
-
const eq2 = parseInt(macroVector[1]);
|
|
113354
|
-
const eq3 = parseInt(macroVector[2]);
|
|
113355
|
-
const eq4 = parseInt(macroVector[3]);
|
|
113356
|
-
const eq5 = parseInt(macroVector[4]);
|
|
113357
|
-
const eq6 = parseInt(macroVector[5]);
|
|
113424
|
+
const eq1 = parseInt(macroVector[0], 10);
|
|
113425
|
+
const eq2 = parseInt(macroVector[1], 10);
|
|
113426
|
+
const eq3 = parseInt(macroVector[2], 10);
|
|
113427
|
+
const eq4 = parseInt(macroVector[3], 10);
|
|
113428
|
+
const eq5 = parseInt(macroVector[4], 10);
|
|
113429
|
+
const eq6 = parseInt(macroVector[5], 10);
|
|
113358
113430
|
let meanDistance = 0;
|
|
113359
113431
|
let eqCount = 0;
|
|
113360
113432
|
const eq1NextLower = getNextLowerScore(macroVector, 0);
|
|
@@ -114063,6 +114135,30 @@ var CVSS_SCORER_SYSTEM_PROMPT = `You are a CVSS 4.0 scoring specialist. Your tas
|
|
|
114063
114135
|
- If production-like or confirmed unintentional: **E:A**
|
|
114064
114136
|
- Severity: typically **HIGH-CRITICAL** for real bypasses, **MEDIUM** for intentional test credentials in sandbox
|
|
114065
114137
|
|
|
114138
|
+
### Denial of Service / Resource Exhaustion (unbounded input, algorithmic complexity, amplification)
|
|
114139
|
+
- Typically: **AV:N, AC:L, AT:N, PR** varies (often **L** for authenticated endpoints), **UI:N**
|
|
114140
|
+
- **VC:N, VI:N** — DoS has no confidentiality/integrity impact unless separately demonstrated
|
|
114141
|
+
- **VA** must reflect what the evidence actually proves, not the worst case it implies:
|
|
114142
|
+
- **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**.
|
|
114143
|
+
- **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.
|
|
114144
|
+
- **E:P** when only the resource-consumption gap was demonstrated; **E:A** only if an actual outage was produced and observed during testing.
|
|
114145
|
+
- Severity range: typically **LOW-MEDIUM**. Reserve **HIGH** for a demonstrated full outage with no easy mitigation.
|
|
114146
|
+
|
|
114147
|
+
### OAuth Flow Weaknesses (missing PKCE, weak/predictable state, state integrity)
|
|
114148
|
+
- 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.
|
|
114149
|
+
- **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.
|
|
114150
|
+
- 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).
|
|
114151
|
+
- 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.
|
|
114152
|
+
|
|
114153
|
+
## Severity Calibration Principles
|
|
114154
|
+
|
|
114155
|
+
Apply these across every vulnerability class. They override the worst-case instinct.
|
|
114156
|
+
|
|
114157
|
+
- **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.
|
|
114158
|
+
- **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**.
|
|
114159
|
+
- **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.
|
|
114160
|
+
- **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.
|
|
114161
|
+
|
|
114066
114162
|
## Analysis Instructions
|
|
114067
114163
|
|
|
114068
114164
|
1. Read the finding description and evidence carefully
|
|
@@ -114070,8 +114166,8 @@ var CVSS_SCORER_SYSTEM_PROMPT = `You are a CVSS 4.0 scoring specialist. Your tas
|
|
|
114070
114166
|
3. Assess complexity based on whether special conditions were needed
|
|
114071
114167
|
4. Determine privileges based on authentication requirements
|
|
114072
114168
|
5. Evaluate user interaction based on exploit mechanics
|
|
114073
|
-
6. Assess impact on both the vulnerable system AND potential subsequent systems
|
|
114074
|
-
7.
|
|
114169
|
+
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)
|
|
114170
|
+
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
114171
|
|
|
114076
114172
|
Always provide brief reasoning explaining your key decisions.
|
|
114077
114173
|
|
|
@@ -114103,6 +114199,8 @@ In addition to CVSS metrics, assign one or more CWE identifiers to the finding.
|
|
|
114103
114199
|
| Hardcoded Credentials | CWE-798 | CWE-259 (Hard-coded Password), CWE-321 (Hard-coded Crypto Key) |
|
|
114104
114200
|
| Captcha Bypass | CWE-804 | CWE-837 (Improper Enforcement of Single Access) |
|
|
114105
114201
|
| Missing Security Headers | CWE-1021 | CWE-693 (Protection Mechanism Failure), CWE-16 (Configuration) |
|
|
114202
|
+
| Denial of Service / Resource Exhaustion | CWE-400 | CWE-770 (Allocation Without Limits), CWE-1333 (Inefficient Regex) |
|
|
114203
|
+
| OAuth Flow Weakness (missing PKCE, weak state) | CWE-352 | CWE-345 (Insufficient Verification of Authenticity), CWE-1275 (Sensitive Cookie Weak Attributes) |
|
|
114106
114204
|
|
|
114107
114205
|
### CWE Assignment Rules
|
|
114108
114206
|
|
|
@@ -114142,7 +114240,7 @@ var MAX_IMPACT_CHARS = 2000;
|
|
|
114142
114240
|
function truncateField(value, limit) {
|
|
114143
114241
|
if (value.length <= limit)
|
|
114144
114242
|
return value;
|
|
114145
|
-
return value.substring(0, limit)
|
|
114243
|
+
return `${value.substring(0, limit)}
|
|
114146
114244
|
... [truncated]`;
|
|
114147
114245
|
}
|
|
114148
114246
|
function buildScoringPrompt(input) {
|
|
@@ -114228,8 +114326,8 @@ function extractContextSummary(messages) {
|
|
|
114228
114326
|
return `No additional context available from testing conversation.
|
|
114229
114327
|
`;
|
|
114230
114328
|
}
|
|
114231
|
-
return contextParts.join(`
|
|
114232
|
-
`)
|
|
114329
|
+
return `${contextParts.join(`
|
|
114330
|
+
`)}
|
|
114233
114331
|
`;
|
|
114234
114332
|
}
|
|
114235
114333
|
|
|
@@ -114239,7 +114337,7 @@ init_lazyLogger();
|
|
|
114239
114337
|
var log6 = scopedLogger(() => createLogger("FindingJudge"));
|
|
114240
114338
|
async function judgeFinding(input, ctx) {
|
|
114241
114339
|
try {
|
|
114242
|
-
const { FindingJudgeAgent } = await import("./agent-
|
|
114340
|
+
const { FindingJudgeAgent } = await import("./agent-3n8msbqb.js");
|
|
114243
114341
|
const agent = new FindingJudgeAgent({
|
|
114244
114342
|
finding: input,
|
|
114245
114343
|
model: ctx.model,
|
|
@@ -114529,14 +114627,14 @@ CRITICAL RULES — READ BEFORE CALLING:
|
|
|
114529
114627
|
description: `POC execution output for ${filename}`
|
|
114530
114628
|
});
|
|
114531
114629
|
const timestamp = new Date().toISOString();
|
|
114532
|
-
const outputDir = isVulnerability ? session.findingsPath :
|
|
114630
|
+
const outputDir = isVulnerability ? session.findingsPath : join13(session.rootPath, "informational");
|
|
114533
114631
|
if (!isVulnerability) {
|
|
114534
114632
|
mkdirSync7(outputDir, { recursive: true });
|
|
114535
114633
|
}
|
|
114536
114634
|
let evidenceForPrompt = materializedEvidence;
|
|
114537
114635
|
if (materializedEvidence.length > EVIDENCE_FILE_THRESHOLD) {
|
|
114538
114636
|
const evidenceFilename = `${timestamp.split("T")[0]}-${slugify2(input.title, 40)}-evidence.txt`;
|
|
114539
|
-
const evidenceFilePath =
|
|
114637
|
+
const evidenceFilePath = join13(outputDir, evidenceFilename);
|
|
114540
114638
|
writeFileSync10(evidenceFilePath, materializedEvidence);
|
|
114541
114639
|
evidenceForPrompt = materializedEvidence.substring(0, EVIDENCE_FILE_THRESHOLD) + `
|
|
114542
114640
|
... [truncated — full output saved to ${evidenceFilename}]`;
|
|
@@ -114678,8 +114776,8 @@ CRITICAL RULES — READ BEFORE CALLING:
|
|
|
114678
114776
|
const findingId = `${timestamp.split("T")[0]}-${slugify2(finding.title, 50)}`;
|
|
114679
114777
|
const jsonFilename = `${findingId}.json`;
|
|
114680
114778
|
const mdFilename = `${findingId}.md`;
|
|
114681
|
-
const jsonPath =
|
|
114682
|
-
const mdPath =
|
|
114779
|
+
const jsonPath = join13(outputDir, jsonFilename);
|
|
114780
|
+
const mdPath = join13(outputDir, mdFilename);
|
|
114683
114781
|
try {
|
|
114684
114782
|
writeFileSync10(jsonPath, JSON.stringify(findingWithMeta, null, 2));
|
|
114685
114783
|
const cvssSection = cvssWarning ? `## CVSS 4.0 Assessment
|
|
@@ -114762,7 +114860,7 @@ ${finding.references}` : ""}
|
|
|
114762
114860
|
`;
|
|
114763
114861
|
writeFileSync10(mdPath, markdown);
|
|
114764
114862
|
if (isVulnerability) {
|
|
114765
|
-
const summaryPath =
|
|
114863
|
+
const summaryPath = join13(session.rootPath, "findings-summary.md");
|
|
114766
114864
|
const cweTag = cvssResult.cwes?.length ? ` (${cvssResult.cwes.map((c) => c.id).join(", ")})` : "";
|
|
114767
114865
|
const cvssTag = cvssWarning ? "(estimated)" : `(CVSS ${cvssResult.score})`;
|
|
114768
114866
|
const summaryEntry = `- [${finding.severity}] ${cvssTag}${cweTag} ${finding.title} - \`findings/${mdFilename}\`
|
|
@@ -114820,7 +114918,7 @@ async function executeLocalPoc(ctx, input) {
|
|
|
114820
114918
|
mkdirSync7(pocsPath, { recursive: true });
|
|
114821
114919
|
}
|
|
114822
114920
|
const { filename, pocContent } = preparePoc(input);
|
|
114823
|
-
const pocPath =
|
|
114921
|
+
const pocPath = join13(pocsPath, filename);
|
|
114824
114922
|
writeFileSync10(pocPath, pocContent);
|
|
114825
114923
|
chmodSync(pocPath, 493);
|
|
114826
114924
|
const { stdout, stderr, exitCode } = await runScript(POC_RUNNERS[input.pocType], pocPath, 60000, ctx.abortSignal);
|
|
@@ -114838,7 +114936,7 @@ async function executeSandboxPoc(ctx, input) {
|
|
|
114838
114936
|
if (!existsSync8(localPocsPath)) {
|
|
114839
114937
|
mkdirSync7(localPocsPath, { recursive: true });
|
|
114840
114938
|
}
|
|
114841
|
-
const localPocPath =
|
|
114939
|
+
const localPocPath = join13(localPocsPath, filename);
|
|
114842
114940
|
writeFileSync10(localPocPath, pocContent);
|
|
114843
114941
|
const sandboxPocPath = `/tmp/pocs/${filename}`;
|
|
114844
114942
|
const base64Content = Buffer.from(pocContent).toString("base64");
|
|
@@ -114871,10 +114969,10 @@ POC file has been deleted.`,
|
|
|
114871
114969
|
}
|
|
114872
114970
|
function cleanupPocFiles(ctx, filename) {
|
|
114873
114971
|
try {
|
|
114874
|
-
unlinkSync(
|
|
114972
|
+
unlinkSync(join13(ctx.session.pocsPath, filename));
|
|
114875
114973
|
} catch {}
|
|
114876
114974
|
try {
|
|
114877
|
-
unlinkSync(
|
|
114975
|
+
unlinkSync(join13(ctx.session.pocsPath, `${filename}.output.json`));
|
|
114878
114976
|
} catch {}
|
|
114879
114977
|
}
|
|
114880
114978
|
function sanitizeFilename(str) {
|
|
@@ -114935,7 +115033,7 @@ ${commentChar} Created: ${new Date().toISOString()}
|
|
|
114935
115033
|
}
|
|
114936
115034
|
function writePocOutputSidecar(pocsPath, filename, stdout, stderr, exitCode, description) {
|
|
114937
115035
|
try {
|
|
114938
|
-
const outputPath =
|
|
115036
|
+
const outputPath = join13(pocsPath, `${filename}.output.json`);
|
|
114939
115037
|
writeFileSync10(outputPath, JSON.stringify({
|
|
114940
115038
|
stdout,
|
|
114941
115039
|
stderr,
|
|
@@ -117765,7 +117863,7 @@ function hasImapAttachments(bodyStructure) {
|
|
|
117765
117863
|
return false;
|
|
117766
117864
|
}
|
|
117767
117865
|
function truncate(s, max) {
|
|
117768
|
-
return s.length > max ? s.slice(0, max)
|
|
117866
|
+
return s.length > max ? `${s.slice(0, max)}
|
|
117769
117867
|
|
|
117770
117868
|
(truncated)` : s;
|
|
117771
117869
|
}
|
|
@@ -118005,7 +118103,8 @@ Sets the read/seen flag on a message so it no longer appears as unread.`,
|
|
|
118005
118103
|
|
|
118006
118104
|
// src/core/agents/offSecAgent/tools/email/sendEmail.ts
|
|
118007
118105
|
init_dist();
|
|
118008
|
-
import { readFile } from "fs/promises";
|
|
118106
|
+
import { readFile } from "node:fs/promises";
|
|
118107
|
+
import { basename } from "node:path";
|
|
118009
118108
|
|
|
118010
118109
|
// node_modules/mime-types/index.js
|
|
118011
118110
|
/*!
|
|
@@ -118126,7 +118225,6 @@ var $createTransport = function(transporter, defaults) {
|
|
|
118126
118225
|
|
|
118127
118226
|
// src/core/agents/offSecAgent/tools/email/sendEmail.ts
|
|
118128
118227
|
init_zod();
|
|
118129
|
-
import { basename } from "path";
|
|
118130
118228
|
var attachmentSchema = exports_external.object({
|
|
118131
118229
|
path: exports_external.string().describe("Absolute path to the file on disk to attach. The file is read and included inline."),
|
|
118132
118230
|
filename: exports_external.string().optional().describe("Override the attachment filename (defaults to the basename of the path)")
|
|
@@ -118249,13 +118347,181 @@ var SEND_EMAIL_TOOL_NAME = "send_email";
|
|
|
118249
118347
|
// src/core/agents/offSecAgent/tools/executeCommand.ts
|
|
118250
118348
|
init_dist();
|
|
118251
118349
|
init_zod();
|
|
118252
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync11 } from "fs";
|
|
118253
|
-
import { join as
|
|
118350
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
118351
|
+
import { join as join15 } from "node:path";
|
|
118352
|
+
|
|
118353
|
+
// src/core/prompt-injections/index.ts
|
|
118354
|
+
init_zod();
|
|
118355
|
+
import { createHash } from "node:crypto";
|
|
118356
|
+
import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
118357
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join14, relative, resolve as resolve2, sep } from "node:path";
|
|
118358
|
+
function sha256(value) {
|
|
118359
|
+
return createHash("sha256").update(value).digest("hex");
|
|
118360
|
+
}
|
|
118361
|
+
var PromptInjectionCatalogEntrySchema = exports_external.object({
|
|
118362
|
+
id: exports_external.string().min(1),
|
|
118363
|
+
name: exports_external.string().min(1),
|
|
118364
|
+
category: exports_external.enum([
|
|
118365
|
+
"instruction-hijack",
|
|
118366
|
+
"data-exfiltration",
|
|
118367
|
+
"tool-misuse",
|
|
118368
|
+
"role-confusion",
|
|
118369
|
+
"encoding"
|
|
118370
|
+
]),
|
|
118371
|
+
description: exports_external.string(),
|
|
118372
|
+
tags: exports_external.array(exports_external.string()).default([]),
|
|
118373
|
+
deliveryHints: exports_external.array(exports_external.string()).default([]),
|
|
118374
|
+
expectedObservation: exports_external.string().default(""),
|
|
118375
|
+
payloadPath: exports_external.string().min(1)
|
|
118376
|
+
});
|
|
118377
|
+
var PromptInjectionLibraryFileSchema = exports_external.union([
|
|
118378
|
+
exports_external.array(PromptInjectionCatalogEntrySchema),
|
|
118379
|
+
exports_external.object({
|
|
118380
|
+
payloads: exports_external.array(PromptInjectionCatalogEntrySchema)
|
|
118381
|
+
}),
|
|
118382
|
+
exports_external.object({
|
|
118383
|
+
injections: exports_external.array(PromptInjectionCatalogEntrySchema)
|
|
118384
|
+
})
|
|
118385
|
+
]);
|
|
118386
|
+
function isPromptInjectionRef(value) {
|
|
118387
|
+
return typeof value === "object" && value !== null && value.kind === "prompt_injection_ref" && typeof value.id === "string";
|
|
118388
|
+
}
|
|
118389
|
+
|
|
118390
|
+
class StaticPromptInjectionLibrary {
|
|
118391
|
+
ordered;
|
|
118392
|
+
entries;
|
|
118393
|
+
hashes;
|
|
118394
|
+
constructor(entries) {
|
|
118395
|
+
this.ordered = entries;
|
|
118396
|
+
this.entries = new Map(entries.map((entry) => [entry.id, entry]));
|
|
118397
|
+
this.hashes = new Map(entries.map((entry) => [entry.id, sha256(entry.payload)]));
|
|
118398
|
+
}
|
|
118399
|
+
listCatalog() {
|
|
118400
|
+
return this.ordered.map(({ payload, payloadFilePath: _path, ...entry }) => ({
|
|
118401
|
+
...entry,
|
|
118402
|
+
payloadHash: sha256(payload)
|
|
118403
|
+
}));
|
|
118404
|
+
}
|
|
118405
|
+
getPayload(id) {
|
|
118406
|
+
return this.entries.get(id)?.payload;
|
|
118407
|
+
}
|
|
118408
|
+
getPayloadFilePath(id) {
|
|
118409
|
+
return this.entries.get(id)?.payloadFilePath;
|
|
118410
|
+
}
|
|
118411
|
+
getPayloadHash(id) {
|
|
118412
|
+
return this.hashes.get(id);
|
|
118413
|
+
}
|
|
118414
|
+
has(id) {
|
|
118415
|
+
return this.entries.has(id);
|
|
118416
|
+
}
|
|
118417
|
+
}
|
|
118418
|
+
var EMPTY_PROMPT_INJECTION_LIBRARY = new StaticPromptInjectionLibrary([]);
|
|
118419
|
+
var SOURCE_CACHE = new Map;
|
|
118420
|
+
function resolvePromptInjectionLibrarySource(explicit) {
|
|
118421
|
+
return explicit || process.env.PENSAR_PROMPT_INJECTION_LIBRARY || process.env.APEX_PROMPT_INJECTION_LIBRARY;
|
|
118422
|
+
}
|
|
118423
|
+
function resolveCatalogPath(source) {
|
|
118424
|
+
if (source.startsWith("https://") || source.startsWith("http://")) {
|
|
118425
|
+
throw new Error("Prompt injection library source must be a local path, not a URL.");
|
|
118426
|
+
}
|
|
118427
|
+
const path = source.startsWith("file://") ? source.slice(7) : source;
|
|
118428
|
+
const resolved = isAbsolute2(path) ? path : resolve2(process.cwd(), path);
|
|
118429
|
+
const stat = statSync2(resolved);
|
|
118430
|
+
if (stat.isDirectory()) {
|
|
118431
|
+
return { catalogPath: join14(resolved, "catalog.json"), root: resolved };
|
|
118432
|
+
}
|
|
118433
|
+
return { catalogPath: resolved, root: dirname5(resolved) };
|
|
118434
|
+
}
|
|
118435
|
+
function resolvePayloadPath(root, payloadPath) {
|
|
118436
|
+
if (isAbsolute2(payloadPath)) {
|
|
118437
|
+
throw new Error("Prompt injection payloadPath must be relative.");
|
|
118438
|
+
}
|
|
118439
|
+
const resolved = resolve2(root, payloadPath);
|
|
118440
|
+
const relativePath = relative(root, resolved);
|
|
118441
|
+
if (relativePath === ".." || relativePath.startsWith(`..${sep}`)) {
|
|
118442
|
+
throw new Error("Prompt injection payloadPath must stay within the library.");
|
|
118443
|
+
}
|
|
118444
|
+
return resolved;
|
|
118445
|
+
}
|
|
118446
|
+
function parsePromptInjectionLibrary(raw, root) {
|
|
118447
|
+
const parsed = PromptInjectionLibraryFileSchema.parse(JSON.parse(raw));
|
|
118448
|
+
const catalogEntries = Array.isArray(parsed) ? parsed : ("payloads" in parsed) ? parsed.payloads : parsed.injections;
|
|
118449
|
+
const entries = catalogEntries.map((entry) => {
|
|
118450
|
+
const payloadFile = resolvePayloadPath(root, entry.payloadPath);
|
|
118451
|
+
const payload = readFileSync2(payloadFile, "utf-8");
|
|
118452
|
+
const { payloadPath: _payloadPath, ...safeEntry } = entry;
|
|
118453
|
+
return { ...safeEntry, payload, payloadFilePath: payloadFile };
|
|
118454
|
+
});
|
|
118455
|
+
return new StaticPromptInjectionLibrary(entries);
|
|
118456
|
+
}
|
|
118457
|
+
async function readSource(source) {
|
|
118458
|
+
if (source.startsWith("https://") || source.startsWith("http://")) {
|
|
118459
|
+
throw new Error("Prompt injection library source must be a local path, not a URL.");
|
|
118460
|
+
}
|
|
118461
|
+
const { catalogPath, root } = resolveCatalogPath(source);
|
|
118462
|
+
return parsePromptInjectionLibrary(readFileSync2(catalogPath, "utf-8"), root);
|
|
118463
|
+
}
|
|
118464
|
+
async function loadPromptInjectionLibrary(source) {
|
|
118465
|
+
let cached = SOURCE_CACHE.get(source);
|
|
118466
|
+
if (!cached) {
|
|
118467
|
+
cached = readSource(source).catch((err) => {
|
|
118468
|
+
SOURCE_CACHE.delete(source);
|
|
118469
|
+
throw err;
|
|
118470
|
+
});
|
|
118471
|
+
SOURCE_CACHE.set(source, cached);
|
|
118472
|
+
}
|
|
118473
|
+
return cached;
|
|
118474
|
+
}
|
|
118475
|
+
async function getPromptInjectionLibrary(opts) {
|
|
118476
|
+
if (opts?.library)
|
|
118477
|
+
return opts.library;
|
|
118478
|
+
const source = resolvePromptInjectionLibrarySource(opts?.source);
|
|
118479
|
+
if (!source)
|
|
118480
|
+
return EMPTY_PROMPT_INJECTION_LIBRARY;
|
|
118481
|
+
return loadPromptInjectionLibrary(source);
|
|
118482
|
+
}
|
|
118483
|
+
function resolvePromptInjectionRefs(value, library) {
|
|
118484
|
+
if (isPromptInjectionRef(value)) {
|
|
118485
|
+
const payload = library.getPayload(value.id);
|
|
118486
|
+
if (!payload)
|
|
118487
|
+
throw new Error(`Unknown prompt injection id: ${value.id}`);
|
|
118488
|
+
return payload;
|
|
118489
|
+
}
|
|
118490
|
+
if (Array.isArray(value)) {
|
|
118491
|
+
return value.map((item) => resolvePromptInjectionRefs(item, library));
|
|
118492
|
+
}
|
|
118493
|
+
if (typeof value === "object" && value !== null) {
|
|
118494
|
+
const resolved = {};
|
|
118495
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
118496
|
+
resolved[key] = resolvePromptInjectionRefs(nested, library);
|
|
118497
|
+
}
|
|
118498
|
+
return resolved;
|
|
118499
|
+
}
|
|
118500
|
+
return value;
|
|
118501
|
+
}
|
|
118502
|
+
function redactPromptInjectionPayloads(value, library) {
|
|
118503
|
+
let redacted = value;
|
|
118504
|
+
for (const entry of library.listCatalog()) {
|
|
118505
|
+
const payload = library.getPayload(entry.id);
|
|
118506
|
+
if (!payload)
|
|
118507
|
+
continue;
|
|
118508
|
+
redacted = redacted.split(payload).join(`[PROMPT_INJECTION:${entry.id}]`);
|
|
118509
|
+
}
|
|
118510
|
+
return redacted;
|
|
118511
|
+
}
|
|
118512
|
+
|
|
118513
|
+
// src/core/agents/offSecAgent/tools/executeCommand.ts
|
|
118254
118514
|
var MAX_INLINE = 50000;
|
|
118255
118515
|
var MS_TIMEOUT_THRESHOLD = 1e4;
|
|
118516
|
+
var DEFAULT_PROMPT_INJECTION_FILE_ENV = "APEX_PROMPT_INJECTION_FILE";
|
|
118517
|
+
var promptInjectionPointerSchema = exports_external.object({
|
|
118518
|
+
id: exports_external.string().min(1).describe("Stable prompt-injection id returned by list_prompt_injections."),
|
|
118519
|
+
envVar: exports_external.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional().describe(`Environment variable that will contain the payload file path at runtime. Defaults to ${DEFAULT_PROMPT_INJECTION_FILE_ENV}.`)
|
|
118520
|
+
});
|
|
118256
118521
|
var executeCommandInputSchema = exports_external.object({
|
|
118257
118522
|
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing (e.g., 'Scanning for open ports on target')"),
|
|
118258
118523
|
command: exports_external.string().describe("The shell command to execute"),
|
|
118524
|
+
promptInjection: promptInjectionPointerSchema.optional().describe("Optional runtime prompt-injection file pointer. The tool resolves the id to a local payload file path and exposes that path through envVar. The raw payload text is never inserted into the command."),
|
|
118259
118525
|
timeout: exports_external.number().optional().describe("Timeout in seconds. If omitted, the command runs until completion or abort."),
|
|
118260
118526
|
allow_unprotected: exports_external.boolean().optional().describe("Acknowledge that this command will run WITHOUT the session's configured custom HTTP headers because the tool is unrecognized or the command is pipelined. Defaults to false (fail-closed). Use only when you understand the headers will not be applied.")
|
|
118261
118527
|
});
|
|
@@ -118272,13 +118538,13 @@ function maybeSaveFullOutput(raw, ctx) {
|
|
|
118272
118538
|
if (raw.length <= MAX_INLINE) {
|
|
118273
118539
|
return { text: raw || "(no output)" };
|
|
118274
118540
|
}
|
|
118275
|
-
const outputDir =
|
|
118541
|
+
const outputDir = join15(ctx.session.logsPath, "cmd-output");
|
|
118276
118542
|
if (!existsSync9(outputDir)) {
|
|
118277
118543
|
mkdirSync8(outputDir, { recursive: true });
|
|
118278
118544
|
}
|
|
118279
118545
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
118280
118546
|
const filename = `output-${ts}.txt`;
|
|
118281
|
-
const filePath =
|
|
118547
|
+
const filePath = join15(outputDir, filename);
|
|
118282
118548
|
try {
|
|
118283
118549
|
writeFileSync11(filePath, raw);
|
|
118284
118550
|
} catch {
|
|
@@ -118296,6 +118562,49 @@ function maybeSaveFullOutput(raw, ctx) {
|
|
|
118296
118562
|
file: filePath
|
|
118297
118563
|
};
|
|
118298
118564
|
}
|
|
118565
|
+
function shellQuote2(value) {
|
|
118566
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
118567
|
+
}
|
|
118568
|
+
function wrapCommandWithEnv(command, envVars) {
|
|
118569
|
+
if (!envVars || Object.keys(envVars).length === 0)
|
|
118570
|
+
return command;
|
|
118571
|
+
const assignments = Object.entries(envVars).map(([name, value]) => {
|
|
118572
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
118573
|
+
throw new Error(`Invalid environment variable name: ${name}`);
|
|
118574
|
+
}
|
|
118575
|
+
return `${name}=${shellQuote2(value)}`;
|
|
118576
|
+
}).join(" ");
|
|
118577
|
+
return `env ${assignments} bash -lc ${shellQuote2(command)}`;
|
|
118578
|
+
}
|
|
118579
|
+
async function resolvePromptInjectionEnv(promptInjection, ctx) {
|
|
118580
|
+
if (!promptInjection)
|
|
118581
|
+
return {};
|
|
118582
|
+
const library = await getPromptInjectionLibrary({
|
|
118583
|
+
library: ctx.promptInjectionLibrary,
|
|
118584
|
+
source: ctx.promptInjectionLibrarySource
|
|
118585
|
+
});
|
|
118586
|
+
const payloadContent = library.getPayload(promptInjection.id);
|
|
118587
|
+
if (payloadContent === undefined) {
|
|
118588
|
+
return {
|
|
118589
|
+
library,
|
|
118590
|
+
error: `Unknown prompt injection id: ${promptInjection.id}`
|
|
118591
|
+
};
|
|
118592
|
+
}
|
|
118593
|
+
const payloadFilePath = library.getPayloadFilePath(promptInjection.id);
|
|
118594
|
+
if (!payloadFilePath) {
|
|
118595
|
+
return {
|
|
118596
|
+
library,
|
|
118597
|
+
error: `Unknown prompt injection id or no payload file path available: ` + promptInjection.id
|
|
118598
|
+
};
|
|
118599
|
+
}
|
|
118600
|
+
return {
|
|
118601
|
+
library,
|
|
118602
|
+
payloadContent,
|
|
118603
|
+
envVars: {
|
|
118604
|
+
[promptInjection.envVar ?? DEFAULT_PROMPT_INJECTION_FILE_ENV]: payloadFilePath
|
|
118605
|
+
}
|
|
118606
|
+
};
|
|
118607
|
+
}
|
|
118299
118608
|
function executeCommand(ctx) {
|
|
118300
118609
|
return tool({
|
|
118301
118610
|
description: `Execute a shell command for penetration testing activities.
|
|
@@ -118356,10 +118665,18 @@ results to disk before any signal arrives.
|
|
|
118356
118665
|
- nmap: prefer --host-timeout, --max-rtt-timeout, and -T4 / --min-rate
|
|
118357
118666
|
to bound total runtime against slow networks.
|
|
118358
118667
|
|
|
118668
|
+
PROMPT-INJECTION PAYLOADS:
|
|
118669
|
+
- Do not put raw prompt-injection payload text in the command.
|
|
118670
|
+
- To run a local harness with a payload, set promptInjection.id to an id from
|
|
118671
|
+
list_prompt_injections and reference "$APEX_PROMPT_INJECTION_FILE" in the
|
|
118672
|
+
command. The tool resolves that variable to the local payload file path only
|
|
118673
|
+
at runtime.
|
|
118674
|
+
|
|
118359
118675
|
IMPORTANT: Always analyze results and adjust your approach based on findings.`,
|
|
118360
118676
|
inputSchema: executeCommandInputSchema,
|
|
118361
118677
|
execute: async ({
|
|
118362
118678
|
command,
|
|
118679
|
+
promptInjection,
|
|
118363
118680
|
timeout,
|
|
118364
118681
|
allow_unprotected
|
|
118365
118682
|
}) => {
|
|
@@ -118398,7 +118715,35 @@ IMPORTANT: Always analyze results and adjust your approach based on findings.`,
|
|
|
118398
118715
|
command
|
|
118399
118716
|
};
|
|
118400
118717
|
}
|
|
118401
|
-
const
|
|
118718
|
+
const commandWithHeaders = inject.status === "injected" ? inject.command : command;
|
|
118719
|
+
let promptInjectionLibrary;
|
|
118720
|
+
let promptInjectionEnvVars;
|
|
118721
|
+
let promptInjectionPayloadContent;
|
|
118722
|
+
try {
|
|
118723
|
+
const resolved = await resolvePromptInjectionEnv(promptInjection, ctx);
|
|
118724
|
+
if (resolved.error) {
|
|
118725
|
+
return {
|
|
118726
|
+
success: false,
|
|
118727
|
+
error: resolved.error,
|
|
118728
|
+
stdout: "",
|
|
118729
|
+
stderr: resolved.error,
|
|
118730
|
+
command
|
|
118731
|
+
};
|
|
118732
|
+
}
|
|
118733
|
+
promptInjectionLibrary = resolved.library;
|
|
118734
|
+
promptInjectionEnvVars = resolved.envVars;
|
|
118735
|
+
promptInjectionPayloadContent = resolved.payloadContent;
|
|
118736
|
+
} catch (error) {
|
|
118737
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
118738
|
+
return {
|
|
118739
|
+
success: false,
|
|
118740
|
+
error: msg,
|
|
118741
|
+
stdout: "",
|
|
118742
|
+
stderr: msg,
|
|
118743
|
+
command
|
|
118744
|
+
};
|
|
118745
|
+
}
|
|
118746
|
+
const redact = (value) => promptInjectionLibrary ? redactPromptInjectionPayloads(value, promptInjectionLibrary) : value;
|
|
118402
118747
|
if (ctx.sandbox) {
|
|
118403
118748
|
try {
|
|
118404
118749
|
const ssmOpts = {};
|
|
@@ -118406,13 +118751,38 @@ IMPORTANT: Always analyze results and adjust your approach based on findings.`,
|
|
|
118406
118751
|
if (normalizedTimeout != null) {
|
|
118407
118752
|
ssmOpts.timeout = normalizedTimeout;
|
|
118408
118753
|
}
|
|
118409
|
-
|
|
118410
|
-
|
|
118754
|
+
if (promptInjectionPayloadContent && promptInjection) {
|
|
118755
|
+
const envVarName = promptInjection.envVar ?? DEFAULT_PROMPT_INJECTION_FILE_ENV;
|
|
118756
|
+
const sandboxTempFile = `/tmp/apex_payload_${Date.now()}.txt`;
|
|
118757
|
+
const escapedPayload = promptInjectionPayloadContent.replace(/\\/g, "\\\\").replace(/'/g, "'\\''");
|
|
118758
|
+
const writeCommand = `printf '%s' '${escapedPayload}' > ${sandboxTempFile}`;
|
|
118759
|
+
const writeResult = await ctx.sandbox.execute(writeCommand, {
|
|
118760
|
+
timeout: normalizedTimeout ?? 30
|
|
118761
|
+
});
|
|
118762
|
+
if (!writeResult.success) {
|
|
118763
|
+
const errorMsg = `Failed to write prompt injection payload to sandbox: ${writeResult.stderr || "unknown error"}`;
|
|
118764
|
+
return {
|
|
118765
|
+
success: false,
|
|
118766
|
+
error: errorMsg,
|
|
118767
|
+
stdout: writeResult.stdout,
|
|
118768
|
+
stderr: writeResult.stderr || errorMsg,
|
|
118769
|
+
command
|
|
118770
|
+
};
|
|
118771
|
+
}
|
|
118772
|
+
ssmOpts.envVars = {
|
|
118773
|
+
[envVarName]: sandboxTempFile
|
|
118774
|
+
};
|
|
118775
|
+
} else if (promptInjectionEnvVars) {
|
|
118776
|
+
ssmOpts.envVars = promptInjectionEnvVars;
|
|
118777
|
+
}
|
|
118778
|
+
const result = await ctx.sandbox.execute(commandWithHeaders, ssmOpts);
|
|
118779
|
+
const { text: stdout, file: outputFile } = maybeSaveFullOutput(redact(result.stdout), ctx);
|
|
118780
|
+
const stderr = redact(result.stderr || "");
|
|
118411
118781
|
return {
|
|
118412
118782
|
success: result.success,
|
|
118413
|
-
error: !result.success ?
|
|
118783
|
+
error: !result.success ? stderr || "Command failed" : "",
|
|
118414
118784
|
stdout,
|
|
118415
|
-
stderr
|
|
118785
|
+
stderr,
|
|
118416
118786
|
command,
|
|
118417
118787
|
outputFile
|
|
118418
118788
|
};
|
|
@@ -118430,14 +118800,15 @@ IMPORTANT: Always analyze results and adjust your approach based on findings.`,
|
|
|
118430
118800
|
if (ctx.persistentShell) {
|
|
118431
118801
|
try {
|
|
118432
118802
|
const normalizedTimeout = normalizeExecuteCommandTimeout(timeout);
|
|
118433
|
-
const onData = ctx.eventBus ? (data) => ctx.eventBus
|
|
118434
|
-
const result = await ctx.persistentShell.execute(
|
|
118435
|
-
const { text: stdout, file: outputFile } = maybeSaveFullOutput(result.stdout, ctx);
|
|
118803
|
+
const onData = ctx.eventBus ? (data) => ctx.eventBus?.emit("command-output", { data: redact(data) }) : undefined;
|
|
118804
|
+
const result = await ctx.persistentShell.execute(wrapCommandWithEnv(commandWithHeaders, promptInjectionEnvVars), normalizedTimeout, onData, ctx.abortSignal);
|
|
118805
|
+
const { text: stdout, file: outputFile } = maybeSaveFullOutput(redact(result.stdout), ctx);
|
|
118806
|
+
const stderr = redact(result.stderr);
|
|
118436
118807
|
return {
|
|
118437
118808
|
success: result.exitCode === 0,
|
|
118438
118809
|
error: result.exitCode === 124 ? "Command timed out" : result.exitCode !== 0 ? `Exit code: ${result.exitCode}` : "",
|
|
118439
118810
|
stdout,
|
|
118440
|
-
stderr
|
|
118811
|
+
stderr,
|
|
118441
118812
|
command,
|
|
118442
118813
|
outputFile
|
|
118443
118814
|
};
|
|
@@ -118654,7 +119025,7 @@ BEST PRACTICES:
|
|
|
118654
119025
|
// src/core/agents/offSecAgent/tools/grep.ts
|
|
118655
119026
|
init_dist();
|
|
118656
119027
|
init_zod();
|
|
118657
|
-
import { spawn as spawn3 } from "child_process";
|
|
119028
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
118658
119029
|
var grepInputSchema = exports_external.object({
|
|
118659
119030
|
pattern: exports_external.string().describe("The pattern to search for"),
|
|
118660
119031
|
directory: exports_external.string().optional().describe("Directory or file path to search in (defaults to current working directory)"),
|
|
@@ -118698,7 +119069,7 @@ search with flags or a more specific directory if results are truncated.`,
|
|
|
118698
119069
|
const defaultFlags = hasRecursive ? [] : ["-r"];
|
|
118699
119070
|
const args = [...defaultFlags, ...userFlags, "--", pattern, dir];
|
|
118700
119071
|
const command = `grep ${args.join(" ")}`;
|
|
118701
|
-
return new Promise((
|
|
119072
|
+
return new Promise((resolve3) => {
|
|
118702
119073
|
const child = spawn3("grep", args, {
|
|
118703
119074
|
cwd,
|
|
118704
119075
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -118712,7 +119083,7 @@ search with flags or a more specific directory if results are truncated.`,
|
|
|
118712
119083
|
ctx.abortSignal.addEventListener("abort", abortHandler, {
|
|
118713
119084
|
once: true
|
|
118714
119085
|
});
|
|
118715
|
-
abortCleanup = () => ctx.abortSignal
|
|
119086
|
+
abortCleanup = () => ctx.abortSignal?.removeEventListener("abort", abortHandler);
|
|
118716
119087
|
}
|
|
118717
119088
|
const safeResolve = (result) => {
|
|
118718
119089
|
if (resolved)
|
|
@@ -118720,7 +119091,7 @@ search with flags or a more specific directory if results are truncated.`,
|
|
|
118720
119091
|
resolved = true;
|
|
118721
119092
|
clearTimeout(timeout);
|
|
118722
119093
|
abortCleanup?.();
|
|
118723
|
-
|
|
119094
|
+
resolve3(result);
|
|
118724
119095
|
};
|
|
118725
119096
|
const timeout = setTimeout(() => {
|
|
118726
119097
|
child.kill("SIGTERM");
|
|
@@ -118764,28 +119135,44 @@ search with flags or a more specific directory if results are truncated.`,
|
|
|
118764
119135
|
// src/core/agents/offSecAgent/tools/httpRequest.ts
|
|
118765
119136
|
init_dist();
|
|
118766
119137
|
init_zod();
|
|
118767
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync12 } from "fs";
|
|
118768
|
-
import { join as
|
|
119138
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync12 } from "node:fs";
|
|
119139
|
+
import { join as join16 } from "node:path";
|
|
118769
119140
|
var MAX_INLINE_BODY = 5000;
|
|
119141
|
+
var promptInjectionRefSchema = exports_external.object({
|
|
119142
|
+
kind: exports_external.literal("prompt_injection_ref"),
|
|
119143
|
+
id: exports_external.string().describe("Stable prompt-injection id returned by list_prompt_injections")
|
|
119144
|
+
});
|
|
118770
119145
|
var httpRequestInputSchema = exports_external.object({
|
|
118771
119146
|
url: exports_external.string().describe("The URL to request"),
|
|
118772
119147
|
method: exports_external.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]).default("GET"),
|
|
118773
119148
|
headers: exports_external.string().optional().describe(`HTTP headers as a JSON-encoded object string, e.g. '{"Content-Type": "application/json", "Authorization": "Bearer token"}'`),
|
|
118774
|
-
body: exports_external.string().optional().describe("Request body (for POST, PUT, PATCH)"),
|
|
119149
|
+
body: exports_external.union([exports_external.string(), promptInjectionRefSchema]).optional().describe("Request body (for POST, PUT, PATCH). To use a hidden prompt-injection payload, pass a PromptInjectionRef object instead of raw payload text."),
|
|
118775
119150
|
followRedirects: exports_external.boolean().default(false).describe("Whether to follow HTTP redirects (3xx). Defaults to false so you can see redirect responses with Location and Set-Cookie headers."),
|
|
118776
119151
|
timeout: exports_external.number().default(1e4),
|
|
118777
119152
|
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing (e.g., 'Testing SQL injection on login endpoint')")
|
|
118778
119153
|
});
|
|
119154
|
+
function containsPromptInjectionRef(value) {
|
|
119155
|
+
if (typeof value === "object" && value !== null && value.kind === "prompt_injection_ref") {
|
|
119156
|
+
return true;
|
|
119157
|
+
}
|
|
119158
|
+
if (Array.isArray(value)) {
|
|
119159
|
+
return value.some((item) => containsPromptInjectionRef(item));
|
|
119160
|
+
}
|
|
119161
|
+
if (typeof value === "object" && value !== null) {
|
|
119162
|
+
return Object.values(value).some((nested) => containsPromptInjectionRef(nested));
|
|
119163
|
+
}
|
|
119164
|
+
return false;
|
|
119165
|
+
}
|
|
118779
119166
|
function maybeSaveBody(body, ctx) {
|
|
118780
119167
|
if (body.length <= MAX_INLINE_BODY)
|
|
118781
119168
|
return { text: body };
|
|
118782
|
-
const outputDir =
|
|
119169
|
+
const outputDir = join16(ctx.session.logsPath, "http-responses");
|
|
118783
119170
|
if (!existsSync10(outputDir)) {
|
|
118784
119171
|
mkdirSync9(outputDir, { recursive: true });
|
|
118785
119172
|
}
|
|
118786
119173
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
118787
119174
|
const filename = `response-${ts}.txt`;
|
|
118788
|
-
const filePath =
|
|
119175
|
+
const filePath = join16(outputDir, filename);
|
|
118789
119176
|
try {
|
|
118790
119177
|
writeFileSync12(filePath, body);
|
|
118791
119178
|
} catch {
|
|
@@ -118868,16 +119255,39 @@ COMMON TESTING PATTERNS:
|
|
|
118868
119255
|
}
|
|
118869
119256
|
throw e;
|
|
118870
119257
|
}
|
|
118871
|
-
|
|
119258
|
+
let headers = parseHeaders(rawHeaders);
|
|
119259
|
+
let resolvedBody;
|
|
119260
|
+
let library = EMPTY_PROMPT_INJECTION_LIBRARY;
|
|
119261
|
+
try {
|
|
119262
|
+
const needsLibrary = containsPromptInjectionRef(body) || containsPromptInjectionRef(headers);
|
|
119263
|
+
library = needsLibrary ? await getPromptInjectionLibrary({
|
|
119264
|
+
library: ctx.promptInjectionLibrary,
|
|
119265
|
+
source: ctx.promptInjectionLibrarySource
|
|
119266
|
+
}) : EMPTY_PROMPT_INJECTION_LIBRARY;
|
|
119267
|
+
headers = resolvePromptInjectionRefs(headers, library);
|
|
119268
|
+
resolvedBody = body === undefined ? undefined : String(resolvePromptInjectionRefs(body, library));
|
|
119269
|
+
} catch (e) {
|
|
119270
|
+
return {
|
|
119271
|
+
success: false,
|
|
119272
|
+
error: e instanceof Error ? e.message : String(e),
|
|
119273
|
+
url,
|
|
119274
|
+
method,
|
|
119275
|
+
status: 0,
|
|
119276
|
+
statusText: "",
|
|
119277
|
+
headers: {},
|
|
119278
|
+
body: "",
|
|
119279
|
+
redirected: false
|
|
119280
|
+
};
|
|
119281
|
+
}
|
|
118872
119282
|
if (ctx.sandbox) {
|
|
118873
119283
|
return executeSandboxHttpRequest(ctx, {
|
|
118874
119284
|
url,
|
|
118875
119285
|
method,
|
|
118876
119286
|
headers,
|
|
118877
|
-
body,
|
|
119287
|
+
body: resolvedBody,
|
|
118878
119288
|
followRedirects,
|
|
118879
119289
|
timeout
|
|
118880
|
-
});
|
|
119290
|
+
}, library);
|
|
118881
119291
|
}
|
|
118882
119292
|
let timeoutId;
|
|
118883
119293
|
try {
|
|
@@ -118900,7 +119310,7 @@ COMMON TESTING PATTERNS:
|
|
|
118900
119310
|
const response = await targetFetch(resolverSessionFromCtx(ctx), url, {
|
|
118901
119311
|
method,
|
|
118902
119312
|
headers,
|
|
118903
|
-
body:
|
|
119313
|
+
body: resolvedBody || undefined,
|
|
118904
119314
|
redirect: followRedirects ? "follow" : "manual",
|
|
118905
119315
|
signal: combinedSignal
|
|
118906
119316
|
});
|
|
@@ -118915,12 +119325,17 @@ COMMON TESTING PATTERNS:
|
|
|
118915
119325
|
} catch {
|
|
118916
119326
|
responseBody = "(unable to read response body)";
|
|
118917
119327
|
}
|
|
118918
|
-
const
|
|
119328
|
+
const redactedBody = redactPromptInjectionPayloads(responseBody, library);
|
|
119329
|
+
const redactedHeaders = Object.fromEntries(Object.entries(responseHeaders).map(([key, value]) => [
|
|
119330
|
+
key,
|
|
119331
|
+
redactPromptInjectionPayloads(value, library)
|
|
119332
|
+
]));
|
|
119333
|
+
const { text: truncatedBody } = maybeSaveBody(redactedBody, ctx);
|
|
118919
119334
|
return {
|
|
118920
119335
|
success: true,
|
|
118921
119336
|
status: response.status,
|
|
118922
119337
|
statusText: response.statusText,
|
|
118923
|
-
headers:
|
|
119338
|
+
headers: redactedHeaders,
|
|
118924
119339
|
body: truncatedBody,
|
|
118925
119340
|
url: response.url,
|
|
118926
119341
|
redirected: response.redirected
|
|
@@ -118945,7 +119360,7 @@ COMMON TESTING PATTERNS:
|
|
|
118945
119360
|
}
|
|
118946
119361
|
});
|
|
118947
119362
|
}
|
|
118948
|
-
async function executeSandboxHttpRequest(ctx, opts) {
|
|
119363
|
+
async function executeSandboxHttpRequest(ctx, opts, library) {
|
|
118949
119364
|
const { url, method, headers, body, followRedirects, timeout } = opts;
|
|
118950
119365
|
try {
|
|
118951
119366
|
let curlCommand = `curl -i -X ${method}`;
|
|
@@ -118953,9 +119368,30 @@ async function executeSandboxHttpRequest(ctx, opts) {
|
|
|
118953
119368
|
for (const [key, value] of Object.entries(mergedHeaders)) {
|
|
118954
119369
|
curlCommand += ` -H "${shellQuote(`${key}: ${value}`)}"`;
|
|
118955
119370
|
}
|
|
119371
|
+
const { sandbox } = ctx;
|
|
119372
|
+
if (!sandbox) {
|
|
119373
|
+
throw new Error("executeSandboxHttpRequest requires a sandbox");
|
|
119374
|
+
}
|
|
119375
|
+
let bodyTempFile = null;
|
|
118956
119376
|
if (body && ["POST", "PUT", "PATCH"].includes(method)) {
|
|
118957
|
-
|
|
118958
|
-
|
|
119377
|
+
bodyTempFile = `/tmp/apex_http_body_${Date.now()}_${Math.random().toString(36).slice(2, 11)}.txt`;
|
|
119378
|
+
const escapedForPrintf = body.replace(/\\/g, "\\\\").replace(/%/g, "%%");
|
|
119379
|
+
const writeCommand = `printf '%s' '${escapedForPrintf.replace(/'/g, "'\\''")}' > ${bodyTempFile}`;
|
|
119380
|
+
const writeResult = await sandbox.execute(writeCommand, { timeout: 30 });
|
|
119381
|
+
if (!writeResult.success || writeResult.exitCode !== 0) {
|
|
119382
|
+
return {
|
|
119383
|
+
success: false,
|
|
119384
|
+
error: `Failed to write request body to sandbox temp file: ${writeResult.stderr || writeResult.stdout}`,
|
|
119385
|
+
url,
|
|
119386
|
+
method,
|
|
119387
|
+
status: 0,
|
|
119388
|
+
statusText: "",
|
|
119389
|
+
headers: {},
|
|
119390
|
+
body: "",
|
|
119391
|
+
redirected: false
|
|
119392
|
+
};
|
|
119393
|
+
}
|
|
119394
|
+
curlCommand += ` --data-binary @${bodyTempFile}`;
|
|
118959
119395
|
}
|
|
118960
119396
|
if (followRedirects) {
|
|
118961
119397
|
curlCommand += " -L";
|
|
@@ -118964,7 +119400,7 @@ async function executeSandboxHttpRequest(ctx, opts) {
|
|
|
118964
119400
|
curlCommand += ` --max-time ${timeoutSeconds}`;
|
|
118965
119401
|
curlCommand += ` "${url}" 2>&1`;
|
|
118966
119402
|
const ssmTimeout = Math.max(timeoutSeconds, 30);
|
|
118967
|
-
const result = await
|
|
119403
|
+
const result = await sandbox.execute(curlCommand, {
|
|
118968
119404
|
timeout: ssmTimeout
|
|
118969
119405
|
});
|
|
118970
119406
|
const output = result.stdout || "";
|
|
@@ -118991,16 +119427,21 @@ async function executeSandboxHttpRequest(ctx, opts) {
|
|
|
118991
119427
|
}
|
|
118992
119428
|
}
|
|
118993
119429
|
const statusMatch = statusLine.match(/HTTP\/[\d.]+\s+(\d+)\s+(.+)/);
|
|
118994
|
-
const status = statusMatch ? parseInt(statusMatch[1]) : 0;
|
|
119430
|
+
const status = statusMatch ? parseInt(statusMatch[1], 10) : 0;
|
|
118995
119431
|
const statusText = statusMatch ? statusMatch[2] : "Unknown";
|
|
118996
119432
|
const responseBody = lines.slice(bodyStartIndex).join(`
|
|
118997
119433
|
`);
|
|
118998
|
-
const
|
|
119434
|
+
const redactedBody = redactPromptInjectionPayloads(responseBody, library);
|
|
119435
|
+
const redactedHeaders = Object.fromEntries(Object.entries(responseHeaders).map(([key, value]) => [
|
|
119436
|
+
key,
|
|
119437
|
+
redactPromptInjectionPayloads(value, library)
|
|
119438
|
+
]));
|
|
119439
|
+
const { text: truncatedBody } = maybeSaveBody(redactedBody, ctx);
|
|
118999
119440
|
return {
|
|
119000
119441
|
success: status >= 200 && status < 400,
|
|
119001
119442
|
status,
|
|
119002
119443
|
statusText,
|
|
119003
|
-
headers:
|
|
119444
|
+
headers: redactedHeaders,
|
|
119004
119445
|
body: truncatedBody,
|
|
119005
119446
|
url,
|
|
119006
119447
|
redirected: false
|
|
@@ -119023,8 +119464,8 @@ async function executeSandboxHttpRequest(ctx, opts) {
|
|
|
119023
119464
|
// src/core/agents/offSecAgent/tools/listFiles.ts
|
|
119024
119465
|
init_dist();
|
|
119025
119466
|
init_zod();
|
|
119026
|
-
import { readdir, stat } from "fs/promises";
|
|
119027
|
-
import { isAbsolute as
|
|
119467
|
+
import { readdir, stat } from "node:fs/promises";
|
|
119468
|
+
import { isAbsolute as isAbsolute3, join as join17, relative as relative2, resolve as resolve3 } from "node:path";
|
|
119028
119469
|
var listFilesInputSchema = exports_external.object({
|
|
119029
119470
|
directory: exports_external.string().optional().describe("Absolute or relative path to the directory to list (defaults to current working directory)"),
|
|
119030
119471
|
recursive: exports_external.boolean().optional().describe("If true, list files recursively (default: false)"),
|
|
@@ -119044,7 +119485,7 @@ async function listRecursive(dir, maxEntries) {
|
|
|
119044
119485
|
}
|
|
119045
119486
|
for (const entry of entries) {
|
|
119046
119487
|
total++;
|
|
119047
|
-
const fullPath =
|
|
119488
|
+
const fullPath = join17(current, entry.name);
|
|
119048
119489
|
if (entry.isDirectory()) {
|
|
119049
119490
|
if (results.length < maxEntries)
|
|
119050
119491
|
results.push(`${fullPath}/`);
|
|
@@ -119061,7 +119502,7 @@ async function listRecursive(dir, maxEntries) {
|
|
|
119061
119502
|
function toRelative(base, paths) {
|
|
119062
119503
|
return paths.map((p) => {
|
|
119063
119504
|
const isDir = p.endsWith("/");
|
|
119064
|
-
const rel =
|
|
119505
|
+
const rel = relative2(base, isDir ? p.slice(0, -1) : p);
|
|
119065
119506
|
return isDir ? `${rel}/` : rel;
|
|
119066
119507
|
});
|
|
119067
119508
|
}
|
|
@@ -119081,7 +119522,7 @@ Each directory entry is suffixed with "/" for easy identification.`,
|
|
|
119081
119522
|
directory,
|
|
119082
119523
|
recursive = false
|
|
119083
119524
|
}) => {
|
|
119084
|
-
const dir = directory ?
|
|
119525
|
+
const dir = directory ? isAbsolute3(directory) ? directory : resolve3(ctx.agentCwd, directory) : ctx.agentCwd;
|
|
119085
119526
|
try {
|
|
119086
119527
|
const info = await stat(dir);
|
|
119087
119528
|
if (!info.isDirectory()) {
|
|
@@ -119107,7 +119548,7 @@ Each directory entry is suffixed with "/" for easy identification.`,
|
|
|
119107
119548
|
}
|
|
119108
119549
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
119109
119550
|
const fullPaths = entries.map((e) => {
|
|
119110
|
-
const name =
|
|
119551
|
+
const name = join17(dir, e.name);
|
|
119111
119552
|
return e.isDirectory() ? `${name}/` : name;
|
|
119112
119553
|
});
|
|
119113
119554
|
const capped = fullPaths.slice(0, MAX_NON_RECURSIVE);
|
|
@@ -119237,38 +119678,38 @@ Returns tasks sorted by ID with per-status counts.`,
|
|
|
119237
119678
|
}
|
|
119238
119679
|
|
|
119239
119680
|
// src/core/agents/offSecAgent/tools/persistentShell.ts
|
|
119240
|
-
import { spawn as spawn4, spawnSync } from "child_process";
|
|
119241
|
-
import { randomBytes } from "crypto";
|
|
119681
|
+
import { spawn as spawn4, spawnSync } from "node:child_process";
|
|
119682
|
+
import { randomBytes } from "node:crypto";
|
|
119242
119683
|
import {
|
|
119243
119684
|
closeSync,
|
|
119244
119685
|
mkdirSync as mkdirSync10,
|
|
119245
119686
|
openSync,
|
|
119246
|
-
readFileSync as
|
|
119687
|
+
readFileSync as readFileSync3,
|
|
119247
119688
|
readSync,
|
|
119248
|
-
statSync as
|
|
119689
|
+
statSync as statSync3,
|
|
119249
119690
|
unlinkSync as unlinkSync2
|
|
119250
|
-
} from "fs";
|
|
119251
|
-
import { tmpdir } from "os";
|
|
119252
|
-
import { join as
|
|
119691
|
+
} from "node:fs";
|
|
119692
|
+
import { tmpdir } from "node:os";
|
|
119693
|
+
import { join as join18 } from "node:path";
|
|
119253
119694
|
var MAX_BUFFER = 5000000;
|
|
119254
|
-
var APEX_TMP_ROOT =
|
|
119695
|
+
var APEX_TMP_ROOT = join18(tmpdir(), `apex-shell-${process.pid}`);
|
|
119255
119696
|
try {
|
|
119256
119697
|
mkdirSync10(APEX_TMP_ROOT, { recursive: true });
|
|
119257
119698
|
} catch {}
|
|
119258
119699
|
function readTempfileCapped(path) {
|
|
119259
119700
|
try {
|
|
119260
|
-
const stat2 =
|
|
119701
|
+
const stat2 = statSync3(path);
|
|
119261
119702
|
if (stat2.size === 0)
|
|
119262
119703
|
return "";
|
|
119263
119704
|
if (stat2.size <= MAX_BUFFER) {
|
|
119264
|
-
return
|
|
119705
|
+
return readFileSync3(path, "utf-8");
|
|
119265
119706
|
}
|
|
119266
119707
|
const fd = openSync(path, "r");
|
|
119267
119708
|
try {
|
|
119268
119709
|
const buf = Buffer.alloc(MAX_BUFFER);
|
|
119269
119710
|
readSync(fd, buf, 0, MAX_BUFFER, stat2.size - MAX_BUFFER);
|
|
119270
119711
|
return `(stdout truncated)...
|
|
119271
|
-
|
|
119712
|
+
${buf.toString("utf-8")}`;
|
|
119272
119713
|
} finally {
|
|
119273
119714
|
closeSync(fd);
|
|
119274
119715
|
}
|
|
@@ -119426,16 +119867,16 @@ class PersistentShell {
|
|
|
119426
119867
|
let commandOutput = cmd.authoritativeStdout.substring(0, markerIdx);
|
|
119427
119868
|
if (cmd.stdoutTruncated) {
|
|
119428
119869
|
commandOutput = `(stdout truncated)...
|
|
119429
|
-
|
|
119870
|
+
${commandOutput}`;
|
|
119430
119871
|
}
|
|
119431
|
-
const effectiveExit = cmd.forcedExitCode != null ? cmd.forcedExitCode : isNaN(naturalExitCode) ? 1 : naturalExitCode;
|
|
119872
|
+
const effectiveExit = cmd.forcedExitCode != null ? cmd.forcedExitCode : Number.isNaN(naturalExitCode) ? 1 : naturalExitCode;
|
|
119432
119873
|
const effectiveStderr = cmd.forcedStderrSuffix ? (cmd.stderr || "") + cmd.forcedStderrSuffix : cmd.stderr || "";
|
|
119433
119874
|
unlinkSafe(cmd.outPath);
|
|
119434
119875
|
unlinkSafe(cmd.errPath);
|
|
119435
|
-
const
|
|
119876
|
+
const resolve4 = cmd.resolve;
|
|
119436
119877
|
this.current = null;
|
|
119437
119878
|
this.pendingCancel = null;
|
|
119438
|
-
|
|
119879
|
+
resolve4({
|
|
119439
119880
|
stdout: commandOutput || "(no output)",
|
|
119440
119881
|
stderr: effectiveStderr,
|
|
119441
119882
|
exitCode: effectiveExit
|
|
@@ -119471,7 +119912,7 @@ class PersistentShell {
|
|
|
119471
119912
|
}
|
|
119472
119913
|
cmd.stderr += chunk;
|
|
119473
119914
|
if (cmd.stderr.length > MAX_BUFFER) {
|
|
119474
|
-
cmd.stderr = cmd.stderr.substring(0, MAX_BUFFER)
|
|
119915
|
+
cmd.stderr = `${cmd.stderr.substring(0, MAX_BUFFER)}...
|
|
119475
119916
|
(stderr truncated)`;
|
|
119476
119917
|
}
|
|
119477
119918
|
}
|
|
@@ -119492,10 +119933,10 @@ class PersistentShell {
|
|
|
119492
119933
|
}
|
|
119493
119934
|
this.ensureAlive();
|
|
119494
119935
|
const proc = this.proc;
|
|
119495
|
-
if (!proc
|
|
119936
|
+
if (!proc?.stdin || !proc.stdout || !proc.stderr) {
|
|
119496
119937
|
return { stdout: "", stderr: "Failed to spawn shell", exitCode: 1 };
|
|
119497
119938
|
}
|
|
119498
|
-
return await new Promise((
|
|
119939
|
+
return await new Promise((resolve4) => {
|
|
119499
119940
|
let resolved = false;
|
|
119500
119941
|
let timeoutTimer;
|
|
119501
119942
|
let abortCleanup;
|
|
@@ -119503,8 +119944,8 @@ class PersistentShell {
|
|
|
119503
119944
|
const marker = `__APEX_${nonceHex}__`;
|
|
119504
119945
|
const exitMarkerPrefix = `${marker}_EXIT_`;
|
|
119505
119946
|
const cutoverMarker = `${marker}_CUTOVER`;
|
|
119506
|
-
const outPath =
|
|
119507
|
-
const errPath =
|
|
119947
|
+
const outPath = join18(APEX_TMP_ROOT, `${nonceHex}.out`);
|
|
119948
|
+
const errPath = join18(APEX_TMP_ROOT, `${nonceHex}.err`);
|
|
119508
119949
|
const pending = {
|
|
119509
119950
|
streamedStdout: "",
|
|
119510
119951
|
authoritativeStdout: "",
|
|
@@ -119531,7 +119972,7 @@ class PersistentShell {
|
|
|
119531
119972
|
pending._activeSalvageId = undefined;
|
|
119532
119973
|
if (abortCleanup)
|
|
119533
119974
|
abortCleanup();
|
|
119534
|
-
|
|
119975
|
+
resolve4(result);
|
|
119535
119976
|
}
|
|
119536
119977
|
};
|
|
119537
119978
|
this.current = pending;
|
|
@@ -119581,7 +120022,7 @@ class PersistentShell {
|
|
|
119581
120022
|
].join(`
|
|
119582
120023
|
`);
|
|
119583
120024
|
try {
|
|
119584
|
-
proc.stdin
|
|
120025
|
+
proc.stdin?.write(wrapped);
|
|
119585
120026
|
} catch {
|
|
119586
120027
|
pending.resolve({
|
|
119587
120028
|
stdout: "",
|
|
@@ -119922,8 +120363,8 @@ Returns discovered endpoints and recommended login approach.`,
|
|
|
119922
120363
|
// src/core/agents/offSecAgent/tools/provideComparisonResults.ts
|
|
119923
120364
|
init_dist();
|
|
119924
120365
|
init_zod();
|
|
119925
|
-
import { writeFileSync as writeFileSync13 } from "fs";
|
|
119926
|
-
import { join as
|
|
120366
|
+
import { writeFileSync as writeFileSync13 } from "node:fs";
|
|
120367
|
+
import { join as join19 } from "node:path";
|
|
119927
120368
|
function provideComparisonResults(ctx) {
|
|
119928
120369
|
return tool({
|
|
119929
120370
|
description: `Provide the final comparison results with matched, missed, and extra findings.
|
|
@@ -119969,7 +120410,7 @@ Results will be saved to: comparison-results.json in the session directory.`,
|
|
|
119969
120410
|
recall,
|
|
119970
120411
|
precision
|
|
119971
120412
|
};
|
|
119972
|
-
const resultsPath =
|
|
120413
|
+
const resultsPath = join19(ctx.session.rootPath, "comparison-results.json");
|
|
119973
120414
|
writeFileSync13(resultsPath, JSON.stringify(result, null, 2));
|
|
119974
120415
|
return {
|
|
119975
120416
|
success: true,
|
|
@@ -119983,8 +120424,8 @@ Results will be saved to: comparison-results.json in the session directory.`,
|
|
|
119983
120424
|
// src/core/agents/offSecAgent/tools/readFile.ts
|
|
119984
120425
|
init_dist();
|
|
119985
120426
|
init_zod();
|
|
119986
|
-
import { readFile as fsReadFile } from "fs/promises";
|
|
119987
|
-
import { isAbsolute as
|
|
120427
|
+
import { readFile as fsReadFile } from "node:fs/promises";
|
|
120428
|
+
import { isAbsolute as isAbsolute4, resolve as resolve4 } from "node:path";
|
|
119988
120429
|
var readFileInputSchema = exports_external.object({
|
|
119989
120430
|
path: exports_external.string().describe("Absolute or relative path to the file to read. Must be a file, not a directory."),
|
|
119990
120431
|
startLine: exports_external.number().optional().describe("1-based line number to start reading from (inclusive)"),
|
|
@@ -120002,7 +120443,7 @@ the end. If only endLine is given, reads from the beginning to that line.
|
|
|
120002
120443
|
Output lines are prefixed with their line number for easy reference.`,
|
|
120003
120444
|
inputSchema: readFileInputSchema,
|
|
120004
120445
|
execute: async ({ path, startLine, endLine }) => {
|
|
120005
|
-
const resolved =
|
|
120446
|
+
const resolved = isAbsolute4(path) ? path : resolve4(ctx.agentCwd, path);
|
|
120006
120447
|
try {
|
|
120007
120448
|
const raw = await fsReadFile(resolved, "utf-8");
|
|
120008
120449
|
const allLines = raw.split(`
|
|
@@ -120123,7 +120564,7 @@ should be passed directly to spawn_pentest_swarm for deep testing.`,
|
|
|
120123
120564
|
});
|
|
120124
120565
|
if (cwd) {
|
|
120125
120566
|
try {
|
|
120126
|
-
const { WhiteboxAttackSurfaceAgent } = await import("./index-
|
|
120567
|
+
const { WhiteboxAttackSurfaceAgent } = await import("./index-speg9ebc.js");
|
|
120127
120568
|
const localBus = new AgentEventBus;
|
|
120128
120569
|
AgentEventBus.attachChild(localBus, ctx.eventBus, subagentId);
|
|
120129
120570
|
const agent = new WhiteboxAttackSurfaceAgent({
|
|
@@ -120173,7 +120614,7 @@ should be passed directly to spawn_pentest_swarm for deep testing.`,
|
|
|
120173
120614
|
}
|
|
120174
120615
|
}
|
|
120175
120616
|
try {
|
|
120176
|
-
const { BlackboxAttackSurfaceAgent } = await import("./blackboxAgent-
|
|
120617
|
+
const { BlackboxAttackSurfaceAgent } = await import("./blackboxAgent-80y8j499.js");
|
|
120177
120618
|
const localBus = new AgentEventBus;
|
|
120178
120619
|
AgentEventBus.attachChild(localBus, ctx.eventBus, subagentId);
|
|
120179
120620
|
const agent = new BlackboxAttackSurfaceAgent({
|
|
@@ -120251,7 +120692,7 @@ Omit \`cwd\` for blackbox mode (live target probing only).`,
|
|
|
120251
120692
|
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
|
|
120252
120693
|
}),
|
|
120253
120694
|
execute: async ({ target, cwd }) => {
|
|
120254
|
-
const { runPentestWorkflow: workflow } = await import("./pentest-
|
|
120695
|
+
const { runPentestWorkflow: workflow } = await import("./pentest-4v06edvf.js");
|
|
120255
120696
|
if (!ctx.model) {
|
|
120256
120697
|
return {
|
|
120257
120698
|
success: false,
|
|
@@ -120317,9 +120758,9 @@ async function runWithBoundedConcurrency(items, concurrency, fn, abortSignal) {
|
|
|
120317
120758
|
const parentContext = import_api.context.active();
|
|
120318
120759
|
let nextIdx = 0;
|
|
120319
120760
|
let completed = 0;
|
|
120320
|
-
await new Promise((
|
|
120761
|
+
await new Promise((resolve5) => {
|
|
120321
120762
|
if (items.length === 0) {
|
|
120322
|
-
|
|
120763
|
+
resolve5();
|
|
120323
120764
|
return;
|
|
120324
120765
|
}
|
|
120325
120766
|
let active = 0;
|
|
@@ -120328,7 +120769,7 @@ async function runWithBoundedConcurrency(items, concurrency, fn, abortSignal) {
|
|
|
120328
120769
|
}
|
|
120329
120770
|
function next() {
|
|
120330
120771
|
if (completed >= nextIdx && !canLaunchMore()) {
|
|
120331
|
-
|
|
120772
|
+
resolve5();
|
|
120332
120773
|
return;
|
|
120333
120774
|
}
|
|
120334
120775
|
while (canLaunchMore()) {
|
|
@@ -120356,29 +120797,29 @@ init_zod();
|
|
|
120356
120797
|
|
|
120357
120798
|
// src/core/whitebox/adapters.ts
|
|
120358
120799
|
import { existsSync as existsSync12 } from "node:fs";
|
|
120359
|
-
import { join as
|
|
120800
|
+
import { join as join21 } from "node:path";
|
|
120360
120801
|
|
|
120361
120802
|
// src/core/whitebox/artifacts.ts
|
|
120362
120803
|
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
120363
|
-
import { basename as basename2, join as
|
|
120804
|
+
import { basename as basename2, join as join20 } from "node:path";
|
|
120364
120805
|
|
|
120365
120806
|
// src/core/whitebox/paths.ts
|
|
120366
120807
|
import { existsSync as existsSync11, realpathSync } from "node:fs";
|
|
120367
|
-
import { isAbsolute as
|
|
120808
|
+
import { isAbsolute as isAbsolute5, normalize, relative as relative3, resolve as resolve5 } from "node:path";
|
|
120368
120809
|
function resolveWhiteboxCodebaseRoot(input) {
|
|
120369
|
-
return
|
|
120810
|
+
return resolve5(normalize(input.codebasePath ?? input.agentCwd));
|
|
120370
120811
|
}
|
|
120371
120812
|
function isUnderRoot(root, candidate) {
|
|
120372
|
-
const rel =
|
|
120813
|
+
const rel = relative3(root, candidate);
|
|
120373
120814
|
if (rel === "")
|
|
120374
120815
|
return true;
|
|
120375
|
-
if (rel.startsWith("..") ||
|
|
120816
|
+
if (rel.startsWith("..") || isAbsolute5(rel))
|
|
120376
120817
|
return false;
|
|
120377
120818
|
return true;
|
|
120378
120819
|
}
|
|
120379
120820
|
function resolvePathWithinCodebaseRoot(root, subPath = ".") {
|
|
120380
|
-
const absRoot =
|
|
120381
|
-
const candidate =
|
|
120821
|
+
const absRoot = resolve5(normalize(root));
|
|
120822
|
+
const candidate = isAbsolute5(subPath) ? resolve5(normalize(subPath)) : resolve5(absRoot, normalize(subPath));
|
|
120382
120823
|
if (!isUnderRoot(absRoot, candidate)) {
|
|
120383
120824
|
throw new Error(`Path escapes codebase root: ${subPath}`);
|
|
120384
120825
|
}
|
|
@@ -120401,8 +120842,8 @@ function resolveSessionWhiteboxArtifactPath(input) {
|
|
|
120401
120842
|
if (!allowed) {
|
|
120402
120843
|
throw new Error(`Artifact path must start with logs/whitebox/ or scratchpad/whitebox/: ${input.artifactRelativePath}`);
|
|
120403
120844
|
}
|
|
120404
|
-
const resolved =
|
|
120405
|
-
if (!isUnderRoot(
|
|
120845
|
+
const resolved = resolve5(input.sessionRootPath, posixPath);
|
|
120846
|
+
if (!isUnderRoot(resolve5(input.sessionRootPath), resolved)) {
|
|
120406
120847
|
throw new Error("Invalid artifact path");
|
|
120407
120848
|
}
|
|
120408
120849
|
if (existsSync11(resolved)) {
|
|
@@ -120421,10 +120862,10 @@ function safeName(value) {
|
|
|
120421
120862
|
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/\.{2,}/g, ".").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
120422
120863
|
}
|
|
120423
120864
|
function getWhiteboxLogsDir(session) {
|
|
120424
|
-
return
|
|
120865
|
+
return join20(session.logsPath, "whitebox");
|
|
120425
120866
|
}
|
|
120426
120867
|
function getWhiteboxScratchDir(session) {
|
|
120427
|
-
return
|
|
120868
|
+
return join20(session.scratchpadPath, "whitebox");
|
|
120428
120869
|
}
|
|
120429
120870
|
async function ensureWhiteboxDirs(session) {
|
|
120430
120871
|
await Promise.all([
|
|
@@ -120437,7 +120878,7 @@ async function writeWhiteboxArtifact(input) {
|
|
|
120437
120878
|
const baseDir = input.area === "scratchpad" ? getWhiteboxScratchDir(input.session) : getWhiteboxLogsDir(input.session);
|
|
120438
120879
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
120439
120880
|
const filename = `${timestamp}-${safeName(input.name)}${input.extension ?? ".txt"}`;
|
|
120440
|
-
const absolutePath =
|
|
120881
|
+
const absolutePath = join20(baseDir, filename);
|
|
120441
120882
|
await writeFile2(absolutePath, input.content, "utf-8");
|
|
120442
120883
|
const relativeRoot = input.area === "scratchpad" ? "scratchpad/whitebox" : "logs/whitebox";
|
|
120443
120884
|
return {
|
|
@@ -120490,7 +120931,7 @@ async function runSpawnBounded(input) {
|
|
|
120490
120931
|
};
|
|
120491
120932
|
}
|
|
120492
120933
|
const detached = input.detached === true && process.platform !== "win32";
|
|
120493
|
-
return new Promise((
|
|
120934
|
+
return new Promise((resolve6) => {
|
|
120494
120935
|
const child = spawn5(program, input.command.slice(1), {
|
|
120495
120936
|
cwd: input.cwd,
|
|
120496
120937
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -120512,7 +120953,7 @@ async function runSpawnBounded(input) {
|
|
|
120512
120953
|
clearTimeout(timeoutTimer);
|
|
120513
120954
|
if (escalateTimer)
|
|
120514
120955
|
clearTimeout(escalateTimer);
|
|
120515
|
-
|
|
120956
|
+
resolve6(result);
|
|
120516
120957
|
};
|
|
120517
120958
|
const append = (target, data) => {
|
|
120518
120959
|
if (totalBytes >= input.maxTotalBytes) {
|
|
@@ -120679,7 +121120,7 @@ var WHITEBOX_SCAN_ADAPTERS = [
|
|
|
120679
121120
|
id: "npm-audit",
|
|
120680
121121
|
kind: "dependencies",
|
|
120681
121122
|
tool: "npm",
|
|
120682
|
-
detect: (profile) => hasTool(profile, "npm") && (profile.packageManagers.includes("npm") || existsSync12(
|
|
121123
|
+
detect: (profile) => hasTool(profile, "npm") && (profile.packageManagers.includes("npm") || existsSync12(join21(profile.rootPath, "package-lock.json"))),
|
|
120683
121124
|
buildCommand: () => ["npm", "audit", "--json"],
|
|
120684
121125
|
parseSummary: (raw) => simpleLineFindings("npm audit", raw)
|
|
120685
121126
|
},
|
|
@@ -120788,15 +121229,15 @@ ${raw.stderr}` : "");
|
|
|
120788
121229
|
// src/core/whitebox/candidates.ts
|
|
120789
121230
|
import { existsSync as existsSync13 } from "node:fs";
|
|
120790
121231
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
120791
|
-
import { join as
|
|
121232
|
+
import { join as join22 } from "node:path";
|
|
120792
121233
|
var CANDIDATES_FILE = "candidates.json";
|
|
120793
121234
|
var writeLocks = new Map;
|
|
120794
121235
|
function withCandidateLock(session, fn) {
|
|
120795
121236
|
const key = session.scratchpadPath;
|
|
120796
121237
|
const prev = writeLocks.get(key) ?? Promise.resolve();
|
|
120797
121238
|
let release;
|
|
120798
|
-
const gate = new Promise((
|
|
120799
|
-
release =
|
|
121239
|
+
const gate = new Promise((resolve6) => {
|
|
121240
|
+
release = resolve6;
|
|
120800
121241
|
});
|
|
120801
121242
|
writeLocks.set(key, gate);
|
|
120802
121243
|
return prev.then(fn).finally(() => {
|
|
@@ -120815,10 +121256,10 @@ var ALLOWED_TRANSITIONS = {
|
|
|
120815
121256
|
deferred: ["hypothesis", "investigating"]
|
|
120816
121257
|
};
|
|
120817
121258
|
function candidatesDir(session) {
|
|
120818
|
-
return
|
|
121259
|
+
return join22(session.scratchpadPath, "whitebox");
|
|
120819
121260
|
}
|
|
120820
121261
|
function candidatesPath(session) {
|
|
120821
|
-
return
|
|
121262
|
+
return join22(candidatesDir(session), CANDIDATES_FILE);
|
|
120822
121263
|
}
|
|
120823
121264
|
function makeId2(title) {
|
|
120824
121265
|
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 48);
|
|
@@ -121284,11 +121725,11 @@ import {
|
|
|
121284
121725
|
existsSync as existsSync14,
|
|
121285
121726
|
mkdirSync as mkdirSync11,
|
|
121286
121727
|
openSync as openSync2,
|
|
121287
|
-
readFileSync as
|
|
121728
|
+
readFileSync as readFileSync4,
|
|
121288
121729
|
readSync as readSync2,
|
|
121289
|
-
statSync as
|
|
121730
|
+
statSync as statSync4
|
|
121290
121731
|
} from "node:fs";
|
|
121291
|
-
import { join as
|
|
121732
|
+
import { join as join23 } from "node:path";
|
|
121292
121733
|
var MAX_JOB_LOG_INLINE = 40000;
|
|
121293
121734
|
var MAX_JOB_LOG_BYTES = 10 * 1024 * 1024;
|
|
121294
121735
|
var PRUNE_AFTER_MS = 60000;
|
|
@@ -121363,7 +121804,7 @@ function startWhiteboxJob(input) {
|
|
|
121363
121804
|
const logsDir = getWhiteboxLogsDir(input.session);
|
|
121364
121805
|
mkdirSync11(logsDir, { recursive: true });
|
|
121365
121806
|
const safeName2 = (input.name ?? "job").replace(/[^a-z0-9._-]+/gi, "-").replace(/\.{2,}/g, ".");
|
|
121366
|
-
const logPath =
|
|
121807
|
+
const logPath = join23(logsDir, `${id}-${safeName2}.log`);
|
|
121367
121808
|
const now = new Date().toISOString();
|
|
121368
121809
|
writeLog(logPath, `$ ${input.command}
|
|
121369
121810
|
|
|
@@ -121467,9 +121908,9 @@ function readWhiteboxJobLog(id, sessionId) {
|
|
|
121467
121908
|
if (!record3 || !existsSync14(record3.logPath)) {
|
|
121468
121909
|
return { content: "", truncated: false, record: record3 };
|
|
121469
121910
|
}
|
|
121470
|
-
const fileSize =
|
|
121911
|
+
const fileSize = statSync4(record3.logPath).size;
|
|
121471
121912
|
if (fileSize <= MAX_JOB_LOG_INLINE) {
|
|
121472
|
-
const raw =
|
|
121913
|
+
const raw = readFileSync4(record3.logPath, "utf-8");
|
|
121473
121914
|
return { content: raw, truncated: false, record: record3 };
|
|
121474
121915
|
}
|
|
121475
121916
|
const readBytes = Math.min(fileSize, MAX_JOB_LOG_INLINE);
|
|
@@ -121489,12 +121930,12 @@ function readWhiteboxJobLog(id, sessionId) {
|
|
|
121489
121930
|
};
|
|
121490
121931
|
}
|
|
121491
121932
|
// src/core/whitebox/repoProfile.ts
|
|
121492
|
-
import { execFile } from "node:child_process";
|
|
121933
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
121493
121934
|
import { existsSync as existsSync15, realpathSync as realpathSync2 } from "node:fs";
|
|
121494
121935
|
import { readdir as readdir2, readFile as readFile5, stat as stat2 } from "node:fs/promises";
|
|
121495
|
-
import { basename as basename3, extname as extname2, join as
|
|
121496
|
-
import { promisify } from "node:util";
|
|
121497
|
-
var
|
|
121936
|
+
import { basename as basename3, extname as extname2, join as join24, relative as relative4, resolve as resolve6 } from "node:path";
|
|
121937
|
+
import { promisify as promisify2 } from "node:util";
|
|
121938
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
121498
121939
|
var MAX_PROFILE_FILES = 5000;
|
|
121499
121940
|
var LANGUAGE_BY_EXTENSION = {
|
|
121500
121941
|
".ts": "typescript",
|
|
@@ -121580,7 +122021,7 @@ function shouldSkipDir(name) {
|
|
|
121580
122021
|
}
|
|
121581
122022
|
async function walkFiles(rootPath) {
|
|
121582
122023
|
const files = [];
|
|
121583
|
-
const absRoot =
|
|
122024
|
+
const absRoot = resolve6(rootPath);
|
|
121584
122025
|
function isWithinRoot(path) {
|
|
121585
122026
|
try {
|
|
121586
122027
|
const real = realpathSync2(path);
|
|
@@ -121601,7 +122042,7 @@ async function walkFiles(rootPath) {
|
|
|
121601
122042
|
for (const entry of entries) {
|
|
121602
122043
|
if (files.length >= MAX_PROFILE_FILES)
|
|
121603
122044
|
return;
|
|
121604
|
-
const fullPath =
|
|
122045
|
+
const fullPath = join24(current, entry.name);
|
|
121605
122046
|
if (entry.isDirectory()) {
|
|
121606
122047
|
if (shouldSkipDir(entry.name))
|
|
121607
122048
|
continue;
|
|
@@ -121611,7 +122052,7 @@ async function walkFiles(rootPath) {
|
|
|
121611
122052
|
continue;
|
|
121612
122053
|
}
|
|
121613
122054
|
if (entry.isFile()) {
|
|
121614
|
-
files.push(
|
|
122055
|
+
files.push(relative4(rootPath, fullPath));
|
|
121615
122056
|
}
|
|
121616
122057
|
}
|
|
121617
122058
|
}
|
|
@@ -121663,7 +122104,7 @@ function detectEntryHints(files) {
|
|
|
121663
122104
|
return hints.slice(0, 100);
|
|
121664
122105
|
}
|
|
121665
122106
|
async function readPackageScripts(rootPath) {
|
|
121666
|
-
const packageJsonPath =
|
|
122107
|
+
const packageJsonPath = join24(rootPath, "package.json");
|
|
121667
122108
|
if (!existsSync15(packageJsonPath)) {
|
|
121668
122109
|
return { buildCommands: [], testCommands: [], runCommands: [] };
|
|
121669
122110
|
}
|
|
@@ -121682,7 +122123,7 @@ async function readPackageScripts(rootPath) {
|
|
|
121682
122123
|
}
|
|
121683
122124
|
async function runGit(rootPath, args) {
|
|
121684
122125
|
try {
|
|
121685
|
-
const { stdout } = await
|
|
122126
|
+
const { stdout } = await execFileAsync2("git", args, {
|
|
121686
122127
|
cwd: rootPath,
|
|
121687
122128
|
timeout: 5000,
|
|
121688
122129
|
maxBuffer: 1024 * 1024
|
|
@@ -121695,7 +122136,7 @@ async function runGit(rootPath, args) {
|
|
|
121695
122136
|
async function detectTool(name) {
|
|
121696
122137
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
121697
122138
|
try {
|
|
121698
|
-
const { stdout } = await
|
|
122139
|
+
const { stdout } = await execFileAsync2(cmd, [name], {
|
|
121699
122140
|
timeout: 2000,
|
|
121700
122141
|
maxBuffer: 1024 * 64
|
|
121701
122142
|
});
|
|
@@ -121827,7 +122268,7 @@ Returns an array of results with the text output from each agent.`,
|
|
|
121827
122268
|
});
|
|
121828
122269
|
}
|
|
121829
122270
|
async function runSingleCodingAgent(ctx, codebasePath, objective, agentIndex, name) {
|
|
121830
|
-
const { CodeAgent } = await import("./agent-
|
|
122271
|
+
const { CodeAgent } = await import("./agent-m3bsrs09.js");
|
|
121831
122272
|
const subagentId = `coding-agent-${agentIndex}`;
|
|
121832
122273
|
ctx.eventBus?.emit("subagent-spawn", {
|
|
121833
122274
|
subagentId,
|
|
@@ -121876,8 +122317,8 @@ init_zod();
|
|
|
121876
122317
|
init_ai();
|
|
121877
122318
|
init_structured();
|
|
121878
122319
|
init_lazyLogger();
|
|
121879
|
-
import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as
|
|
121880
|
-
import { join as
|
|
122320
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
122321
|
+
import { join as join25 } from "node:path";
|
|
121881
122322
|
var log9 = scopedLogger(() => createLogger("findings:registry"));
|
|
121882
122323
|
var VULN_CLASS_PATTERNS = [
|
|
121883
122324
|
[/sql\s*injection/i, "sql-injection"],
|
|
@@ -122000,6 +122441,15 @@ ${existingList}
|
|
|
122000
122441
|
|
|
122001
122442
|
Is the new finding a duplicate of any existing finding?`;
|
|
122002
122443
|
}
|
|
122444
|
+
var SEVERITY_RANK = {
|
|
122445
|
+
CRITICAL: 4,
|
|
122446
|
+
HIGH: 3,
|
|
122447
|
+
MEDIUM: 2,
|
|
122448
|
+
LOW: 1
|
|
122449
|
+
};
|
|
122450
|
+
function maxSeverity(severities) {
|
|
122451
|
+
return severities.reduce((acc, s) => SEVERITY_RANK[s] > SEVERITY_RANK[acc] ? s : acc, "LOW");
|
|
122452
|
+
}
|
|
122003
122453
|
var RootCauseGroupResultSchema = exports_external.object({
|
|
122004
122454
|
groups: exports_external.array(exports_external.object({
|
|
122005
122455
|
groupId: exports_external.string().describe("Short kebab-case identifier for the group"),
|
|
@@ -122060,7 +122510,7 @@ class FindingsRegistry {
|
|
|
122060
122510
|
log9.debug(`fromDirectory: path=${findingsPath}, jsonFiles=${files.length}`);
|
|
122061
122511
|
for (const file of files) {
|
|
122062
122512
|
try {
|
|
122063
|
-
const raw =
|
|
122513
|
+
const raw = readFileSync5(join25(findingsPath, file), "utf-8");
|
|
122064
122514
|
const finding = JSON.parse(raw);
|
|
122065
122515
|
if (finding && typeof finding.title === "string" && typeof finding.endpoint === "string") {
|
|
122066
122516
|
registry.indexFinding(finding);
|
|
@@ -122104,19 +122554,19 @@ class FindingsRegistry {
|
|
|
122104
122554
|
}
|
|
122105
122555
|
async register(finding) {
|
|
122106
122556
|
log9.debug(`register: checking "${finding.title}" endpoint="${finding.endpoint}"`);
|
|
122107
|
-
const fastResult = await new Promise((
|
|
122557
|
+
const fastResult = await new Promise((resolve7) => {
|
|
122108
122558
|
this.mutex = this.mutex.then(() => {
|
|
122109
122559
|
const check = this.isDuplicate(finding);
|
|
122110
122560
|
if (check.duplicate) {
|
|
122111
122561
|
log9.debug(`register: DUPLICATE (${check.matchType}) "${finding.title}" matched="${check.matchedFinding?.title}"`);
|
|
122112
|
-
|
|
122562
|
+
resolve7(check);
|
|
122113
122563
|
} else if (!this.model || this.findings.length === 0) {
|
|
122114
122564
|
this.indexFinding(finding);
|
|
122115
122565
|
log9.debug(`register: UNIQUE (Tier 1+2, no LLM needed) "${finding.title}" — registry size=${this.size}`);
|
|
122116
|
-
|
|
122566
|
+
resolve7({ duplicate: false });
|
|
122117
122567
|
} else {
|
|
122118
122568
|
log9.debug(`register: Tier 1+2 pass — proceeding to Tier 3 LLM check for "${finding.title}"`);
|
|
122119
|
-
|
|
122569
|
+
resolve7(null);
|
|
122120
122570
|
}
|
|
122121
122571
|
});
|
|
122122
122572
|
});
|
|
@@ -122133,16 +122583,16 @@ class FindingsRegistry {
|
|
|
122133
122583
|
log9.debug(`register: DUPLICATE (semantic/Tier 3) "${finding.title}" matched="${semanticResult.matchedFinding?.title}"`);
|
|
122134
122584
|
return semanticResult;
|
|
122135
122585
|
}
|
|
122136
|
-
return new Promise((
|
|
122586
|
+
return new Promise((resolve7) => {
|
|
122137
122587
|
this.mutex = this.mutex.then(() => {
|
|
122138
122588
|
const recheck = this.isDuplicate(finding);
|
|
122139
122589
|
if (recheck.duplicate) {
|
|
122140
122590
|
log9.debug(`register: DUPLICATE (race re-check, ${recheck.matchType}) "${finding.title}" matched="${recheck.matchedFinding?.title}"`);
|
|
122141
|
-
|
|
122591
|
+
resolve7(recheck);
|
|
122142
122592
|
} else {
|
|
122143
122593
|
this.indexFinding(finding);
|
|
122144
122594
|
log9.debug(`register: UNIQUE (all tiers passed) "${finding.title}" — registry size=${this.size}`);
|
|
122145
|
-
|
|
122595
|
+
resolve7({ duplicate: false });
|
|
122146
122596
|
}
|
|
122147
122597
|
});
|
|
122148
122598
|
});
|
|
@@ -122174,10 +122624,10 @@ class FindingsRegistry {
|
|
|
122174
122624
|
return { duplicate: false };
|
|
122175
122625
|
}
|
|
122176
122626
|
async unregister(finding) {
|
|
122177
|
-
return new Promise((
|
|
122627
|
+
return new Promise((resolve7) => {
|
|
122178
122628
|
this.mutex = this.mutex.then(() => {
|
|
122179
122629
|
this.removeFinding(finding);
|
|
122180
|
-
|
|
122630
|
+
resolve7();
|
|
122181
122631
|
});
|
|
122182
122632
|
});
|
|
122183
122633
|
}
|
|
@@ -122202,24 +122652,38 @@ class FindingsRegistry {
|
|
|
122202
122652
|
return [];
|
|
122203
122653
|
}
|
|
122204
122654
|
const sanitised = [];
|
|
122205
|
-
await new Promise((
|
|
122655
|
+
await new Promise((resolve7) => {
|
|
122206
122656
|
this.mutex = this.mutex.then(() => {
|
|
122657
|
+
const baseSeverities = snapshot.map((f) => f.severity);
|
|
122207
122658
|
for (const group of result.groups) {
|
|
122208
122659
|
const validIndices = group.findingIndices.filter((i) => i >= 0 && i < snapshot.length);
|
|
122209
122660
|
if (validIndices.length < 2)
|
|
122210
122661
|
continue;
|
|
122662
|
+
const normalizedSeverity = maxSeverity(validIndices.map((i) => baseSeverities[i]));
|
|
122663
|
+
const leadFindingIndex = validIndices.reduce((best, i) => {
|
|
122664
|
+
const aRank = SEVERITY_RANK[baseSeverities[i]];
|
|
122665
|
+
const bRank = SEVERITY_RANK[baseSeverities[best]];
|
|
122666
|
+
if (aRank !== bRank) {
|
|
122667
|
+
return aRank > bRank ? i : best;
|
|
122668
|
+
}
|
|
122669
|
+
return (snapshot[i].description?.length ?? 0) > (snapshot[best].description?.length ?? 0) ? i : best;
|
|
122670
|
+
}, validIndices[0]);
|
|
122211
122671
|
sanitised.push({
|
|
122212
122672
|
groupId: group.groupId,
|
|
122213
122673
|
findingIndices: validIndices,
|
|
122214
|
-
rootCause: group.rootCause
|
|
122674
|
+
rootCause: group.rootCause,
|
|
122675
|
+
normalizedSeverity,
|
|
122676
|
+
leadFindingIndex
|
|
122215
122677
|
});
|
|
122216
122678
|
for (const idx of validIndices) {
|
|
122217
122679
|
const finding = snapshot[idx];
|
|
122218
122680
|
finding.rootCauseGroup = group.groupId;
|
|
122219
|
-
finding.
|
|
122681
|
+
finding.severity = normalizedSeverity;
|
|
122682
|
+
finding.rootCauseLead = idx === leadFindingIndex;
|
|
122683
|
+
finding.relatedFindings = validIndices.filter((i) => i !== idx).map((i) => snapshot[i]?.title);
|
|
122220
122684
|
}
|
|
122221
122685
|
}
|
|
122222
|
-
|
|
122686
|
+
resolve7();
|
|
122223
122687
|
});
|
|
122224
122688
|
});
|
|
122225
122689
|
return sanitised;
|
|
@@ -122250,19 +122714,19 @@ class FindingsRegistry {
|
|
|
122250
122714
|
}
|
|
122251
122715
|
|
|
122252
122716
|
// src/core/plan/index.ts
|
|
122253
|
-
import { existsSync as existsSync17, readFileSync as
|
|
122254
|
-
import { join as
|
|
122717
|
+
import { existsSync as existsSync17, readFileSync as readFileSync6 } from "node:fs";
|
|
122718
|
+
import { join as join26 } from "node:path";
|
|
122255
122719
|
var PLAN_FILENAME = "plan.md";
|
|
122256
122720
|
function planFilePath(sessionRootPath, subagentId) {
|
|
122257
122721
|
if (subagentId) {
|
|
122258
|
-
return
|
|
122722
|
+
return join26(sessionRootPath, "subagents", `${subagentId}-plan.md`);
|
|
122259
122723
|
}
|
|
122260
|
-
return
|
|
122724
|
+
return join26(sessionRootPath, PLAN_FILENAME);
|
|
122261
122725
|
}
|
|
122262
122726
|
function readPlan(sessionRootPath, subagentId) {
|
|
122263
122727
|
const path = planFilePath(sessionRootPath, subagentId);
|
|
122264
122728
|
try {
|
|
122265
|
-
return
|
|
122729
|
+
return readFileSync6(path, "utf-8");
|
|
122266
122730
|
} catch {
|
|
122267
122731
|
return null;
|
|
122268
122732
|
}
|
|
@@ -122310,7 +122774,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
|
|
|
122310
122774
|
message: "spawn_pentest_agent requires a model in the tool context."
|
|
122311
122775
|
};
|
|
122312
122776
|
}
|
|
122313
|
-
const { TargetedPentestAgent } = await import("./agent-
|
|
122777
|
+
const { TargetedPentestAgent } = await import("./agent-067x1n67.js");
|
|
122314
122778
|
const findingsRegistry = ctx.findingsRegistry ?? FindingsRegistry.fromDirectory(ctx.session.findingsPath, {
|
|
122315
122779
|
model: ctx.model,
|
|
122316
122780
|
authConfig: ctx.authConfig,
|
|
@@ -122464,7 +122928,7 @@ Pass every target you want tested — the swarm handles concurrency automaticall
|
|
|
122464
122928
|
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing")
|
|
122465
122929
|
}),
|
|
122466
122930
|
execute: async ({ targets }) => {
|
|
122467
|
-
const { runPentestSwarm, DEFAULT_CONCURRENCY: DEFAULT_CONCURRENCY2 } = await import("./pentest-
|
|
122931
|
+
const { runPentestSwarm, DEFAULT_CONCURRENCY: DEFAULT_CONCURRENCY2 } = await import("./pentest-4v06edvf.js");
|
|
122468
122932
|
if (!ctx.model) {
|
|
122469
122933
|
return {
|
|
122470
122934
|
success: false,
|
|
@@ -122532,7 +122996,7 @@ Only call this when the plan is complete and ready for review.`,
|
|
|
122532
122996
|
const planPath = planFilePath(ctx.session.rootPath, scopeId);
|
|
122533
122997
|
log11.debug(`enter: path=${planPath}`);
|
|
122534
122998
|
const plan = readPlan(ctx.session.rootPath, scopeId);
|
|
122535
|
-
if (!plan
|
|
122999
|
+
if (!plan?.trim()) {
|
|
122536
123000
|
return {
|
|
122537
123001
|
success: false,
|
|
122538
123002
|
error: "Plan file not found or empty. Write the plan first using write_plan.",
|
|
@@ -122630,8 +123094,8 @@ Use this to:
|
|
|
122630
123094
|
// src/core/agents/offSecAgent/tools/updateFile.ts
|
|
122631
123095
|
init_dist();
|
|
122632
123096
|
init_zod();
|
|
122633
|
-
import { readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
122634
|
-
import { isAbsolute as
|
|
123097
|
+
import { readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
123098
|
+
import { isAbsolute as isAbsolute6, resolve as resolve7 } from "node:path";
|
|
122635
123099
|
var updateFileInputSchema = exports_external.object({
|
|
122636
123100
|
path: exports_external.string().describe("Absolute or relative path to the file to update"),
|
|
122637
123101
|
oldContent: exports_external.string().describe("The exact string to find in the file. Must match character-for-character including whitespace and indentation."),
|
|
@@ -122659,7 +123123,7 @@ operation fails with an error — double-check whitespace and indentation.`,
|
|
|
122659
123123
|
newContent,
|
|
122660
123124
|
replaceAll = false
|
|
122661
123125
|
}) => {
|
|
122662
|
-
const resolved =
|
|
123126
|
+
const resolved = isAbsolute6(filePath) ? filePath : resolve7(ctx.agentCwd, filePath);
|
|
122663
123127
|
if (ctx.sandbox) {
|
|
122664
123128
|
return executeSandboxUpdate(ctx, resolved, oldContent, newContent, replaceAll);
|
|
122665
123129
|
}
|
|
@@ -123099,9 +123563,9 @@ async function handleSearchResponse(response) {
|
|
|
123099
123563
|
init_dist();
|
|
123100
123564
|
init_zod();
|
|
123101
123565
|
init_structured();
|
|
123102
|
-
import { mkdirSync as mkdirSync12 } from "fs";
|
|
123103
|
-
import { writeFile as writeFile5 } from "fs/promises";
|
|
123104
|
-
import { dirname as
|
|
123566
|
+
import { mkdirSync as mkdirSync12 } from "node:fs";
|
|
123567
|
+
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
123568
|
+
import { dirname as dirname6 } from "node:path";
|
|
123105
123569
|
init_lazyLogger();
|
|
123106
123570
|
var log12 = scopedLogger(() => createLogger("write_plan"));
|
|
123107
123571
|
var writePlanInputSchema = exports_external.object({
|
|
@@ -123127,7 +123591,7 @@ Required plan sections:
|
|
|
123127
123591
|
execute: async ({ content }) => {
|
|
123128
123592
|
const scopeId = ctx.planSubagentId ?? ctx.subagentId;
|
|
123129
123593
|
const planPath = planFilePath(ctx.session.rootPath, scopeId);
|
|
123130
|
-
mkdirSync12(
|
|
123594
|
+
mkdirSync12(dirname6(planPath), { recursive: true });
|
|
123131
123595
|
log12.debug(`enter: contentLen=${content.length}, path=${planPath}`);
|
|
123132
123596
|
try {
|
|
123133
123597
|
await writeFile5(planPath, content, "utf-8");
|
|
@@ -123146,6 +123610,38 @@ Required plan sections:
|
|
|
123146
123610
|
}
|
|
123147
123611
|
});
|
|
123148
123612
|
}
|
|
123613
|
+
// src/core/agents/offSecAgent/tools/listPromptInjections.ts
|
|
123614
|
+
init_dist();
|
|
123615
|
+
init_zod();
|
|
123616
|
+
function listPromptInjections(ctx) {
|
|
123617
|
+
return tool({
|
|
123618
|
+
description: "List available prompt-injection test payloads by safe metadata only. " + "The raw payload text is never returned. Use the returned id as a " + 'PromptInjectionRef: {"kind":"prompt_injection_ref","id":"<id>"} when a tool supports runtime injection references.',
|
|
123619
|
+
inputSchema: exports_external.object({
|
|
123620
|
+
category: exports_external.enum([
|
|
123621
|
+
"instruction-hijack",
|
|
123622
|
+
"data-exfiltration",
|
|
123623
|
+
"tool-misuse",
|
|
123624
|
+
"role-confusion",
|
|
123625
|
+
"encoding"
|
|
123626
|
+
]).optional().describe("Optional category filter."),
|
|
123627
|
+
tag: exports_external.string().optional().describe("Optional tag filter."),
|
|
123628
|
+
toolCallDescription: exports_external.string().describe("A concise, human-readable description of why you are listing prompt-injection payloads.")
|
|
123629
|
+
}),
|
|
123630
|
+
execute: async ({ category, tag }) => {
|
|
123631
|
+
const library = await getPromptInjectionLibrary({
|
|
123632
|
+
library: ctx.promptInjectionLibrary,
|
|
123633
|
+
source: ctx.promptInjectionLibrarySource
|
|
123634
|
+
});
|
|
123635
|
+
const injections = library.listCatalog().filter((entry) => !category || entry.category === category).filter((entry) => !tag || entry.tags.includes(tag));
|
|
123636
|
+
return {
|
|
123637
|
+
success: true,
|
|
123638
|
+
configured: library.listCatalog().length > 0,
|
|
123639
|
+
count: injections.length,
|
|
123640
|
+
injections
|
|
123641
|
+
};
|
|
123642
|
+
}
|
|
123643
|
+
});
|
|
123644
|
+
}
|
|
123149
123645
|
// src/core/agents/offSecAgent/tools/profileCodebase.ts
|
|
123150
123646
|
init_dist();
|
|
123151
123647
|
init_zod();
|
|
@@ -123321,7 +123817,7 @@ Use this instead of asking for the whole playbook. Examples:
|
|
|
123321
123817
|
// src/core/agents/offSecAgent/tools/runCodeQuery.ts
|
|
123322
123818
|
init_dist();
|
|
123323
123819
|
init_zod();
|
|
123324
|
-
import { relative as
|
|
123820
|
+
import { relative as relative5 } from "node:path";
|
|
123325
123821
|
var MAX_INLINE_MATCHES = 40;
|
|
123326
123822
|
var MAX_QUERY_CAPTURE_BYTES = 2 * 1024 * 1024;
|
|
123327
123823
|
var QueryEngineSchema = exports_external.enum(["rg", "grep", "ast-grep", "comby"]);
|
|
@@ -123392,7 +123888,7 @@ instead of flooding context with every match. Output size and runtime are bounde
|
|
|
123392
123888
|
let targetPathForEngine = ".";
|
|
123393
123889
|
try {
|
|
123394
123890
|
const absTarget = resolvePathWithinCodebaseRoot(rootPath, query.path ?? ".");
|
|
123395
|
-
targetPathForEngine =
|
|
123891
|
+
targetPathForEngine = relative5(rootPath, absTarget) || ".";
|
|
123396
123892
|
} catch (error) {
|
|
123397
123893
|
results.push({
|
|
123398
123894
|
engine,
|
|
@@ -123757,10 +124253,10 @@ function listWhiteboxCandidates2(ctx) {
|
|
|
123757
124253
|
// src/core/agents/offSecAgent/tools/whiteboxJobs.ts
|
|
123758
124254
|
init_dist();
|
|
123759
124255
|
init_zod();
|
|
123760
|
-
import { isAbsolute as
|
|
124256
|
+
import { isAbsolute as isAbsolute7, relative as relative6, sep as sep2 } from "node:path";
|
|
123761
124257
|
function displayPath(ctx, path) {
|
|
123762
|
-
const prefix = ctx.session.rootPath.endsWith(
|
|
123763
|
-
return path === ctx.session.rootPath || path.startsWith(prefix) ?
|
|
124258
|
+
const prefix = ctx.session.rootPath.endsWith(sep2) ? ctx.session.rootPath : `${ctx.session.rootPath}${sep2}`;
|
|
124259
|
+
return path === ctx.session.rootPath || path.startsWith(prefix) ? relative6(ctx.session.rootPath, path) : path;
|
|
123764
124260
|
}
|
|
123765
124261
|
function startWhiteboxJob2(ctx) {
|
|
123766
124262
|
return tool({
|
|
@@ -123895,9 +124391,9 @@ function stopWhiteboxJob2(ctx) {
|
|
|
123895
124391
|
});
|
|
123896
124392
|
}
|
|
123897
124393
|
function normalizeArtifactRelativePath(ctx, userPath) {
|
|
123898
|
-
if (
|
|
123899
|
-
const rel =
|
|
123900
|
-
if (rel.startsWith("..") || rel === ".." ||
|
|
124394
|
+
if (isAbsolute7(userPath)) {
|
|
124395
|
+
const rel = relative6(ctx.session.rootPath, userPath);
|
|
124396
|
+
if (rel.startsWith("..") || rel === ".." || isAbsolute7(rel)) {
|
|
123901
124397
|
throw new Error("Artifact path must be inside the session directory.");
|
|
123902
124398
|
}
|
|
123903
124399
|
return rel.replace(/\\/g, "/");
|
|
@@ -124030,6 +124526,7 @@ function createAllTools(ctx) {
|
|
|
124030
124526
|
add_memory: addMemory2(ctx),
|
|
124031
124527
|
list_memories: listMemories2(ctx),
|
|
124032
124528
|
get_memory: getMemory2(ctx),
|
|
124529
|
+
list_prompt_injections: listPromptInjections(ctx),
|
|
124033
124530
|
...createEmailToolset(ctx),
|
|
124034
124531
|
email_list_inboxes: emailListInboxes(ctx),
|
|
124035
124532
|
email_list_messages: emailListMessages(ctx),
|
|
@@ -124092,6 +124589,7 @@ var ALL_TOOL_NAMES = [
|
|
|
124092
124589
|
"add_memory",
|
|
124093
124590
|
"list_memories",
|
|
124094
124591
|
"get_memory",
|
|
124592
|
+
"list_prompt_injections",
|
|
124095
124593
|
"email_list_inboxes",
|
|
124096
124594
|
"email_list_messages",
|
|
124097
124595
|
"email_get_message",
|
|
@@ -124138,6 +124636,7 @@ var PLAN_MODE_TOOL_NAMES = [
|
|
|
124138
124636
|
"add_memory",
|
|
124139
124637
|
"list_memories",
|
|
124140
124638
|
"get_memory",
|
|
124639
|
+
"list_prompt_injections",
|
|
124141
124640
|
"email_list_inboxes",
|
|
124142
124641
|
"email_list_messages",
|
|
124143
124642
|
"email_get_message",
|
|
@@ -124154,9 +124653,9 @@ var PLAN_MODE_TOOL_NAMES = [
|
|
|
124154
124653
|
var SKILL_TOOL_NAMES = ["read_skill"];
|
|
124155
124654
|
|
|
124156
124655
|
// src/core/agents/offSecAgent/trace.ts
|
|
124157
|
-
import { createHash } from "crypto";
|
|
124158
|
-
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync13 } from "fs";
|
|
124159
|
-
import { dirname as
|
|
124656
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
124657
|
+
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync13 } from "node:fs";
|
|
124658
|
+
import { dirname as dirname7 } from "node:path";
|
|
124160
124659
|
var OUTPUT_PREVIEW_LIMIT = 2000;
|
|
124161
124660
|
var INPUT_PREVIEW_LIMIT = 1000;
|
|
124162
124661
|
var SYSTEM_PROMPT_HASH_PREFIX_LENGTH = 12;
|
|
@@ -124217,7 +124716,7 @@ class StepTraceWriter {
|
|
|
124217
124716
|
const now = Date.now();
|
|
124218
124717
|
this.agentStartTime = now;
|
|
124219
124718
|
this.lastStepTime = now;
|
|
124220
|
-
mkdirSync13(
|
|
124719
|
+
mkdirSync13(dirname7(this.tracePath), { recursive: true });
|
|
124221
124720
|
}
|
|
124222
124721
|
writeInit(data) {
|
|
124223
124722
|
const { systemPrompt, ...rest } = data;
|
|
@@ -124225,7 +124724,7 @@ class StepTraceWriter {
|
|
|
124225
124724
|
type: "init",
|
|
124226
124725
|
timestamp: new Date().toISOString(),
|
|
124227
124726
|
agentId: this.agentId,
|
|
124228
|
-
systemPromptHash:
|
|
124727
|
+
systemPromptHash: createHash2("sha256").update(systemPrompt).digest("hex").slice(0, SYSTEM_PROMPT_HASH_PREFIX_LENGTH),
|
|
124229
124728
|
systemPrompt,
|
|
124230
124729
|
...rest
|
|
124231
124730
|
};
|
|
@@ -124341,7 +124840,7 @@ class StepTraceWriter {
|
|
|
124341
124840
|
}
|
|
124342
124841
|
appendRecord(record3) {
|
|
124343
124842
|
try {
|
|
124344
|
-
appendFileSync3(this.tracePath, JSON.stringify(record3)
|
|
124843
|
+
appendFileSync3(this.tracePath, `${JSON.stringify(record3)}
|
|
124345
124844
|
`);
|
|
124346
124845
|
} catch {}
|
|
124347
124846
|
try {
|
|
@@ -124356,15 +124855,15 @@ class StepTraceWriter {
|
|
|
124356
124855
|
// src/core/agents/offSecAgent/offensiveSecurityAgent.ts
|
|
124357
124856
|
init_dist();
|
|
124358
124857
|
init_ai();
|
|
124359
|
-
import { existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
|
|
124360
|
-
import { writeFile as writeFile6 } from "fs/promises";
|
|
124361
|
-
import { join as
|
|
124858
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync14 } from "node:fs";
|
|
124859
|
+
import { writeFile as writeFile6 } from "node:fs/promises";
|
|
124860
|
+
import { join as join27 } from "node:path";
|
|
124362
124861
|
init_structured();
|
|
124363
124862
|
init_observability();
|
|
124364
124863
|
|
|
124365
124864
|
// src/core/operator/approvalGate.ts
|
|
124366
|
-
import { randomBytes as randomBytes2 } from "crypto";
|
|
124367
|
-
import { EventEmitter as EventEmitter3 } from "events";
|
|
124865
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
124866
|
+
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
124368
124867
|
var DEFAULT_DECISION_TIMEOUT_MS = 15 * 60 * 1000;
|
|
124369
124868
|
var INTERNAL_ID_PATTERN = /^(apr|act|tc)_\d+_[0-9a-f]{8}$/;
|
|
124370
124869
|
|
|
@@ -124409,8 +124908,8 @@ class ApprovalGate extends EventEmitter3 {
|
|
|
124409
124908
|
tier: 1,
|
|
124410
124909
|
timestamp: Date.now()
|
|
124411
124910
|
};
|
|
124412
|
-
return new Promise((
|
|
124413
|
-
const deferred = { approval, resolve:
|
|
124911
|
+
return new Promise((resolve8, reject) => {
|
|
124912
|
+
const deferred = { approval, resolve: resolve8, reject };
|
|
124414
124913
|
this.pendingApprovals.set(approval.id, deferred);
|
|
124415
124914
|
const timeoutMs = this.config.decisionTimeoutMs;
|
|
124416
124915
|
if (timeoutMs !== undefined && timeoutMs > 0) {
|
|
@@ -124648,6 +125147,7 @@ class OffensiveSecurityAgent {
|
|
|
124648
125147
|
subagentId;
|
|
124649
125148
|
persistentShell;
|
|
124650
125149
|
browserSession;
|
|
125150
|
+
ownsBrowserSession;
|
|
124651
125151
|
abortSignal;
|
|
124652
125152
|
userPrompt;
|
|
124653
125153
|
_session;
|
|
@@ -124687,19 +125187,22 @@ class OffensiveSecurityAgent {
|
|
|
124687
125187
|
}
|
|
124688
125188
|
if (!input.sandbox) {
|
|
124689
125189
|
const sessionHeaders = input.target ? resolveEffectiveHeaders(input.session, input.target) : input.session.config?.headers;
|
|
125190
|
+
this.ownsBrowserSession = !input.browserSession;
|
|
124690
125191
|
this.browserSession = input.browserSession ?? new PlaywrightMcpSession({
|
|
124691
125192
|
extraHttpHeaders: stripBrowserManagedHeaders(sessionHeaders)
|
|
124692
125193
|
});
|
|
125194
|
+
} else {
|
|
125195
|
+
this.ownsBrowserSession = false;
|
|
124693
125196
|
}
|
|
124694
|
-
const messagesDir = input.messagesDir ?? (input.subagentId ?
|
|
124695
|
-
const tracePath = input.subagentId ?
|
|
125197
|
+
const messagesDir = input.messagesDir ?? (input.subagentId ? join27(input.session.rootPath, "subagents", input.subagentId) : input.session.rootPath);
|
|
125198
|
+
const tracePath = input.subagentId ? join27(input.session.rootPath, "subagents", `${input.subagentId}.trace.jsonl`) : join27(messagesDir, "trace.jsonl");
|
|
124696
125199
|
const traceWriter = new StepTraceWriter({
|
|
124697
125200
|
tracePath,
|
|
124698
125201
|
agentId: input.subagentId ?? null,
|
|
124699
125202
|
eventBus: this.eventBus
|
|
124700
125203
|
});
|
|
124701
125204
|
const taskDriven = input.session.config?.taskDriven ?? false;
|
|
124702
|
-
const tasksDir = input.tasksDir ?? (taskDriven ? input.subagentId ?
|
|
125205
|
+
const tasksDir = input.tasksDir ?? (taskDriven ? input.subagentId ? join27(input.session.rootPath, "subagents", `${input.subagentId}-tasks`) : join27(input.session.rootPath, "tasks") : undefined);
|
|
124703
125206
|
const credentialManager = input.credentialManager ?? input.session.credentialManager;
|
|
124704
125207
|
const builtinTools = createAllTools({
|
|
124705
125208
|
session: input.session,
|
|
@@ -124715,6 +125218,8 @@ class OffensiveSecurityAgent {
|
|
|
124715
125218
|
credentialManager,
|
|
124716
125219
|
persistentShell: this.persistentShell,
|
|
124717
125220
|
skillsRegistry: input.skillsRegistry,
|
|
125221
|
+
promptInjectionLibrary: input.promptInjectionLibrary,
|
|
125222
|
+
promptInjectionLibrarySource: input.promptInjectionLibrarySource ?? input.session.config?.promptInjectionLibrarySource,
|
|
124718
125223
|
traceWriter,
|
|
124719
125224
|
tasksDir,
|
|
124720
125225
|
enableThinking: input.enableThinking,
|
|
@@ -124775,7 +125280,7 @@ class OffensiveSecurityAgent {
|
|
|
124775
125280
|
if (!existsSync18(messagesDir)) {
|
|
124776
125281
|
mkdirSync14(messagesDir, { recursive: true });
|
|
124777
125282
|
}
|
|
124778
|
-
const messagesPath =
|
|
125283
|
+
const messagesPath = join27(messagesDir, "messages.json");
|
|
124779
125284
|
const initialMessagesRef = {
|
|
124780
125285
|
current: input.messages ? [...input.messages] : [
|
|
124781
125286
|
{
|
|
@@ -124893,6 +125398,9 @@ class OffensiveSecurityAgent {
|
|
|
124893
125398
|
}
|
|
124894
125399
|
} finally {
|
|
124895
125400
|
this.persistentShell?.dispose();
|
|
125401
|
+
if (this.ownsBrowserSession && this.browserSession) {
|
|
125402
|
+
await this.browserSession.disconnect().catch(() => {});
|
|
125403
|
+
}
|
|
124896
125404
|
}
|
|
124897
125405
|
if (this.abortSignal?.aborted) {
|
|
124898
125406
|
throw new DOMException("Agent aborted by user", "AbortError");
|