refactor(wme-importer): AST-restricted B-UML import — banner-free sectioning + exec() removal (#560) - #574
Draft
ArmenSl wants to merge 3 commits into
Draft
refactor(wme-importer): AST-restricted B-UML import — banner-free sectioning + exec() removal (#560)#574ArmenSl wants to merge 3 commits into
ArmenSl wants to merge 3 commits into
Conversation
…ot comment banners project_to_json determined the diagram set by counting '# X MODEL #' comment banners, coupling the importer to the generator's comment formatting. A duplicate or renamed banner produced phantom empty diagrams (root cause of WME #161). Rework: parse the file AST, read the authoritative Project(models=[...]) list, resolve each model's diagram type from its constructor, and slice each model's source as the transitive closure of the statements it references. Exactly one diagram per model in the list; banners are no longer load-bearing. Object diagrams keep the domain-code prepend; single-diagram files (no Project wrapper) keep the existing fallback. Adds a banner-immunity regression test: stray / duplicate / renumbered banners must not change the output. Behavioral notes: - diagram titles now come from each model's name= argument; - a project's User reference DomainModel now surfaces as its own ClassDiagram instead of being merged into the app class diagram. Relates to BESSER#560 (move WME converters off exec/text heuristics).
The class/gui/bpmn/quantum B-UML importers reconstructed the model by exec()-ing the uploaded .py in a restricted namespace. That sandbox is escapable (().__class__.__bases__[0].__subclasses__() needs no builtins), so exec() on untrusted upload is an RCE risk however the globals are trimmed. Add a shared safe AST evaluator (buml_to_json/_safe_eval.py): ast.parse + a whitelist interpreter allowing only literals/collections, calls to whitelisted constructors, method-chains on namespace values, enum reads, and the if/try the generated files rely on. Every attribute access (read/write/call) is refused when the name is a dunder -- the one control that defeats the known escapes (__class__/__globals__/__subclasses__/...). Non-whitelisted bare calls (__import__, getattr, eval), str/bytes-receiver method calls (format-leak gadgets), subscript, lambda, comprehensions, f-strings and imports are all refused; UnsafeConstruct cannot be swallowed by an uploaded try/except. The four converters call safe_exec() instead of exec(); their safe_globals whitelists and JSON walkers are unchanged. OCL Constraint expressions round-trip verbatim (opaque strings, never parsed/executed). Tests: shared-evaluator suite with 26 adversarial payloads (all refused, un-catchable) + OCL constraint round-trip; full WME suite green. Completes the exec() removal for the buml_to_json importers (BESSER#560).
domain_model_to_code emitted types={} / associations={} / generalizations={}
for empty collections. {} is a dict, but the metamodel's setters expect a set,
so re-importing an empty domain model crashed with 'unsupported operand type(s)
for |: dict and set'. Emit set() instead, so empty models round-trip.
Updates the test that previously documented this as a known limitation to
assert the model now both compiles and exec()s.
Collaborator
Author
End-to-end validation against a local backendRan the branch backend locally and drove real import/export + OCL through the HTTP endpoints:
Follow-up fix included here (
|
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.
Completes #560 — move the WME B-UML importers off text/
exec()heuristics to AST-restricted parsing. Two parts:Part 1 — banner-free sectioning
project_to_jsonused to decide the diagram set by counting# X MODEL #comment banners, coupling the importer to the generator's comment formatting (root cause of WME #161). Now it parses the AST, reads the authoritativeProject(models=[...])list, resolves each model's type from its constructor, and slices each model's source as its dependency closure. One diagram per model in the list; banners are no longer load-bearing. Adds a banner-immunity test (stray/duplicate/renumbered banners can't create phantom diagrams).Part 2 —
exec()removal (the security core)The
class/gui/bpmn/quantumconverters reconstructed the model byexec()-ing the uploaded.pyin a restricted namespace — escapable (().__class__.__bases__[0].__subclasses__()needs no builtins), i.e. RCE on untrusted upload. Replaced with a shared whitelist AST evaluator (_safe_eval.py):if/trythe generated files use.UnsafeConstruct, un-catchable by uploadedtry/except): any__dunder__attribute (read/write/call) — the single control defeating__class__/__globals__/__subclasses__/__import__; non-whitelisted bare calls (__import__,getattr,eval);str/bytes-receiver method calls (format-leak gadgets); subscript, lambda, comprehensions, f-strings, imports.safe_globalswhitelist and JSON walker are unchanged; only theexecstep swapped. OCLConstraintexpressions round-trip verbatim (opaque strings, never parsed/executed).Diff & tests
+1207 / −195across 8 files.exec(real calls inbuml_to_json: 0.__import__/getattr-to-globals, str.format leak, lambda/comprehension/subscript…) all refused and proven un-swallowable; OCL constraint round-trip.Reviewer notes / honest caveats
name=(verified the class-diagram count is unchanged for auth/User-model projects — an earlier claim of a count change was wrong and removed).str/bytesnamespace value (shared evaluator, not a per-converter method allowlist); the dunder block is the load-bearing control and no dangerous callable is reachable (quantum's blanket module seed exposes only BUML classes — noos/sys/open).dir(module)seed to an explicit whitelist so a future strayimportin that module can't widen the surface.Draft → mark ready when you've reviewed. #161 is already fixed & live independently; this is the durable, security-hardening version.