GHSA-j3x4-v3p2-4mq3
Stats and consensus sandbox static gates can be bypassed to reach blocked primitives (incomplete fix of #336 / GHSA-6rxr-gq43-694g)
highPublished 16 October 2026
Impact
The code that runs inside the Docker sandbox on the stats and consensus paths is screened by four static gates (a substring scan, an AST call-name matcher, a raw-pattern scan, and the executor's AST matchers). A family of constructs defeats these gates while evaluating, at runtime, to a primitive the gates are meant to block (process execution, or eval/exec via the interpreter namespace). All are certified "safe" by every static gate.
Verified constructs (described at the gate level; harmless id/true placeholders, nothing executed):
- A reference to a blocked callable stored through an assignment, instead of a direct call.
- A reference to a reflection builtin that yields the builtins namespace.
- Banned names assembled at runtime from split or repeated string literals, so no banned substring appears in the source.
- Names carried as string constants into a runtime
getattr, so no attribute node or blocked call-name appears. - The
os/subprocessmodule re-acquired throughsys.modules[...]after renamingsys, defeating thesysallowlist. - A call-name denylist gap (
posix_spawn,posix_spawnp,Popen). - On the consensus expression path,
chr()-synthesised names andvars()/dict.get()chains.
Affected
All releases through 7.2.3; reproduced at the gate level on main @ a4d6714. Exploitation requires the Docker executor to be enabled (stock deployments fail closed — without a Docker socket the path returns BLOCKED runtime_unavailable). In-container execution and any host-side impact were not reproduced here (no Docker); the control-integrity bypass is unconditional on this path.
Details
src/qwed_new/core/secure_code_executor.py: the OS-call denylist and the AST matchers that key on call nodes / name-rooted chains only (around 50-193).src/qwed_new/core/stats_verifier.py: the bundled validation and the alias/substring checks (around 333-382, 821-883).src/qwed_new/core/code_verifier.py: the substring and AST process-execution checks (the latter resolves import aliases only). This is the same structural class as closed #336 (every added matcher still keys on identifier syntax) and the import-alias-only limit of GHSA-6rxr; it also covers the stats-path analogue of the split-string-literal bypass fixed for/verify/mathin #329.
Fix
Stop extending name-set denylists. Isolate the wrapper namespace so the executed code has no access to eval/exec/vars/getattr/__import__/sys.modules/reflection builtins (a capability allowlist for builtins and modules), and reject non-call first-class references to blocked names. Add the missing posix_spawn/posix_spawnp/Popen names as a stopgap only.
Source
OpenVuln external audit (2026-10), stats and consensus sandbox-gate findings. Reported privately.
Affected and fixed
- Package
- qwed
- Affected
- <= 7.2.3
- Fixed in
- 7.3.0
- Severity
- high
- Weakness
- CWE-94 Improper Control of Generation of Code ('Code Injection')CWE-184 Incomplete List of Disallowed Inputs