SBOM生成ツールSyftの使い方

近年、ソフトウェアの部品構成を一覧化したSBOM (Software Bill of Materials)が注目されています。そこで、この記事では、SBOM生成ツールの一つであるsyftを使い、実際にSBOMを生成してみます。ここでは，syftの使い方のみを解説しているため、SBOMとは何か？については解説がされていません。
# 目次
- [Syftのインストール方法](#syftのインストール方法)
    - [0. 解凍用の一時的なファイルの作成](#0-解凍用の一時的なファイルの作成)
    - [1. syftのバイナリファイルの取得](#1-syftのバイナリファイルの取得)
    - [2. syftのバイナリファイルの解凍](#2-syftのバイナリファイルの解凍)
    - [3. syftのpathを通す](#3-syftのpathを通す)
- [syftでSBOMを生成する](#syftでsbomを生成する)
- [参考文献](#参考文献)

# Syftのインストール方法
まず、syftをインストールしていきます。今回はバイナリを解凍しインストールを行います。
### 0. 解凍用の一時的なファイルの作成
まず、syftを解凍するための一時的なディレクトリを作成し、そのディレクトリへ移動します：
```bash
$ mkdir /tmp/syft/ && cd /tmp/syft/
```

### 1. syftのバイナリファイルの取得
Syftの[リリース情報](https://github.com/anchore/syft/releases)から、アーカイブファイルを取得します。
今回は、[syft-v0.91.0](https://github.com/anchore/syft/releases/download/v0.91.0/syft_0.91.0_linux_amd64.tar.gz)から、バージョン0.91.0のsyftのアーカイブである`syft_0.91.0_linux_amd64.tar.gz`を取得します：
```bash
$ wget https://github.com/anchore/syft/releases/download/v0.91.0/syft_0.91.0_linux_amd64.tar.gz
```

### 2. syftのバイナリファイルの解凍
そして、解凍行います：
```bash
$ tar xvf syft_0.91.0_linux_amd64.tar.gz
CHANGELOG.md
LICENSE
README.md
syft
```

### 3. syftのpathを通す
最後にsyftを実行するために適切な場所へsyftを移動します：
```bash
$ sudo mv syft /usr/local/bin
```
今回は、`/usr/local/bin`へsyftを配置しました。
一時的なディレクトリ`/tmp/syft`は必要ないので削除してしまってもよいです：
```bash
$ cd ~
$ rm -rf /tmp/syft/
```

syftの場所とバージョンを確認し、以下のように出力されていれば、syftのインストールは成功です：

```bash
$ which syft
/usr/local/bin/syft
```

```bash
$ syft version
Application:     syft
Version:         0.91.0
BuildDate:       2023-09-20T19:42:04Z
GitCommit:       b7fa75d7f82a6816d307805ac07e6965c799e938
GitDescription:  v0.91.0
Platform:        linux/amd64
GoVersion:       go1.21.1
Compiler:        gc
```


# syftでSBOMを生成する
ここでは、コンテナイメージをsyftでスキャンし、SBOMを生成します。コンテナイメージについては、各自`docker build`やDockerHubから`docker pull`し、用意してください。今回は、以下の`Dockerfile`を用いてコンテナイメージをビルドし用意しました。
```Dockerfile
FROM python:latest

RUN apt-get update && \
    pip install --upgrade pip

COPY ./requirements.txt .

RUN pip install -r requirements.txt
```
ここで、インストールしたライブラリは以下です：
```txt:requirements.txt
flask
matplotlib
networkx
numpy
pandas
scipy
sympy
```
今回、このコンテナイメージには`python-test`と命名しました。このコンテナイメージをビルドするためのコマンドは以下です：
```bash
docker build -t python-test <Dockerfileがあるディレクトリ>
```

次にビルドしたコンテナイメージをスキャンしていきます。生成したSBOMを保存するためのディレクトリを作成します。
```bash
mkdir syft-test && cd syft-test
```
以下のコマンドでコンテナイメージをスキャンし、cycloneDX形式でSBOMを生成し、`syft-python.json`へ出力してみます：
```bash
syft python-test:latest -o cyclonedx-json > syft-python.json
```
すると、467個のパッケージを検出できたと出力されました：
```bash
✔ Cataloged packages              [467 packages]
```
実際に出力されるSBOMファイル`syft-python.json`は約5万行の膨大なデータが記述されています。そのため、今回は抜粋して、掲載します。また、SBOMの中身については、cyclonedx形式、SPDX形式の公式ドキュメントを読むことで、各フィールドが何を表しているかがわかるため、別の記事で解説する予定です。
```json
{
  "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
  "bomFormat": "CycloneDX",
  "specVersion": "1.4",
  "serialNumber": "urn:uuid:10266051-6483-423d-957e-7b63cfe90d65",
  "version": 1,
  "metadata": {
    "timestamp": "2023-10-08T12:51:37+09:00",
    "tools": [
      {
        "vendor": "anchore",
        "name": "syft",
        "version": "0.91.0"
      }
    ],
    "component": {
      "bom-ref": "45c383e99a44e01f",
      "type": "container",
      "name": "python-test",
      "version": "sha256:9012d7e7c9b45d12616fb4e91f246d5973a1ca5c5bac25fa9bde6079fdb96b3a"
    }
  },
  "components": [
    {
      "bom-ref": "pkg:pypi/flask@3.0.0?package-id=af4401cd7bfffcc5",
      "type": "library",
      "name": "Flask",
      "version": "3.0.0",
      "cpe": "cpe:2.3:a:palletsprojects:flask:3.0.0:*:*:*:*:*:*:*",
      "purl": "pkg:pypi/Flask@3.0.0",
      "properties": [
        {
          "name": "syft:package:foundBy",
          "value": "python-package-cataloger"
        },
        {
          "name": "syft:package:language",
          "value": "python"
        },
        {
          "name": "syft:package:metadataType",
          "value": "PythonPackageMetadata"
        },
        {
          "name": "syft:package:type",
          "value": "python"
        },
        {
          "name": "syft:location:0:layerID",
          "value": "sha256:842b060658edc1e2aa04162822d057f4d04bf4368fd09fc42fe38164dc9f771a"
        },
        {
          "name": "syft:location:0:path",
          "value": "/usr/local/lib/python3.12/site-packages/flask-3.0.0.dist-info/METADATA"
        },
        {
          "name": "syft:location:1:layerID",
          "value": "sha256:842b060658edc1e2aa04162822d057f4d04bf4368fd09fc42fe38164dc9f771a"
        },
        {
          "name": "syft:location:1:path",
          "value": "/usr/local/lib/python3.12/site-packages/flask-3.0.0.dist-info/RECORD"
        }
      ]
    },
    ..............
    ..............
  ]
}
```
syftで生成されるSBOMには、ソフトウェアの依存関係(ソフトウェアの繋がり)が記述されている`dependencies`という項目が出力されませんでした。これでは、あるソフトウェアがどのソフトウェアを用いて構築されているかがわかりません。今後、追加されることを期待したいです。

(ここからは、個人的な意見なので、読み飛ばしてもらって大丈夫です)
また、多くの日本企業が海外の動向を察して、SBOM導入に力をいれているのではないのかと思います。しかし、実際にSBOMを導入する際に多くの課題があると思います。例えば、以下の論文[「S3C2 Summit 2023-06: Government Secure Supply Chain Summit」](https://arxiv.org/abs/2308.06850)で、SBOMの出力がツール間で一貫していないこと、組み込みソフトウェアツールのサポートは遅れていること、SBOMへの署名の必要性など様々な課題が挙げられています。実際に出力を見た方の中に気づいた方もいると思いますが、SBOMの出力内容をよく理解していないと出力しただけで終わってしまいます。結局SBOMを使って何をするかが曖昧になっている、つまりSBOM導入がゴールになってしまっている気がします。SBOMにとらわれすぎるのは避け、SBOMで何ができるのかを吟味し、導入するべきかを慎重に判断していく必要があると思います。

SBOMはまだ発展途上の技術であり、個人的には興味深く面白く技術だと考えています。今後、syftを含めた、多くのSBOMツールが精錬されていってほしいなと思っています。

次回は、syftで生成したSBOMをgrypeというツールでスキャンし、脆弱性の検知を行います。

# 参考文献
- [syft公式ドキュメント](https://github.com/anchore/syft)
- [「S3C2 Summit 2023-06: Government Secure Supply Chain Summit」](https://arxiv.org/abs/2308.06850)
- [cycloneDX公式ドキュメント](https://cyclonedx.org/docs/1.5/json/)