Severity: Medium
The structured-candidate JSON codec has a marker-collision vulnerability that silently corrupts candidates, and non-finite floats can enter "JSON-safe" checkpoint payloads despite the documented contract.
1. Marker collision silently corrupts legitimate candidates — src/variopt/spaces/serialization.py:30-45, 72-84
A legitimate mapping-shaped candidate whose key happens to equal an internal marker round-trips to the wrong type:
space_candidate_to_dict({"__variopt_bytes__": "abcd"})
# decodes back as b'\xab\xcd', not the original mapping
Any structured space with such a key silently corrupts every checkpoint containing it — this surfaces later as a confusing space.validate failure, or (if the space happens to accept bytes) silent state corruption with no error at all.
Relatedly, space_candidate_to_dict's docstring promises a TypeError for unsupported values, but no such branch exists — unsupported leftovers (e.g. a stray list or None) pass through silently and only fail later at decode or at json.dumps.
2. Non-finite floats pass validation and break the JSON-safe contract — src/variopt/algorithms/population/csa/banking/growth/state.py:36, banking/bank.py:85-94
CSABankGrowthPolicy(initial_energy_gap_limit=float("inf")) passes validation (only < 0 is rejected) and serializes as the non-standard JSON token Infinity, contradicting the checkpoint docs' "JSON-safe" claim. Conversely, BankEntry.from_dict accepts inf/NaN (and True) for value with no finiteness gate on decode.
Fix direction
Escape marker-shaped keys on encode (e.g. wrap plain mappings whose key set matches a marker), or reject them with a clear TypeError; add the missing else-branch TypeError for unsupported leftovers. Validate finiteness at the growth-policy to_dict/from_dict boundary, or explicitly require/document json.dumps(..., allow_nan=False) for checkpoint writers.
Severity: Medium
The structured-candidate JSON codec has a marker-collision vulnerability that silently corrupts candidates, and non-finite floats can enter "JSON-safe" checkpoint payloads despite the documented contract.
1. Marker collision silently corrupts legitimate candidates —
src/variopt/spaces/serialization.py:30-45, 72-84A legitimate mapping-shaped candidate whose key happens to equal an internal marker round-trips to the wrong type:
Any structured space with such a key silently corrupts every checkpoint containing it — this surfaces later as a confusing
space.validatefailure, or (if the space happens to accept bytes) silent state corruption with no error at all.Relatedly,
space_candidate_to_dict's docstring promises aTypeErrorfor unsupported values, but no such branch exists — unsupported leftovers (e.g. a straylistorNone) pass through silently and only fail later at decode or atjson.dumps.2. Non-finite floats pass validation and break the JSON-safe contract —
src/variopt/algorithms/population/csa/banking/growth/state.py:36,banking/bank.py:85-94CSABankGrowthPolicy(initial_energy_gap_limit=float("inf"))passes validation (only< 0is rejected) and serializes as the non-standard JSON tokenInfinity, contradicting the checkpoint docs' "JSON-safe" claim. Conversely,BankEntry.from_dictaccepts inf/NaN (andTrue) forvaluewith no finiteness gate on decode.Fix direction
Escape marker-shaped keys on encode (e.g. wrap plain mappings whose key set matches a marker), or reject them with a clear
TypeError; add the missing else-branchTypeErrorfor unsupported leftovers. Validate finiteness at the growth-policyto_dict/from_dictboundary, or explicitly require/documentjson.dumps(..., allow_nan=False)for checkpoint writers.