- ContentSecurityPolicyMiddleware : nonce aléatoire par requête dans la
ContextVar `CSP_NONCE` (posée avant `call_next` → visible des templates),
`script-src 'self' 'unsafe-eval' 'nonce-…'` — plus aucun script inline
sans nonce ne tourne (fin des XSS injectés en JS)
- 38 tags `<script>` des templates : `nonce="{{ csp_nonce() }}"` (passage
scripté, vérifié : 0 restant) ; `LOCAL_LOGIN_HTML` (constante de module) :
helper `_with_nonce()` au rendu ; collections.py : 3 scripts Python
(chart/form/map) noncés
- `<meta name="htmx-config" content='{"inlineScriptNonce": …}'>` dans base.html
: htmx ré-injecte les <script> des réponses boostées avec le bon nonce
- `script-src-attr 'unsafe-inline'` : les 74 handlers `onclick=` inline
restent couverts (le nonce les aurait désactivés aussi)
- chart.js (cdn.jsdelivr.net) et leaflet (unpkg) ajoutés à script-src/style-src
: vues chart/map déjà BLOQUÉES par la CSP depuis toujours
(commentaire ponytail: upgrade = vendoriser puis retirer les hôtes)
- reste d'A20 : unsafe-eval (Alpine x-data → @alpinejs/csp), externalisation
JS (A27), resserrer img-src/connect-src
test : test_csp_nonce_per_request (page base.html + page hors template,
nonce unique par requête)
suite **1037/1037** · `ruff check app tests` OK · docs à jour
Rocket Loader (Cloudflare) reecrit les balises script inline en type=...-text/javascript et les execute de facon differee, ce qui casse l'enregistrement des composants Alpine et l'init des pages lors d'un acces direct via le tunnel (ex. /settings ne se chargeait pas au complet). Ajout de data-cfasync=false sur les scripts inline des pages completes et des partiels du shell (settings, accounts, trash, workspace, board, gitea_workspace, welcome, import, page_editor_collection/embed, _database_table_scripts, _notification_bell, public_page, local_workspace). Les fragments charges via HTMX restent inchanges.
Config:
- Fixed SQLite path parsing on Windows (handle drive letters without prepending /)
- Changed default theme from dark to light
- Updated OAuth test credentials (gitea_oauth_client_id/secret)
Auth:
- First real user (excluding default admin with no password) becomes admin
- Changed user count query to exclude users without password_hash
File viewer:
- Removed separate _render_file_viewer() —
Erreur: block 'page_title' defined twice → TemplateAssertionError
Fix: title_prefix (nouveau bloc) pour <title>, page_title (existant) pour Alpine
10 templates mis à jour avec title_prefix.
Alpine 3.x a renommé .away en .outside. Toutes les occurrences dans:
- base.html (3: user menu, context menu, ws ctx)
- page_editor.html (5: share, perm, access, more, gsMore)
- workspaces.html (1: dialog overlay)
- workspace.html (1: modal overlay)
- local_workspace.html (6: create, rename, delete modals, context menus)
Ces .away cassées empêchaient tous les @click Alpine de fonctionner
sur ces pages (Cancel, Delete, et tous les autres boutons modaux).
- local_workspace.html: supprimé Alpine.data dupliqué + ajout hamburger
- workspaces.html, workspace.html, settings.html, page_editor.html: +hamburger
- auth.py: +path="/" sur tous les set_cookie de session (Chrome compat)
- csrf.py: +path="/" sur cookie CSRF
Root cause des bugs:
1. Chrome: cookie sans path="/" → non envoyé sur certaines routes
2. Firefox: les templates écrasaient le block topbar → pas de hamburger
→ sidebar inaccessible sur mobile (overlay + slide-in ne fonctionnaient pas)
- GET /workspace — HTML page showing all projects
- GET /api/workspace/projects — JSON API returning builtin + gitea + github
- POST /api/workspace/projects — create new built-in project
- Workspace template with project cards, forge badges, create modal
- Built-in projects: pages without workspace/parent
- Gitea repos: via existing GiteaClient
- GitHub repos: via OAuth token from user_oauth_tokens