@node9/proxy 2.13.0 → 2.14.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/README.md +1 -1
- package/dist/cli.js +763 -521
- package/dist/cli.mjs +763 -521
- package/dist/dashboard.mjs +2806 -2331
- package/dist/index.js +375 -92
- package/dist/index.mjs +375 -92
- package/dist/scan-ink.mjs +86 -12
- package/package.json +1 -1
package/dist/scan-ink.mjs
CHANGED
|
@@ -709,13 +709,32 @@ var DLP_PATTERNS_GLOBAL = DLP_PATTERNS.map(
|
|
|
709
709
|
})
|
|
710
710
|
);
|
|
711
711
|
var SENSITIVE_PATH_PATTERNS = [
|
|
712
|
-
/[/\\]\.ssh[/\\]/i,
|
|
713
|
-
/[/\\]\.aws[/\\]/i,
|
|
712
|
+
/[/\\]\.ssh([/\\]|$)/i,
|
|
713
|
+
/[/\\]\.aws([/\\]|$)/i,
|
|
714
714
|
/[/\\]\.config[/\\]gcloud[/\\]/i,
|
|
715
715
|
/[/\\]\.azure[/\\]/i,
|
|
716
716
|
/[/\\]\.kube[/\\]config$/i,
|
|
717
|
-
|
|
718
|
-
// .
|
|
717
|
+
// ⚠️ ONE SEMANTIC, FOUR COPIES. This is the AST tier's `.env` rule verbatim
|
|
718
|
+
// (shell/index.ts SENSITIVE_PATH_RULES), whose reasoning is documented there:
|
|
719
|
+
// structural suffix chain rather than a hand-written list, `example|sample|
|
|
720
|
+
// template` exempt because a fixture stays a fixture whatever follows, and
|
|
721
|
+
// `.test` anchored because `test` names an ENVIRONMENT -- `.env.test` is the
|
|
722
|
+
// committed template, `.env.test.local` is gitignored and holds real values.
|
|
723
|
+
//
|
|
724
|
+
// It was previously `[/\\]\.env($|\.)` with NO exemptions, so `Read .env.example`
|
|
725
|
+
// blocked while `cat .env.example` allowed: the same file, opposite verdicts,
|
|
726
|
+
// decided only by which tool asked. See src/__tests__/jail-both-doors.test.ts,
|
|
727
|
+
// which is the contract that now holds these copies in step, and stage 5 of
|
|
728
|
+
// doc/credential-jail-architecture.md, which replaces them with one generated
|
|
729
|
+
// source.
|
|
730
|
+
// ⚠️ The `.local` branch comes FIRST and takes no exemption. A fixture stays a
|
|
731
|
+
// fixture whatever follows it -- `.env.example.md` is documentation -- but
|
|
732
|
+
// `.env.example.local` is gitignored by the `.env*.local` convention and holds
|
|
733
|
+
// real values, exactly the reasoning that anchors `(?!\.test$)` rather than
|
|
734
|
+
// using `\b`. Without this branch the fixture exemption also bought a two-step
|
|
735
|
+
// bypass: `cp .env .env.sample`, then read the copy.
|
|
736
|
+
/[/\\]\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)/i,
|
|
737
|
+
// .env + any suffix chain; fixtures exempt unless .local
|
|
719
738
|
/[/\\]\.git-credentials$/i,
|
|
720
739
|
/[/\\]\.npmrc$/i,
|
|
721
740
|
/[/\\]\.docker[/\\]config\.json$/i,
|
|
@@ -782,6 +801,10 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
|
782
801
|
"od",
|
|
783
802
|
"xxd",
|
|
784
803
|
"hexdump",
|
|
804
|
+
// Emits the file's bytes, re-encoded, so it is a read by the set's own test
|
|
805
|
+
// ("does it emit file contents"). Absent until 2026-09-10, which is why
|
|
806
|
+
// `base64 ~/.ssh/id_rsa` printed a private key with no verdict.
|
|
807
|
+
"base64",
|
|
785
808
|
"strings",
|
|
786
809
|
"sort",
|
|
787
810
|
"uniq",
|
|
@@ -789,8 +812,55 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
|
789
812
|
"nl",
|
|
790
813
|
"dd"
|
|
791
814
|
]);
|
|
815
|
+
var SCP_VALUE_FLAGS = ["i", "F", "o", "c", "S", "P", "J", "D", "W", "l"];
|
|
816
|
+
var RSYNC_SKIP = [
|
|
817
|
+
"e",
|
|
818
|
+
"--rsh",
|
|
819
|
+
"--exclude",
|
|
820
|
+
"--exclude-from",
|
|
821
|
+
"--include",
|
|
822
|
+
"--include-from",
|
|
823
|
+
"--files-from",
|
|
824
|
+
"f",
|
|
825
|
+
"--filter"
|
|
826
|
+
];
|
|
827
|
+
var COPY_VERBS = {
|
|
828
|
+
cp: { source: "allButLast", targetDirFlag: true },
|
|
829
|
+
mv: { source: "allButLast", targetDirFlag: true },
|
|
830
|
+
install: { source: "allButLast", targetDirFlag: true },
|
|
831
|
+
ln: { source: "first", targetDirFlag: true },
|
|
832
|
+
scp: { source: "allButLast", skipFlags: SCP_VALUE_FLAGS },
|
|
833
|
+
rsync: { source: "allButLast", skipFlags: RSYNC_SKIP },
|
|
834
|
+
tar: {
|
|
835
|
+
source: "archive",
|
|
836
|
+
archive: "tar",
|
|
837
|
+
skipFlags: ["f", "X", "T", "--file", "--exclude", "--exclude-from", "--files-from"]
|
|
838
|
+
},
|
|
839
|
+
zip: { source: "archive", archive: "zip", skipFlags: ["x", "i", "--exclude", "--include"] },
|
|
840
|
+
ar: { source: "archive", archive: "ar" },
|
|
841
|
+
"7z": { source: "archive", archive: "7z", skipFlags: ["x", "--exclude"] },
|
|
842
|
+
gzip: { source: "all" },
|
|
843
|
+
bzip2: { source: "all" },
|
|
844
|
+
xz: { source: "all" },
|
|
845
|
+
"docker cp": { source: "allButLast" },
|
|
846
|
+
"kubectl cp": { source: "allButLast" },
|
|
847
|
+
"gsutil cp": { source: "allButLast" },
|
|
848
|
+
"gsutil rsync": { source: "allButLast" },
|
|
849
|
+
"rclone copy": { source: "allButLast" },
|
|
850
|
+
"rclone sync": { source: "allButLast" },
|
|
851
|
+
"aws s3 cp": { source: "allButLast" },
|
|
852
|
+
"aws s3 mv": { source: "allButLast" },
|
|
853
|
+
"aws s3 sync": { source: "allButLast" },
|
|
854
|
+
"gcloud storage cp": { source: "allButLast" },
|
|
855
|
+
"az storage blob upload": { source: "flagOperand", sourceFlags: ["f", "--file"] }
|
|
856
|
+
};
|
|
857
|
+
var COPY_VERB_HEADS = new Set(Object.keys(COPY_VERBS).map((k) => k.split(" ")[0]));
|
|
792
858
|
var FS_OP_PRESCREEN_RE = new RegExp(
|
|
793
|
-
|
|
859
|
+
// A quote is a separator too: `eval "cat X"` and `sh -c 'cat X'` put the
|
|
860
|
+
// reader right after `"` / `'`, and without these two characters the
|
|
861
|
+
// prescreen rejected every string-wrapped read before the parser ran.
|
|
862
|
+
// Found 2026-09-11 by instrumenting the walk -- no CallExpr was ever visited.
|
|
863
|
+
`(?:^|[\\s|;&("'\`\\n/])(?:rm|${[...FS_READ_TOOLS, ...COPY_VERB_HEADS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)`
|
|
794
864
|
);
|
|
795
865
|
function deriveRedirOp(sample) {
|
|
796
866
|
try {
|
|
@@ -807,10 +877,14 @@ function deriveRedirOp(sample) {
|
|
|
807
877
|
}
|
|
808
878
|
}
|
|
809
879
|
var REDIR_TRUNCATE_OPS = /* @__PURE__ */ new Set([deriveRedirOp(">_f")]);
|
|
880
|
+
var REDIR_FILE_IN_OPS = new Set(
|
|
881
|
+
[deriveRedirOp("cat < f"), deriveRedirOp("cat <> f")].filter((op) => op >= 0)
|
|
882
|
+
);
|
|
810
883
|
var REDIR_HEREDOC_OPS = /* @__PURE__ */ new Set([
|
|
811
884
|
deriveRedirOp("cat <<X\nX"),
|
|
812
885
|
deriveRedirOp("cat <<-X\nX")
|
|
813
886
|
]);
|
|
887
|
+
var SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
|
|
814
888
|
var aws_default = {
|
|
815
889
|
name: "aws",
|
|
816
890
|
description: "Protects AWS infrastructure from destructive AI operations",
|
|
@@ -1362,7 +1436,7 @@ var project_jail_default = {
|
|
|
1362
1436
|
{
|
|
1363
1437
|
field: "command",
|
|
1364
1438
|
op: "matches",
|
|
1365
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.ssh[\\/\\\\]",
|
|
1439
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.ssh[\\/\\\\]",
|
|
1366
1440
|
flags: "i"
|
|
1367
1441
|
}
|
|
1368
1442
|
],
|
|
@@ -1376,7 +1450,7 @@ var project_jail_default = {
|
|
|
1376
1450
|
{
|
|
1377
1451
|
field: "command",
|
|
1378
1452
|
op: "matches",
|
|
1379
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.aws[\\/\\\\]",
|
|
1453
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.aws[\\/\\\\]",
|
|
1380
1454
|
flags: "i"
|
|
1381
1455
|
}
|
|
1382
1456
|
],
|
|
@@ -1390,7 +1464,7 @@ var project_jail_default = {
|
|
|
1390
1464
|
{
|
|
1391
1465
|
field: "command",
|
|
1392
1466
|
op: "matches",
|
|
1393
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.env(\\.(local|production|staging|development|production\\.local|staging\\.local|development\\.local))?(?=\\s|$|[;&|>)<])",
|
|
1467
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.env(\\.(local|production|staging|development|production\\.local|staging\\.local|development\\.local))?(?=\\s|$|[;&|>)<])",
|
|
1394
1468
|
flags: "i"
|
|
1395
1469
|
}
|
|
1396
1470
|
],
|
|
@@ -1404,7 +1478,7 @@ var project_jail_default = {
|
|
|
1404
1478
|
{
|
|
1405
1479
|
field: "command",
|
|
1406
1480
|
op: "matches",
|
|
1407
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*(credentials\\.json|\\.netrc|\\.npmrc|\\.docker[\\/\\\\]config\\.json|gcloud[\\/\\\\]credentials)",
|
|
1481
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*(credentials\\.json|\\.netrc|\\.npmrc|\\.docker[\\/\\\\]config\\.json|gcloud[\\/\\\\]credentials)",
|
|
1408
1482
|
flags: "i"
|
|
1409
1483
|
}
|
|
1410
1484
|
],
|
|
@@ -1418,7 +1492,7 @@ var project_jail_default = {
|
|
|
1418
1492
|
{
|
|
1419
1493
|
field: "file_path",
|
|
1420
1494
|
op: "matches",
|
|
1421
|
-
value: "(
|
|
1495
|
+
value: "([\\/\\\\]\\.ssh([\\/\\\\]|$)|^\\.ssh[\\/\\\\])",
|
|
1422
1496
|
flags: "i"
|
|
1423
1497
|
}
|
|
1424
1498
|
],
|
|
@@ -1432,7 +1506,7 @@ var project_jail_default = {
|
|
|
1432
1506
|
{
|
|
1433
1507
|
field: "file_path",
|
|
1434
1508
|
op: "matches",
|
|
1435
|
-
value: "(
|
|
1509
|
+
value: "([\\/\\\\]\\.aws([\\/\\\\]|$)|^\\.aws[\\/\\\\])",
|
|
1436
1510
|
flags: "i"
|
|
1437
1511
|
}
|
|
1438
1512
|
],
|
|
@@ -1446,7 +1520,7 @@ var project_jail_default = {
|
|
|
1446
1520
|
{
|
|
1447
1521
|
field: "file_path",
|
|
1448
1522
|
op: "matches",
|
|
1449
|
-
value: "(^|[\\/\\\\])\\.env(
|
|
1523
|
+
value: "(^|[\\/\\\\])\\.env(?![\\w-])(?:[\\w.-]*\\.local$|(?!\\.(example|sample|template)\\b)(?!\\.test$)[\\w.-]*$)",
|
|
1450
1524
|
flags: "i"
|
|
1451
1525
|
}
|
|
1452
1526
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|