56 lines
2.9 KiB
TypeScript
56 lines
2.9 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getAppBaseUrl, getVkRedirectUri } from '../src/lib/auth/app-config';
|
|
import { setSessionCookie, clearSessionCookie } from '../src/lib/auth/session';
|
|
import { validateCsrfOrigin } from '../src/lib/auth/csrf-guard';
|
|
|
|
afterEach(() => vi.unstubAllEnvs());
|
|
function config(base: string, profile = 'local-single-user') {
|
|
vi.stubEnv('NODE_ENV', 'production');
|
|
vi.stubEnv('DEPLOYMENT_PROFILE', profile);
|
|
vi.stubEnv('APP_BASE_URL', base);
|
|
vi.stubEnv('VK_REDIRECT_URI', `${base}/api/auth/vk/callback`);
|
|
}
|
|
describe('production deployment profiles', () => {
|
|
it('allows HTTP only in the explicit loopback profile and uses HttpOnly Lax cookies', () => {
|
|
config('http://localhost:3100');
|
|
expect(getAppBaseUrl()).toBe('http://localhost:3100');
|
|
expect(getVkRedirectUri()).toBe('http://localhost:3100/api/auth/vk/callback');
|
|
const res = NextResponse.json({});
|
|
setSessionCookie(res, 'fake-session');
|
|
expect(res.headers.get('set-cookie')).toContain('HttpOnly');
|
|
expect(res.headers.get('set-cookie')).toContain('SameSite=lax');
|
|
expect(res.headers.get('set-cookie')).not.toContain('Secure');
|
|
clearSessionCookie(res);
|
|
expect(res.headers.get('set-cookie')).not.toContain('Secure');
|
|
});
|
|
it.each(['http://evil.test', 'http://localhost.evil.test', 'http://192.168.1.81:3100', 'https://localhost:3100', 'http://localhost:3100/path'])('rejects invalid local origin %s', (base) => {
|
|
config(base);
|
|
expect(() => getAppBaseUrl()).toThrow();
|
|
});
|
|
it('keeps strict HTTPS and Secure cookies outside the local profile', () => {
|
|
config('http://localhost:3100', 'strict');
|
|
expect(() => getAppBaseUrl()).toThrow();
|
|
config('https://random.ochenstarik.ru', 'strict');
|
|
expect(getAppBaseUrl()).toBe('https://random.ochenstarik.ru');
|
|
const res = NextResponse.json({});
|
|
setSessionCookie(res, 'fake-session');
|
|
expect(res.headers.get('set-cookie')).toContain('Secure');
|
|
});
|
|
it('rejects redirect origin and path mismatch', () => {
|
|
config('http://localhost:3100');
|
|
vi.stubEnv('VK_REDIRECT_URI', 'http://localhost:3101/api/auth/vk/callback');
|
|
expect(() => getVkRedirectUri()).toThrow();
|
|
vi.stubEnv('VK_REDIRECT_URI', 'http://localhost:3100/other');
|
|
expect(() => getVkRedirectUri()).toThrow();
|
|
});
|
|
it('ignores forged Host/forwarded headers and rejects scheme downgrade for CSRF', () => {
|
|
config('https://random.ochenstarik.ru', 'strict');
|
|
const request = (origin: string) => new NextRequest('https://random.ochenstarik.ru/api/giveaways', {
|
|
method: 'POST', headers: { origin, host: 'evil.test', 'x-forwarded-host': 'evil.test' },
|
|
});
|
|
expect(() => validateCsrfOrigin(request('https://evil.test'))).toThrow();
|
|
expect(() => validateCsrfOrigin(request('http://random.ochenstarik.ru'))).toThrow();
|
|
expect(() => validateCsrfOrigin(request('https://random.ochenstarik.ru'))).not.toThrow();
|
|
});
|
|
});
|