djust 0.9.5rc3

Pre-releaseSecurityReleased

This is a pre-release. djust 0.9.5 has shipped since: read the djust 0.9.5 release notes.

Before you upgrade, read Removed below.

Added

  • Bundle-init-order structural lint: scripts/check-bundle-init-order.mjs (#1372, #1370 follow-up). Static check for the direct-top-level-read TDZ subclass — catches the case where a late-declared let/const is referenced directly at top level of an earlier module. Enumerates module-scope let/const across python/djust/static/djust/src/*.js, finds top-level use sites via acorn AST, and flags any cross-module use where the use-site lex-orders BEFORE the declaration. Does NOT catch transitive call-graph TDZ (e.g., djustInit() calling mountHooks() whose body reads a late let — this is exactly the #1370 shape). The runtime regression test bundle-init-no-tdz.test.js continues to catch transitive cases via JSDOM eval. The two checks are complementary; extending this lint to a depth-N call-graph walker is filed as a follow-up. Wired into Makefile (make check-bundle-init-order is part of make check) and pre-push hook. Currently clean on main.

Changed

  • JS micro-cleanup: deduplicated transition helpers + tightened routeMap access (#1360, #1361). Two follow-ups deferred from PR #1359 Stage 11.
    • _parseTimeMs and _computeTransitionTiming extracted from 41-dj-transition.js and 42-dj-remove.js into a new shared 40a-transition-helpers.js (loads before both consumers per the bundle's lexicographic concat order). CodeQL alerts at client.js:13162 and :13171 ("Conflicting function declarations") clear; bundle has exactly one definition of each (#1360).
    • routeMap[pathname] access in 18-navigation.js replaced with an Object.entries(routeMap) walk — prototype-pollution-immune by construction (own enumerable string-keyed entries only). Lints cleanly without eslint-disable-next-line. Same shape applied to 46-state-snapshot.js. Map conversion (option B) rejected because it would change the wire-protocol shape emitted by python/djust/routing.py and break downstream consumers (#1361).

Removed

  • Dead InMemoryStateBackend.get_and_update() removed (#1356). Method had zero callers and would re-introduce the #1353 shared-mutable-state race class if a future caller was added without auditing. PR #1355 fixed the sibling get() to clone via msgpack round-trip; get_and_update() was overlooked. Per the issue body's preferred-fix order, deletion was cleanest. Removes ~22 lines of dead code from python/djust/state_backends/memory.py. (Surfaced as PR #1355 Stage 13 Re-Review #1.)

Fixed

  • Node::Include round-trip no longer double-quotes the template path (#1396). Parser was preserving the outer quotes on Include.template; emitter nodes_to_template_string then wrapped again, producing {% include ""partials/header.html"" %} on round-trip. Surfaced during PR #1397's conversion of round-trip tests to drive from parser output (Action #158 working as designed). Fixed by aligning the parser to strip outer quotes (matching the existing Extends, Static, and Now contracts) — single source of truth, emitter unchanged. test_nodes_to_template_string_include un-ignored. Added test_nodes_to_template_string_now for defense-in-depth.

Security

  • sanitize_for_log cache_key on HTTP cache-lookup debug log (#1368). Pre-existing log-injection asymmetry between WebSocket and HTTP paths in python/djust/mixins/rust_bridge.py: the WS site at line 333 sanitized correctly; the HTTP site at line 363 did not. Since cache_key derives from request.path (user-controlled), an attacker-supplied path like /page/\n[FAKE LOG ENTRY] could inject newlines into the log stream. Mirrors the WS-path call to sanitize_for_log(self._cache_key) and adds the matching CodeQL annotation. Surfaced in PR #1367 Stage 11 SHOULD-FIX #3 (deferred per Action #1079).

All releases · Atom feed