@blogic-cz/agent-tools 1.6.4 → 1.6.5

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 CHANGED
@@ -672,6 +672,8 @@ The bounded Python subprocess proof supports one immutable literal argv binding
672
672
 
673
673
  Supported runtime script/tool operands form an argument boundary: a later tool `-c` configuration flag is data. Genuine eval and loader flags, Bun exec/repl and unproved startup-option arity still refuse. Literal Herdr prompts can compose with the existing literal agent-get and pane-read forms and passive consumers. One literal `--sql` operand on the supported `bun run db-tool sql/query` shape can contain SQL braces; substitutions, duplicate SQL operands and unknown wrapper options receive no exception. SQL output pipelines require closed passive consumers, so shell, xargs and unknown executors refuse. Numeric `$?` can appear only in passive echo/printf data operands, with passive pipeline consumers. Explicitly approved environment names can appear in quoted equality or blankness tests, including the quoted empty default `${NAME:-}`. This does not add default-approved names or permit shell evaluators. One sed print program with numeric or escaped-regex addresses/ranges and known `-n`/`-E` flags is passive; execution/read/write callbacks, extra programs and script files receive no print proof. Local file bindings use that same closed print proof and never a generic sed-word fallback. Ordinary path and configured command policies still apply to all these forms.
674
674
 
675
+ Literal shell bodies receive recursive shared policy replay under exact `sh -c`/`bash -c` and `zsh -f -c` invocations, using only these bare executable names or their `/bin/` and `/usr/bin/` paths and known RTK/command wrappers. Both protected paths and blocked CLIs remain enforced inside the body, along with environment and custom-command policies. A conventional shell-family name or numeric version in an unproved executing operand/control position refuses; proved literal text, reader and file-metadata operands remain data. Only the listed sh/bash/zsh identities receive body replay; rbash/rksh/ksh93 and versioned names refuse, and arbitrary renamed executables are outside this identity proof. Replay permits only existing closed body roles/materializations, at most eight shell levels and 64 KiB per invocation. Unproved options, environment prefixes, active exec/nohup/sudo wrappers, positional/script/stdin forms, executable binders, control flow and outer directory changes around a wrapper refuse. A body-internal proved cd affects body readers; outer redirects retain the outer shell directory. This is a textual body proof, not verification of native binaries or all host startup code. In particular, zsh -f still sources the global zshenv file. For unsupported executable inventories, use separate literal metadata requests and literal candidate invocations; no program name or --version flag receives a special exception.
676
+
675
677
  A separate bounded Node proof supports literal `package.json` inventory loops that print `bin` metadata or use the supported scripts filter. The complete invocation must preserve loader identity through known passive commands, closed literal writers, or proved inventories. Startup/environment mutations, unknown commands, loader changes, and other dynamic loading remain unproved and are refused.
676
678
 
677
679
  ### Setup for OpenCode
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credential-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAK5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAM/D,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,CAAC;AAQF,+CAA+C;AAC/C,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3E,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjF,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD,CAAC;AAwhFF,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAErE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAMD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,eAAe,CA4TrF;AAQD,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,UA93FD,SAAS,UAAU,UAAU,KAAK,IA83FM,CAAC;AAE5E,oDAAoD;AACpD,eAAO,MAAM,aAAa,YAh4FC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAg4FjB,CAAC;AAExD,yEAAyE;AACzE,eAAO,MAAM,aAAa,aAl4FE,MAAM,KAAK,OAk4FgB,CAAC;AAExD,yDAAyD;AACzD,eAAO,MAAM,aAAa,aAp4FE,MAAM,KAAK,OAo4FgB,CAAC;AAExD,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,YAt4FC,MAAM,KAAK,OAs4F0B,CAAC;AAE1E,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,YAx4FC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAw4Ff,CAAC;AAEhE,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,YA14FC,MAAM,KAAK,OA04FsB,CAAC;AAElE,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,YA54FC,MAAM,KAAK,MAAM,GAAG,IA44Fa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credential-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAK5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAM/D,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,CAAC;AAQF,+CAA+C;AAC/C,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3E,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjF,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD,CAAC;AA2oFF,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAErE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;AAMD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,eAAe,CA4VrF;AAQD,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,UAjhGD,SAAS,UAAU,UAAU,KAAK,IAihGM,CAAC;AAE5E,oDAAoD;AACpD,eAAO,MAAM,aAAa,YAnhGC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAmhGjB,CAAC;AAExD,yEAAyE;AACzE,eAAO,MAAM,aAAa,aArhGE,MAAM,KAAK,OAqhGgB,CAAC;AAExD,yDAAyD;AACzD,eAAO,MAAM,aAAa,aAvhGE,MAAM,KAAK,OAuhGgB,CAAC;AAExD,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,YAzhGC,MAAM,KAAK,OAyhG0B,CAAC;AAE1E,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,YA3hGC,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IA2hGf,CAAC;AAEhE,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,YA7hGC,MAAM,KAAK,OA6hGsB,CAAC;AAElE,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,YA/hGC,MAAM,KAAK,MAAM,GAAG,IA+hGa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/agent-tools",
3
- "version": "1.6.4",
3
+ "version": "1.6.5",
4
4
  "description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure platform, Azure DevOps, logs, sessions, and audit",
