運用記録: 依存パッケージの日常メンテナンス SVGO(推移的依存)脆弱性への対応
事象
package.jsonに直接書いていないパッケージでの警告。
SVGO: removeScripts allows executable links through namespace and control-character bypasses #2
脆弱性の確認
| 項目 | 内容 |
|---|---|
| 脆弱性ID | GHSA-w27v-7q3p-w38r(CVE-2026-84370) |
| 重大度 | High(CVSS 8.2) |
| 対象パッケージ | svgo(SVGファイルの最適化ツール) |
| 影響を受けるバージョン | 4.0.0以上4.1.0未満 |
| 修正バージョン | 4.1.0以降 |
| 混入経路 | astro 7.3.2 → svgo 4.0.2 |
推移的依存関係とは何か
package.jsonのdependenciesにはastroやvueは書いてありますが、svgoという文字列はどこにも登場しません。
svgoの脆弱性が検出されたのは、svgoがAstro自身が内部で使っているパッケージだからです。
astro 7.3.2
└── svgo 4.0.2 (Astroがビルド時のSVG最適化に使用)
直接インストールしていないが、依存先(さらにその依存先)を辿った先に脆弱なパッケージが存在する、という状態を**推移的依存関係(transitive dependency)**と呼びます。
以前、npm ls astroの結果がdedupedという表示を含んでいたことを確認しましたが、この「依存関係ツリー」の先に、こうした形で意図せず脆弱なパッケージが含まれることがあります。
自サイトでのリスク評価
removeScriptsは、SVGファイルから<script>タグなど実行可能なコードを除去する、SVGOのオプトイン(明示的に有効化しないと動かない)機能です。今回の脆弱性は、名前空間プレフィックス付きのSVGアンカー(<svg:a>のような書き方)や、URLスキーム内にASCIIタブ・改行文字を混入させる手法によって、javascript:のような実行可能なリンクをこのチェックがすり抜けてしまう、というものでした。悪用されると、信頼できないSVGファイルを最適化した際にXSS(クロスサイトスクリプティング)につながる可能性があります。
CVSS 8.2(High)という数字だけを見ると身構えますが、自サイトの実装を確認すると、
- SVGOは
astro:assetsの画像最適化処理の一部として、間接的に入っているだけで、自分たちのコードから直接呼び出してはいない removeScriptsというオプトイン機能を、明示的に有効化する実装は一切していない- 扱っている画像は、
profile-avatar.jpgやvue-logo.pngなど自分で用意した信頼できるファイルのみで、外部から任意のSVGを受け付けて最適化する経路が存在しない
という状態でした。攻撃の前提条件(removeScriptsを有効化した上で、信頼できないSVGを処理させる)がこのサイトには存在しないため、実害のリスクは低いと判断しました。
対応方法
直接の依存ではないため、更新の仕方も少し異なります。
npm update svgo
npm install svgo@latestのようにバージョンを直接指定するのではなく、npm updateを使いました。理由は、svgoのバージョンは最終的に**astroが許容する範囲内**でしか意味を持たないためです。直接指定してしまうと、astroが期待するバージョン範囲から外れてしまい依存関係が壊れる可能性があります。npm updateであれば、依存ツリー全体の制約を踏まえた上で、許容範囲内の最新版に引き上げてくれます。
更新後は、通常の確認手順を実行しました。
npm ls svgo
npm run build
npm run buildが問題なく通ることで、SVG最適化処理を含むビルドパイプライン自体に支障がないことも確認できています。
直接依存と推移的依存で、対応の考え方はどう変わるか
今回の一連の対応を振り返ると、依存パッケージの脆弱性対応は、大きく次の観点で対応する必要があります。
| 観点 | 直接依存(Astro、Vue) | 推移的依存(SVGO) |
|---|---|---|
package.jsonへの記載 |
あり | なし(間接的に含まれる) |
| バージョン更新の指定方法 | npm install <パッケージ>@latest |
npm update <パッケージ>(依存元の制約に従う) |
| リスク評価のポイント | 自分のコードがその機能を直接使っているか | 中間の依存先(今回はastro)が、脆弱な機能を有効化する形で使っているか |
いずれの場合も、「重大度のラベルだけで判断せず、自サイトの実装が実際にその攻撃経路を持っているかを確認する」という基本方針は変わりません。