147 lines
4.4 KiB
TypeScript
147 lines
4.4 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { pasta } from '../fixtures';
|
|
import { exportRecipes } from '../../src/lib/import';
|
|
const headers = { origin: 'http://localhost:3100' };
|
|
test('authorization, CSRF, optimistic concurrency, validation and transactional imports', async ({
|
|
request,
|
|
}) => {
|
|
expect((await request.post('/api/recipes', { headers, data: pasta })).status()).toBe(401);
|
|
expect(
|
|
(
|
|
await request.post('/api/auth/login', {
|
|
headers: { origin: 'https://evil.invalid' },
|
|
data: { username: 'testadmin', password: process.env.TEST_PASSWORD },
|
|
})
|
|
).status(),
|
|
).toBe(403);
|
|
const login = await request.post('/api/auth/login', {
|
|
headers,
|
|
data: { username: 'testadmin', password: process.env.TEST_PASSWORD },
|
|
});
|
|
expect(login.ok()).toBe(true);
|
|
expect(login.headers()['set-cookie']).toMatch(/HttpOnly/i);
|
|
expect(login.headers()['set-cookie']).toMatch(/SameSite=strict/i);
|
|
const created = await request.post('/api/recipes', {
|
|
headers,
|
|
data: { ...pasta, id: undefined, title: 'API Rezept', published: false },
|
|
});
|
|
expect(created.status()).toBe(201);
|
|
const r = await created.json();
|
|
expect(
|
|
(
|
|
await request.put(`/api/recipes/${r.id}`, {
|
|
headers,
|
|
data: { ...r, title: 'API Rezept geändert' },
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
expect(
|
|
(
|
|
await request.put(`/api/recipes/${r.id}`, {
|
|
headers,
|
|
data: { ...r, title: 'Veraltete Änderung' },
|
|
})
|
|
).status(),
|
|
).toBe(409);
|
|
expect(
|
|
(await request.put(`/api/recipes/${r.id}`, { headers, data: { ...r, servings: 0 } })).status(),
|
|
).toBe(422);
|
|
expect(
|
|
(
|
|
await request.post('/api/import', {
|
|
headers,
|
|
data: {
|
|
format: 'json',
|
|
preview: false,
|
|
raw: exportRecipes(
|
|
[{ ...pasta, id: undefined, title: 'Transaktion darf nicht bleiben' }, pasta],
|
|
'json',
|
|
),
|
|
},
|
|
})
|
|
).status(),
|
|
).toBe(409);
|
|
expect(
|
|
(await (await request.get('/api/recipes?admin=1')).json()).some(
|
|
(r: { title: string }) => r.title === 'Transaktion darf nicht bleiben',
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
(
|
|
await request.post('/api/import', {
|
|
headers,
|
|
data: {
|
|
format: 'xml',
|
|
preview: true,
|
|
raw: '<!DOCTYPE x [<!ENTITY e SYSTEM "file:///etc/passwd">]><x/>',
|
|
},
|
|
})
|
|
).status(),
|
|
).toBe(422);
|
|
expect(
|
|
(
|
|
await request.post('/api/media', {
|
|
headers: { ...headers, 'content-type': 'image/svg+xml' },
|
|
data: '<svg />',
|
|
})
|
|
).status(),
|
|
).toBe(415);
|
|
expect(
|
|
(
|
|
await request.post('/api/media', {
|
|
headers: { ...headers, 'content-type': 'image/png' },
|
|
data: 'not a png',
|
|
})
|
|
).status(),
|
|
).toBe(422);
|
|
await request.post('/api/auth/logout', { headers });
|
|
expect((await request.get(`/api/recipes/${r.id}`)).status()).toBe(404);
|
|
expect((await request.get('/api/export')).status()).toBe(401);
|
|
expect((await request.delete(`/api/recipes/${r.id}`, { headers })).status()).toBe(401);
|
|
});
|
|
test('password change revokes other sessions and brute force is rate limited', async ({
|
|
request,
|
|
playwright,
|
|
}) => {
|
|
const other = await playwright.request.newContext({ baseURL: 'http://localhost:3100' });
|
|
const credentials = { username: 'testadmin', password: process.env.TEST_PASSWORD };
|
|
await request.post('/api/auth/login', { headers, data: credentials });
|
|
await other.post('/api/auth/login', { headers, data: credentials });
|
|
const replacement = `${process.env.TEST_PASSWORD}-changed`;
|
|
expect(
|
|
(
|
|
await request.post('/api/auth/password', {
|
|
headers,
|
|
data: { current: process.env.TEST_PASSWORD, password: replacement },
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
expect((await other.get('/api/export')).status()).toBe(401);
|
|
expect(
|
|
(
|
|
await request.post('/api/auth/password', {
|
|
headers,
|
|
data: { current: replacement, password: process.env.TEST_PASSWORD },
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
for (let n = 0; n < 8; n++)
|
|
expect(
|
|
(
|
|
await other.post('/api/auth/login', {
|
|
headers,
|
|
data: { username: 'nonexistent', password: 'wrong' },
|
|
})
|
|
).status(),
|
|
).toBe(401);
|
|
expect(
|
|
(
|
|
await other.post('/api/auth/login', {
|
|
headers,
|
|
data: { username: 'nonexistent', password: 'wrong' },
|
|
})
|
|
).status(),
|
|
).toBe(429);
|
|
await other.dispose();
|
|
});
|