@node9/policy-engine 1.67.2 → 1.67.4

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/index.d.mts CHANGED
@@ -153,6 +153,21 @@ declare function matchSensitivePath(resolvedPath: string, originalPath: string):
153
153
  declare function sensitivePathMatch(originalPath: string): DlpMatch;
154
154
  /** Exposed so the proxy can apply the same patterns directly if needed. */
155
155
  declare const SENSITIVE_PATH_REGEXES: readonly RegExp[];
156
+ /**
157
+ * The bounds scanArgs works within, published so callers can tell "scanned
158
+ * clean" apart from "never looked". A caller that must FAIL CLOSED on an
159
+ * unscanned value (e.g. the audit writer, which would otherwise persist an
160
+ * unexamined credential in plaintext) needs these to decide whether the scan
161
+ * actually covered its input. Exported rather than duplicated at the call
162
+ * site so the two can never drift apart.
163
+ */
164
+ declare const DLP_SCAN_LIMITS: {
165
+ /** Max characters examined per individual string field; the remainder of an
166
+ * oversized string is NOT scanned. */
167
+ readonly maxStringBytes: 100000;
168
+ /** Max nesting depth walked; anything deeper is NOT scanned. */
169
+ readonly maxDepth: 5;
170
+ };
156
171
  /**
157
172
  * Recursively scans an args value for known secret patterns.
158
173
  * Handles nested objects, arrays, and JSON-encoded strings.
@@ -1117,7 +1132,7 @@ declare const LONG_OUTPUT_THRESHOLD_BYTES: number;
1117
1132
  * and fails CI when the hash drifts without a version bump — forgetting
1118
1133
  * is loud, not silent.
1119
1134
  */
1120
- declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v8";
1135
+ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v9";
1121
1136
  /**
1122
1137
  * SHA-256 prefix of the detector-source files
1123
1138
  * (canonical.ts + pii.ts + destructive-regex.ts).
@@ -1128,7 +1143,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v8";
1128
1143
  * files changed, this hash must change too, and you must consciously
1129
1144
  * decide whether to bump CANONICAL_EXTRACTOR_VERSION."
1130
1145
  */
1131
- declare const CANONICAL_EXTRACTOR_HASH = "80f40f974263b281";
1146
+ declare const CANONICAL_EXTRACTOR_HASH = "4ebf40dfe1d7c0a1";
1132
1147
  declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
1133
1148
  declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
1134
1149
  /**
@@ -1155,4 +1170,4 @@ declare function previewArgs(input: Record<string, unknown>, max: number): strin
1155
1170
  /** Engine version stamped on audit entries for future drift detection. */
1156
1171
  declare const ENGINE_VERSION = "1.4.0";
1157
1172
 
1158
- export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COST_PER_LOOP_ITER_USD, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTRUCTIVE_OP_RE, DLP_PATTERNS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, resolvePinned, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
1173
+ export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COST_PER_LOOP_ITER_USD, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTRUCTIVE_OP_RE, DLP_PATTERNS, DLP_SCAN_LIMITS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, resolvePinned, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
package/dist/index.d.ts CHANGED
@@ -153,6 +153,21 @@ declare function matchSensitivePath(resolvedPath: string, originalPath: string):
153
153
  declare function sensitivePathMatch(originalPath: string): DlpMatch;
154
154
  /** Exposed so the proxy can apply the same patterns directly if needed. */
155
155
  declare const SENSITIVE_PATH_REGEXES: readonly RegExp[];
156
+ /**
157
+ * The bounds scanArgs works within, published so callers can tell "scanned
158
+ * clean" apart from "never looked". A caller that must FAIL CLOSED on an
159
+ * unscanned value (e.g. the audit writer, which would otherwise persist an
160
+ * unexamined credential in plaintext) needs these to decide whether the scan
161
+ * actually covered its input. Exported rather than duplicated at the call
162
+ * site so the two can never drift apart.
163
+ */
164
+ declare const DLP_SCAN_LIMITS: {
165
+ /** Max characters examined per individual string field; the remainder of an
166
+ * oversized string is NOT scanned. */
167
+ readonly maxStringBytes: 100000;
168
+ /** Max nesting depth walked; anything deeper is NOT scanned. */
169
+ readonly maxDepth: 5;
170
+ };
156
171
  /**
157
172
  * Recursively scans an args value for known secret patterns.
158
173
  * Handles nested objects, arrays, and JSON-encoded strings.
@@ -1117,7 +1132,7 @@ declare const LONG_OUTPUT_THRESHOLD_BYTES: number;
1117
1132
  * and fails CI when the hash drifts without a version bump — forgetting
1118
1133
  * is loud, not silent.
1119
1134
  */
