T09 · Insecure Skill Coding Practices
- Location
scripts/verify.py:63- Finding
Unsafe SymPy parsing of JSON-controlled expressions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/verify.py:63-103
Vulnerability Type: Unsafe expression evaluation
Risk Level: HighThe verification file is generated from worksheet content and may therefore contain user-influenced values. Multiple fields from that file are passed directly to
sympy.sympifyorsympy.nsimplifywithout an allowlist, grammar validation, input-size limits, or process isolation.python elif ptype == "solve": x = symbols('x') expr = sympify(p["expr"]) roots = sorted([nsimplify(r) for r in solve(expr, x)]) expected = sorted([nsimplify(e) for e in p["expected"]]) ok = roots == expected results.append((pid, "PASS" if ok else "FAIL", f"solve({p['expr']}) → {roots} (expected {expected})")) elif ptype == "zeros": x = symbols('x') expr = sympify(p["expr"]) roots = sorted(set([nsimplify(r) for r in solve(expr, x)])) expected = sorted(set([nsimplify(e) for e in p["expected"]])) ok = roots == expected results.append((pid, "PASS" if ok else "FAIL", f"zeros({p['expr']}) → {roots} (expected {expected})")) elif ptype == "factor": x = symbols('x') expr = sympify(p["expr"]) factored = str(factor(expr)) expected = str(sympify(p["expected"])) ok = simplify(sympify(factored) - sympify(expected)) == 0 results.append((pid, "PASS" if ok else "FAIL", f"factor({p['expr']}) → {factored} (expected {p['expected']})")) elif ptype == "expand": x = symbols('x') expr = sympify(p["expr"]) expanded = str(expand(expr)) expected = str(sympify(p["expected"])) ok = simplify(sympify(expanded) - sympify(expected)) == 0 results.append((pid, "PASS" if ok else "FAIL", f"expand({p['expr']}) → {expanded} (expected {p['expected']})")) elif ptype == "eval": subs = {symbols(k): nsimplify(v) for k, v in p["at"].items()} expr = sympify(p["expr"]) ...[truncated 2743 chars]- Remediation
View remediation
Remediation Suggestions
- Do not pass untrusted strings directly to
sympifyornsimplify. - Define and enforce a small arithmetic grammar supporting only required constructs, such as:
- Integer and bounded rational literals
- Explicitly approved variables such as
xandy - Addition, subtraction, multiplication, division, and bounded exponentiation
- A narrowly defined allowlist of mathematical functions
- Parse input into an abstract syntax tree and reject attribute access, indexing, comprehensions, imports, function definitions, unknown identifiers, and other Python constructs before creating SymPy objects.
- If SymPy parsing remains necessary, provide explicit restricted symbol and function dictionaries rather than relying on default parser namespaces.
- Validate the JSON schema before processing, including maximum limits for:
- File size and problem count
- Expression length and nesting depth
- Integer digit count and exponent magnitude
- Number of symbols and function calls
- Apply execution timeouts, memory limits, and CPU limits around each symbolic operation.
- Run verification in a sandboxed subprocess with no network access and access only to the verification file.
- Avoid exposing secrets or broad filesystem permissions to the verification process.
- Add adversarial tests covering parser injection, very large exponents, deeply nested expressions, expensive factorization, and symbolic equation-solving timeouts.
- Revise the documentation so that its security claims accurately describe the remaining parser risk.
- Do not pass untrusted strings directly to
