fix(security): sandbox sympy expression parsing against arbitrary code execution (#42) - #51
Merged
Merged
Conversation
Assisted-by: Claude Code (sonnet subagent) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Assisted-by: Claude Code (sonnet subagent) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Assisted-by: Claude Code (sonnet subagent) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…#42) Assisted-by: Claude Code (sonnet subagent) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on (#42) Add regression tests ensuring sympy's whitelisted string-accepting constructors (Float, Rational, Integer, S) cannot execute arbitrary code when fed malicious string payloads. Tests cover: - Float/Rational/Integer with lambda/os.system/import payloads - Payloads with and without __ deny-list substrings - Mocking os.system and subprocess.Popen to verify no execution - All entry points: safe_parse_expr, _canonical_key, validate_answer, grader Test count: +5 parametrized cases across 4 test functions (56 total safety tests). Assisted-by: Claude Code (haiku subagent) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Closes #42
Vulnerability
sympy.parse_expr/sympifycompile+evaltheir string input; with unrestricted globals a string like__import__("os").system(...)runs arbitrary code. Reachable via_canonical_key(model-generated choice text) and the eval graders (fixture/model text);MathGenerator.validate_answeris designed for student input (not handler-wired today, fixed preemptively). Confirmed with a liveos.systemPoC.Fix —
src/services/practice/safe_expr.py(new), one helper used everywhere_SAFE_GLOBALScontains only safe math constructors/functions/constants (Symbol, Integer, Rational, Float, sqrt, trig, log, exp, factorial, gcd, …) with__builtins__stripped. Crucially it does NOT includesympify/S/parse_expr/lambdify— anything that re-parses/compiles a string — so those degrade to inert auto-symbols instead of executing.__, backtick).global_dict/local_dictforce-set so a caller can't reintroduce the bypass._canonical_key,_parse_answer, 3 grader sites) rewired;simplify()on live sympy objects left untouched; each site's existing fallback preserved.Review journey (this is why it took the rounds it did)
import *+ deny-list): a fresh review executed a real RCE viasympify("_"+"_"+"imp"+"ort"+…)— the namespace itself contained a re-parsing callable, and a concatenated payload evaded the static deny-list. Green tests missed it.Float/Rational/Integerconstructors) and could not break it.Verification
run_eval: PASS. black/isort/flake8 clean.assert_not_calledacross all call sites for every payload class); legit math parses identically.🤖 Generated with Claude Code
https://claude.ai/code/session_016v4C1daTwNakL6QDbmy3MJ