Skip to content

fix(nitro): gate chrome devtools workspace endpoint to local requests#35201

Merged
danielroe merged 2 commits into
mainfrom
fix/local-devtools
May 28, 2026
Merged

fix(nitro): gate chrome devtools workspace endpoint to local requests#35201
danielroe merged 2 commits into
mainfrom
fix/local-devtools

Conversation

@danielroe

Copy link
Copy Markdown
Member

🔗 Linked issue

📚 Description

out of an abundance of caution, this prevents us from serving the chrome devtools json integration to a non-allowed-host, just as we do for the vite dev server.

@github-actions github-actions Bot added 5.x 🐛 bug Something isn't working as expected labels May 27, 2026
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 547fda9f-3a98-427c-b338-ac98e2cff1e6

📥 Commits

Reviewing files that changed from the base of the PR and between 339dbb0 and df392b8.

📒 Files selected for processing (1)
  • packages/nitro-server/src/index.ts

Walkthrough

This change adds access control to the Chrome devtools JSON endpoint in the Nitro server. A type-only import of H3Event is added, then loopback host constants and two helpers: getDevHandlerAllowedHosts builds an allowlist from Vite server configuration, and isLocalDevRequest checks Host, sec-fetch-site, and origin/referer against that allowlist. The endpoint /.well-known/appspecific/com.chrome.devtools.json now invokes the validation and returns HTTP 403 with "Forbidden" for non-local requests.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: gating the Chrome DevTools endpoint to local requests for security.
Description check ✅ Passed The description is directly related to the changeset, explaining the rationale for restricting the DevTools endpoint to local requests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/local-devtools

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/nitro-server/src/index.ts`:
- Around line 1071-1078: The host extraction in isLocalDevRequest is incorrectly
splitting IPv6 Host headers by ':'; update the parsing to correctly handle IPv6
bracketed hosts and ports: if hostHeader starts with '[' extract the substring
between '[' and ']' as the host, otherwise strip the port by taking the
substring before the last ':' (or use URL/authority parsing). Then use that
parsed host when checking allowedHosts.has(host) so IPv6 addresses like
`[::1]:3000` are recognized by the allowedHosts check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9eaf028c-263f-4033-a34f-d4e64b2710e5

📥 Commits

Reviewing files that changed from the base of the PR and between 5668d45 and 339dbb0.

📒 Files selected for processing (1)
  • packages/nitro-server/src/index.ts

Comment on lines +1071 to +1078
function isLocalDevRequest (event: H3Event, allowedHosts: ReadonlySet<string> | true): boolean {
const hostHeader = event.req.headers.get('host')
if (allowedHosts !== true) {
const host = hostHeader?.split(':')[0]
if (!host || !allowedHosts.has(host)) {
return false
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

IPv6 host parsing is broken.

Using hostHeader?.split(':')[0] to strip the port does not work for IPv6 addresses. For a Host header like [::1]:3000, splitting on : yields ['[', '', '1]', '3000'], so the extracted host becomes [ instead of [::1]. This would incorrectly reject all IPv6 loopback requests.

🐛 Proposed fix for IPv6 handling
 function isLocalDevRequest (event: H3Event, allowedHosts: ReadonlySet<string> | true): boolean {
   const hostHeader = event.req.headers.get('host')
   if (allowedHosts !== true) {
-    const host = hostHeader?.split(':')[0]
+    let host: string | undefined
+    if (hostHeader?.startsWith('[')) {
+      // IPv6: [::1] or [::1]:port
+      const closingBracket = hostHeader.indexOf(']')
+      host = closingBracket !== -1 ? hostHeader.slice(0, closingBracket + 1) : undefined
+    } else {
+      // IPv4 or hostname: strip port if present
+      host = hostHeader?.split(':')[0]
+    }
     if (!host || !allowedHosts.has(host)) {
       return false
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function isLocalDevRequest (event: H3Event, allowedHosts: ReadonlySet<string> | true): boolean {
const hostHeader = event.req.headers.get('host')
if (allowedHosts !== true) {
const host = hostHeader?.split(':')[0]
if (!host || !allowedHosts.has(host)) {
return false
}
}
function isLocalDevRequest (event: H3Event, allowedHosts: ReadonlySet<string> | true): boolean {
const hostHeader = event.req.headers.get('host')
if (allowedHosts !== true) {
let host: string | undefined
if (hostHeader?.startsWith('[')) {
// IPv6: [::1] or [::1]:port
const closingBracket = hostHeader.indexOf(']')
host = closingBracket !== -1 ? hostHeader.slice(0, closingBracket + 1) : undefined
} else {
// IPv4 or hostname: strip port if present
host = hostHeader?.split(':')[0]
}
if (!host || !allowedHosts.has(host)) {
return false
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nitro-server/src/index.ts` around lines 1071 - 1078, The host
extraction in isLocalDevRequest is incorrectly splitting IPv6 Host headers by
':'; update the parsing to correctly handle IPv6 bracketed hosts and ports: if
hostHeader starts with '[' extract the substring between '[' and ']' as the
host, otherwise strip the port by taking the substring before the last ':' (or
use URL/authority parsing). Then use that parsed host when checking
allowedHosts.has(host) so IPv6 addresses like `[::1]:3000` are recognized by the
allowedHosts check.

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Actionable comments posted: 0

@danielroe danielroe closed this May 28, 2026
@danielroe danielroe reopened this May 28, 2026
@pkg-pr-new

pkg-pr-new Bot commented May 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@35201

@nuxt/nitro-server

npm i https://pkg.pr.new/@nuxt/nitro-server@35201

nuxt

npm i https://pkg.pr.new/nuxt@35201

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@35201

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@35201

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@35201

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@35201

commit: df392b8

@codspeed-hq

codspeed-hq Bot commented May 28, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 20 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing fix/local-devtools (df392b8) with main (456d896)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@danielroe danielroe merged commit 0289381 into main May 28, 2026
37 checks passed
@danielroe danielroe deleted the fix/local-devtools branch May 28, 2026 23:12
danielroe added a commit that referenced this pull request Jun 2, 2026
This was referenced Jun 1, 2026
danielroe added a commit that referenced this pull request Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5.x 🐛 bug Something isn't working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant