GHSA-CC7C-9JFF-58WJ
Vulnerability from github – Published: 2026-09-16 13:55 – Updated: 2026-09-16 13:55Impact
djust.mixins.model_binding.ModelBindingMixin provides a default update_model 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 (FORBIDDEN_MODEL_FIELDS); optional allowed_model_fields 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.account_id / self.is_admin / self.total_price as view state does not realize a client can set ALL of them via {type:event, event:"update_model", params:{field, value}} over the WebSocket. Type coercion matches the target attribute's type (so "true" -> bool True), aiding the attacker.
Severity High for apps that hold authorization/ownership/business state in public view attributes (the normal djust pattern) -> state tampering / IDOR / authz-flag manipulation; Low otherwise. Default-on across every LiveView. For a public (no-login) view an anonymous client can mass-assign; for an authenticated view a logged-in user can tamper their own session's view state (the IDOR/authz vector when downstream handlers act on it without re-authorizing).
Reproduced: a view with account_id/is_admin/total_price (none bound with dj-model) had all three set via update_model calls.
Patches
Restrict the default handler to fields actually exposed via dj-model=: have the template renderer record the bound-field set per render and reject any field outside it (preferred, secure + zero-config); or make allowed_model_fields fail-closed (required). Keep FORBIDDEN_MODEL_FIELDS only as defense-in-depth. Add a regression that a non-dj-model public attribute (e.g. is_admin) is rejected while a bound field still updates.
Workarounds
Set allowed_model_fields explicitly on every view using dj-model (or subclassing LiveView) to the minimal list of bindable fields; do not keep authorization/ownership state in public view attributes that share the view with dj-model bindings.
References
Reproducer + finding writeup retained privately by the maintainer.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "djust"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.0.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-61598"
],
"database_specific": {
"cwe_ids": [
"CWE-915"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-16T13:55:48Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Impact\n`djust.mixins.model_binding.ModelBindingMixin` provides a default `update_model` event handler and is part of the **LiveView base MRO**, so every LiveView exposes it. It `setattr`s a view attribute whose **name is client-supplied** (`field`), gated only by: reject `_`-prefixed names; reject a **14-entry denylist** of framework internals (`FORBIDDEN_MODEL_FIELDS`); optional `allowed_model_fields` which **defaults to None = allow all**; and `hasattr` existence.\n\nResult: a client can set **any public, existing view attribute \u2014 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.account_id` / `self.is_admin` / `self.total_price` as view state does not realize a client can set ALL of them via `{type:event, event:\"update_model\", params:{field, value}}` over the WebSocket. Type coercion matches the target attribute\u0027s type (so `\"true\"` -\u003e bool True), aiding the attacker.\n\n**Severity High** for apps that hold authorization/ownership/business state in public view attributes (the normal djust pattern) -\u003e state tampering / IDOR / authz-flag manipulation; Low otherwise. Default-on across every LiveView. For a public (no-login) view an anonymous client can mass-assign; for an authenticated view a logged-in user can tamper their own session\u0027s view state (the IDOR/authz vector when downstream handlers act on it without re-authorizing).\n\nReproduced: a view with `account_id/is_admin/total_price` (none bound with dj-model) had all three set via `update_model` calls.\n\n### Patches\nRestrict the default handler to fields actually exposed via `dj-model=`: have the template renderer record the bound-field set per render and reject any `field` outside it (preferred, secure + zero-config); or make `allowed_model_fields` fail-closed (required). Keep `FORBIDDEN_MODEL_FIELDS` only as defense-in-depth. Add a regression that a non-`dj-model` public attribute (e.g. `is_admin`) is rejected while a bound field still updates.\n\n### Workarounds\nSet `allowed_model_fields` explicitly on every view using dj-model (or subclassing LiveView) to the minimal list of bindable fields; do not keep authorization/ownership state in public view attributes that share the view with dj-model bindings.\n\n### References\nReproducer + finding writeup retained privately by the maintainer.",
"id": "GHSA-cc7c-9jff-58wj",
"modified": "2026-09-16T13:55:48Z",
"published": "2026-09-16T13:55:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/djust-org/djust/security/advisories/GHSA-cc7c-9jff-58wj"
},
{
"type": "PACKAGE",
"url": "https://github.com/djust-org/djust"
},
{
"type": "WEB",
"url": "https://github.com/djust-org/djust/releases/tag/v1.0.7"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "djust: Client mass-assignment of arbitrary view attributes via the default dj-model update_model handler"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.