{"id":"GHSA-hf59-9xwc-2g4p","kind":"vulnerability","aliases":["GHSA-hf59-9xwc-2g4p"],"related":[],"title":"Colliding island props can share one `/__nuxt_island` path (incomplete fix for CVE-2026-46342)","description":"### Summary\n\nThe fix for CVE-2026-46342 (GHSA-g8wj-3cr3-6w7v) binds each `/__nuxt_island/<Name>_<hash>.json` path to its props by hashing the `(name, props, context, source)` tuple. That hash was computed over `ohash`'s serialization, which does not escape quotes inside strings or delimiters inside keys, so two different prop objects can produce the same hash. A request with crafted props can therefore pass validation for another island's path and render different content at that path.\n\n### Impact\n\nFor example, these two prop objects serialize identically and share one island path:\n\n```js\n{ html: '', note: '<img src=x onerror=\"alert(1)\">' }\n{ html: '\\',note:\\'<img src=x onerror=\"alert(1)\">' }\n```\n\nThis reopens the scenarios described in GHSA-g8wj-3cr3-6w7v:\n\n- **`nuxt generate` / prerendering.** Nuxt caches prerendered island responses by path. If the crawler reaches a link to the colliding URL (for example in user-submitted content included in the build) before it renders the page that uses the legitimate props, the colliding response is written into the generated page.\n- **Path-keyed shared caches.** A CDN or reverse proxy that caches `/__nuxt_island/*` by path only, ignoring the query, can be primed with the colliding response.\n\nAn attacker has to construct props that collide with a specific legitimate tuple, which in practice means at least part of that tuple is a string they control (for example a stored comment or profile field passed as a prop). Where a prop reaches an unsafe HTML sink in the island (`v-html`, `innerHTML`, a renderer treating a prop as HTML), the substituted response can execute script in the site's origin. Without such a sink, the impact is content substitution.\n\nApplications are not affected if they do not use server components or `<NuxtIsland>`, or if no island receives attacker-influenced string props. Server-rendered responses that are not cached by path alone are also unaffected.\n\n### Patches\n\nFixed in `nuxt@4.6.4` and `@nuxt/nitro-server@4.6.4`. The island hash is now computed over a canonical JSON encoding of the tuple, in which strings and keys are escaped, so different props can no longer produce the same hash.\n\nIsland hashes change as a result. Clients that build island URLs themselves should use the exported `getIslandHash` rather than hashing with `ohash` directly.\n\nNuxt 3 has reached end of life and will not receive a fix. Upgrade to Nuxt 4.\n\n### Workarounds\n\n- Ensure any intermediary cache keys `/__nuxt_island/*` on the full query string, not on the path alone.\n- Treat island props as untrusted input and do not pass them to `v-html` or other HTML sinks without sanitisation.\n- When prerendering, avoid including user-submitted links to `/__nuxt_island/` in crawled pages, for example by excluding that prefix with `nitro.prerender.ignore`:\n\n```ts\nexport default defineNuxtConfig({\n  nitro: {\n    prerender: {\n      ignore: [route => route.startsWith('/__nuxt_island/') && route.includes('?')],\n    },\n  },\n})\n```\n\n### Credits\n\nThanks to rat5ak for reporting this issue.","summary":"Nuxt server islands hash their props to pick a URL path, and the hash can collide for different prop objects, so a crafted request can get another island's path to render attacker-chosen content. Where a prop reaches an unsafe HTML sink, this can run script in the site's origin; otherwise the impact is content substitution.","severity":"low","cvss":3.1,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"nuxt","fixed":["4.6.4"],"affected":">= 4.4.6, < 4.6.4","ecosystem":"npm"},{"name":"nuxt","fixed":[],"affected":">= 3.21.6, < 4.0.0","ecosystem":"npm"},{"name":"@nuxt/nitro-server","fixed":["4.6.4"],"affected":">= 4.4.6, < 4.6.4","ecosystem":"npm"},{"name":"@nuxt/nitro-server","fixed":[],"affected":">= 3.21.6, < 4.0.0","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cdn","cwe-116","cwe-349","nitro","nuxt","package","self-hosted","ssr"],"not_affected_if":["no-server-islands","no-user-string-props","query-aware-cache","static-export-no-user-links"],"how_to_check":"- Check the installed Nuxt version with `npx nuxt --version` or the `nuxt` entry in `package.json` and `package-lock.json`.\n- Check `@nuxt/nitro-server` in the lockfile for a version at or above `4.6.4`.\n- Look for `<NuxtIsland>` or server components in the app, and for island props that take user-supplied strings.\n- Check whether a CDN or reverse proxy caches `/__nuxt_island/*` by path without the query string.","fix":"- `nuxt` `4.6.4` (for the 4.x line, affected `>= 4.4.6`, `< 4.6.4`)\n- `@nuxt/nitro-server` `4.6.4` (for the 4.x line, affected `>= 4.4.6`, `< 4.6.4`)\n- Nuxt 3 has reached end of life and will not receive a fix; upgrade to Nuxt 4.","status":"open","published_at":"2026-11-02T20:59:56+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","triaged_at":"2026-11-09T22:57:27+00:00","updated_at":"2026-11-09T22:57:27+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:57:27Z","model":"claude-haiku-5-5","output":{"fix":"- `nuxt` `4.6.4` (for the 4.x line, affected `>= 4.4.6`, `< 4.6.4`)\n- `@nuxt/nitro-server` `4.6.4` (for the 4.x line, affected `>= 4.4.6`, `< 4.6.4`)\n- Nuxt 3 has reached end of life and will not receive a fix; upgrade to Nuxt 4.","reach":"internet","title":"Nuxt: colliding island props can substitute island content at one path","impact":"medium","summary":"Nuxt server islands hash their props to pick a URL path, and the hash can collide for different prop objects, so a crafted request can get another island's path to render attacker-chosen content. Where a prop reaches an unsafe HTML sink, this can run script in the site's origin; otherwise the impact is content substitution.","audience":"developers","category":"package","nicknames":[],"who_cares":["nuxt","nitro","self-hosted","ssr","cdn"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"island path serves attacker content; script runs if an HTML sink is used","analyst_note":"","how_to_check":"- Check the installed Nuxt version with `npx nuxt --version` or the `nuxt` entry in `package.json` and `package-lock.json`.\n- Check `@nuxt/nitro-server` in the lockfile for a version at or above `4.6.4`.\n- Look for `<NuxtIsland>` or server components in the app, and for island props that take user-supplied strings.\n- Check whether a CDN or reverse proxy caches `/__nuxt_island/*` by path without the query string.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-server-islands","no-user-string-props","query-aware-cache","static-export-no-user-links"],"who_is_affected":"Applications that use Nuxt server components or `<NuxtIsland>` with island props that carry attacker-influenced strings, such as stored comments or profile fields. Impact is highest when prerendering with user-submitted links present, or when a path-keyed CDN or reverse proxy caches island responses. Applications that do not use islands, or where no island receives attacker-controlled string props, are not affected. Nuxt 3 has no fix because it is end of life.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"low"},"cost_usd":0.000989,"input_tokens":5757,"output_tokens":827,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-116':31B '-2026':13A,105C '-349':33B '-46342':14A,106C '/__nuxt_island':7A,114C,270C,487C,528C,549C '2g4p':19A,24A '3':465C '3cr3':110C,206C '4':480C '4.6.4':406C,409C '6w7v':111C,207C '9xwc':18A,23A 'affect':369C 'alon':398C,497C 'also':400C 'anoth':67B,167C 'applic':366C 'attack':74B,285C,384C 'attacker-chosen':73B 'attacker-influenc':383C 'avoid':521C 'bind':112C 'build':238C,449C 'cach':212C,262C,269C,395C,485C 'canon':418C 'cdn':29B,264C 'chang':443C 'chosen':75B 'client':447C 'collid':1A,56B,226C,250C,282C,291C 'comment':316C 'compon':376C 'comput':131C,415C 'construct':288C 'content':76B,98B,174C,234C,364C 'context':125C 'control':311C 'craft':63B,160C 'crawl':530C 'crawler':220C 'credit':551C 'cve':12A,104C 'cwe':30B,32B 'default':542C 'definenuxtconfig':543C 'delimit':144C 'describ':201C 'differ':58B,149C,173C,432C 'direct':463C 'encod':420C 'end':468C 'ensur':482C 'escap':139C,430C 'exampl':180C,229C,313C,533C 'exclud':535C 'execut':350C 'export':456C,541C 'field':319C 'fix':10A,102C,403C,476C 'full':490C 'g8wj':109C,205C 'generat':209C,256C 'get':66B 'getislandhash':457C 'ghsa':16A,21A,108C,204C 'ghsa-g8wj-3cr3-6w7v':107C,203C 'ghsa-hf59-9xwc-2g4p':15A,20A 'hash':44B,54B,121C,129C,156C,412C,440C,442C,460C 'hf59':17A,22A 'host':39B 'html':83B,193C,195C,330C,337C,345C,512C,515C 'ident':186C 'ignor':274C,546C 'impact':96B,178C,362C 'includ':235C,522C 'incomplet':9A 'influenc':385C 'innerhtml':338C 'input':503C 'insid':141C,145C 'intermediari':484C 'island':2A,43B,68B,168C,190C,214C,334C,381C,411C,441C,450C,499C 'issu':558C 'js':192C 'json':115C,419C 'key':146C,260C,428C,486C 'least':302C 'legitim':247C,295C 'life':470C 'link':223C,526C 'longer':436C 'mean':300C 'name':123C 'nitro':34B,544C 'nitro.prerender.ignore':539C 'note':194C,196C 'nuxt':25A,26A,35B,41B,208C,211C,405C,464C,479C 'nuxt/nitro-server':27A,28A,408C 'object':60B,151C,184C 'ohash':133C,462C 'one':6A,189C 'origin':93B,356C 'otherwis':94B 'packag':36B 'page':243C,257C,531C 'part':303C 'pass':164C,320C,507C 'patch':402C 'path':8A,51B,70B,116C,170C,177C,191C,217C,259C,272C,397C,496C 'path-key':258C 'pick':48B 'practic':299C 'prefix':537C 'prerend':210C,213C,520C,545C 'prime':279C 'produc':153C,437C 'profil':318C 'prop':3A,46B,59B,79B,119C,124C,150C,161C,183C,248C,289C,323C,326C,343C,387C,433C,500C 'proxi':267C 'queri':276C,491C 'quot':140C 'rat5ak':554C 'rather':458C 'reach':80B,221C,327C,467C 'receiv':382C,474C 'render':72B,172C,241C,340C,390C 'reopen':198C 'report':556C 'request':64B,158C 'respons':215C,251C,283C,348C,391C 'result':446C 'revers':266C 'rout':547C 'route.includes':550C 'route.startswith':548C 'run':87B 'sanitis':518C 'scenario':200C 'script':88B,351C 'self':38B 'self-host':37B 'serial':135C,185C 'server':42B,375C,389C 'server-rend':388C 'share':5A,188C,261C 'sink':84B,331C,360C,516C 'site':91B,354C 'sourc':126C 'specif':294C 'ssr':40B 'store':315C 'string':142C,309C,386C,426C,492C 'submit':233C,525C 'substitut':99B,347C,365C 'summari':100C 'thank':552C 'therefor':163C 'treat':341C,498C 'ts':540C 'tupl':127C,296C,306C,423C 'two':148C,182C 'unaffect':401C 'unsaf':82B,329C 'untrust':502C 'upgrad':477C 'url':50B,227C,451C 'use':245C,374C,454C 'user':232C,524C 'user-submit':231C,523C 'v':336C,511C 'v-html':335C,510C 'valid':165C 'without':357C,517C 'workaround':481C 'written':253C","nicknames":[],"mitigations":[],"who_affected":"Applications that use Nuxt server components or `<NuxtIsland>` with island props that carry attacker-influenced strings, such as stored comments or profile fields. Impact is highest when prerendering with user-submitted links present, or when a path-keyed CDN or reverse proxy caches island responses. Applications that do not use islands, or where no island receives attacker-controlled string props, are not affected. Nuxt 3 has no fix because it is end of life.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["nitroserver","nuxt"],"ptokens":["nitro","nuxt","server"],"labels":["cdn","cwe116","cwe349","low","nitro","npm","nuxt","package","selfhosted","ssr"],"campaign_type":null,"search_a":"'-2026':13A '-46342':14A '/__nuxt_island':7A '2g4p':19A,24A '9xwc':18A,23A 'collid':1A 'cve':12A 'fix':10A 'ghsa':16A,21A 'ghsa-hf59-9xwc-2g4p':15A,20A 'hf59':17A,22A 'incomplet':9A 'island':2A 'nuxt':25A,26A 'nuxt/nitro-server':27A,28A 'one':6A 'path':8A 'prop':3A 'share':5A","search_b":"'-116':31B '-349':33B 'anoth':67B 'attack':74B 'attacker-chosen':73B 'cdn':29B 'chosen':75B 'collid':56B 'content':76B,98B 'craft':63B 'cwe':30B,32B 'differ':58B 'get':66B 'hash':44B,54B 'host':39B 'html':83B 'impact':96B 'island':43B,68B 'nitro':34B 'nuxt':35B,41B 'object':60B 'origin':93B 'otherwis':94B 'packag':36B 'path':51B,70B 'pick':48B 'prop':46B,59B,79B 'reach':80B 'render':72B 'request':64B 'run':87B 'script':88B 'self':38B 'self-host':37B 'server':42B 'sink':84B 'site':91B 'ssr':40B 'substitut':99B 'unsaf':82B 'url':50B","search_c":"'-2026':105C '-46342':106C '/__nuxt_island':114C,270C,487C,528C,549C '3':465C '3cr3':110C,206C '4':480C '4.6.4':406C,409C '6w7v':111C,207C 'affect':369C 'alon':398C,497C 'also':400C 'anoth':167C 'applic':366C 'attack':285C,384C 'attacker-influenc':383C 'avoid':521C 'bind':112C 'build':238C,449C 'cach':212C,262C,269C,395C,485C 'canon':418C 'cdn':264C 'chang':443C 'client':447C 'collid':226C,250C,282C,291C 'comment':316C 'compon':376C 'comput':131C,415C 'construct':288C 'content':174C,234C,364C 'context':125C 'control':311C 'craft':160C 'crawl':530C 'crawler':220C 'credit':551C 'cve':104C 'default':542C 'definenuxtconfig':543C 'delimit':144C 'describ':201C 'differ':149C,173C,432C 'direct':463C 'encod':420C 'end':468C 'ensur':482C 'escap':139C,430C 'exampl':180C,229C,313C,533C 'exclud':535C 'execut':350C 'export':456C,541C 'field':319C 'fix':102C,403C,476C 'full':490C 'g8wj':109C,205C 'generat':209C,256C 'getislandhash':457C 'ghsa':108C,204C 'ghsa-g8wj-3cr3-6w7v':107C,203C 'hash':121C,129C,156C,412C,440C,442C,460C 'html':193C,195C,330C,337C,345C,512C,515C 'ident':186C 'ignor':274C,546C 'impact':178C,362C 'includ':235C,522C 'influenc':385C 'innerhtml':338C 'input':503C 'insid':141C,145C 'intermediari':484C 'island':168C,190C,214C,334C,381C,411C,441C,450C,499C 'issu':558C 'js':192C 'json':115C,419C 'key':146C,260C,428C,486C 'least':302C 'legitim':247C,295C 'life':470C 'link':223C,526C 'longer':436C 'mean':300C 'name':123C 'nitro':544C 'nitro.prerender.ignore':539C 'note':194C,196C 'nuxt':208C,211C,405C,464C,479C 'nuxt/nitro-server':408C 'object':151C,184C 'ohash':133C,462C 'one':189C 'origin':356C 'page':243C,257C,531C 'part':303C 'pass':164C,320C,507C 'patch':402C 'path':116C,170C,177C,191C,217C,259C,272C,397C,496C 'path-key':258C 'practic':299C 'prefix':537C 'prerend':210C,213C,520C,545C 'prime':279C 'produc':153C,437C 'profil':318C 'prop':119C,124C,150C,161C,183C,248C,289C,323C,326C,343C,387C,433C,500C 'proxi':267C 'queri':276C,491C 'quot':140C 'rat5ak':554C 'rather':458C 'reach':221C,327C,467C 'receiv':382C,474C 'render':172C,241C,340C,390C 'reopen':198C 'report':556C 'request':158C 'respons':215C,251C,283C,348C,391C 'result':446C 'revers':266C 'rout':547C 'route.includes':550C 'route.startswith':548C 'sanitis':518C 'scenario':200C 'script':351C 'serial':135C,185C 'server':375C,389C 'server-rend':388C 'share':188C,261C 'sink':331C,360C,516C 'site':354C 'sourc':126C 'specif':294C 'store':315C 'string':142C,309C,386C,426C,492C 'submit':233C,525C 'substitut':347C,365C 'summari':100C 'thank':552C 'therefor':163C 'treat':341C,498C 'ts':540C 'tupl':127C,296C,306C,423C 'two':148C,182C 'unaffect':401C 'unsaf':329C 'untrust':502C 'upgrad':477C 'url':227C,451C 'use':245C,374C,454C 'user':232C,524C 'user-submit':231C,523C 'v':336C,511C 'v-html':335C,510C 'valid':165C 'without':357C,517C 'workaround':481C 'written':253C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-02T20:59:56+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-hf59-9xwc-2g4p","url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-hf59-9xwc-2g4p","severity":"low","published_at":"2026-11-02T20:59:56+00:00","modified_at":"2026-11-02T20:59:56+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","fetched_at":"2026-11-09T22:53:16+00:00","note":null}],"documents":[{"id":110428,"url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-hf59-9xwc-2g4p","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}