fix: return full origin with port in CORS headers for localhost#30
fix: return full origin with port in CORS headers for localhost#30
Conversation
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
globalping-mcp-server | c122fff | Mar 14 2026, 01:35 PM |
WalkthroughgetMatchingOrigin now preserves and returns the request origin including its port when the request hostname is localhost, 127.0.0.1, or [::1] and the base origin is whitelisted. isLoopbackIPv6 detection was refactored to construct a URL from the cleaned IPv6 and read its origin inside a try/catch (returns false on error). Other edits are formatting-only (target validation and input schema descriptions). Unit tests for CORS now expect port-inclusive origins. Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/target-validation.ts (1)
110-118:⚠️ Potential issue | 🟠 MajorCanonicalize IPv6 before checking for loopback.
A valid loopback like
0:0:0:0:0:0::1still misses this string list, soisPublicTarget()can accept a localhost target as public. That weakens the guard all of the measurement tools rely on.Suggested fix
export function isLoopbackIPv6(ip: string): boolean { - const cleanIp = ip.replace(/^\[|\]$/g, "").toLowerCase(); - - // ::1 is the only IPv6 loopback - return ( - cleanIp === "::1" || - cleanIp === "0:0:0:0:0:0:0:1" || - cleanIp === "0000:0000:0000:0000:0000:0000:0000:0001" - ); + const cleanIp = ip.replace(/^\[|\]$/g, ""); + + try { + return new URL(`http://[${cleanIp}]`).origin === "http://[::1]"; + } catch { + return false; + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/target-validation.ts` around lines 110 - 118, isLoopbackIPv6 currently compares raw strings and misses equivalent loopback forms like 0:0:0:0:0:0::1; update isLoopbackIPv6 to canonicalize/parse the input IPv6 before checking (e.g., use a proper IPv6 parser such as ipaddr.js to parse the ip string, normalize/expand it or use its isLoopback()/range checks) and then compare against the canonical loopback (::1) or use the parser's loopback test; ensure you still strip surrounding brackets as done today and replace the current raw-string comparisons in isLoopbackIPv6 with the parser-based check to catch all zero-compressed or expanded equivalents.
🧹 Nitpick comments (1)
src/lib/security.ts (1)
228-236: Update the inline comment to match the new behavior.Line 228 still says this branch returns the base origin, but Line 236 now preserves the localhost port. The comment now describes the old bug, not the current logic.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/security.ts` around lines 228 - 236, The inline comment above the localhost handling is outdated: update the comment that references "return the base origin" to accurately state that for localhost/127.0.0.1/[::1] the function will return the base origin including the port when present (i.e., it preserves the localhost port), and mention it checks CORS_CONFIG.ALLOWED_ORIGINS against the base origin before appending the port; modify the comment near requestOrigin/originUrl/hostname/baseOrigin to reflect this new behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/lib/target-validation.ts`:
- Around line 110-118: isLoopbackIPv6 currently compares raw strings and misses
equivalent loopback forms like 0:0:0:0:0:0::1; update isLoopbackIPv6 to
canonicalize/parse the input IPv6 before checking (e.g., use a proper IPv6
parser such as ipaddr.js to parse the ip string, normalize/expand it or use its
isLoopback()/range checks) and then compare against the canonical loopback (::1)
or use the parser's loopback test; ensure you still strip surrounding brackets
as done today and replace the current raw-string comparisons in isLoopbackIPv6
with the parser-based check to catch all zero-compressed or expanded
equivalents.
---
Nitpick comments:
In `@src/lib/security.ts`:
- Around line 228-236: The inline comment above the localhost handling is
outdated: update the comment that references "return the base origin" to
accurately state that for localhost/127.0.0.1/[::1] the function will return the
base origin including the port when present (i.e., it preserves the localhost
port), and mention it checks CORS_CONFIG.ALLOWED_ORIGINS against the base origin
before appending the port; modify the comment near
requestOrigin/originUrl/hostname/baseOrigin to reflect this new behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e8e3fc2d-0de3-4503-9fbe-8beae7caa715
📒 Files selected for processing (4)
src/lib/security.tssrc/lib/target-validation.tssrc/mcp/tools.tstest/unit/lib/security.test.ts
|
|
@jimaek Could you explain in more detail? I didn't understand where this is coming from |
|
Failed Cloudflare build |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 35: package.json is missing the direct dependency "ai" required by
"agents": "0.2.21"; add "ai": "5.0.78" to the dependencies object in
package.json alongside "agents": "0.2.21" and update the lockfile
(npm/yarn/pnpm) or run install so the package is resolved and Cloudflare
bundling errors are prevented.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| "@cloudflare/workers-oauth-provider": "^0.1.0", | ||
| "@modelcontextprotocol/sdk": "^1.20.2", | ||
| "agents": "^0.2.21", | ||
| "agents": "0.2.21", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Confirm pinned agents version"
rg -nP '"agents"\s*:\s*"0\.2\.21"' package.json
echo
echo "2) Inspect [email protected] metadata from npm"
curl -s https://registry.npmjs.org/agents/0.2.21 | jq '{name,version,dependencies,peerDependencies,optionalDependencies}'
echo
echo "3) Check repo config for bundler handling of ai (external/alias/noExternal)"
fd -HI 'wrangler.*|vite.*|vitest.*|esbuild.*|rollup.*|webpack.*|package.json' . \
| xargs -I{} sh -c 'echo "---- {} ----"; rg -nP "external|alias|noExternal|\\bai\\b" "{}" || true'
echo
echo "4) Check for local patches related to agents/ai"
fd -HI 'patches|*.patch' . \
| xargs -I{} sh -c 'echo "---- {} ----"; rg -nP "agents|import\\(\"ai\"\\)|\\bai\\b" "{}" || true'Repository: jsdelivr/globalping-mcp-server
Length of output: 1315
🏁 Script executed:
cat package.json | jq '.dependencies | keys | .[]' | grep -i "^ai$"Repository: jsdelivr/globalping-mcp-server
Length of output: 56
🏁 Script executed:
cat package.json | jq '.dependencies'Repository: jsdelivr/globalping-mcp-server
Length of output: 287
Add ai to dependencies—[email protected] requires it but it's missing from package.json.
[email protected] declares [email protected] as a direct dependency. Pinning agents alone will not resolve the Cloudflare bundling error; the missing ai package must be added to the dependencies object.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 35, package.json is missing the direct dependency "ai"
required by "agents": "0.2.21"; add "ai": "5.0.78" to the dependencies object in
package.json alongside "agents": "0.2.21" and update the lockfile
(npm/yarn/pnpm) or run install so the package is resolved and Cloudflare
bundling errors are prevented.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/index.ts (1)
499-504: Remove the//@ts-ignore`` by typing the request context.The
Propstype is already imported. Create a small context alias to eliminate the type suppression:Refactor
+type AgentRequestContext = ExecutionContext & { props?: Props }; + async function handleAPITokenRequest( agent: typeof GlobalpingMCP, req: Request, env: GlobalpingEnv, - ctx: ExecutionContext, + ctx: AgentRequestContext, ) { // ... ctx.props = {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/index.ts` around lines 499 - 504, The // `@ts-ignore` should be removed by explicitly typing the request context: create a small alias (e.g. type RequestContext = Props['ctx'] or another appropriate property from the imported Props) and use it in the handleAPITokenRequest signature so it reads async function handleAPITokenRequest(agent: typeof GlobalpingMCP, req: Request, env: GlobalpingEnv, ctx: RequestContext). Replace the ts-ignore with this typed alias and ensure Props is referenced so the compiler recognizes the type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/index.ts`:
- Around line 499-504: The // `@ts-ignore` should be removed by explicitly typing
the request context: create a small alias (e.g. type RequestContext =
Props['ctx'] or another appropriate property from the imported Props) and use it
in the handleAPITokenRequest signature so it reads async function
handleAPITokenRequest(agent: typeof GlobalpingMCP, req: Request, env:
GlobalpingEnv, ctx: RequestContext). Replace the ts-ignore with this typed alias
and ensure Props is referenced so the compiler recognizes the type.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b0a4503d-d518-409c-8f5f-03470cbdbe01
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
package.jsonsrc/index.tssrc/types/globalping.tsworker-configuration.d.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- package.json
Summary
Root cause
Browser-based MCP clients (e.g. llama.cpp webui on http://127.0.0.1:8080) send Origin: http://127.0.0.1:8080. The server validated this correctly but then returned Access-Control-Allow-Origin: http://127.0.0.1 — per
CORS spec these are different origins.
Fix
Reconstruct the CORS origin from validated components (baseOrigin + parsed port) instead of returning only the base origin. This is safe because both the hostname and port are extracted via new URL() parsing.