GHSA-g2v6-rqmx-r4w6
## Description `@vue/server-renderer` was investigated specifically because it's the one place in Vue where template rendering output becomes a real HTTP response body -- a genuine server-side trust boundary, unlike client-side rendering which only ever affects the same browser session that's already running the app's own JS. `ssrRenderAttrs()` (in `packages/server-renderer/src/helpers/ssrRenderAttrs.ts`) is what compiled SSR output calls for something like `<div v-bind="userObject">`. It loops over the object's own keys and, for each one, renders it as an HTML attribute: ```ts export function ssrRenderDynamicAttr( key: string, value: unknown, tag?: string, ): string { if (!isRenderableAttrValue(value)) { return `` } const attrKey = ... if (isBooleanAttr(attrKey) || ...) { return includeBooleanAttr(value) ? ` ${attrKey}` : `` } else if (isSSRSafeAttrName(attrKey)) { return value === '' ? ` ${attrKey}` : ` ${attrKey}="${escapeHtml(value)}"` } else { console.warn(`[@vue/server-renderer] Skipped rendering unsafe attribute name: ${attrKey}`) return `` } } ``` The value always goes through `escapeHtml()` -- that part is correctly and consistently applied everywhere in this file. But the attribute name (`attrKey`) is only checked against a character blacklist, then spliced directly into the output with no escaping of its own: ```ts // packages/shared/src/domAttrConfig.ts const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/ export function isSSRSafeAttrName(name: string): boolean { if (attrValidationCache.hasOwnProperty(name)) { return attrValidationCache[name] } const isUnsafe = unsafeAttrCharRE.test(name) if (isUnsafe) { console.error(`unsafe attribute name: ${name}`) } return (attrValidationCache[name] = !isUnsafe) } ``` That blacklist covers: `>`, `/`, `=`, `"`, apostrophe, tab (U+0009), line feed (U+000A), form feed (U+000C), and space (U+0020). It does not cover U+000D -- carriage return (CR, wr
Properties
- summary
- @vue/server-renderer: XSS via missing CR in attribute-name blacklist
- severity
- high
- cvss_score
- 7.2
- retrieved_at
- 2026-10-05T22:59:07+00:00
- ghsa_published
- 2026-10-05T22:50:55Z
- source_url
- https://github.com/advisories/GHSA-g2v6-rqmx-r4w6
- ghsa_updated
- 2026-10-05T22:50:56Z
- ghsa_id
- GHSA-g2v6-rqmx-r4w6
- last_source
- GitHub Advisory Database
- cve_id
- GHSA-g2v6-rqmx-r4w6
- cvss_vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
- signal_observed_at
- 2026-10-05T22:59:07+00:00
- is_ghsa_only
- true
Related Entities (5)
HAS_WEAKNESS (2)
REPORTED_BY (1)
VULNERABLE_TO (1)
AFFECTS (1)
Explore deeper with Ninja Signal's threat intelligence graph