@node9/policy-engine 2.14.0 → 2.14.1
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 +47 -3
- package/dist/index.d.ts +47 -3
- package/dist/index.js +606 -37
- package/dist/index.mjs +603 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ __export(src_exports, {
|
|
|
52
52
|
LOOP_MAX_RECORDS: () => LOOP_MAX_RECORDS,
|
|
53
53
|
LOOP_THRESHOLD_FOR_WASTE: () => LOOP_THRESHOLD_FOR_WASTE,
|
|
54
54
|
NET_BINARIES: () => NET_BINARIES,
|
|
55
|
+
PATTERN_VERB_NAMES: () => PATTERN_VERB_NAMES,
|
|
55
56
|
PRIVILEGE_ESCALATION_RE: () => PRIVILEGE_ESCALATION_RE,
|
|
56
57
|
REALTIME_PII_PATTERNS: () => REALTIME_PII_PATTERNS,
|
|
57
58
|
SCAN_SIGNAL_WEIGHTS: () => SCAN_SIGNAL_WEIGHTS,
|
|
@@ -88,6 +89,7 @@ __export(src_exports, {
|
|
|
88
89
|
extractSessionLevelFindings: () => extractSessionLevelFindings,
|
|
89
90
|
extractShellDestTokens: () => extractShellDestTokens,
|
|
90
91
|
extractShellDestinations: () => extractShellDestinations,
|
|
92
|
+
fileOperandFlagsOf: () => fileOperandFlagsOf,
|
|
91
93
|
getCompiledRegex: () => getCompiledRegex,
|
|
92
94
|
getNestedValue: () => getNestedValue,
|
|
93
95
|
hostMatches: () => hostMatches,
|
|
@@ -107,6 +109,7 @@ __export(src_exports, {
|
|
|
107
109
|
normalizeIpLiteral: () => normalizeIpLiteral,
|
|
108
110
|
parseAllSshHostsFromCommand: () => parseAllSshHostsFromCommand,
|
|
109
111
|
parseDestHost: () => parseDestHost,
|
|
112
|
+
patternShapeOf: () => patternShapeOf,
|
|
110
113
|
positionedArgs: () => positionedArgs,
|
|
111
114
|
previewArgs: () => previewArgs,
|
|
112
115
|
redactText: () => redactText,
|
|
@@ -1254,6 +1257,19 @@ function parseShared(command) {
|
|
|
1254
1257
|
astCache.set(command, parsed);
|
|
1255
1258
|
return parsed;
|
|
1256
1259
|
}
|
|
1260
|
+
function byteOffsetToCharIndex(command) {
|
|
1261
|
+
if (!/[^\u0000-\u007F]/.test(command)) return null;
|
|
1262
|
+
const map = /* @__PURE__ */ new Map();
|
|
1263
|
+
let byte = 0;
|
|
1264
|
+
for (let i = 0; i < command.length; ) {
|
|
1265
|
+
map.set(byte, i);
|
|
1266
|
+
const cp = command.codePointAt(i);
|
|
1267
|
+
byte += cp < 128 ? 1 : cp < 2048 ? 2 : cp < 65536 ? 3 : 4;
|
|
1268
|
+
i += cp > 65535 ? 2 : 1;
|
|
1269
|
+
}
|
|
1270
|
+
map.set(byte, command.length);
|
|
1271
|
+
return (b) => map.get(b) ?? -1;
|
|
1272
|
+
}
|
|
1257
1273
|
function cachedNormalize(command, compute) {
|
|
1258
1274
|
const hit = normalizeCache.get(command);
|
|
1259
1275
|
if (hit !== void 0) {
|
|
@@ -1283,6 +1299,8 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
1283
1299
|
const f = parseShared(command);
|
|
1284
1300
|
if (f === PARSE_FAIL) return { posix: command, separator: command };
|
|
1285
1301
|
try {
|
|
1302
|
+
const toCharIndex = byteOffsetToCharIndex(command);
|
|
1303
|
+
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
1286
1304
|
const strips = [];
|
|
1287
1305
|
const rewrites = [];
|
|
1288
1306
|
const quoteOnlyRewrites = [];
|
|
@@ -1303,8 +1321,9 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
1303
1321
|
const quotedNode = nextParts[0];
|
|
1304
1322
|
const nt = syntax.NodeType(quotedNode);
|
|
1305
1323
|
const markStrip = () => {
|
|
1306
|
-
const s = next.Pos().Offset();
|
|
1307
|
-
const e = next.End().Offset();
|
|
1324
|
+
const s = at(next.Pos().Offset());
|
|
1325
|
+
const e = at(next.End().Offset());
|
|
1326
|
+
if (s < 0 || e < 0) return;
|
|
1308
1327
|
strips.push([s, e]);
|
|
1309
1328
|
msgSpans.add(`${s}:${e}`);
|
|
1310
1329
|
};
|
|
@@ -1321,8 +1340,9 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
1321
1340
|
}
|
|
1322
1341
|
}
|
|
1323
1342
|
for (const arg of args) {
|
|
1324
|
-
const s = arg.Pos().Offset();
|
|
1325
|
-
const e = arg.End().Offset();
|
|
1343
|
+
const s = at(arg.Pos().Offset());
|
|
1344
|
+
const e = at(arg.End().Offset());
|
|
1345
|
+
if (s < 0 || e < 0) continue;
|
|
1326
1346
|
if (msgSpans.has(`${s}:${e}`)) continue;
|
|
1327
1347
|
const resolved = resolveWordLiteral(arg);
|
|
1328
1348
|
if (resolved === null) continue;
|
|
@@ -1445,7 +1465,347 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1445
1465
|
"nl",
|
|
1446
1466
|
"dd"
|
|
1447
1467
|
]);
|
|
1468
|
+
var GREP_SHAPE = {
|
|
1469
|
+
takesValue: /* @__PURE__ */ new Set([
|
|
1470
|
+
"-A",
|
|
1471
|
+
"-B",
|
|
1472
|
+
"-C",
|
|
1473
|
+
"-D",
|
|
1474
|
+
"-d",
|
|
1475
|
+
"-e",
|
|
1476
|
+
"-f",
|
|
1477
|
+
"-m",
|
|
1478
|
+
"--after-context",
|
|
1479
|
+
"--before-context",
|
|
1480
|
+
"--binary-files",
|
|
1481
|
+
"--context",
|
|
1482
|
+
"--devices",
|
|
1483
|
+
"--directories",
|
|
1484
|
+
"--exclude",
|
|
1485
|
+
"--exclude-dir",
|
|
1486
|
+
"--exclude-from",
|
|
1487
|
+
"--file",
|
|
1488
|
+
"--include",
|
|
1489
|
+
"--label",
|
|
1490
|
+
"--max-count",
|
|
1491
|
+
"--regexp"
|
|
1492
|
+
]),
|
|
1493
|
+
noValue: /* @__PURE__ */ new Set([
|
|
1494
|
+
"-E",
|
|
1495
|
+
"-F",
|
|
1496
|
+
"-G",
|
|
1497
|
+
"-P",
|
|
1498
|
+
"-i",
|
|
1499
|
+
"-y",
|
|
1500
|
+
"-v",
|
|
1501
|
+
"-V",
|
|
1502
|
+
"-w",
|
|
1503
|
+
"-x",
|
|
1504
|
+
"-c",
|
|
1505
|
+
"-l",
|
|
1506
|
+
"-L",
|
|
1507
|
+
"-o",
|
|
1508
|
+
"-q",
|
|
1509
|
+
"-s",
|
|
1510
|
+
"-b",
|
|
1511
|
+
"-H",
|
|
1512
|
+
"-h",
|
|
1513
|
+
"-n",
|
|
1514
|
+
"-T",
|
|
1515
|
+
"-Z",
|
|
1516
|
+
"-z",
|
|
1517
|
+
"-R",
|
|
1518
|
+
"-r",
|
|
1519
|
+
"-U",
|
|
1520
|
+
"-u",
|
|
1521
|
+
"-I",
|
|
1522
|
+
"-a",
|
|
1523
|
+
"--basic-regexp",
|
|
1524
|
+
"--binary",
|
|
1525
|
+
"--byte-offset",
|
|
1526
|
+
"--color",
|
|
1527
|
+
"--colour",
|
|
1528
|
+
"--count",
|
|
1529
|
+
"--dereference-recursive",
|
|
1530
|
+
"--extended-regexp",
|
|
1531
|
+
"--files-with-matches",
|
|
1532
|
+
"--files-without-match",
|
|
1533
|
+
"--fixed-strings",
|
|
1534
|
+
"--help",
|
|
1535
|
+
"--ignore-case",
|
|
1536
|
+
"--initial-tab",
|
|
1537
|
+
"--invert-match",
|
|
1538
|
+
"--line-buffered",
|
|
1539
|
+
"--line-number",
|
|
1540
|
+
"--line-regexp",
|
|
1541
|
+
"--no-filename",
|
|
1542
|
+
"--no-group-separator",
|
|
1543
|
+
"--no-ignore-case",
|
|
1544
|
+
"--no-messages",
|
|
1545
|
+
"--null",
|
|
1546
|
+
"--null-data",
|
|
1547
|
+
"--only-matching",
|
|
1548
|
+
"--perl-regexp",
|
|
1549
|
+
"--quiet",
|
|
1550
|
+
"--recursive",
|
|
1551
|
+
"--silent",
|
|
1552
|
+
"--text",
|
|
1553
|
+
"--version",
|
|
1554
|
+
"--with-filename",
|
|
1555
|
+
"--word-regexp"
|
|
1556
|
+
]),
|
|
1557
|
+
patternFlags: /* @__PURE__ */ new Set(["-e", "--regexp"]),
|
|
1558
|
+
noPatternFlags: /* @__PURE__ */ new Set(["-f", "--file"])
|
|
1559
|
+
};
|
|
1560
|
+
var PATTERN_VERBS = {
|
|
1561
|
+
grep: GREP_SHAPE,
|
|
1562
|
+
// /usr/bin/egrep and /usr/bin/fgrep are 41-byte shell wrappers that exec
|
|
1563
|
+
// `grep -E` and `grep -F`. Read in full, not assumed.
|
|
1564
|
+
egrep: GREP_SHAPE,
|
|
1565
|
+
fgrep: GREP_SHAPE,
|
|
1566
|
+
// ripgrep 14.1.1, complete from its own --help. An earlier comment here said rg
|
|
1567
|
+
// was not installed on the measuring machine; it is, and that stale claim is why
|
|
1568
|
+
// this table shipped incomplete for two rounds, blocking ordinary searches like
|
|
1569
|
+
// `rg --sort-files .env src` (/code-review round 4).
|
|
1570
|
+
rg: {
|
|
1571
|
+
takesValue: /* @__PURE__ */ new Set([
|
|
1572
|
+
"-A",
|
|
1573
|
+
"-B",
|
|
1574
|
+
"-C",
|
|
1575
|
+
"-d",
|
|
1576
|
+
"-E",
|
|
1577
|
+
"-e",
|
|
1578
|
+
"-f",
|
|
1579
|
+
"-g",
|
|
1580
|
+
"-j",
|
|
1581
|
+
"-M",
|
|
1582
|
+
"-m",
|
|
1583
|
+
"-r",
|
|
1584
|
+
"-t",
|
|
1585
|
+
"-T",
|
|
1586
|
+
"--after-context",
|
|
1587
|
+
"--before-context",
|
|
1588
|
+
"--color",
|
|
1589
|
+
"--colors",
|
|
1590
|
+
"--context",
|
|
1591
|
+
"--context-separator",
|
|
1592
|
+
"--dfa-size-limit",
|
|
1593
|
+
"--encoding",
|
|
1594
|
+
"--engine",
|
|
1595
|
+
"--field-context-separator",
|
|
1596
|
+
"--field-match-separator",
|
|
1597
|
+
"--file",
|
|
1598
|
+
"--generate",
|
|
1599
|
+
"--glob",
|
|
1600
|
+
"--hostname-bin",
|
|
1601
|
+
"--hyperlink-format",
|
|
1602
|
+
"--iglob",
|
|
1603
|
+
"--ignore-file",
|
|
1604
|
+
"--max-columns",
|
|
1605
|
+
"--max-count",
|
|
1606
|
+
"--max-depth",
|
|
1607
|
+
// An ALIAS of --max-depth, and it takes a value. The round-5 extraction read
|
|
1608
|
+
// alias lines as plain switches and swept it into noValue, which hard-blocked
|
|
1609
|
+
// `rg --maxdepth 2 .env src` while `--max-depth 2` ran (/code-review round 7).
|
|
1610
|
+
"--maxdepth",
|
|
1611
|
+
"--max-filesize",
|
|
1612
|
+
"--path-separator",
|
|
1613
|
+
"--pre",
|
|
1614
|
+
"--pre-glob",
|
|
1615
|
+
"--regexp",
|
|
1616
|
+
"--regex-size-limit",
|
|
1617
|
+
"--replace",
|
|
1618
|
+
"--sort",
|
|
1619
|
+
"--sortr",
|
|
1620
|
+
"--threads",
|
|
1621
|
+
"--type",
|
|
1622
|
+
"--type-add",
|
|
1623
|
+
"--type-clear",
|
|
1624
|
+
"--type-not"
|
|
1625
|
+
]),
|
|
1626
|
+
noValue: /* @__PURE__ */ new Set([
|
|
1627
|
+
"-.",
|
|
1628
|
+
"-0",
|
|
1629
|
+
"-a",
|
|
1630
|
+
"-b",
|
|
1631
|
+
"-c",
|
|
1632
|
+
"-F",
|
|
1633
|
+
"-h",
|
|
1634
|
+
"-H",
|
|
1635
|
+
"-i",
|
|
1636
|
+
"-I",
|
|
1637
|
+
"-l",
|
|
1638
|
+
"-L",
|
|
1639
|
+
"-n",
|
|
1640
|
+
"-N",
|
|
1641
|
+
"-o",
|
|
1642
|
+
"-p",
|
|
1643
|
+
"-P",
|
|
1644
|
+
"-q",
|
|
1645
|
+
"-s",
|
|
1646
|
+
"-S",
|
|
1647
|
+
"-u",
|
|
1648
|
+
"-U",
|
|
1649
|
+
"-v",
|
|
1650
|
+
"-V",
|
|
1651
|
+
"-w",
|
|
1652
|
+
"-x",
|
|
1653
|
+
"-z",
|
|
1654
|
+
"--auto-hybrid-regex",
|
|
1655
|
+
"--binary",
|
|
1656
|
+
"--block-buffered",
|
|
1657
|
+
"--byte-offset",
|
|
1658
|
+
"--case-sensitive",
|
|
1659
|
+
"--column",
|
|
1660
|
+
"--count",
|
|
1661
|
+
"--count-matches",
|
|
1662
|
+
"--crlf",
|
|
1663
|
+
"--debug",
|
|
1664
|
+
"--files",
|
|
1665
|
+
"--files-with-matches",
|
|
1666
|
+
"--files-without-match",
|
|
1667
|
+
"--fixed-strings",
|
|
1668
|
+
"--follow",
|
|
1669
|
+
"--glob-case-insensitive",
|
|
1670
|
+
"--heading",
|
|
1671
|
+
"--help",
|
|
1672
|
+
"--hidden",
|
|
1673
|
+
"--ignore-case",
|
|
1674
|
+
"--ignore-file-case-insensitive",
|
|
1675
|
+
"--include-zero",
|
|
1676
|
+
"--invert-match",
|
|
1677
|
+
"--json",
|
|
1678
|
+
"--line-buffered",
|
|
1679
|
+
"--line-number",
|
|
1680
|
+
"--line-regexp",
|
|
1681
|
+
"--max-columns-preview",
|
|
1682
|
+
"--mmap",
|
|
1683
|
+
"--multiline",
|
|
1684
|
+
"--multiline-dotall",
|
|
1685
|
+
"--no-column",
|
|
1686
|
+
"--no-config",
|
|
1687
|
+
"--no-context-separator",
|
|
1688
|
+
"--no-encoding",
|
|
1689
|
+
"--no-filename",
|
|
1690
|
+
"--no-ignore",
|
|
1691
|
+
"--no-ignore-dot",
|
|
1692
|
+
"--no-ignore-exclude",
|
|
1693
|
+
"--no-ignore-files",
|
|
1694
|
+
"--no-ignore-global",
|
|
1695
|
+
"--no-ignore-messages",
|
|
1696
|
+
"--no-ignore-parent",
|
|
1697
|
+
"--no-ignore-vcs",
|
|
1698
|
+
"--no-line-number",
|
|
1699
|
+
"--no-messages",
|
|
1700
|
+
"--no-pcre2-unicode",
|
|
1701
|
+
"--no-pre",
|
|
1702
|
+
"--no-require-git",
|
|
1703
|
+
"--no-unicode",
|
|
1704
|
+
"--null",
|
|
1705
|
+
"--null-data",
|
|
1706
|
+
"--one-file-system",
|
|
1707
|
+
"--only-matching",
|
|
1708
|
+
"--passthru",
|
|
1709
|
+
"--pcre2",
|
|
1710
|
+
"--pcre2-version",
|
|
1711
|
+
"--pretty",
|
|
1712
|
+
"--print0",
|
|
1713
|
+
"--quiet",
|
|
1714
|
+
"--search-zip",
|
|
1715
|
+
"--smart-case",
|
|
1716
|
+
"--sort-files",
|
|
1717
|
+
"--stats",
|
|
1718
|
+
"--stop-on-nonmatch",
|
|
1719
|
+
"--text",
|
|
1720
|
+
"--trace",
|
|
1721
|
+
"--trim",
|
|
1722
|
+
"--type-list",
|
|
1723
|
+
"--unrestricted",
|
|
1724
|
+
"--version",
|
|
1725
|
+
"--vimgrep",
|
|
1726
|
+
"--with-filename",
|
|
1727
|
+
"--word-regexp",
|
|
1728
|
+
// The COMPLETE remainder of `rg --help`, added after /code-review round 5
|
|
1729
|
+
// found 40 documented switches in neither set. The table is now the whole
|
|
1730
|
+
// option list: `rg --help` yields 149 long options, 35 of them value-taking.
|
|
1731
|
+
"--ignore",
|
|
1732
|
+
"--ignore-dot",
|
|
1733
|
+
"--ignore-exclude",
|
|
1734
|
+
"--ignore-files",
|
|
1735
|
+
"--ignore-global",
|
|
1736
|
+
"--ignore-messages",
|
|
1737
|
+
"--ignore-parent",
|
|
1738
|
+
"--ignore-vcs",
|
|
1739
|
+
"--messages",
|
|
1740
|
+
"--no-auto-hybrid-regex",
|
|
1741
|
+
"--no-binary",
|
|
1742
|
+
"--no-block-buffered",
|
|
1743
|
+
"--no-byte-offset",
|
|
1744
|
+
"--no-crlf",
|
|
1745
|
+
"--no-fixed-strings",
|
|
1746
|
+
"--no-follow",
|
|
1747
|
+
"--no-glob-case-insensitive",
|
|
1748
|
+
"--no-heading",
|
|
1749
|
+
"--no-hidden",
|
|
1750
|
+
"--no-ignore-file-case-insensitive",
|
|
1751
|
+
"--no-include-zero",
|
|
1752
|
+
"--no-invert-match",
|
|
1753
|
+
"--no-json",
|
|
1754
|
+
"--no-line-buffered",
|
|
1755
|
+
"--no-max-columns-preview",
|
|
1756
|
+
"--no-mmap",
|
|
1757
|
+
"--no-multiline",
|
|
1758
|
+
"--no-multiline-dotall",
|
|
1759
|
+
"--no-one-file-system",
|
|
1760
|
+
"--no-pcre2",
|
|
1761
|
+
"--no-search-zip",
|
|
1762
|
+
"--no-sort-files",
|
|
1763
|
+
"--no-stats",
|
|
1764
|
+
"--no-text",
|
|
1765
|
+
"--no-trim",
|
|
1766
|
+
"--passthrough",
|
|
1767
|
+
"--pcre2-unicode",
|
|
1768
|
+
"--require-git",
|
|
1769
|
+
"--unicode"
|
|
1770
|
+
]),
|
|
1771
|
+
patternFlags: /* @__PURE__ */ new Set(["-e", "--regexp"]),
|
|
1772
|
+
// `--files` and `--type-list` list or enumerate without a pattern. Founder
|
|
1773
|
+
// decision 2026-09-13: `rg --files ~/.ssh` stays BLOCKED, which this achieves
|
|
1774
|
+
// by leaving the directory in the judged list.
|
|
1775
|
+
noPatternFlags: /* @__PURE__ */ new Set(["-f", "--file", "--files", "--type-list", "--pcre2-version"])
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
var PATTERN_VERB_NAMES = Object.keys(PATTERN_VERBS);
|
|
1779
|
+
var patternShapeOf = (verb) => PATTERN_VERBS[verb];
|
|
1780
|
+
var fileOperandFlagsOf = (verb) => FILE_OPERAND_FLAGS[verb];
|
|
1781
|
+
var GREP_FILE_OPERANDS = /* @__PURE__ */ new Set(["-f", "--file", "--exclude-from", "--include"]);
|
|
1782
|
+
var READER_VALUE_LETTERS = {
|
|
1783
|
+
awk: ["F", "v", "f"],
|
|
1784
|
+
gawk: ["F", "v", "f", "e", "E", "i", "l", "o", "p", "D"],
|
|
1785
|
+
sed: ["e", "f", "i", "l"],
|
|
1786
|
+
sort: ["C", "k", "o", "S", "t", "T"]
|
|
1787
|
+
};
|
|
1788
|
+
var FILE_OPERAND_FLAGS = {
|
|
1789
|
+
grep: GREP_FILE_OPERANDS,
|
|
1790
|
+
egrep: GREP_FILE_OPERANDS,
|
|
1791
|
+
fgrep: GREP_FILE_OPERANDS,
|
|
1792
|
+
// rg and gawk are NOT installed on the measuring machine: these two rows are
|
|
1793
|
+
// from the shipped documentation (ripgrep `-f/--file`, `--ignore-file`; gawk
|
|
1794
|
+
// `-f/--file`) and are marked as such in the spec.
|
|
1795
|
+
// `-g/--glob/--iglob` name which files ripgrep SEARCHES, so an operand naming
|
|
1796
|
+
// a credential makes rg open it -- the same reason grep's `--include` is here.
|
|
1797
|
+
// Measured (/code-review round 3): `rg --hidden -g .env AWS tree` opened .env
|
|
1798
|
+
// and printed its contents.
|
|
1799
|
+
rg: /* @__PURE__ */ new Set(["-f", "--file", "--ignore-file", "-g", "--glob", "--iglob"]),
|
|
1800
|
+
sed: /* @__PURE__ */ new Set(["-f", "--file"]),
|
|
1801
|
+
awk: /* @__PURE__ */ new Set(["-f", "--file"]),
|
|
1802
|
+
gawk: /* @__PURE__ */ new Set(["-f", "--file"]),
|
|
1803
|
+
sort: /* @__PURE__ */ new Set(["--files0-from"])
|
|
1804
|
+
};
|
|
1448
1805
|
var SCP_VALUE_FLAGS = ["i", "F", "o", "c", "S", "P", "J", "D", "W", "l"];
|
|
1806
|
+
var TAR_VALUE_LETTERS = ["b", "C", "f", "F", "g", "H", "I", "K", "L", "N", "T", "V", "X"];
|
|
1807
|
+
var ZIP_VALUE_LETTERS = ["b", "n", "P", "t", "s", "O", "x", "i"];
|
|
1808
|
+
var RSYNC_VALUE_LETTERS = ["e", "f", "T", "B", "M"];
|
|
1449
1809
|
var RSYNC_SKIP = [
|
|
1450
1810
|
"e",
|
|
1451
1811
|
"--rsh",
|
|
@@ -1457,21 +1817,35 @@ var RSYNC_SKIP = [
|
|
|
1457
1817
|
"f",
|
|
1458
1818
|
"--filter"
|
|
1459
1819
|
];
|
|
1820
|
+
var CP_VALUE_LETTERS = ["S", "t"];
|
|
1821
|
+
var INSTALL_VALUE_LETTERS = ["S", "t", "g", "m", "o"];
|
|
1460
1822
|
var COPY_VERBS = {
|
|
1461
|
-
cp: { source: "allButLast", targetDirFlag: true },
|
|
1462
|
-
mv: { source: "allButLast", targetDirFlag: true },
|
|
1463
|
-
install: { source: "allButLast", targetDirFlag: true },
|
|
1464
|
-
ln: { source: "first", targetDirFlag: true },
|
|
1465
|
-
scp: { source: "allButLast", skipFlags: SCP_VALUE_FLAGS },
|
|
1466
|
-
rsync: { source: "allButLast", skipFlags: RSYNC_SKIP },
|
|
1823
|
+
cp: { source: "allButLast", targetDirFlag: true, valueLetters: CP_VALUE_LETTERS },
|
|
1824
|
+
mv: { source: "allButLast", targetDirFlag: true, valueLetters: CP_VALUE_LETTERS },
|
|
1825
|
+
install: { source: "allButLast", targetDirFlag: true, valueLetters: INSTALL_VALUE_LETTERS },
|
|
1826
|
+
ln: { source: "first", targetDirFlag: true, valueLetters: CP_VALUE_LETTERS },
|
|
1827
|
+
scp: { source: "allButLast", skipFlags: SCP_VALUE_FLAGS, valueLetters: SCP_VALUE_FLAGS },
|
|
1828
|
+
rsync: { source: "allButLast", skipFlags: RSYNC_SKIP, valueLetters: RSYNC_VALUE_LETTERS },
|
|
1467
1829
|
tar: {
|
|
1468
1830
|
source: "archive",
|
|
1469
1831
|
archive: "tar",
|
|
1470
|
-
skipFlags: ["f", "X", "T", "--file", "--exclude", "--exclude-from", "--files-from"]
|
|
1832
|
+
skipFlags: ["f", "X", "T", "--file", "--exclude", "--exclude-from", "--files-from"],
|
|
1833
|
+
valueLetters: TAR_VALUE_LETTERS
|
|
1834
|
+
},
|
|
1835
|
+
zip: {
|
|
1836
|
+
source: "archive",
|
|
1837
|
+
archive: "zip",
|
|
1838
|
+
skipFlags: ["x", "i", "--exclude", "--include"],
|
|
1839
|
+
valueLetters: ZIP_VALUE_LETTERS
|
|
1471
1840
|
},
|
|
1472
|
-
zip: { source: "archive", archive: "zip", skipFlags: ["x", "i", "--exclude", "--include"] },
|
|
1473
1841
|
ar: { source: "archive", archive: "ar" },
|
|
1474
|
-
|
|
1842
|
+
// 7z switches are INLINE only (`-mx9`, `-px`, `-x!pat`), so no following word is
|
|
1843
|
+
// ever a switch's operand -- which is why the short `x` is NOT a skipFlag here
|
|
1844
|
+
// and valueLetters is empty. Keeping the short `x` made `7z a out.7z -mx KEY`
|
|
1845
|
+
// drop the credential as an exclusion operand (/code-review round 8), and the
|
|
1846
|
+
// derived "every skipped letter is a value letter" row in jail-copy.spec.ts is
|
|
1847
|
+
// what keeps the two tables honest about it.
|
|
1848
|
+
"7z": { source: "archive", archive: "7z", skipFlags: ["--exclude"], valueLetters: [] },
|
|
1475
1849
|
gzip: { source: "all" },
|
|
1476
1850
|
bzip2: { source: "all" },
|
|
1477
1851
|
xz: { source: "all" },
|
|
@@ -2331,7 +2705,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
2331
2705
|
return result?.verdict !== "block";
|
|
2332
2706
|
}
|
|
2333
2707
|
if (nodeType !== "CallExpr") return true;
|
|
2334
|
-
const { name, flags, paths, words } = extractLiteralArgs(n);
|
|
2708
|
+
const { name, flags, paths, words, args } = extractLiteralArgs(n);
|
|
2335
2709
|
if (!name) return true;
|
|
2336
2710
|
if (name === "rm") {
|
|
2337
2711
|
const flagStr = flags.join("").toLowerCase();
|
|
@@ -2371,7 +2745,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
2371
2745
|
return true;
|
|
2372
2746
|
}
|
|
2373
2747
|
}
|
|
2374
|
-
const readPaths = FS_READ_TOOLS.has(name) ?
|
|
2748
|
+
const readPaths = FS_READ_TOOLS.has(name) ? [...readTargets(name, args, flags, words, 1), ...flagOperandFiles(name, words, 1)] : wrappedReadPaths(words, name);
|
|
2375
2749
|
if (readPaths) {
|
|
2376
2750
|
for (const p of readPaths) {
|
|
2377
2751
|
result = stricter(result, matchSensitivePath2(p));
|
|
@@ -2407,9 +2781,22 @@ function flagIs(w, names) {
|
|
|
2407
2781
|
const f = flagInfo(w);
|
|
2408
2782
|
return names.some((n) => n.startsWith("--") ? f.long === n : f.letter === n);
|
|
2409
2783
|
}
|
|
2410
|
-
function operandOf(a, names) {
|
|
2784
|
+
function operandOf(a, names, valueLetters) {
|
|
2411
2785
|
if (!names || a.afterFlag === null) return false;
|
|
2412
|
-
|
|
2786
|
+
const w = a.afterFlag;
|
|
2787
|
+
if (!w.startsWith("--") && valueLetters) {
|
|
2788
|
+
const letters = w.slice(1);
|
|
2789
|
+
for (let i = 0; i < letters.length; i++) {
|
|
2790
|
+
if (!valueLetters.includes(letters[i])) continue;
|
|
2791
|
+
return i === letters.length - 1 && names.includes(letters[i]);
|
|
2792
|
+
}
|
|
2793
|
+
return false;
|
|
2794
|
+
}
|
|
2795
|
+
if (w.startsWith("--") && !w.includes("=")) {
|
|
2796
|
+
const longs = names.filter((n) => n.startsWith("--"));
|
|
2797
|
+
if (longs.some((n) => n === w || w.length >= 3 && n.startsWith(w))) return true;
|
|
2798
|
+
}
|
|
2799
|
+
return flagIs(w, names) && flagInfo(w).attached === null;
|
|
2413
2800
|
}
|
|
2414
2801
|
function resolveCopyShape(words, h) {
|
|
2415
2802
|
const verb = baseWord(words[h]);
|
|
@@ -2434,7 +2821,7 @@ function findStartPoints(words, h) {
|
|
|
2434
2821
|
const k = words.findIndex((w, i) => i > h && w !== null && FIND_EXEC_FLAGS.has(w));
|
|
2435
2822
|
if (k < 0) return { k, starts: [] };
|
|
2436
2823
|
const firstPredicate = words.findIndex(
|
|
2437
|
-
(w, i) => i > h && w !== null && w.startsWith("-") && !FIND_OPTIONS.has(w)
|
|
2824
|
+
(w, i) => i > h && w !== null && w !== "--" && w.startsWith("-") && !FIND_OPTIONS.has(w)
|
|
2438
2825
|
);
|
|
2439
2826
|
const end = firstPredicate > h ? firstPredicate : k;
|
|
2440
2827
|
return { k, starts: positionalAfter(words, h + 1, end) };
|
|
@@ -2454,14 +2841,35 @@ function copySourcePaths(words) {
|
|
|
2454
2841
|
const { shape, last } = r;
|
|
2455
2842
|
const args = positionedArgs(words, last + 1);
|
|
2456
2843
|
const tail = words.slice(last + 1);
|
|
2457
|
-
const
|
|
2458
|
-
const
|
|
2459
|
-
const
|
|
2844
|
+
const copyOptionsEnd = tail.findIndex((w) => w === "--");
|
|
2845
|
+
const copyPastOptions = (a) => copyOptionsEnd >= 0 && a.argv > last + 1 + copyOptionsEnd;
|
|
2846
|
+
const skipped = (a) => !copyPastOptions(a) && operandOf(a, shape.skipFlags, shape.valueLetters);
|
|
2847
|
+
const firstValueLetter = (w) => {
|
|
2848
|
+
const letters = w.slice(1);
|
|
2849
|
+
for (let i = 0; i < letters.length; i++) {
|
|
2850
|
+
if ((shape.valueLetters ?? []).includes(letters[i]))
|
|
2851
|
+
return { letter: letters[i], last: i === letters.length - 1 };
|
|
2852
|
+
}
|
|
2853
|
+
return null;
|
|
2854
|
+
};
|
|
2855
|
+
const isTargetDirFlag = (w) => {
|
|
2856
|
+
if (w.startsWith("--")) {
|
|
2857
|
+
const name = w.includes("=") ? w.slice(0, w.indexOf("=")) : w;
|
|
2858
|
+
return name.length >= 3 && "--target-directory".startsWith(name);
|
|
2859
|
+
}
|
|
2860
|
+
return firstValueLetter(w)?.letter === "t";
|
|
2861
|
+
};
|
|
2862
|
+
const targetDir = shape.targetDirFlag === true && tail.some((w) => w !== null && w.startsWith("-") && isTargetDirFlag(w));
|
|
2863
|
+
const targetTakesNextWord = (w) => {
|
|
2864
|
+
if (w.startsWith("--"))
|
|
2865
|
+
return !w.includes("=") && w.length >= 3 && "--target-directory".startsWith(w);
|
|
2866
|
+
const f = firstValueLetter(w);
|
|
2867
|
+
return f !== null && f.letter === "t" && f.last;
|
|
2868
|
+
};
|
|
2869
|
+
const targetOperand = (a) => targetDir && a.afterFlag !== null && !copyPastOptions(a) && targetTakesNextWord(a.afterFlag);
|
|
2460
2870
|
const lastOperand = [...tail].reverse().find((w) => w === null || !w.startsWith("-"));
|
|
2461
2871
|
const dynamicDest = lastOperand === null;
|
|
2462
2872
|
const destIsLastOperand = (shape.source === "allButLast" || shape.source === "first") && !targetDir && !dynamicDest;
|
|
2463
|
-
if (destIsLastOperand && typeof lastOperand === "string" && matchSensitivePath2(lastOperand))
|
|
2464
|
-
return [];
|
|
2465
2873
|
let src;
|
|
2466
2874
|
switch (shape.source) {
|
|
2467
2875
|
case "all":
|
|
@@ -2471,11 +2879,17 @@ function copySourcePaths(words) {
|
|
|
2471
2879
|
src = targetDir ? args : args.slice(0, 1);
|
|
2472
2880
|
break;
|
|
2473
2881
|
case "flagOperand": {
|
|
2474
|
-
const
|
|
2882
|
+
const namesSource = (f) => {
|
|
2883
|
+
const names = shape.sourceFlags ?? [];
|
|
2884
|
+
if (f.long !== null)
|
|
2885
|
+
return names.some(
|
|
2886
|
+
(n) => n.startsWith("--") && (n === f.long || f.long.length >= 3 && n.startsWith(f.long))
|
|
2887
|
+
);
|
|
2888
|
+
return f.letter !== null && names.includes(f.letter);
|
|
2889
|
+
};
|
|
2890
|
+
const inline = tail.filter((w) => w !== null && w.startsWith("-")).map((w) => flagInfo(w)).filter((f) => f.attached !== null && namesSource(f)).map((f) => f.attached);
|
|
2475
2891
|
return [
|
|
2476
|
-
...args.filter(
|
|
2477
|
-
(a) => flagIs(a.afterFlag, shape.sourceFlags ?? []) && flagInfo(a.afterFlag).attached === null
|
|
2478
|
-
).map((a) => a.value),
|
|
2892
|
+
...args.filter((a) => !copyPastOptions(a) && operandOf(a, shape.sourceFlags, shape.valueLetters)).map((a) => a.value),
|
|
2479
2893
|
...inline
|
|
2480
2894
|
];
|
|
2481
2895
|
}
|
|
@@ -2486,7 +2900,14 @@ function copySourcePaths(words) {
|
|
|
2486
2900
|
src = targetDir || dynamicDest ? args : args.slice(0, -1);
|
|
2487
2901
|
break;
|
|
2488
2902
|
}
|
|
2489
|
-
|
|
2903
|
+
const sources = src.filter((a) => !skipped(a) && !targetOperand(a)).map((a) => a.value);
|
|
2904
|
+
if (destIsLastOperand && typeof lastOperand === "string" && matchSensitivePath2(lastOperand)) {
|
|
2905
|
+
const jailedSources = sources.filter((p) => matchSensitivePath2(p));
|
|
2906
|
+
const dirOf = (p) => /[\\/]/.test(p) ? p.replace(/[\\/][^\\/]*$/, "") : "";
|
|
2907
|
+
if (jailedSources.length === 0) return [];
|
|
2908
|
+
if (jailedSources.every((p) => dirOf(p) === dirOf(lastOperand))) return [];
|
|
2909
|
+
}
|
|
2910
|
+
return sources;
|
|
2490
2911
|
}
|
|
2491
2912
|
function archiveInputs(kind, args, tail) {
|
|
2492
2913
|
const first = args[0];
|
|
@@ -2498,13 +2919,17 @@ function archiveInputs(kind, args, tail) {
|
|
|
2498
2919
|
const writing = /[cruA]/.test(bareKey ? first.value : "") || /(^|\s)-[a-zA-Z]*[cruA]|--create|--append|--update|--concatenate/.test(flagsText);
|
|
2499
2920
|
if (extracting && !writing) return [];
|
|
2500
2921
|
void mode;
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
if (
|
|
2922
|
+
if (!bareKey) return args;
|
|
2923
|
+
let i = 1;
|
|
2924
|
+
const fromDirs = [];
|
|
2925
|
+
for (const ch of first.value) {
|
|
2926
|
+
if (!TAR_VALUE_LETTERS.includes(ch)) continue;
|
|
2927
|
+
const operand = args[i];
|
|
2928
|
+
if (!operand || operand.afterFlag !== null) break;
|
|
2929
|
+
i += 1;
|
|
2930
|
+
if (ch === "C") fromDirs.push(operand);
|
|
2506
2931
|
}
|
|
2507
|
-
return args.slice(i);
|
|
2932
|
+
return [...fromDirs, ...args.slice(i)];
|
|
2508
2933
|
}
|
|
2509
2934
|
if (kind === "zip") return first && first.afterFlag === "-" ? args : args.slice(1);
|
|
2510
2935
|
if (kind === "ar") return bareKey ? args.slice(2) : args.slice(1);
|
|
@@ -2556,6 +2981,140 @@ function baseWord(w) {
|
|
|
2556
2981
|
}
|
|
2557
2982
|
var isReaderWord = (w) => w !== null && FS_READ_TOOLS.has(baseWord(w));
|
|
2558
2983
|
var positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
|
|
2984
|
+
function namesFlag(name, candidates, known) {
|
|
2985
|
+
if (candidates.has(name)) return true;
|
|
2986
|
+
if (!name.startsWith("--") || name.length < 3) return false;
|
|
2987
|
+
if (known?.has(name)) return false;
|
|
2988
|
+
for (const c of candidates) if (c.startsWith(name)) return true;
|
|
2989
|
+
return false;
|
|
2990
|
+
}
|
|
2991
|
+
function knownLongFlags(verb) {
|
|
2992
|
+
const out = /* @__PURE__ */ new Set();
|
|
2993
|
+
const shape = PATTERN_VERBS[verb];
|
|
2994
|
+
if (shape) {
|
|
2995
|
+
for (const set of [shape.takesValue, shape.noValue, shape.patternFlags, shape.noPatternFlags])
|
|
2996
|
+
for (const f of set) if (f.startsWith("--")) out.add(f);
|
|
2997
|
+
}
|
|
2998
|
+
for (const f of FILE_OPERAND_FLAGS[verb] ?? []) if (f.startsWith("--")) out.add(f);
|
|
2999
|
+
return out;
|
|
3000
|
+
}
|
|
3001
|
+
function flagNamesOf(token, shape) {
|
|
3002
|
+
if (token.startsWith("--")) {
|
|
3003
|
+
const eq = token.indexOf("=");
|
|
3004
|
+
return [eq > 0 ? token.slice(0, eq) : token];
|
|
3005
|
+
}
|
|
3006
|
+
const out = [];
|
|
3007
|
+
for (const c of token.slice(1)) {
|
|
3008
|
+
const name = `-${c}`;
|
|
3009
|
+
out.push(name);
|
|
3010
|
+
if (shape?.takesValue.has(name)) break;
|
|
3011
|
+
}
|
|
3012
|
+
return out;
|
|
3013
|
+
}
|
|
3014
|
+
var NONE = { kind: "none" };
|
|
3015
|
+
var UNKNOWN = { kind: "unknown" };
|
|
3016
|
+
function flagEffect(token, shape, known) {
|
|
3017
|
+
if (token === "--") return NONE;
|
|
3018
|
+
if (/^-+$/.test(token)) return UNKNOWN;
|
|
3019
|
+
if (/^-\d+$/.test(token)) return NONE;
|
|
3020
|
+
if (token.startsWith("--")) {
|
|
3021
|
+
if (token.includes("=")) return NONE;
|
|
3022
|
+
const takes = namesFlag(token, shape.takesValue, known);
|
|
3023
|
+
const none = namesFlag(token, shape.noValue, known);
|
|
3024
|
+
if (takes && none) return UNKNOWN;
|
|
3025
|
+
if (takes) return { kind: "takes", flag: token };
|
|
3026
|
+
if (none) return NONE;
|
|
3027
|
+
return UNKNOWN;
|
|
3028
|
+
}
|
|
3029
|
+
const letters = token.slice(1);
|
|
3030
|
+
if (!letters) return NONE;
|
|
3031
|
+
for (let i = 0; i < letters.length; i++) {
|
|
3032
|
+
const name = `-${letters[i]}`;
|
|
3033
|
+
if (shape.takesValue.has(name)) {
|
|
3034
|
+
return i === letters.length - 1 ? { kind: "takes", flag: name } : NONE;
|
|
3035
|
+
}
|
|
3036
|
+
if (!shape.noValue.has(name)) return UNKNOWN;
|
|
3037
|
+
}
|
|
3038
|
+
return NONE;
|
|
3039
|
+
}
|
|
3040
|
+
function readTargets(verb, args, flags, words = [], from = 1) {
|
|
3041
|
+
const shape = PATTERN_VERBS[verb];
|
|
3042
|
+
if (!shape) return args.map((a) => a.value);
|
|
3043
|
+
const known = knownLongFlags(verb);
|
|
3044
|
+
const names = flags.flatMap((f) => flagNamesOf(f, shape));
|
|
3045
|
+
const patternElsewhere = names.some(
|
|
3046
|
+
(n) => namesFlag(n, shape.patternFlags, known) || namesFlag(n, shape.noPatternFlags, known)
|
|
3047
|
+
);
|
|
3048
|
+
const excused = /* @__PURE__ */ new Set();
|
|
3049
|
+
const fileFlags = FILE_OPERAND_FLAGS[verb];
|
|
3050
|
+
const optionsEnd = words.findIndex((w, i) => i >= from && w === "--");
|
|
3051
|
+
const pastOptions = (a) => optionsEnd >= 0 && a.argv > optionsEnd;
|
|
3052
|
+
for (const a of args) {
|
|
3053
|
+
if (a.afterFlag === null || pastOptions(a)) continue;
|
|
3054
|
+
const e = flagEffect(a.afterFlag, shape, known);
|
|
3055
|
+
if (e.kind !== "takes") continue;
|
|
3056
|
+
if (fileFlags && namesFlag(e.flag, fileFlags, known)) continue;
|
|
3057
|
+
excused.add(a);
|
|
3058
|
+
}
|
|
3059
|
+
const patternArgv = (() => {
|
|
3060
|
+
for (let i = from; i < words.length; i++) {
|
|
3061
|
+
const w = words[i];
|
|
3062
|
+
if (w === null) return -1;
|
|
3063
|
+
if (w === "--") return i + 1;
|
|
3064
|
+
if (w.startsWith("-")) {
|
|
3065
|
+
const e = flagEffect(w, shape, known);
|
|
3066
|
+
if (e.kind === "takes") i += 1;
|
|
3067
|
+
else if (e.kind === "unknown") return -1;
|
|
3068
|
+
continue;
|
|
3069
|
+
}
|
|
3070
|
+
return i;
|
|
3071
|
+
}
|
|
3072
|
+
return -1;
|
|
3073
|
+
})();
|
|
3074
|
+
if (!patternElsewhere && patternArgv >= 0) {
|
|
3075
|
+
const a = args.find((x) => x.argv === patternArgv);
|
|
3076
|
+
if (a) excused.add(a);
|
|
3077
|
+
}
|
|
3078
|
+
return args.filter((a) => !excused.has(a)).map((a) => a.value);
|
|
3079
|
+
}
|
|
3080
|
+
function flagOperandFiles(verb, words, from) {
|
|
3081
|
+
const flags = FILE_OPERAND_FLAGS[verb];
|
|
3082
|
+
if (!flags) return [];
|
|
3083
|
+
const known = knownLongFlags(verb);
|
|
3084
|
+
const out = [];
|
|
3085
|
+
for (let i = from; i < words.length; i++) {
|
|
3086
|
+
const w = words[i];
|
|
3087
|
+
if (w === null || !w.startsWith("-") || w === "--") continue;
|
|
3088
|
+
if (w.startsWith("--")) {
|
|
3089
|
+
const eq = w.indexOf("=");
|
|
3090
|
+
if (eq <= 0) continue;
|
|
3091
|
+
const name = w.slice(0, eq);
|
|
3092
|
+
const value = w.slice(eq + 1);
|
|
3093
|
+
if (!value) continue;
|
|
3094
|
+
if (namesFlag(name, flags, known)) {
|
|
3095
|
+
out.push(value);
|
|
3096
|
+
continue;
|
|
3097
|
+
}
|
|
3098
|
+
const shape2 = PATTERN_VERBS[verb];
|
|
3099
|
+
if (!shape2) continue;
|
|
3100
|
+
const recognised = namesFlag(name, shape2.takesValue, known) || namesFlag(name, shape2.noValue, known) || namesFlag(name, shape2.patternFlags, known) || namesFlag(name, shape2.noPatternFlags, known);
|
|
3101
|
+
if (!recognised) out.push(value);
|
|
3102
|
+
continue;
|
|
3103
|
+
}
|
|
3104
|
+
const shape = PATTERN_VERBS[verb];
|
|
3105
|
+
const letters = w.slice(1);
|
|
3106
|
+
for (let j = 0; j < letters.length; j++) {
|
|
3107
|
+
const name = `-${letters[j]}`;
|
|
3108
|
+
const isFileFlag = flags.has(name);
|
|
3109
|
+
const argTaking = isFileFlag || (shape?.takesValue.has(name) ?? false) || (READER_VALUE_LETTERS[verb] ?? []).includes(letters[j]);
|
|
3110
|
+
if (!argTaking) continue;
|
|
3111
|
+
const attached = letters.slice(j + 1);
|
|
3112
|
+
if (isFileFlag && attached) out.push(attached);
|
|
3113
|
+
break;
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
return out;
|
|
3117
|
+
}
|
|
2559
3118
|
function wrappedReadPaths(words, name) {
|
|
2560
3119
|
if (name === "find") {
|
|
2561
3120
|
const { k, starts } = findStartPoints(words, 0);
|
|
@@ -2563,7 +3122,14 @@ function wrappedReadPaths(words, name) {
|
|
|
2563
3122
|
}
|
|
2564
3123
|
if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
|
|
2565
3124
|
const h = unwrapCommandHead(words);
|
|
2566
|
-
|
|
3125
|
+
if (h <= 0 || !isReaderWord(words[h] ?? null)) return null;
|
|
3126
|
+
const head = baseWord(words[h]);
|
|
3127
|
+
const rest = words.slice(h + 1);
|
|
3128
|
+
const restFlags = rest.filter((w) => w !== null && w.startsWith("-"));
|
|
3129
|
+
return [
|
|
3130
|
+
...readTargets(head, positionedArgs(words, h + 1), restFlags, words, h + 1),
|
|
3131
|
+
...flagOperandFiles(head, words, h + 1)
|
|
3132
|
+
];
|
|
2567
3133
|
}
|
|
2568
3134
|
function literalShellPayload(words, name) {
|
|
2569
3135
|
const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
|
|
@@ -5083,8 +5649,8 @@ function matchCanaryArgs(args, values) {
|
|
|
5083
5649
|
|
|
5084
5650
|
// src/scan/canonical.ts
|
|
5085
5651
|
var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
5086
|
-
var CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
5087
|
-
var CANONICAL_EXTRACTOR_HASH = "
|
|
5652
|
+
var CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
|
|
5653
|
+
var CANONICAL_EXTRACTOR_HASH = "5dad4c8f07121f6c";
|
|
5088
5654
|
var DEDUPE_PREVIEW_LEN = 120;
|
|
5089
5655
|
function extractCanonicalFindings(call, ctx) {
|
|
5090
5656
|
const out = [];
|
|
@@ -5476,6 +6042,7 @@ var ENGINE_VERSION = "1.4.0";
|
|
|
5476
6042
|
LOOP_MAX_RECORDS,
|
|
5477
6043
|
LOOP_THRESHOLD_FOR_WASTE,
|
|
5478
6044
|
NET_BINARIES,
|
|
6045
|
+
PATTERN_VERB_NAMES,
|
|
5479
6046
|
PRIVILEGE_ESCALATION_RE,
|
|
5480
6047
|
REALTIME_PII_PATTERNS,
|
|
5481
6048
|
SCAN_SIGNAL_WEIGHTS,
|
|
@@ -5512,6 +6079,7 @@ var ENGINE_VERSION = "1.4.0";
|
|
|
5512
6079
|
extractSessionLevelFindings,
|
|
5513
6080
|
extractShellDestTokens,
|
|
5514
6081
|
extractShellDestinations,
|
|
6082
|
+
fileOperandFlagsOf,
|
|
5515
6083
|
getCompiledRegex,
|
|
5516
6084
|
getNestedValue,
|
|
5517
6085
|
hostMatches,
|
|
@@ -5531,6 +6099,7 @@ var ENGINE_VERSION = "1.4.0";
|
|
|
5531
6099
|
normalizeIpLiteral,
|
|
5532
6100
|
parseAllSshHostsFromCommand,
|
|
5533
6101
|
parseDestHost,
|
|
6102
|
+
patternShapeOf,
|
|
5534
6103
|
positionedArgs,
|
|
5535
6104
|
previewArgs,
|
|
5536
6105
|
redactText,
|