@node9/proxy 1.18.2 → 1.18.3

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.js CHANGED
@@ -888,9 +888,70 @@ var MESSAGE_FLAGS = /* @__PURE__ */ new Set([
888
888
  ]);
889
889
  var SHELL_INTERPRETERS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "fish", "dash", "ksh"]);
890
890
  var DOWNLOAD_CMDS = /* @__PURE__ */ new Set(["curl", "wget"]);
891
+ function isCatHeredocOrLit(part) {
892
+ if (!part) return false;
893
+ const t = syntax.NodeType(part);
894
+ if (t === "Lit") return true;
895
+ if (t !== "CmdSubst") return false;
896
+ const stmts = part.Stmts || [];
897
+ if (stmts.length !== 1) return false;
898
+ const stmt = stmts[0];
899
+ const redirs = stmt.Redirs || stmt.Cmd?.Redirs || [];
900
+ const hasHeredoc = redirs.some((r) => r && r.Hdoc);
901
+ if (!hasHeredoc) return false;
902
+ const cmd = stmt.Cmd;
903
+ if (!cmd || syntax.NodeType(cmd) !== "CallExpr") return false;
904
+ const firstArg = cmd.Args?.[0]?.Parts || [];
905
+ if (firstArg.length !== 1 || syntax.NodeType(firstArg[0]) !== "Lit") return false;
906
+ return (firstArg[0].Value || "").toLowerCase() === "cat";
907
+ }
908
+ var NORMALIZE_CACHE_MAX = 5e3;
909
+ var normalizeCache = /* @__PURE__ */ new Map();
910
+ var AST_CACHE_MAX = 5e3;
911
+ var astCache = /* @__PURE__ */ new Map();
912
+ var PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
913
+ function parseShared(command) {
914
+ const cached = astCache.get(command);
915
+ if (cached !== void 0) {
916
+ astCache.delete(command);
917
+ astCache.set(command, cached);
918
+ return cached;
919
+ }
920
+ let parsed;
921
+ try {
922
+ parsed = sharedParser.Parse(command, "cmd");
923
+ } catch {
924
+ parsed = PARSE_FAIL;
925
+ }
926
+ if (astCache.size >= AST_CACHE_MAX) {
927
+ const oldest = astCache.keys().next().value;
928
+ if (oldest !== void 0) astCache.delete(oldest);
929
+ }
930
+ astCache.set(command, parsed);
931
+ return parsed;
932
+ }
933
+ function cachedNormalize(command, compute) {
934
+ const hit = normalizeCache.get(command);
935
+ if (hit !== void 0) {
936
+ normalizeCache.delete(command);
937
+ normalizeCache.set(command, hit);
938
+ return hit;
939
+ }
940
+ const result = compute();
941
+ if (normalizeCache.size >= NORMALIZE_CACHE_MAX) {
942
+ const oldest = normalizeCache.keys().next().value;
943
+ if (oldest !== void 0) normalizeCache.delete(oldest);
944
+ }
945
+ normalizeCache.set(command, result);
946
+ return result;
947
+ }
891
948
  function normalizeCommandForPolicy(command) {
949
+ return cachedNormalize(command, () => normalizeCommandForPolicyImpl(command));
950
+ }
951
+ function normalizeCommandForPolicyImpl(command) {
952
+ const f = parseShared(command);
953
+ if (f === PARSE_FAIL) return command;
892
954
  try {
893
- const f = sharedParser.Parse(command, "cmd");
894
955
  const strips = [];
895
956
  syntax.Walk(f, (node) => {
896
957
  if (!node) return false;
@@ -912,7 +973,11 @@ function normalizeCommandForPolicy(command) {
912
973
  } else if (nt === "DblQuoted") {
913
974
  const innerParts = quotedNode.Parts || [];
914
975
  const allLit = innerParts.length === 0 || innerParts.every((p) => syntax.NodeType(p) === "Lit");
915
- if (allLit) strips.push([next.Pos().Offset(), next.End().Offset()]);
976
+ if (allLit) {
977
+ strips.push([next.Pos().Offset(), next.End().Offset()]);
978
+ } else if (innerParts.every((p) => isCatHeredocOrLit(p))) {
979
+ strips.push([next.Pos().Offset(), next.End().Offset()]);
980
+ }
916
981
  }
917
982
  }
918
983
  return true;
@@ -1401,10 +1466,18 @@ function getNestedValue(obj, path13) {
1401
1466
  function evaluateSmartConditions(args, rule) {
1402
1467
  if (!rule.conditions || rule.conditions.length === 0) return true;
1403
1468
  const mode = rule.conditionMode ?? "all";
1469
+ const fieldCache = /* @__PURE__ */ new Map();
1470
+ const resolveField = (field) => {
1471
+ if (fieldCache.has(field)) return fieldCache.get(field) ?? null;
1472
+ const rawVal = getNestedValue(args, field);
1473
+ const rawStr = rawVal !== null && rawVal !== void 0 ? String(rawVal) : null;
1474
+ const stripped = field === "command" && rawStr !== null ? normalizeCommandForPolicy(rawStr) : rawStr;
1475
+ const val = stripped !== null ? stripped.replace(/\s+/g, " ").trim() : null;
1476
+ fieldCache.set(field, val);
1477
+ return val;
1478
+ };
1404
1479
  const results = rule.conditions.map((cond) => {
1405
- const rawVal = getNestedValue(args, cond.field);
1406
- const normalized = rawVal !== null && rawVal !== void 0 ? String(rawVal).replace(/\s+/g, " ").trim() : null;
1407
- const val = cond.field === "command" && normalized !== null ? normalizeCommandForPolicy(normalized) : normalized;
1480
+ const val = resolveField(cond.field);
1408
1481
  switch (cond.op) {
1409
1482
  case "exists":
1410
1483
  return val !== null && val !== "";
package/dist/index.mjs CHANGED
@@ -858,9 +858,70 @@ var MESSAGE_FLAGS = /* @__PURE__ */ new Set([
858
858
  ]);
859
859
  var SHELL_INTERPRETERS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "fish", "dash", "ksh"]);
860
860
  var DOWNLOAD_CMDS = /* @__PURE__ */ new Set(["curl", "wget"]);
861
+ function isCatHeredocOrLit(part) {
862
+ if (!part) return false;
863
+ const t = syntax.NodeType(part);
864
+ if (t === "Lit") return true;
865
+ if (t !== "CmdSubst") return false;
866
+ const stmts = part.Stmts || [];
867
+ if (stmts.length !== 1) return false;
868
+ const stmt = stmts[0];
869
+ const redirs = stmt.Redirs || stmt.Cmd?.Redirs || [];
870
+ const hasHeredoc = redirs.some((r) => r && r.Hdoc);
871
+ if (!hasHeredoc) return false;
872
+ const cmd = stmt.Cmd;
873
+ if (!cmd || syntax.NodeType(cmd) !== "CallExpr") return false;
874
+ const firstArg = cmd.Args?.[0]?.Parts || [];
875
+ if (firstArg.length !== 1 || syntax.NodeType(firstArg[0]) !== "Lit") return false;
876
+ return (firstArg[0].Value || "").toLowerCase() === "cat";
877
+ }
878
+ var NORMALIZE_CACHE_MAX = 5e3;
879
+ var normalizeCache = /* @__PURE__ */ new Map();
880
+ var AST_CACHE_MAX = 5e3;
881
+ var astCache = /* @__PURE__ */ new Map();
882
+ var PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
883
+ function parseShared(command) {
884
+ const cached = astCache.get(command);
885
+ if (cached !== void 0) {
886
+ astCache.delete(command);
887
+ astCache.set(command, cached);
888
+ return cached;
889
+ }
890
+ let parsed;
891
+ try {
892
+ parsed = sharedParser.Parse(command, "cmd");
893
+ } catch {
894
+ parsed = PARSE_FAIL;
895
+ }
896
+ if (astCache.size >= AST_CACHE_MAX) {
897
+ const oldest = astCache.keys().next().value;
898
+ if (oldest !== void 0) astCache.delete(oldest);
899
+ }
900
+ astCache.set(command, parsed);
901
+ return parsed;
902
+ }
903
+ function cachedNormalize(command, compute) {
904
+ const hit = normalizeCache.get(command);
905
+ if (hit !== void 0) {
906
+ normalizeCache.delete(command);
907
+ normalizeCache.set(command, hit);
908
+ return hit;
909
+ }
910
+ const result = compute();
911
+ if (normalizeCache.size >= NORMALIZE_CACHE_MAX) {
912
+ const oldest = normalizeCache.keys().next().value;
913
+ if (oldest !== void 0) normalizeCache.delete(oldest);
914
+ }
915
+ normalizeCache.set(command, result);
916
+ return result;
917
+ }
861
918
  function normalizeCommandForPolicy(command) {
919
+ return cachedNormalize(command, () => normalizeCommandForPolicyImpl(command));
920
+ }
921
+ function normalizeCommandForPolicyImpl(command) {
922
+ const f = parseShared(command);
923
+ if (f === PARSE_FAIL) return command;
862
924
  try {
863
- const f = sharedParser.Parse(command, "cmd");
864
925
  const strips = [];
865
926
  syntax.Walk(f, (node) => {
866
927
  if (!node) return false;
@@ -882,7 +943,11 @@ function normalizeCommandForPolicy(command) {
882
943
  } else if (nt === "DblQuoted") {
883
944
  const innerParts = quotedNode.Parts || [];
884
945
  const allLit = innerParts.length === 0 || innerParts.every((p) => syntax.NodeType(p) === "Lit");
885
- if (allLit) strips.push([next.Pos().Offset(), next.End().Offset()]);
946
+ if (allLit) {
947
+ strips.push([next.Pos().Offset(), next.End().Offset()]);
948
+ } else if (innerParts.every((p) => isCatHeredocOrLit(p))) {
949
+ strips.push([next.Pos().Offset(), next.End().Offset()]);
950
+ }
886
951
  }
887
952
  }
888
953
  return true;
@@ -1371,10 +1436,18 @@ function getNestedValue(obj, path13) {
1371
1436
  function evaluateSmartConditions(args, rule) {
1372
1437
  if (!rule.conditions || rule.conditions.length === 0) return true;
1373
1438
  const mode = rule.conditionMode ?? "all";
1439
+ const fieldCache = /* @__PURE__ */ new Map();
1440
+ const resolveField = (field) => {
1441
+ if (fieldCache.has(field)) return fieldCache.get(field) ?? null;
1442
+ const rawVal = getNestedValue(args, field);
1443
+ const rawStr = rawVal !== null && rawVal !== void 0 ? String(rawVal) : null;
1444
+ const stripped = field === "command" && rawStr !== null ? normalizeCommandForPolicy(rawStr) : rawStr;
1445
+ const val = stripped !== null ? stripped.replace(/\s+/g, " ").trim() : null;
1446
+ fieldCache.set(field, val);
1447
+ return val;
1448
+ };
1374
1449
  const results = rule.conditions.map((cond) => {
1375
- const rawVal = getNestedValue(args, cond.field);
1376
- const normalized = rawVal !== null && rawVal !== void 0 ? String(rawVal).replace(/\s+/g, " ").trim() : null;
1377
- const val = cond.field === "command" && normalized !== null ? normalizeCommandForPolicy(normalized) : normalized;
1450
+ const val = resolveField(cond.field);
1378
1451
  switch (cond.op) {
1379
1452
  case "exists":
1380
1453
  return val !== null && val !== "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "1.18.2",
3
+ "version": "1.18.3",
4
4
  "description": "The Sudo Command for AI Agents. Execution Security for Claude Code & MCP.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",