Files

36 lines
1.2 KiB
Python

"""Content-Security-Policy nonces (ROADMAP #87, tranche 5b).
Chaque réponse HTTP reçoit un nonce frais (``request.state.csp_nonce``)
injecté dans ``script-src``. Les routes servant du HTML avec des scripts
inline (index, popout, admin, editor-poc, excalidraw, page de partage)
l'injectent dans le balisage via :func:`inject_csp_nonce` — mêmes
emplacements, aucun script déplacé.
Tant que ``'unsafe-inline'`` reste dans la politique (retrait en T5c),
l'injection est inerte : elle prépare la bascule sans changer le
comportement.
"""
from __future__ import annotations
import re
import secrets
# Balises <script> exécutables sans `src` et sans nonce existant :
# `<script>`, `<script type="module">`, `<script type="importmap">`.
# Les blocs non-JS (ex. `type="text/plain"`) et les scripts externes
# (`src=…`, couverts par 'self'/hôtes CDN) sont laissés intacts.
_SCRIPT_TAG_RE = re.compile(
r"<script(?=>|\s+type=\"(?:module|importmap)\"\s*>)",
)
def new_nonce() -> str:
"""Generate a fresh per-response CSP nonce."""
return secrets.token_urlsafe(16)
def inject_csp_nonce(html: str, nonce: str) -> str:
"""Add ``nonce="…"`` to bare executable inline ``<script>`` tags."""
return _SCRIPT_TAG_RE.sub(f'<script nonce="{nonce}"', html)