auth-hardening.test.ts66 lines · main
1import { describe, expect, test } from 'bun:test';
2
3import {
4 domainVerificationTxt,
5 sanitizeRelayState,
6 sdkKeyAllowsMethod,
7 txtRecordsContainDomainToken,
8} from './auth-hardening.js';
9
10describe('txtRecordsContainDomainToken', () => {
11 const token = 'tok_abc123';
12
13 test('matches preferred briven-domain-verification= form', () => {
14 const records = [[domainVerificationTxt(token)]];
15 expect(txtRecordsContainDomainToken(records, token)).toBe(true);
16 });
17
18 test('matches raw token for backward compatibility', () => {
19 expect(txtRecordsContainDomainToken([['other', token]], token)).toBe(true);
20 });
21
22 test('rejects missing token', () => {
23 expect(txtRecordsContainDomainToken([['v=spf1']], token)).toBe(false);
24 expect(txtRecordsContainDomainToken([], token)).toBe(false);
25 });
26});
27
28describe('sanitizeRelayState', () => {
29 const origins = ['https://app.example.com', 'https://briven.tech'];
30
31 test('allows relative paths', () => {
32 expect(sanitizeRelayState('/dashboard', origins)).toBe('/dashboard');
33 expect(sanitizeRelayState('/', origins)).toBe('/');
34 });
35
36 test('allows absolute URL on allowlist', () => {
37 expect(sanitizeRelayState('https://app.example.com/cb', origins)).toBe(
38 'https://app.example.com/cb',
39 );
40 });
41
42 test('blocks open redirects and evil schemes', () => {
43 expect(sanitizeRelayState('https://evil.com/phish', origins)).toBe('/');
44 expect(sanitizeRelayState('//evil.com/phish', origins)).toBe('/');
45 expect(sanitizeRelayState('javascript:alert(1)', origins)).toBe('/');
46 expect(sanitizeRelayState('data:text/html,hi', origins)).toBe('/');
47 });
48
49 test('empty / null falls back to /', () => {
50 expect(sanitizeRelayState(null, origins)).toBe('/');
51 expect(sanitizeRelayState('', origins)).toBe('/');
52 });
53});
54
55describe('sdkKeyAllowsMethod', () => {
56 test('read scope is GET/HEAD/OPTIONS only', () => {
57 expect(sdkKeyAllowsMethod('read', 'GET')).toBe(true);
58 expect(sdkKeyAllowsMethod('read', 'POST')).toBe(false);
59 expect(sdkKeyAllowsMethod('read', 'DELETE')).toBe(false);
60 });
61
62 test('read-write and admin allow mutating methods', () => {
63 expect(sdkKeyAllowsMethod('read-write', 'POST')).toBe(true);
64 expect(sdkKeyAllowsMethod('admin', 'DELETE')).toBe(true);
65 });
66});