@node9/policy-engine 1.45.0 → 1.47.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/index.d.mts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +17 -1
- package/dist/index.mjs +16 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -49,6 +49,19 @@ interface SmartRule {
|
|
|
49
49
|
* permanently destroy teammates' work."
|
|
50
50
|
*/
|
|
51
51
|
description?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Provenance of a synced cloud rule, e.g. "SHIELD:aws" or "USER". Shipped by
|
|
54
|
+
* the SaaS so the proxy can attribute a rule to its origin shield (so
|
|
55
|
+
* `node9 shield list` can show a shield as cloud-enabled). Absent on local rules.
|
|
56
|
+
*/
|
|
57
|
+
source?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Manager "pinned" rule (Phase 3b): when set, this rule wins a conflict — a
|
|
60
|
+
* developer's local rule can't override it. Cloud-authored + admin-gated;
|
|
61
|
+
* local rules never set it. See resolvePinned. Default unset = a normal rule,
|
|
62
|
+
* resolved exactly as today (first match wins).
|
|
63
|
+
*/
|
|
64
|
+
pinned?: boolean;
|
|
52
65
|
}
|
|
53
66
|
/**
|
|
54
67
|
* Result of a DLP scan — either a single match or null. The redactedSample
|
|
@@ -365,6 +378,19 @@ interface PolicyVerdict {
|
|
|
365
378
|
/** Plain-English description of what the rule does (from SmartRule.description). */
|
|
366
379
|
ruleDescription?: string;
|
|
367
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Resolve which of several MATCHING smart rules decides the verdict (the
|
|
383
|
+
* pinned-only conflict engine, Phase 3b). The model:
|
|
384
|
+
* 1. If any matching rule is `pinned` (a manager "keep mine" cloud rule),
|
|
385
|
+
* only the pinned rules compete and the MOST-RESTRICTIVE pinned wins
|
|
386
|
+
* (block > review > allow) — so a manager's locked rule can't be
|
|
387
|
+
* overridden by a developer's local rule.
|
|
388
|
+
* 2. Otherwise the FIRST match wins — byte-for-byte the previous `.find()`
|
|
389
|
+
* behaviour. This is the zero-regression guarantee: with nothing pinned,
|
|
390
|
+
* nothing changes.
|
|
391
|
+
* Exported so the resolution is unit-testable in isolation.
|
|
392
|
+
*/
|
|
393
|
+
declare function resolvePinned(matches: SmartRule[]): SmartRule | undefined;
|
|
368
394
|
/**
|
|
369
395
|
* Checks a SQL string for dangerous unscoped mutations.
|
|
370
396
|
* Returns a reason string if dangerous, null if safe.
|
|
@@ -1029,4 +1055,4 @@ declare function previewArgs(input: Record<string, unknown>, max: number): strin
|
|
|
1029
1055
|
/** Engine version stamped on audit entries for future drift detection. */
|
|
1030
1056
|
declare const ENGINE_VERSION = "1.4.0";
|
|
1031
1057
|
|
|
1032
|
-
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, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
|
|
1058
|
+
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, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, resolvePinned, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,19 @@ interface SmartRule {
|
|
|
49
49
|
* permanently destroy teammates' work."
|
|
50
50
|
*/
|
|
51
51
|
description?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Provenance of a synced cloud rule, e.g. "SHIELD:aws" or "USER". Shipped by
|
|
54
|
+
* the SaaS so the proxy can attribute a rule to its origin shield (so
|
|
55
|
+
* `node9 shield list` can show a shield as cloud-enabled). Absent on local rules.
|
|
56
|
+
*/
|
|
57
|
+
source?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Manager "pinned" rule (Phase 3b): when set, this rule wins a conflict — a
|
|
60
|
+
* developer's local rule can't override it. Cloud-authored + admin-gated;
|
|
61
|
+
* local rules never set it. See resolvePinned. Default unset = a normal rule,
|
|
62
|
+
* resolved exactly as today (first match wins).
|
|
63
|
+
*/
|
|
64
|
+
pinned?: boolean;
|
|
52
65
|
}
|
|
53
66
|
/**
|
|
54
67
|
* Result of a DLP scan — either a single match or null. The redactedSample
|
|
@@ -365,6 +378,19 @@ interface PolicyVerdict {
|
|
|
365
378
|
/** Plain-English description of what the rule does (from SmartRule.description). */
|
|
366
379
|
ruleDescription?: string;
|
|
367
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Resolve which of several MATCHING smart rules decides the verdict (the
|
|
383
|
+
* pinned-only conflict engine, Phase 3b). The model:
|
|
384
|
+
* 1. If any matching rule is `pinned` (a manager "keep mine" cloud rule),
|
|
385
|
+
* only the pinned rules compete and the MOST-RESTRICTIVE pinned wins
|
|
386
|
+
* (block > review > allow) — so a manager's locked rule can't be
|
|
387
|
+
* overridden by a developer's local rule.
|
|
388
|
+
* 2. Otherwise the FIRST match wins — byte-for-byte the previous `.find()`
|
|
389
|
+
* behaviour. This is the zero-regression guarantee: with nothing pinned,
|
|
390
|
+
* nothing changes.
|
|
391
|
+
* Exported so the resolution is unit-testable in isolation.
|
|
392
|
+
*/
|
|
393
|
+
declare function resolvePinned(matches: SmartRule[]): SmartRule | undefined;
|
|
368
394
|
/**
|
|
369
395
|
* Checks a SQL string for dangerous unscoped mutations.
|
|
370
396
|
* Returns a reason string if dangerous, null if safe.
|
|
@@ -1029,4 +1055,4 @@ declare function previewArgs(input: Record<string, unknown>, max: number): strin
|
|
|
1029
1055
|
/** Engine version stamped on audit entries for future drift detection. */
|
|
1030
1056
|
declare const ENGINE_VERSION = "1.4.0";
|
|
1031
1057
|
|
|
1032
|
-
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, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
|
|
1058
|
+
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, computeArgsHash, computeBlendedSecurityScore, computeScanScore, computeSecurityScore, dedupeCanonicalFindings, detectArgsPii, detectDangerousEval, detectDangerousShellExec, detectPii, evaluateEgress, evaluateLoopWindow, evaluatePolicy, evaluateSmartConditions, extractAllSshHosts, extractCanonicalFindings, extractNetworkTargets, extractPositionalArgs, extractSessionLevelFindings, extractShellDestinations, getCompiledRegex, getNestedValue, hostMatches, isBashTool, isIgnoredTool, isPrivateHost, isProtectedHomePath, isShieldVerdict, matchSensitivePath, matchesPattern, narrativeRuleLabel, normalizeCommandForPolicy, parseAllSshHostsFromCommand, parseDestHost, previewArgs, redactText, resolvePinned, scanArgs, scanInjection, scanText, sensitivePathMatch, summarizeBlast, summarizeScan, toScanFinding, truncateBlastPath, validateOverrides, validateRegex, validateShieldDefinition };
|
package/dist/index.js
CHANGED
|
@@ -92,6 +92,7 @@ __export(src_exports, {
|
|
|
92
92
|
parseDestHost: () => parseDestHost,
|
|
93
93
|
previewArgs: () => previewArgs,
|
|
94
94
|
redactText: () => redactText,
|
|
95
|
+
resolvePinned: () => resolvePinned,
|
|
95
96
|
scanArgs: () => scanArgs,
|
|
96
97
|
scanInjection: () => scanInjection,
|
|
97
98
|
scanText: () => scanText,
|
|
@@ -2083,6 +2084,19 @@ function evaluateSmartConditions(args, rule) {
|
|
|
2083
2084
|
}
|
|
2084
2085
|
|
|
2085
2086
|
// src/policy/index.ts
|
|
2087
|
+
var VERDICT_RANK = {
|
|
2088
|
+
allow: 0,
|
|
2089
|
+
review: 1,
|
|
2090
|
+
block: 2
|
|
2091
|
+
};
|
|
2092
|
+
function resolvePinned(matches) {
|
|
2093
|
+
if (matches.length === 0) return void 0;
|
|
2094
|
+
const pinned = matches.filter((r) => r.pinned);
|
|
2095
|
+
if (pinned.length === 0) return matches[0];
|
|
2096
|
+
return pinned.reduce(
|
|
2097
|
+
(best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2086
2100
|
function tokenize2(toolName) {
|
|
2087
2101
|
return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
|
|
2088
2102
|
}
|
|
@@ -2204,9 +2218,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
2204
2218
|
}
|
|
2205
2219
|
}
|
|
2206
2220
|
if (config.policy.smartRules.length > 0) {
|
|
2207
|
-
const
|
|
2221
|
+
const matches = config.policy.smartRules.filter(
|
|
2208
2222
|
(rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
|
|
2209
2223
|
);
|
|
2224
|
+
const matchedRule = resolvePinned(matches);
|
|
2210
2225
|
if (matchedRule) {
|
|
2211
2226
|
if (matchedRule.verdict === "allow")
|
|
2212
2227
|
return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
|
|
@@ -3929,6 +3944,7 @@ var ENGINE_VERSION = "1.4.0";
|
|
|
3929
3944
|
parseDestHost,
|
|
3930
3945
|
previewArgs,
|
|
3931
3946
|
redactText,
|
|
3947
|
+
resolvePinned,
|
|
3932
3948
|
scanArgs,
|
|
3933
3949
|
scanInjection,
|
|
3934
3950
|
scanText,
|
package/dist/index.mjs
CHANGED
|
@@ -1975,6 +1975,19 @@ function evaluateSmartConditions(args, rule) {
|
|
|
1975
1975
|
}
|
|
1976
1976
|
|
|
1977
1977
|
// src/policy/index.ts
|
|
1978
|
+
var VERDICT_RANK = {
|
|
1979
|
+
allow: 0,
|
|
1980
|
+
review: 1,
|
|
1981
|
+
block: 2
|
|
1982
|
+
};
|
|
1983
|
+
function resolvePinned(matches) {
|
|
1984
|
+
if (matches.length === 0) return void 0;
|
|
1985
|
+
const pinned = matches.filter((r) => r.pinned);
|
|
1986
|
+
if (pinned.length === 0) return matches[0];
|
|
1987
|
+
return pinned.reduce(
|
|
1988
|
+
(best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
|
|
1989
|
+
);
|
|
1990
|
+
}
|
|
1978
1991
|
function tokenize2(toolName) {
|
|
1979
1992
|
return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
|
|
1980
1993
|
}
|
|
@@ -2096,9 +2109,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
2096
2109
|
}
|
|
2097
2110
|
}
|
|
2098
2111
|
if (config.policy.smartRules.length > 0) {
|
|
2099
|
-
const
|
|
2112
|
+
const matches = config.policy.smartRules.filter(
|
|
2100
2113
|
(rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
|
|
2101
2114
|
);
|
|
2115
|
+
const matchedRule = resolvePinned(matches);
|
|
2102
2116
|
if (matchedRule) {
|
|
2103
2117
|
if (matchedRule.verdict === "allow")
|
|
2104
2118
|
return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
|
|
@@ -3820,6 +3834,7 @@ export {
|
|
|
3820
3834
|
parseDestHost,
|
|
3821
3835
|
previewArgs,
|
|
3822
3836
|
redactText,
|
|
3837
|
+
resolvePinned,
|
|
3823
3838
|
scanArgs,
|
|
3824
3839
|
scanInjection,
|
|
3825
3840
|
scanText,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/policy-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.47.0",
|
|
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",
|