fix: A43 TERMINÉ + A38 phase 1 — CSRF rendu côté serveur, helper unique (v7.33.0)
A43 (les 4 items sont clos) :
- `__CSRF_PLACEHOLDER__` supprimé : ContextVar CSRF_TOKEN posée par
CSRFMiddleware avant call_next (même mécanique que le nonce CSP),
global `{{ csrf_token() }}` dans templating, base.html rend
`{"X-CSRF-Token":{{ csrf_token()|tojson }}}` — vide si cookie absent,
`htmx:configRequest` re-lit le cookie à chaque appel → plus jamais de
jeton factice servi.
- Palette : `fetch('/api/search…')` SANS header (GET ∈ SAFE_METHODS →
CSRF inapplicable) — le JSON.parse du body-attr par frappe disparaît.
- utcnow déprécié = 0 dans app/**.py ; health loggé (A25) + booléens db/
gitea = raison ; probe réseau = voulu (test de connectivité).
A38 phase 1 (CSRF unifié) :
- `window.getCsrf()` unique dans le <head> de base.html.
- 76 lectures brutes du cookie → getCsrf() dans 13 fichiers (47 formes
`(…||[])[1]||''`, 25 déclarations `const X = match(…)` avec conversion
de leurs usages `X?X[1]:''` → `X` (noms collectés PAR FICHIER, jamais
de règle globale), 4 formes espacées).
- Définitions dupliquées supprimées : card_detail.html (multi-lignes),
database_table.js (1 ligne dans IIFE → chute sur le global).
- Les 3 variantes de base.html (IIFE getCsrf + 2 getCsrfToken) →
`return getCsrf()`.
- welcome.html garde sa lecture locale (page autonome sans base) ;
reste exactement 1 raw dans base.html (la déf head) + 2 dans welcome.
Test : test_csrf_server_rendered_no_placeholder (pas de placeholder +
token du hx-headers == cookie csrf_token).
suite **1092/1092** · ruff OK · node --check vert · docs à jour
This commit is contained in:
@@ -97,7 +97,7 @@
|
||||
var payload = {prompt: message};
|
||||
if(context && context.trim()) payload.context = String(context).slice(0, 20000);
|
||||
return fetch('/api/agent/generate', {
|
||||
method:'POST', headers:{'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
|
||||
method:'POST', headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'},
|
||||
body: JSON.stringify(payload)
|
||||
}).then(function(resp){
|
||||
return resp.json();
|
||||
@@ -243,7 +243,7 @@
|
||||
if(self.llmModel) payload.model = self.llmModel;
|
||||
|
||||
fetch('/api/agent/generate', {
|
||||
method:'POST', headers:{'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'}, body: JSON.stringify(payload)
|
||||
method:'POST', headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'}, body: JSON.stringify(payload)
|
||||
}).then(function(r){ return r.json(); }).then(function(d){
|
||||
self.messages = self.messages.filter(function(m){ return m.id !== thinkId; });
|
||||
if(d && d.ok && d.text){
|
||||
@@ -366,7 +366,7 @@
|
||||
installGallerySkill(slug, icon, name){
|
||||
var self = this;
|
||||
fetch('/api/agent/skills/gallery/' + encodeURIComponent(slug) + '/install', {
|
||||
method: 'POST', headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type': 'application/json'}, body: '{}'
|
||||
method: 'POST', headers: {'X-CSRF-Token': getCsrf(), 'Content-Type': 'application/json'}, body: '{}'
|
||||
}).then(function(r){
|
||||
return r.json().then(function(d){ return {ok: r.ok, d: d}; });
|
||||
}).then(function(res){
|
||||
@@ -530,7 +530,7 @@
|
||||
var body = {title:'Nouvelle conversation'};
|
||||
if(self.llmProvider) body.provider = self.llmProvider;
|
||||
if(self.llmModel) body.model = self.llmModel;
|
||||
fetch('/api/agent/conversations', {method:'POST', headers:{'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'}, body: JSON.stringify(body)})
|
||||
fetch('/api/agent/conversations', {method:'POST', headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'}, body: JSON.stringify(body)})
|
||||
.then(function(r){return r.json()}).then(function(d){
|
||||
var conv = {id:d.id, title:d.title, updated_at:new Date().toISOString()};
|
||||
self.conversations.unshift(conv); self.currentConv = conv;
|
||||
@@ -547,7 +547,7 @@
|
||||
if(self.llmProvider) body.provider = self.llmProvider;
|
||||
if(self.llmModel) body.model = self.llmModel;
|
||||
self._ensuring = fetch('/api/agent/conversations', {
|
||||
method:'POST', headers:{'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'}, body: JSON.stringify(body)
|
||||
method:'POST', headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'}, body: JSON.stringify(body)
|
||||
}).then(function(r){return r.json()}).then(function(d){
|
||||
var conv = {id:d.id, title:d.title, updated_at:new Date().toISOString()};
|
||||
self.conversations.unshift(conv); self.currentConv = conv;
|
||||
@@ -569,7 +569,7 @@
|
||||
if(self.llmProvider) body.provider = self.llmProvider;
|
||||
if(self.llmModel) body.model = self.llmModel;
|
||||
fetch('/api/agent/conversations/'+self.currentConv.id, {
|
||||
method:'PATCH', headers:{'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'}, body: JSON.stringify(body)
|
||||
method:'PATCH', headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'}, body: JSON.stringify(body)
|
||||
}).catch(function(){});
|
||||
},
|
||||
|
||||
@@ -1370,7 +1370,7 @@
|
||||
if(m.id && /^\d+$/.test(String(m.id))) payload.message_id = Number(m.id);
|
||||
fetch('/api/agent/feedback', {
|
||||
method: 'POST',
|
||||
headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type': 'application/json'},
|
||||
headers: {'X-CSRF-Token': getCsrf(), 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(payload)
|
||||
}).then(function(r){ return r.json(); }).then(function(d){
|
||||
if(d && d.status === 'recorded'){ m.fb = rating; }
|
||||
@@ -1468,7 +1468,7 @@
|
||||
|
||||
return fetch('/api/agent/conversations/'+self.currentConv.id+'/run', {
|
||||
method:'POST',
|
||||
headers:{'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
|
||||
headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'},
|
||||
body: JSON.stringify(payload)
|
||||
}).then(function(resp){
|
||||
if(!resp.ok){ return resp.json().then(function(j){ throw new Error(j.detail || ('HTTP '+resp.status)); }); }
|
||||
|
||||
Reference in New Issue
Block a user