diff --git a/specs/experimental/switch_locale_path_link_ssr.spec.ts b/specs/experimental/switch_locale_path_link_ssr.spec.ts
index e43de3883..881c315b2 100644
--- a/specs/experimental/switch_locale_path_link_ssr.spec.ts
+++ b/specs/experimental/switch_locale_path_link_ssr.spec.ts
@@ -55,4 +55,14 @@ describe('experimental.switchLocalePathLinkSSR', async () => {
expect(product2dom.querySelector('#i18n-alt-en').href).toEqual('/products/red-mug')
expect(product2dom.querySelector('#switch-locale-path-link-en').href).toEqual('/products/red-mug')
})
+
+ test('encode localized path to prevent XSS', async () => {
+ const url = `/experimental//"><`
+
+ const html = await $fetch(url)
+ const dom = getDom(html)
+
+ // the localized should be the same as encoded
+ expect(dom.querySelector('#slp-xss a').href).toEqual(encodeURI('/nl' + url))
+ })
})
diff --git a/specs/fixtures/basic_usage/pages/experimental/[...slug].vue b/specs/fixtures/basic_usage/pages/experimental/[...slug].vue
new file mode 100644
index 000000000..5c550a091
--- /dev/null
+++ b/specs/fixtures/basic_usage/pages/experimental/[...slug].vue
@@ -0,0 +1,8 @@
+
+
+
+ No XSS
+
+
diff --git a/src/runtime/components/SwitchLocalePathLink.ts b/src/runtime/components/SwitchLocalePathLink.ts
index 906a04fbd..3166d0cce 100644
--- a/src/runtime/components/SwitchLocalePathLink.ts
+++ b/src/runtime/components/SwitchLocalePathLink.ts
@@ -21,7 +21,7 @@ export default defineComponent({
return () => [
h(Comment, `${SWITCH_LOCALE_PATH_LINK_IDENTIFIER}-[${props.locale}]`),
- h(NuxtLink, { ...attrs, to: switchLocalePath(props.locale) }, slots.default),
+ h(NuxtLink, { ...attrs, to: encodeURI(switchLocalePath(props.locale)) }, slots.default),
h(Comment, `/${SWITCH_LOCALE_PATH_LINK_IDENTIFIER}`)
]
}
diff --git a/src/runtime/plugins/switch-locale-path-ssr.ts b/src/runtime/plugins/switch-locale-path-ssr.ts
index 87d3911d2..598665889 100644
--- a/src/runtime/plugins/switch-locale-path-ssr.ts
+++ b/src/runtime/plugins/switch-locale-path-ssr.ts
@@ -25,7 +25,8 @@ export default defineNuxtPlugin({
ctx.renderResult.html = ctx.renderResult.html.replaceAll(
switchLocalePathLinkWrapperExpr,
- (match: string, p1: string) => match.replace(/href="([^"]+)"/, `href="${switchLocalePath(p1 ?? '')}"`)
+ (match: string, p1: string) =>
+ match.replace(/href="([^"]+)"/, `href="${encodeURI(switchLocalePath(p1 ?? ''))}"`)
)
})
}