Skip to content

Commit 78e2b4c

Browse files
authored
fix: Python 3.10-compatible add_note pollution test; drop em dashes from 3.2.1 notes (#642)
1 parent 0dad86c commit 78e2b4c

4 files changed

Lines changed: 17 additions & 14 deletions

File tree

‎docs/io/security.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,17 @@ every format (SCXML, JSON and YAML).
201201
A set of follow-up advisories hardened the restricted mode further and prompted the
202202
confidentiality/integrity vs availability framing above:
203203

204-
- [GHSA-fj3w-533r-fvf6](https://github.com/fgmacedo/python-statemachine/security/advisories/GHSA-fj3w-533r-fvf6)
205-
— `<data src="file:…">` and `<invoke src="…">` read local files during loading, regardless of
204+
- [GHSA-fj3w-533r-fvf6](https://github.com/fgmacedo/python-statemachine/security/advisories/GHSA-fj3w-533r-fvf6):
205+
`<data src="file:…">` and `<invoke src="…">` read local files during loading, regardless of
206206
`trusted`. Loading now rejects external `src` references unless `trusted=True`, and refuses
207207
`<!DOCTYPE>`/DTD to block XML entity-expansion bombs.
208208
- [GHSA-v3qq-3xvg-m77g](https://github.com/fgmacedo/python-statemachine/security/advisories/GHSA-v3qq-3xvg-m77g)
209-
/ [GHSA-4857-ggqc-p3jc](https://github.com/fgmacedo/python-statemachine/security/advisories/GHSA-4857-ggqc-p3jc)
210-
— a document could write to a dunder/private/protected attribute (notably traversing
209+
/ [GHSA-4857-ggqc-p3jc](https://github.com/fgmacedo/python-statemachine/security/advisories/GHSA-4857-ggqc-p3jc):
210+
a document could write to a dunder/private/protected attribute (notably traversing
211211
`__class__`) and corrupt the shared model class process-wide. Write targets are now confined
212212
to public model attributes on every path segment.
213-
- [GHSA-r8gj-366q-cgvj](https://github.com/fgmacedo/python-statemachine/security/advisories/GHSA-r8gj-366q-cgvj)
214-
— `**`/`*` in the restricted evaluator had no magnitude bound, so a tiny expression could
213+
- [GHSA-r8gj-366q-cgvj](https://github.com/fgmacedo/python-statemachine/security/advisories/GHSA-r8gj-366q-cgvj):
214+
`**`/`*` in the restricted evaluator had no magnitude bound, so a tiny expression could
215215
exhaust CPU or memory. They are now magnitude-capped.
216216

217217
These were released together in 3.2.1.

‎docs/releases/3.2.1.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ See [](../io/security.md).
1515
```{note}
1616
**Am I affected?**
1717
18-
- **Yes** — if you load documents you did **not** author (via `statemachine.io.load(...)` /
18+
- **Yes**, if you load documents you did **not** author (via `statemachine.io.load(...)` /
1919
`build_processor(...)` / `SCXMLProcessor`) with the default `trusted=False`.
20-
- **No** — if you define machines in Python, only load documents you wrote yourself, or already
20+
- **No**, if you define machines in Python, only load documents you wrote yourself, or already
2121
load with `trusted=True` for fully controlled documents.
2222
2323
**Affected versions:** `>= 3.2.0, < 3.2.1` (this attack surface shipped with the

‎statemachine/spec_parser.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def recurse(child):
289289
op_type = type(node.op)
290290
if op_type not in binary_operators:
291291
# e.g. bitwise ``^``/``|``/``<<`` are outside the allowlist. (``**`` and ``*``
292-
# are allowed but magnitude-capped — see ``binary_operators``.)
292+
# are allowed but magnitude-capped, see ``binary_operators``.)
293293
raise ValueError(f"Binary operator '{op_type.__name__}' is not allowed")
294294
return build_binop(binary_operators[op_type], recurse(node.left), recurse(node.right))
295295
case ast.List(elts=elts) if allow_value_nodes:

‎tests/io/test_security.py‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -551,11 +551,14 @@ def test_shared_exception_class_not_corrupted(self, fmt):
551551
try:
552552
sm = _run_exec(scxml, native, fmt)
553553
assert "failed" in _config(sm)
554-
# The shared class is intact: add_note is still the inherited method, not int 1,
555-
# and a normal exception still constructs and carries a note.
556-
assert callable(TransitionNotAllowed.add_note)
557-
err = TransitionNotAllowed(None, set())
558-
err.add_note("still works")
554+
# The shared class is intact: the exploit did not inject ``add_note = 1`` onto it.
555+
# ``BaseException.add_note`` only exists on Python 3.11+, so assert on the injection
556+
# site (the class ``__dict__``) rather than the inherited method, to stay
557+
# version-agnostic.
558+
assert "add_note" not in TransitionNotAllowed.__dict__
559+
assert getattr(TransitionNotAllowed, "add_note", None) != 1
560+
# A normal exception still constructs.
561+
TransitionNotAllowed(None, set())
559562
finally:
560563
# Defensive: if a regression ever mutated the shared class, restore it so the
561564
# rest of the suite is not corrupted.

0 commit comments

Comments
 (0)