VAITP Dataset

← Back to the dataset

CVE-2026-61599

Unauthenticated client imports arbitrary modules via view path, code exec.

  • CVSS 8.8
  • 470
  • Input Validation and Sanitization
  • Remote

djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. Prior to version 1.0.7, 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 = "<any.importable.module>.AnyName"` and cause the server to import — and execute the top-level code of — any importable Python module by name. Version 1.0.7 fixes the issue with 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`). As a workaround, 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.)

CWE
470
CVSS base score
8.8
Published
2026-09-16
OWASP
A04 Insecure Design
Orthogonal defect classification
Checking
Code defect classification
Incorrect Check
Category
Input Validation and Sanitization
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
djust
Fixed by upgrading
Yes

Solution

Upgrade djust to version 1.0.7 or later.

Vulnerable code sample

import sys
from typing import Type

# Placeholder for the framework's LiveView base class
class LiveView:
    pass

# Framework setting (default fail‑open)
LIVEVIEW_ALLOWED_MODULES = []  # e.g. ["myapp.views"]

def resolve_live_view(view_path: str) -> Type[LiveView]:
    """
    Resolve a dotted ``module.Class`` string supplied by a client into a LiveView subclass.
    """
    # VULNERABLE: imports untrusted module before any validation
    module_path, _, class_name = view_path.rpartition('.')
    module = __import__(module_path, fromlist=[class_name])
    view_cls = getattr(module, class_name)

    # Allowlist is checked *after* the import and uses a lax startswith
    if LIVEVIEW_ALLOWED_MODULES:
        if not any(module_path.startswith(allowed) for allowed in LIVEVIEW_ALLOWED_MODULES):
            raise PermissionError("Module not allowed")

    if not issubclass(view_cls, LiveView):
        raise TypeError("Resolved class is not a LiveView")
    return view_cls

Patched code sample

import sys
from typing import Type

# Placeholder for the framework's LiveView base class
class LiveView:
    pass

# Framework setting (default fail‑open)
LIVEVIEW_ALLOWED_MODULES = []  # e.g. ["myapp.views"]

def is_view_import_allowed(module_path: str) -> bool:
    """
    Return True if the module is permitted for lazy import.
    """
    # Fail‑closed: reject when allowlist is empty
    if not LIVEVIEW_ALLOWED_MODULES:
        return False
    # Ensure match occurs on a segment boundary
    return any(module_path == allowed or module_path.startswith(f"{allowed}.") for allowed in LIVEVIEW_ALLOWED_MODULES)

def resolve_live_view(view_path: str) -> Type[LiveView]:
    """
    Resolve a dotted ``module.Class`` string supplied by a client into a LiveView subclass.
    """
    # FIX: validate module before importing any code
    module_path, _, class_name = view_path.rpartition('.')
    if not is_view_import_allowed(module_path):
        raise PermissionError("Module not allowed")

    # Import only if the module hasn't been loaded already (no new code execution)
    if module_path in sys.modules:
        module = sys.modules[module_path]
    else:
        module = __import__(module_path, fromlist=[class_name])

    view_cls = getattr(module, class_name)
    if not issubclass(view_cls, LiveView):
        raise TypeError("Resolved class is not a LiveView")
    return view_cls

Payload

__VAITP_MODEL_REFUSED__

Cite this entry

@misc{vaitp:cve202661599,
  title        = {{Unauthenticated client imports arbitrary modules via view path, code exec.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-61599},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-61599/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::