Before you upgrade, read Backwards incompatible changes in 1.1.4 below.
djust 1.1.4 is a security maintenance release for the 1.1 line. It was released on the same day as djust 1.2 and carries the same security fixes, without 1.2's new features or its template-engine changes.
Every project on 1.1.x should upgrade. Several of the fixes close a hole by changing a default, so read Backwards incompatible changes in 1.1.4 before you deploy. If you are ready to move to the 1.2 line, the 1.2 release notes describe the full upgrade, and 1.2.0 includes every fix listed here.
Python and Django support are unchanged from 1.1: Python 3.10 to 3.14 and Django 4.2 to 5.2.
Security
1.1.4 includes fixes for the following advisories. Each advisory has the details, the affected versions and the credit.
| Advisory | Area |
|---|---|
| GHSA-fccp-5h88-g34j | DataTable sorting, filtering and grouping |
| GHSA-jv2m-fcq9-94xf | djust admin (admin_ext) permissions |
| GHSA-c44q-w252-mr67 | Observability endpoints |
| GHSA-hc2m-gvfj-x6r3 | Built-in components |
| GHSA-r372-rrpw-5cgj | Built-in components |
| GHSA-j23m-jxwp-m3vq | Theming |
| GHSA-6q7c-hvpc-ff2q | Presence and channel groups |
| GHSA-5ffg-p52h-v2ph | Back-navigation state snapshots |
| GHSA-74vj-mpp4-45cg | Sticky {% live_render %} children |
| GHSA-7fcf-23mf-rhhm | Resumable uploads |
| GHSA-p9vp-rh5f-2cvq | Custom template filters (fixed on the 1.2 line in 1.2.0rc2) |
Backwards incompatible changes in 1.1.4
Each of these closes one of the advisories above. They are the same changes 1.2.0 makes, and the 1.2 notes explain each one in more detail.
- djust admin enforces model permissions.
DjustModelAdmincheckshas_view_permission,has_add_permission,has_change_permissionandhas_delete_permissionon every page, save and delete, and the defaults now calluser.has_perm()with the model'sview_/add_/change_/delete_permission, as Django'sModelAdmindoes. Staff users who aren't superusers need those permissions; before, any active staff account had full access. - Built-in components HTML-escape the values they render. HTML passed to a content slot (a modal, card or tab body, a header, a footer, an icon) must be marked safe with
mark_safe(), or it is shown as text. Link and image URLs with ajavascript:,vbscript:or non-imagedata:scheme render as#. The rich-text editor's value is cleaned to an HTML allowlist instead of escaped. - Custom template filters registered with
is_safe=Truekeep their output escaped unless their input was already safe, as in Django. A filter that produces markup must returnmark_safe(...)orformat_html(...)itself. - The observability endpoints need a token. Requests must come straight from a loopback client with no proxy headers and must send the
X-Djust-Observability-Tokenheader.manage.py djust_mcpsends it for you; print it for other tools withmanage.py djust_observability_token. DataTablesorts, filters and groups only on declared columns. A column is sortable unless it sets"sortable": False, and filterable only when it sets"filterable": True. Unknown column names are ignored.- Back-navigation state snapshots from earlier versions are rejected once and the view mounts fresh.
- Resumable uploads started before the upgrade restart from the beginning, and resume only in the session that started them.
- Theme cookies naming an unregistered pack or an invalid layout fall back to the defaults.
Bug fixes
- A LiveView joins its channel groups (view, presence,
db_notify) only after its permission checks andon_mounthooks pass. A refused mount leaves every group, and presence and push messages no longer reach a connection with no mounted view. - A reused sticky
{% live_render %}child re-runs its view and object permission checks on every parent render, andlive_redirectcarry-over re-checks object permissions. {% theme_css_link %}URL-encodes its query string.AzureBlockBlobWriterraisesRuntimeErrorwhen it is used before it is opened, instead of passing an unset blob name to the Azure client.
The full entry is in the [1.1.4] section of the 1.1 branch's CHANGELOG.
Upgrading
pip install "djust==1.1.4"
python manage.py collectstatic --noinput
python manage.py check
Then work through the list above: grant model permissions to staff who use djust admin, mark component slot HTML safe, add "filterable": True to DataTable columns you filter on, and give any observability client the token.
All changes in 1.1.4
The CHANGELOG.md entry for djust 1.1.4.
A maintenance release for the 1.1 line with the fixes from 1.2.0. Review the behaviour changes below before upgrading.
Security
- This release and 1.2.0 include fixes for the following advisories; see each advisory for details:
- GHSA-fccp-5h88-g34j (DataTable)
- GHSA-jv2m-fcq9-94xf (admin_ext)
- GHSA-c44q-w252-mr67 (observability)
- GHSA-hc2m-gvfj-x6r3 and GHSA-r372-rrpw-5cgj (components)
- GHSA-j23m-jxwp-m3vq (theming)
- GHSA-6q7c-hvpc-ff2q (presence)
- GHSA-5ffg-p52h-v2ph (state snapshots)
- GHSA-74vj-mpp4-45cg (sticky live_render)
- GHSA-7fcf-23mf-rhhm (uploads)
- GHSA-p9vp-rh5f-2cvq (template filters; fixed in 1.2.0rc2)
Changed
- djust admin applies the
DjustModelAdminpermission hooks to every page, save and delete, and the default hooks follow Django's model permissions.has_view_permission,has_add_permission,has_change_permissionandhas_delete_permissionare checked before mount (list, add) or per object (change, delete), and again insave,form_valid,confirm_deleteand thedelete_selectedaction. The defaults now calluser.has_perm()with the model'sview_/add_/change_/delete_codename, as Django'sModelAdmindoes. Staff users who aren't superusers now need those permissions; previously any active staff account had full access. The admin index lists only models the user has some permission on. - Built-in components HTML-escape the values they render. Every component class,
{% ... %}component tag and Rust component renderer now passes interpolated values throughconditional_escape, in all CSS-framework variants. HTML passed to a content slot must be marked safe (mark_safe, or another component's rendered output), or it is shown as text: this affects modal/card/sheet/popover/tabs/accordion bodies, headers and footers, icons, and similar slots.href/srcvalues usingjavascript:,vbscript:or non-imagedata:render as#. The rich-text editor is the exception: its value is cleaned to an HTML allowlist (formatting kept, scripts and event attributes removed) instead of escaped. - The observability endpoints require a project token. Besides
DEBUGand a loopback client, a request must carry no proxy headers (X-Forwarded-For,Forwarded,X-Real-IP,X-Forwarded-Host,X-Forwarded-Proto) and must sendX-Djust-Observability-Token. The token is derived fromSECRET_KEY(override with theDJUST_OBSERVABILITY_TOKENenvironment variable) and printed bymanage.py djust_observability_token.manage.py djust_mcpsends it automatically; other tools and hand-written requests must add the header. - Back-navigation state snapshots are bound to a keyed digest of the session key instead of the key itself. Snapshots issued by earlier versions fail verification once, and the view mounts fresh.
- A custom template filter registered with
is_safe=Truekeeps its output escaped unless its input was already safe, as in Django (is_safe and isinstance(obj, SafeData)). Previously the flag alone left the output unescaped in the Rust renderer. A filter that produces markup must returnmark_safe(...)/format_html(...)itself;{{ value|safe|myfilter }}andmark_safecontext values behave as before.
Fixed
- DataTable sorts, filters and groups only on declared columns.
on_table_sort,on_table_filter, group-by and expression filters now accept only keys listed intable_columns, checked again where the queryset is built. Sorting is on unless a column sets"sortable": False; filtering needs"filterable": True;table_default_sortis always accepted. Unknown column names are ignored. - A LiveView joins its channel groups (view, presence, db_notify) only after its permission checks and on_mount hooks pass. A refused mount, including one inside
mount_batch, leaves every group, and presence and client-push messages are no longer delivered to a connection with no mounted view.check_permissionsand on_mount hooks now run beforeon_view_mounted. - Resumable uploads record the session that started them. Resume over WebSocket and the HTTP upload-status endpoint answer only that session; the status endpoint previously returned 404 even to the owner. Uploads started before the upgrade, or whose session key changed (for example at login), restart from the beginning.
- A reused sticky
{% live_render %}child re-runs its view and object permission checks on every parent render, andlive_redirectcarry-over re-checks object permissions too. A child the user may no longer see is unmounted and the render fails the same way a fresh mount would. - Theme cookies that name an unregistered pack or an invalid layout fall back to the defaults, and
{% theme_css_link %}URL-encodes its query. An unknown pack now resolves to the session pack, then the configured default. Layout names must match[A-Za-z0-9_-]{1,64}. AzureBlockBlobWriterraisesRuntimeErrorwhen used before it is opened, instead of passing an unset blob name to the Azure client.