Skip to content

Fix CSP blocking inline translations and broken routes.py#6

Merged
JuliusScheuerer merged 1 commit into
mainfrom
worktree-fix-csp-inline-translations
Mar 25, 2026
Merged

Fix CSP blocking inline translations and broken routes.py#6
JuliusScheuerer merged 1 commit into
mainfrom
worktree-fix-csp-inline-translations

Conversation

@JuliusScheuerer
Copy link
Copy Markdown
Owner

Summary

  • Fix SyntaxError in routes.py — the i18n PR (Add i18n support for web UI (German + English) #5) left lines 100-107 at column 0 (module level), breaking the _template_response() function and preventing the app from starting
  • Fix CSP violation — add per-request cryptographic nonce (secrets.token_urlsafe) to script-src directive and to the inline <script> tag that loads translations into window.__translations
  • Fix test rate-limit cascade — add autouse fixture that resets the rate limiter's internal state before each test, preventing 429s when the full suite runs

Test plan

  • make check passes (lint + mypy strict + 265 tests, 96.53% coverage)
  • All pre-commit hooks pass (ruff, ruff format, mypy, bandit)
  • New tests verify: nonce present in CSP, nonce unique per request, nonce matches between CSP header and HTML script tag
  • Manual: open browser, verify no CSP console errors, verify window.__t("key") returns translated text

…#5 followup)

The i18n PR (#5) introduced two bugs: (1) broken indentation in
_template_response() causing SyntaxError at import, and (2) an inline
<script> violating script-src 'self' CSP policy.

Fixes:
- Re-indent lines 100-107 of routes.py back inside _template_response()
- Add per-request CSP nonce via secrets.token_urlsafe() in middleware
- Add nonce attribute to inline translations <script> in base.html
- Add rate limiter reset fixture to prevent 429 cascade across tests
- Add tests verifying nonce presence, uniqueness, and header/HTML match
@JuliusScheuerer JuliusScheuerer merged commit 7e867e4 into main Mar 25, 2026
4 checks passed
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 25, 2026

Greptile Summary

This PR delivers three targeted fixes introduced by the i18n PR (#5): a SyntaxError in routes.py caused by accidentally dedented lines, a CSP violation from the nonce-less inline translations <script> tag, and a test-suite rate-limit cascade. All three fixes are correct and well-scoped.

Key changes:

  • middleware.py — generates a per-request cryptographic nonce via secrets.token_urlsafe(16) (128 bits entropy), stores it in request.state, and injects it into the script-src CSP directive.
  • routes.py — restores proper indentation for the translations block and reads request.state.csp_nonce with a safe getattr fallback, passing it to every template context.
  • base.html — adds nonce="{{ csp_nonce }}" to the one inline <script> tag so it matches the CSP allowlist.
  • conftest.py — introduces an autouse fixture that clears the rate limiter's internal _requests dict before each test, preventing 429 cascades when the full suite runs.
  • test_routes.py — adds three new tests verifying nonce presence, uniqueness per request, and header/HTML consistency.

One minor style nit: import re inside test_csp_nonce_matches_inline_script should be lifted to the module-level imports.

Confidence Score: 5/5

  • Safe to merge — all three fixes are correct, well-tested, and non-breaking.
  • The SyntaxError fix is straightforward and verified by the app booting. The nonce implementation follows CSP best practices (URL-safe base64 is explicitly permitted by the CSP3 grammar). The rate-limiter fixture correctly resets shared state without interfering with test logic. The only open item is a trivial P2 style nit (import re inside a test method) that has no runtime impact.
  • No files require special attention.

Important Files Changed

Filename Overview
src/document_anonymizer/security/middleware.py Generates a per-request secrets.token_urlsafe(16) nonce, stores it in request.state.csp_nonce, and injects it into the script-src CSP directive. Implementation is secure and correct.
src/document_anonymizer/web/routes.py Fixes the indentation bug from PR #5 (lines were at module scope, causing a SyntaxError). Adds nonce forwarding from request.state to the Jinja2 template context. Logic is correct.
src/document_anonymizer/web/templates/base.html Adds nonce="{{ csp_nonce }}" to the one inline <script> tag, making it match the CSP script-src directive. Straightforward one-line fix.
tests/conftest.py Adds an autouse fixture that walks the middleware stack and clears limiter._requests before each test. Relies on a private attribute; a dedicated reset() method would be cleaner, but acceptable given this is test-only code.
tests/test_web/test_routes.py Adds three focused CSP nonce tests: presence, per-request uniqueness, and header/HTML consistency. Covers the contract well; minor style nit with an in-method import re.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant SecurityMiddleware
    participant RouteHandler
    participant Jinja2Template

    Browser->>SecurityMiddleware: GET /
    SecurityMiddleware->>SecurityMiddleware: nonce = secrets.token_urlsafe(16)
    SecurityMiddleware->>SecurityMiddleware: request.state.csp_nonce = nonce
    SecurityMiddleware->>RouteHandler: call_next(request)
    RouteHandler->>RouteHandler: read csp_nonce from request.state
    RouteHandler->>Jinja2Template: render index.html with csp_nonce + translations_json
    Jinja2Template-->>RouteHandler: HTML with script nonce attribute set
    RouteHandler-->>SecurityMiddleware: HTMLResponse
    SecurityMiddleware->>SecurityMiddleware: Set CSP header with nonce in script-src
    SecurityMiddleware-->>Browser: Response with CSP header and HTML body
Loading

Reviews (1): Last reviewed commit: "Fix CSP blocking inline translations and..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant