Skip to content

Commit 8748bd4

Browse files
committed
chore(security): add proxy rate-limit regression coverage
1 parent 8e9aeb9 commit 8748bd4

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { NextRequest } from 'next/server'
5+
import { beforeEach, describe, expect, it, vi } from 'vitest'
6+
7+
const { mockCheckRateLimitDirect } = vi.hoisted(() => ({
8+
mockCheckRateLimitDirect: vi.fn(),
9+
}))
10+
11+
vi.unmock('@/lib/core/utils/request')
12+
13+
vi.mock('@/lib/core/config/env', () => ({
14+
env: { AUTH_TRUSTED_PROXIES: '10.0.0.0/8' },
15+
}))
16+
17+
vi.mock('@/lib/core/rate-limiter/rate-limiter', () => ({
18+
RateLimiter: class {
19+
checkRateLimitDirect = mockCheckRateLimitDirect
20+
},
21+
}))
22+
23+
import { enforceIpRateLimit } from '@/lib/core/rate-limiter/route-helpers'
24+
25+
describe('route rate-limit client IP resolution', () => {
26+
beforeEach(() => {
27+
vi.clearAllMocks()
28+
mockCheckRateLimitDirect.mockResolvedValue({
29+
allowed: true,
30+
resetAt: new Date('2026-01-01T00:00:00.000Z'),
31+
})
32+
})
33+
34+
it('keys the bucket on the first untrusted hop from the right', async () => {
35+
const request = new NextRequest('http://localhost/api/test', {
36+
method: 'POST',
37+
headers: {
38+
'x-forwarded-for': '198.51.100.20, 203.0.113.30, 10.0.0.12',
39+
},
40+
})
41+
42+
const response = await enforceIpRateLimit('public-bucket', request)
43+
44+
expect(response).toBeNull()
45+
expect(mockCheckRateLimitDirect).toHaveBeenCalledWith(
46+
'route:public-bucket:ip:203.0.113.30',
47+
expect.objectContaining({ maxTokens: 10 })
48+
)
49+
})
50+
})

0 commit comments

Comments
 (0)