@node9/proxy 2.48.1 → 2.48.2

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/scan-ink.mjs CHANGED
@@ -238,10 +238,11 @@ import os from "os";
238
238
 
239
239
  // packages/policy-engine/dist/index.mjs
240
240
  import { fileURLToPath } from "url";
241
- import picomatch from "picomatch";
241
+ import picomatch2 from "picomatch";
242
242
  import safeRegex from "safe-regex2";
243
243
  import pm from "picomatch";
244
244
  import { createRequire } from "module";
245
+ import picomatch from "picomatch";
245
246
  import safeRegex2 from "safe-regex2";
246
247
  import { createHash as createHash2 } from "crypto";
247
248
  var getFilename = () => fileURLToPath(import.meta.url);
@@ -2234,6 +2235,11 @@ var CHECK_TEXT = {
2234
2235
  example: "Copying ~/.npmrc into /tmp",
2235
2236
  advice: "Fixed at Review for now."
2236
2237
  },
2238
+ "data.credential-files-unknown": {
2239
+ plain: "The agent reads files whose names a listing produces only when the command runs, so node9 cannot tell in advance whether a credential file is among them.",
2240
+ example: "find . | xargs cat",
2241
+ advice: "Standard records it; Strict asks. Set Review if your agents should not read files they find this way."
2242
+ },
2237
2243
  "data.pipe-chain": {
2238
2244
  plain: "The agent reads a secret file and sends it over the network in the same command.",
2239
2245
  example: "An SSH key file piped into curl",
@@ -2357,6 +2363,8 @@ var STRICT = {
2357
2363
  "data.secrets-weak": "block",
2358
2364
  "data.pipe-chain": "block",
2359
2365
  "data.env-exfil": "block",
2366
+ // Names a listing produces at run time: Standard records, Strict asks.
2367
+ "data.credential-files-unknown": "review",
2360
2368
  "loading.unknown-registry": "block",
2361
2369
  "network.unknown-host": "review",
2362
2370
  "network.internal-addresses": "block",
@@ -2651,6 +2659,14 @@ var PRODUCT_CHECKS = [
2651
2659
  values: ALL,
2652
2660
  defaultValue: "review"
2653
2661
  },
2662
+ {
2663
+ id: "data.credential-files-unknown",
2664
+ group: "data",
2665
+ title: "Files read by names known only at run time",
2666
+ catches: "a reader fed names from a listing that can include credential files (find . | xargs cat)",
2667
+ values: ALL,
2668
+ defaultValue: "log"
2669
+ },
2654
2670
  {
2655
2671
  id: "data.pipe-chain",
2656
2672
  group: "data",
@@ -3145,6 +3161,25 @@ var SYSTEM_DIRS = [
3145
3161
  ];
3146
3162
  var UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
3147
3163
  var LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
3164
+ function opOf(sample) {
3165
+ return parseShared(sample).Stmts[0].Redirs[0].Op;
3166
+ }
3167
+ var ops = lazy(() => ({
3168
+ WRITES: new Set([">x", ">>x", ">|x", "&>x", "&>>x", "<>x"].map(opOf)),
3169
+ APPENDS: new Set([">>x", "&>>x"].map(opOf)),
3170
+ READS: new Set(["<x", "<>x"].map(opOf)),
3171
+ /** `>&word` with a non-numeric word is `&>word` in bash. */
3172
+ DUP_OUT: opOf(">&x"),
3173
+ HERE_STRING: opOf("<<<x"),
3174
+ AND: parseShared("a && b").Stmts[0].Cmd.Op,
3175
+ OR: parseShared("a || b").Stmts[0].Cmd.Op,
3176
+ /** `${x:-d}` `${x-d}` `${x:=d}` `${x=d}`: the default is the value when x is unset. */
3177
+ DEFAULT_OPS: new Set(
3178
+ ["${x:-y}", "${x-y}", "${x:=y}", "${x=y}"].map(
3179
+ (s) => parseShared(`echo ${s}`).Stmts[0].Cmd.Args[1].Parts[0].Exp.Op
3180
+ )
3181
+ )
3182
+ }));
3148
3183
  var MAX_INLINE_CODE = 64 * 1024;
3149
3184
  var OUTSIDE = String.raw`(?:~|\.\.[\\/]|/(?!(?:tmp|var/tmp)/(?![^'"\n]*\.\.)|dev/(?:null|stdout|stderr)\b))`;
3150
3185
  var pyImport = (mods) => new RegExp(String.raw`\b(?:import\s+(?:[\w.]+(?:\s+as\s+\w+)?\s*,\s*)*|from\s+)(?:${mods})\b`);
@@ -3405,6 +3440,13 @@ var FS_READ_TOOLS = /* @__PURE__ */ new Set([
3405
3440
  "tac",
3406
3441
  "nl",
3407
3442
  "dd",
3443
+ // Compare or merge files and print their lines (jail-indirect-reads,
3444
+ // 2026-11-02): `diff .env /dev/null` printed every line of the file.
3445
+ "diff",
3446
+ "cmp",
3447
+ "comm",
3448
+ "paste",
3449
+ "join",
3408
3450
  // — PowerShell, absent until 2026-09-22 —
3409
3451
  // Same membership test, "does it emit file contents": Get-Content/gc print
3410
3452
  // the file (cat), Select-String/sls print matching lines (grep), Format-Hex
@@ -3882,6 +3924,14 @@ var REDIR_FILE_IN_OPS = lazy(
3882
3924
  var REDIR_HEREDOC_OPS = lazy(
3883
3925
  () => /* @__PURE__ */ new Set([deriveRedirOp("cat <<X\nX"), deriveRedirOp("cat <<-X\nX")])
3884
3926
  );
3927
+ var pipeOps = lazy(
3928
+ () => new Set(
3929
+ ["a | b", "a |& b"].map((t) => {
3930
+ const f = parseShared(t);
3931
+ return typeof f === "symbol" ? -1 : f.Stmts[0].Cmd.Op;
3932
+ })
3933
+ )
3934
+ );
3885
3935
  var BASH_TOOL_NAMES = /* @__PURE__ */ new Set([
3886
3936
  "bash",
3887
3937
  "execute_bash",
@@ -3902,25 +3952,6 @@ var MCP_EXECUTORS = /* @__PURE__ */ new Set([
3902
3952
  "run_terminal_command",
3903
3953
  "start_process"
3904
3954
  ]);
3905
- function opOf(sample) {
3906
- return parseShared(sample).Stmts[0].Redirs[0].Op;
3907
- }
3908
- var ops = lazy(() => ({
3909
- WRITES: new Set([">x", ">>x", ">|x", "&>x", "&>>x", "<>x"].map(opOf)),
3910
- APPENDS: new Set([">>x", "&>>x"].map(opOf)),
3911
- READS: new Set(["<x", "<>x"].map(opOf)),
3912
- /** `>&word` with a non-numeric word is `&>word` in bash. */
3913
- DUP_OUT: opOf(">&x"),
3914
- HERE_STRING: opOf("<<<x"),
3915
- AND: parseShared("a && b").Stmts[0].Cmd.Op,
3916
- OR: parseShared("a || b").Stmts[0].Cmd.Op,
3917
- /** `${x:-d}` `${x-d}` `${x:=d}` `${x=d}`: the default is the value when x is unset. */
3918
- DEFAULT_OPS: new Set(
3919
- ["${x:-y}", "${x-y}", "${x:=y}", "${x=y}"].map(
3920
- (s) => parseShared(`echo ${s}`).Stmts[0].Cmd.Args[1].Parts[0].Exp.Op
3921
- )
3922
- )
3923
- }));
3924
3955
  var SEQUENCE_OPS = lazy(() => {
3925
3956
  const ops2 = /* @__PURE__ */ new Set();
3926
3957
  for (const text of ["x && y", "x || y"]) {
@@ -7297,6 +7328,7 @@ var MAP_GOVERNED = /* @__PURE__ */ new Set([
7297
7328
  "commands.reverse-shell",
7298
7329
  "data.pipe-chain",
7299
7330
  "data.env-exfil",
7331
+ "data.credential-files-unknown",
7300
7332
  "loading.unknown-registry",
7301
7333
  // The general action checks (node9Firewall#390): smart rules the map governs.
7302
7334
  "actions.money",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "2.48.1",
3
+ "version": "2.48.2",
4
4
  "description": "Access control for AI agents. Set what Claude Code, Codex, Gemini, Cursor and any MCP server are allowed to do, review risky actions before they run, and keep every action on the record.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",