最近、オープンソースソフトウェアを利用したサプライチェイン攻撃が増加しています。そのため、プロジェクトの依存関係管理と脆弱性対策の重要性が高まっています。僕は開発プロジェクトで多くの依存パッケージを利用していますが、その依存関係の可視化と潜在的な脆弱性の検知が必要だと感じていました。
今回は、syftとgrypeという2つのツールを使って、JavaプロジェクトのPOM(pom.xml)とNode.jsプロジェクトのpackage.jsonから依存関係を可視化し、脆弱性を検知する方法を紹介します。
- はじめに
- syftとgrypeとは
- ローカルでの環境構築
- Javaプロジェクト(pom.xml)の解析
- Node.jsプロジェクト(package.json)の解析
- 脆弱性検知の実例
- 実践的な使用方法
- まとめ
はじめに
なぜSBOMが重要か
ソフトウェアの依存関係は年々複雑化しています。直接的な依存だけでなく、間接的な依存も含めると、一つのプロジェクトで数百から数千のパッケージを利用することも珍しくありません。この状況で、以下のような課題が発生しています:
- 利用しているパッケージの全容把握が困難
- 脆弱性が発見された際の影響範囲の特定に時間がかかる
- ライセンスコンプライアンスの確認が複雑
これらの課題に対応するため、SBOMが注目されています。SBOMとは「Software Bill of Materials」の略で、ソフトウェアの構成要素を一覧化したものです。食品の原材料表示のように、ソフトウェアの「材料」を明確にすることで、セキュリティとコンプライアンスの管理を効率化できます。
本記事で使用するツール
今回使用する2つのツールの役割は以下の通りです:
- syft:依存関係の可視化とSBOMの生成
- grype:生成されたSBOMを基にした脆弱性スキャン
これらのツールを組み合わせることで、継続的なセキュリティ管理が可能になります。
syftとgrypeとは
まず、syftは、Anchore社が開発したソフトウェアコンポーネントの依存関係を分析し、SBOMを生成するツールです。以下のような特徴があります:
- 多様なパッケージマネージャーのサポート(Maven、npm、etc)
- 複数のフォーマットでのSBOM生成
- JSON:プログラムでの処理に最適
- SPDX:標準化されたフォーマットで、ツール間の互換性が高い
- CycloneDX:セキュリティ分析に特化した詳細な情報を含む
- コンテナイメージの解析にも対応
- 実行が高速
また、grypeもAnchore社が開発した脆弱性スキャンツールで、syftと連携して動作します。主な特徴は以下の通りです:
- 最新の脆弱性データベースを活用
- National Vulnerability Database (NVD)
- GitHub Security Advisories
- その他、各言語エコシステムの脆弱性データベース
- 重要度に基づく脆弱性の分類(Critical、High、Medium、Low)
- 豊富な出力フォーマット
- 高速な脆弱性スキャン
ローカルでの環境構築
まず、syftとgrypeをローカルPCにインストールします。
# syftのインストール curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin # grypeのインストール curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
インストールが完了したら、バージョンを確認します。
$ syft version Application: syft Version: 1.20.0 BuildDate: 2025-02-21T20:31:16Z GitCommit: Homebrew GitDescription: [not provided] Platform: darwin/arm64 GoVersion: go1.24.0 Compiler: gc $ grype version Application: grype Version: 0.87.0 BuildDate: 2025-01-22T20:31:08Z GitCommit: Homebrew GitDescription: [not provided] Platform: darwin/arm64 GoVersion: go1.23.5 Compiler: gc Syft Version: v1.19.0 Supported DB Schema: 5
Javaプロジェクト(pom.xml)の解析
まず、手元のJavaプロジェクトの依存関係を解析してみましょう。以下は、nablarch-coreプロジェクトを例にしています。
cd /Users/kiririmode/src/github.com/nablarch/nablarch-core syft pom.xml
このコマンドを実行すると、以下のような形式でプロジェクトの依存関係が表示されます。
✔ Indexed file system pom.xml ✔ Cataloged contents edd2b8dd54f1c8190d6e78966c59d78d0380cc917d896769b347f2067e547caa ├── ✔ Packages [4 packages] ├── ✔ File digests [1 files] ├── ✔ File metadata [1 locations] └── ✔ Executables [0 executables] NAME VERSION TYPE json-path-assert 2.4.0 java-archive mockito-core UNKNOWN java-archive nablarch-core 2.2.0 java-archive nablarch-slf4j-adaptor UNKNOWN java-archive
より詳細な情報をSBOMで出力します。例えば、SPDX形式で出力し、ソフトウェアコンポーネント(packages
)の一部情報を表示します。
$ syft -q pom.xml -o spdx-json | jq -r '.packages[] | [ .name, .SPDXID, .versionInfo ] | @csv ' "json-path-assert","SPDXRef-Package-java-archive-json-path-assert-b9f8b5dad3d5ab1d","2.4.0" "mockito-core","SPDXRef-Package-java-archive-mockito-core-b1a15a6e93dab83d","UNKNOWN" "nablarch-core","SPDXRef-Package-java-archive-nablarch-core-20c66a65499e2f90","2.2.0" "nablarch-slf4j-adaptor","SPDXRef-Package-java-archive-nablarch-slf4j-adaptor-f3cc12b003c1b302","UNKNOWN" "pom.xml","SPDXRef-DocumentRoot-File-pom.xml","sha256:3b110735d9a8c1b259d3b123e286e4ce48c876012e6e4d2fd8c9020a65c84e12"
親POMからの依存関係の解決
一部の依存関係でUNKNOWN
というバージョンが表示されています。これは、syftが親POMを参照できていないことが原因です。例えば、mockito-coreの依存定義を見てみましょう。
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency>
バージョンが指定されていないのは、親POMで管理されているためです。実際にmvn dependency:tree
で確認すると、以下のようにバージョンが解決されています。
mvn dependency:tree | sed -n '/dependency/,/----/p' [INFO] --- dependency:3.7.0:tree (default-cli) @ nablarch-core --- [INFO] com.nablarch.framework:nablarch-core:jar:2.2.0 [INFO] +- org.mockito:mockito-core:jar:5.3.0:test [INFO] | +- net.bytebuddy:byte-buddy:jar:1.14.4:test [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.14.4:test [INFO] | \- org.objenesis:objenesis:jar:3.3:test [INFO] +- com.jayway.jsonpath:json-path-assert:jar:2.4.0:test [INFO] | +- com.jayway.jsonpath:json-path:jar:2.4.0:test [INFO] | | \- net.minidev:json-smart:jar:2.3:test [INFO] | | \- net.minidev:accessors-smart:jar:1.2:test [INFO] | | \- org.ow2.asm:asm:jar:5.0.4:test [INFO] | +- org.hamcrest:hamcrest-core:jar:1.3:test [INFO] | +- org.hamcrest:hamcrest-library:jar:1.3:test [INFO] | \- org.slf4j:slf4j-api:jar:1.7.25:test [INFO] +- com.nablarch.integration:nablarch-slf4j-adaptor:jar:2.1.0:test [INFO] +- junit:junit:jar:4.13.1:test [INFO] +- org.hamcrest:hamcrest-all:jar:1.3:test [INFO] \- org.jacoco:org.jacoco.agent:jar:runtime:0.8.8:test [INFO] ------------------------------------------------------------------------
syftでもこれらのバージョンを正しく解決するには、2つの方法があります:
ローカルのMaven Cacheを参照する方法
SYFT_JAVA_USE_MAVEN_LOCAL_REPOSITORY=true syft pom.xml
ネットワーク経由でPOMを取得する方法
SYFT_JAVA_USE_NETWORK=true syft pom.xml
これらの環境変数は公式ドキュメントには記載されていませんが、GitHubのIssueで確認できます:
SYFT_JAVA_USE_MAVEN_LOCAL_REPOSITORY
:Issue #3207SYFT_JAVA_USE_NETWORK
:PR #2769
どちらの方法を使っても、以下のように正しいバージョンが表示されます:
$ diff -u <(syft -q pom.xml) <(SYFT_JAVA_USE_NETWORK=true syft -q pom.xml) --- /dev/fd/11 2025-03-03 14:25:27 +++ /dev/fd/12 2025-03-03 14:25:28 @@ -1,5 +1,5 @@ NAME VERSION TYPE json-path-assert 2.4.0 java-archive -mockito-core UNKNOWN java-archive +mockito-core 5.3.0 java-archive nablarch-core 2.2.0 java-archive -nablarch-slf4j-adaptor UNKNOWN java-archive +nablarch-slf4j-adaptor 2.1.0 java-archive
脆弱性の検知
続いて、grypeを使って脆弱性をスキャンします。まず、syftでSBOMを生成し、それをgrypeに渡します。
$ SYFT_JAVA_USE_NETWORK=true syft -q pom.xml -o cyclonedx-json=nablarch-core.cyclonedx.json $ grype -q nablarch-core.cyclonedx.json No vulnerabilities found
nablarch-coreには脆弱性は存在していないようです。これは良いニュースですが、実際のプロジェクトでは脆弱性が見つかることも多いでしょう。
Node.jsプロジェクト(package.json)の解析
Node.jsプロジェクトの解析では、まずpackage.jsonを試してみましょう。
$ cd /Users/kiririmode/src/github.com/Fintan-contents/promptis $ syft -q package.json No packages discovered
パッケージが検出されません。パッケージが検出されない理由はおそらく、JavaScriptパッケージを解析するためのカタログが適切に選択されていないためです。JavaScriptパッケージカタログを明示的に指定してみましょう。
$ syft -q package-lock.json --select-catalogers '+javascript-package-cataloger' | head NAME VERSION TYPE @tootallnate/quickjs-emscripten 0.23.0 npm agent-base 7.1.1 npm ast-types 0.13.4 npm balanced-match 1.0.2 npm basic-ftp 5.0.5 npm brace-expansion 2.0.1 npm data-uri-to-buffer 6.0.2 npm debug 4.3.7 npm degenerator 5.0.1 npm
catalogersの仕組み
syftは、ファイルの種類や内容に応じて適切なカタログ(解析プラグイン)を自動選択します。カタログには以下のような種類があります:
- 言語固有のカタログ(Java、Node.js、Python等)
- パッケージマネージャー固有のカタログ(npm、Maven、pip等)
- コンテナ関連のカタログ(Docker、OCI等)
--select-catalogers
オプションを使うと、特定のカタログを明示的に指定できます。利用可能なカタログは以下のコマンドで確認できます:
$ syft cataloger list -o json | jq -r '.catalogers[].name' alpm-db-cataloger apk-db-cataloger binary-classifier-cataloger bitnami-cataloger cargo-auditable-binary-cataloger ...(省略)...
脆弱性の検知
package-lock.jsonの依存関係に対して脆弱性スキャンを実行します:
$ syft -q package-lock.json --select-catalogers '+javascript-package-cataloger' -o cyclonedx-json=promptis.cyclonedx.json $ grype -q promptis.cyclonedx.json No vulnerabilities found
このプロジェクトでも脆弱性は見つかりませんでした。では、実際に脆弱性がある例を見てみましょう。
脆弱性検知の実例
このBlogプロジェクトのファイルシステム上のファイルにある依存パッケージをスキャンしてみます:
$ syft . -qo cyclonedx-json | grype -q NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY black 23.12.1 24.3.0 python GHSA-fj7x-q9j7-g6q6 Medium jinja2 3.1.2 3.1.5 python GHSA-q2x7-8rv6-6q7h Medium jinja2 3.1.2 3.1.4 python GHSA-h75v-3vvj-5mfj Medium jinja2 3.1.2 3.1.3 python GHSA-h5c8-rqwp-cp95 Medium jinja2 3.1.2 3.1.5 python GHSA-gmj6-6f8f-6699 Medium werkzeug 3.0.1 3.0.3 python GHSA-2g68-c3qc-8985 High werkzeug 3.0.1 3.0.6 python GHSA-q34m-jh98-gwm2 Medium werkzeug 3.0.1 3.0.6 python GHSA-f9vj-2wh5-fj8j Medium
ここでは複数の脆弱性が検出されました。特に注目すべき点は:
- 重要度の違い(High vs Medium)
- 同一パッケージでの複数の脆弱性(jinja2)
実践的な使用方法
grypeは脆弱性の重要度を以下の4段階で表示します:
- Critical:直ちに対応が必要
- High:優先的に対応が必要
- Medium:計画的に対応
- Low:状況に応じて対応
特定の重要度以上の脆弱性が存在する場合にコマンドを失敗させることもできます:
$ syft . -qo cyclonedx-json | grype -q --fail-on high > /dev/null $ echo $? 1
この機能は、CIパイプラインでの品質ゲートとして活用できます。
まとめ
syftとgrypeを活用することで、以下のメリットが得られます:
- プロジェクトの依存関係の可視化が容易に
- 潜在的な脆弱性を素早く発見可能
- 脆弱性の重要度に基づく対応の優先順位付けが可能
- 定期的なセキュリティチェックの自動化
JavaとNode.jsのプロジェクトでは、数多くの依存パッケージを使用することが一般的です。これらのツールを活用して、プロジェクトのセキュリティ管理を効率的に行いましょう。