From e9e00d840700be4891ae0c54a24722b68e367cfc Mon Sep 17 00:00:00 2001 From: Christopher McCrow Date: Thu, 23 Apr 2026 23:47:11 +0700 Subject: [PATCH] Whitelist argparse and getopt as safe modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both are pure argv-parsing stdlib modules. Their only side effects are stdout (--help text), stderr (error messages), and sys.exit on bad args. None of these is in the threat model: - print is already whitelisted, so --help / error text to stdout/stderr is consistent with existing policy - sys.exit just terminates the script; it is not code execution, file I/O, or network access - Neither module can read or write files, open sockets, spawn processes, or access the filesystem In practice, almost every legitimate CLI script imports argparse, so listing it as dangerous caused the static analyser to ask for approval on essentially every real-world script — defeating the handler's purpose of auto-approving provably safe code. Deliberately not changed: logging (real FileHandler), sys (sys.modules manipulation, sys.stdin/stdout as file objects), getpass (reads from tty), atexit (deferred exec of unreviewed handlers). These retain defensible I/O or exec vectors. Tests: adds argparse and getopt cases to TestPythonScriptAnalysis, mirroring existing safe-import test style. Full python suite (127 tests) still passes. --- src/dippy/cli/python.py | 7 +++++-- tests/cli/test_python.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/dippy/cli/python.py b/src/dippy/cli/python.py index 1dcb5e7..b626937 100644 --- a/src/dippy/cli/python.py +++ b/src/dippy/cli/python.py @@ -98,6 +98,11 @@ "html", "html.parser", "html.entities", + # Argv parsing (pure string processing; sys.exit/--help to stdout is + # part of the CLI contract, not a security concern — print is already + # whitelisted, and termination is not in the threat model). + "argparse", + "getopt", } ) @@ -205,8 +210,6 @@ "cmd", "shlex", "getpass", - "getopt", - "argparse", # Can sys.exit "logging", # Can write to files "atexit", # CGI (Bandit B412: httpoxy vulnerabilities) diff --git a/tests/cli/test_python.py b/tests/cli/test_python.py index b8fec89..4ac2072 100644 --- a/tests/cli/test_python.py +++ b/tests/cli/test_python.py @@ -125,6 +125,34 @@ class Person: result = check(f"python {script}") assert is_approved(result), "Dataclass script should be approved" + def test_safe_script_argparse_approved(self, check, tmp_path): + """Script using argparse should be approved (pure argv parsing).""" + script = tmp_path / "argparse_script.py" + script.write_text(""" +import argparse + +parser = argparse.ArgumentParser(description="Demo") +parser.add_argument("--count", type=int, default=1) +parser.add_argument("name") +args = parser.parse_args() +print(f"Hello {args.name} x{args.count}") +""") + result = check(f"python {script}") + assert is_approved(result), "argparse script should be approved" + + def test_safe_script_getopt_approved(self, check, tmp_path): + """Script using getopt should be approved (pure argv parsing).""" + script = tmp_path / "getopt_script.py" + script.write_text(""" +import getopt + +opts, args = getopt.getopt(["--name", "Alice"], "", ["name="]) +for opt, val in opts: + print(opt, val) +""") + result = check(f"python {script}") + assert is_approved(result), "getopt script should be approved" + def test_dangerous_import_os_blocked(self, check, tmp_path): """Script importing os should be blocked.""" script = tmp_path / "dangerous_os.py"