/** * Unit tests for validateAnalyzeToken — the optional `token` validation on * POST /api/analyze. Tested directly (not via a booted server) since the * route validation is otherwise only reachable through createServer. * * Closes the test gap (finding 12) and locks the github.com host-bind * (finding 6) and CRLF/charset guards from the PR #2223 tri-review. */ import { describe, it, expect } from 'vitest'; import { validateAnalyzeToken } from '../../src/server/api.js'; const GH = 'https://github.com/owner/repo'; describe('validateAnalyzeToken', () => { it('returns null when no token is provided', () => { expect(validateAnalyzeToken(undefined, GH)).toBeNull(); expect(validateAnalyzeToken(undefined, undefined)).toBeNull(); }); it('accepts a well-formed token for a github.com URL', () => { expect(validateAnalyzeToken('ghp_abc123', GH)).toBeNull(); expect(validateAnalyzeToken('ghp_abc123', 'https://www.github.com/o/r')).toBeNull(); }); it('rejects a non-string token', () => { expect(validateAnalyzeToken(123 as unknown, GH)).toEqual({ status: 400, error: '"token" must be a string', }); }); it('rejects an empty or over-long token', () => { expect(validateAnalyzeToken('', GH)?.error).toBe('"token" length must be between 1 and 256'); expect(validateAnalyzeToken('a'.repeat(257), GH)?.error).toBe( '"token" length must be between 1 and 256', ); }); it('rejects a token with characters that could smuggle a header (CRLF/space/colon)', () => { for (const bad of ['abc def', 'abc\r\nHost: x', 'x-access-token:abc', 'abc