Skip to content

fix: cap integer products, concatenation, nested collections and % formatting in the restricted evaluator - #687

Merged
fgmacedo merged 1 commit into
mainfrom
fix/restricted-evaluator-bounds
Oct 13, 2026
Merged

fgmacedo merged 1 commit into
mainfrom
fix/restricted-evaluator-bounds

Conversation

@fgmacedo

Copy link
Copy Markdown
Owner

Why

The restricted evaluator used by io.load() with the default trusted=False promises that a small expression cannot exhaust CPU or memory. The caps added in 3.2.1 covered ** and sequence replication with * only, so a small untrusted document could still build a huge value through other operators or through collections that repeat a reference.

What changes

  • int * int is capped by result bits, like **.
  • seq * n, seq + seq and every list, tuple, set and dict literal are capped by a reference-expanded size (_value_size): a shared reference counts every time it appears, memoized by identity so the measurement stays cheap.
  • % on str/bytes operands (printf-style formatting) is rejected; numeric % is unaffected.
  • docs/io/security.md and the 3.2.2 release notes describe the caps, including the % formatting change under "Behavior changes".

Evidence

  • New cases in tests/test_spec_parser_values.py and tests/io/test_security.py (SCXML, JSON and YAML) for each rejected form and for ordinary arithmetic and collections that keep working.
  • Full suite: 2697 passed, 25 skipped, 44 xfailed, including the W3C SCXML corpus; 100% branch coverage via the pre-commit hook.
  • Two adversarial review rounds; the first found the nested-collection case, now covered.

Not verified

  • in/is comparisons in a restricted expression still fail with a raw KeyError at load time (pre-existing, the document is still rejected); left for a follow-up.

…rmatting in the restricted evaluator

The restricted evaluator capped ** and sequence replication with *, but a
small untrusted document could still build a huge value: a product of two
integers, a sequence concatenation with +, a nested collection that
repeats a reference (cheap to build, but compared, hashed or printed item
by item), or % string formatting sized by its field width.

Cap the integer product, measure every sequence and collection the
evaluator builds by the size of its nested items counted per reference,
and reject % on str and bytes operands.

Signed-off-by: Fernando Macedo <fgmacedo@gmail.com>
@fgmacedo fgmacedo added this to the 3.2.2 milestone Oct 13, 2026
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (0be2460) to head (f43ed07).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #687   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           53        53           
  Lines         5816      5857   +41     
  Branches       926       936   +10     
=========================================
+ Hits          5816      5857   +41     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@fgmacedo fgmacedo self-assigned this Oct 13, 2026
@fgmacedo
fgmacedo merged commit 4d4003d into main Oct 13, 2026
14 checks passed
@fgmacedo
fgmacedo deleted the fix/restricted-evaluator-bounds branch October 13, 2026 22:48
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