1120
- declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v8";
1135
+ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v9";
1121
1136
  /**
1122
1137
  * SHA-256 prefix of the detector-source files
1123
1138
  * (canonical.ts + pii.ts + destructive-regex.ts).
@@ -1128,7 +1143,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v8";
1128
1143
  * files changed, this hash must change too, and you must consciously
1129
1144
  * decide whether to bump CANONICAL_EXTRACTOR_VERSION."
1130
1145
  */
1131
- declare const CANONICAL_EXTRACTOR_HASH = "80f40f974263b281";
1146
+ declare const CANONICAL_EXTRACTOR_HASH = "4ebf40dfe1d7c0a1";
1132
1147
  declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
1133
1148
  declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
1134
1149
  /**
@@ -1155,4 +1170,4 @@ declare function previewArgs(input: Record<string, unknown>, max: number): strin
1155
1170
  /** Engine version stamped on audit entries for future drift detection. */
1156
1171
  declare const ENGINE_VERSION = "1.4.0";
1157
1172
 
1158
- export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COST_PER_LOOP_ITER_USD, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTRUCTIVE_OP_RE, DLP_PATTERNS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, resolvePinned, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
1173
+ export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COST_PER_LOOP_ITER_USD, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTRUCTIVE_OP_RE, DLP_PATTERNS, DLP_SCAN_LIMITS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, resolvePinned, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ __export(src_exports, {
39
39
  DEFAULT_EGRESS_ALLOWLIST: () => DEFAULT_EGRESS_ALLOWLIST,
40
40
  DESTRUCTIVE_OP_RE: () => DESTRUCTIVE_OP_RE,
41
41
  DLP_PATTERNS: () => DLP_PATTERNS,
42
+ DLP_SCAN_LIMITS: () => DLP_SCAN_LIMITS,
42
43
  ENGINE_VERSION: () => ENGINE_VERSION,
43
44
  FILE_TOOLS: () => FILE_TOOLS,
44
45
  FLAGS_WITH_VALUES: () => FLAGS_WITH_VALUES,
@@ -715,6 +716,13 @@ function maskSecret(raw, pattern) {
715
716
  var MAX_DEPTH = 5;
716
717
  var MAX_STRING_BYTES = 1e5;
717
718
  var MAX_JSON_PARSE_BYTES = 1e4;
719
+ var DLP_SCAN_LIMITS = {
720
+ /** Max characters examined per individual string field; the remainder of an
721
+ * oversized string is NOT scanned. */
722
+ maxStringBytes: MAX_STRING_BYTES,
723
+ /** Max nesting depth walked; anything deeper is NOT scanned. */
724
+ maxDepth: MAX_DEPTH
725
+ };
718
726
  function scanArgs(args, depth = 0, fieldPath = "args") {
719
727
  if (depth > MAX_DEPTH || args === null || args === void 0) return null;
720
728
  if (Array.isArray(args)) {
@@ -1160,9 +1168,34 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
1160
1168
  "vi",
1161
1169
  "emacs",
1162
1170
  "code",
1163
- "type"
1171
+ "type",
1172
+ // — the 22 that were missing —
1173
+ "grep",
1174
+ "egrep",
1175
+ "fgrep",
1176
+ "rg",
1177
+ "ag",
1178
+ "ack",
1179
+ "awk",
1180
+ "gawk",
1181
+ "sed",
1182
+ "cut",
1183
+ "tr",
1184
+ "jq",
1185
+ "yq",
1186
+ "od",
1187
+ "xxd",
1188
+ "hexdump",
1189
+ "strings",
1190
+ "sort",
1191
+ "uniq",
1192
+ "tac",
1193
+ "nl",
1194
+ "dd"
1164
1195
  ]);
1165
- var FS_OP_PRESCREEN_RE = /(?:^|[\s|;&(`\n])(?:rm|cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type)\b/;
1196
+ var FS_OP_PRESCREEN_RE = new RegExp(
1197
+ `(?:^|[\\s|;&(\`\\n])(?:rm|${[...FS_READ_TOOLS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b`
1198
+ );
1166
1199
  var HOME_CACHE_ALLOWLIST = [
1167
1200
  ".cache",
1168
1201
  ".npm/_npx",
@@ -1201,9 +1234,37 @@ var SENSITIVE_PATH_RULES = [
1201
1234
  // for the canonical test-asserted contract.
1202
1235
  rule: "shield:project-jail:block-read-env",
1203
1236
  reason: "Reading .env files is blocked by project-jail shield",
1204
- match: (p) => /(?:^|[\\/])\.env(?:\.(?:local|production|staging|development|production\.local|staging\.local|development\.local))?$/i.test(
1205
- p
1206
- )
1237
+ // Structural, not a list. The previous form enumerated seven suffixes and
1238
+ // anchored on `$`, so `.env.prod`, `.env.ci` and `.env.local.bak` — all
1239
+ // gitignored, all routinely holding real secrets — were never covered. A
1240
+ // hand-written list of what to protect is only ever as complete as the day
1241
+ // it was typed; this says "`.env` plus any suffix chain" and then names the
1242
+ // exceptions, which is the direction that fails safe.
1243
+ //
1244
+ // \.env the segment itself
1245
+ // (?![\w-]) a boundary, so `.environment` and `.envrc` are NOT .env
1246
+ // files. Without it a flat suffix class swallows both.
1247
+ // (?![\w-]) a boundary, so `.environment` and `.envrc` are NOT .env
1248
+ // [\w.-]*$ any suffix chain. Flat class, no nested quantifier —
1249
+ // `(\.[\w-]+)*` reads the same but is rejected by
1250
+ // safe-regex2, and this pattern runs on the hook hot path.
1251
+ //
1252
+ // The two exclusions are NOT the same shape, because the words do not mean
1253
+ // the same thing:
1254
+ //
1255
+ // (?!\.(?:example|sample|template)\b) — "this file is a fixture", and it
1256
+ // stays a fixture whatever follows, so `.env.example.md` is allowed too.
1257
+ // These are checked into git by convention: already public, so blocking
1258
+ // them buys nothing and costs the most common legitimate agent read.
1259
+ //
1260
+ // (?!\.test$) — anchored, because `test` names an ENVIRONMENT, not a
1261
+ // fixture. `.env.test` is the committed template and stays allowed, but
1262
+ // `.env.test.local` is gitignored by the `.env*.local` convention and
1263
+ // holds real values, so it must block. Using `\b` here — the obvious
1264
+ // symmetry — silently exempts every `.env.test.*` file.
1265
+ //
1266
+ // shields.test.ts:983-995 is the canonical contract; keep both in step.
1267
+ match: (p) => /(?:^|[\\/])\.env(?![\w-])(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$/i.test(p)
1207
1268
  },
1208
1269
  {
1209
1270
  // verdict: 'review' (not 'block') is a deliberate design choice
@@ -3917,8 +3978,8 @@ function detectArgsPii(args) {
3917
3978
 
3918
3979
  // src/scan/canonical.ts
3919
3980
  var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
3920
- var CANONICAL_EXTRACTOR_VERSION = "canonical-v8";
3921
- var CANONICAL_EXTRACTOR_HASH = "80f40f974263b281";
3981
+ var CANONICAL_EXTRACTOR_VERSION = "canonical-v9";
3982
+ var CANONICAL_EXTRACTOR_HASH = "4ebf40dfe1d7c0a1";
3922
3983
  var DEDUPE_PREVIEW_LEN = 120;
3923
3984
  function extractCanonicalFindings(call, ctx) {
3924
3985
  const out = [];
@@ -4276,6 +4337,7 @@ var ENGINE_VERSION = "1.4.0";
4276
4337
  DEFAULT_EGRESS_ALLOWLIST,
4277
4338
  DESTRUCTIVE_OP_RE,
4278
4339
  DLP_PATTERNS,
4340
+ DLP_SCAN_LIMITS,
4279
4341
  ENGINE_VERSION,
4280
4342
  FILE_TOOLS,
4281
4343
  FLAGS_WITH_VALUES,
package/dist/index.mjs CHANGED
@@ -602,6 +602,13 @@ function maskSecret(raw, pattern) {
602
602
  var MAX_DEPTH = 5;
603
603
  var MAX_STRING_BYTES = 1e5;
604
604
  var MAX_JSON_PARSE_BYTES = 1e4;
605
+ var DLP_SCAN_LIMITS = {
606
+ /** Max characters examined per individual string field; the remainder of an
607
+ * oversized string is NOT scanned. */
608
+ maxStringBytes: MAX_STRING_BYTES,
609
+ /** Max nesting depth walked; anything deeper is NOT scanned. */
610
+ maxDepth: MAX_DEPTH
611
+ };
605
612
  function scanArgs(args, depth = 0, fieldPath = "args") {
606
613
  if (depth > MAX_DEPTH || args === null || args === void 0) return null;
607
614
  if (Array.isArray(args)) {
@@ -1047,9 +1054,34 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
1047
1054
  "vi",
1048
1055
  "emacs",
1049
1056
  "code",
1050
- "type"
1057
+ "type",
1058
+ // — the 22 that were missing —
1059
+ "grep",
1060
+ "egrep",
1061
+ "fgrep",
1062
+ "rg",
1063
+ "ag",
1064
+ "ack",
1065
+ "awk",
1066
+ "gawk",
1067
+ "sed",
1068
+ "cut",
1069
+ "tr",
1070
+ "jq",
1071
+ "yq",
1072
+ "od",
1073
+ "xxd",
1074
+ "hexdump",
1075
+ "strings",
1076
+ "sort",
1077
+ "uniq",
1078
+ "tac",
1079
+ "nl",
1080
+ "dd"
1051
1081
  ]);
1052
- var FS_OP_PRESCREEN_RE = /(?:^|[\s|;&(`\n])(?:rm|cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type)\b/;
1082
+ var FS_OP_PRESCREEN_RE = new RegExp(
1083
+ `(?:^|[\\s|;&(\`\\n])(?:rm|${[...FS_READ_TOOLS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b`
1084
+ );
1053
1085
  var HOME_CACHE_ALLOWLIST = [
1054
1086
  ".cache",
1055
1087
  ".npm/_npx",
@@ -1088,9 +1120,37 @@ var SENSITIVE_PATH_RULES = [
1088
1120
  // for the canonical test-asserted contract.
1089
1121
  rule: "shield:project-jail:block-read-env",
1090
1122
  reason: "Reading .env files is blocked by project-jail shield",
1091
- match: (p) => /(?:^|[\\/])\.env(?:\.(?:local|production|staging|development|production\.local|staging\.local|development\.local))?$/i.test(
1092
- p
1093
- )
1123
+ // Structural, not a list. The previous form enumerated seven suffixes and
1124
+ // anchored on `$`, so `.env.prod`, `.env.ci` and `.env.local.bak` — all
1125
+ // gitignored, all routinely holding real secrets — were never covered. A
1126
+ // hand-written list of what to protect is only ever as complete as the day
1127
+ // it was typed; this says "`.env` plus any suffix chain" and then names the
1128
+ // exceptions, which is the direction that fails safe.
1129
+ //
1130
+ // \.env the segment itself
1131
+ // (?![\w-]) a boundary, so `.environment` and `.envrc` are NOT .env
1132
+ // files. Without it a flat suffix class swallows both.
1133
+ // (?![\w-]) a boundary, so `.environment` and `.envrc` are NOT .env
1134
+ // [\w.-]*$ any suffix chain. Flat class, no nested quantifier —
1135
+ // `(\.[\w-]+)*` reads the same but is rejected by
1136
+ // safe-regex2, and this pattern runs on the hook hot path.
1137
+ //
1138
+ // The two exclusions are NOT the same shape, because the words do not mean
1139
+ // the same thing:
1140
+ //
1141
+ // (?!\.(?:example|sample|template)\b) — "this file is a fixture", and it
1142
+ // stays a fixture whatever follows, so `.env.example.md` is allowed too.
1143
+ // These are checked into git by convention: already public, so blocking
1144
+ // them buys nothing and costs the most common legitimate agent read.
1145
+ //
1146
+ // (?!\.test$) — anchored, because `test` names an ENVIRONMENT, not a
1147
+ // fixture. `.env.test` is the committed template and stays allowed, but
1148
+ // `.env.test.local` is gitignored by the `.env*.local` convention and
1149
+ // holds real values, so it must block. Using `\b` here — the obvious
1150
+ // symmetry — silently exempts every `.env.test.*` file.
1151
+ //
1152
+ // shields.test.ts:983-995 is the canonical contract; keep both in step.
1153
+ match: (p) => /(?:^|[\\/])\.env(?![\w-])(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$/i.test(p)
1094
1154
  },
1095
1155
  {
1096
1156
  // verdict: 'review' (not 'block') is a deliberate design choice
@@ -3804,8 +3864,8 @@ function detectArgsPii(args) {
3804
3864
 
3805
3865
  // src/scan/canonical.ts
3806
3866
  var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
3807
- var CANONICAL_EXTRACTOR_VERSION = "canonical-v8";
3808
- var CANONICAL_EXTRACTOR_HASH = "80f40f974263b281";
3867
+ var CANONICAL_EXTRACTOR_VERSION = "canonical-v9";
3868
+ var CANONICAL_EXTRACTOR_HASH = "4ebf40dfe1d7c0a1";
3809
3869
  var DEDUPE_PREVIEW_LEN = 120;
3810
3870
  function extractCanonicalFindings(call, ctx) {
3811
3871
  const out = [];
@@ -4162,6 +4222,7 @@ export {
4162
4222
  DEFAULT_EGRESS_ALLOWLIST,
4163
4223
  DESTRUCTIVE_OP_RE,
4164
4224
  DLP_PATTERNS,
4225
+ DLP_SCAN_LIMITS,
4165
4226
  ENGINE_VERSION,
4166
4227
  FILE_TOOLS,
4167
4228
  FLAGS_WITH_VALUES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/policy-engine",
3
- "version": "1.67.2",
3
+ "version": "1.67.4",
4
4
  "description": "Shared policy evaluation engine for node9 — DLP, smart rules, AST shell parsing, shields, loop detection. Pure functions, no I/O. Used by both node9-proxy and the node9 SaaS firewall.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://node9.ai",