fix(tools): keep non-bash -c invocations covered by the shell guard

The _bash_exec_payload delegation rejected short-option bundles with
letters outside bash's alphabet, so 'zsh -yc', 'dash -Vc' and 'ksh -Gc'
scripts stopped being scanned — a fail-open regression for shells the
guard's _SHELL_EXECUTABLES explicitly covers. Try the bash grammar
first (catches operand-hidden -c), then fall back to the permissive
positional scan; a block-guard fails closed.
This commit is contained in:
kshitij
2026-08-08 14:53:41 +05:30
parent df0a5c3ee4
commit c8e558c72c
2 changed files with 21 additions and 6 deletions

View File

@@ -84,6 +84,9 @@ class TestBlocksMutationsInSourceRepo:
"bash -lc 'git switch main'",
"bash -o pipefail -c 'git checkout main'",
"bash +O extglob -c 'git checkout main'",
"zsh -yc 'git checkout main'",
"dash -Vc 'git checkout main'",
"ksh -Gc 'git checkout main'",
],
)
def test_wrappers_and_nested_shells(self, repo, command):

View File

@@ -331,14 +331,26 @@ def _cd_target(executable: str, args: list[str], cwd: Path) -> Path | None:
def _shell_script_arg(args: list[str]) -> str | None:
"""Return the script string owned by a shell's ``-c``, if present.
Delegates to approval.py's ``_bash_exec_payload``, which parses bash's
real option grammar (``-O/-o`` consume the next argument, short-option
bundles, ``--init-file``/``--rcfile``). A naive "leading option containing
'c'" scan fails open on ``bash -o pipefail -c '<script>'`` — the ``-o``
operand hides the ``-c`` and the script is never scanned.
Tries approval.py's ``_bash_exec_payload`` first: it parses bash's real
option grammar (``-O/-o`` consume the next argument, short-option
bundles, ``--init-file``/``--rcfile``), catching payloads a naive scan
misses — ``bash -o pipefail -c '<script>'`` hides the ``-c`` behind an
operand. When it finds no ``-c``, fall back to the permissive positional
scan: ``_SHELL_EXECUTABLES`` also covers zsh/dash/ksh, whose option
letters (``zsh -yc``, ``dash -Vc``) fall outside bash's alphabet and
would otherwise make this block-guard fail open.
"""
has_c, payload = _bash_exec_payload(args)
return payload if has_c else None
if has_c:
return payload
for index, arg in enumerate(args):
if arg == "--":
break
if arg.startswith("-") and "c" in arg[1:]:
return args[index + 1] if index + 1 < len(args) else None
if not arg.startswith("-"):
break
return None
def _heredoc_specs(line: str) -> list[_Heredoc]: