fix: attach CSRF token to state-changing requests
ci / docker (push) Successful in 5m20s

This commit is contained in:
2026-09-05 17:33:30 +08:00
parent 0dff033a7f
commit 799b522f9b
7 changed files with 391 additions and 5 deletions
+32
View File
@@ -0,0 +1,32 @@
import { describe, expect, it, beforeEach, afterEach } from 'vitest'
import { csrfHeader } from './csrf'
let originalCookie = ''
describe('csrf utilities', () => {
beforeEach(() => {
originalCookie = document.cookie
document.cookie = 'dodo_csrf=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'
})
afterEach(() => {
document.cookie = originalCookie
})
it('adds the x-csrf-token header for write methods', () => {
document.cookie = 'dodo_csrf=token123; path=/'
expect(csrfHeader('POST')).toEqual({ 'x-csrf-token': 'token123' })
expect(csrfHeader('PATCH')).toEqual({ 'x-csrf-token': 'token123' })
expect(csrfHeader('DELETE')).toEqual({ 'x-csrf-token': 'token123' })
})
it('leaves read methods untouched', () => {
document.cookie = 'dodo_csrf=token123; path=/'
expect(csrfHeader('GET')).toEqual({})
expect(csrfHeader(undefined)).toEqual({})
})
it('does not add a header when the cookie is missing', () => {
expect(csrfHeader('POST')).toEqual({})
})
})
+13
View File
@@ -0,0 +1,13 @@
export function csrfHeader(method?: string) {
if (method && !['GET', 'HEAD', 'OPTIONS'].includes(method.toUpperCase())) {
const token = getCookie('dodo_csrf')
if (token) return { 'x-csrf-token': token }
}
return {}
}
export function getCookie(name: string) {
if (typeof document === 'undefined') return ''
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const match = document.cookie.match(new RegExp(`(?:^|; )${escaped}=([^;]*)`))
return match ? decodeURIComponent(match[1]) : ''
}