Skip to content

Commit cb7ff7e

Browse files
committed
fix: protect against broken new URL when proxying
Fixes #608
1 parent b5fea8f commit cb7ff7e

File tree

4 files changed

+21
-14
lines changed

4 files changed

+21
-14
lines changed

src/module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ export default defineNuxtPlugin({
484484
? parsed.pathname.slice(rule.pathPrefix.length)
485485
: parsed.pathname;
486486
const separator = pathWithoutPrefix.startsWith('/') ? '' : '/';
487-
const proxyUrl = rule.target + separator + pathWithoutPrefix + parsed.search;
487+
const proxyUrl = window.location.origin + rule.target + separator + pathWithoutPrefix + parsed.search;
488488
489489
return originalBeacon(proxyUrl, data);
490490
}

src/runtime/registry/posthog.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,12 @@ export function useScriptPostHog<T extends PostHogApi>(_options?: PostHogInput)
7575
}
7676

7777
const region = options?.region || 'us'
78-
const apiHost = options?.apiHost || (region === 'eu'
78+
let apiHost = options?.apiHost || (region === 'eu'
7979
? 'https://eu.i.posthog.com'
8080
: 'https://us.i.posthog.com')
81+
// Resolve relative proxy paths to absolute URLs so SDKs using new URL() don't throw
82+
if (apiHost.startsWith('/'))
83+
apiHost = window.location.origin + apiHost
8184

8285
window.__posthogInitPromise = import('posthog-js')
8386
.then(({ default: posthog }) => {

src/runtime/utils/pure.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ export function rewriteScriptUrls(content: string, rewrites: ProxyRewrite[]): st
9393
const rewritten = rewriteSuffix === '/' || rewriteSuffix.startsWith('?') || rewriteSuffix.startsWith('#')
9494
? to + rewriteSuffix
9595
: joinURL(to, rewriteSuffix)
96-
return quote + rewritten + quote
96+
// Produce absolute URL by prepending self.location.origin as an expression.
97+
// This breaks out of the string literal: "https://example.com/path" → self.location.origin+"/_proxy/path"
98+
// Necessary because some SDKs (e.g. TikTok) pass URLs to new URL() without a base argument,
99+
// which requires an absolute URL.
100+
return 'self.location.origin+' + quote + rewritten + quote
97101
}
98102

99103
return match
@@ -105,11 +109,11 @@ export function rewriteScriptUrls(content: string, rewrites: ProxyRewrite[]): st
105109
if (gaRewrite) {
106110
result = result.replace(
107111
/"https:\/\/"\+\(.*?\)\+"\.google-analytics\.com\/g\/collect"/g,
108-
`"${gaRewrite.to}"`,
112+
`self.location.origin+"${gaRewrite.to}"`,
109113
)
110114
result = result.replace(
111115
/"https:\/\/"\+\(.*?\)\+"\.analytics\.google\.com\/g\/collect"/g,
112-
`"${gaRewrite.to}"`,
116+
`self.location.origin+"${gaRewrite.to}"`,
113117
)
114118
}
115119

test/unit/proxy-configs.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,39 @@ describe('proxy configs', () => {
88
const output = rewriteScriptUrls(input, [
99
{ from: 'www.google-analytics.com', to: '/_scripts/c/ga' },
1010
])
11-
expect(output).toBe(`fetch("/_scripts/c/ga/g/collect")`)
11+
expect(output).toBe(`fetch(self.location.origin+"/_scripts/c/ga/g/collect")`)
1212
})
1313

1414
it('rewrites https URLs with single quotes', () => {
1515
const input = `url='https://www.google-analytics.com/analytics.js'`
1616
const output = rewriteScriptUrls(input, [
1717
{ from: 'www.google-analytics.com', to: '/_scripts/c/ga' },
1818
])
19-
expect(output).toBe(`url='/_scripts/c/ga/analytics.js'`)
19+
expect(output).toBe(`url=self.location.origin+'/_scripts/c/ga/analytics.js'`)
2020
})
2121

2222
it('rewrites https URLs with backticks', () => {
2323
const input = 'const u=`https://www.google-analytics.com/collect`'
2424
const output = rewriteScriptUrls(input, [
2525
{ from: 'www.google-analytics.com', to: '/_scripts/c/ga' },
2626
])
27-
expect(output).toBe('const u=`/_scripts/c/ga/collect`')
27+
expect(output).toBe('const u=self.location.origin+`/_scripts/c/ga/collect`')
2828
})
2929

3030
it('rewrites protocol-relative URLs', () => {
3131
const input = `"//www.google-analytics.com/analytics.js"`
3232
const output = rewriteScriptUrls(input, [
3333
{ from: 'www.google-analytics.com', to: '/_scripts/c/ga' },
3434
])
35-
expect(output).toBe(`"/_scripts/c/ga/analytics.js"`)
35+
expect(output).toBe(`self.location.origin+"/_scripts/c/ga/analytics.js"`)
3636
})
3737

3838
it('rewrites http URLs', () => {
3939
const input = `"http://www.google-analytics.com/analytics.js"`
4040
const output = rewriteScriptUrls(input, [
4141
{ from: 'www.google-analytics.com', to: '/_scripts/c/ga' },
4242
])
43-
expect(output).toBe(`"/_scripts/c/ga/analytics.js"`)
43+
expect(output).toBe(`self.location.origin+"/_scripts/c/ga/analytics.js"`)
4444
})
4545

4646
it('handles multiple rewrites in single content', () => {
@@ -52,24 +52,24 @@ describe('proxy configs', () => {
5252
{ from: 'www.google-analytics.com', to: '/_scripts/c/ga' },
5353
{ from: 'analytics.google.com', to: '/_scripts/c/ga' },
5454
])
55-
expect(output).toContain(`"/_scripts/c/ga/g/collect"`)
56-
expect(output).toContain(`"/_scripts/c/ga/collect"`)
55+
expect(output).toContain(`self.location.origin+"/_scripts/c/ga/g/collect"`)
56+
expect(output).toContain(`self.location.origin+"/_scripts/c/ga/collect"`)
5757
})
5858

5959
it('handles GTM URLs', () => {
6060
const input = `src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXX"`
6161
const output = rewriteScriptUrls(input, [
6262
{ from: 'www.googletagmanager.com', to: '/_scripts/c/gtm' },
6363
])
64-
expect(output).toBe(`src="/_scripts/c/gtm/gtm.js?id=GTM-XXXX"`)
64+
expect(output).toBe(`src=self.location.origin+"/_scripts/c/gtm/gtm.js?id=GTM-XXXX"`)
6565
})
6666

6767
it('handles Meta Pixel URLs', () => {
6868
const input = `"https://connect.facebook.net/en_US/fbevents.js"`
6969
const output = rewriteScriptUrls(input, [
7070
{ from: 'connect.facebook.net', to: '/_scripts/c/meta' },
7171
])
72-
expect(output).toBe(`"/_scripts/c/meta/en_US/fbevents.js"`)
72+
expect(output).toBe(`self.location.origin+"/_scripts/c/meta/en_US/fbevents.js"`)
7373
})
7474

7575
it('does not rewrite bare domain strings without fromPath', () => {

0 commit comments

Comments
 (0)