{"id":"CVE-2026-114133","kind":"vulnerability","aliases":["CVE-2026-114133","GHSA-xfq5-w38q-pc2x"],"related":[],"title":"Nuxt server islands render `useHeadSafe` input without sanitisation","description":"## Impact\n\n`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.\n\nWhen `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.\n\nAs 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.\n\n```vue\n<!-- components/AuthorMeta.server.vue -->\n<script setup lang=\"ts\">\nconst { data: author } = await useFetch('/api/author')\n// author.value.head is user-authored, e.g. { bodyAttrs: { onload: '...' } }\nuseHeadSafe(author.value.head)\n</script>\n```\n\n## Mitigating factors\n\n- 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>`.\n- 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.\n\n## Affected versions\n\n`nuxt` `>=3.13.0`, when head support for server islands was introduced. Nuxt 2 is not affected.\n\n## Patches\n\nFixed in `nuxt@4.6.2`. Nuxt 3 reached end of life on 31 July 2026 and will not receive a fix.\n\nPatched 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.\n\n## Workarounds\n\nUpgrade to `nuxt@4.6.2`. If you cannot upgrade, including on Nuxt 3:\n\n1. Call `useHeadSafe` with untrusted input from a regular component or page rather than from inside a server island.\n2. Alternatively, validate untrusted head input against your own allowlist before passing it to `useHeadSafe` inside a server island.","summary":"Nuxt server islands serialise raw useHeadSafe input without the safe-input flag, so tags and attributes that useHeadSafe would strip (such as bodyAttrs.onload or executable script content) get rendered into the page. If an app passes user-controlled data to useHeadSafe inside a server island, an attacker can run stored XSS on the app's origin.","severity":"medium","cvss":5.400000095367432,"epss":0.0017600000137463212,"exploited":false,"exploited_at":null,"products":[{"name":"nuxt","fixed":["4.6.2"],"affected":">= 3.13.0, < 4.6.2","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-79","next.js","nuxt","runtime","self-hosted","vue"],"not_affected_if":["default-config","fixed-version","no-server-islands","nuxt-2","static-export"],"how_to_check":"Search the codebase for useHeadSafe inside .server.vue components/pages or components rendered with NuxtIsland, and check whether any of that input comes from user-controlled data (e.g. an API response that users can write to).\nCheck the installed nuxt version in node_modules/nuxt/package.json or your lockfile; affected if >=3.13.0 and <4.6.2.","fix":"Upgrade nuxt to 4.6.2 or later (Nuxt 3 is end of life and will not get a fix).\nUntil then, call useHeadSafe with untrusted input from a regular component or page, or validate that input against your own allowlist before passing it inside a server island.","status":"open","published_at":"2026-10-21T18:17:26+00:00","first_seen_at":"2026-10-23T19:11:31+00:00","triaged_at":"2026-10-23T19:58:15+00:00","updated_at":"2026-10-23T21:57:29+00:00","category":"runtime","confidence":0.85,"triage":{"at":"2026-10-23T19:58:15Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade nuxt to 4.6.2 or later (Nuxt 3 is end of life and will not get a fix).\nUntil then, call useHeadSafe with untrusted input from a regular component or page, or validate that input against your own allowlist before passing it inside a server island.","summary":"Nuxt server islands serialise raw useHeadSafe input without the safe-input flag, so tags and attributes that useHeadSafe would strip (such as bodyAttrs.onload or executable script content) get rendered into the page. If an app passes user-controlled data to useHeadSafe inside a server island, an attacker can run stored XSS on the app's origin.","category":"runtime","who_cares":["nuxt","next.js","self-hosted","vue"],"confidence":0.85,"analyst_note":"","how_to_check":"Search the codebase for useHeadSafe inside .server.vue components/pages or components rendered with NuxtIsland, and check whether any of that input comes from user-controlled data (e.g. an API response that users can write to).\nCheck the installed nuxt version in node_modules/nuxt/package.json or your lockfile; affected if >=3.13.0 and <4.6.2.","needs_analyst":false,"not_affected_if":["static-export","default-config","no-server-islands","nuxt-2","fixed-version"],"severity_if_unknown":"unknown"},"cost_usd":0.000519,"input_tokens":2865,"output_tokens":466,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-114133':11A,19A '-2026':10A,18A '-79':22B '1':256C '2':182C,275C '2026':200C '3':192C,255C '3.13.0':172C '31':198C '4.6.2':190C,247C 'activ':125C 'add':224C 'affect':168C,169C,185C 'allow':242C 'allowlist':95C,284C 'altern':276C 'app':65B,85B 'appli':209C 'applic':128C,143C 'attack':78B,147C 'attacker-control':146C 'attribut':46B,100C,112C 'bodyattr':103C 'bodyattrs.onload':53B 'call':157C,257C 'cannot':250C 'class':107C 'compon':119C,132C,163C,265C 'contain':238C 'content':57B 'control':69B,148C 'cve':9A,17A 'cwe':21B 'data':70B,149C 'defin':129C 'enabl':136C 'end':194C 'entri':217C 'event':110C 'exampl':102C 'execut':55B 'experiment':121C 'experimental.componentislands':137C 'exploit':140C 'factor':117C 'filter':214C 'fix':187C,206C 'flag':42B 'get':58B 'ghsa':13A 'ghsa-xfq5-w38q-pc2x':12A 'handler':111C 'head':91C,174C,279C 'hook':235C 'host':28B 'impact':88C 'includ':231C,252C 'input':6A,36B,41B,92C,213C,239C,261C,280C 'insid':73B,152C,271C,290C 'introduc':180C 'island':3A,32B,76B,155C,162C,178C,227C,234C,274C,293C 'islandresponse.head':230C 'juli':199C 'life':196C 'limit':105C 'mitig':116C 'next.js':23B 'non':161C 'non-island':160C 'nuxt':1A,20A,24B,30B,171C,181C,189C,191C,246C,254C 'origin':87B 'page':62B,134C,165C,267C 'pass':66B,145C,286C 'patch':186C,207C 'pc2x':16A 'rather':268C 'raw':34B 'reach':193C 'receiv':204C 'regular':159C,264C 'releas':208C 'remov':114C 'render':4A,59B,139C,233C 'requir':141C 'respons':228C 'restrict':90C 'run':80B 'runtim':25B 'safe':40B,97C,212C 'safe-input':39B 'sanitis':8A 'script':56B 'see':236C 'self':27B 'self-host':26B 'serialis':33B 'server':2A,31B,75B,118C,154C,177C,220C,273C,292C 'server.vue':131C 'store':81B 'strip':50B 'style':109C 'support':175C 'tag':44B,98C 'unhead':210C 'untrust':260C,278C 'upgrad':244C,251C 'useheadsaf':5A,35B,48B,72B,89C,151C,156C,216C,241C,258C,289C 'user':68B 'user-control':67B 'valid':277C 'version':170C 'vue':29B 'w38q':15A 'without':7A,37B 'workaround':243C 'would':49B 'xfq5':14A 'xss':82B","nicknames":[],"mitigations":[],"who_affected":null,"tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-21T18:17:26+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-xfq5-w38q-pc2x","url":"https://github.com/advisories/GHSA-xfq5-w38q-pc2x","severity":"medium","published_at":"2026-10-22T16:58:00+00:00","modified_at":"2026-10-22T16:58:02+00:00","first_seen_at":"2026-10-23T19:11:31+00:00","fetched_at":"2026-10-23T19:11:31+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-114133","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-114133","severity":null,"published_at":"2026-10-21T18:17:26+00:00","modified_at":"2026-10-22T16:46:48+00:00","first_seen_at":"2026-10-23T21:57:29+00:00","fetched_at":"2026-10-23T22:29:01+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}