36 lines
1.2 KiB
Python
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)
|