@node9/policy-engine 2.14.1 → 2.14.3

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
@@ -571,6 +571,19 @@ interface PolicyVerdict {
571
571
  matchedField?: string;
572
572
  matchedWord?: string;
573
573
  tier?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
574
+ /**
575
+ * False when NO human may override this verdict: the SSRF tier-1 floors,
576
+ * whose own reason string already tells the user "This address cannot be
577
+ * allowlisted". Absent means an override is a legitimate product decision,
578
+ * which is every other verdict.
579
+ *
580
+ * It exists because the host downgrades a hard block into a review whenever a
581
+ * human approver is reachable (orchestrator.ts, `mayDowngrade`). That is right
582
+ * for a smart-rule block and wrong for a floor, and the host could not tell
583
+ * the two apart: `overridable` was computed in egress/ssrf.ts, rendered into
584
+ * the reason sentence, and then dropped here.
585
+ */
586
+ overridable?: boolean;
574
587
  ruleName?: string;
575
588
  /** State predicates from the matched smart rule (only when decision is 'block'). */
576
589
  dependsOnStatePredicates?: string[];
@@ -647,6 +660,27 @@ declare function validateRegex(pattern: string): string | null;
647
660
  */
648
661
  declare function getCompiledRegex(pattern: string, flags?: string): RegExp | null;
649
662
 
663
+ /** See TERMINAL_ESCAPE_RE. Keeps tab, newline and carriage return. */
664
+ declare function stripTerminalEscapes(s: string): string;
665
+ /** See CONTROL_CHAR_RE. Removes every C0 control and DEL, whitespace included. */
666
+ declare function stripControlChars(s: string): string;
667
+ /**
668
+ * One safe line, for any string that came from outside this process before it
669
+ * reaches a terminal or a log file.
670
+ *
671
+ * node9's terminal output IS the user's trust signal: a "connected and
672
+ * governed" line is what tells someone the machine is protected. A response
673
+ * field carrying CR plus SGR codes can paint a line that looks exactly like
674
+ * one of ours, and a newline in a value written to hook-debug.log forges a
675
+ * second journal entry. So: escape sequences removed, all whitespace collapsed
676
+ * to single spaces (one value can never become two lines), and a length cap so
677
+ * a hostile or broken peer cannot flood the log.
678
+ *
679
+ * Accepts unknown because most call sites hold a caught `error` or an optional
680
+ * response field.
681
+ */
682
+ declare function safeMessage(value: unknown, max?: number): string;
683
+
650
684
  interface ShieldDefinition {
651
685
  name: string;
652
686
  description: string;
@@ -1058,6 +1092,11 @@ interface BlastSummary {
1058
1092
  /** Number of env vars flagged as credentials. No keys included. */
1059
1093
  envExposureCount: number;
1060
1094
  }
1095
+ /**
1096
+ * Longest path `truncateBlastPath` will look at, tail-anchored. PATH_MAX is
1097
+ * 4096 on Linux, so anything past this is not a path we could have read.
1098
+ */
1099
+ declare const MAX_BLAST_PATH = 4096;
1061
1100
  /**
1062
1101
  * Sanitise a sensitive path for transmission. Keeps only the trailing 2
1063
1102
  * segments — enough to identify the kind of file ("~/.aws/credentials"
@@ -1306,7 +1345,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
1306
1345
  * files changed, this hash must change too, and you must consciously
1307
1346
  * decide whether to bump CANONICAL_EXTRACTOR_VERSION."
1308
1347
  */
1309
- declare const CANONICAL_EXTRACTOR_HASH = "5dad4c8f07121f6c";
1348
+ declare const CANONICAL_EXTRACTOR_HASH = "2823cd6a54a1fca7";
1310
1349
  declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
1311
1350
  declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
1312
1351
  /**
@@ -1426,4 +1465,4 @@ declare function ssrfDestinationFloor(toolName: string, args: unknown, opts?: {
1426
1465
  /** Engine version stamped on audit entries for future drift detection. */
1427
1466
  declare const ENGINE_VERSION = "1.4.0";
1428
1467
 
1429
- export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANARY_MIN_LENGTH, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COMMAND_WRAPPERS, COPY_VERBS, COST_PER_LOOP_ITER_USD, type CanaryHit, type CanaryValue, type CanaryView, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTINATION_ARGS, DESTRUCTIVE_OP_RE, DLP_PATTERNS, DLP_SCAN_LIMITS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, FS_READ_TOOLS, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, NET_BINARIES, PATTERN_VERB_NAMES, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type PositionedArg, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, SSRF_MAX_HOST, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestToken, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type SsrfFloorOptions, type SsrfMatch, type SsrfTier, type SsrfVerdict, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, classifySsrf, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestTokens, extractShellDestinations, fileOperandFlagsOf, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, isStrictGatedTier, matchCanary, matchCanaryArgs, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, normalizeIpLiteral, parseAllSshHostsFromCommand, parseDestHost, patternShapeOf, positionedArgs, previewArgs, redactText, resolvePinned, sampleCopyCommand, scanArgs, scanInjection, scanText, sensitivePathMatch, ssrfDestinationFloor, ssrfFloor, ssrfReason, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, unwrapCommandHead, validateOverrides, validateRegex, validateShieldDefinition };
1468
+ export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANARY_MIN_LENGTH, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COMMAND_WRAPPERS, COPY_VERBS, COST_PER_LOOP_ITER_USD, type CanaryHit, type CanaryValue, type CanaryView, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTINATION_ARGS, DESTRUCTIVE_OP_RE, DLP_PATTERNS, DLP_SCAN_LIMITS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, FS_READ_TOOLS, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, MAX_BLAST_PATH, NET_BINARIES, PATTERN_VERB_NAMES, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type PositionedArg, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, SSRF_MAX_HOST, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestToken, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type SsrfFloorOptions, type SsrfMatch, type SsrfTier, type SsrfVerdict, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, classifySsrf, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestTokens, extractShellDestinations, fileOperandFlagsOf, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, isStrictGatedTier, matchCanary, matchCanaryArgs, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, normalizeIpLiteral, parseAllSshHostsFromCommand, parseDestHost, patternShapeOf, positionedArgs, previewArgs, redactText, resolvePinned, safeMessage, sampleCopyCommand, scanArgs, scanInjection, scanText, sensitivePathMatch, ssrfDestinationFloor, ssrfFloor, ssrfReason, stripControlChars, stripTerminalEscapes, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, unwrapCommandHead, validateOverrides, validateRegex, validateShieldDefinition };
package/dist/index.d.ts CHANGED
@@ -571,6 +571,19 @@ interface PolicyVerdict {
571
571
  matchedField?: string;
572
572
  matchedWord?: string;
573
573
  tier?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
574
+ /**
575
+ * False when NO human may override this verdict: the SSRF tier-1 floors,
576
+ * whose own reason string already tells the user "This address cannot be
577
+ * allowlisted". Absent means an override is a legitimate product decision,
578
+ * which is every other verdict.
579
+ *
580
+ * It exists because the host downgrades a hard block into a review whenever a
581
+ * human approver is reachable (orchestrator.ts, `mayDowngrade`). That is right
582
+ * for a smart-rule block and wrong for a floor, and the host could not tell
583
+ * the two apart: `overridable` was computed in egress/ssrf.ts, rendered into
584
+ * the reason sentence, and then dropped here.
585
+ */
586
+ overridable?: boolean;
574
587
  ruleName?: string;
575
588
  /** State predicates from the matched smart rule (only when decision is 'block'). */
576
589
  dependsOnStatePredicates?: string[];
@@ -647,6 +660,27 @@ declare function validateRegex(pattern: string): string | null;
647
660
  */
648
661
  declare function getCompiledRegex(pattern: string, flags?: string): RegExp | null;
649
662
 
663
+ /** See TERMINAL_ESCAPE_RE. Keeps tab, newline and carriage return. */
664
+ declare function stripTerminalEscapes(s: string): string;
665
+ /** See CONTROL_CHAR_RE. Removes every C0 control and DEL, whitespace included. */
666
+ declare function stripControlChars(s: string): string;
667
+ /**
668
+ * One safe line, for any string that came from outside this process before it
669
+ * reaches a terminal or a log file.
670
+ *
671
+ * node9's terminal output IS the user's trust signal: a "connected and
672
+ * governed" line is what tells someone the machine is protected. A response
673
+ * field carrying CR plus SGR codes can paint a line that looks exactly like
674
+ * one of ours, and a newline in a value written to hook-debug.log forges a
675
+ * second journal entry. So: escape sequences removed, all whitespace collapsed
676
+ * to single spaces (one value can never become two lines), and a length cap so
677
+ * a hostile or broken peer cannot flood the log.
678
+ *
679
+ * Accepts unknown because most call sites hold a caught `error` or an optional
680
+ * response field.
681
+ */
682
+ declare function safeMessage(value: unknown, max?: number): string;
683
+
650
684
  interface ShieldDefinition {
651
685
  name: string;
652
686
  description: string;
@@ -1058,6 +1092,11 @@ interface BlastSummary {
1058
1092
  /** Number of env vars flagged as credentials. No keys included. */
1059
1093
  envExposureCount: number;
1060
1094
  }
1095
+ /**
1096
+ * Longest path `truncateBlastPath` will look at, tail-anchored. PATH_MAX is
1097
+ * 4096 on Linux, so anything past this is not a path we could have read.
1098
+ */
1099
+ declare const MAX_BLAST_PATH = 4096;
1061
1100
  /**
1062
1101
  * Sanitise a sensitive path for transmission. Keeps only the trailing 2
1063
1102
  * segments — enough to identify the kind of file ("~/.aws/credentials"
@@ -1306,7 +1345,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
1306
1345
  * files changed, this hash must change too, and you must consciously
1307
1346
  * decide whether to bump CANONICAL_EXTRACTOR_VERSION."
1308
1347
  */
1309
- declare const CANONICAL_EXTRACTOR_HASH = "5dad4c8f07121f6c";
1348
+ declare const CANONICAL_EXTRACTOR_HASH = "2823cd6a54a1fca7";
1310
1349
  declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
1311
1350
  declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
1312
1351
  /**
@@ -1426,4 +1465,4 @@ declare function ssrfDestinationFloor(toolName: string, args: unknown, opts?: {
1426
1465
  /** Engine version stamped on audit entries for future drift detection. */
1427
1466
  declare const ENGINE_VERSION = "1.4.0";
1428
1467
 
1429
- export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANARY_MIN_LENGTH, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COMMAND_WRAPPERS, COPY_VERBS, COST_PER_LOOP_ITER_USD, type CanaryHit, type CanaryValue, type CanaryView, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTINATION_ARGS, DESTRUCTIVE_OP_RE, DLP_PATTERNS, DLP_SCAN_LIMITS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, FS_READ_TOOLS, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, NET_BINARIES, PATTERN_VERB_NAMES, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type PositionedArg, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, SSRF_MAX_HOST, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestToken, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type SsrfFloorOptions, type SsrfMatch, type SsrfTier, type SsrfVerdict, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, classifySsrf, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestTokens, extractShellDestinations, fileOperandFlagsOf, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, isStrictGatedTier, matchCanary, matchCanaryArgs, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, normalizeIpLiteral, parseAllSshHostsFromCommand, parseDestHost, patternShapeOf, positionedArgs, previewArgs, redactText, resolvePinned, sampleCopyCommand, scanArgs, scanInjection, scanText, sensitivePathMatch, ssrfDestinationFloor, ssrfFloor, ssrfReason, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, unwrapCommandHead, validateOverrides, validateRegex, validateShieldDefinition };
1468
+ export { AST_FS_REGEX_RULES, type AuditEntryForClassify, BASH_TOOL_NAMES, BUILTIN_SHIELDS, type BlastEnvFinding, type BlastFinding, type BlastResult, type BlastSummary, CANARY_MIN_LENGTH, CANONICAL_EXTRACTOR_HASH, CANONICAL_EXTRACTOR_VERSION, COMMAND_WRAPPERS, COPY_VERBS, COST_PER_LOOP_ITER_USD, type CanaryHit, type CanaryValue, type CanaryView, type CanonicalAgent, type CanonicalFinding, type CanonicalFindingType, type CanonicalSourceType, DEFAULT_EGRESS_ALLOWLIST, DESTINATION_ARGS, DESTRUCTIVE_OP_RE, DLP_PATTERNS, DLP_SCAN_LIMITS, type DlpMatch, ENGINE_VERSION, type EgressPolicy, type EgressVerdict, type ExtractContext, FILE_TOOLS, FLAGS_WITH_VALUES, FS_READ_TOOLS, type FsOpVerdict, type InjectionConfidence, type InjectionContext, type InjectionMatch, LONG_OUTPUT_THRESHOLD_BYTES, LOOP_MAX_RECORDS, LOOP_THRESHOLD_FOR_WASTE, type LoopWindowEvaluation, MAX_BLAST_PATH, NET_BINARIES, PATTERN_VERB_NAMES, PRIVILEGE_ESCALATION_RE, type PiiPattern, type PipeChainAnalysis, type PolicyConfig, type PolicyContext, type PolicyHostHooks, type PolicyVerdict, type PositionedArg, type ProvenanceLookup, type ProvenanceTrust, REALTIME_PII_PATTERNS, type RiskMetadata, SCAN_SIGNAL_WEIGHTS, SENSITIVE_PATH_RE, SENSITIVE_PATH_REGEXES, SSRF_MAX_HOST, type ScanFinding, type ScanSignals, type ScanSummary, type ScoreTier, type SessionExtractContext, type SessionToolCall, type Severity, type ShellCommandAnalysis, type ShellDestToken, type ShellDestination, type ShieldDefinition, type ShieldOverrides, type ShieldVerdict, type SmartCondition, type SmartRule, type SsrfFloorOptions, type SsrfMatch, type SsrfTier, type SsrfVerdict, type ToolCallEntry, type ToolCallRecord, analyzeFsOperation, analyzePipeChain, analyzeShellCommand, checkDangerousSql, classifyAuditEntry, classifyRuleSeverity, classifyScanSignal, classifySsrf, computeAgentDeviceScore, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectInlineExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestTokens, extractShellDestinations, fileOperandFlagsOf, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShellShapedTool, isShieldVerdict, isStrictGatedTier, matchCanary, matchCanaryArgs, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, normalizeIpLiteral, parseAllSshHostsFromCommand, parseDestHost, patternShapeOf, positionedArgs, previewArgs, redactText, resolvePinned, safeMessage, sampleCopyCommand, scanArgs, scanInjection, scanText, sensitivePathMatch, ssrfDestinationFloor, ssrfFloor, ssrfReason, stripControlChars, stripTerminalEscapes, summarizeBlast, summarizeScan, toScanFinding, toolMatchesRule, truncateBlastPath, unwrapCommandHead, validateOverrides, validateRegex, validateShieldDefinition };
package/dist/index.js CHANGED
@@ -51,6 +51,7 @@ __export(src_exports, {
51
51
  LONG_OUTPUT_THRESHOLD_BYTES: () => LONG_OUTPUT_THRESHOLD_BYTES,
52
52
  LOOP_MAX_RECORDS: () => LOOP_MAX_RECORDS,
53
53
  LOOP_THRESHOLD_FOR_WASTE: () => LOOP_THRESHOLD_FOR_WASTE,
54
+ MAX_BLAST_PATH: () => MAX_BLAST_PATH,
54
55
  NET_BINARIES: () => NET_BINARIES,
55
56
  PATTERN_VERB_NAMES: () => PATTERN_VERB_NAMES,
56
57
  PRIVILEGE_ESCALATION_RE: () => PRIVILEGE_ESCALATION_RE,
@@ -114,6 +115,7 @@ __export(src_exports, {
114
115
  previewArgs: () => previewArgs,
115
116
  redactText: () => redactText,
116
117
  resolvePinned: () => resolvePinned,
118
+ safeMessage: () => safeMessage,
117
119
  sampleCopyCommand: () => sampleCopyCommand,
118
120
  scanArgs: () => scanArgs,
119
121
  scanInjection: () => scanInjection,
@@ -122,6 +124,8 @@ __export(src_exports, {
122
124
  ssrfDestinationFloor: () => ssrfDestinationFloor,
123
125
  ssrfFloor: () => ssrfFloor,
124
126
  ssrfReason: () => ssrfReason,
127
+ stripControlChars: () => stripControlChars,
128
+ stripTerminalEscapes: () => stripTerminalEscapes,
125
129
  summarizeBlast: () => summarizeBlast,
126
130
  summarizeScan: () => summarizeScan,
127
131
  toScanFinding: () => toScanFinding,
@@ -4005,7 +4009,8 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
4005
4009
  reason: dest.reason,
4006
4010
  ruleName: `ssrf:${dest.tier}:${toolName}:${dest.host}`,
4007
4011
  ruleDescription: dest.reason,
4008
- tier: 3
4012
+ tier: 3,
4013
+ overridable: dest.overridable
4009
4014
  };
4010
4015
  }
4011
4016
  }
@@ -4154,7 +4159,8 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
4154
4159
  reason: ssrf.reason,
4155
4160
  ruleName: `ssrf:${ssrf.tier}:${ssrf.binary}:${ssrf.host}`,
4156
4161
  ruleDescription: ssrf.reason,
4157
- tier: 3
4162
+ tier: 3,
4163
+ overridable: ssrf.overridable
4158
4164
  };
4159
4165
  }
4160
4166
  }
@@ -4279,6 +4285,21 @@ function isIgnoredTool(toolName, config) {
4279
4285
  return matchesPattern(toolName, config.policy.ignoredTools);
4280
4286
  }
4281
4287
 
4288
+ // src/utils/safe-text.ts
4289
+ var TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
4290
+ var CONTROL_CHAR_RE = /[\x00-\x1F\x7F]/g;
4291
+ function stripTerminalEscapes(s) {
4292
+ return s.replace(TERMINAL_ESCAPE_RE, "");
4293
+ }
4294
+ function stripControlChars(s) {
4295
+ return s.replace(CONTROL_CHAR_RE, "");
4296
+ }
4297
+ function safeMessage(value, max = 300) {
4298
+ const raw = typeof value === "string" ? value : value instanceof Error ? value.message : String(value ?? "");
4299
+ const s = stripTerminalEscapes(raw).replace(/\s+/g, " ").trim();
4300
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
4301
+ }
4302
+
4282
4303
  // src/shields/index.ts
4283
4304
  var import_safe_regex23 = __toESM(require("safe-regex2"));
4284
4305
 
@@ -5360,9 +5381,13 @@ function computeAgentDeviceScore(opts) {
5360
5381
  }
5361
5382
 
5362
5383
  // src/blast/index.ts
5384
+ var MAX_BLAST_PATH = 4096;
5363
5385
  function truncateBlastPath(full) {
5364
5386
  if (!full) return "";
5365
- const cleaned = full.replace(/[/\\]+$/, "");
5387
+ if (full.length > MAX_BLAST_PATH) full = full.slice(-MAX_BLAST_PATH);
5388
+ let end = full.length;
5389
+ while (end > 0 && (full[end - 1] === "/" || full[end - 1] === "\\")) end--;
5390
+ const cleaned = full.slice(0, end);
5366
5391
  const parts = cleaned.split(/[/\\]+/).filter((p) => p.length > 0);
5367
5392
  if (parts.length <= 2) {
5368
5393
  return cleaned.startsWith("~") && !cleaned.startsWith("~/") ? cleaned : cleaned.startsWith("~/") ? cleaned : parts.join("/");
@@ -5650,7 +5675,7 @@ function matchCanaryArgs(args, values) {
5650
5675
  // src/scan/canonical.ts
5651
5676
  var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
5652
5677
  var CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
5653
- var CANONICAL_EXTRACTOR_HASH = "5dad4c8f07121f6c";
5678
+ var CANONICAL_EXTRACTOR_HASH = "2823cd6a54a1fca7";
5654
5679
  var DEDUPE_PREVIEW_LEN = 120;
5655
5680
  function extractCanonicalFindings(call, ctx) {
5656
5681
  const out = [];
@@ -5969,13 +5994,9 @@ function toScanFinding(c) {
5969
5994
  lineIndex: c.lineIndex
5970
5995
  };
5971
5996
  }
5972
- var TERMINAL_ESCAPE_RE = (
5973
- // eslint-disable-next-line no-control-regex
5974
- /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g
5975
- );
5976
5997
  function previewArgs(input, max) {
5977
5998
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
5978
- const s = String(cmd).replace(TERMINAL_ESCAPE_RE, "").replace(/\s+/g, " ").trim();
5999
+ const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
5979
6000
  return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
5980
6001
  }
5981
6002
  function makeFinding(args) {
@@ -6041,6 +6062,7 @@ var ENGINE_VERSION = "1.4.0";
6041
6062
  LONG_OUTPUT_THRESHOLD_BYTES,
6042
6063
  LOOP_MAX_RECORDS,
6043
6064
  LOOP_THRESHOLD_FOR_WASTE,
6065
+ MAX_BLAST_PATH,
6044
6066
  NET_BINARIES,
6045
6067
  PATTERN_VERB_NAMES,
6046
6068
  PRIVILEGE_ESCALATION_RE,
@@ -6104,6 +6126,7 @@ var ENGINE_VERSION = "1.4.0";
6104
6126
  previewArgs,
6105
6127
  redactText,
6106
6128
  resolvePinned,
6129
+ safeMessage,
6107
6130
  sampleCopyCommand,
6108
6131
  scanArgs,
6109
6132
  scanInjection,
@@ -6112,6 +6135,8 @@ var ENGINE_VERSION = "1.4.0";
6112
6135
  ssrfDestinationFloor,
6113
6136
  ssrfFloor,
6114
6137
  ssrfReason,
6138
+ stripControlChars,
6139
+ stripTerminalEscapes,
6115
6140
  summarizeBlast,
6116
6141
  summarizeScan,
6117
6142
  toScanFinding,
package/dist/index.mjs CHANGED
@@ -3869,7 +3869,8 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
3869
3869
  reason: dest.reason,
3870
3870
  ruleName: `ssrf:${dest.tier}:${toolName}:${dest.host}`,
3871
3871
  ruleDescription: dest.reason,
3872
- tier: 3
3872
+ tier: 3,
3873
+ overridable: dest.overridable
3873
3874
  };
3874
3875
  }
3875
3876
  }
@@ -4018,7 +4019,8 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
4018
4019
  reason: ssrf.reason,
4019
4020
  ruleName: `ssrf:${ssrf.tier}:${ssrf.binary}:${ssrf.host}`,
4020
4021
  ruleDescription: ssrf.reason,
4021
- tier: 3
4022
+ tier: 3,
4023
+ overridable: ssrf.overridable
4022
4024
  };
4023
4025
  }
