GitHub Advisoryadvisory · PyPI
GHSA-7prp-2623-8g45 djust has an unauthenticated arbitrary module import via the WebSocket/SSE view-mount path
HIGH
Official page ↗
- Aliases
- CVE-2026-61599
- Ecosystem
- PyPI
- Packages
- djust
- Severity
- High
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
- Weaknesses
- CWE-470
- Reviewed
- 2026-09-16
Affected packages
• PyPI djust: < 1.0.7 → fixed in 1.0.7
Description
Impact
The djust live transport resolves the LiveView to mount from a client-supplied dotted path by calling `__import__(module_path, ...)`. The module is imported — running its top-level code (import side effects) — *before* the framework checks that the resolved object is a `LiveView` subclass and *before* any per-view authentication. The `LIVEVIEW_ALLOWED_MODULES` allowlist that should contain this is fail-open (`if allowed_modules:` — skipped when the setting is unset, the framework default) and uses loose `startswith` matching.
An unauthenticated WebSocket client (the WS handshake does not require auth; per-view auth runs only after import + instantiate) can therefore send a `mount` / `live_redirect_mount` / `url_change` frame (or an SSE mount) with `view = ".AnyName"` and cause the server to import — and execute the top-level code of — any importable Python module by name.
Consequences: server-side execution of arbitrary importable modules' import-time side effects by an unauthenticated client (effectively RCE-by-proxy on any host that has a side-effectful importable module), denial of service (import bombs / expensive dependency trees), and a module/class enumeration oracle via distinct error strings.
Reproduced end-to-end: an unauthenticated `WebsocketCommunicator` `mount` frame with the allowlist unset imported and executed a sentinel non-LiveView module before the "not a LiveView subclass" rejection.
Affected code
• `python/djust/websocket.py` `handle_mount` (`__import__` of the client `view`)
• `python/djust/runtime.py` `ViewRuntime.dispatch_mount` / `_instantiate_view` (SSE + `url_change` path)
• `python/djust/sse.py` SSE mount
Threat-model entry T4 (`docs/audits/websocket-auth-2026-06.md`) previously noted the default-open allowlist but understated the impact as mere LiveView-class probing; the real primitive is arbitrary-module import + top-level code execution, independent of whether the target is a LiveView.
Patches
Fixed by a fail-closed resolution gate (`djust._view_resolution.is_view_import_allowed`): a client view path resolves only if (a) its module is already loaded (`sys.modules` — so resolving runs no new code; URL-routed views loaded by URLconf at startup keep working with zero config) or (b) it matches `LIVEVIEW_ALLOWED_MODULES` on a module-segment boundary (explicit opt-in for lazily-imported views). The gate runs before `__import__` at all three sinks (+ defense-in-depth inside `_instantiate_view`).
Workarounds
Set `LIVEVIEW_ALLOWED_MODULES` to the narrow list of modules that contain your mountable LiveView classes. (Note: pre-patch the allowlist is `startswith`-matched and the import still precedes the subclass check, so this is mitigation, not a complete fix.)
References
Reproducer + finding writeup retained privately by the maintainer.
Related
Alias · CVE (1)
- CVE-2026-61599 djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. vulnerability
Weakness · CWE (1)
- CWE-470 Use of Externally-Controlled Input to Select Classes or Code weakness