Nuxt のサイトで npm audit を実行すると、vue と @vue/server-renderer に「high」が出る。指摘は GHSA-g2v6-rqmx-r4w6 で、vuejs/core のリポジトリでのアドバイザリの公開は8月31日、GitHub Advisory Database への登録は10月5日(いずれも UTC。後者は日本時間では6日朝)です。CMS で編集者が決めた項目名を data-* 属性としてカードに出していたり、APIの応答をそのまま v-bind で要素に渡していたりするサーバーサイドレンダリング(SSR)のサイトでは、ページを開いただけで JavaScript が動く XSS になり得ます(想定の場面)。
一方で、修正版の 3.5.42 は8月27日に npm へ公開済みで、その後に依存を更新したサイトはすでに直っています。本記事では、当たるコードの条件、版の確かめ方、Nuxt で更新したつもりでも古い版が残る例を整理します。後半の結果は、編集部の検証環境で Vue と Nuxt を実際に動かして確かめたものです。
当たるのは「属性の名前」を外から受け取るSSR
Vue の SSR は、<div v-bind="obj"> のようにオブジェクトを属性へ展開するとき、@vue/server-renderer の ssrRenderAttrs() で HTML の文字列を組み立てます。属性の値は毎回エスケープされます。ところが属性の名前は、使えない文字の一覧(> / = " '、タブ、改行(LF)、改ページ、空白)に当たらないかを見るだけで、そのまま出力されていました。
この一覧に、復帰の文字(CR、\r)が入っていませんでした。アドバイザリによれば、ブラウザーは HTML を読む最初の段階で単独の CR を LF に置き換えます。LF は属性の名前の区切りになるため、Vue が1つの名前として出した x\rautofocus\ronfocus が、ブラウザーでは x・autofocus・onfocus の3つの属性として解釈されます。値の側にスクリプトを置けば、読み込んだ時点で実行されます。
アドバイザリが挙げる条件は次のとおりです。
- 外部から来たキー(値だけでなく名前)を持つオブジェクトを、
v-bind="object"かそれに相当する形で SSR の要素に渡している - 例として、権限の弱い利用者が項目名を設定できる CMS やフォームビルダー、別の場所で検証した設定オブジェクトを要素に広げるコンポーネント、データベースのレコード・APIの応答・クエリ文字列を変換したオブジェクトを属性にする処理が挙げられている
- ブラウザー側の描画(クライアントの Vue)は
setAttribute()が名前を検査するため、報告者はこの問題に到達できなかったとしている。SSR に限った問題
値だけを外から受け取る通常の使い方(:title="post.title" など)は、もともとエスケープされるため対象外です。重大度は HIGH(CVSS 3.1 のベクトルは AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N)です。
テンプレートの v-bind だけではない
編集部の検証環境(Linux、Node.js v22.22.0)で、無害なキー data-a\rdata-injected を3通りの書き方で SSR に渡し、出力を HTML5 のパーサー parse5 で読みました。
@vue/server-renderer 3.5.41
<div v-bind="attrs"> をSSR用にコンパイル → ssrRenderAttrs を呼ぶコードになる
ssrRenderAttrs(attrs) → " id=\"card\" data-a\rdata-injected=\"yes\""
parse5 の解釈 → id="card" data-a="" data-injected="yes"
renderToString(h('div', attrs)) → 同じく data-a と data-injected に分かれた
<Card v-bind="attrs">(ルート要素への引き継ぎ) → class, data-a, data-injected に分かれた
@vue/server-renderer 3.5.42 / 3.5.44
3つとも出力は id="card"(または class="card")だけ。危険なキーは出力されない
テンプレートの v-bind に加えて、描画関数の h() と、props として宣言していない属性が子のルート要素へ引き継がれる場合も、同じ経路を通りました。コードを探すときは、この3つの形を対象にします。
自社のコードで探すところ
ここからは、アドバイザリの条件をリポジトリのどこで確かめるかについての編集部の整理です。
.vueファイルで、引数なしのv-bind="..."を検索する。渡しているオブジェクトのキーが、CMS・API・データベース・URL のクエリから来ていないかを見るh(やrender()で、第2引数に外部のデータをそのまま渡していないかを見る- 子コンポーネントに
v-bind="..."でまとめて渡していないかを見る。子が props を宣言していなければ、キーはルート要素の属性になる Object.fromEntriesや...spreadで外部の項目名から属性のオブジェクトを作る補助関数がないかを見る
キーを外から受け取る設計が必要な場合は、更新に加えて、data- で始まり英数字とハイフンだけからなる名前のように、許可する形を決めて絞る方法が考えられます。これは編集部の提案で、アドバイザリが求めている対策は更新です。
修正版は3.5.42、公開は8月27日
| 系列 | 対象の版 | 修正版 | npm の公開日時(UTC) |
|---|---|---|---|
| 3.5 系 | 3.5.41 以前(範囲の下限は 0) | 3.5.42 | 2026年8月27日 |
| 3.6 の RC | 3.6.0-rc.0〜rc.5 | 3.6.0-rc.6 | 2026年8月28日 |
修正は vuejs/core の PR #15266(コミット a2b40db、8月11日)で、packages/shared/src/domAttrConfig.ts の一覧に \u000d を1文字加える変更です。3.5.42 の変更履歴にも「server-renderer: reject CR in attribute names」とあります。vuejs/core のリポジトリのアドバイザリは8月31日(UTC)に公開されましたが、GitHub Advisory Database に載ったのは10月5日(UTC)で、npm audit が指摘を始めたのはそれ以降です。
10月21日時点の npm の latest は 3.5.44(10月13日公開)、rc は 3.6.0-rc.12 です。3.5.42 以降の版に上げれば修正が入ります。
確認しておきたいのは、修正のある場所です。危険な名前を判定する関数 isSSRSafeAttrName() は @vue/shared にあり、@vue/server-renderer はそれを読み込んで使います。npm では両者の版が完全一致で指定されているため通常はそろいますが、検証で overrides を使って @vue/server-renderer 3.5.42 と @vue/shared 3.5.41 を組み合わせたところ、属性は分かれ、npm audit の指摘は0件でした。依存の固定や上書きをしているプロジェクトでは、@vue/shared の版も確かめます。
Nuxtの古いロックファイルでは更新が届かない
Nuxt 4.6.2(10月16日公開)は vue を ^3.5.44 で要求するため、4.6.2 に上げれば Vue も上がります。一方、Nuxt 3 系の最新 3.21.11(8月5日公開)の要求は ^3.5.40 で、3.5.41 のままでも条件を満たします。
Nuxt 3.21.11 と Vue 3.5.41 で作ったロックファイルを使い、手元で次の順に試しました(npm 10.9.4。Nuxt の依存の解決で npm がエラーを出したため --legacy-peer-deps を付けています)。
| 操作 | ロックファイルの vue と @vue/server-renderer | npm audit |
|---|---|---|
package.json の vue を ^3.5.41 にして npm install | 3.5.41 のまま | high(GHSA-g2v6) |
npm update vue | 3.5.44 | 指摘なし |
| 新規に Nuxt 4.6.2 で作成 | 3.5.44 | ― |
^ の範囲に新しい版が含まれていても、npm install はロックファイルの版を保ちます。Nuxt 3 系のサイトは npm update vue(pnpm や yarn ではそれぞれの update)でロックファイルを更新し、差分に @vue/server-renderer と @vue/shared の 3.5.42 以降が入っていることを確かめます。
ビルド結果の package.json だけを見ない
同じ Nuxt 3.21.11 の最小のアプリ(URL のクエリを属性に展開する、検証のためだけの危険な書き方)をビルドし、.output/server を起動して確かめました。

更新前のビルドでは、.output/server/package.json に "@vue/shared": "3.5.44" と書かれ、.output/server/node_modules/@vue/shared も 3.5.44 を指していました。ところが @vue/server-renderer は 3.5.41 で、その下に別の node_modules/@vue/shared があり、ビルド結果には 3.5.41 の @vue/shared も同梱されていました。/?data-a%0Ddata-injected=yes を開くと、応答の <div> は parse5 で id・data-a・data-injected の3つに分かれました。
npm update vue のあとに再ビルドすると、.output/server の Vue の関連パッケージはすべて 3.5.44 になり、同じ要求の応答は <div id="card"> だけになりました。サーバーのログには unsafe attribute name と Skipped rendering unsafe attribute name が出ました(NODE_ENV=production で単体の Vue を動かした場合も同じ表示)。
ビルド結果で確かめるときは、.output/server/package.json の @vue/shared の行ではなく、@vue/server-renderer の版と、その下に古い @vue/shared が残っていないかを見ます。
更新後に確かめること
- 本番の成果物で
@vue/server-rendererが 3.5.42 以上であること(npm ls @vue/server-renderer @vue/shared、Nuxt は.output/server/node_modulesも) - ステージングで、属性に展開しているページへキーに
%0Dを含む値を送り、応答の属性が増えないこと。クエリ以外から属性名を受け取る場合は、CMS の検証用の項目などで試す - 更新後のサーバーのログに
unsafe attribute nameが出たら、属性名に不正な文字を含むデータがどこから来たかを調べる(編集部の提案)
依存の更新を誰がどの周期で行うかの決め方は、React2Shellの放置サイトと保守契約の見直しで整理しています。Nuxt 4.6 への更新で確かめる点はNuxt 4.6のnuxt/server 書き換え前に確かめる点、同じ時期の Next.js のアドバイザリはNext.js 10月の脆弱性9件にまとめています。
確認した範囲
2026年10月21日に、github/advisory-database の GHSA-g2v6-rqmx-r4w6 のJSON、vuejs/core のリポジトリ(修正コミット a2b40db、タグ v3.5.41〜v3.5.44 と v3.6.0-rc.5・rc.6、CHANGELOG.md)、npm registry の vue・@vue/server-renderer・@vue/shared・nuxt の公開日時と依存範囲を直接取得して確認しました(資料調査)。あわせて、Linux・Node.js v22.22.0・npm 10.9.4 で、@vue/server-renderer 3.5.41・3.5.42・3.5.44 と、3.5.42 に @vue/shared 3.5.41 を組み合わせた構成の出力を parse5 で解析し、Nuxt 3.21.11 のロックファイルの更新と
npm audit、最小のアプリのビルドと起動を試しました(実機検証)。実際のブラウザーでのスクリプトの実行、Vue 2(vue-server-renderer)、pnpm・yarn での更新、Nuxt 3.21.11 以外の版のビルド結果、クライアント側の描画で到達しないことは確かめていません。NVD と vuejs.org は開いていません。公開前の10月21日には、GitHub の API で vuejs/core のアドバイザリ本体(8月31日公開)を確かめ、10月5日より後に Vue の追加のアドバイザリや Nuxt 3 系の新しい版が出ていないことも確認しました。
Vue・Nuxt で作ったサイトの依存の更新や、SSR のコードの見直しは、開発・AI・自動化のご相談からお問い合わせください。
参考資料
- GHSA-g2v6-rqmx-r4w6: @vue/server-renderer: XSS via missing CR in attribute-name blacklist(vuejs/core)
- GHSA-g2v6-rqmx-r4w6(github/advisory-database)
- vuejs/core — fix(server-renderer): reject CR in attribute names (#15266)
- vuejs/core — CHANGELOG.md(3.5.42)
- vuejs/core — packages/shared/src/domAttrConfig.ts(v3.5.42)
- @vue/server-renderer — npm registry
- nuxt — npm registry