4024
4026
  }
@@ -4143,6 +4145,21 @@ function isIgnoredTool(toolName, config) {
4143
4145
  return matchesPattern(toolName, config.policy.ignoredTools);
4144
4146
  }
4145
4147
 
4148
+ // src/utils/safe-text.ts
4149
+ var TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
4150
+ var CONTROL_CHAR_RE = /[\x00-\x1F\x7F]/g;
4151
+ function stripTerminalEscapes(s) {
4152
+ return s.replace(TERMINAL_ESCAPE_RE, "");
4153
+ }
4154
+ function stripControlChars(s) {
4155
+ return s.replace(CONTROL_CHAR_RE, "");
4156
+ }
4157
+ function safeMessage(value, max = 300) {
4158
+ const raw = typeof value === "string" ? value : value instanceof Error ? value.message : String(value ?? "");
4159
+ const s = stripTerminalEscapes(raw).replace(/\s+/g, " ").trim();
4160
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
4161
+ }
4162
+
4146
4163
  // src/shields/index.ts
4147
4164
  import safeRegex3 from "safe-regex2";
4148
4165
 
@@ -5224,9 +5241,13 @@ function computeAgentDeviceScore(opts) {
5224
5241
  }
5225
5242
 
5226
5243
  // src/blast/index.ts
