SBOM生成ツールsyftのcatalogerのアップデートについて

以前、[別の記事](https://qiita.com/ichijouji/items/21a797029568594cb223)にて紹介したSBOM生成ツールの一つsyftのcatalogerに関する機能に少しアップデートがあったので、紹介したいと思います。

syftでは、catalogerという言語ごとに、どのファイルからパッケージのメタ情報を抽出するかに応じて、それぞれ異なるアルゴリズムが裏で動いています。

例えば、pythonの`requirements.txt`からパッケージのメタ情報を抽出する場合は、`python-package-cataloger`というcatalogerが動きます。

今回のアップデートで、
- syftのcatalogerの選択方法について仕様が変更された
- syftがcatalogerのリストを出力できるようになった

ため、本記事ではその詳細について解説をしていきます。

# Catalogerについて
catalogerのdefault機能では、スキャン対象がコンテナイメージ、ディレクトリ、またはその両方で動くかでcatalogerが分類されています。


コンテナイメージスキャン用のcatalogerはすでにパッケージがインストールされていることを前提として使用されます。
例えば、Pythonのパッケージを特定するために、syftは`site-packages`ディレクトリ下に存在する`egg`や`wheel`のメタデータをスキャンするために起動します。ここで、イメージ用のcatalogerではrequirements.txtなどからパッケージ情報は抽出できません。

ディレクトリスキャン用のcatalogerではすでにインストールされているパッケージ情報だけでなく、依存関係を宣言したファイル(requirements.txtやpom.xml)などもスキャン対象にしています。そのため、インストールしていないパッケージ情報についても検出を行います。


## Catalogerとそれに対応するtagの一覧
以下は、syftが動かしているすべてのcatalogerとそれが対応するtagの一覧です。こちらの一覧は`cataloger list`というコマンドで出力を行いました。こちらのコマンドについては後で解説を行います。このリストのtagを確認すれば、使用したいcatalogerがコンテナイメージ用かディレクトリ用か、それとも両方に対応しているかを確認することができます。

| CATALOGER | TAGS |
| --- | --- |
| alpm-db-cataloger  | alpm, archlinux, directory, image, installed, linux, os, package          |
| apk-db-cataloger                       | alpine, apk, directory, image, installed, linux, os, package              |
| binary-cataloger                       | binary, declared, directory, image, installed, package                    |
| cargo-auditable-binary-cataloger       | binary, image, installed, language, package, rust                         |
| cocoapods-cataloger                    | cocoapods, declared, directory, language, package, swift                  |
| conan-cataloger                        | conan, cpp, declared, directory, language, package                        |
| conan-info-cataloger                   | conan, cpp, image, installed, language, package                           |
| dart-pubspec-lock-cataloger            | dart, declared, directory, language, package                              |
| dotnet-deps-cataloger                  | c#, declared, directory, dotnet, language, package                        |
| dotnet-portable-executable-cataloger   | binary, c#, directory, dotnet, image, installed, language, package        |
| dpkg-db-cataloger                      | debian, directory, dpkg, image, installed, linux, os, package             |
| elixir-mix-lock-cataloger              | declared, directory, elixir, language, package                            |
| erlang-rebar-lock-cataloger            | declared, directory, erlang, language, package                            |
| github-action-workflow-usage-cataloger | declared, directory, github, github-actions, package                      |
| github-actions-usage-cataloger         | declared, directory, github, github-actions, package                      |
| go-module-binary-cataloger             | binary, directory, go, golang, gomod, image, installed, language, package |
| go-module-file-cataloger               | declared, directory, go, golang, gomod, language, package                 |
| graalvm-native-image-cataloger         | directory, image, installed, java, language, package                      |
| haskell-cataloger                      | cabal, declared, directory, hackage, haskell, language, package           |
| java-archive-cataloger                 | directory, image, installed, java, language, maven, package               |
| java-gradle-lockfile-cataloger         | declared, directory, gradle, java, language, package                      |
| java-pom-cataloger                     | declared, directory, java, language, maven, package                       |
| javascript-lock-cataloger              | declared, directory, javascript, language, node, npm, package             |
| javascript-package-cataloger           | image, installed, javascript, language, node, package                     |
| linux-kernel-cataloger                 | declared, directory, image, installed, kernel, linux, package             |
| nix-store-cataloger                    | directory, image, installed, language, nix, package                       |
| php-composer-installed-cataloger       | composer, image, installed, language, package, php                        |
| php-composer-lock-cataloger            | composer, declared, directory, language, package, php                     |
| portage-cataloger                      | directory, gentoo, image, installed, linux, os, package, portage          |
| python-installed-package-cataloger     | directory, image, installed, language, package, python                    |
| python-package-cataloger               | declared, directory, language, package, python                            |
| r-package-cataloger                    | image, installed, language, package, r                                    |
| rpm-archive-cataloger                  | declared, directory, linux, os, package, redhat, rpm                      |
| rpm-db-cataloger                       | directory, image, installed, linux, os, package, redhat, rpm              |
| ruby-gemfile-cataloger                 | declared, directory, gem, language, package, ruby                         |
| ruby-gemspec-cataloger                 | declared, directory, gem, gemspec, language, package, ruby                |
| ruby-installed-gemspec-cataloger       | gem, gemspec, image, installed, language, package, ruby                   |
| rust-cargo-lock-cataloger              | cargo, declared, directory, language, package, rust                       |
| sbom-cataloger                         | package, sbom                                                             |
| swift-package-manager-cataloger        | declared, directory, language, package, spm, swift                        |


## catalogerの選択について
catalogerを指定する方法は2種類あります。
- cataloger名による指定方法
    - cataloger名をフルネームで指定する
    - 例：`java-pom-cataloger` や `java-archive-cataloger`
- catalogerに関係するtagで指定する方法
    - catalogerにはそれぞれ関係するtagがついているため、それを指定する
    - 例：`java`、`image`


catalogerを明示的に指定する場合はCLI flag `--override-default-catalogers` を指定します。


また、CLI flag `--select-catalogers`を使用することで、デフォルトのcatalogerの集合に対して、
- 必要なcatalogerのみを選択する
- 新たにcatalogerを**追加**する
- 必要ないcatalogerを**削除**する


ことができます。

必要なcatalogerを選択するためには、単にタグを指定してください（例：`--select-catalogers TAG`）。
例えば、`--select-catalogers java,go` とすればdefaultのcatalogerからすべての `java` と `go`のcatalogerを選択します。  

catalogerを **追加** するには、cataloger名の前に `+` を付けてください (例 `--select-catalogers +NAME`)。  

catalogerを**削除**するには、cataloger名またはtagの前に `-` を付けてください（例：`--select-catalogers -NAME_OR_TAG`）。
These rules and the dynamic default cataloger sets approximates to the following logic:

```
image_catalogers = all_catalogers AND catalogers_tagged("image")

directory_catalogers = all_catalogers AND catalogers_tagged("directory")

default_catalogers = image_catalogers OR directory_catalogers

sub_selected_catalogers = default_catalogers INTERSECT catalogers_tagged(TAG) [ UNION sub_selected_catalogers ... ]

base_catalogers = default_catalogers OR sub_selected_catalogers

final_set = (base_catalogers SUBTRACT removed_catalogers) UNION added_catalogers
```


#### Examples
コンテナイメージをスキャンし、Pythonパッケージに関するcatalogerを起動させる場合
```bash
syft <some container image> --select-catalogers "python"
# この場合、以下のcatalogerのみが起動する
# - python-installed-package-cataloger
```
同様のコマンドでディレクトリに対してスキャンを実行する場合は、
```bash
syft <a directory> --select-catalogers "python"
# この場合、以下のcatalogerが起動
# - python-installed-package-cataloger
# - python-package-cataloger
```

catalogerを追加、削除する場合は
```bash
syft ... --select-catalogers "+sbom-cataloger"
```

```bash
syft ... --select-catalogers "-rpm"
```

defaultのcatalogerからgoのcatalogerのみを選択し、`sbom-cataloger`を追加する場合

```bash
syft <some container image> --select-catalogers "go,+sbom-cataloger"
# この場合以下のcatalogerが起動する
# - go-module-binary-cataloger
# - sbom-cataloger
```

`--override-default-catalogers`を使用すると、コンテナイメージ、ディレクトリをスキャンするかに関係なく、catalogerを指定することができます。

binaryを明示的にスキャンする場合は`--override-default-catalogers`を用いて
```bash
syft ... --override-default-catalogers "binary"
# この場合binary tagに関するcatalogerのみが起動する
# - binary-cataloger
# - cargo-auditable-binary-cataloger
# - dotnet-portable-executable-cataloger
# - go-module-binary-cataloger
```

```bash
syft ... --override-default-catalogers "go-module-binary-cataloger,go-module-file-cataloger"
```


## Catalogerリスト
前節では、catalogerの指定方法について解説しました。ここでは、そのcatalogerのリストをどう見るのか？について解説をします。

syftでは[v0.101.0](https://github.com/anchore/syft/releases/tag/v0.101.0)でcataloger listコマンドが追加されました。関連するIssueは[こちら](https://github.com/anchore/syft/pull/2366)をご覧ください。

### コマンドの使い方
それでは、`cataloger list`コマンドを利用してみましょう。コマンドのオプションは前節で説明したものと同じものが使用できます。

```bash
syft cataloger list  --select-catalogers "python, java, go" 
```
すると、以下のように、python, java, goに関連する、catalogerとそれに関連するtagを見ることができます。
```
Default selections:
  - "all"
Selected by expressions:
  - "python, java, go"
┌────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────┐
│ CATALOGER                          │ TAGS                                                                      │
├────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┤
│ go-module-binary-cataloger         │ binary, directory, go, golang, gomod, image, installed, language, package │
│ go-module-file-cataloger           │ declared, directory, go, golang, gomod, language, package                 │
│ graalvm-native-image-cataloger     │ directory, image, installed, java, language, package                      │
│ java-archive-cataloger             │ directory, image, installed, java, language, maven, package               │
│ java-gradle-lockfile-cataloger     │ declared, directory, gradle, java, language, package                      │
│ java-pom-cataloger                 │ declared, directory, java, language, maven, package                       │
│ python-installed-package-cataloger │ directory, image, installed, language, package, python                    │
│ python-package-cataloger           │ declared, directory, language, package, python                            │
└────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────┘
```

また、これらのcatalogerリストは、`-o`というオプションで、出力をjsonに指定することもできます：
```bash
syft cataloger list -o json | jq
```
ここで、最後に`jq`コマンドを使用しています。

出力結果：
```json
{
  "default": [
    "all"
  ],
  "selection": [],
  "catalogers": [
    {
      "name": "alpm-db-cataloger",
      "tags": [
        "alpm",
        "archlinux",
        "directory",
        "image",
        "installed",
        "linux",
        "os",
        "package"
      ]
    },
    {
      "name": "apk-db-cataloger",
      "tags": [
        "alpine",
        "apk",
        "directory",
        "image",
        "installed",
        "linux",
        "os",
        "package"
      ]
    },
    {
      "name": "binary-cataloger",
      "tags": [
        "binary",
        "declared",
        "directory",
        "image",
        "package"
      ]
    },
  ...
}
```





# 参考文献
- [syft v0.101.0 リリース情報](https://github.com/anchore/syft/releases/tag/v0.101.0)
- [syft cataloger listが提案されたissue](https://github.com/anchore/syft/pull/2366)
- [SBOM生成ツールSyftの使い方](https://qiita.com/ichijouji/items/21a797029568594cb223)