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:
@@ -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):
|
||||
|
||||
@@ -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]:
|
||||
|
||||
Reference in New Issue
Block a user