fix(tools): parse bash option grammar before extracting the -c script

The guard's _shell_script_arg treated any leading option containing 'c'
as -c and looked no further, so 'bash -o pipefail -c "git checkout
main"' returned None and the script was never scanned (fail-open).
approval.py's _bash_exec_payload already parses bash's real option
grammar (-O/-o consume operands, short-option bundles, --init-file);
delegate to it instead of keeping a second, weaker parser.
This commit is contained in:
kshitij 2026-08-08 14:32:51 +05:30
parent 4cc3ea01f6
commit bb311b3951
2 changed files with 13 additions and 8 deletions

View File

@ -80,6 +80,8 @@ class TestBlocksMutationsInSourceRepo:
"/usr/bin/git checkout main",
"sh -c 'git checkout main'",
"bash -lc 'git switch main'",
"bash -o pipefail -c 'git checkout main'",
"bash +O extglob -c 'git checkout main'",
],
)
def test_wrappers_and_nested_shells(self, repo, command):

View File

@ -10,6 +10,7 @@ from dataclasses import dataclass, field
from pathlib import Path
from tools.approval import (
_bash_exec_payload,
_deobfuscate_shell_word_for_detection,
_iter_shell_command_starts,
_read_shell_word,
@ -308,14 +309,16 @@ def _cd_target(executable: str, args: list[str], cwd: Path) -> Path | None:
def _shell_script_arg(args: list[str]) -> str | None:
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
"""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.
"""
has_c, payload = _bash_exec_payload(args)
return payload if has_c else None
def _heredoc_specs(line: str) -> list[_Heredoc]: