124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
/** @jest-environment node */
|
|
|
|
import {
|
|
getAllowedCorsOrigins,
|
|
getRequestHostOrigin,
|
|
isAllowedCorsOrigin,
|
|
} from '@/lib/cors-origin'
|
|
|
|
const originalAppPublicUrl = process.env.APP_PUBLIC_URL
|
|
const originalNextAuthUrl = process.env.NEXTAUTH_URL
|
|
const originalCorsAllowedOrigins = process.env.CORS_ALLOWED_ORIGINS
|
|
const originalNextPublicAppUrl = process.env.NEXT_PUBLIC_APP_PUBLIC_URL
|
|
|
|
function createRequest(headers: Record<string, string>, nextUrlOrigin = 'http://127.0.0.1:3000') {
|
|
return {
|
|
headers: {
|
|
get(name: string) {
|
|
return headers[name.toLowerCase()] ?? null
|
|
},
|
|
},
|
|
nextUrl: {
|
|
origin: nextUrlOrigin,
|
|
protocol: new URL(nextUrlOrigin).protocol,
|
|
},
|
|
url: `${nextUrlOrigin}/api/work-review/projects`,
|
|
}
|
|
}
|
|
|
|
describe('cors origin helpers', () => {
|
|
beforeEach(() => {
|
|
process.env.NEXT_PUBLIC_APP_PUBLIC_URL = 'https://sc.jiansun.vip'
|
|
process.env.APP_PUBLIC_URL = 'https://sc.jiansun.vip'
|
|
process.env.NEXTAUTH_URL = 'https://sc.jiansun.vip'
|
|
process.env.CORS_ALLOWED_ORIGINS = ''
|
|
})
|
|
|
|
afterAll(() => {
|
|
if (originalNextPublicAppUrl === undefined) delete process.env.NEXT_PUBLIC_APP_PUBLIC_URL
|
|
else process.env.NEXT_PUBLIC_APP_PUBLIC_URL = originalNextPublicAppUrl
|
|
|
|
if (originalAppPublicUrl === undefined) delete process.env.APP_PUBLIC_URL
|
|
else process.env.APP_PUBLIC_URL = originalAppPublicUrl
|
|
|
|
if (originalNextAuthUrl === undefined) delete process.env.NEXTAUTH_URL
|
|
else process.env.NEXTAUTH_URL = originalNextAuthUrl
|
|
|
|
if (originalCorsAllowedOrigins === undefined) delete process.env.CORS_ALLOWED_ORIGINS
|
|
else process.env.CORS_ALLOWED_ORIGINS = originalCorsAllowedOrigins
|
|
})
|
|
|
|
test('prefers forwarded public host origin when nextUrl resolves to an internal address', () => {
|
|
const request = createRequest({
|
|
host: 'sc.jiansun.vip',
|
|
'x-forwarded-proto': 'https',
|
|
origin: 'https://sc.jiansun.vip',
|
|
})
|
|
|
|
expect(getRequestHostOrigin(request)).toBe('https://sc.jiansun.vip')
|
|
expect(Array.from(getAllowedCorsOrigins(request))).toContain('https://sc.jiansun.vip')
|
|
expect(isAllowedCorsOrigin(request, 'https://sc.jiansun.vip')).toBe(true)
|
|
})
|
|
|
|
test('allows trusted intranet entry origins when host headers point to the intranet domain', () => {
|
|
const request = createRequest(
|
|
{
|
|
host: '192.168.58.20:4320',
|
|
'x-forwarded-proto': 'http',
|
|
origin: 'http://192.168.58.20:4320',
|
|
},
|
|
'https://sc.jiansun.vip',
|
|
)
|
|
|
|
expect(getRequestHostOrigin(request)).toBe('http://192.168.58.20:4320')
|
|
expect(isAllowedCorsOrigin(request, 'http://192.168.58.20:4320')).toBe(true)
|
|
})
|
|
|
|
test('keeps the public sc.jiansun.vip origin allowed when production only exposes an intranet NEXTAUTH_URL', () => {
|
|
delete process.env.NEXT_PUBLIC_APP_PUBLIC_URL
|
|
delete process.env.APP_PUBLIC_URL
|
|
process.env.NEXTAUTH_URL = 'http://192.168.58.20:4320'
|
|
process.env.CORS_ALLOWED_ORIGINS = ''
|
|
|
|
const request = createRequest(
|
|
{
|
|
host: '192.168.58.20:4320',
|
|
'x-forwarded-proto': 'http',
|
|
origin: 'https://sc.jiansun.vip',
|
|
},
|
|
'http://192.168.58.20:4320',
|
|
)
|
|
|
|
expect(Array.from(getAllowedCorsOrigins(request))).toContain('https://sc.jiansun.vip')
|
|
expect(isAllowedCorsOrigin(request, 'https://sc.jiansun.vip')).toBe(true)
|
|
})
|
|
|
|
test('rejects foreign origins that do not match the request host or configured whitelist', () => {
|
|
const request = createRequest({
|
|
host: 'sc.jiansun.vip',
|
|
'x-forwarded-proto': 'https',
|
|
origin: 'https://evil.example.com',
|
|
})
|
|
|
|
expect(isAllowedCorsOrigin(request, 'https://evil.example.com')).toBe(false)
|
|
})
|
|
|
|
test('does not re-open the retired legacy public domain unless it is explicitly configured', () => {
|
|
delete process.env.NEXT_PUBLIC_APP_PUBLIC_URL
|
|
delete process.env.APP_PUBLIC_URL
|
|
process.env.NEXTAUTH_URL = 'http://192.168.58.20:4320'
|
|
process.env.CORS_ALLOWED_ORIGINS = ''
|
|
|
|
const request = createRequest(
|
|
{
|
|
host: '192.168.58.20:4320',
|
|
'x-forwarded-proto': 'http',
|
|
origin: 'https://3d.yydd.ai',
|
|
},
|
|
'http://192.168.58.20:4320',
|
|
)
|
|
|
|
expect(isAllowedCorsOrigin(request, 'https://3d.yydd.ai')).toBe(false)
|
|
})
|
|
})
|