@node9/proxy 2.32.0 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +780 -452
- package/dist/cli.mjs +762 -434
- package/dist/dashboard.mjs +369 -29
- package/dist/index.js +583 -325
- package/dist/index.mjs +583 -325
- package/dist/scan-ink.mjs +202 -190
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -1662,32 +1662,102 @@ function evaluateSmartConditions(args, rule) {
|
|
|
1662
1662
|
function isBashTool(tool) {
|
|
1663
1663
|
return BASH_TOOL_NAMES.has(tool.toLowerCase());
|
|
1664
1664
|
}
|
|
1665
|
-
function
|
|
1665
|
+
function isShellEntry(value) {
|
|
1666
|
+
return typeof value === "object" && value !== null && value.kind === "shell" && typeof value.field === "string" && Object.keys(value).length === 2;
|
|
1667
|
+
}
|
|
1668
|
+
function isInspectionLayers(input) {
|
|
1669
|
+
const trusted = input.trusted;
|
|
1670
|
+
return typeof trusted === "object" && trusted !== null && !isShellEntry(trusted);
|
|
1671
|
+
}
|
|
1672
|
+
function layersOf(input) {
|
|
1673
|
+
return isInspectionLayers(input) ? { trusted: input.trusted, project: input.project ?? {} } : { trusted: input, project: {} };
|
|
1674
|
+
}
|
|
1675
|
+
function entryOf(tool, inspection) {
|
|
1666
1676
|
const pattern = Object.keys(inspection).find((p) => matchesPattern(tool, p));
|
|
1667
1677
|
return pattern === void 0 ? void 0 : inspection[pattern];
|
|
1668
1678
|
}
|
|
1679
|
+
function executorName(tool) {
|
|
1680
|
+
const name = tool.toLowerCase();
|
|
1681
|
+
if (!name.startsWith("mcp__")) return name;
|
|
1682
|
+
const at = name.lastIndexOf("__");
|
|
1683
|
+
return at >= "mcp__".length ? name.slice(at + 2) : name;
|
|
1684
|
+
}
|
|
1685
|
+
function entryFields(entry) {
|
|
1686
|
+
if (typeof entry === "object") return [entry.field];
|
|
1687
|
+
return entry === "command" ? ["command"] : null;
|
|
1688
|
+
}
|
|
1669
1689
|
function shellFields(tool, inspection) {
|
|
1670
1690
|
if (isBashTool(tool)) return ["command"];
|
|
1671
|
-
const
|
|
1672
|
-
|
|
1673
|
-
if (
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1691
|
+
const { trusted, project } = layersOf(inspection);
|
|
1692
|
+
const trustedEntry = entryOf(tool, trusted);
|
|
1693
|
+
if (trustedEntry !== void 0) return entryFields(trustedEntry);
|
|
1694
|
+
const builtin = MCP_EXECUTORS.has(executorName(tool)) ? BUILTIN_FIELDS : null;
|
|
1695
|
+
const projectEntry = entryOf(tool, project);
|
|
1696
|
+
if (projectEntry === void 0) return builtin;
|
|
1697
|
+
const added = entryFields(projectEntry);
|
|
1698
|
+
if (!builtin) return added;
|
|
1699
|
+
if (!added) return builtin;
|
|
1700
|
+
return [.../* @__PURE__ */ new Set([...builtin, ...added])];
|
|
1676
1701
|
}
|
|
1677
1702
|
function isShellShapedTool(tool, inspection = {}) {
|
|
1678
1703
|
return shellFields(tool, inspection) !== null;
|
|
1679
1704
|
}
|
|
1705
|
+
function shellWord(word) {
|
|
1706
|
+
if (BARE_WORD.test(word)) return word;
|
|
1707
|
+
return word === "" ? "''" : `'${word.replace(/'/g, `'\\''`)}'`;
|
|
1708
|
+
}
|
|
1709
|
+
function isStringArray(value) {
|
|
1710
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1711
|
+
}
|
|
1712
|
+
function commandLine(args, fieldPath, value, native) {
|
|
1713
|
+
const missing = { reason: "Shell command field is missing or is not a string" };
|
|
1714
|
+
const unreadable = { reason: "Shell command arguments could not be read" };
|
|
1715
|
+
if (typeof value !== "string" && (native || !Array.isArray(value))) return missing;
|
|
1716
|
+
const dot = fieldPath.lastIndexOf(".");
|
|
1717
|
+
const parent = dot === -1 ? args : getNestedValue(args, fieldPath.slice(0, dot));
|
|
1718
|
+
const siblings = ARGV_FIELDS.map((key) => ({
|
|
1719
|
+
key,
|
|
1720
|
+
value: parent && typeof parent === "object" ? parent[key] : void 0
|
|
1721
|
+
})).filter(({ value: value2 }) => value2 !== void 0 && value2 !== null);
|
|
1722
|
+
let argv = siblings[0];
|
|
1723
|
+
if (native) argv = siblings.find(({ value: v }) => isStringArray(v));
|
|
1724
|
+
else if (siblings.length === 2 && JSON.stringify(siblings[0].value) !== JSON.stringify(siblings[1].value))
|
|
1725
|
+
return { reason: "Shell command arguments disagree" };
|
|
1726
|
+
let command;
|
|
1727
|
+
let source = fieldPath;
|
|
1728
|
+
if (Array.isArray(value)) {
|
|
1729
|
+
if (argv || !value.length || !isStringArray(value)) return unreadable;
|
|
1730
|
+
command = value.map(shellWord).join(" ");
|
|
1731
|
+
} else if (!argv) {
|
|
1732
|
+
return { command: value, fieldPath };
|
|
1733
|
+
} else {
|
|
1734
|
+
if (!isStringArray(argv.value)) return unreadable;
|
|
1735
|
+
if (!argv.value.length) return { command: value, fieldPath };
|
|
1736
|
+
command = `${value} ${argv.value.map(shellWord).join(" ")}`;
|
|
1737
|
+
source = `${fieldPath}+${argv.key}`;
|
|
1738
|
+
}
|
|
1739
|
+
if (command.length > MAX_ARGV_CHARACTERS)
|
|
1740
|
+
return { reason: "Shell command arguments exceed the inspectable size" };
|
|
1741
|
+
return { command, fieldPath: source };
|
|
1742
|
+
}
|
|
1680
1743
|
function resolveShellInput(tool, args, inspection = {}) {
|
|
1681
1744
|
const fields = shellFields(tool, inspection);
|
|
1682
1745
|
if (!fields) return { kind: "not-shell" };
|
|
1683
1746
|
const values = fields.map((fieldPath) => ({ fieldPath, value: getNestedValue(args, fieldPath) })).filter(({ value }) => value !== void 0 && value !== null && value !== "");
|
|
1684
|
-
if (!values.length
|
|
1747
|
+
if (!values.length) {
|
|
1685
1748
|
return { kind: "uninspectable", reason: "Shell command field is missing or is not a string" };
|
|
1686
1749
|
}
|
|
1687
|
-
|
|
1750
|
+
const native = isBashTool(tool);
|
|
1751
|
+
const lines = values.map(({ fieldPath, value }) => commandLine(args, fieldPath, value, native));
|
|
1752
|
+
const failed = lines.find((line) => "reason" in line);
|
|
1753
|
+
if (failed) return { kind: "uninspectable", reason: failed.reason };
|
|
1754
|
+
const commands = lines.filter(
|
|
1755
|
+
(line) => "command" in line
|
|
1756
|
+
);
|
|
1757
|
+
if (new Set(commands.map(({ command }) => command)).size !== 1) {
|
|
1688
1758
|
return { kind: "uninspectable", reason: "Multiple shell command fields disagree" };
|
|
1689
1759
|
}
|
|
1690
|
-
return { kind: "shell",
|
|
1760
|
+
return { kind: "shell", ...commands[0] };
|
|
1691
1761
|
}
|
|
1692
1762
|
function shellRuleArgs(tool, args, ruleTool, inspection = {}) {
|
|
1693
1763
|
if (!ruleTool || !matchesPattern("bash", ruleTool) || matchesPattern(tool, ruleTool)) return args;
|
|
@@ -1697,6 +1767,22 @@ function shellRuleArgs(tool, args, ruleTool, inspection = {}) {
|
|
|
1697
1767
|
function opOf(sample) {
|
|
1698
1768
|
return parseShared(sample).Stmts[0].Redirs[0].Op;
|
|
1699
1769
|
}
|
|
1770
|
+
function validateIban(raw) {
|
|
1771
|
+
const s = raw.replace(/[ -]/g, "").toUpperCase();
|
|
1772
|
+
if (!/^[A-Z]{2}\d{2}/.test(s)) return false;
|
|
1773
|
+
const want = IBAN_LENGTH[s.slice(0, 2)];
|
|
1774
|
+
if (want === void 0 || s.length < want) return false;
|
|
1775
|
+
const iban = s.slice(0, want);
|
|
1776
|
+
if (!/^[A-Z0-9]+$/.test(iban)) return false;
|
|
1777
|
+
const rearranged = iban.slice(4) + iban.slice(0, 4);
|
|
1778
|
+
let mod = 0;
|
|
1779
|
+
for (const ch of rearranged) {
|
|
1780
|
+
const code = ch.charCodeAt(0);
|
|
1781
|
+
const digits = code >= 65 ? String(code - 55) : ch;
|
|
1782
|
+
for (const d of digits) mod = (mod * 10 + (d.charCodeAt(0) - 48)) % 97;
|
|
1783
|
+
}
|
|
1784
|
+
return mod === 1;
|
|
1785
|
+
}
|
|
1700
1786
|
function validateBase58Check(s) {
|
|
1701
1787
|
if (!s) return null;
|
|
1702
1788
|
let n = 0n;
|
|
@@ -2093,6 +2179,79 @@ function configurableValues(id) {
|
|
|
2093
2179
|
if (def.pack || MAP_GOVERNED.has(id)) return def.values;
|
|
2094
2180
|
return KNOB_GOVERNED[id] ?? [];
|
|
2095
2181
|
}
|
|
2182
|
+
function normalizeHost(raw) {
|
|
2183
|
+
let h = raw.trim().toLowerCase();
|
|
2184
|
+
if (h.startsWith("[") && h.endsWith("]")) h = h.slice(1, -1);
|
|
2185
|
+
if (!h || h === "localhost") return null;
|
|
2186
|
+
if (/^[0-9a-f:]+$/.test(h) && h.includes(":")) return h;
|
|
2187
|
+
if (!h.includes(".") || !/^[\p{L}\p{N}.-]+$/u.test(h)) return null;
|
|
2188
|
+
return h;
|
|
2189
|
+
}
|
|
2190
|
+
function hostOfUrl(value) {
|
|
2191
|
+
const v = value.replace(/[.,;:!?)\]}>'"`]+$/, "");
|
|
2192
|
+
try {
|
|
2193
|
+
const u = new URL(v.toLowerCase().startsWith("www.") ? `http://${v}` : v);
|
|
2194
|
+
return normalizeHost(u.hostname);
|
|
2195
|
+
} catch {
|
|
2196
|
+
return null;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
function addIbans(text, out) {
|
|
2200
|
+
for (const m of text.toUpperCase().matchAll(IBAN_RE)) {
|
|
2201
|
+
const compact = m[0].replace(/[ -]/g, "");
|
|
2202
|
+
if (validateIban(compact)) out.add(`iban:${compact}`);
|
|
2203
|
+
else if (/^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/.test(compact)) out.add(`account:${compact}`);
|
|
2204
|
+
if (out.size >= MAX_DESTINATIONS) return;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
function addEmails(text, out) {
|
|
2208
|
+
for (const m of text.matchAll(EMAIL_RE)) {
|
|
2209
|
+
out.add(`email:${m[0].toLowerCase()}`);
|
|
2210
|
+
if (out.size >= MAX_DESTINATIONS) return;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
function addUrlHosts(text, out) {
|
|
2214
|
+
for (const m of text.matchAll(URL_RE)) {
|
|
2215
|
+
const h = hostOfUrl(m[0]);
|
|
2216
|
+
if (h) out.add(`host:${h}`);
|
|
2217
|
+
if (out.size >= MAX_DESTINATIONS) return;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
function extractArgDestinations(args) {
|
|
2221
|
+
const out = /* @__PURE__ */ new Set();
|
|
2222
|
+
try {
|
|
2223
|
+
walk(args, void 0, out, 0);
|
|
2224
|
+
} catch {
|
|
2225
|
+
}
|
|
2226
|
+
return [...out];
|
|
2227
|
+
}
|
|
2228
|
+
function walk(value, key, out, depth) {
|
|
2229
|
+
if (out.size >= MAX_DESTINATIONS || depth > 12) return;
|
|
2230
|
+
if (typeof value === "string") {
|
|
2231
|
+
const s = value.length > MAX_SCAN_CHARS ? value.slice(0, MAX_SCAN_CHARS) : value;
|
|
2232
|
+
if (s.includes("@")) addEmails(s, out);
|
|
2233
|
+
addUrlHosts(s, out);
|
|
2234
|
+
addIbans(s, out);
|
|
2235
|
+
if (key && RECIPIENT_KEYS.has(key.toLowerCase()) && !/[\s@]/.test(s)) {
|
|
2236
|
+
const h = normalizeHost(
|
|
2237
|
+
s.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "").replace(/^\/\//, "").split(/[/?#]/)[0].replace(/:\d+$/, "")
|
|
2238
|
+
);
|
|
2239
|
+
if (h && (h.includes(":") || /\.\p{L}{2,}$/u.test(h))) out.add(`host:${h}`);
|
|
2240
|
+
const acct = s.replace(/[ .-]/g, "").toUpperCase();
|
|
2241
|
+
if (/^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/.test(acct) && !validateIban(acct))
|
|
2242
|
+
out.add(`account:${acct}`);
|
|
2243
|
+
}
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
if (Array.isArray(value)) {
|
|
2247
|
+
for (const v of value) walk(v, key, out, depth + 1);
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
if (value && typeof value === "object") {
|
|
2251
|
+
for (const [k, v] of Object.entries(value))
|
|
2252
|
+
walk(v, k, out, depth + 1);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2096
2255
|
function stripTerminalEscapes(s) {
|
|
2097
2256
|
return s.replace(TERMINAL_ESCAPE_RE, "");
|
|
2098
2257
|
}
|
|
@@ -2204,7 +2363,7 @@ function matchCanaryArgs(args, values) {
|
|
|
2204
2363
|
return null;
|
|
2205
2364
|
}
|
|
2206
2365
|
}
|
|
2207
|
-
var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, syntax2, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, LEGACY_SYSTEM_PATH_RE, BIP39_ENGLISH, indices, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax3, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, SOURCE_COMMANDS, syntax4, PUBLISH, OWN_HEADS, TERMINAL_ESCAPE_RE, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
2366
|
+
var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, LEGACY_SYSTEM_PATH_RE, BIP39_ENGLISH, indices, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax3, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, SOURCE_COMMANDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, syntax4, PUBLISH, OWN_HEADS, TERMINAL_ESCAPE_RE, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
2208
2367
|
var init_dist = __esm({
|
|
2209
2368
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2210
2369
|
"use strict";
|
|
@@ -4387,7 +4546,23 @@ var init_dist = __esm({
|
|
|
4387
4546
|
"shell",
|
|
4388
4547
|
"exec_command"
|
|
4389
4548
|
]);
|
|
4390
|
-
MCP_EXECUTORS = /* @__PURE__ */ new Set([
|
|
4549
|
+
MCP_EXECUTORS = /* @__PURE__ */ new Set([
|
|
4550
|
+
...BASH_TOOL_NAMES,
|
|
4551
|
+
"exec",
|
|
4552
|
+
"run_command",
|
|
4553
|
+
"terminal",
|
|
4554
|
+
"execute_command",
|
|
4555
|
+
"execute_shell",
|
|
4556
|
+
"execute_shell_command",
|
|
4557
|
+
"shell_execute",
|
|
4558
|
+
"run_shell",
|
|
4559
|
+
"run_terminal_command",
|
|
4560
|
+
"start_process"
|
|
4561
|
+
]);
|
|
4562
|
+
BUILTIN_FIELDS = ["command", "cmd", "script"];
|
|
4563
|
+
ARGV_FIELDS = ["args", "argv"];
|
|
4564
|
+
BARE_WORD = /^[A-Za-z0-9_./+:@%,-]+$/;
|
|
4565
|
+
MAX_ARGV_CHARACTERS = 262144;
|
|
4391
4566
|
({ syntax: syntax2 } = mvdanSh2);
|
|
4392
4567
|
WRITES = new Set([">x", ">>x", ">|x", "&>x", "&>>x", "<>x"].map(opOf));
|
|
4393
4568
|
READS = new Set(["<x", "<>x"].map(opOf));
|
|
@@ -6450,6 +6625,98 @@ zero
|
|
|
6450
6625
|
zone
|
|
6451
6626
|
zoo`.split("\n");
|
|
6452
6627
|
indices = new Map(BIP39_ENGLISH.map((word, i) => [word, i]));
|
|
6628
|
+
IBAN_LENGTH = {
|
|
6629
|
+
AD: 24,
|
|
6630
|
+
AE: 23,
|
|
6631
|
+
AL: 28,
|
|
6632
|
+
AT: 20,
|
|
6633
|
+
AZ: 28,
|
|
6634
|
+
BA: 20,
|
|
6635
|
+
BE: 16,
|
|
6636
|
+
BG: 22,
|
|
6637
|
+
BH: 22,
|
|
6638
|
+
BI: 27,
|
|
6639
|
+
BR: 29,
|
|
6640
|
+
BY: 28,
|
|
6641
|
+
CH: 21,
|
|
6642
|
+
CR: 22,
|
|
6643
|
+
CY: 28,
|
|
6644
|
+
CZ: 24,
|
|
6645
|
+
DE: 22,
|
|
6646
|
+
DJ: 27,
|
|
6647
|
+
DK: 18,
|
|
6648
|
+
DO: 28,
|
|
6649
|
+
EE: 20,
|
|
6650
|
+
EG: 29,
|
|
6651
|
+
ES: 24,
|
|
6652
|
+
FI: 18,
|
|
6653
|
+
FK: 18,
|
|
6654
|
+
FO: 18,
|
|
6655
|
+
FR: 27,
|
|
6656
|
+
GB: 22,
|
|
6657
|
+
GE: 22,
|
|
6658
|
+
GI: 23,
|
|
6659
|
+
GL: 18,
|
|
6660
|
+
GR: 27,
|
|
6661
|
+
GT: 28,
|
|
6662
|
+
HN: 28,
|
|
6663
|
+
HR: 21,
|
|
6664
|
+
HU: 28,
|
|
6665
|
+
IE: 22,
|
|
6666
|
+
IL: 23,
|
|
6667
|
+
IQ: 23,
|
|
6668
|
+
IS: 26,
|
|
6669
|
+
IT: 27,
|
|
6670
|
+
JO: 30,
|
|
6671
|
+
KW: 30,
|
|
6672
|
+
KZ: 20,
|
|
6673
|
+
LB: 28,
|
|
6674
|
+
LC: 32,
|
|
6675
|
+
LI: 21,
|
|
6676
|
+
LT: 20,
|
|
6677
|
+
LU: 20,
|
|
6678
|
+
LV: 21,
|
|
6679
|
+
LY: 25,
|
|
6680
|
+
MC: 27,
|
|
6681
|
+
MD: 24,
|
|
6682
|
+
ME: 22,
|
|
6683
|
+
MK: 19,
|
|
6684
|
+
MN: 20,
|
|
6685
|
+
MR: 27,
|
|
6686
|
+
MT: 31,
|
|
6687
|
+
MU: 30,
|
|
6688
|
+
NI: 28,
|
|
6689
|
+
NL: 18,
|
|
6690
|
+
NO: 15,
|
|
6691
|
+
OM: 23,
|
|
6692
|
+
PK: 24,
|
|
6693
|
+
PL: 28,
|
|
6694
|
+
PS: 29,
|
|
6695
|
+
PT: 25,
|
|
6696
|
+
QA: 29,
|
|
6697
|
+
RO: 24,
|
|
6698
|
+
RS: 22,
|
|
6699
|
+
RU: 33,
|
|
6700
|
+
SA: 24,
|
|
6701
|
+
SC: 31,
|
|
6702
|
+
SD: 18,
|
|
6703
|
+
SE: 24,
|
|
6704
|
+
SI: 19,
|
|
6705
|
+
SK: 24,
|
|
6706
|
+
SM: 27,
|
|
6707
|
+
SN: 28,
|
|
6708
|
+
SO: 23,
|
|
6709
|
+
ST: 25,
|
|
6710
|
+
SV: 28,
|
|
6711
|
+
TL: 23,
|
|
6712
|
+
TN: 24,
|
|
6713
|
+
TR: 26,
|
|
6714
|
+
UA: 29,
|
|
6715
|
+
VA: 22,
|
|
6716
|
+
VG: 24,
|
|
6717
|
+
XK: 20,
|
|
6718
|
+
YE: 30
|
|
6719
|
+
};
|
|
6453
6720
|
B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
6454
6721
|
B58_INDEX = Object.fromEntries(
|
|
6455
6722
|
[...B58].map((c, i) => [c, i])
|
|
@@ -6464,6 +6731,8 @@ zoo`.split("\n");
|
|
|
6464
6731
|
);
|
|
6465
6732
|
CASK_TWO_ZERO_SUFFIX = /* @__PURE__ */ new Set([..."AEIMQUYcgkosw048"]);
|
|
6466
6733
|
CASK_FOUR_ZERO_SUFFIX = /* @__PURE__ */ new Set([..."AQgw"]);
|
|
6734
|
+
INVISIBLE_RE = /[͏----︀-️]|[\u{E0000}-\u{E007F}]/gu;
|
|
6735
|
+
INVISIBLE_ONE = new RegExp(`^(?:${INVISIBLE_RE.source.split("|")[0]})$`, "u");
|
|
6467
6736
|
HOMOGLYPHS = {
|
|
6468
6737
|
// Cyrillic lowercase
|
|
6469
6738
|
\u0430: "a",
|
|
@@ -7212,6 +7481,38 @@ zoo`.split("\n");
|
|
|
7212
7481
|
(c) => configurableValues(c.id).length > 0
|
|
7213
7482
|
).map((c) => c.id);
|
|
7214
7483
|
SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
|
|
7484
|
+
MAX_DESTINATIONS = 200;
|
|
7485
|
+
MAX_SCAN_CHARS = 1e5;
|
|
7486
|
+
EMAIL_RE = /[\p{L}\p{N}._%+-]+@[\p{L}\p{N}](?:[\p{L}\p{N}-]*[\p{L}\p{N}])?(?:\.[\p{L}\p{N}](?:[\p{L}\p{N}-]*[\p{L}\p{N}])?)*\.\p{L}{2,}/gu;
|
|
7487
|
+
URL_RE = /\b(?:[a-z][a-z0-9+.-]*:\/\/|www\.)[^\s<>"'`){}]+/gi;
|
|
7488
|
+
IBAN_RE = /\b[A-Z]{2}\d{2}(?:[ -]?[A-Z0-9]{4}){2,7}(?:[ -]?[A-Z0-9]{1,4})?\b/g;
|
|
7489
|
+
RECIPIENT_KEYS = /* @__PURE__ */ new Set([
|
|
7490
|
+
"to",
|
|
7491
|
+
"cc",
|
|
7492
|
+
"bcc",
|
|
7493
|
+
"recipient",
|
|
7494
|
+
"recipients",
|
|
7495
|
+
"email",
|
|
7496
|
+
"participants",
|
|
7497
|
+
"attendees",
|
|
7498
|
+
"destination",
|
|
7499
|
+
"iban",
|
|
7500
|
+
"account",
|
|
7501
|
+
// Where a request goes, when the value is a bare host or a scheme-less URL.
|
|
7502
|
+
"url",
|
|
7503
|
+
"uri",
|
|
7504
|
+
"host",
|
|
7505
|
+
"hostname",
|
|
7506
|
+
"endpoint",
|
|
7507
|
+
"server",
|
|
7508
|
+
"domain",
|
|
7509
|
+
"address",
|
|
7510
|
+
"webhook",
|
|
7511
|
+
"webhook_url",
|
|
7512
|
+
"callback",
|
|
7513
|
+
"callback_url",
|
|
7514
|
+
"target"
|
|
7515
|
+
]);
|
|
7215
7516
|
({ syntax: syntax4 } = mvdanSh4);
|
|
7216
7517
|
PUBLISH = {
|
|
7217
7518
|
npm: { verbs: /* @__PURE__ */ new Set(["publish"]), registry: "registry.npmjs.org", flag: ["--registry"] },
|
|
@@ -7560,18 +7861,18 @@ function statAndFirstLine(file) {
|
|
|
7560
7861
|
}
|
|
7561
7862
|
function listCodexSessionFiles(base = codexSessionsDir()) {
|
|
7562
7863
|
const files = [];
|
|
7563
|
-
const
|
|
7864
|
+
const walk2 = (dir) => {
|
|
7564
7865
|
try {
|
|
7565
7866
|
for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
|
|
7566
7867
|
const file = path3.join(dir, entry.name);
|
|
7567
|
-
if (entry.isDirectory())
|
|
7868
|
+
if (entry.isDirectory()) walk2(file);
|
|
7568
7869
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file);
|
|
7569
7870
|
}
|
|
7570
7871
|
} catch {
|
|
7571
7872
|
}
|
|
7572
7873
|
};
|
|
7573
|
-
|
|
7574
|
-
if (path3.basename(base) === "sessions")
|
|
7874
|
+
walk2(base);
|
|
7875
|
+
if (path3.basename(base) === "sessions") walk2(path3.join(path3.dirname(base), "archived_sessions"));
|
|
7575
7876
|
const sessions = /* @__PURE__ */ new Map();
|
|
7576
7877
|
for (const file of files.sort()) {
|
|
7577
7878
|
try {
|
|
@@ -7894,6 +8195,26 @@ var init_daemon = __esm({
|
|
|
7894
8195
|
}
|
|
7895
8196
|
});
|
|
7896
8197
|
|
|
8198
|
+
// src/auth/trusted-recipients.ts
|
|
8199
|
+
function normalizeRecipient(raw) {
|
|
8200
|
+
const v = raw.trim();
|
|
8201
|
+
if (!v) return null;
|
|
8202
|
+
if (/^(email|host|iban):/i.test(v)) {
|
|
8203
|
+
const i = v.indexOf(":");
|
|
8204
|
+
const kind = v.slice(0, i).toLowerCase();
|
|
8205
|
+
const inner = normalizeRecipient(v.slice(i + 1));
|
|
8206
|
+
return inner && inner.startsWith(`${kind}:`) ? inner : null;
|
|
8207
|
+
}
|
|
8208
|
+
const found = extractArgDestinations({ to: v });
|
|
8209
|
+
return found.length === 1 ? found[0] : null;
|
|
8210
|
+
}
|
|
8211
|
+
var init_trusted_recipients = __esm({
|
|
8212
|
+
"src/auth/trusted-recipients.ts"() {
|
|
8213
|
+
"use strict";
|
|
8214
|
+
init_dist();
|
|
8215
|
+
}
|
|
8216
|
+
});
|
|
8217
|
+
|
|
7897
8218
|
// src/config-schema.ts
|
|
7898
8219
|
import { z } from "zod";
|
|
7899
8220
|
function formatIssues(issues) {
|
|
@@ -8447,7 +8768,7 @@ var init_build = __esm({
|
|
|
8447
8768
|
});
|
|
8448
8769
|
|
|
8449
8770
|
// src/auth/trusted-hosts.ts
|
|
8450
|
-
function
|
|
8771
|
+
function normalizeHost2(raw) {
|
|
8451
8772
|
return raw.toLowerCase().replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^[^@]+@/, "").replace(/:\d+$/, "");
|
|
8452
8773
|
}
|
|
8453
8774
|
var init_trusted_hosts = __esm({
|
|
@@ -8768,6 +9089,7 @@ function getConfig(cwd) {
|
|
|
8768
9089
|
dangerousWords: [...DEFAULT_CONFIG.policy.dangerousWords],
|
|
8769
9090
|
ignoredTools: [...DEFAULT_CONFIG.policy.ignoredTools],
|
|
8770
9091
|
toolInspection: { ...DEFAULT_CONFIG.policy.toolInspection },
|
|
9092
|
+
projectToolInspection: {},
|
|
8771
9093
|
// Shipped rules carry `builtin`: the catalog's per-check value governs
|
|
8772
9094
|
// them and only them (a user rule that reuses a name keeps its verdict).
|
|
8773
9095
|
smartRules: DEFAULT_CONFIG.policy.smartRules.map((r) => ({ ...r, builtin: true })),
|
|
@@ -8794,7 +9116,8 @@ function getConfig(cwd) {
|
|
|
8794
9116
|
untrustedContent: {
|
|
8795
9117
|
...DEFAULT_CONFIG.policy.untrustedContent,
|
|
8796
9118
|
orgDomains: [...DEFAULT_CONFIG.policy.untrustedContent.orgDomains],
|
|
8797
|
-
roles: {}
|
|
9119
|
+
roles: {},
|
|
9120
|
+
trustedRecipients: []
|
|
8798
9121
|
},
|
|
8799
9122
|
// Left empty on purpose: the local file is read fresh at policy-eval time
|
|
8800
9123
|
// (getCachedHosts via isTrustedHost), NOT snapshotted into the frozen config
|
|
@@ -8840,8 +9163,14 @@ function getConfig(cwd) {
|
|
|
8840
9163
|
if (p.sandboxPaths) mergedPolicy.sandboxPaths.push(...p.sandboxPaths);
|
|
8841
9164
|
if (p.ignoredTools) mergedPolicy.ignoredTools.push(...p.ignoredTools);
|
|
8842
9165
|
if (p.dangerousWords) mergedPolicy.dangerousWords = [...p.dangerousWords];
|
|
8843
|
-
if (p.toolInspection)
|
|
8844
|
-
|
|
9166
|
+
if (p.toolInspection) {
|
|
9167
|
+
if (isProject)
|
|
9168
|
+
mergedPolicy.projectToolInspection = {
|
|
9169
|
+
...mergedPolicy.projectToolInspection,
|
|
9170
|
+
...p.toolInspection
|
|
9171
|
+
};
|
|
9172
|
+
else mergedPolicy.toolInspection = { ...mergedPolicy.toolInspection, ...p.toolInspection };
|
|
9173
|
+
}
|
|
8845
9174
|
if (p.smartRules) {
|
|
8846
9175
|
const defaultBlocks = mergedPolicy.smartRules.filter((r) => r.verdict === "block");
|
|
8847
9176
|
const defaultNonBlocks = mergedPolicy.smartRules.filter((r) => r.verdict !== "block");
|
|
@@ -9191,7 +9520,7 @@ function getConfig(cwd) {
|
|
|
9191
9520
|
}
|
|
9192
9521
|
if (Array.isArray(mc.trustedHosts)) {
|
|
9193
9522
|
mergedPolicy.trustedHostsManaged = true;
|
|
9194
|
-
mergedPolicy.trustedHosts = mc.trustedHosts.filter((h) => typeof h === "string").map((h) =>
|
|
9523
|
+
mergedPolicy.trustedHosts = mc.trustedHosts.filter((h) => typeof h === "string").map((h) => normalizeHost2(h));
|
|
9195
9524
|
}
|
|
9196
9525
|
if (mc.appPermissions && typeof mc.appPermissions === "object" && !Array.isArray(mc.appPermissions)) {
|
|
9197
9526
|
const coerced = {};
|
|
@@ -9460,6 +9789,13 @@ function applyUntrustedContent(cur, raw, replaceLists = false, isProject = false
|
|
|
9460
9789
|
if (replaceLists) cur.orgDomains = domains;
|
|
9461
9790
|
else for (const d of domains) if (!cur.orgDomains.includes(d)) cur.orgDomains.push(d);
|
|
9462
9791
|
}
|
|
9792
|
+
if (replaceLists && Array.isArray(uc.trustedRecipients)) {
|
|
9793
|
+
cur.trustedRecipients = [
|
|
9794
|
+
...new Set(
|
|
9795
|
+
uc.trustedRecipients.filter((v) => typeof v === "string").map((v) => normalizeRecipient(v)).filter((v) => v !== null)
|
|
9796
|
+
)
|
|
9797
|
+
].slice(0, 200);
|
|
9798
|
+
}
|
|
9463
9799
|
if (uc.roles && typeof uc.roles === "object" && !Array.isArray(uc.roles)) {
|
|
9464
9800
|
const next = replaceLists ? {} : cur.roles;
|
|
9465
9801
|
for (const [server, tools] of Object.entries(uc.roles)) {
|
|
@@ -9480,6 +9816,7 @@ var DANGEROUS_WORDS, RUNTIME_ONLY_CONFIG_KEYS, DEFAULT_CONFIG, RM_SAFE_PATH_PATT
|
|
|
9480
9816
|
var init_config = __esm({
|
|
9481
9817
|
"src/config/index.ts"() {
|
|
9482
9818
|
"use strict";
|
|
9819
|
+
init_trusted_recipients();
|
|
9483
9820
|
init_config_schema();
|
|
9484
9821
|
init_shields();
|
|
9485
9822
|
init_managed();
|
|
@@ -9734,7 +10071,8 @@ var init_config = __esm({
|
|
|
9734
10071
|
spotlight: true,
|
|
9735
10072
|
afterExposure: "ask-once",
|
|
9736
10073
|
orgDomains: [],
|
|
9737
|
-
roles: {}
|
|
10074
|
+
roles: {},
|
|
10075
|
+
trustedRecipients: []
|
|
9738
10076
|
},
|
|
9739
10077
|
trustedHosts: [],
|
|
9740
10078
|
trustedHostsManaged: false,
|
|
@@ -9852,7 +10190,7 @@ import * as fs7 from "fs";
|
|
|
9852
10190
|
import * as path8 from "path";
|
|
9853
10191
|
function listSessionFiles(dir, maxDepth = 6) {
|
|
9854
10192
|
const out = [];
|
|
9855
|
-
const
|
|
10193
|
+
const walk2 = (d, rel, depth) => {
|
|
9856
10194
|
if (depth > maxDepth) return;
|
|
9857
10195
|
let entries;
|
|
9858
10196
|
try {
|
|
@@ -9862,11 +10200,11 @@ function listSessionFiles(dir, maxDepth = 6) {
|
|
|
9862
10200
|
}
|
|
9863
10201
|
for (const e of entries) {
|
|
9864
10202
|
const childRel = rel ? path8.join(rel, e.name) : e.name;
|
|
9865
|
-
if (e.isDirectory())
|
|
10203
|
+
if (e.isDirectory()) walk2(path8.join(d, e.name), childRel, depth + 1);
|
|
9866
10204
|
else if (e.name.endsWith(".jsonl")) out.push(childRel);
|
|
9867
10205
|
}
|
|
9868
10206
|
};
|
|
9869
|
-
|
|
10207
|
+
walk2(dir, "", 0);
|
|
9870
10208
|
return out;
|
|
9871
10209
|
}
|
|
9872
10210
|
function sessionIdOf(relPath) {
|
|
@@ -10464,7 +10802,9 @@ function dimensionOfBlock(checkedBy, ruleName) {
|
|
|
10464
10802
|
if (checkedBy.includes("egress")) return "network";
|
|
10465
10803
|
if (ruleName.startsWith("ssrf:")) return "network";
|
|
10466
10804
|
if (checkedBy.includes("pii") || checkedBy.includes("dlp")) return "data";
|
|
10467
|
-
if (checkedBy === "loop-detected" || checkedBy === "mcp-pin-mismatch" || checkedBy.startsWith("injection")
|
|
10805
|
+
if (checkedBy === "loop-detected" || checkedBy === "mcp-pin-mismatch" || checkedBy.startsWith("injection") || // fw#318: a send held after the session read outside content, and the
|
|
10806
|
+
// read rows themselves. Mirrors node9Firewall canon-taxonomy.ts.
|
|
10807
|
+
ruleName.startsWith("exposure") || checkedBy.startsWith("exposure"))
|
|
10468
10808
|
return "detection";
|
|
10469
10809
|
if (checkedBy === "app-permission-block" || ruleName.startsWith("app-permission:")) return "apps";
|
|
10470
10810
|
if (ruleName.startsWith("shield:project-jail")) return "files";
|
|
@@ -11143,19 +11483,19 @@ function scrubDecoys(subject, values) {
|
|
|
11143
11483
|
for (const v of values) if (v) out = out.split(v).join("[node9-decoy]");
|
|
11144
11484
|
return out;
|
|
11145
11485
|
};
|
|
11146
|
-
const
|
|
11486
|
+
const walk2 = (v, depth) => {
|
|
11147
11487
|
if (depth > 6) return v;
|
|
11148
11488
|
if (typeof v === "string") return scrubText(v);
|
|
11149
|
-
if (Array.isArray(v)) return v.map((x) =>
|
|
11489
|
+
if (Array.isArray(v)) return v.map((x) => walk2(x, depth + 1));
|
|
11150
11490
|
if (v && typeof v === "object") {
|
|
11151
11491
|
const out = {};
|
|
11152
11492
|
for (const [k, child] of Object.entries(v))
|
|
11153
|
-
out[k] =
|
|
11493
|
+
out[k] = walk2(child, depth + 1);
|
|
11154
11494
|
return out;
|
|
11155
11495
|
}
|
|
11156
11496
|
return v;
|
|
11157
11497
|
};
|
|
11158
|
-
return
|
|
11498
|
+
return walk2(subject, 0);
|
|
11159
11499
|
}
|
|
11160
11500
|
function pushFsOpAstFinding(command, toolName, input, timestamp2, projLabel, sessionId, agent, result, dedup) {
|
|
11161
11501
|
const fsVerdict = analyzeFsOperation(command);
|