GHSA-xfq5-w38q-pc2x: XSS
Impact
useHeadSafe restricts head input to an allowlist of safe tags and attributes. For example, bodyAttrs is limited to class and style, event handler attributes are removed, and <script> tags are only kept for JSON types.
When useHeadSafe is called inside a server island (a .server.vue component or page, or a component rendered with <NuxtIsland>), the island renderer serialises the raw input of every head entry into the island response without the option that marks it as safe input. <NuxtIsland> then pushes that input into the page's head as a regular entry, both during server rendering and when hydrating or refetching on the client.
As a result, input that useHeadSafe would normally strip (such as bodyAttrs.onload or executable <script> content) is rendered into the page. If an application passes user-controlled data to useHeadSafe inside a server island, an attacker who controls that data can achieve stored cross-site scripting on the application's origin.
vue <!-- components/AuthorMeta.server.vue --> <script setup lang="ts"> const { data: author } = await useFetch('/api/author') // author.value.head is user-authored, e.g. { bodyAttrs: { onload: '...' } } useHeadSafe(author.value.head) </script>
Mitigating factors
- Server components are experimental. They are only active when the application defines a .server.vue component or page, or enables experimental.componentIslands and renders <NuxtIsland>. - Exploitation requires the application to pass attacker-controlled data to useHeadSafe inside a server island. useHeadSafe called from regular (non-island) components and pages is not affected.
Affected versions
nuxt >=3.13.0, when head support for server islands was introduced. Nuxt 2 is not affected.
Patches
Fixed in nuxt@4.6.2. Nuxt 3 reached end of life on 31 July 2026 and will not receive a fix.
Patched releases apply Unhead's safe input filtering to useHeadSafe entries on the server before they are added to the island response, so islandResponse.head (including what render:island hooks see) only contains input that useHeadSafe allows.
Workarounds
Upgrade to nuxt@4.6.2. If you cannot upgrade, including on Nuxt 3:
1. Call useHeadSafe with untrusted input from a regular component or page rather than from inside a server island. 2. Alternatively, validate untrusted head input against your own allowlist before passing it to useHeadSafe inside a server island.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/nuxtto a version that resolves this vulnerability.Fixed in 4.6.2 - Upgrade
Upgrade
nuxtto a version that resolves this vulnerability.Fixed in 4.6.2 - Compensating control
Call useHeadSafe with untrusted input from a regular (non-island) component or page rather than from inside a server island.
- Compensating control
Validate untrusted head input against an application-defined allowlist before passing it to useHeadSafe inside a server island.
Event History
Frequently Asked Questions
Which applications are exposed to this issue?
Applications are exposed when they call useHeadSafe inside a server island, such as a .server.vue component or page or a component rendered with <NuxtIsland>, and pass user-controlled data to it. The impact is stored cross-site scripting on the application's origin.
What does an attacker need to exploit the vulnerability?
An attacker needs control over data that the application supplies to useHeadSafe from within a server island. Examples of dangerous input include bodyAttrs.onload or executable script content that useHeadSafe would normally remove.
How can I determine whether my application is affected?
Review .server.vue components, server pages, and components rendered with <NuxtIsland> for useHeadSafe calls. Determine whether any head-entry input passed to those calls originates from user-authored or otherwise attacker-controlled data.