claude-warden 1.1.10 → 1.2.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-warden",
3
- "version": "1.1.10",
3
+ "version": "1.2.0",
4
4
  "description": "Smart command safety filter for Claude Code — parses shell pipelines and evaluates per-command safety rules to auto-approve safe commands and block dangerous ones",
5
5
  "author": {
6
6
  "name": "banyudu"
@@ -51,6 +51,17 @@ askOnSubshell: true
51
51
  # - my-sprite
52
52
  # - dev-*
53
53
 
54
+ # Override rules when evaluating commands inside trusted remote contexts
55
+ # (docker exec, kubectl exec, ssh, sprite exec). These overrides are applied
56
+ # as the highest-priority layer only for remote command evaluation.
57
+ # trustedContextOverrides:
58
+ # alwaysAllow:
59
+ # - sudo
60
+ # - apt
61
+ # - apt-get
62
+ # alwaysDeny: []
63
+ # rules: []
64
+
54
65
  # Command-specific rules (override built-in rules by command name).
55
66
  # The first scope (project > user > default) with a rule for a given command wins.
56
67
  # rules:
package/dist/index.cjs CHANGED
@@ -18583,7 +18583,7 @@ function evaluateSSHCommand(cmd, config) {
18583
18583
  };
18584
18584
  }
18585
18585
  const parsed = parseCommand(remoteCommand);
18586
- const result = evaluate(parsed, config);
18586
+ const result = evaluate(parsed, configWithContextOverrides(config));
18587
18587
  return {
18588
18588
  command,
18589
18589
  args: args2,
@@ -18602,6 +18602,35 @@ var DOCKER_EXEC_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set([
18602
18602
  "--workdir",
18603
18603
  "--detach-keys"
18604
18604
  ]);
18605
+ var INTERACTIVE_SHELLS = /* @__PURE__ */ new Set(["bash", "sh", "zsh"]);
18606
+ function configWithContextOverrides(config) {
18607
+ if (!config.trustedContextOverrides) return config;
18608
+ return {
18609
+ ...config,
18610
+ layers: [config.trustedContextOverrides, ...config.layers]
18611
+ };
18612
+ }
18613
+ function evaluateRemoteCommand(remoteArgs, config) {
18614
+ const overriddenConfig = configWithContextOverrides(config);
18615
+ if (remoteArgs.length === 0) {
18616
+ return { decision: "allow", reason: "interactive", details: [] };
18617
+ }
18618
+ const remoteCmd = remoteArgs[0];
18619
+ if (INTERACTIVE_SHELLS.has(remoteCmd) && remoteArgs.length === 1) {
18620
+ return { decision: "allow", reason: "interactive shell", details: [] };
18621
+ }
18622
+ if (INTERACTIVE_SHELLS.has(remoteCmd) && remoteArgs[1] === "-c" && remoteArgs.length >= 3) {
18623
+ const innerCommand = remoteArgs.slice(2).join(" ");
18624
+ const parsed2 = parseCommand(innerCommand);
18625
+ return evaluate(parsed2, overriddenConfig);
18626
+ }
18627
+ const parsed = {
18628
+ commands: [{ command: remoteCmd, args: remoteArgs.slice(1) }],
18629
+ hasSubshell: false,
18630
+ subshellCommands: []
18631
+ };
18632
+ return evaluate(parsed, overriddenConfig);
18633
+ }
18605
18634
  function parseDockerExecArgs(args2) {
18606
18635
  let target = null;
18607
18636
  const remoteArgs = [];
@@ -18627,29 +18656,19 @@ function parseDockerExecArgs(args2) {
18627
18656
  }
18628
18657
  i++;
18629
18658
  }
18630
- return { target, remoteCommand: remoteArgs.length > 0 ? remoteArgs.join(" ") : null };
18659
+ return { target, remoteArgs };
18631
18660
  }
18632
18661
  function evaluateDockerExec(cmd, config) {
18633
18662
  const { command, args: args2 } = cmd;
18634
18663
  if (args2[0] !== "exec") return null;
18635
- const { target, remoteCommand } = parseDockerExecArgs(args2.slice(1));
18664
+ const { target, remoteArgs } = parseDockerExecArgs(args2.slice(1));
18636
18665
  if (!target || !matchesPattern(target, config.trustedDockerContainers || [])) return null;
18637
- if (!remoteCommand) {
18638
- return {
18639
- command,
18640
- args: args2,
18641
- decision: "allow",
18642
- reason: `Trusted Docker container "${target}" (interactive)`,
18643
- matchedRule: "trustedDockerContainers"
18644
- };
18645
- }
18646
- const parsed = parseCommand(remoteCommand);
18647
- const result = evaluate(parsed, config);
18666
+ const result = evaluateRemoteCommand(remoteArgs, config);
18648
18667
  return {
18649
18668
  command,
18650
18669
  args: args2,
18651
18670
  decision: result.decision,
18652
- reason: `Trusted Docker container "${target}": ${result.reason}`,
18671
+ reason: `Trusted Docker container "${target}" (${result.reason})`,
18653
18672
  matchedRule: "trustedDockerContainers"
18654
18673
  };
18655
18674
  }
