diff --git a/.jules/flash.md b/.jules/flash.md new file mode 100644 index 0000000..6f6987d --- /dev/null +++ b/.jules/flash.md @@ -0,0 +1,6 @@ + +## 2024-05-18 - [Fix IP Extraction from x-forwarded-for] +**Category:** Security | Bug +**Finding:** The `x-forwarded-for` header can contain multiple comma-separated IP addresses when a request goes through proxies. Previously, the raw string or untrimmed string was being directly assigned to `ip_address` in database logs, which has a length limit of 45 (`varchar(45)`). +**Learning:** For Next.js/Supabase architectures, processing headers containing proxy chains must always handle string extraction properly. Passing a raw comma-separated list of IPs can crash database inserts, and ignoring whitespace can cause malformed data. +**Action:** Always extract the actual client IP by splitting the `x-forwarded-for` header by commas and explicitly trimming whitespace: `ip.split(',')[0].trim()`. diff --git a/src/app/api/v1/payment-links/route.ts b/src/app/api/v1/payment-links/route.ts index 679f324..288c743 100644 --- a/src/app/api/v1/payment-links/route.ts +++ b/src/app/api/v1/payment-links/route.ts @@ -147,7 +147,7 @@ export async function POST(req: NextRequest) { method: 'POST', status_code: 201, request_body: body, - ip_address: clientIp.split(',')[0], + ip_address: clientIp.split(',')[0].trim(), // eslint-disable-next-line @typescript-eslint/no-explicit-any }).then(({ error }: any) => { if (error) console.error('Failed to log API call', error) diff --git a/src/lib/api/verify-api-key.ts b/src/lib/api/verify-api-key.ts index 9b858a8..fc504ef 100644 --- a/src/lib/api/verify-api-key.ts +++ b/src/lib/api/verify-api-key.ts @@ -57,7 +57,7 @@ export async function verifyApiKey(req: NextRequest) { endpoint: req.nextUrl.pathname, method: req.method, status_code: 200, // Assumed success if we get here - ip_address: req.headers.get('x-forwarded-for') || 'unknown', + ip_address: (req.headers.get('x-forwarded-for') || 'unknown').split(',')[0].trim(), user_agent: req.headers.get('user-agent') || 'unknown' // eslint-disable-next-line @typescript-eslint/no-explicit-any }).then(({ error }: any) => {