Conversation
"Explicit is better than implicit." (PEP 20) Every ast node of the supported Python versions now has an explicit visit_<node> method in RestrictingNodeTransformer. generic_visit stays the safety net for nodes of new Python versions nobody has reviewed yet; it is no longer the place where the decision about a known language feature is recorded. - Deny explicitly the nodes which were only denied by generic_visit: AnnAssign, Match with all pattern nodes and match_case, TypeAlias, TypeVar, ParamSpec, TypeVarTuple, FunctionType and TypeIgnore. - Explain in the docstring of every denying method why the node is denied and which guard or check it would bypass; also for the existing ones (Nonlocal, TryStar, async). - Add tests which allow the pattern nodes and show the bypasses: class, mapping and sequence patterns access the subject without _getattr_, _getitem_ and _getiter_; capture names escape the check for a leading underscore. - Add a test which fails for every ast node of the running Python without a visit_<node> method. - Replace the open "Option 1 / Option 2" todo in the contributing documentation by the rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
icemac
requested changes
Sep 28, 2026
icemac
left a comment
Member
There was a problem hiding this comment.
Sorry, but I do not like this PR at all. (You'd probably expected this.)
We already have an explicit deny of unknown AST nodes in
RestrictedPython/src/RestrictedPython/transformer.py
Lines 500 to 514 in c5066f7
The idea behind this PR makes maintaining this package even harder and adds nothing (I see no security gain at all!). I have been fighting against it for years and it seems that my message has not been come across clearly enough: This PR leads into a direction we should not go.
There is also Although practicality beats purity. in the Zen of Python and this PR feels like violating this principle.
loechel
marked this pull request as draft
September 29, 2026 11:16
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR makes a rule out of it: every ast node has an explicit
visit_<node>method inRestrictingNodeTransformer, the denied ones included, and the docstring of a denying method explains why the node is denied and which guard or check it would bypass.generic_visitstays what it is meant to be: the safety net for nodes of a new Python version which nobody has reviewed yet. It is not the place where the decision about a known language feature is recorded. A node denied only bygeneric_visitlooks exactly like a node nobody has ever looked at; an explicit method records the review and its result at the place where the next reviewer looks for it. In a security package that knowledge is worth more than the lines it takes.This settles the open
todo"Option 1 / Option 2" indocs/contributing/index.rstin favour of being explicit; the section is replaced by the rule (new section Every AST node has an explicitvisit_<AST Node>method).Changes
New explicit denials for all nodes which were only denied implicitly (none of them had a test before):
AnnAssignMatch,match_case,MatchValue,MatchSingleton,MatchSequence,MatchMapping,MatchClass,MatchStar,MatchAs,MatchOrTypeAlias,TypeVar,ParamSpec,TypeVarTuple(Python 3.12+)FunctionType,TypeIgnore(only reachable via a pre-parsed ast)Documented reasons in the docstrings of these and of the existing denying methods (
Nonlocal,TryStar,AsyncFunctionDef,Await,AsyncFor,AsyncWith). Where no concrete bypass is known (Nonlocal), the docstring says so and that a security review is missing.The rule is enforced by a test:
test_explicit_deny__1fails for every ast node of the running Python version without avisit_<node>method. Adding a new Python version to the matrix therefore fails until each new node has been decided on.The reasons are backed by tests where they describe interpreter behaviour. With the pattern nodes allowed, the tests show:
__match_args__) without_getattr_,_getitem_,_getiter_,case _secret,*_secret,**_secret,as _secret) escape the check for a leading underscore — the same class of bug as GHSA-ffg3-p8fm-mjx2,compile()in a value pattern.If a future Python changes that behaviour, the test fails and the docstring gets reviewed.
Behaviour change
None for the security boundary: every affected node was denied before and is denied now, with the same error message
"<Node> statements are not allowed.". The only visible difference is that the warning"<Node> statement is not known to RestrictedPython"fromgeneric_visitis no longer emitted for these nodes.Tests
Run locally:
tox -e py310,py311,py312,py313,py314,py315,py311-datetime— all greentox -e lint(incl. flake8, isort, mypy, sphinx-lint) — greentox -e docs— build succeeded (the 4 remaining warnings are pre-existingtodoentries)tox -e coverage— 100 %Risks
compile_restricted_*see the "not known" warning disappear for the nodes listed above.🤖 Generated with Claude Code