Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.
Open advisories
16 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.
Affected version / file / line Version: djust v1.1.4 (latest release, published 2026-09-23). File: python/djust/mcp/server.py:354-433 (MCP tool findhandlersfortemplate). Confirmed identical on the commit used for triage, a7e4fafd1667b4db0b1465652d4a63cb17117a07. Description When templatepath is an absolute path to an existing file, it is used directly, with no confinement to the project/template directories — no realpath() + prefix check, no allowlist. open(resolvedpath, ...) then reads the file. This is inconsistent with the sibling tool in the same file (loaddata, ~line 290-303), which explicitly rejects paths outside the project directory for its own path-like parameter: findhandlersfortemplate has no equivalent check for templatepath before the open() call. Impact An MCP client with access to this tool (available whenever the Django project is running the MCP server via python manage.py djustmcp — no separate opt-in flag gates this specific tool beyond that) can read the contents…
Recommended action: Upgrade to 1.3.0rc8 (pre-release), 1.2.4, 1.1.7 or 1.0.10 by release line, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
A context safety grant was inherited by a template binding that never earned it, so rebinding a name the view had marked safe left the mark attached to the new, attacker-controlled value. djust's context safety channel is keyed by name, not by value. Every bind copied the value and left the grant in place: Affected binds Eight distinct shapes were live. All are fixed in 1.1.2: | shape | example | |---|---| | {% with %} rebinding a marked name | {% with p=hostile %}{{ p }}{% endwith %} | | {% with %} descendant of a rebound name | {% with p=hostile %}{{ p.a }}{% endwith %} | | {% with %} binding over a marked name | {% with q=hostile %}{{ q }}{% endwith %} | | {% for %} loop variable | {% for p in hostiles %}{{ p }}{% endfor %} | | {% for %} descendant | {% for p in hostiles %}{{ p.a }}{% endfor %} | | {% for %} tuple unpacking | {% for k, v in rows %}{{ v }}{% endfor %} | | {% include … with %} | {% include "c.html" with q=hostile %} | | assign tags | {% … as x %} | No filter chain…
Recommended action: Upgrade to 1.1.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Five independent defects in djust's template auto-escaping cause attacker-controlled input to be rendered as live markup where Django escapes it. All four are present in shipped 1.1.0 and are fixed in 1.1.1. They share one shape: a filter or grant that escapes nothing itself and relies on the render-time auto-escape, which something downstream then removes. They are grouped into a single advisory because the mitigation is identical — upgrade to 1.1.1 — and because no single one of them is meaningfully actionable in isolation. 1. linenumbers never escaped its input (#2291) The filter deferred all escaping to render time; a trailing |safe suppressed exactly that. The exposure is wider than the |safe form: any downstream filter that reads the output as markup is affected, including {{ p|linenumbers|truncatecharshtml:"5" }}, which contains no |safe at all. 2. escape was a no-op (#2281) Django's escape is eager (conditionalescape, returning SafeString). djust's returned its input…
Recommended action: Upgrade to 1.1.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact The djust live transport resolves the LiveView to mount from a client-supplied dotted path by calling import(modulepath, ...). 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 LIVEVIEWALLOWEDMODULES allowlist that should contain this is fail-open (if allowedmodules: — 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 / liveredirectmount / urlchange 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. Consequences: server-side execution of arbitrary importable modules' import-time side effects by an unauthenticated…
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact The WebSocket handlemount and ViewRuntime.buildrequest rebuild an HttpRequest via RequestFactory().get(...) with no HTTPHOST, so request.gethost() defaulted to "testserver" on the live path. Host/subdomain/domain TenantResolvers then misresolved the tenant — None on the live path while the HTTP path resolved correctly. With STRICTMODE=False the tenant-scoped managers returned unscoped rows (cross-tenant disclosure); with the default they returned an empty queryset (broken tenancy). Patches Fixed in djust 1.0.7. The handshake Host is extracted from the ASGI scope, validated against ALLOWEDHOSTS (the same logic as the CSWSH Origin gate, parsed with Django's splitdomainport so malformed Hosts are rejected at the boundary), and propagated — with the TLS scheme — into the reconstructed request, so live-path tenant resolution matches HTTP exactly. Workarounds No workaround on the live path short of upgrading. Most exposed when combined with STRICTMODE=False.
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact djust's per-object authorization (getobject + hasobjectpermission, ADR-017) was enforced on the WebSocket mount and event paths but not on three other render entry points: (a) the initial HTTP GET render, (b) SPA urlchange navigation, and (c) {% liverender %} embedded child views. An authenticated user could therefore view (and on some paths act on) an object they are not authorized for by loading the page directly, navigating to it via SPA url-change, or composing it as an embedded child — a classic IDOR / broken object-level access control on object-scoped views. Patches Fixed in djust 1.0.7. All render entry points now route through a shared enforceobjectpermission chokepoint: HTTP GET returns 403, urlchange emits a permissiondenied frame and skips the render, and {% liverender %} (eager + lazy) refuses the embed. Views without a custom getobject are unaffected (no-op). Workarounds No reliable workaround short of upgrading. Do not expose object-scoped views through the…
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact When a Django Model instance is assigned to a public view attribute, djust serialized it to the client with no sensitive-field denylist — sending fields such as password (the hash), privilege flags (e.g. isstaff / issuperuser), tokens, and other PII to the browser. Because exposing model objects to templates is a normal djust pattern, this could leak credentials/PII without the developer realizing the full object crossed the wire. Patches Fixed in djust 1.0.7. Model serialization applies a secure-by-default sensitive-field denylist (password/hash/token/secret-style fields and known privilege flags are withheld) with an identity-subset fallback. Workarounds Keep Model instances on private attributes and expose only the specific fields needed, until patched.
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact The live (WebSocket) transport authorizes a mount via checkviewauth, not Django's View.dispatch() chain. As a result, standard Django authorization — LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin, @methoddecorator(loginrequired, name="dispatch"), and custom dispatch() guards — and the djust admin extension's staff gate (applied only in the HTTP asview wrapper) were enforced on the initial HTTP GET but silently bypassed over WebSocket, where all events and state flow. An anonymous or under-privileged client could open a WebSocket and mount such a view — including admin list/create/change/delete — and dispatch its handlers. Patches Fixed in djust 1.0.7. checkviewauth now honors the Django AccessMixin family on every transport; a new system check S004 fails loud at startup on auth patterns the runtime cannot safely replay (decorator/overridden-dispatch forms); and the admin base mixin declares loginrequired = True + an active-staff checkpermissions gate.…
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact For views that opt into state snapshots, the snapshot statejson embedded in the client page was restored on reconnect as trusted view state with no integrity check. A client could edit the unsigned statejson in their page and return it in the reconnect mount frame to inject arbitrary view attributes — e.g. flip isadmin to True, or change accountid / balance — escalating privilege or tampering with business state held in public view attributes (the normal djust pattern). Patches Fixed in djust 1.0.7. State snapshots are signed; unsigned or forged snapshots are rejected on the back-navigation restore path. Workarounds Do not enable state snapshots; do not hold authorization/ownership state in public view attributes.
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact SSE sessions were keyed solely by a client-chosen sessionid with no binding to the authenticated user — a control the WebSocket transport has but that was dropped on SSE. An attacker who learns (or a victim who leaks) a sessionid could connect to the message endpoint and dispatch event handlers that execute with the victim's identity and state. Patches Fixed in djust 1.0.7. Each SSE session is bound to its owning principal at creation and cross-principal access is rejected; SSE session creation is additionally capped per principal. Workarounds Disable the SSE transport short of upgrading.
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact Many djust built-in component template tags (djust.components.templatetags.) render a developer/user-supplied URL into an href / action attribute, HTML-escaping it with conditionalescape but never validating the URL scheme. HTML escaping prevents attribute breakout but does not neutralize a javascript: URI (which needs no escapable characters), so a URL value of javascript:alert(document.cookie) lands verbatim in <a href="javascript:alert(document.cookie)" and executes in the victim's session on click. Affected sinks (executing href/action contexts; not exhaustive): breadcrumb (djustcomponents.py:1535; advanced.py:1999,2022), djnav link/dropdown/brand (djustcomponents.py:5632,5779,5807,5830), citation URL (:6936), cookie-consent privacy link (:8240), error-page action (advanced.py:1728), form action (forms.py:1239). <img src sinks are lower risk (javascript: does not execute via img src). No scheme validation exists anywhere in the component tags; a docstring at…
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact The SSE client→server POST endpoints are @csrfexempt and the SSE GET stream endpoint had no Origin check, so a cross-origin page could drive a victim-cookie-authenticated SSE session: force the victim's browser to GET the stream URL (which creates and mounts a LiveView as the victim) and POST to the message endpoint with credentials: include to fire state-changing event handlers as the victim. The URL sessionid is client-chosen (validated only for UUID format), so it is not a CSRF token, and a JSON body sent as text/plain is a CORS simple request with no preflight. Patches Fixed in djust 1.0.7. All three SSE endpoints validate the request Origin against ALLOWEDHOSTS (mirroring the WebSocket CSWSH defense) and reject cross-origin requests with 403; the POST endpoints additionally require Content-Type: application/json (415 otherwise), closing the text/plain simple-request bypass. Workarounds Disable the SSE transport, or front it with a proxy that enforces an Origin allowlist.
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact djust.tenants isolation was enforced only on the HTTP path. The current tenant was stored in threading.local() and set exclusively by the HTTP-only TenantMiddleware, so on the live (WebSocket/SSE) path getcurrenttenant() was always None during mount and every event handler — and the tenant-aware QuerySet manager failed OPEN (returned the unfiltered queryset, ignoring STRICTMODE), disclosing every tenant's rows to whoever held the socket. threading.local was additionally shared across connections on the synctoasync executor thread. Patches Fixed in djust 1.0.7. Tenant storage moved to a contextvars.ContextVar (per async task); the resolved tenant is bound around WS/SSE mount and every dispatch; both managers scope the base queryset once and fail CLOSED (.none() under the default STRICTMODE); and system check S006 warns when STRICTMODE=False. Workarounds No workaround on the live path short of upgrading.
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact djust.mixins.modelbinding.ModelBindingMixin provides a default updatemodel event handler and is part of the LiveView base MRO, so every LiveView exposes it. It setattrs a view attribute whose name is client-supplied (field), gated only by: reject -prefixed names; reject a 14-entry denylist of framework internals (FORBIDDENMODELFIELDS); optional allowedmodelfields which defaults to None = allow all; and hasattr existence. Result: a client can set any public, existing view attribute — not just the fields actually bound with dj-model= in the rendered template. The denylist covers framework plumbing but nothing about developer business/authz state, and the allowlist is opt-in (off by default). A developer who binds one dj-model="search" input and also keeps self.accountid / self.isadmin / self.totalprice as view state does not realize a client can set ALL of them via {type:event, event:"updatemodel", params:{field, value}} over the WebSocket. Type coercion matches the target…
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact djust's observability endpoints expose live view/session state and a remote method-invocation surface (evalhandler). The localhost restriction was an opt-in middleware that the documented setup omits; the views themselves enforced only DEBUG. In the misconfigured-but-documented scenario (DEBUG on, middleware not installed) a non-localhost client could read live application state and invoke handlers remotely. Patches Fixed in djust 1.0.7. The localhost restriction is enforced in-view on every observability endpoint (no longer dependent on a separately-installed middleware), and evalhandler is restricted; gated requests receive a non-disclosing response. Workarounds Ensure DEBUG=False in production, and do not expose the observability endpoints to untrusted networks.
Recommended action: Upgrade to 1.0.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Impact djust's LiveViewConsumer mounts a LiveView over a WebSocket. When a view is gated (loginrequired / permissionrequired, or an onmount hook that returns a redirect) and the connecting user is not authorized, the consumer sent the client a {"type":"navigate","to":...} redirect frame and then returned — without closing the socket and without clearing self.viewinstance. Only the PermissionDenied branch closed the connection (close(4403)). A real browser obeys the navigate frame and leaves, hiding the problem. A raw WebSocket client that ignores the redirect keeps an open, mounted socket. Because handleevent did not re-check authentication/authorization after mount, that client could then send {"type":"event", ...} frames and invoke any @eventhandler method on the gated view with no authenticated session — an authentication bypass on the live mutation path. Who is affected: apps that expose LiveViews gated by loginrequired / permissionrequired / a redirecting onmount hook, where the…
Recommended action: Upgrade to 1.0.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.