feat(proxy): support path aliases to hide third-party hostnames#821
Conversation
Proxy paths embed the verbatim third-party hostname (e.g.
/_scripts/p/us.i.posthog.com/e/), which leaks self-hosted/internal
domains and is trivially classified by ad-blockers. Add
`scripts.proxy.alias` to replace the hostname segment with an opaque
or custom alias.
- `alias: true` auto-generates a short deterministic hash per domain
- `alias: { domain: 'x' }` maps specific domains to custom aliases
The alias is threaded through every point that emits a proxy path:
build-time rewrites, auto-injected endpoints, the runtime intercept
plugin, and Partytown resolveUrl. The server handler resolves the
alias back to the real domain before forwarding; verbatim hostnames
still resolve, so the change is non-breaking.
Resolves #814
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
✅ Files skipped from review due to trivial changes (3)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughA new Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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.
Actionable comments posted: 4
🤖 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/script/src/module.ts`:
- Around line 950-956: The alias validation in module.ts still allows ambiguous
mappings to flow through after invertAliasMap(), so fix the proxy alias handling
in the buildDomainAliasMap/invertAliasMap path to reject any duplicate alias
collisions instead of only warning. Update the logic around aliasToDomain to
explicitly detect and stop on aliases that resolve to the same value, and also
treat aliases that match a literal proxied domain as invalid so the
verbatim-hostname fallback cannot select the wrong domain. Use the existing
logger and the domainAliases/aliasToDomain symbols to locate the check, and make
sure the function does not continue with an ambiguous alias map.
- Around line 325-337: `ModuleOptions.proxy` currently only models the alias
config, but the public API still advertises `proxy: false` as a supported
opt-out. Update the `proxy` type in `module.ts` so `false` remains part of the
union if it is still valid, and make sure the `ProxyAliasConfig`-related
docs/warning text stay consistent with the actual accepted shape; if `false` is
no longer supported, then remove it from the docs/messages instead. Use
`ModuleOptions` and `ProxyAliasConfig` as the anchors when updating the option
shape.
In `@packages/script/src/proxy-alias.ts`:
- Line 29: The proxy alias lookup in proxy-alias.ts returns custom aliases
without validating that they are safe path segments. Update the alias resolution
logic in the alias lookup function to reject or normalize explicit aliases
containing slashes, query chars, or other URL-breaking characters before they
are used by the script path builder. Keep the fix scoped to the alias resolution
flow so the value later interpolated into /_scripts/p/<alias>/... is always a
single safe segment.
In `@packages/script/src/runtime/server/proxy-handler.ts`:
- Around line 91-93: The alias resolution in proxy-handler is allowing a custom
alias to override a real proxied hostname, which makes verbatim hostname paths
ambiguous. Update the setup/validation around the alias map in proxy-handler so
any alias that matches an allowed real domain is rejected, or change the
resolution logic in the path handling code so the literal segment wins over
aliasToDomain when both exist. Use the existing proxy path handling symbols like
aliasToDomain and the domain resolution in the proxy-handler flow to locate the
fix.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1ed07866-090f-4be7-819f-1c99172cb33b
📒 Files selected for processing (13)
FIRST_PARTY.mddocs/content/docs/1.guides/2.first-party.mdpackages/script/src/devtools.tspackages/script/src/module.tspackages/script/src/plugins/intercept.tspackages/script/src/plugins/transform.tspackages/script/src/proxy-alias.tspackages/script/src/registry.tspackages/script/src/runtime/composables/useScript.tspackages/script/src/runtime/server/proxy-handler.tstest/unit/proxy-alias-generators.test.tstest/unit/proxy-alias.test.tstest/unit/proxy-handler-alias.test.ts
| proxy?: { | ||
| /** | ||
| * Replace third-party hostnames in proxy paths with aliases, so the real domain | ||
| * (e.g. a self-hosted analytics host) never appears in client-facing URLs. | ||
| * | ||
| * - `false` (default): paths embed the verbatim hostname (`/_scripts/p/us.i.posthog.com/...`) | ||
| * - `true`: auto-generate a short opaque alias per domain (`/_scripts/p/a1b2c3d4/...`) | ||
| * - `Record<domain, alias>`: explicit aliases (domain → alias); unlisted domains stay verbatim | ||
| * | ||
| * @default false | ||
| * @example { 'us.i.posthog.com': 'ph', 'analytics.internal.example.com': 'a' } | ||
| */ | ||
| alias?: ProxyAliasConfig |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether top-level `proxy: false` is documented or tested as public config.
rg -n -C3 '\bproxy\s*:\s*false|proxy\?:|disable with `?proxy: false`?|isProxyDisabled' .Repository: nuxt/scripts
Length of output: 22788
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the module option definition and surrounding setup logic.
ast-grep outline packages/script/src/module.ts --view expanded
printf '\n--- module.ts relevant slices ---\n'
sed -n '300,380p' packages/script/src/module.ts
printf '\n--- module.ts proxy usage slice ---\n'
sed -n '960,995p' packages/script/src/module.ts
printf '\n--- public docs/types references ---\n'
sed -n '220,245p' FIRST_PARTY.md
sed -n '70,110p' docs/content/docs/1.guides/2.first-party.md
sed -n '15,25p' docs/content/docs/3.api/5.nuxt-config.mdRepository: nuxt/scripts
Length of output: 9852
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect how top-level config.proxy is consumed and whether boolean values are handled.
rg -n -C3 '\bconfig\.proxy\b|\bproxy\s*:\s*(true|false)\b|alias\s*\?' packages/script/src/module.ts packages/script/src -g '!**/dist/**'
printf '\n--- setup area around config.proxy ---\n'
sed -n '540,760p' packages/script/src/module.tsRepository: nuxt/scripts
Length of output: 24437
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find every use of the top-level module proxy option and the surrounding type exports.
rg -n -C2 '\bconfig\.proxy\b|\bModuleOptions\b|\bproxy\?:\s*false\s*\||\bproxy\?:\s*\{' packages/script/src docs/content FIRST_PARTY.md
printf '\n--- module.ts around proxy-related setup ---\n'
sed -n '860,910p' packages/script/src/module.ts
printf '\n--- type declarations around module options ---\n'
sed -n '301,345p' packages/script/src/module.tsRepository: nuxt/scripts
Length of output: 8486
Keep proxy: false on the module option shape — ModuleOptions.proxy is alias-only here, but the public docs and warning text still expose proxy: false as the opt-out. If that option is still supported, keep it in the union; otherwise update the docs/message to the new shape.
🤖 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/script/src/module.ts` around lines 325 - 337, `ModuleOptions.proxy`
currently only models the alias config, but the public API still advertises
`proxy: false` as a supported opt-out. Update the `proxy` type in `module.ts` so
`false` remains part of the union if it is still valid, and make sure the
`ProxyAliasConfig`-related docs/warning text stay consistent with the actual
accepted shape; if `false` is no longer supported, then remove it from the
docs/messages instead. Use `ModuleOptions` and `ProxyAliasConfig` as the anchors
when updating the option shape.
| if (alias === true) | ||
| // 8 hex chars (32 bits) — collision-free for the handful of domains a site proxies. | ||
| return createHash('sha256').update(domain).digest('hex').slice(0, 8) | ||
| return alias[domain] || undefined |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Validate explicit aliases before using them as path segments.
A custom alias like foo/bar or foo?x=1 is returned unchanged and later interpolated into /_scripts/p/<alias>/..., which changes the URL shape and breaks server alias resolution.
Suggested fix
const WILDCARD_RE = /\*/
+const SAFE_ALIAS_RE = /^[A-Za-z0-9._~-]+$/
@@
if (alias === true)
// 8 hex chars (32 bits) — collision-free for the handful of domains a site proxies.
return createHash('sha256').update(domain).digest('hex').slice(0, 8)
- return alias[domain] || undefined
+ const explicit = alias[domain]
+ if (!explicit)
+ return undefined
+ if (!SAFE_ALIAS_RE.test(explicit))
+ throw new Error(`[nuxt-scripts] Invalid proxy alias "${explicit}" for "${domain}". Use a single URL path segment.`)
+ return explicit
}📝 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.
| return alias[domain] || undefined | |
| const SAFE_ALIAS_RE = /^[A-Za-z0-9._~-]+$/ | |
| const explicit = alias[domain] | |
| if (!explicit) | |
| return undefined | |
| if (!SAFE_ALIAS_RE.test(explicit)) | |
| throw new Error(`[nuxt-scripts] Invalid proxy alias "${explicit}" for "${domain}". Use a single URL path segment.`) | |
| return explicit |
🤖 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/script/src/proxy-alias.ts` at line 29, The proxy alias lookup in
proxy-alias.ts returns custom aliases without validating that they are safe path
segments. Update the alias resolution logic in the alias lookup function to
reject or normalize explicit aliases containing slashes, query chars, or other
URL-breaking characters before they are used by the script path builder. Keep
the fix scoped to the alias resolution flow so the value later interpolated into
/_scripts/p/<alias>/... is always a single safe segment.
| // Resolve path alias back to the real third-party domain. Falls back to the | ||
| // segment itself so verbatim-hostname paths keep working when aliasing is off. | ||
| const domain = aliasToDomain?.[segment] ?? segment |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Reserve alias names from real hostnames.
Line 93 lets aliasToDomain win over the literal segment, so a custom alias that matches another proxied hostname becomes ambiguous. For example, if foo.vendor.com -> bar.vendor.com is configured while bar.vendor.com is also allowed, /_scripts/p/bar.vendor.com/... will be forwarded to foo.vendor.com, which breaks the “verbatim hostname paths still work” guarantee. Please reject aliases that match any real proxied domain during setup, or otherwise make exact hostnames win here.
🤖 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/script/src/runtime/server/proxy-handler.ts` around lines 91 - 93,
The alias resolution in proxy-handler is allowing a custom alias to override a
real proxied hostname, which makes verbatim hostname paths ambiguous. Update the
setup/validation around the alias map in proxy-handler so any alias that matches
an allowed real domain is rejected, or change the resolution logic in the path
handling code so the literal segment wins over aliasToDomain when both exist.
Use the existing proxy path handling symbols like aliasToDomain and the domain
resolution in the proxy-handler flow to locate the fix.
- proxy-handler: guard alias lookup with Object.hasOwn so a crafted segment (e.g. /_scripts/p/toString/) can't resolve to a prototype member and 500 instead of 403 - proxy-alias: widen auto-alias to 48 bits and guard explicit-map lookups against inherited members - module: fail the build on an alias collision instead of warning, since two domains sharing an alias mis-route at runtime
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)
docs/content/docs/1.guides/2.first-party.md (1)
159-159: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse a 12-character alias in the example The auto-generated alias is 12 hex chars, so
/_scripts/p/a1b2c3d4/e/should be updated to a 12-char value like/_scripts/p/a1b2c3d4e5f6/e/.🤖 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 `@docs/content/docs/1.guides/2.first-party.md` at line 159, Update the example alias value to match the 12-character auto-generated format described in the comment. In the docs example around the alias setting, replace the current shorter path fragment with a 12-hex-character alias so the example matches the behavior of the alias generation logic and the surrounding explanation.
♻️ Duplicate comments (1)
packages/script/src/proxy-alias.ts (1)
30-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winValidate custom aliases before returning them.
Line 32 still accepts any user-provided alias verbatim. Values containing
/,?, or#will change the/_scripts/p/<alias>/...path shape and break alias resolution downstream.Proposed fix
+const SAFE_ALIAS_RE = /^[A-Za-z0-9._~-]+$/ + export function aliasForDomain(domain: string, alias: ProxyAliasConfig): string | undefined { if (!alias || WILDCARD_RE.test(domain)) return undefined if (alias === true) // 12 hex chars (48 bits) — collision space large enough that auto-aliasing never // silently misroutes; explicit-alias collisions are caught at build time instead. return createHash('sha256').update(domain).digest('hex').slice(0, 12) // Own-property guard so a domain literally named `toString`/`constructor` can't // resolve to an inherited prototype member. - return Object.hasOwn(alias, domain) ? (alias[domain] || undefined) : undefined + if (!Object.hasOwn(alias, domain)) + return undefined + const explicit = alias[domain] + if (!explicit) + return undefined + if (!SAFE_ALIAS_RE.test(explicit)) + throw new Error(`[nuxt-scripts] Invalid proxy alias "${explicit}" for "${domain}". Use a single URL path segment.`) + return explicit }🤖 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/script/src/proxy-alias.ts` around lines 30 - 32, Validate the user-provided alias in proxy-alias.ts before returning it from the alias lookup, since Object.hasOwn(alias, domain) currently accepts any string verbatim and values with /, ?, or # can break the /_scripts/p/<alias>/... path. Update the logic around the domain-to-alias resolution to reject or normalize invalid aliases, and keep the existing own-property guard so only safe aliases are returned from the alias map.
🤖 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.
Outside diff comments:
In `@docs/content/docs/1.guides/2.first-party.md`:
- Line 159: Update the example alias value to match the 12-character
auto-generated format described in the comment. In the docs example around the
alias setting, replace the current shorter path fragment with a 12-hex-character
alias so the example matches the behavior of the alias generation logic and the
surrounding explanation.
---
Duplicate comments:
In `@packages/script/src/proxy-alias.ts`:
- Around line 30-32: Validate the user-provided alias in proxy-alias.ts before
returning it from the alias lookup, since Object.hasOwn(alias, domain) currently
accepts any string verbatim and values with /, ?, or # can break the
/_scripts/p/<alias>/... path. Update the logic around the domain-to-alias
resolution to reject or normalize invalid aliases, and keep the existing
own-property guard so only safe aliases are returned from the alias map.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 042407fe-b444-4a28-9954-bf86d58f1795
📒 Files selected for processing (6)
docs/content/docs/1.guides/2.first-party.mdpackages/script/src/module.tspackages/script/src/proxy-alias.tspackages/script/src/runtime/server/proxy-handler.tstest/unit/proxy-alias.test.tstest/unit/proxy-handler-alias.test.ts
✅ Files skipped from review due to trivial changes (1)
- test/unit/proxy-alias.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- test/unit/proxy-handler-alias.test.ts
- packages/script/src/runtime/server/proxy-handler.ts
- packages/script/src/module.ts
Address review feedback on alias safety: - reject aliases that are not single URL-safe path segments (a slash or query char would split the proxy path) - reject an alias that equals a real proxied domain, which would make the verbatim-hostname fallback ambiguous - keep failing on duplicate aliases Adds isSafeAliasSegment helper with unit coverage.
|
Thanks @coderabbitai — addressed in 034db37:
|
|
Tip For best results, initiate chat on the files or code changes.
The hard failure for alias collisions, the rejection of aliases that overlap real proxied domains, and the Nicely tightened up. 🐇 ✏️ Learnings added
If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
Builds a minimal app with `scripts.proxy.alias` and asserts the full module wiring: the auto-injected Plausible endpoint is rewritten to the aliased path, the proxy handler resolves a known alias back to the real domain, and an unknown alias segment is rejected with 403.
🔗 Linked issue
Resolves #814
❓ Type of change
📚 Description
First-party proxy paths embed the real third-party hostname verbatim, e.g.
/_scripts/p/us.i.posthog.com/e/. For self-hosted services this leaks an internal domain (/_scripts/p/analytics.internal.example.com/api/send) into client-facing URLs, and the raw hostname is trivially classified by ad-blockers and network observers.This adds
scripts.proxy.aliasto replace the hostname segment with an alias. Off by default, so existing paths are unchanged.The alias map is built from every proxied domain and threaded through every point that emits a proxy path: build-time URL rewrites (
transform.ts), auto-injected endpoints like PostHog'sapiHost(applyAutoInject), the runtime intercept plugin (intercept.ts, covers dynamically-constructed URLs such as self-hosted collection endpoints), and Partytown'sresolveUrl. The server handler resolves the alias back to the real domain via an inverted map before allowlist matching and forwarding. Verbatim-hostname paths still resolve, so enabling aliasing is non-breaking. Wildcard allowlist domains are never aliased since they have no literal path form.Core logic is isolated in a pure
proxy-alias.tsmodule. Devtools network attribution resolves aliases back to domains so proxied requests still map to their script.Tests: pure alias logic, generated intercept + Partytown code (executed), and end-to-end handler resolution (alias → domain, verbatim fallback, unknown-alias 403).