57 lines
1.9 KiB
TypeScript
57 lines
1.9 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
/**
|
|
* E2E tests for Issue #20 — CSRF Origin header validation
|
|
* Verifies that mutating requests with mismatched Origin are rejected.
|
|
*/
|
|
|
|
test.describe('CSRF Origin Validation (Issue #20)', () => {
|
|
const TEST_USER = process.env.AUTH_USER || 'testadmin'
|
|
const TEST_PASS = process.env.AUTH_PASS || 'testpass1234!'
|
|
const TEST_API_KEY = process.env.API_KEY || 'test-api-key-e2e-12345'
|
|
|
|
test('POST with mismatched Origin is rejected', async ({ request }) => {
|
|
const res = await request.post('/api/auth/login', {
|
|
data: { username: 'test', password: 'test' },
|
|
headers: {
|
|
'origin': 'https://evil.example.com',
|
|
'host': '127.0.0.1:3005'
|
|
}
|
|
})
|
|
expect(res.status()).toBe(403)
|
|
const body = await res.json()
|
|
expect(body.error).toContain('CSRF')
|
|
})
|
|
|
|
test('POST with matching Origin is allowed', async ({ request }) => {
|
|
const res = await request.post('/api/auth/login', {
|
|
data: { username: TEST_USER, password: TEST_PASS },
|
|
headers: {
|
|
'origin': 'http://127.0.0.1:3005',
|
|
'host': '127.0.0.1:3005'
|
|
}
|
|
})
|
|
// Should not be 403 CSRF — may be 200 (success) or other status
|
|
expect(res.status()).not.toBe(403)
|
|
})
|
|
|
|
test('POST without Origin header is allowed (non-browser client)', async ({ request }) => {
|
|
const res = await request.post('/api/auth/login', {
|
|
data: { username: TEST_USER, password: TEST_PASS },
|
|
})
|
|
// No Origin = non-browser client, should be allowed through CSRF check
|
|
expect(res.status()).not.toBe(403)
|
|
})
|
|
|
|
test('GET requests are not subject to CSRF check', async ({ request }) => {
|
|
const res = await request.get('/api/agents', {
|
|
headers: {
|
|
'origin': 'https://evil.example.com',
|
|
'x-api-key': TEST_API_KEY
|
|
}
|
|
})
|
|
// GET is exempt from CSRF — should not be 403
|
|
expect(res.status()).not.toBe(403)
|
|
})
|
|
})
|