File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -16,6 +16,7 @@ import { getSecondsUntilUTCMidnight, getSecondsUntilMidnightInTimezone } from '@
1616import type { BadgeParams } from '@/types' ;
1717import { themes } from '@/lib/svg/themes' ;
1818import { streakParamsSchema } from '@/lib/validations' ;
19+ import { sanitizeHexColor } from '@/lib/svg/sanitizer' ;
1920
2021const SVG_CSP_HEADER =
2122 "default-src 'none'; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src https://fonts.gstatic.com;" ;
@@ -255,15 +256,15 @@ function buildErrorResponse(error: unknown, parseResult: ParseResult): NextRespo
255256 message . toLowerCase ( ) . includes ( 'validation' ) ||
256257 message . toLowerCase ( ) . includes ( 'strictly for organizations' ) ;
257258
258- const errBg = `#${ ( parseResult . success && parseResult . data . bg ) || '0d1117' } ` ;
259- const errAccent = `# ${
259+ const errBg = `#${ sanitizeHexColor ( ( parseResult . success && parseResult . data . bg ) || '0d1117' ) } ` ;
260+ const errAccentRaw =
260261 ( parseResult . success &&
261262 ( Array . isArray ( parseResult . data . accent )
262263 ? parseResult . data . accent [ parseResult . data . accent . length - 1 ]
263264 : parseResult . data . accent ) ) ||
264- '58a6ff'
265- } `;
266- const errText = `#${ ( parseResult . success && parseResult . data . text ) || 'c9d1d9' } ` ;
265+ '58a6ff' ;
266+ const errAccent = `# ${ sanitizeHexColor ( errAccentRaw ) } `;
267+ const errText = `#${ sanitizeHexColor ( ( parseResult . success && parseResult . data . text ) || 'c9d1d9' ) } ` ;
267268 const errRadius = parseResult . success
268269 ? ( ( ) => {
269270 const r = Number ( parseResult . data . radius ) ;
Original file line number Diff line number Diff line change @@ -4,8 +4,12 @@ import { User } from '@/models/User';
44import { trackUserRateLimiter } from '@/lib/rate-limit' ;
55
66export async function POST ( req : Request ) {
7- // Get IP for rate limiting
8- const ip = req . headers . get ( 'x-forwarded-for' ) || req . headers . get ( 'x-real-ip' ) || 'unknown' ;
7+ // Get IP for rate limiting.
8+ // x-real-ip is provided by Vercel/Nginx as the true client IP.
9+ // We fall back to the LAST IP in the x-forwarded-for chain, which is appended by the Vercel proxy.
10+ const forwardedFor = req . headers . get ( 'x-forwarded-for' ) ;
11+ const fallbackIp = forwardedFor ? forwardedFor . split ( ',' ) . pop ( ) ?. trim ( ) : 'unknown' ;
12+ const ip = req . headers . get ( 'x-real-ip' ) || fallbackIp || 'unknown' ;
913
1014 if ( ip !== 'unknown' && ! ( await trackUserRateLimiter . check ( ip ) ) ) {
1115 return NextResponse . json (
You can’t perform that action at this time.
0 commit comments