@integsec/agentic-pentest-proxy 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cloudflare/wrangler.jsonc +1 -1
- package/dist/bin/integsec-agentic-pentest-bridge.js +2 -2
- package/dist/bin/integsec-agentic-pentest-gateway.js +107 -49
- package/dist/bin/integsec-agentic-pentest-gateway.js.map +1 -1
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.d.ts +3 -0
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.d.ts.map +1 -0
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.js +39 -0
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.js.map +1 -0
- package/dist/src/config.js +1 -1
- package/dist/src/gateway/admin.html +1 -1
- package/dist/src/gateway/admin.js +13 -4
- package/dist/src/gateway/docs-home.html +1 -1
- package/dist/src/gateway/local-control.d.ts +27 -0
- package/dist/src/gateway/local-control.d.ts.map +1 -0
- package/dist/src/gateway/local-control.js +176 -0
- package/dist/src/gateway/local-control.js.map +1 -0
- package/dist/src/gateway/runtime.d.ts.map +1 -1
- package/dist/src/gateway/runtime.js +17 -4
- package/dist/src/gateway/runtime.js.map +1 -1
- package/dist/src/gateway/server.d.ts +2 -0
- package/dist/src/gateway/server.d.ts.map +1 -1
- package/dist/src/gateway/server.js +22 -1
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/state.d.ts +1 -0
- package/dist/src/gateway/state.d.ts.map +1 -1
- package/dist/src/gateway/state.js +9 -0
- package/dist/src/gateway/state.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/manifest-schema.d.ts +1 -0
- package/dist/src/manifest-schema.d.ts.map +1 -1
- package/dist/src/manifest-schema.js +1 -0
- package/dist/src/manifest-schema.js.map +1 -1
- package/dist/src/proxy.d.ts +2 -1
- package/dist/src/proxy.d.ts.map +1 -1
- package/dist/src/proxy.js +4 -3
- package/dist/src/proxy.js.map +1 -1
- package/dist/src/scanners/runner.d.ts +4 -0
- package/dist/src/scanners/runner.d.ts.map +1 -0
- package/dist/src/scanners/runner.js +95 -0
- package/dist/src/scanners/runner.js.map +1 -0
- package/dist/src/technique-checker.d.ts.map +1 -1
- package/dist/src/technique-checker.js +6 -1
- package/dist/src/technique-checker.js.map +1 -1
- package/dist/src/tui/DecisionRow.d.ts.map +1 -1
- package/dist/src/tui/DecisionRow.js +2 -1
- package/dist/src/tui/DecisionRow.js.map +1 -1
- package/dist/src/tui/destination.d.ts +4 -0
- package/dist/src/tui/destination.d.ts.map +1 -0
- package/dist/src/tui/destination.js +43 -0
- package/dist/src/tui/destination.js.map +1 -0
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/validator.d.ts +5 -2
- package/dist/src/validator.d.ts.map +1 -1
- package/dist/src/validator.js +209 -5
- package/dist/src/validator.js.map +1 -1
- package/docs/managed-gateway.md +11 -3
- package/docs/security.md +6 -0
- package/docs/tui.md +3 -1
- package/package.json +6 -3
- package/scripts/postinstall.cjs +4 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { isIP } from "node:net";
|
|
3
|
+
const MAX_OUTPUT_BYTES = 64 * 1024;
|
|
4
|
+
const MAX_SCAN_MS = 120_000;
|
|
5
|
+
function singleHost(value) {
|
|
6
|
+
if (typeof value !== "string" || value.length > 253 || value !== value.trim() || !value) {
|
|
7
|
+
throw new Error("Enter one hostname or IP address.");
|
|
8
|
+
}
|
|
9
|
+
if (isIP(value))
|
|
10
|
+
return value;
|
|
11
|
+
if (!/^(?=.{1,253}$)(?:[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?\.)*[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?$/i.test(value) || !value.includes(".")) {
|
|
12
|
+
throw new Error("Nmap accepts one hostname or IP address, without a range, URL, or command options.");
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function ports(value) {
|
|
17
|
+
if (value === undefined)
|
|
18
|
+
return "80,443";
|
|
19
|
+
if (typeof value !== "string" || !/^\d{1,5}(?:-\d{1,5})?(?:,\d{1,5}(?:-\d{1,5})?)*$/.test(value)) {
|
|
20
|
+
throw new Error("Ports must be a comma-separated list of numbers or ranges, such as 80,443,8000-8010.");
|
|
21
|
+
}
|
|
22
|
+
let total = 0;
|
|
23
|
+
for (const part of value.split(",")) {
|
|
24
|
+
const [start, end = start] = part.split("-").map(Number);
|
|
25
|
+
if (start < 1 || end > 65535 || end < start)
|
|
26
|
+
throw new Error("Port numbers must be between 1 and 65535 in ascending ranges.");
|
|
27
|
+
total += end - start + 1;
|
|
28
|
+
}
|
|
29
|
+
if (total > 100)
|
|
30
|
+
throw new Error("A single Nmap call may cover at most 100 ports.");
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
export function nmapArgs(input) {
|
|
34
|
+
if (Object.keys(input).some((key) => !["target", "ports", "serviceDetection"].includes(key)))
|
|
35
|
+
throw new Error("Unknown Nmap scan option.");
|
|
36
|
+
const target = singleHost(input.target);
|
|
37
|
+
const selectedPorts = ports(input.ports);
|
|
38
|
+
if (input.serviceDetection !== undefined && typeof input.serviceDetection !== "boolean")
|
|
39
|
+
throw new Error("serviceDetection must be true or false.");
|
|
40
|
+
return ["-sT", "-Pn", "--max-rate", "20", "--max-retries", "1", "--host-timeout", "120s", "-p", selectedPorts,
|
|
41
|
+
...(input.serviceDetection ? ["-sV"] : []), target];
|
|
42
|
+
}
|
|
43
|
+
export function nucleiArgs(input) {
|
|
44
|
+
if (Object.keys(input).some((key) => key !== "url"))
|
|
45
|
+
throw new Error("Unknown Nuclei scan option.");
|
|
46
|
+
if (typeof input.url !== "string" || input.url.length > 2048 || input.url !== input.url.trim() || /[\x00-\x20\x7f]/.test(input.url)) {
|
|
47
|
+
throw new Error("Enter one HTTP or HTTPS URL.");
|
|
48
|
+
}
|
|
49
|
+
let url;
|
|
50
|
+
try {
|
|
51
|
+
url = new URL(input.url);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw new Error("Enter one HTTP or HTTPS URL.");
|
|
55
|
+
}
|
|
56
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
|
|
57
|
+
throw new Error("Nuclei requires one HTTP or HTTPS URL without credentials, query, or fragment.");
|
|
58
|
+
}
|
|
59
|
+
return ["-u", url.href, "-pt", "http", "-rl", "5", "-c", "1", "-timeout", "5", "-retries", "0", "-ni", "-duc",
|
|
60
|
+
"-etags", "dos,bruteforce,fuzz,headless,code", "-j"];
|
|
61
|
+
}
|
|
62
|
+
export async function runScanner(binary, args) {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const child = spawn(binary, args, { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
65
|
+
let output = "";
|
|
66
|
+
let size = 0;
|
|
67
|
+
let finished = false;
|
|
68
|
+
const fail = (error) => { if (!finished) {
|
|
69
|
+
finished = true;
|
|
70
|
+
child.kill();
|
|
71
|
+
reject(error);
|
|
72
|
+
} };
|
|
73
|
+
const timer = setTimeout(() => fail(new Error(`${binary} exceeded the two-minute scan limit.`)), MAX_SCAN_MS);
|
|
74
|
+
const collect = (data) => {
|
|
75
|
+
size += data.length;
|
|
76
|
+
if (size > MAX_OUTPUT_BYTES)
|
|
77
|
+
return fail(new Error(`${binary} output exceeded 64 KiB; narrow the scan.`));
|
|
78
|
+
output += data.toString("utf8");
|
|
79
|
+
};
|
|
80
|
+
child.stdout.on("data", collect);
|
|
81
|
+
child.stderr.on("data", collect);
|
|
82
|
+
child.on("error", (error) => fail(new Error(`${binary} could not start. Install it on this computer and check that it is on PATH. ${error.message}`)));
|
|
83
|
+
child.on("close", (code) => {
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
if (finished)
|
|
86
|
+
return;
|
|
87
|
+
finished = true;
|
|
88
|
+
if (code !== 0)
|
|
89
|
+
reject(new Error(`${binary} exited with code ${code}. ${output.slice(0, 2000)}`));
|
|
90
|
+
else
|
|
91
|
+
resolve(output || `${binary} completed with no findings.`);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/scanners/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"technique-checker.d.ts","sourceRoot":"","sources":["../../src/technique-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"technique-checker.d.ts","sourceRoot":"","sources":["../../src/technique-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AA+GjE;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAezE;AAMD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C;;;;;OAKG;gBACS,cAAc,CAAC,EAAE,iBAAiB;IAQ9C;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAoB5C;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,OAAO;IAalE;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAW7B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;CAsB7B"}
|
|
@@ -37,7 +37,7 @@ const DEFAULT_MAPPINGS = {
|
|
|
37
37
|
"dnsx", "httpx", "katana", "gowitness", "masscan",
|
|
38
38
|
"shuffledns", "subfinder", "uncover", "waybackurls", "notify",
|
|
39
39
|
"nmap", "nmap_scan",
|
|
40
|
-
"trivy", "checkov", "kube-hunter", "kubeaudit", "kube-bench",
|
|
40
|
+
"trivy", "trivy_version", "scan_filesystem", "scan_repository", "findings_list", "findings_get", "checkov", "kube-hunter", "kubeaudit", "kube-bench",
|
|
41
41
|
"shodan", "virustotal", "bloodhound",
|
|
42
42
|
// Hex Strike AI MCP
|
|
43
43
|
"rustscan_fast_scan", "subfinder_scan", "katana_crawl",
|
|
@@ -55,6 +55,7 @@ const DEFAULT_MAPPINGS = {
|
|
|
55
55
|
"basic_scan", "intense_scan", "stealth_scan", "quick_scan",
|
|
56
56
|
// Semgrep MCP
|
|
57
57
|
"semgrep", "security_check", "semgrep_scan", "semgrep_scan_with_custom_rule", "semgrep_findings",
|
|
58
|
+
"semgrep_rule_schema", "get_supported_languages", "get_abstract_syntax_tree",
|
|
58
59
|
// Gitleaks MCP
|
|
59
60
|
"gitleaks", "gitleaks_scan",
|
|
60
61
|
// Ghidra MCP
|
|
@@ -84,6 +85,10 @@ const DEFAULT_MAPPINGS = {
|
|
|
84
85
|
"vulnerability_scan", "web_enumeration",
|
|
85
86
|
// Burp Suite MCP (PortSwigger official)
|
|
86
87
|
"start_scan",
|
|
88
|
+
"send_http1_request", "send_http2_request",
|
|
89
|
+
"create_repeater_tab", "create_repeater_tab_http2", "send_to_intruder",
|
|
90
|
+
"url_encode", "url_decode", "base64_encode", "base64_decode",
|
|
91
|
+
"generate_random_string",
|
|
87
92
|
// Acunetix MCP
|
|
88
93
|
"acunetix",
|
|
89
94
|
// Next.js CVE scanner / Unified all-in-one
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"technique-checker.js","sourceRoot":"","sources":["../../src/technique-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,MAAM,gBAAgB,GAAsB;IAC1C,GAAG,EAAE;QACH,KAAK,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC;QACtD,QAAQ,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;KAC3B;IACD,WAAW,EAAE;QACX,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;KACvC;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,CAAC;QACxD,QAAQ,EAAE,EAAE;KACb;IACD,KAAK,EAAE;QACL,KAAK,EAAE;YACL,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO;YACjD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS;YACjD,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ;YAC7D,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"technique-checker.js","sourceRoot":"","sources":["../../src/technique-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,MAAM,gBAAgB,GAAsB;IAC1C,GAAG,EAAE;QACH,KAAK,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC;QACtD,QAAQ,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;KAC3B;IACD,WAAW,EAAE;QACX,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;KACvC;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,CAAC;QACxD,QAAQ,EAAE,EAAE;KACb;IACD,KAAK,EAAE;QACL,KAAK,EAAE;YACL,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO;YACjD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS;YACjD,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ;YAC7D,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY;YACpJ,QAAQ,EAAE,YAAY,EAAE,YAAY;YACpC,oBAAoB;YACpB,oBAAoB,EAAE,gBAAgB,EAAE,cAAc;YACtD,iBAAiB;YACjB,WAAW,EAAE,UAAU,EAAE,mBAAmB,EAAE,gBAAgB;YAC9D,aAAa,EAAE,cAAc;YAC7B,4BAA4B;YAC5B,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB;YAChE,UAAU;YACV,kBAAkB;YAClB,iBAAiB;YACjB,gBAAgB,EAAE,eAAe,EAAE,mBAAmB,EAAE,YAAY;YACpE,sBAAsB,EAAE,qBAAqB,EAAE,yBAAyB;YACxE,2CAA2C;YAC3C,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY;YAC1D,cAAc;YACd,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,+BAA+B,EAAE,kBAAkB;YAChG,qBAAqB,EAAE,yBAAyB,EAAE,0BAA0B;YAC5E,eAAe;YACf,UAAU,EAAE,eAAe;YAC3B,aAAa;YACb,QAAQ,EAAE,oBAAoB;YAC9B,YAAY;YACZ,OAAO,EAAE,UAAU;YACnB,cAAc;YACd,SAAS;YACT,0BAA0B;YAC1B,cAAc,EAAE,oBAAoB;SACrC;QACD,QAAQ,EAAE;YACR,iEAAiE;YACjE,OAAO;YACP,iEAAiE;YACjE,SAAS;SACV;KACF;IACD,OAAO,EAAE;QACP,KAAK,EAAE;YACL,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO;YACvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK;YACxD,WAAW,EAAE,WAAW,EAAE,kBAAkB;YAC5C,6BAA6B;YAC7B,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,kBAAkB;YAC9E,iBAAiB;YACjB,oBAAoB,EAAE,iBAAiB;YACvC,wCAAwC;YACxC,YAAY;YACZ,oBAAoB,EAAE,oBAAoB;YAC1C,qBAAqB,EAAE,2BAA2B,EAAE,kBAAkB;YACtE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe;YAC5D,wBAAwB;YACxB,eAAe;YACf,UAAU;YACV,2CAA2C;YAC3C,aAAa,EAAE,gBAAgB,EAAE,cAAc;SAChD;QACD,QAAQ,EAAE,EAAE;KACb;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,SAAS;YACtC,oBAAoB;YACpB,cAAc,EAAE,eAAe,EAAE,gBAAgB;YACjD,iBAAiB;YACjB,kBAAkB,EAAE,eAAe;YACnC,eAAe;YACf,cAAc;YACd,0CAA0C;YAC1C,QAAQ;SACT;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC;QAChC,QAAQ,EAAE,EAAE;KACb;CACF,CAAC;AAEF,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CACtC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EACjB,CAAC,CAAC,MAAM,CAAC;IACP,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACtC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC1C,CAAC,CACH,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,oCAAoC,QAAQ,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACrG,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,iBAAiB,MAAM,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E,MAAM,OAAO,gBAAgB;IACV,QAAQ,CAAoB;IAE7C;;;;;OAKG;IACH,YAAY,cAAkC;QAC5C,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC,aAAa,CAAC,gBAAgB,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,4EAA4E;IAC5E,aAAa;IACb,4EAA4E;IAE5E;;;OAGG;IACH,WAAW,CAAC,QAAgB;QAC1B,IAAI,QAAQ,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAEjC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QAErC,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChE,uDAAuD;YACvD,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;gBAC/D,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,mBAAmB;YACnB,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;gBACxF,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,QAAgB,EAAE,kBAA4B;QACtD,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAElD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAEpC,OAAO,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,4EAA4E;IAC5E,kBAAkB;IAClB,4EAA4E;IAE5E;;;;;;OAMG;IACK,MAAM,CAAC,cAAc,CAAC,aAAqB,EAAE,OAAe;QAClE,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE;YAAE,OAAO,KAAK,CAAC;QAC3B,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACxB,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,aAAa;aACjB,KAAK,CAAC,YAAY,CAAC;aACnB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACK,MAAM,CAAC,aAAa,CAC1B,IAAuB,EACvB,MAAyB;QAEzB,iBAAiB;QACjB,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAClD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/E,CAAC;QAED,qCAAqC;QACrC,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/E,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBACzC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DecisionRow.d.ts","sourceRoot":"","sources":["../../../src/tui/DecisionRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"DecisionRow.d.ts","sourceRoot":"","sources":["../../../src/tui/DecisionRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,UAAU,gBAAgB;IACxB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE,gBAAgB,GAAG,KAAK,CAAC,YAAY,CAmB3E"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Text } from "ink";
|
|
3
|
+
import { auditDestination } from "./destination.js";
|
|
3
4
|
export function DecisionRow({ entry }) {
|
|
4
5
|
const time = entry.timestamp.slice(11, 19);
|
|
5
6
|
const isBlocked = entry.decision.startsWith("BLOCKED");
|
|
@@ -7,7 +8,7 @@ export function DecisionRow({ entry }) {
|
|
|
7
8
|
const icon = isBlocked ? "✗" : "✓";
|
|
8
9
|
const color = isBlocked ? "red" : isNoTarget ? "gray" : "green";
|
|
9
10
|
const shortDecision = entry.decision.replace("BLOCKED_", "");
|
|
10
|
-
const target = entry
|
|
11
|
+
const target = auditDestination(entry);
|
|
11
12
|
const reason = isBlocked ? ` [${shortDecision}]` : "";
|
|
12
13
|
const server = entry.server_name ? `[${entry.server_name}] ` : "";
|
|
13
14
|
return (_jsxs(Text, { color: color, children: [time, " ", server, icon, " ", entry.decision.split("_")[0].padEnd(7), " ", entry.tool_name, " \u2192 ", target, reason, " (", entry.duration_ms, "ms)"] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DecisionRow.js","sourceRoot":"","sources":["../../../src/tui/DecisionRow.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"DecisionRow.js","sourceRoot":"","sources":["../../../src/tui/DecisionRow.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAE3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAMpD,MAAM,UAAU,WAAW,CAAC,EAAE,KAAK,EAAoB;IACrD,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,KAAK,mBAAmB,CAAC;IAE1D,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACnC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAEhE,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,aAAa,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAEtD,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAElE,OAAO,CACL,MAAC,IAAI,IAAC,KAAK,EAAE,KAAK,aACf,IAAI,OAAG,MAAM,EAAE,IAAI,OAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAG,KAAK,CAAC,SAAS,cAAK,MAAM,EAAE,MAAM,QAAI,KAAK,CAAC,WAAW,WAClH,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destination.d.ts","sourceRoot":"","sources":["../../../src/tui/destination.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAe9C,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CA2B1D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
function validPort(value) {
|
|
3
|
+
return Number.isInteger(value) && value >= 1 && value <= 65535;
|
|
4
|
+
}
|
|
5
|
+
function normalizedHost(value) {
|
|
6
|
+
return value.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
|
|
7
|
+
}
|
|
8
|
+
function endpoint(host, protocol, port) {
|
|
9
|
+
const displayedHost = isIP(host) === 6 ? `[${host}]` : host;
|
|
10
|
+
return `${protocol}://${displayedHost}:${port}`;
|
|
11
|
+
}
|
|
12
|
+
/** Display only destination details that were recorded in structured audit fields. */
|
|
13
|
+
export function auditDestination(entry) {
|
|
14
|
+
const target = entry.extracted_target;
|
|
15
|
+
if (!target)
|
|
16
|
+
return "—";
|
|
17
|
+
const params = entry.tool_parameters;
|
|
18
|
+
if (!params || typeof params !== "object" || Array.isArray(params))
|
|
19
|
+
return target;
|
|
20
|
+
if (typeof params.targetHostname === "string" &&
|
|
21
|
+
normalizedHost(params.targetHostname) === normalizedHost(target) &&
|
|
22
|
+
validPort(params.targetPort) && typeof params.usesHttps === "boolean") {
|
|
23
|
+
const protocol = params.usesHttps ? "https" : "http";
|
|
24
|
+
const address = endpoint(target, protocol, params.targetPort);
|
|
25
|
+
const version = /(?:http2_request|repeater_tab_http2)$/.test(entry.tool_name) ? "HTTP/2"
|
|
26
|
+
: /(?:http1_request|repeater_tab|send_to_intruder)$/.test(entry.tool_name) ? "HTTP/1.1"
|
|
27
|
+
: undefined;
|
|
28
|
+
return version ? `${address} (${version})` : address;
|
|
29
|
+
}
|
|
30
|
+
for (const [key, value] of Object.entries(params)) {
|
|
31
|
+
if (!/^(?:url|uri|target_url|targeturl|base_url|baseurl)$/i.test(key) || typeof value !== "string")
|
|
32
|
+
continue;
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(value);
|
|
35
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:") || normalizedHost(url.hostname) !== normalizedHost(target))
|
|
36
|
+
continue;
|
|
37
|
+
return endpoint(target, url.protocol === "https:" ? "https" : "http", Number(url.port || (url.protocol === "https:" ? 443 : 80)));
|
|
38
|
+
}
|
|
39
|
+
catch { /* Older audit entries can contain non-URL values. */ }
|
|
40
|
+
}
|
|
41
|
+
return target;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=destination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destination.js","sourceRoot":"","sources":["../../../src/tui/destination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAK,KAAgB,IAAI,CAAC,IAAK,KAAgB,IAAI,KAAK,CAAC;AACzF,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAA0B,EAAE,IAAY;IACtE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,OAAO,GAAG,QAAQ,MAAM,aAAa,IAAI,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,MAAM,GAAG,KAAK,CAAC,gBAAgB,CAAC;IACtC,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IACxB,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,CAAC;IACrC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAElF,IAAI,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ;QACzC,cAAc,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,cAAc,CAAC,MAAM,CAAC;QAChE,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC1E,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;QACrD,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9D,MAAM,OAAO,GAAG,uCAAuC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ;YACtF,CAAC,CAAC,kDAAkD,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU;gBACvF,CAAC,CAAC,SAAS,CAAC;QACd,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;IACvD,CAAC;IAED,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,sDAAsD,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAS;QAC7G,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,cAAc,CAAC,MAAM,CAAC;gBAAE,SAAS;YACjI,OAAO,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpI,CAAC;QAAC,MAAM,CAAC,CAAC,qDAAqD,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface ScopeManifest {
|
|
|
36
36
|
cloud_accounts: string[];
|
|
37
37
|
};
|
|
38
38
|
excluded_targets: string[];
|
|
39
|
+
/** Admin-approved local source directories for Semgrep. */
|
|
40
|
+
authorized_local_paths?: string[];
|
|
39
41
|
authorized_techniques: string[];
|
|
40
42
|
excluded_techniques: string[];
|
|
41
43
|
engagement_window: {
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAChB,SAAS,GACT,yBAAyB,GACzB,sBAAsB,GACtB,wBAAwB,GACxB,mBAAmB,GACnB,oBAAoB,GACpB,uBAAuB,GACvB,yBAAyB,GACzB,+BAA+B,GAC/B,wBAAwB,GACxB,mBAAmB,CAAC;AAExB,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE;QAClB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,cAAc,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;IACF,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,iBAAiB,EAAE;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAChB,SAAS,GACT,yBAAyB,GACzB,sBAAsB,GACtB,wBAAwB,GACxB,mBAAmB,GACnB,oBAAoB,GACpB,uBAAuB,GACvB,yBAAyB,GACzB,+BAA+B,GAC/B,wBAAwB,GACxB,mBAAmB,CAAC;AAExB,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE;QAClB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,cAAc,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;IACF,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,2DAA2D;IAC3D,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,iBAAiB,EAAE;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH"}
|
package/dist/src/validator.d.ts
CHANGED
|
@@ -35,8 +35,11 @@ export declare class ScopeValidator {
|
|
|
35
35
|
private readonly urlHosts;
|
|
36
36
|
private readonly allowUnstructuredCommands;
|
|
37
37
|
private readonly requireMappedTechnique;
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
private readonly executionProfile?;
|
|
39
|
+
private readonly trivyBatchOwners;
|
|
40
|
+
constructor(manifest: ScopeManifest, dnsResolver?: DnsResolver, techniqueChecker?: TechniqueChecker, allowUnstructuredCommands?: boolean, requireMappedTechnique?: boolean, executionProfile?: "isolated" | "restricted" | "connected");
|
|
41
|
+
validate(toolName: string, params: Record<string, unknown>, actor?: string): Promise<ValidationResult>;
|
|
42
|
+
registerTrivyBatch(batchId: string, actor?: string): void;
|
|
40
43
|
private validateTarget;
|
|
41
44
|
/**
|
|
42
45
|
* Build a ValidationResult, automatically populating durationMs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAElE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAiB1D,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAC3D,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAU;IACpD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAA0C;IAC5E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;gBAG5D,QAAQ,EAAE,aAAa,EACvB,WAAW,CAAC,EAAE,WAAW,EACzB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,yBAAyB,UAAQ,EACjC,sBAAsB,UAAQ,EAC9B,gBAAgB,CAAC,EAAE,UAAU,GAAG,YAAY,GAAG,WAAW;IAatD,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,gBAAgB,CAAC;IA4L5B,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;YAU3C,cAAc;IAuJ5B;;OAEG;IACH,OAAO,CAAC,MAAM;CAMf"}
|
package/dist/src/validator.js
CHANGED
|
@@ -25,13 +25,20 @@
|
|
|
25
25
|
* check order matters — do not reorder without updating tests.
|
|
26
26
|
*/
|
|
27
27
|
import { isIP } from "node:net";
|
|
28
|
+
import { realpathSync, statSync } from "node:fs";
|
|
29
|
+
import { isAbsolute, relative, parse as parsePath } from "node:path";
|
|
28
30
|
import { extractTargets } from "./extractor.js";
|
|
29
31
|
import { DnsResolver } from "./dns-resolver.js";
|
|
30
32
|
import { isCidr, isIpInCidr, isRangeWithin, isRestrictedIp, rangesOverlap } from "./ip-matcher.js";
|
|
31
33
|
import { matchesDomain } from "./domain-matcher.js";
|
|
32
34
|
import { TechniqueChecker } from "./technique-checker.js";
|
|
33
35
|
const SAFE_TARGETLESS_TOOLS = new Set([
|
|
34
|
-
"whoami", "date", "ls", "list_tools", "get_version", "list_pentests",
|
|
36
|
+
"whoami", "date", "ls", "list_tools", "get_version", "trivy_version", "list_pentests",
|
|
37
|
+
"semgrep_rule_schema", "get_supported_languages", "get_abstract_syntax_tree",
|
|
38
|
+
"url_encode", "url_decode", "base64_encode", "base64_decode", "generate_random_string",
|
|
39
|
+
]);
|
|
40
|
+
const BURP_REQUEST_TOOLS = new Set([
|
|
41
|
+
"send_http1_request", "send_http2_request", "create_repeater_tab", "create_repeater_tab_http2", "send_to_intruder",
|
|
35
42
|
]);
|
|
36
43
|
const UNSTRUCTURED_KEYS = new Set([
|
|
37
44
|
"command", "cmd", "command_line", "commandline", "cmdline", "shell_command",
|
|
@@ -45,17 +52,20 @@ export class ScopeValidator {
|
|
|
45
52
|
urlHosts;
|
|
46
53
|
allowUnstructuredCommands;
|
|
47
54
|
requireMappedTechnique;
|
|
48
|
-
|
|
55
|
+
executionProfile;
|
|
56
|
+
trivyBatchOwners = new Map();
|
|
57
|
+
constructor(manifest, dnsResolver, techniqueChecker, allowUnstructuredCommands = false, requireMappedTechnique = false, executionProfile) {
|
|
49
58
|
this.manifest = manifest;
|
|
50
59
|
this.dnsResolver = dnsResolver ?? new DnsResolver();
|
|
51
60
|
this.techniqueChecker = techniqueChecker ?? new TechniqueChecker();
|
|
52
61
|
this.allowUnstructuredCommands = allowUnstructuredCommands;
|
|
53
62
|
this.requireMappedTechnique = requireMappedTechnique;
|
|
63
|
+
this.executionProfile = executionProfile;
|
|
54
64
|
this.urlHosts = manifest.authorized_targets.urls
|
|
55
65
|
.map((url) => ({ host: hostOf(url), url }))
|
|
56
66
|
.filter((u) => u.host !== null);
|
|
57
67
|
}
|
|
58
|
-
async validate(toolName, params) {
|
|
68
|
+
async validate(toolName, params, actor) {
|
|
59
69
|
const startTime = Date.now();
|
|
60
70
|
// ─────────────────────────────────────────────────────────────────────────
|
|
61
71
|
// Step 0: Request shape — anything we cannot inspect is blocked
|
|
@@ -124,7 +134,100 @@ export class ScopeValidator {
|
|
|
124
134
|
// ─────────────────────────────────────────────────────────────────────────
|
|
125
135
|
// Steps 3–4: Extract every target
|
|
126
136
|
// ─────────────────────────────────────────────────────────────────────────
|
|
127
|
-
|
|
137
|
+
if (toolName.toLowerCase() === "semgrep_scan" || toolName.toLowerCase() === "scan_filesystem") {
|
|
138
|
+
const isTrivy = toolName.toLowerCase() === "scan_filesystem";
|
|
139
|
+
const files = params.code_files;
|
|
140
|
+
const semgrepFiles = Array.isArray(files) ? files : [];
|
|
141
|
+
const targetPath = params.target;
|
|
142
|
+
const paths = isTrivy ? [targetPath] : semgrepFiles.map((item) => item?.path);
|
|
143
|
+
if (isTrivy && (params.targetType !== "filesystem" || params.outputFormat !== "json" ||
|
|
144
|
+
!Array.isArray(params.scanType) || !params.scanType.length ||
|
|
145
|
+
params.scanType.some((item) => !["vuln", "misconfig", "license", "secret"].includes(item)) ||
|
|
146
|
+
Object.keys(params).some((key) => !["target", "targetType", "scanType", "outputFormat", "severities", "fixedOnly"].includes(key)))) {
|
|
147
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: "Trivy filesystem scans require an absolute approved path, JSON output, and reviewed scan types." });
|
|
148
|
+
}
|
|
149
|
+
if ((!isTrivy && (Object.keys(params).some((key) => key !== "code_files") || !Array.isArray(files) || !files.length || files.length > 100)) ||
|
|
150
|
+
!this.manifest.authorized_local_paths?.length) {
|
|
151
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: `${isTrivy ? "Trivy" : "Semgrep"} scans require an administrator-approved local code folder in this engagement.` });
|
|
152
|
+
}
|
|
153
|
+
let roots;
|
|
154
|
+
try {
|
|
155
|
+
roots = this.manifest.authorized_local_paths.map((path) => {
|
|
156
|
+
const root = realpathSync(path);
|
|
157
|
+
if (!statSync(root).isDirectory() || root === parsePath(root).root)
|
|
158
|
+
throw new Error("not an approved folder");
|
|
159
|
+
return root;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: "An approved local code folder is missing or inaccessible on this computer." });
|
|
164
|
+
}
|
|
165
|
+
for (let index = 0; index < paths.length; index++) {
|
|
166
|
+
const item = isTrivy ? null : semgrepFiles[index];
|
|
167
|
+
const path = paths[index];
|
|
168
|
+
if ((!isTrivy && (!item || typeof item !== "object" || Array.isArray(item) || Object.keys(item).some((key) => key !== "path"))) ||
|
|
169
|
+
typeof path !== "string" || !isAbsolute(path)) {
|
|
170
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: `${isTrivy ? "Trivy target" : "Each Semgrep code_files item"} must be an absolute local path.` });
|
|
171
|
+
}
|
|
172
|
+
let file;
|
|
173
|
+
try {
|
|
174
|
+
file = realpathSync(path);
|
|
175
|
+
if (isTrivy ? !statSync(file).isDirectory() && !statSync(file).isFile() : !statSync(file).isFile())
|
|
176
|
+
throw new Error("invalid path");
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: `A ${isTrivy ? "Trivy path" : "Semgrep file"} is missing or inaccessible on this computer.` });
|
|
180
|
+
}
|
|
181
|
+
if (!roots.some((root) => { const rel = relative(root, file); return (isTrivy || rel !== "") && rel !== ".." && !rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute(rel); })) {
|
|
182
|
+
return this.result(startTime, { decision: "BLOCKED_NOT_IN_SCOPE", reason: `${isTrivy ? "Trivy path" : "Semgrep file"} is outside the approved local code folders.` });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return this.result(startTime, { decision: "ALLOWED_NO_TARGET", reason: `${isTrivy ? "Trivy path" : `All ${paths.length} Semgrep files`} verified inside approved local code folders.` });
|
|
186
|
+
}
|
|
187
|
+
if (["findings_list", "findings_get"].includes(toolName.toLowerCase())) {
|
|
188
|
+
const batchId = params.batchID;
|
|
189
|
+
if (typeof batchId !== "string" || !/^[0-9a-f-]{36}$/i.test(batchId) || this.trivyBatchOwners.get(batchId) !== (actor ?? "local")) {
|
|
190
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: "Trivy findings require a batch created by this caller under the active engagement." });
|
|
191
|
+
}
|
|
192
|
+
return this.result(startTime, { decision: "ALLOWED_NO_TARGET", reason: "Trivy batch belongs to this caller and engagement." });
|
|
193
|
+
}
|
|
194
|
+
if (toolName.toLowerCase() === "scan_repository") {
|
|
195
|
+
if (this.executionProfile && this.executionProfile !== "connected") {
|
|
196
|
+
return this.result(startTime, { decision: "BLOCKED_TECHNIQUE", reason: "Remote repository scans require a connected execution profile." });
|
|
197
|
+
}
|
|
198
|
+
const repository = params.target;
|
|
199
|
+
const scanType = params.scanType;
|
|
200
|
+
let url;
|
|
201
|
+
try {
|
|
202
|
+
if (params.targetType !== "repository" || typeof repository !== "string")
|
|
203
|
+
throw new Error("invalid target");
|
|
204
|
+
url = new URL(repository);
|
|
205
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash ||
|
|
206
|
+
url.pathname === "/" || !Array.isArray(scanType) || scanType.length === 0 ||
|
|
207
|
+
scanType.some((item) => !["vuln", "misconfig", "license"].includes(item)))
|
|
208
|
+
throw new Error("invalid target");
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: "Trivy repository scans require an approved HTTPS repository URL and only vuln, misconfig, or license scan types." });
|
|
212
|
+
}
|
|
213
|
+
const requested = `${url.origin}${url.pathname.replace(/\/$/, "")}`;
|
|
214
|
+
if (!this.manifest.authorized_targets.urls.some((allowed) => {
|
|
215
|
+
try {
|
|
216
|
+
const approved = new URL(allowed);
|
|
217
|
+
return !approved.search && !approved.hash && `${approved.origin}${approved.pathname.replace(/\/$/, "")}` === requested;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
})) {
|
|
223
|
+
return this.result(startTime, { decision: "BLOCKED_NOT_IN_SCOPE", reason: "Trivy repository URL must be listed exactly in authorized_targets.urls." });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const burpRequest = BURP_REQUEST_TOOLS.has(toolName.toLowerCase()) ? parseBurpRequest(toolName.toLowerCase(), params) : undefined;
|
|
227
|
+
if (burpRequest?.error) {
|
|
228
|
+
return this.result(startTime, { decision: "BLOCKED_INVALID_REQUEST", reason: burpRequest.error });
|
|
229
|
+
}
|
|
230
|
+
const extractions = extractTargets(toolName, burpRequest ? { targetHostname: params.targetHostname } : params);
|
|
128
231
|
if (extractions.length === 0) {
|
|
129
232
|
return this.result(startTime, SAFE_TARGETLESS_TOOLS.has(toolName.toLowerCase())
|
|
130
233
|
? { decision: "ALLOWED_NO_TARGET", reason: `Targetless local tool "${toolName}" is explicitly allowed.` }
|
|
@@ -134,7 +237,7 @@ export class ScopeValidator {
|
|
|
134
237
|
// Steps 5–10: Every target must be in scope
|
|
135
238
|
// ─────────────────────────────────────────────────────────────────────────
|
|
136
239
|
const allowed = [];
|
|
137
|
-
const requestUrls = collectHttpUrls(params);
|
|
240
|
+
const requestUrls = burpRequest?.url ? [burpRequest.url] : collectHttpUrls(params);
|
|
138
241
|
for (const { target } of extractions) {
|
|
139
242
|
const r = await this.validateTarget(target, requestUrls);
|
|
140
243
|
if (r.decision !== "ALLOWED") {
|
|
@@ -153,6 +256,13 @@ export class ScopeValidator {
|
|
|
153
256
|
resolvedIps: [...new Set(allowed.flatMap((r) => r.resolvedIps ?? []))],
|
|
154
257
|
});
|
|
155
258
|
}
|
|
259
|
+
registerTrivyBatch(batchId, actor) {
|
|
260
|
+
if (!/^[0-9a-f-]{36}$/i.test(batchId))
|
|
261
|
+
return;
|
|
262
|
+
this.trivyBatchOwners.set(batchId, actor ?? "local");
|
|
263
|
+
if (this.trivyBatchOwners.size > 1000)
|
|
264
|
+
this.trivyBatchOwners.delete(this.trivyBatchOwners.keys().next().value);
|
|
265
|
+
}
|
|
156
266
|
// ---------------------------------------------------------------------------
|
|
157
267
|
// Per-target pipeline
|
|
158
268
|
// ---------------------------------------------------------------------------
|
|
@@ -356,6 +466,100 @@ function collectHttpUrls(value, depth = 0) {
|
|
|
356
466
|
}
|
|
357
467
|
return [];
|
|
358
468
|
}
|
|
469
|
+
/** Derive the actual Burp request URL instead of trusting targetHostname alone. */
|
|
470
|
+
function parseBurpRequest(toolName, params) {
|
|
471
|
+
const { targetHostname, targetPort, usesHttps } = params;
|
|
472
|
+
if (typeof targetHostname !== "string" || !targetHostname ||
|
|
473
|
+
!Number.isInteger(targetPort) || targetPort < 1 || targetPort > 65535 ||
|
|
474
|
+
typeof usesHttps !== "boolean") {
|
|
475
|
+
return { error: "Burp request requires a valid targetHostname, targetPort, and usesHttps." };
|
|
476
|
+
}
|
|
477
|
+
const scheme = usesHttps ? "https:" : "http:";
|
|
478
|
+
let base;
|
|
479
|
+
try {
|
|
480
|
+
base = new URL(`${scheme}//${targetHostname}:${targetPort}`);
|
|
481
|
+
if (base.username || base.password || normalizeHost(base.hostname) !== normalizeHost(targetHostname) ||
|
|
482
|
+
base.pathname !== "/" || base.search || base.hash)
|
|
483
|
+
throw new Error("invalid host");
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
return { error: "Burp targetHostname must be a hostname or IP address." };
|
|
487
|
+
}
|
|
488
|
+
const http2 = toolName === "send_http2_request" || toolName === "create_repeater_tab_http2";
|
|
489
|
+
let path;
|
|
490
|
+
let authority;
|
|
491
|
+
if (http2) {
|
|
492
|
+
const pseudo = params.pseudoHeaders;
|
|
493
|
+
const headers = params.headers;
|
|
494
|
+
if (!pseudo || typeof pseudo !== "object" || Array.isArray(pseudo) ||
|
|
495
|
+
!headers || typeof headers !== "object" || Array.isArray(headers)) {
|
|
496
|
+
return { error: "Burp HTTP/2 request requires pseudoHeaders and headers objects." };
|
|
497
|
+
}
|
|
498
|
+
const fields = pseudo;
|
|
499
|
+
if (typeof fields[":method"] !== "string" || fields[":method"].toUpperCase() === "CONNECT" ||
|
|
500
|
+
fields[":scheme"] !== scheme.slice(0, -1) || typeof fields[":authority"] !== "string" ||
|
|
501
|
+
typeof fields[":path"] !== "string") {
|
|
502
|
+
return { error: "Burp HTTP/2 request needs a non-CONNECT method and matching :scheme, :authority, and :path." };
|
|
503
|
+
}
|
|
504
|
+
path = fields[":path"];
|
|
505
|
+
authority = fields[":authority"];
|
|
506
|
+
const hostHeaders = Object.entries(headers).filter(([key]) => key.toLowerCase() === "host");
|
|
507
|
+
if (hostHeaders.length > 1 || (hostHeaders.length === 1 && hostHeaders[0][1] !== authority)) {
|
|
508
|
+
return { error: "Burp Host header must match :authority." };
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
const content = params.content;
|
|
513
|
+
if (typeof content !== "string" || !content)
|
|
514
|
+
return { error: "Burp HTTP/1 request content is required." };
|
|
515
|
+
const lines = content.split(/\r?\n/);
|
|
516
|
+
const requestLine = /^([A-Z]+) (\S+) HTTP\/1\.[01]$/.exec(lines[0]);
|
|
517
|
+
if (!requestLine || requestLine[1] === "CONNECT")
|
|
518
|
+
return { error: "Burp HTTP/1 request needs a non-CONNECT request line." };
|
|
519
|
+
path = requestLine[2];
|
|
520
|
+
const blankLine = lines.findIndex((line, index) => index > 0 && line === "");
|
|
521
|
+
if (blankLine < 0)
|
|
522
|
+
return { error: "Burp HTTP/1 request needs a blank line after its headers." };
|
|
523
|
+
const headerLines = lines.slice(1, blankLine);
|
|
524
|
+
if (headerLines.some((line) => !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+:[^\r\n]*$/.test(line))) {
|
|
525
|
+
return { error: "Burp HTTP/1 request contains an ambiguous header." };
|
|
526
|
+
}
|
|
527
|
+
const hosts = headerLines.filter((line) => /^host\s*:/i.test(line));
|
|
528
|
+
if (hosts.length !== 1)
|
|
529
|
+
return { error: "Burp HTTP/1 request needs exactly one Host header." };
|
|
530
|
+
authority = hosts[0].replace(/^host\s*:/i, "").trim();
|
|
531
|
+
}
|
|
532
|
+
let hostUrl;
|
|
533
|
+
try {
|
|
534
|
+
hostUrl = new URL(`${scheme}//${authority}`);
|
|
535
|
+
if (hostUrl.username || hostUrl.password || hostUrl.pathname !== "/" || hostUrl.search || hostUrl.hash ||
|
|
536
|
+
hostUrl.origin !== base.origin)
|
|
537
|
+
throw new Error("mismatch");
|
|
538
|
+
}
|
|
539
|
+
catch {
|
|
540
|
+
return { error: "Burp request authority must match the approved target hostname and port." };
|
|
541
|
+
}
|
|
542
|
+
let url;
|
|
543
|
+
try {
|
|
544
|
+
if (path.startsWith("//") || path.includes("\\") || path.includes("#") ||
|
|
545
|
+
/%(?:2e|2f|5c|00)/i.test(path.split("?", 1)[0]) ||
|
|
546
|
+
path.split("?", 1)[0].split("/").some((segment) => segment === "." || segment === "..")) {
|
|
547
|
+
throw new Error("ambiguous path");
|
|
548
|
+
}
|
|
549
|
+
if (path.startsWith("/"))
|
|
550
|
+
url = new URL(path, base);
|
|
551
|
+
else if (/^https?:\/\//i.test(path))
|
|
552
|
+
url = new URL(path);
|
|
553
|
+
else
|
|
554
|
+
throw new Error("unsupported request target");
|
|
555
|
+
if (url.username || url.password || url.origin !== base.origin)
|
|
556
|
+
throw new Error("mismatch");
|
|
557
|
+
}
|
|
558
|
+
catch {
|
|
559
|
+
return { error: "Burp request path or absolute URL cannot be verified against the approved target." };
|
|
560
|
+
}
|
|
561
|
+
return { url };
|
|
562
|
+
}
|
|
359
563
|
function hasUnstructuredCommand(value, depth = 0) {
|
|
360
564
|
if (depth > 8 || value === null || typeof value !== "object")
|
|
361
565
|
return false;
|