From cbd945e3a68c216f1423e4279b2e16270dfa0051 Mon Sep 17 00:00:00 2001 From: Mantra <87142457+mantrakp04@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:59:18 -0700 Subject: [PATCH] [codex] Fix Neon malformed Basic auth validation (#1381) ## What changed This fixes Sentry issue [STACK-BACKEND-1A3](https://stackframe-pw.sentry.io/issues/7436639623/?project=4507442898272256&query=is%3Aunresolved&referrer=issue-stream&seerDrawer=true). A request with this malformed header: ```http Authorization: Basic ``` used to crash the Neon auth validator with a `StackAssertionError`, which turned a bad client request into a 500. The fix makes `neonAuthorizationHeaderSchema` only validate Neon client credentials after the Basic auth header successfully decodes. If decoding fails, the Neon-specific validator returns `true` and lets `basicAuthorizationHeaderSchema` produce the intended 400 schema error: `Authorization header must be in the format "Basic "`. ## Reviewer walkthrough There are two checks chained together: 1. `basicAuthorizationHeaderSchema` checks that the header is structurally valid Basic auth. 2. `neonAuthorizationHeaderSchema` checks that the decoded `client_id:client_secret` matches a configured Neon client. Yup may still run the second check after the first one has failed, because route validation collects errors with `abortEarly: false`. The old code assumed the first check had already passed and called `throwErr(...)` when decoding returned `null`. This PR changes that path to return `true`, because the format error is already owned by the first check. ## Tests - `pnpm -C packages/stack-shared exec vitest run --maxWorkers=1 --minWorkers=1 src/schema-fields.ts` - `pnpm -C apps/e2e exec vitest run --maxWorkers=1 --minWorkers=1 tests/backend/endpoints/api/v1/integrations/neon/projects/transfer.test.ts -t "malformed"` - `pnpm -C packages/stack-shared lint` - `pnpm -C packages/stack-shared typecheck` - `pnpm -C apps/e2e lint` - `pnpm -C apps/e2e typecheck` ## Summary by CodeRabbit * **Bug Fixes** * Enhanced authorization header validation in API endpoints with improved error handling, ensuring malformed credentials return clear, specific validation error messages. * **Tests** * Added comprehensive end-to-end test coverage for API request validation, including edge cases for authorization headers. --- .../neon/projects/transfer.test.ts | 34 ++++++++++++++++++- packages/stack-shared/src/schema-fields.ts | 9 +++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/transfer.test.ts b/apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/transfer.test.ts index ea78be2eb..27c60a1fa 100644 --- a/apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/transfer.test.ts +++ b/apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/transfer.test.ts @@ -215,6 +215,39 @@ it("should fail if the neon client details are missing", async ({ expect }) => { `); }); +it("should fail if the neon client authorization header is malformed", async ({ expect }) => { + // This project ID is arbitrary because malformed Basic auth is rejected before any project lookup runs. + const projectId = "73782539-cf39-486b-a9f8-9b2893f79ef2"; + const response = await niceBackendFetch(urlString`/api/v1/integrations/neon/projects/transfer?project_id=${projectId}`, { + method: "GET", + headers: { + "Authorization": "Basic", + }, + }); + expect(response).toMatchInlineSnapshot(` + NiceResponse { + "status": 400, + "body": { + "code": "SCHEMA_ERROR", + "details": { + "message": deindent\` + Request validation failed on GET /api/v1/integrations/neon/projects/transfer: + - Authorization header must be in the format "Basic " + \`, + }, + "error": deindent\` + Request validation failed on GET /api/v1/integrations/neon/projects/transfer: + - Authorization header must be in the format "Basic " + \`, + }, + "headers": Headers { + "x-stack-known-error": "SCHEMA_ERROR", +