P1 - OAuth auth_url origin 验证: - 添加 validateOAuthUrl() 函数验证 OAuth URL origin - 仅允许同源或可信 OAuth 提供商 - LoginPage 和 ProfileSecurityPage 调用前验证 P2 - API 响应运行时类型验证: - 添加 isApiResponse() 运行时验证函数 - parseJsonResponse 验证响应结构完整性 P2 - 缓存击穿防护 (singleflight): - AuthMiddleware.isJTIBlacklisted 使用 singleflight.Group - 防止 L1 miss 时并发请求同时打 L2 P2 - Webhook 服务优雅关闭: - WebhookService 添加 Shutdown() 方法 - 服务器关闭时等待 worker 完成 - main.go 集成 shutdown 调用
67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
import { afterAll, describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
buildOAuthCallbackReturnTo,
|
|
parseOAuthCallbackHash,
|
|
sanitizeAuthRedirect,
|
|
validateOAuthUrl,
|
|
} from './oauth'
|
|
|
|
describe('oauth auth helpers', () => {
|
|
it('sanitizes redirect paths to internal routes only', () => {
|
|
expect(sanitizeAuthRedirect('/users')).toBe('/users')
|
|
expect(sanitizeAuthRedirect('https://evil.example.com')).toBe('/dashboard')
|
|
expect(sanitizeAuthRedirect('//evil.example.com')).toBe('/dashboard')
|
|
expect(sanitizeAuthRedirect('users')).toBe('/dashboard')
|
|
})
|
|
|
|
it('builds oauth callback return url on current origin', () => {
|
|
expect(buildOAuthCallbackReturnTo('/users')).toBe('http://localhost:3000/login/oauth/callback?redirect=%2Fusers')
|
|
})
|
|
|
|
it('parses oauth callback hash payload', () => {
|
|
expect(parseOAuthCallbackHash('#status=success&code=abc&provider=github')).toEqual({
|
|
status: 'success',
|
|
code: 'abc',
|
|
provider: 'github',
|
|
message: '',
|
|
})
|
|
})
|
|
|
|
describe('validateOAuthUrl', () => {
|
|
const originalOrigin = window.location.origin
|
|
|
|
afterAll(() => {
|
|
// 恢复原始 origin
|
|
Object.defineProperty(window, 'location', {
|
|
value: { origin: originalOrigin },
|
|
writable: true,
|
|
})
|
|
})
|
|
|
|
it('allows same-origin URLs', () => {
|
|
Object.defineProperty(window, 'location', {
|
|
value: { origin: 'http://localhost:3000' },
|
|
writable: true,
|
|
})
|
|
expect(validateOAuthUrl('http://localhost:3000/api/v1/auth/oauth/authorize')).toBe(true)
|
|
})
|
|
|
|
it('allows trusted OAuth provider origins', () => {
|
|
expect(validateOAuthUrl('https://github.com/login/oauth/authorize')).toBe(true)
|
|
expect(validateOAuthUrl('https://google.com/oauth/authorize')).toBe(true)
|
|
expect(validateOAuthUrl('https://facebook.com/v1.0/oauth/authorize')).toBe(true)
|
|
})
|
|
|
|
it('rejects untrusted origins', () => {
|
|
expect(validateOAuthUrl('https://evil.example.com/oauth/authorize')).toBe(false)
|
|
expect(validateOAuthUrl('https://attacker.com/callback')).toBe(false)
|
|
})
|
|
|
|
it('rejects invalid URLs', () => {
|
|
expect(validateOAuthUrl('not-a-url')).toBe(false)
|
|
expect(validateOAuthUrl('')).toBe(false)
|
|
})
|
|
})
|
|
})
|