f8e8ada581
Batch 1 of the test-strategy rollout. Fills the highest-value gaps where existing specs were only `toBeDefined()` smoke tests or absent. Test-only, no production source touched. - token.service.behavior.spec.ts: verifyJwt type-mismatch rejection (confused deputy), generateAccessToken/generateCollabToken disabled-user -> Forbidden, agent `actor` claim only from signed provenance, correct expiry. - auth.util.spec.ts: computeEmailSignature (stable HMAC, case-normalized), throwIfEmailNotVerified, validateSsoEnforcement, validateAllowedEmail; it.todo flags the unguarded `@`-less email TypeError. - guards/setup.guard.spec.ts: cloud blocks setup, first-run allows, re-run on an initialised instance is forbidden (privilege escalation guard). - security-headers.spec.ts: resolveFrameHeader clickjacking/CSP branches. - utils.security.spec.ts: redactSensitiveUrl, extractBearerTokenFromHeader, parseRedisUrl, normalizePostgresUrl, diffAuditTrackedFields, isUserDisabled. 60 tests + 1 todo, all green. Reviewed for mutation resistance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
1.7 KiB
TypeScript
53 lines
1.7 KiB
TypeScript
import { resolveFrameHeader } from './security-headers';
|
|
|
|
describe('resolveFrameHeader', () => {
|
|
describe('iframe embedding disabled (clickjacking protection)', () => {
|
|
it('returns X-Frame-Options SAMEORIGIN and ignores origins', () => {
|
|
expect(resolveFrameHeader(false, [])).toEqual({
|
|
name: 'X-Frame-Options',
|
|
value: 'SAMEORIGIN',
|
|
});
|
|
});
|
|
|
|
it('still returns X-Frame-Options even when origins are configured', () => {
|
|
// A wrong branch could leak a permissive CSP here; origins must be ignored
|
|
// when embedding is disabled so clickjacking protection stays intact.
|
|
const result = resolveFrameHeader(false, [
|
|
'https://a.com',
|
|
'https://b.com',
|
|
]);
|
|
expect(result).toEqual({
|
|
name: 'X-Frame-Options',
|
|
value: 'SAMEORIGIN',
|
|
});
|
|
expect(result?.name).not.toBe('Content-Security-Policy');
|
|
});
|
|
});
|
|
|
|
describe('iframe embedding allowed', () => {
|
|
it('returns null when there are no allowed origins', () => {
|
|
expect(resolveFrameHeader(true, [])).toBeNull();
|
|
});
|
|
|
|
it('builds a frame-ancestors CSP for a single origin', () => {
|
|
expect(resolveFrameHeader(true, ['https://a.com'])).toEqual({
|
|
name: 'Content-Security-Policy',
|
|
value: "frame-ancestors 'self' https://a.com",
|
|
});
|
|
});
|
|
|
|
it('space-joins multiple origins after self', () => {
|
|
expect(
|
|
resolveFrameHeader(true, [
|
|
'https://a.com',
|
|
'https://b.com',
|
|
'https://c.com',
|
|
]),
|
|
).toEqual({
|
|
name: 'Content-Security-Policy',
|
|
value: "frame-ancestors 'self' https://a.com https://b.com https://c.com",
|
|
});
|
|
});
|
|
});
|
|
});
|