5244
+ var MAX_BLAST_PATH = 4096;
5227
5245
  function truncateBlastPath(full) {
5228
5246
  if (!full) return "";
5229
- const cleaned = full.replace(/[/\\]+$/, "");
5247
+ if (full.length > MAX_BLAST_PATH) full = full.slice(-MAX_BLAST_PATH);
5248
+ let end = full.length;
5249
+ while (end > 0 && (full[end - 1] === "/" || full[end - 1] === "\\")) end--;
5250
+ const cleaned = full.slice(0, end);
5230
5251
  const parts = cleaned.split(/[/\\]+/).filter((p) => p.length > 0);
5231
5252
  if (parts.length <= 2) {
5232
5253
  return cleaned.startsWith("~") && !cleaned.startsWith("~/") ? cleaned : cleaned.startsWith("~/") ? cleaned : parts.join("/");
@@ -5514,7 +5535,7 @@ function matchCanaryArgs(args, values) {
5514
5535
  // src/scan/canonical.ts
5515
5536
  var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
5516
5537
  var CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
5517
- var CANONICAL_EXTRACTOR_HASH = "5dad4c8f07121f6c";
5538
+ var CANONICAL_EXTRACTOR_HASH = "2823cd6a54a1fca7";
5518
5539
  var DEDUPE_PREVIEW_LEN = 120;
5519
5540
  function extractCanonicalFindings(call, ctx) {
5520
5541
  const out = [];
@@ -5833,13 +5854,9 @@ function toScanFinding(c) {
5833
5854
  lineIndex: c.lineIndex
5834
5855
  };
5835
5856
  }
5836
- var TERMINAL_ESCAPE_RE = (
5837
- // eslint-disable-next-line no-control-regex
5838
- /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g
5839
- );
5840
5857
  function previewArgs(input, max) {
5841
5858
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
5842
- const s = String(cmd).replace(TERMINAL_ESCAPE_RE, "").replace(/\s+/g, " ").trim();
5859
+ const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
5843
5860
  return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
5844
5861
  }
5845
5862
  function makeFinding(args) {
@@ -5904,6 +5921,7 @@ export {
5904
5921
  LONG_OUTPUT_THRESHOLD_BYTES,
5905
5922
  LOOP_MAX_RECORDS,
5906
5923
  LOOP_THRESHOLD_FOR_WASTE,
5924
+ MAX_BLAST_PATH,
5907
5925
  NET_BINARIES,
5908
5926
  PATTERN_VERB_NAMES,
5909
5927
  PRIVILEGE_ESCALATION_RE,
@@ -5967,6 +5985,7 @@ export {
5967
5985
  previewArgs,
5968
5986
  redactText,
5969
5987
  resolvePinned,
5988
+ safeMessage,
5970
5989
  sampleCopyCommand,
5971
5990
  scanArgs,
5972
5991
  scanInjection,
@@ -5975,6 +5994,8 @@ export {
5975
5994
  ssrfDestinationFloor,
5976
5995
  ssrfFloor,
5977
5996
  ssrfReason,
5997
+ stripControlChars,
5998
+ stripTerminalEscapes,
5978
5999
  summarizeBlast,
5979
6000
  summarizeScan,
5980
6001
  toScanFinding,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/policy-engine",
3
- "version": "2.14.1",
3
+ "version": "2.14.3",
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",