Commit Graph
3 Commits
Author SHA1 Message Date
GGThedandClaude Opus 5 15bfebf4fc feat(csp): infrastructure de sortie de unsafe-inline, et couche partagee migree
SEC-WEB-001 / OPS-010. script-src porte toujours 'unsafe-inline' : c'est
pour cela que le XSS stocke de SEC-XSS-001 s'executait au lieu d'etre
bloque. Le retirer n'est pas un changement d'une ligne.

Ce qui bloque reellement
  Un nonce autorise des elements <script> ; il ne peut rien pour un
  attribut onclick="...". Mesure faite : 76 gestionnaires en ligne repartis
  dans 15 gabarits. Tant qu'il en reste un, la politique ne peut pas etre
  durcie.

  Piege supplementaire, documente dans build_csp() : en CSP niveau 3, un
  navigateur ignore 'unsafe-inline' des qu'un nonce est present. Emettre
  les deux ne serait donc pas une transition douce -- ce serait couper
  d'un coup tous les scripts en ligne et tous les onclick, et uniquement
  sur les navigateurs recents. La bascule doit etre atomique, d'ou un
  drapeau unique : CSP_ALLOW_INLINE_SCRIPT.

Infrastructure posee
  build_csp() assemble l'en-tete selon le drapeau. Un nonce est genere par
  requete et n'est emis que lorsque l'inline est interdit. Les 15 blocs
  <script> portent deja nonce="{{ csp_nonce }}", inerte aujourd'hui : la
  bascule finale sera un changement de configuration, pas de gabarits.

Couche partagee migree en premier
  base.html et macros.html sont rendus sur absolument toutes les pages. Six
  gestionnaires retires, remplaces par des attributs data-action et un
  ecouteur delegue unique dans main.js. La delegation plutot qu'un
  ecouteur par widget : le contenu injecte dynamiquement herite du
  comportement sans re-attachement.

Un cliquet plutot qu'une promesse
  tests/test_csp.py fixe un budget par gabarit qui ne peut que baisser.
  Ajouter un gestionnaire en ligne fait echouer la suite ; en retirer sans
  mettre le budget a jour aussi, ce qui force a enregistrer la progression
  dans le diff. A zero, il ne reste qu'a basculer le drapeau.

  Le cliquet a d'ailleurs corrige mon propre relevé : mon grep initial
  comptait 83 gestionnaires, la mesure exacte en donne 76 -- le motif ne
  verifiait pas l'espace avant l'attribut.

style-src conserve 'unsafe-inline' : les attributs style="" sont partout et
ne constituent pas un vecteur XSS a eux seuls. Migration distincte.

192 tests.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 20:44:16 -04:00
GGThedandClaude Opus 5 a92600c305 feat(i18n): francais comme langue principale, anglais accessible
Le site s'affiche desormais en francais par defaut, avec un selecteur de
langue permettant de basculer vers l'anglais.

Choix de conception : les chaines sources restent en anglais
  Elles servent d'identifiants gettext, et le francais est fourni par
  catalogue avec BABEL_DEFAULT_LOCALE = 'fr'. Le code reste ainsi dans une
  seule langue -- la meme que ses commentaires et docstrings -- tandis que
  ce qu'un membre voit est du francais.

  Consequence qui rend la migration praticable : une chaine non encore
  traduite retombe en anglais, pas sur un identifiant brut. Les gabarits
  peuvent donc etre migres un par un sans jamais laisser le site a moitie
  casse.

Selection de la langue (app/i18n.py)
  1. choix explicite via le selecteur, garde en session
  2. sinon en-tete Accept-Language du navigateur, restreint a fr et en
  3. sinon francais
  Un choix explicite prime toujours, y compris sur un navigateur anglophone.

Selecteur
  Extrait en partiel et inclus dans les deux branches de la mise en page :
  barre laterale une fois connecte, ET page d'authentification. Quelqu'un
  qui ne lit pas la langue courante doit pouvoir en changer AVANT de se
  connecter -- le laisser derriere l'authentification aurait ete un defaut
  d'accessibilite. Chaque langue est ecrite dans sa propre langue.

  La route /lang/<locale> valide le Referer avant de rediriger : sans ce
  controle, elle constituait une redirection ouverte.

Migre dans cette passe
  navigation complete, page de connexion, les cinq pages d'erreur, et
  l'integralite des messages flash de routes/auth.py. 64 chaines, dont
  aucune non traduite.

Verification
  25 tests, dont deux garde-fous d'integrite : un catalogue .mo manquant
  ou une entree non traduite font echouer la suite. Sans cela, une
  compilation oubliee servirait de l'anglais partout, en silence et sans
  rien dans les journaux.

Un test existant a du etre corrige, et c'est instructif
  test_login_failure_message_does_not_reveal_account_existence cherchait la
  sous-chaine anglaise 'attempt(s) remaining'. La page etant desormais en
  francais, elle etait absente des deux cotes, l'assertion passait, et le
  mode strict a signale le faux succes. Le test comparait donc l'anglais,
  pas le comportement. Il compare desormais les messages flash rendus,
  quelle que soit la langue. La faille SEC-AUTH-006 reste ouverte, et le
  test la documente toujours.

Les catalogues .po ET .mo sont versionnes : le deploiement est un simple
miroir de fichiers, sans etape de compilation. messages.pot, regenerable,
ne l'est pas.

docs/translations.md documente le processus, les deux pieges (concatenation
de phrases, traduction a l'import), et l'etat de la migration. A noter pour
la suite : les chaines dans les blocs <script> ne peuvent pas etre balisees
telles quelles, il faudra les passer par des attributs data- -- ce qui
rejoint le chantier de sortie de unsafe-inline (OPS-010).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 20:31:25 -04:00
cedrick2711 3feae80767 remodulation du projet et des classes 2026-07-28 23:33:31 -04:00