Skip to content

fix(security): Python security vulnerabilities audit fixes#34

Merged
ruvnet merged 1 commit into
mainfrom
fix/security-audit-2026-05-23
May 23, 2026
Merged

fix(security): Python security vulnerabilities audit fixes#34
ruvnet merged 1 commit into
mainfrom
fix/security-audit-2026-05-23

Conversation

@ruvnet
Copy link
Copy Markdown
Owner

@ruvnet ruvnet commented May 23, 2026

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.py

run_interactive_command() used os.system(f"script -q -c {shlex.quote(shell_cmd)} …"). Even though shlex.quote wraps the outer argument, os.system always 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 via echo $? written inside the shell string rather than being captured from the process itself.

Fix: Replaced os.system() with subprocess.run() using a list argument (no shell expansion). The return code is now taken from proc.returncode. Removed the retcode_file temp file that was only needed to work around the os.system limitation.


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__ to None does not prevent escape — the dunder-attribute chain (().__class__.__bases__[0].__subclasses__()) reaches arbitrary Python built-ins regardless:

>>> eval('().__class__.__bases__[0].__subclasses__()', {'__builtins__': None}, {})
# Returns list of all subclasses — full escape confirmed

Fix: Replaced eval() entirely with sympy.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:

  • The AST node whitelist included ast.Num, which is deprecated since Python 3.8 and removed in 3.10+. On Python 3.10+ (the project's minimum supported version per requires-python) numeric literals produce ast.Constant, which is not in the whitelist, causing ValueError for all plain numeric inputs.
  • Even if the whitelist were correct, the 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 ast import and the eval() call entirely. Replaced _safe_eval() with sympy.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/passwd or 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.py was missing it.

Fix: Added _check_safe_path() (identical guard pattern already used elsewhere) and called it at the top of the per-file loop in read_files_with_limit().


5. Dependency CVEs (pyproject.toml, requirements-dev.txt)

Package Old constraint New constraint CVEs addressed
GitPython >=3.1 >=3.1.50 CVE-2026-42215, CVE-2026-42284, CVE-2026-44244, GHSA-mv93-w799-cj2w
aider-chat >=0.69.1 >=0.82.0 Upstream security fixes; pulls in patched GitPython transitively
requirements-dev.txt aider-chat ==0.69.* (pinned) >=0.82.0 Same as above
requirements-dev.txt playwright ==1.49.* (pinned) >=1.49.0 Unpin to allow patched releases

Files changed

  • sparc_cli/proc/interactive.py
  • sparc_cli/tools/math/evaluator.py
  • sparc_cli/tools/math/validator.py
  • sparc_cli/tools/expert.py
  • pyproject.toml
  • requirements-dev.txt

🤖 Generated with claude-flow

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>
@ruvnet ruvnet merged commit 9656782 into main May 23, 2026
3 of 6 checks passed
@ruvnet ruvnet deleted the fix/security-audit-2026-05-23 branch May 23, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant