fix: A19 terminé — plus aucun préfixe cookie-auth exempt du CSRF (v7.3.6)
FlowDeck CI / test (push) Successful in 20m10s
FlowDeck CI / lint (push) Successful in 1m51s
FlowDeck CI / docker (push) Canceled after 0s

- 46 appels non-GET équipés de `X-CSRF-Token` (expression cookie en ligne,
  portée indifférente) : agent_panel (9), settings (12), local_workspace (15),
  gitea_workspace (4), workspace (2), workspaces (5), library (2), welcome (2 en v7.3.5)
- 5 derniers préfixes sortis d'`EXCLUDED_PATHS` : /api/workspace (+/api/workspaces),
  /api/local-workspace, /api/settings, /api/gitea, /api/agent
- il ne reste exempté que : Bearer (/api/webhook, /api/v1, /api/v2, /scim/v2),
  callbacks /auth/*, pages publiques (/s/, /f/), /api/csrf-token et /api/frontend-error
- vérif : `node --check` des <script> des 39 templates → 0 échec (avant et après)
- tests : `anon_csrf()` sur les 5 tests d'anonymat devenus CSRF-first
- suite **1026/1026** · `ruff check app tests` OK
This commit is contained in:
2026-10-01 07:41:23 -04:00
parent cf76e00f12
commit 1f705ce512
16 changed files with 87 additions and 69 deletions
+12 -12
View File
@@ -1337,7 +1337,7 @@ function settingsInit() {
var n = this.newTagName.trim();
if (!n) return;
var r = await fetch('/api/settings/tags', {
method: 'POST', headers: {'Content-Type':'application/json'},
method: 'POST', headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
body: JSON.stringify({name: n, color: this.newTagColor})
});
if (r.ok) { this.newTagName = ''; this.newTagColor = '#787774'; await this.loadTags(); }
@@ -1345,7 +1345,7 @@ function settingsInit() {
async updateTagColor(id, color) {
await fetch('/api/settings/tags/' + id, {
method: 'PUT', headers: {'Content-Type':'application/json'},
method: 'PUT', headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
body: JSON.stringify({color: color})
});
await this.loadTags();
@@ -1353,7 +1353,7 @@ function settingsInit() {
async deleteTag(id) {
if (!confirm('Delete this tag?')) return;
await fetch('/api/settings/tags/' + id, { method: 'DELETE' });
await fetch('/api/settings/tags/' + id, {headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||''}, method: 'DELETE' });
await this.loadTags();
},
@@ -1375,7 +1375,7 @@ function settingsInit() {
this.renamingTag = null; return;
}
await fetch('/api/settings/tags/' + tag.id, {
method: 'PUT', headers: {'Content-Type':'application/json'},
method: 'PUT', headers: {'Content-Type':'application/json', 'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||''},
body: JSON.stringify({name: newName})
});
this.renamingTag = null;
@@ -1648,7 +1648,7 @@ function settingsInit() {
try {
var r = await fetch('/api/agent/keys/' + id + '/models', {
method: 'POST',
headers: {'Content-Type':'application/json'},
headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
body: JSON.stringify({api_key: f.api_key, api_base: f.api_base})
});
var d = await r.json();
@@ -1677,7 +1677,7 @@ function settingsInit() {
if (f.models && f.models.length) body.models = f.models;
var r = await fetch('/api/agent/keys/' + id, {
method: 'PUT',
headers: {'Content-Type':'application/json'},
headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
body: JSON.stringify(body)
});
var d = await r.json();
@@ -1713,7 +1713,7 @@ function settingsInit() {
if (f.api_key) body.api_key = f.api_key;
var r = await fetch('/api/agent/keys/' + id + '/test', {
method: 'POST',
headers: {'Content-Type':'application/json'},
headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
body: JSON.stringify(body)
});
var d = await r.json();
@@ -1739,7 +1739,7 @@ function settingsInit() {
var f = this.keyForm(id);
f.deleting = true; f.msg = ''; f.ok = false;
try {
var r = await fetch('/api/agent/keys/' + id, { method: 'DELETE' });
var r = await fetch('/api/agent/keys/' + id, {headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||''}, method: 'DELETE' });
var d = await r.json();
if (r.ok) {
f.has_key = false; f.api_key = ''; f.models = []; f.model = ''; f.last_error = '';
@@ -1899,7 +1899,7 @@ function settingsInit() {
if (!file) return;
var form = new FormData();
form.append('file', file);
var r = await fetch('/api/settings/avatar', { method: 'POST', body: form });
var r = await fetch('/api/settings/avatar', {headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||''}, method: 'POST', body: form });
if (r.ok) {
var d = await r.json();
this.avatarUrl = d.avatar_url + '?t=' + Date.now();
@@ -1910,7 +1910,7 @@ function settingsInit() {
async selectAvatarColor(color) {
this.avatarColor = color;
var r = await fetch('/api/settings/avatar-color', {
method: 'POST', headers: {'Content-Type':'application/json'},
method: 'POST', headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'},
body: JSON.stringify({color: color})
});
if (r.ok) { this.avatarUrl = ''; }
@@ -2055,7 +2055,7 @@ function settingsInit() {
// ── v5.2.0 API tokens ──
async loadApiTokens() {
try {
var r = await fetch('/api/settings/tokens', {credentials:'same-origin'});
var r = await fetch('/api/settings/tokens', {headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||''}, credentials:'same-origin'});
var d = await r.json();
this.apiTokens = d.tokens || [];
} catch(e) { this.apiTokens = []; }
@@ -2065,7 +2065,7 @@ function settingsInit() {
if (!name) return;
try {
var r = await fetch('/api/settings/tokens', {method:'POST', credentials:'same-origin',
headers: {'Content-Type':'application/json'}, body: JSON.stringify({name: name})});
headers: {'X-CSRF-Token': (document.cookie.match(/csrf_token=([^;]+)/)||[])[1]||'', 'Content-Type':'application/json'}, body: JSON.stringify({name: name})});
var d = await r.json();
if (!r.ok) { window.showToast && window.showToast(d.detail || 'Erreur', 'error'); return; }
this.newToken = d;