@@ -18684,11 +18703,9 @@ function parseKubectlExecArgs(args2) {
18684
18703
  let pod = null;
18685
18704
  const remoteArgs = [];
18686
18705
  let i = 0;
18687
- let pastSeparator = false;
18688
18706
  while (i < args2.length) {
18689
18707
  const arg = args2[i];
18690
18708
  if (arg === "--") {
18691
- pastSeparator = true;
18692
18709
  i++;
18693
18710
  while (i < args2.length) {
18694
18711
  remoteArgs.push(args2[i]);
@@ -18717,30 +18734,20 @@ function parseKubectlExecArgs(args2) {
18717
18734
  }
18718
18735
  i++;
18719
18736
  }
18720
- return { context, pod, remoteCommand: remoteArgs.length > 0 ? remoteArgs.join(" ") : null };
18737
+ return { context, pod, remoteArgs };
18721
18738
  }
18722
18739
  function evaluateKubectlExec(cmd, config) {
18723
18740
  const { command, args: args2 } = cmd;
18724
18741
  if (args2[0] !== "exec") return null;
18725
- const { context, pod, remoteCommand } = parseKubectlExecArgs(args2.slice(1));
18742
+ const { context, pod, remoteArgs } = parseKubectlExecArgs(args2.slice(1));
18726
18743
  const trustedContexts = config.trustedKubectlContexts || [];
18727
18744
  if (!context || !matchesPattern(context, trustedContexts)) return null;
18728
- if (!remoteCommand) {
18729
- return {
18730
- command,
18731
- args: args2,
18732
- decision: "allow",
18733
- reason: `Trusted kubectl context "${context}"${pod ? `, pod "${pod}"` : ""} (interactive)`,
18734
- matchedRule: "trustedKubectlContexts"
18735
- };
18736
- }
18737
- const parsed = parseCommand(remoteCommand);
18738
- const result = evaluate(parsed, config);
18745
+ const result = evaluateRemoteCommand(remoteArgs, config);
18739
18746
  return {
18740
18747
  command,
18741
18748
  args: args2,
18742
18749
  decision: result.decision,
18743
- reason: `Trusted kubectl context "${context}": ${result.reason}`,
18750
+ reason: `Trusted kubectl context "${context}"${pod ? `, pod "${pod}"` : ""} (${result.reason})`,
18744
18751
  matchedRule: "trustedKubectlContexts"
18745
18752
  };
18746
18753
  }
@@ -18785,7 +18792,7 @@ function parseSpriteExecArgs(args2) {
18785
18792
  i++;
18786
18793
  continue;
18787
18794
  }
18788
- return { spriteName: null, remoteCommand: null };
18795
+ return { spriteName: null, remoteArgs: [] };
18789
18796
  }
18790
18797
  while (i < args2.length) {
18791
18798
  remoteArgs.push(args2[i]);
@@ -18793,32 +18800,19 @@ function parseSpriteExecArgs(args2) {
18793
18800
  }
18794
18801
  break;
18795
18802
  }
18796
- return {
18797
- spriteName,
18798
- remoteCommand: remoteArgs.length > 0 ? remoteArgs.join(" ") : null
18799
- };
18803
+ return { spriteName, remoteArgs };
18800
18804
  }
18801
18805
  function evaluateSpriteExec(cmd, config) {
18802
18806
  const { command, args: args2 } = cmd;
18803
- const { spriteName, remoteCommand } = parseSpriteExecArgs(args2);
18807
+ const { spriteName, remoteArgs } = parseSpriteExecArgs(args2);
18804
18808
  const trustedSprites = config.trustedSprites || [];
18805
18809
  if (!spriteName || !matchesPattern(spriteName, trustedSprites)) return null;
18806
- if (!remoteCommand) {
18807
- return {
18808
- command,
18809
- args: args2,
18810
- decision: "allow",
18811
- reason: `Trusted sprite "${spriteName}" (interactive)`,
18812
- matchedRule: "trustedSprites"
18813
- };
18814
- }
18815
- const parsed = parseCommand(remoteCommand);
18816
- const result = evaluate(parsed, config);
18810
+ const result = evaluateRemoteCommand(remoteArgs, config);
18817
18811
  return {
18818
18812
  command,
18819
18813
  args: args2,
18820
18814
  decision: result.decision,
18821
- reason: `Trusted sprite "${spriteName}": ${result.reason}`,
18815
+ reason: `Trusted sprite "${spriteName}" (${result.reason})`,
18822
18816
  matchedRule: "trustedSprites"
18823
18817
  };
18824
18818
  }
@@ -19359,6 +19353,19 @@ function mergeNonLayerFields(config, raw) {
19359
19353
  if (typeof raw.askOnSubshell === "boolean") {
19360
19354
  config.askOnSubshell = raw.askOnSubshell;
19361
19355
  }
19356
+ if (raw.trustedContextOverrides && typeof raw.trustedContextOverrides === "object") {
19357
+ const overrides = raw.trustedContextOverrides;
19358
+ const layer = extractLayer(overrides);
19359
+ if (config.trustedContextOverrides) {
19360
+ config.trustedContextOverrides = {
19361
+ alwaysAllow: [...layer.alwaysAllow, ...config.trustedContextOverrides.alwaysAllow],
19362
+ alwaysDeny: [...layer.alwaysDeny, ...config.trustedContextOverrides.alwaysDeny],
19363
+ rules: [...layer.rules, ...config.trustedContextOverrides.rules]
19364
+ };
19365
+ } else {
19366
+ config.trustedContextOverrides = layer;
19367
+ }
19368
+ }
19362
19369
  }
19363
19370
 
19364
19371
  // src/suggest.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-warden",
3
- "version": "1.1.10",
3
+ "version": "1.2.0",
4
4
  "description": "Smart command safety filter for Claude Code — auto-approves safe commands, blocks dangerous ones",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",