fix: redact and strip exaApiKey from request URLs to prevent log exposure (CWE-200) - #239
Closed
andesyteoss wants to merge 1 commit into
Closed
Conversation
…sure (CWE-200) - Add redactUrl(): replaces exaApiKey values with "REDACTED" for debug logs - Add stripApiKeyFromUrl(): removes exaApiKey param entirely from the URL after extraction, so downstream libraries and platform request logs never see the secret - Use redactUrl() in debug console.log instead of raw request.url - Strip exaApiKey from request URL before passing to mcp-handler
|
@sebastiondev is attempting to deploy a commit to the Exa Team on Vercel. A member of the Team first needs to authorize it. |
|
Closing this to reduce the open-PR pile-up — we have multiple outstanding security contributions to this repo and that volume is not fair on your review queue. Keeping #246 (fix: use dedicated header with timing-safe comparison for rate-limit bypass toke) as the primary one to focus attention on. Happy to revisit this finding separately later if it is still relevant. Apologies for the noise. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Vulnerability Summary
CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
Severity: Medium
Affected file:
api/mcp.tsData Flow
When a user authenticates via the documented
?exaApiKey=query parameter pattern (e.g.https://mcp.exa.ai/mcp?exaApiKey=exa-abc123xyz), the API key flows through several exposure paths:Debug logging (line 330, pre-fix):
console.log(\[EXA-MCP] Request URL: ${request.url}`)— directly logs the full URL including the API key whendebug=true`.Downstream handler propagation: The
requestobject (with full URL includingexaApiKey) is passed tohandler(request)frommcp-handler. Inside that library, the URL is:createFakeIncomingMessage({url: req.url})(mcp-handler line 330)url: req.url(mcp-handler line 592)logger.log("Published requests:...", serializedRequest)(mcp-handler line 640)Vercel platform logs: Vercel automatically captures request URLs in Function Logs in the dashboard. Anyone with project access sees full API keys.
HTTP Referer headers: If any downstream request is made from the handler context, the full URL may be sent as the
Refererheader to third parties.Related Issues
Fix Description
This PR adds two utility functions and applies them in
handleRequest():1.
redactUrl(urlString)— for debug logsReplaces the
exaApiKeyparameter value withREDACTEDso the debug log line becomes safe:2.
stripApiKeyFromUrl(urlString)— for downstream propagationCompletely removes the
exaApiKeyparameter from the URL. Applied after the key has already been extracted intoconfig, so it does not affect authentication:Rationale
configviagetConfigFromRequest()before the strip runs, so no behavioral change occurs.try/catchwith regex fallback in case URL parsing fails.Test Results
21/21 tests passing ✅
The test file
test-cwe200-api-key-redaction.mjscovers:stripApiKeyFromUrlbasic functionalitystripApiKeyFromUrledge cases (empty value, duplicates, long keys)redactUrlbasic functionalityredactUrledge caseshandleRequestflow simulation)Disprove Analysis
We attempted to invalidate this finding through multiple angles:
Authentication check
The API key is passed via
Authorization: Bearerheader or?exaApiKey=query parameter. There is no separate auth guarding the endpoint — the API key IS the authentication. The fix protects the key from being leaked in logs.Network check
No
localhost-only restriction. This is deployed publicly atmcp.exa.aion Vercel. Themcp-handlerlibrary setsAccess-Control-Allow-Origin: "*". This is internet-facing.Mitigations found
debugdefaults tofalseunless?debug=trueorDEBUG=trueenv var. This limitsconsole.logexposure to opt-in scenarios.Authorization: Bearerheader instead of query params are not affected.Preconditions for exploitation
debug=true(opt-in).Prior art
Verdict: CONFIRMED_VALID (high confidence)
The vulnerability is real. The fix is minimal, correct, and does not change application behavior since the key is extracted into
configbefore being stripped from the URL.Change Summary
Thank you for your consideration. Happy to adjust anything based on feedback.