Files
core2026/packages/common/test/apiOrigin.test.ts
T

52 lines
2.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { createApiOriginGuard, resolveApiAllowedOrigins } from '../src/http/apiOrigin.js';
describe('public API origin policy', () => {
it('derives exact origins from application URLs and explicit extra origins', async () => {
const origins = resolveApiAllowedOrigins('http://localhost:5173,https://sam.hided.net', [
'https://sam.hided.net/gateway/',
'https://sam.hided.net/che/',
]);
expect(origins).toEqual(['https://sam.hided.net', 'http://localhost:5173']);
const guard = createApiOriginGuard(origins);
await expect(guard({ headers: {} })).resolves.toBeUndefined();
for (const origin of origins) await expect(guard({ headers: { origin } })).resolves.toBeUndefined();
for (const origin of [
'https://untrusted.example',
'https://sam.hided.net.untrusted.example',
'http://sam.hided.net',
'https://sam.hided.net:8443',
'null',
'',
'https://sam.hided.net,https://untrusted.example',
'x'.repeat(2049),
['https://sam.hided.net', 'https://untrusted.example'],
]) {
await expect(guard({ headers: { origin } })).rejects.toMatchObject({ statusCode: 403 });
}
});
it.each([
'*',
'null',
'https://user:password@example.com',
'file:///tmp/example',
'https://x.test/path',
'https://x.test?query',
'https://x.test#fragment',
'https://x.test,',
])('rejects unsafe configuration %s', (value) => {
expect(() => resolveApiAllowedOrigins(value, [])).toThrow();
});
it('bounds configuration and fails closed when no public origin is configured', async () => {
expect(() =>
resolveApiAllowedOrigins(Array.from({ length: 33 }, (_, i) => `https://${i}.test`).join(','), [])
).toThrow();
expect(() => resolveApiAllowedOrigins('https://' + 'a'.repeat(2048) + '.test', [])).toThrow();
await expect(
createApiOriginGuard([])({ headers: { origin: 'https://untrusted.example' } })
).rejects.toMatchObject({ statusCode: 403 });
});
});