Files

38 lines
1.9 KiB
TypeScript
Raw Permalink Normal View History

import { test, expect, env, seedUsers, ApiClient } from '@e2e'
// What the backend itself promises about cookies and cross-origin access.
// (Security headers such as HSTS and CSP come from the host's nginx, which is not
// in this repo, so they are checked against a deployment, not here.)
test.describe('security basics', () => {
test('sign-in tokens are HttpOnly, Secure cookies @security', async ({ playwright }) => {
const context = await playwright.request.newContext()
const response = await context.post(`${env.apiUrl}/users/login`, {
form: { username: seedUsers.admin.email, password: seedUsers.admin.password },
})
expect(response.ok()).toBe(true)
const cookies = response.headersArray().filter((h) => h.name.toLowerCase() === 'set-cookie').map((h) => h.value)
for (const name of ['access_token', 'refresh_token']) {
const cookie = cookies.find((c) => c.startsWith(`${name}=`))
expect(cookie, `${name} cookie`).toBeDefined()
expect(cookie).toMatch(/;\s*HttpOnly/i)
expect(cookie).toMatch(/;\s*Secure/i)
}
await context.dispose()
})
test('another website cannot call the API with a visitor\'s credentials @security', async ({ request }) => {
const response = await new ApiClient(request).raw('OPTIONS', '/users/me', {
headers: { Origin: 'https://evil.example', 'Access-Control-Request-Method': 'GET' },
})
expect(response.headers()['access-control-allow-origin']).toBeUndefined()
})
test('the dashboard\'s own origin is allowed @security', async ({ request }) => {
const response = await new ApiClient(request).raw('OPTIONS', '/users/me', {
headers: { Origin: env.frontendUrl, 'Access-Control-Request-Method': 'GET' },
})
expect(response.headers()['access-control-allow-origin']).toBe(env.frontendUrl)
expect(response.headers()['access-control-allow-credentials']).toBe('true')
})
})