5
5
  "keywords": [
6
6
  "agent",
@@ -530,6 +530,114 @@ function isLiteralHerdrRead(argv: string[]): boolean {
530
530
  );
531
531
  }
532
532
 
533
+ /** Conventional platform shell names and numeric versions; not arbitrary renamed executables. */
534
+ function isShellWord(word: string): boolean {
535
+ return /^(?:sh|bash|rbash|zsh|dash|ash|ksh|rksh|csh|tcsh|fish)(?:[.-]?[0-9]+(?:[.][0-9]+)*)?$/.test(
536
+ word.split("/").at(-1) ?? "",
537
+ );
538
+ }
539
+
540
+ /** Shell-named operands are data only under an existing non-executing argument role. */
541
+ function hasProvedShellDataOperands(argv: string[]): boolean {
542
+ const name = argv[0]?.split("/").at(-1) ?? "";
543
+ if (isPassiveTextCommand(argv) || isLiteralHerdrRead(argv) || isStaticNavigationCommand(argv))
544
+ return true;
545
+ if (["cat", "ls", "uniq", "cut"].includes(name)) return true;
546
+ if (["find", "wc", "sort"].includes(name)) return isProvedCwdSubprocessRole(argv);
547
+ return name === "test" && argv.length === 3 && argv[1] === "-e" && !/[$`]/.test(argv[2] ?? "");
548
+ }
549
+
550
+ const SHELL_IDENTITIES = new Set([
551
+ "sh",
552
+ "bash",
553
+ "zsh",
554
+ "/bin/sh",
555
+ "/usr/bin/sh",
556
+ "/bin/bash",
557
+ "/usr/bin/bash",
558
+ "/bin/zsh",
559
+ "/usr/bin/zsh",
560
+ ]);
561
+
562
+ /** Literal argv only. Undefined means a shell was found without a complete invocation proof. */
563
+ function literalShellBodies(command: string): string[] | undefined {
564
+ const parsed = parseStaticShellCommands(command);
565
+ const operands = parsed ?? parseStaticShellCommands(command, true);
566
+ if (!operands || typeof operands === "string") return [];
567
+ const commands = operands.pipelines.flat();
568
+ if (
569
+ commands.some((words) =>
570
+ ["exec", "nohup", "sudo"].includes(unwrapStaticCommand(words)[0]?.split("/").at(-1) ?? ""),
571
+ )
572
+ )
573
+ return undefined;
574
+ for (const words of commands) {
575
+ const argv = unwrapStaticCommand(words);
576
+ if (
577
+ !isShellWord(argv[0] ?? "") &&
578
+ argv.some(
579
+ (word, index) =>
580
+ index !== inlineProgramIndex(argv) &&
581
+ (isShellWord(word) ||
582
+ (/^--?[A-Za-z0-9-]+=/.test(word) && isShellWord(word.slice(word.indexOf("=") + 1)))),
583
+ ) &&
584
+ !hasProvedShellDataOperands(argv) &&
585
+ !isStaticHerdrPrompt(command)
586
+ )
587
+ return undefined;
588
+ }
589
+ const shells = commands.filter((words) => isShellWord(unwrapStaticCommand(words)[0] ?? ""));
590
+ if (!shells.length) return [];
591
+ if (
592
+ !parsed ||
593
+ typeof parsed === "string" ||
594
+ !literalBindingText(command, "", "") ||
595
+ new TextEncoder().encode(command).byteLength > 65_536 ||
596
+ commands.some((words) => unwrapStaticCommand(words)[0]?.split("/").at(-1) === "cd")
597
+ )
598
+ return undefined;
599
+ const bodies: string[] = [];
600
+ for (const words of shells) {
601
+ const argv = unwrapProofCommand(words, "\0");
602
+ if (!argv || !SHELL_IDENTITIES.has(argv[0] ?? "")) return undefined;
603
+ const name = argv[0]?.split("/").at(-1);
604
+ if (argv.length === 2 && ["--version", "--help"].includes(argv[1] ?? "")) continue;
605
+ const index = name === "zsh" && argv[1] === "-f" ? 2 : 1;
606
+ if ((name === "zsh" && index !== 2) || argv[index] !== "-c" || argv.length !== index + 2)
607
+ return undefined;
608
+ bodies.push(argv[index + 1] ?? "");
609
+ }
610
+ return bodies;
611
+ }
612
+
613
+ function isProvedLiteralShellBody(body: string): boolean {
614
+ if (!body.trim()) return true;
615
+ const materialized = materializeLiteralShell(body)?.command ?? body;
616
+ const parsed = parseStaticShellCommands(materialized);
617
+ if (!parsed || typeof parsed === "string" || !hasProvedNavigationSequence(materialized))
618
+ return false;
619
+ if (isStaticHerdrPrompt(materialized)) return true;
620
+ return parsed.pipelines.flat().every((words) => {
621
+ const argv = unwrapProofCommand(words, "\0");
622
+ if (!argv) return false;
623
+ if (argv[0]?.split("/").at(-1) === "env") return false;
624
+ const name = unwrapStaticCommand(argv)[0]?.split("/").at(-1) ?? "";
625
+ if (isShellWord(name))
626
+ return (
627
+ literalShellBodies(
628
+ words.map((word) => "'" + word.replaceAll("'", "'\"'\"'") + "'").join(" "),
629
+ ) !== undefined
630
+ );
631
+ return (
632
+ isProvedCwdSubprocessRole(unwrapStaticCommand(argv)) ||
633
+ (name === "bun" && isSafeLocalAssignmentCommand(argv) && !isUnsupportedInlineRuntime(argv)) ||
634
+ isStaticNavigationCommand(argv) ||
635
+ isLiteralHerdrRead(argv) ||
636
+ (argv.length === 1 && ["true", "false", "pwd"].includes(name))
637
+ );
638
+ });
639
+ }
640
+
533
641
  /** A complete stdin-only JSON projection, with no suffix, imports or executable input. */
534
642
  function isLiteralStdinJsonDisplay(argv: string[]): boolean {
535
643
  if (argv.length !== 3 || !["python", "python3"].includes(argv[0] ?? "") || argv[1] !== "-c")
@@ -1858,6 +1966,7 @@ function hasEnvironmentRead(
1858
1966
  command: string,
1859
1967
  allowedNames: Set<string>,
1860
1968
  allowInventory = hasProvedPackageInventoryInvocation(command),
1969
+ replayedShellBodies: string[] = [],
1861
1970
  ): boolean {
1862
1971
  if (isStaticHerdrPrompt(command)) return false;
1863
1972
  if (isHerdrPrompt(command)) return true;
@@ -1926,7 +2035,13 @@ function hasEnvironmentRead(
1926
2035
  ) {
1927
2036
  return true;
1928
2037
  }
1929
- if (unwrapped.some((argv) => hasStaticEnvironmentRead(argv, allowedNames, allowInventory))) {
2038
+ if (
2039
+ unwrapped.some(
2040
+ (argv) =>
2041
+ !(SHELL_IDENTITIES.has(argv[0] ?? "") && replayedShellBodies.includes(argv.at(-1) ?? "")) &&
2042
+ hasStaticEnvironmentRead(argv, allowedNames, allowInventory),
2043
+ )
2044
+ ) {
1930
2045
  return true;
1931
2046
  }
1932
2047
 
@@ -2743,7 +2858,7 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2743
2858
  return getDangerousBashReason(command) !== null;
2744
2859
  }
2745
2860
 
2746
- function getDangerousBashReason(command: string): string | null {
2861
+ function getDangerousBashReason(command: string, shellDepth = 0): string | null {
2747
2862
  const originalCommand = command;
2748
2863
  const allowPackageInventory = hasProvedPackageInventoryInvocation(originalCommand);
2749
2864
  const rawEnvironmentSource =
@@ -2757,20 +2872,30 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2757
2872
  if (rawEnvironmentSource && hasEnvironmentMutationPrefix(originalCommand))
2758
2873
  return "blocked by the environment/execution policy after an unproved environment mutation";
2759
2874
  command = normalizeProgramHeredocs(command);
2875
+ const shellBodies = literalShellBodies(command);
2876
+ if (!shellBodies || (shellBodies.length && shellDepth >= 8))
2877
+ return "cannot prove a bounded literal shell invocation";
2878
+ for (const body of shellBodies) {
2879
+ const reason = getDangerousBashReason(body, shellDepth + 1);
2880
+ if (reason || getBlockedCliTool(body, true, shellDepth + 1))
2881
+ return reason ?? "invokes a blocked CLI from a shell body";
2882
+ if (!isProvedLiteralShellBody(body)) return "cannot prove a closed literal shell body";
2883
+ }
2760
2884
  const proof = materializeLiteralShell(command);
2761
2885
  if (!proof && /^\s*for\b/.test(command)) return "cannot prove a bounded literal shell loop";
2762
2886
  if (proof) {
2763
2887
  if (proof.paths.some((path) => hasSensitiveFileRead("cat " + path, isPathBlocked)))
2764
2888
  return "accesses a sensitive file path";
2765
2889
  command = proof.command;
2766
- if (getBlockedCliTool(command)) return "invokes a blocked CLI from a literal expansion";
2890
+ if (getBlockedCliTool(command, true, shellDepth))
2891
+ return "invokes a blocked CLI from a literal expansion";
2767
2892
  }
2768
2893
  for (const nested of [
2769
2894
  ...inlineSubprocessCommands(command),
2770
2895
  ...inlinePackageCommands(command, allowPackageInventory),
2771
2896
  ]) {
2772
- const reason = getDangerousBashReason(nested);
2773
- if (reason || getBlockedCliTool(nested))
2897
+ const reason = getDangerousBashReason(nested, shellDepth);
2898
+ if (reason || getBlockedCliTool(nested, true, shellDepth))
2774
2899
  return reason ?? "invokes a blocked CLI from a script";
2775
2900
  }
2776
2901
  if (
@@ -2786,7 +2911,21 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2786
2911
  }
2787
2912
  if (
2788
2913
  hasEnvironmentSourceAccess(command, rawEnvironmentSource, allowedEnvironmentVariables) ||
2789
- hasEnvironmentRead(command, allowedEnvironmentVariables, allowPackageInventory)
2914
+ hasEnvironmentRead(
2915
+ command,
2916
+ allowedEnvironmentVariables,
2917
+ allowPackageInventory,
2918
+ shellBodies.filter((body) => {
2919
+ const parsed = parseStaticShellCommands(body);
2920
+ return (
2921
+ parsed &&
2922
+ typeof parsed !== "string" &&
2923
+ parsed.pipelines
2924
+ .flat()
2925
+ .every((words) => isPassiveTextCommand(unwrapStaticCommand(words)))
2926
+ );
2927
+ }),
2928
+ )
2790
2929
  ) {
2791
2930
  return "blocked by the environment/execution policy; permitted access or non-execution could not be established for this command form";
2792
2931
  }
@@ -2828,16 +2967,24 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
2828
2967
  function getBlockedCliTool(
2829
2968
  command: string,
2830
2969
  expand = true,
2970
+ shellDepth = 0,
2831
2971
  ): { name: string; wrapper: string } | null {
2832
2972
  command = normalizeProgramHeredocs(command);
2973
+ const shellBodies = literalShellBodies(command);
2974
+ if (shellBodies && shellDepth < 8) {
2975
+ for (const body of shellBodies) {
2976
+ const blocked = getBlockedCliTool(body, true, shellDepth + 1);
2977
+ if (blocked) return blocked;
2978
+ }
2979
+ }
2833
2980
  const proof = expand ? materializeLiteralShell(command) : undefined;
2834
2981
  if (proof) {
2835
- const originalBlocked = getBlockedCliTool(command, false);
2982
+ const originalBlocked = getBlockedCliTool(command, false, shellDepth);
2836
2983
  if (originalBlocked) return originalBlocked;
2837
2984
  command = proof.command;
2838
2985
  }
2839
2986
  for (const nested of inlineSubprocessCommands(command)) {
2840
- const blocked = getBlockedCliTool(nested);
2987
+ const blocked = getBlockedCliTool(nested, true, shellDepth);
2841
2988
  if (blocked) return blocked;
2842
2989
  }
2843
2990
  const staticCommands = parseStaticShellCommands(command);