fix(security): replace bare eval() with simpleeval in recipe conditions#3486
fix(security): replace bare eval() with simpleeval in recipe conditions#3486
Conversation
…n evaluator (#3485) Replace the unsafe `eval()` call in `Step.evaluate_condition()` with `simpleeval.EvalWithCompoundTypes`, eliminating arbitrary code execution risk from recipe condition strings. Adds simpleeval>=0.9.13 dependency and security regression tests verifying __import__ and open() are blocked. Closes #3485 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Repo Guardian - PassedAll 4 changed files are durable, reusable project artifacts. No ephemeral content or temporary scripts were detected.
|
PR #3486 Validation Report1. Scope Check: PASSFiles changed (exactly 4, as expected):
amplihack-memory-lib check: NOT modified in uv.lock diff (confirmed via No unrelated changes detected. 2. QA-Team Outside-In Tests: PASS (13/13)Two gadugi YAML scenarios written and executed via companion pytest file: Scenario 1: Recipe conditions still evaluate correctly (
Scenario 2: Malicious eval attempts blocked (
Execution evidence: 3. Quality Audit (3 Cycles)Cycle 1 — SEEK: Reviewed the diff for correctness and security properties.
Cycle 1 — VALIDATE: No issues found. The implementation is a clean drop-in replacement. Cycle 2 — SEEK: Checked for behavioral differences between
Cycle 2 — VALIDATE: No regression risk. simpleeval supports all operators used in recipe conditions. Cycle 3 — SEEK: Checked dependency and lock file quality.
Cycle 3 — VALIDATE: Dependency addition is clean. No issues. FIX: No fixes needed across all 3 cycles. 4. CI Status: PASS (with expected exception)
Atlas failure explanation: The Atlas CI reports layers 2 (compile-deps) and 8 (ast-lsp-bindings) as stale because the PR touches 5. Verdict: READY TO MERGEAll merge criteria pass:
|
PR Triage ReportRisk: Medium | Priority: High | Security Fix Assessment
Findings
Recommendation🔒 Security review then merge. This closes a genuine code execution vulnerability. The fix is conservative (simpleeval is well-known), tests verify both the safety boundary and functional compatibility, and the change is minimal in scope.
|
… workflow (2026-03-24) Document three PRs merged in the last 24 hours following the Diátaxis framework: - PR #3487: recipe runner progress banners ([recipe-runner] Preparing/Launching to stderr) and automatic progress file cleanup on completion - PR #3486: simpleeval replaces bare eval() in Step.evaluate_condition() — blocks __import__ and open(), adds simpleeval>=0.9.13 dependency - PR #3425: version-check.yml is advisory-only; VERSION_WORKFLOWS.md already updated in that PR, no further changes needed Files changed: - docs/recipes/RECENT_FIXES_MARCH_2026.md: two new sections + v0.9.2 history entry - docs/recipes/README.md: update condition field description to note simpleeval - docs/recipes/STREAMING_OUTPUT.md: add startup banners section Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
eval()inStep.evaluate_condition()withsimpleeval.EvalWithCompoundTypesto eliminate arbitrary code execution risk from recipe condition stringssimpleeval>=0.9.13to project dependencies__import__()andopen()are blocked by the new evaluatorCloses #3485
Changes
src/amplihack/recipes/models.pyeval()withEvalWithCompoundTypes, remove# noqa: S307pyproject.tomlsimpleeval>=0.9.13dependencyuv.locktests/unit/recipes/test_models.pytest_in_operator,test_import_blocked,test_open_blocked); consolidate importsTest plan
pytest tests/unit/recipes/test_models.py— 24/24)True->'true') still workscount >= 4) still workis Nonechecks still workinoperator works ('API' in description)__import__('os').system(...)is blocked (returns True via default-on-error)open('/etc/passwd').read()is blockedamplihack-memory-libversion unchanged at v0.2.0 in uv.lock🤖 Generated with Claude Code