cms.c2sgmbh/tests/unit/security/cron-auth.unit.spec.ts
Martin Porwoll e3987e50dc feat: security hardening, monitoring improvements, and API guards
- Hardened cron endpoints with coordination and auth improvements
- Added API guards and input validation layer
- Security observability and secrets health checks
- Monitoring types and service improvements
- PDF URL validation and newsletter unsubscribe security
- Unit tests for security-critical paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 11:42:56 +00:00

111 lines
3.4 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { NextRequest, NextResponse } from 'next/server'
function createCronRequest(
url = 'https://example.com/api/cron/test',
authHeader?: string,
extraHeaders: Record<string, string> = {},
): NextRequest {
const headers = new Headers(extraHeaders)
if (authHeader) {
headers.set('authorization', authHeader)
}
return new NextRequest(url, { headers })
}
describe('cron auth and coordination', () => {
beforeEach(() => {
vi.resetModules()
vi.stubEnv('REDIS_ENABLED', 'false')
vi.stubEnv('CRON_SECRET', 'top-secret-token')
})
afterEach(() => {
vi.unstubAllEnvs()
})
it('denies requests when CRON_SECRET is missing', async () => {
vi.stubEnv('CRON_SECRET', '')
const { requireCronAuth } = await import('@/lib/security/cron-auth')
const response = requireCronAuth(createCronRequest())
expect(response).not.toBeNull()
expect(response?.status).toBe(503)
})
it('denies requests with missing bearer header', async () => {
const { requireCronAuth } = await import('@/lib/security/cron-auth')
const response = requireCronAuth(createCronRequest())
expect(response).not.toBeNull()
expect(response?.status).toBe(401)
})
it('allows requests with valid bearer header', async () => {
const { requireCronAuth } = await import('@/lib/security/cron-auth')
const response = requireCronAuth(
createCronRequest('https://example.com/api/cron/test', 'Bearer top-secret-token'),
)
expect(response).toBeNull()
})
it('ignores duplicate idempotency-key requests', async () => {
const { withCronExecution, resetCronCoordinationStateForTests } = await import(
'@/lib/security/cron-coordination'
)
resetCronCoordinationStateForTests()
const req1 = createCronRequest(
'https://example.com/api/cron/test?idempotencyKey=abc',
'Bearer top-secret-token',
)
const req2 = createCronRequest(
'https://example.com/api/cron/test?idempotencyKey=abc',
'Bearer top-secret-token',
)
const first = await withCronExecution(req1, { endpoint: 'test-cron' }, async () =>
NextResponse.json({ ok: true }),
)
const second = await withCronExecution(req2, { endpoint: 'test-cron' }, async () =>
NextResponse.json({ ok: true }),
)
expect(first.status).toBe(200)
expect(second.status).toBe(202)
})
it('enforces execution lock per endpoint', async () => {
const { withCronExecution, resetCronCoordinationStateForTests } = await import(
'@/lib/security/cron-coordination'
)
resetCronCoordinationStateForTests()
let releaseHandler: (() => void) | null = null
const firstRun = withCronExecution(
createCronRequest('https://example.com/api/cron/test', 'Bearer top-secret-token'),
{ endpoint: 'locked-cron', lockTtlMs: 120000 },
async () =>
new Promise<NextResponse>((resolve) => {
releaseHandler = () => resolve(NextResponse.json({ ok: true }))
}),
)
const second = await withCronExecution(
createCronRequest('https://example.com/api/cron/test', 'Bearer top-secret-token'),
{ endpoint: 'locked-cron', lockTtlMs: 120000 },
async () => NextResponse.json({ ok: true }),
)
expect(second.status).toBe(423)
releaseHandler?.()
const first = await firstRun
expect(first.status).toBe(200)
})
})