CVE-2026-61598: Client mass-assignment of arbitrary view attributes via the default dj-model update_model handler
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 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 accountid/isadmin/totalprice (none bound with dj-model) had all three set via updatemodel 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 allowedmodelfields fail-closed (required). Keep FORBIDDENMODELFIELDS only as defense-in-depth. Add a regression that a non-dj-model public attribute (e.g. isadmin) is rejected while a bound field still updates.
Workarounds Set allowedmodelfields 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.
Other sources
djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. Prior to version 1.0.7, 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. As a 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 attribute's type (so "true" -> bool True), aiding the attacker. This issue is fixed in djust 1.0.7. As a workaround, set allowedmodelfields 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.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/djustto a version that resolves this vulnerability.Fixed in 1.0.7 - Upgrade
Upgrade
djust.mixins.model_binding.ModelBindingMixinto a version that resolves this vulnerability.Fixed in 1.0.7 - Configuration
Set `allowed_model_fields` explicitly on every LiveView using `dj-model=` to the minimal list of bindable fields. Do not rely on the default (`None` = allow all).
djust LiveView dj-model allowed_model_fields = explicit minimal list per view - Configuration
Remove/avoid storing authorization/ownership/business state (e.g., `account_id`, `is_admin`, `total_price`) in public view attributes that coexist in the same view as `dj-model=` bindings, since the default `update_model` handler allows setting any public existing attribute.
djust LiveView dj-model public view attributes usage = do not store authorization/ownership/business state in public view attributes that share the view with dj-model bindings
Event History
Frequently Asked Questions
Which applications are exposed by default?
LiveViews using the affected ModelBindingMixin are exposed because its update_model handler is part of the LiveView base MRO. The optional allowed_model_fields restriction defaults to None, which permits all public existing view attributes unless they are on the framework denylist.
What does an attacker need to exploit this?
An attacker needs to send an update_model event over the LiveView WebSocket with a chosen field name and value. The target field must be a public, existing attribute on the view and must not start with an underscore or appear in FORBIDDEN_MODEL_FIELDS.
What kinds of application state should be reviewed first?
Review public view attributes that influence identity, authorization, pricing, account selection, or other business decisions. Attributes such as account_id, is_admin, and total_price are not protected merely because they were not bound with dj-model in the rendered template.
What can be done before upgrading?
Define allowed_model_fields explicitly so that only intended model-bound fields can be updated. Do not rely on the built-in denylist to protect application-specific state.
Which version addresses the affected behavior?
The issue affects versions prior to 1.0.7. Upgrade to version 1.0.7 or later.