Files
plyngent/tests/test_tools/test_command_scan.py
T
NCBM 2b3b2073a0 core/tools: only value-taking options may swallow the program
The wrapper option table listed flags as if they consumed the next token, so
the program behind them was skipped: `exec -c git -c init.defaultBranch=main
init` resolved to `init` and the policy denied it as a denylisted basename,
while `exec -c rm -rf /`, `command -p rm -rf /` and `command -v rm` hid `rm`
from the denylist completely and `exec -c python -c …` lost the interpreter
review. Drop the entries that take no value — `exec -c`/`-l`, the whole
`command` entry, `pkexec --disable-internal-agent`, `su -p`, and `xargs -e`
(its argument is optional, and the jammed form needs no entry) — so the
resolved program is the one that really runs.
2026-10-06 21:50:21 +08:00

112 lines
4.0 KiB
Python

from __future__ import annotations
from plyngent.tools.command_scan import basename, is_interpreter, unwrap_command
def test_basename() -> None:
assert basename("/usr/bin/python3.12") == "python3.12"
assert basename("C:\\Tools\\RM.EXE") == "rm"
assert basename("pdm") == "pdm"
def test_unwrap_plain_command() -> None:
scan = unwrap_command(["grep", "-c", "needle", "f.txt"])
assert scan.wrappers == ()
assert scan.offset == 0
assert scan.base == "grep"
assert scan.command == ("grep", "-c", "needle", "f.txt")
assert scan.self_review == ()
def test_unwrap_env_assignment_and_options() -> None:
scan = unwrap_command(["env", "FOO=1", "nice", "-n", "10", "python", "-c", "print(1)"])
assert scan.wrappers == ("env", "nice")
assert scan.offset == 5
assert scan.base == "python"
assert scan.command == ("python", "-c", "print(1)")
def test_unwrap_privileged_wrapper() -> None:
scan = unwrap_command(["sudo", "-u", "root", "--", "python", "script.py"])
assert scan.wrappers == ("sudo",)
assert scan.base == "python"
assert scan.command == ("python", "script.py")
def test_unwrap_wrapper_duration_of_its_own() -> None:
scan = unwrap_command(["timeout", "30s", "bash", "-c", "ls"])
assert scan.wrappers == ("timeout",)
assert scan.base == "bash"
scan = unwrap_command(["timeout", "5"])
assert scan.wrappers == ()
assert scan.base == "timeout"
def test_unwrap_run_launcher_skips_its_options() -> None:
scan = unwrap_command(["uv", "run", "--with", "rich", "python", "-c", "print(1)"])
assert scan.wrappers == ("uv",)
assert scan.base == "python"
assert scan.command == ("python", "-c", "print(1)")
def test_unwrap_nested_wrapper_and_launcher() -> None:
scan = unwrap_command(["nohup", "pdm", "run", "python", "script.py"])
assert scan.wrappers == ("nohup", "pdm")
assert scan.offset == 3
assert scan.base == "python"
assert scan.self_review == ("nohup",)
def test_unwrap_stops_when_no_program_follows() -> None:
for argv in (["env"], ["timeout", "5"], ["pdm", "run"], ["nohup"]):
scan = unwrap_command(argv)
assert scan.wrappers == (), argv
assert scan.offset == 0, argv
assert scan.base == argv[0], argv
def test_unwrap_value_free_flags_do_not_swallow_the_program() -> None:
"""Only options that consume the next token may be listed as value-taking.
``exec -c`` (empty environment) and ``exec -l`` (login), ``command
-p|-v|-V``, ``pkexec --disable-internal-agent`` and ``su -p`` are flags: a
table entry for them ate the program behind them, so ``exec -c rm -rf /``
hid ``rm`` from the denylist while ``exec -c git -c … init`` resolved to
``init``.
"""
cases = [
(["exec", "-c", "rm", "-rf", "/"], "rm"),
(["exec", "-c", "git", "-c", "init.defaultBranch=main", "init"], "git"),
(["exec", "-l", "python", "script.py"], "python"),
(["command", "-v", "rm"], "rm"),
(["command", "-p", "rm", "-rf", "/"], "rm"),
(["pkexec", "--disable-internal-agent", "rm", "-rf", "/"], "rm"),
(["su", "-p", "python", "script.py"], "python"),
# Options that do take a value still consume it.
(["exec", "-a", "name", "rm", "-rf", "/"], "rm"),
(["xargs", "-e", "rm", "-rf", "/"], "rm"),
]
for argv, base in cases:
scan = unwrap_command(argv)
assert scan.base == base, argv
assert scan.command[0] == base, argv
def test_unwrap_empty_argv() -> None:
scan = unwrap_command([])
assert scan.base == ""
assert scan.command == ()
def test_is_interpreter_ignores_version_suffix() -> None:
assert is_interpreter("python")
assert is_interpreter("python3.12")
assert is_interpreter("node20")
assert is_interpreter("php8.2")
assert is_interpreter("sqlite3")
assert is_interpreter("bash")
assert not is_interpreter("grep")
assert not is_interpreter("od")
assert not is_interpreter("phpmyadmin")
assert not is_interpreter("")