@node9/proxy 2.33.2 → 2.34.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 +987 -99
- package/dist/cli.mjs +987 -99
- package/dist/dashboard.mjs +230 -13
- package/dist/index.js +956 -83
- package/dist/index.mjs +956 -83
- package/dist/scan-ink.mjs +134 -11
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -968,6 +968,95 @@ function expandPlainParam(p) {
|
|
|
968
968
|
if (recorded !== void 0) return recorded;
|
|
969
969
|
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
970
970
|
}
|
|
971
|
+
function functionBodies(f) {
|
|
972
|
+
const out = /* @__PURE__ */ new Map();
|
|
973
|
+
syntax.Walk(f, (node) => {
|
|
974
|
+
const n = node;
|
|
975
|
+
if (n && syntax.NodeType(n) === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
976
|
+
const list = out.get(n.Name.Value) ?? [];
|
|
977
|
+
list.push(n.Body);
|
|
978
|
+
out.set(n.Name.Value, list);
|
|
979
|
+
}
|
|
980
|
+
return true;
|
|
981
|
+
});
|
|
982
|
+
return out;
|
|
983
|
+
}
|
|
984
|
+
function rebindsPositionals(body) {
|
|
985
|
+
let found = false;
|
|
986
|
+
syntax.Walk(body, (node) => {
|
|
987
|
+
const n = node;
|
|
988
|
+
if (found || !n) return false;
|
|
989
|
+
if (syntax.NodeType(n) === "CallExpr") {
|
|
990
|
+
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
991
|
+
if (words[0] === "shift") found = true;
|
|
992
|
+
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
993
|
+
found = true;
|
|
994
|
+
}
|
|
995
|
+
return !found;
|
|
996
|
+
});
|
|
997
|
+
return found;
|
|
998
|
+
}
|
|
999
|
+
function tableBindings() {
|
|
1000
|
+
if (!assignmentTable) return "";
|
|
1001
|
+
const out = [];
|
|
1002
|
+
for (const [name, rec] of assignmentTable)
|
|
1003
|
+
if (rec.at < currentStmtOffset) out.push(`${name}=${rec.value ?? "\0"}`);
|
|
1004
|
+
return out.sort().join("");
|
|
1005
|
+
}
|
|
1006
|
+
function walkWithCalls(f, visit) {
|
|
1007
|
+
const bodies = functionBodies(f);
|
|
1008
|
+
if (bodies.size === 0) {
|
|
1009
|
+
syntax.Walk(f, visit);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
1013
|
+
const inline = (call) => {
|
|
1014
|
+
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1015
|
+
const list = words[0] ? bodies.get(words[0]) : void 0;
|
|
1016
|
+
if (!list || callDepth >= MAX_CALL_DEPTH) return;
|
|
1017
|
+
const args = words.slice(1);
|
|
1018
|
+
const key = `${words[0]}\0${JSON.stringify(args)}\0${tableBindings()}`;
|
|
1019
|
+
if (seenCalls.has(key)) return;
|
|
1020
|
+
seenCalls.add(key);
|
|
1021
|
+
const outerTable = assignmentTable;
|
|
1022
|
+
const outerOffset = currentStmtOffset;
|
|
1023
|
+
for (const body of list) {
|
|
1024
|
+
if (callWalks >= MAX_CALL_WALKS) return;
|
|
1025
|
+
callWalks++;
|
|
1026
|
+
const table = /* @__PURE__ */ new Map();
|
|
1027
|
+
for (const [name, rec] of outerTable ?? [])
|
|
1028
|
+
if (rec.at < outerOffset) table.set(name, { value: rec.value, at: -1 });
|
|
1029
|
+
for (const name of ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"])
|
|
1030
|
+
table.delete(name);
|
|
1031
|
+
if (!rebindsPositionals(body)) {
|
|
1032
|
+
args.slice(0, 9).forEach((value, i) => table.set(String(i + 1), { value, at: -1 }));
|
|
1033
|
+
if (args.length === 1) {
|
|
1034
|
+
table.set("@", { value: args[0], at: -1 });
|
|
1035
|
+
table.set("*", { value: args[0], at: -1 });
|
|
1036
|
+
}
|
|
1037
|
+
table.set("#", { value: String(args.length), at: -1 });
|
|
1038
|
+
}
|
|
1039
|
+
assignmentTable = table;
|
|
1040
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1041
|
+
callDepth++;
|
|
1042
|
+
try {
|
|
1043
|
+
if (body.Cmd && syntax.NodeType(body.Cmd) === "Block") recordTopLevelAssignments(body.Cmd);
|
|
1044
|
+
syntax.Walk(body, step);
|
|
1045
|
+
} finally {
|
|
1046
|
+
callDepth--;
|
|
1047
|
+
assignmentTable = outerTable;
|
|
1048
|
+
currentStmtOffset = outerOffset;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
const step = (node) => {
|
|
1053
|
+
if (!node) return false;
|
|
1054
|
+
const go = visit(node);
|
|
1055
|
+
if (go !== false && syntax.NodeType(node) === "CallExpr") inline(node);
|
|
1056
|
+
return go;
|
|
1057
|
+
};
|
|
1058
|
+
syntax.Walk(f, step);
|
|
1059
|
+
}
|
|
971
1060
|
function resolveWordLiteral(w) {
|
|
972
1061
|
const parts = w?.Parts || [];
|
|
973
1062
|
let s = "";
|
|
@@ -1042,6 +1131,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1042
1131
|
if (depth === 0) {
|
|
1043
1132
|
seenPayloads = /* @__PURE__ */ new Set();
|
|
1044
1133
|
payloadBudget = PAYLOAD_BUDGET;
|
|
1134
|
+
if (callDepth === 0) callWalks = 0;
|
|
1045
1135
|
}
|
|
1046
1136
|
assignmentTable = new Map(
|
|
1047
1137
|
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
@@ -1049,7 +1139,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1049
1139
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1050
1140
|
try {
|
|
1051
1141
|
recordTopLevelAssignments(f);
|
|
1052
|
-
|
|
1142
|
+
walkWithCalls(f, (node) => {
|
|
1053
1143
|
if (!node || result?.verdict === "block") return false;
|
|
1054
1144
|
const n = node;
|
|
1055
1145
|
const nodeType = syntax.NodeType(n);
|
|
@@ -1924,16 +2014,21 @@ function looksGenerated(value, minLength, minEntropy, lettersOnlyAllowed = false
|
|
|
1924
2014
|
if (EMAIL_VALUE_RE.test(value)) return false;
|
|
1925
2015
|
if (looksLikeJoinedWords(value) || hasSequentialRun(value)) return false;
|
|
1926
2016
|
if (PATH_VALUE_RE.test(value) || DOTTED_REFERENCE_RE.test(value)) return false;
|
|
2017
|
+
if (CONCATENATION_RE.test(value)) return false;
|
|
1927
2018
|
if (rejectedByStructuredValidator(value)) return false;
|
|
1928
2019
|
return shannonEntropy(value) >= minEntropy;
|
|
1929
2020
|
}
|
|
1930
2021
|
function nameHoldsCredential(name) {
|
|
1931
2022
|
return !FIXTURE_NAME_RE.test(name) && !DERIVATIVE_NAME_RE.test(name);
|
|
1932
2023
|
}
|
|
2024
|
+
function isOtpDocSecret(value) {
|
|
2025
|
+
return OTP_DOC_SECRETS.has(value.replace(/=+$/, "").toUpperCase());
|
|
2026
|
+
}
|
|
1933
2027
|
function validateQueryCredential(raw) {
|
|
1934
2028
|
const value = capturedValue(raw, QUERY_CREDENTIAL_VALUE_RE);
|
|
1935
2029
|
if (value === null) return false;
|
|
1936
|
-
if (OTP_SECRET_RE.test(value))
|
|
2030
|
+
if (OTP_SECRET_RE.test(value))
|
|
2031
|
+
return !isOtpDocSecret(value) && looksGenerated(value, 16, 3, true);
|
|
1937
2032
|
if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value)) return false;
|
|
1938
2033
|
return looksGenerated(value, 12, 3);
|
|
1939
2034
|
}
|
|
@@ -1947,13 +2042,40 @@ function keywordEndsAPart(name) {
|
|
|
1947
2042
|
}
|
|
1948
2043
|
function validateAssignedSecret(raw, before = "") {
|
|
1949
2044
|
const value = capturedValue(raw, ASSIGNED_SECRET_VALUE_RE);
|
|
1950
|
-
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE);
|
|
2045
|
+
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE)?.replace(/^_+/, "") ?? null;
|
|
1951
2046
|
if (value === null || name === null) return false;
|
|
1952
|
-
if (!keywordEndsAPart(name)
|
|
1953
|
-
|
|
2047
|
+
if (!keywordEndsAPart(name)) return false;
|
|
2048
|
+
if (ARN_CONTEXT_RE.test(before)) return false;
|
|
2049
|
+
const prefix = (before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "") + (raw.startsWith("_") ? "_" : "");
|
|
1954
2050
|
if (!nameHoldsCredential(prefix + name)) return false;
|
|
1955
2051
|
return looksGenerated(value, 12, 3.5);
|
|
1956
2052
|
}
|
|
2053
|
+
function validateBasicCredential(raw) {
|
|
2054
|
+
const b64 = capturedValue(raw, BASIC_VALUE_RE);
|
|
2055
|
+
if (b64 === null) return false;
|
|
2056
|
+
const bare = b64.replace(/=+$/, "");
|
|
2057
|
+
if (bare.length % 4 === 1) return false;
|
|
2058
|
+
const bytes = Buffer.from(b64, "base64");
|
|
2059
|
+
if (bytes.toString("base64").replace(/=+$/, "") !== bare) return false;
|
|
2060
|
+
const text = bytes.toString("utf8");
|
|
2061
|
+
if (!Buffer.from(text, "utf8").equals(bytes)) return false;
|
|
2062
|
+
for (let i = 0; i < text.length; i++) {
|
|
2063
|
+
const c = text.charCodeAt(i);
|
|
2064
|
+
if (c < 32 || c === 127) return false;
|
|
2065
|
+
}
|
|
2066
|
+
const pair = BASIC_PAIR_RE.exec(text);
|
|
2067
|
+
if (!pair) return false;
|
|
2068
|
+
const password = pair[2];
|
|
2069
|
+
if (!/[0-9]/.test(password) || !/[A-Za-z]/.test(password)) return false;
|
|
2070
|
+
const lower2 = password.toLowerCase();
|
|
2071
|
+
if (DLP_STOPWORDS.some((sw) => lower2.includes(sw)) || hasSequentialRun(password)) return false;
|
|
2072
|
+
return shannonEntropy(password) >= 3;
|
|
2073
|
+
}
|
|
2074
|
+
function validateOtpSecret(raw) {
|
|
2075
|
+
const secret = capturedValue(raw, OTP_CAPTURE_RE);
|
|
2076
|
+
if (secret === null || isOtpDocSecret(secret)) return false;
|
|
2077
|
+
return looksGenerated(secret.replace(/=+$/, ""), 16, 3, true);
|
|
2078
|
+
}
|
|
1957
2079
|
function assertBuiltinPatternsAreSafe() {
|
|
1958
2080
|
for (const p of DLP_PATTERNS) {
|
|
1959
2081
|
if (!safeRegex3(p.regex.source)) {
|
|
@@ -2429,7 +2551,7 @@ function matchCanaryArgs(args, values) {
|
|
|
2429
2551
|
return null;
|
|
2430
2552
|
}
|
|
2431
2553
|
}
|
|
2432
|
-
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, MASKED_KINDS, ENTRY_RE, 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;
|
|
2554
|
+
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, MAX_CALL_DEPTH, MAX_CALL_WALKS, callDepth, callWalks, 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, SYSTEM_DIRS, UPLOAD_SYSTEM_DIRS, 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, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, OTP_CAPTURE_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax3, EDITOR_HEADS, MANAGER_HEADS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, SOURCE_COMMANDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, MASKED_KINDS, ENTRY_RE, 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;
|
|
2433
2555
|
var init_dist = __esm({
|
|
2434
2556
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2435
2557
|
"use strict";
|
|
@@ -3221,6 +3343,11 @@ var init_dist = __esm({
|
|
|
3221
3343
|
example: "cat /etc/shadow",
|
|
3222
3344
|
advice: "Keep Review, or choose Block when agents must never read system password hashes."
|
|
3223
3345
|
},
|
|
3346
|
+
"files.system-dirs": {
|
|
3347
|
+
plain: "The agent writes, creates or deletes a file in a system directory, or sends a system file in a request. /usr/local and a container working directory under /usr/src are ordinary project space and are not included.",
|
|
3348
|
+
example: "wget -O /usr/bin/tool https://example.com/tool",
|
|
3349
|
+
advice: "Keep Review on a workstation. A container image build that installs into /usr can set it to Log."
|
|
3350
|
+
},
|
|
3224
3351
|
// ── Commands ──────────────────────────────────────────────────────────────
|
|
3225
3352
|
"commands.inline-exec": {
|
|
3226
3353
|
plain: "The agent runs a small program written inside the command itself.",
|
|
@@ -3417,7 +3544,7 @@ var init_dist = __esm({
|
|
|
3417
3544
|
advice: "Fixed at Block. A person accepts the change on the machine (node9 mcp pin update)."
|
|
3418
3545
|
},
|
|
3419
3546
|
"loading.malicious-package": {
|
|
3420
|
-
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago.",
|
|
3547
|
+
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago. Covers npm, PyPI, RubyGems, crates.io, Go, NuGet, Maven, Packagist and VS Code extensions; the publish-age and install-script signals are npm and PyPI only.",
|
|
3421
3548
|
example: "npm install of a package flagged as malware",
|
|
3422
3549
|
advice: "Keep Block for known-malicious packages; brand-new packages already stop for Review."
|
|
3423
3550
|
}
|
|
@@ -3455,6 +3582,15 @@ var init_dist = __esm({
|
|
|
3455
3582
|
defaultValue: "review",
|
|
3456
3583
|
legacy: ["review-password-hash-read"]
|
|
3457
3584
|
},
|
|
3585
|
+
{
|
|
3586
|
+
id: "files.system-dirs",
|
|
3587
|
+
group: "files",
|
|
3588
|
+
title: "System directories",
|
|
3589
|
+
catches: "Writes, new files and removals under /usr, /etc, /boot, /bin, /sbin, /lib and /var/log, and uploads of system files",
|
|
3590
|
+
values: ALL,
|
|
3591
|
+
defaultValue: "review",
|
|
3592
|
+
legacy: ["review-system-dir-mutation", "review-system-file-upload"]
|
|
3593
|
+
},
|
|
3458
3594
|
// ── Commands ────────────────────────────────────────────────────────────
|
|
3459
3595
|
{
|
|
3460
3596
|
id: "commands.inline-exec",
|
|
@@ -4580,6 +4716,10 @@ var init_dist = __esm({
|
|
|
4580
4716
|
AND_OP = probeBinOp("a && b");
|
|
4581
4717
|
OR_OP = probeBinOp("a || b");
|
|
4582
4718
|
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
4719
|
+
MAX_CALL_DEPTH = 4;
|
|
4720
|
+
MAX_CALL_WALKS = 256;
|
|
4721
|
+
callDepth = 0;
|
|
4722
|
+
callWalks = 0;
|
|
4583
4723
|
FS_OP_CACHE_MAX = 5e3;
|
|
4584
4724
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
4585
4725
|
REDIR_TRUNCATE_OPS = /* @__PURE__ */ new Set([deriveRedirOp(">_f")]);
|
|
@@ -4641,6 +4781,17 @@ var init_dist = __esm({
|
|
|
4641
4781
|
(s) => parseShared(`echo ${s}`).Stmts[0].Cmd.Args[1].Parts[0].Exp.Op
|
|
4642
4782
|
)
|
|
4643
4783
|
);
|
|
4784
|
+
SYSTEM_DIRS = [
|
|
4785
|
+
"/usr",
|
|
4786
|
+
"/etc",
|
|
4787
|
+
"/boot",
|
|
4788
|
+
"/bin",
|
|
4789
|
+
"/sbin",
|
|
4790
|
+
"/lib",
|
|
4791
|
+
"/lib64",
|
|
4792
|
+
"/var/log"
|
|
4793
|
+
];
|
|
4794
|
+
UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
|
|
4644
4795
|
LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
|
|
4645
4796
|
BIP39_ENGLISH = `abandon
|
|
4646
4797
|
ability
|
|
@@ -6897,15 +7048,20 @@ zoo`.split("\n");
|
|
|
6897
7048
|
IDENTIFIER_VALUE_RE = /^[A-Za-z_.-]+$/;
|
|
6898
7049
|
PATH_VALUE_RE = /^(?:\/|\.{1,2}\/|~\/)/;
|
|
6899
7050
|
DOTTED_REFERENCE_RE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
7051
|
+
CONCATENATION_RE = /^\+[A-Za-z_$][\w$]*\./;
|
|
6900
7052
|
EMAIL_VALUE_RE = /^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$/;
|
|
6901
7053
|
FIXTURE_NAME_RE = /(?:fake|dummy|example|sample|mock|stub|placeholder|test)/i;
|
|
6902
|
-
DERIVATIVE_NAME_RE = /(?:hash|digest|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
|
|
7054
|
+
DERIVATIVE_NAME_RE = /(?:hash|digest|fingerprint|thumbprint|checksum|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
|
|
6903
7055
|
QUERY_CREDENTIAL_VALUE_RE = /=([A-Za-z0-9%+/_.~-]{8,512}={0,2})$/;
|
|
6904
7056
|
OTP_SECRET_RE = /^[A-Z2-7]{16,}=*$/i;
|
|
6905
|
-
|
|
7057
|
+
OTP_DOC_SECRETS = /* @__PURE__ */ new Set(["JBSWY3DPEHPK3PXP", "GEZDGNBVGY3TQOJQ"]);
|
|
7058
|
+
ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
|
|
6906
7059
|
ASSIGNED_SECRET_NAME_RE = /^([A-Za-z0-9_]+)/;
|
|
6907
|
-
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)/i;
|
|
7060
|
+
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|pass)/i;
|
|
6908
7061
|
ARN_CONTEXT_RE = /\barn:[A-Za-z0-9-]*:[^\s"'`]*$/i;
|
|
7062
|
+
BASIC_VALUE_RE = /([A-Za-z0-9+/]{8,512}={0,2})$/;
|
|
7063
|
+
BASIC_PAIR_RE = /^([^:\s]{1,128}):(\S{8,256})$/;
|
|
7064
|
+
OTP_CAPTURE_RE = /["']([A-Z2-7]{16,128}=*)["']$/i;
|
|
6909
7065
|
DLP_PATTERNS = [
|
|
6910
7066
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
6911
7067
|
{
|
|
@@ -7243,6 +7399,22 @@ zoo`.split("\n");
|
|
|
7243
7399
|
contextBoost: true,
|
|
7244
7400
|
minEntropy: 3
|
|
7245
7401
|
},
|
|
7402
|
+
// ── HTTP Basic credential ─────────────────────────────────────────────────
|
|
7403
|
+
// `Authorization: Basic <base64 of user:password>`, or the bare scheme in a
|
|
7404
|
+
// config list. The validator decodes the value and accepts only a
|
|
7405
|
+
// `user:password` whose password looks generated (letters and digits, 8+
|
|
7406
|
+
// characters, the query-credential rule), so prose ("Basic authentication")
|
|
7407
|
+
// and documentation pairs (`admin:changeme`, `user:pass1234`) stay allowed.
|
|
7408
|
+
// review: the same weight as a bare Bearer token.
|
|
7409
|
+
{
|
|
7410
|
+
name: "HTTP Basic Credential",
|
|
7411
|
+
regex: /\bBasic[ \t]{1,8}([A-Za-z0-9+/]{8,512}={0,2})/i,
|
|
7412
|
+
severity: "review",
|
|
7413
|
+
keywords: ["basic"],
|
|
7414
|
+
validate: validateBasicCredential,
|
|
7415
|
+
redactGroup: 1,
|
|
7416
|
+
argumentsOnly: true
|
|
7417
|
+
},
|
|
7246
7418
|
// ── Resend ────────────────────────────────────────────────────────────────
|
|
7247
7419
|
{
|
|
7248
7420
|
name: "Resend API Key",
|
|
@@ -7384,6 +7556,19 @@ zoo`.split("\n");
|
|
|
7384
7556
|
validate: validateQueryCredential,
|
|
7385
7557
|
argumentsOnly: true
|
|
7386
7558
|
},
|
|
7559
|
+
// ── OTP secret in a constructor ───────────────────────────────────────────
|
|
7560
|
+
// `ROTP::TOTP.new('<base32>')`, `pyotp.TOTP("<base32>")`, `new HOTP('...')`:
|
|
7561
|
+
// the shared secret of a one-time-password generator written as a literal.
|
|
7562
|
+
// The otpauth URI form is `Credential in URL Query`. review.
|
|
7563
|
+
{
|
|
7564
|
+
name: "OTP Secret",
|
|
7565
|
+
regex: /\b(?:TOTP|HOTP|OTP)(?:\.new|\.create)?[ \t]{0,8}\(?[ \t]{0,8}["']([A-Z2-7]{16,128}=*)["']/i,
|
|
7566
|
+
severity: "review",
|
|
7567
|
+
keywords: ["otp"],
|
|
7568
|
+
validate: validateOtpSecret,
|
|
7569
|
+
redactGroup: 1,
|
|
7570
|
+
argumentsOnly: true
|
|
7571
|
+
},
|
|
7387
7572
|
// ── Secret assigned to a telling name ─────────────────────────────────────
|
|
7388
7573
|
// `password: ...`, `JWT_SECRET=...`, `api_key = "..."`. The name alone is
|
|
7389
7574
|
// not a finding: most such lines hold a reference (`process.env.X`), a
|
|
@@ -7391,12 +7576,14 @@ zoo`.split("\n");
|
|
|
7391
7576
|
// generated: 12+ characters, not an identifier, Shannon entropy of 3.5
|
|
7392
7577
|
// bits per character or more. review, never block (node9Firewall#348,
|
|
7393
7578
|
// item 4). Tuned on CredData's public split; its look-alike lines are the
|
|
7394
|
-
// negative control.
|
|
7579
|
+
// negative control. A quote may be escaped (`\"access_token\":\"...\"`):
|
|
7580
|
+
// JSON written inside a string literal. The two optional quotes after the
|
|
7581
|
+
// name are written out, not as a counted group, to keep star height 1.
|
|
7395
7582
|
{
|
|
7396
7583
|
name: "Assigned Secret",
|
|
7397
|
-
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)[A-Za-z0-9_]{0,40}["'\]]
|
|
7584
|
+
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|\bpass(?![A-Za-z0-9])|_pass(?![A-Za-z0-9]))[A-Za-z0-9_]{0,40}\\?["'\]]?\\?["'\]]?[ \t]{0,8}(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})/i,
|
|
7398
7585
|
severity: "review",
|
|
7399
|
-
keywords: ["
|
|
7586
|
+
keywords: ["pass", "pwd", "secret", "token", "key", "ticket"],
|
|
7400
7587
|
validate: validateAssignedSecret,
|
|
7401
7588
|
argumentsOnly: true
|
|
7402
7589
|
}
|
|
@@ -7458,6 +7645,35 @@ zoo`.split("\n");
|
|
|
7458
7645
|
MAX_STRING_BYTES = 1e5;
|
|
7459
7646
|
MAX_JSON_PARSE_BYTES = 1e4;
|
|
7460
7647
|
({ syntax: syntax3 } = mvdanSh3);
|
|
7648
|
+
EDITOR_HEADS = ["code", "code-insiders", "codium", "vscodium", "cursor"];
|
|
7649
|
+
MANAGER_HEADS = /* @__PURE__ */ new Set([
|
|
7650
|
+
"gem",
|
|
7651
|
+
"bundle",
|
|
7652
|
+
"cargo",
|
|
7653
|
+
"go",
|
|
7654
|
+
"dotnet",
|
|
7655
|
+
"nuget",
|
|
7656
|
+
"mvn",
|
|
7657
|
+
"mvnw",
|
|
7658
|
+
"composer",
|
|
7659
|
+
...EDITOR_HEADS,
|
|
7660
|
+
"npm",
|
|
7661
|
+
"npx",
|
|
7662
|
+
"pnpm",
|
|
7663
|
+
"yarn",
|
|
7664
|
+
"bun",
|
|
7665
|
+
"bunx",
|
|
7666
|
+
"pip",
|
|
7667
|
+
"pip3",
|
|
7668
|
+
"pipx",
|
|
7669
|
+
"python",
|
|
7670
|
+
"python3",
|
|
7671
|
+
"py",
|
|
7672
|
+
"uv",
|
|
7673
|
+
"uvx",
|
|
7674
|
+
"poetry",
|
|
7675
|
+
"pipenv"
|
|
7676
|
+
]);
|
|
7461
7677
|
TUNING_FIELDS = [
|
|
7462
7678
|
{ checkId: "network.unknown-host", name: "allow", legacy: ["egress", "allow"] },
|
|
7463
7679
|
{ checkId: "network.unknown-host", name: "deny", legacy: ["egress", "deny"] },
|
|
@@ -7516,6 +7732,7 @@ zoo`.split("\n");
|
|
|
7516
7732
|
MAP_GOVERNED = /* @__PURE__ */ new Set([
|
|
7517
7733
|
"files.shell-startup",
|
|
7518
7734
|
"files.password-hashes",
|
|
7735
|
+
"files.system-dirs",
|
|
7519
7736
|
"commands.inline-exec",
|
|
7520
7737
|
"commands.eval-dynamic",
|
|
7521
7738
|
"commands.curl-pipe-shell",
|