Skip to content
Heist/ feed
HighFix available

@vue/server-renderer: XSS via missing CR in attribute-name blacklist

Type: advisorySource: OSV1 source record

## 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 ``. 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 (attrValidationC

Announced Source revision 1
Original source ↗

GHSA-g2v6-rqmx-r4w6

Primary source · Revision 1

Imported from structured advisory data. Source references identify upstream records or fields; verify affected versions and your deployed configuration.

Security context

Source reference: osv:GHSA-g2v6-rqmx-r4w6

@vue/server-renderer

Source reference: /affected/0/package/name

Affected versions
Not stated in this record
Fixed versions
3.5.42

Source reference: /affected/0/ranges

Prerequisites
Not stated in this record

@vue/server-renderer

Source reference: /affected/1/package/name

Affected versions
Not stated in this record
Fixed versions
3.6.0-rc.6

Source reference: /affected/1/ranges

Prerequisites
Not stated in this record

Structured affected-version data

Original package identities and version rules from this source.

[
  {
    "ranges": [
      {
        "type": "SEMVER",
        "events": [
          {
            "introduced": "0"
          },
          {
            "fixed": "3.5.42"
          }
        ]
      }
    ],
    "package": {
      "name": "@vue/server-renderer",
      "purl": "pkg:npm/%40vue/server-renderer",
      "ecosystem": "npm"
    },
    "database_specific": {
      "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-g2v6-rqmx-r4w6/GHSA-g2v6-rqmx-r4w6.json"
    }
  },
  {
    "ranges": [
      {
        "type": "SEMVER",
        "events": [
          {
            "introduced": "3.6.0-rc.0"
          },
          {
            "fixed": "3.6.0-rc.6"
          }
        ]
      }
    ],
    "package": {
      "name": "@vue/server-renderer",
      "purl": "pkg:npm/%40vue/server-renderer",
      "ecosystem": "npm"
    },
    "database_specific": {
      "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-g2v6-rqmx-r4w6/GHSA-g2v6-rqmx-r4w6.json"
    }
  }
]

Publisher severity

high

Source reference: /database_specific/severity

Event
#85865 · Revision 1
Added to Heist Feed
Publisher date
Read the original announcement