fix(security): Python security vulnerabilities audit fixes#34
Merged
Conversation
Findings from security audit 2026-05-23:
CWE-78 — os.system() in proc/interactive.py
Replace os.system() with subprocess.run(). os.system() invokes /bin/sh
with a shell-interpolated string; subprocess.run() passes arguments as a
list, eliminating the shell-expansion attack surface. Also fixes return-
code capture: previously the inner command exit status was read from a
temp file written by `echo $?` inside the shell string — now it is taken
directly from proc.returncode.
CWE-78 / unsafe eval() in tools/math/evaluator.py
CalculatorTool._run() called eval(expression, {"__builtins__": None}, …).
Setting __builtins__ to None is not a sufficient sandbox: Python's dunder-
attribute chain (e.g. ().__class__.__bases__[0].__subclasses__()) can reach
arbitrary built-ins regardless. Replaced with sympy.sympify().evalf() which
performs safe symbolic evaluation without executing arbitrary Python.
CWE-502 / unsafe eval() in tools/math/validator.py
MathValidator._safe_eval() used an AST node whitelist containing ast.Num,
which is deprecated since Python 3.8 and absent in 3.10+ (replaced by
ast.Constant). This caused the whitelist to reject valid numeric literals
on supported Python versions. The underlying eval() call is also vulnerable
via the same dunder-escape technique. Removed the ast import and the eval()
call entirely; replaced with sympy.sympify().evalf().
Path traversal in tools/expert.py
read_files_with_limit() opened arbitrary file paths supplied by the agent
with no containment check. Added _check_safe_path() (mirrors the guard
already present in read_file.py, write_file.py, and file_str_replace.py)
to reject paths that resolve outside Path.cwd().
Dependency CVEs in pyproject.toml / requirements-dev.txt
- GitPython: bumped lower bound from >=3.1 to >=3.1.50 to fix
CVE-2026-42215, CVE-2026-42284, CVE-2026-44244, GHSA-mv93-w799-cj2w.
- aider-chat: bumped lower bound from >=0.69.1 to >=0.82.0 (picks up the
patched GitPython transitive dep and other upstream security fixes).
- requirements-dev.txt: replaced pinned 0.69.* / 1.49.* with open lower-
bound constraints consistent with pyproject.toml.
Co-Authored-By: claude-flow <ruv@ruv.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Security audit performed 2026-05-23 against
sparc_cli/. Five real vulnerabilities found and fixed; no style changes.1. CWE-78 — Shell Injection via
os.system()(proc/interactive.py)File:
sparc_cli/proc/interactive.pyrun_interactive_command()usedos.system(f"script -q -c {shlex.quote(shell_cmd)} …"). Even thoughshlex.quotewraps the outer argument,os.systemalways spawns/bin/sh -c, which means any shell metacharacter that survives into the interpolated string can be exploited. Additionally, the return code was read from a temp file viaecho $?written inside the shell string rather than being captured from the process itself.Fix: Replaced
os.system()withsubprocess.run()using a list argument (no shell expansion). The return code is now taken fromproc.returncode. Removed theretcode_filetemp file that was only needed to work around theos.systemlimitation.2. CWE-78 — Unsafe
eval()Sandbox (tools/math/evaluator.py)File:
sparc_cli/tools/math/evaluator.py,CalculatorTool._run()eval(expression, {"__builtins__": None}, safe_dict)is not a safe sandbox. Setting__builtins__toNonedoes not prevent escape — the dunder-attribute chain (().__class__.__bases__[0].__subclasses__()) reaches arbitrary Python built-ins regardless:Fix: Replaced
eval()entirely withsympy.sympify(expression).evalf(), which performs symbolic math evaluation without executing arbitrary Python.3. CWE-502 — Unsafe
eval()+ Broken AST Whitelist (tools/math/validator.py)File:
sparc_cli/tools/math/validator.py,MathValidator._safe_eval()Two compounding issues:
ast.Num, which is deprecated since Python 3.8 and removed in 3.10+. On Python 3.10+ (the project's minimum supported version perrequires-python) numeric literals produceast.Constant, which is not in the whitelist, causingValueErrorfor all plain numeric inputs.eval(compile(tree, …))call is still vulnerable to the same dunder-escape technique as finding [BUG] can't install or run docker #2.Fix: Removed the
astimport and theeval()call entirely. Replaced_safe_eval()withsympy.sympify().evalf().4. Path Traversal (
tools/expert.py)File:
sparc_cli/tools/expert.py,read_files_with_limit()read_files_with_limit()opened file paths supplied by the LLM agent without any containment check. An agent could pass../../../etc/passwdor any absolute path and the function would read it. The companion tools (read_file.py,write_file.py,file_str_replace.py) all had a_check_safe_path()guard;expert.pywas missing it.Fix: Added
_check_safe_path()(identical guard pattern already used elsewhere) and called it at the top of the per-file loop inread_files_with_limit().5. Dependency CVEs (
pyproject.toml,requirements-dev.txt)GitPython>=3.1>=3.1.50aider-chat>=0.69.1>=0.82.0requirements-dev.txtaider-chat==0.69.*(pinned)>=0.82.0requirements-dev.txtplaywright==1.49.*(pinned)>=1.49.0Files changed
sparc_cli/proc/interactive.pysparc_cli/tools/math/evaluator.pysparc_cli/tools/math/validator.pysparc_cli/tools/expert.pypyproject.tomlrequirements-dev.txt🤖 Generated with claude-flow