電通総研 テックブログ

電通総研が運営する技術ブログ

GitHub Actions で Amazon Inspector を利用した脆弱性スキャンを行う

こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。

2024/6に Amazon Inspector が GitHub Actions でのコンテナイメージスキャンをサポートしたとのアナウンスがありました。コンテナイメージの脆弱性スキャンに既にTrivyを利用している方も多いと思いますが、別の選択肢として Inspector によるスキャンを試してみました。
また、実はコンテナイメージのスキャンだけではなく、言語パッケージのバージョンファイルやDockerfileを静的解析することも可能のため、それもやってみました。

仕組み

公式ドキュメント: https://docs.aws.amazon.com/inspector/latest/user/scanning-cicd.html

アクション「aws-actions/vulnerability-scan-github-action-for-amazon-inspector」を利用します。仕組みは以下です。

  1. Amazon Inspector SBOM Generator を使い、CycloneDX 形式の SBOM を生成する
    • Amazon Inspector SBOM Generator は Linux で動くツール
  2. 生成した SBOM を Amazon Inspector に送信し、脆弱性を解析する
    • そのため AWS アカウントおよび inspector-scan:ScanSbom 権限が必要

気になるのは何に基づいて SBOM を生成するかですが、4つの選択肢があります。

GitHub Actionsの場合、アクションの入力パラメータで指定します。この記事では Node.js アプリを例に、「リポジトリ内のファイル」と「コンテナイメージ」の2つを試してみます。

アクションを紐解く

書き方のサンプルです。

      - name: Inspector Scan
        id: inspector
        uses: aws-actions/[email protected]
        with:
          artifact_type: "repository"
          artifact_path: "./"
          display_vulnerability_findings: "enabled"
          critical_threshold: 1
          high_threshold: 1
          medium_threshold: 1
          low_threshold: 1
          other_threshold: 1
          scanners: "javascript-nodemodules"

よく使いそうなパラメータには以下があります。

  • artifact_type
    • リポジトリ内のファイルをスキャンする場合は repository
    • コンテナをスキャンする場合は container
  • artifact_path
    • リポジトリ内のファイルをスキャンする場合は、基本的にリポジトリルート
    • コンテナをスキャンする場合はコンテナイメージ名
  • display_vulnerability_findings
    • GitHub Actions の実行サマリページに結果を表示するか。表示する場合は enabled
    • 便利なので基本的に enabled がおすすめ
  • sbomgen_version
    • 使用する Amazon Inspector SBOM Generator のバージョンを明示的に指定したい場合に使う
  • critical_threshold high_threshold medium_threshold low_threshold other_threshold
    • 脆弱性の重要度ごとに検出数の閾値を設定
    • 閾値以上の脆弱性が検出されると、出力パラメータの vulnerability_threshold_exceeded が 1 になる
  • scanners
    • 利用するスキャナを指定できる。指定がない場合は全てのスキャナが有効
    • 複数を利用する場合は , 区切りで記述
  • skip_scanners
    • 利用を除外するスキャナを指定できる
    • 複数を除外する場合は , 区切りで記述
  • skip_files
    • スキャン対象から除外したいファイルを明示的に記述
    • 複数指定する場合は , 区切りで記述

特に scanners と skip_scanners の設定値がわかりにくいですが、SBOM Generator v1.4.0 で利用できる設定値には以下の33種類ありました。
(SBOM Generator の inspector-sbomgen list-scanners コマンドで確認した結果)

NAME GROUPS DESCRIPTION
alpine-apk os
pkg-scanner
Scans packages installed with apk
apache-httpd extra-ecosystems
pkg-scanner
Scans Apache HTTP Server based on contents of ap_release.h
binaries binary
pkg-scanner
Scans compiled Rust and Go binaries for package dependencies
csharp-csproj pkg-scanner
programming-language-packages
Scans C# packages based on contents of .csproj files
csharp-depsjson pkg-scanner
programming-language-packages
Scans C# packages based on contents of .deps.json files
csharp-pkgconfig pkg-scanner
programming-language-packages
Scans C# packages based on contents of Packages.config files
csharp-pkglock pkg-scanner
programming-language-packages
Scans C# packages based on contents of packages.lock.json files
debian-distroless os
pkg-scanner
Scans packages installed in Debian distroless containers
dockerfile dockerfile
pkg-scanner
Scans Dockerfile contents for security issues
dpkg os
pkg-scanner
Scans installed Debian packages
go-gopkg pkg-scanner
programming-language-packages
Scans Go packages based on go.sum
go-modcache pkg-scanner
programming-language-packages
Scans Go packages based on contents of $HOME/go/pkg/mod directory
java-installation extra-ecosystems
pkg-scanner
Scans for Java installations in default paths
java-jar pkg-scanner
programming-language-packages
Scans Java packages based on contents of pom.properties and archive files (.jar, .par, .war, .ear)
java-pomxml pkg-scanner
programming-language-packages
Scans Java dependencies based on the content of pom.xml
javascript-nodemodules pkg-scanner
programming-language-packages
Scans for installed packages based on contents of node_modules/*/package.json files
javascript-npm-packagelock pkg-scanner
programming-language-packages
Scans NPM dependencies based on the content of package-lock.json file
javascript-pnpm-yaml pkg-scanner
programming-language-packages
Scans PNPM dependencies based on the content of pnpm-lock.yaml file
javascript-yarnlock pkg-scanner
programming-language-packages
Scans Yarn dependencies based on the content of yarn.lock file
php pkg-scanner
programming-language-packages
Scans PHP packages based on contents of composer.lock and installed.json files
python-pipfile pkg-scanner
programming-language-packages
Scans python packages based on contents of Pipfile.lock files
python-pkg pkg-scanner
programming-language-packages
Scans python packages based on contents of egg-info and dist-info files
python-poetry pkg-scanner
programming-language-packages
Scans python packages based on contents of poetry.lock files
python-requirements pkg-scanner
programming-language-packages
Scans python packages based on content of requirements.txt files
rhel-rpm os
pkg-scanner
Scans installed rpm packages
ruby-gemfiles pkg-scanner
programming-language-packages
Scans Ruby packages based on contents of Gemfile.lock files
ruby-gemspec pkg-scanner
programming-language-packages
Scans Ruby packages based on contents of .gemspec files
ruby-globalgems pkg-scanner
programming-language-packages
Scans Ruby packages based on contents of globally installed gems
rust-cargolock pkg-scanner
programming-language-packages
Scans Rust packages based on contents of Cargo.lock files
rust-cargotoml pkg-scanner
programming-language-packages
Scans Rust packages based on contents of Cargo.toml files
wordpress-installation extra-ecosystems
pkg-scanner
Scans for Wordpress
wordpress-plugin-installation extra-ecosystems
pkg-scanner
Scans for Wordpress plugins
wordpress-theme-installation extra-ecosystems
pkg-scanner
Scans for Wordpress themes

余談ですが、v1.3.2 では javascript-nodemodules javascript-npm-packagelock javascript-pnpm-yaml javascript-yarnlock が存在せず、代わりに javascript-nodejs を指定できました。これを使っていたところ、8月下旬にアクションが利用する SBOM Generator のデフォルトバージョンが 1.4.0 に切り替わったらしく、「何もしていないのに壊れた」状態になり焦りました。

リポジトリ内のファイルをスキャンする場合

artifact_type 入力パラメータは repository を指定します。ジョブの全体像は次のようになります。ファイルを静的解析するため、コンテナをビルドする必要はありません。
生成した SBOM を Inspector に渡すため、aws-actions/configure-aws-credentials アクションで一時的なクレデンシャルを取得します。利用するロールには inspector-scan:ScanSbom 権限をつけておきましょう。

jobs:
  scan:
    runs-on: ubuntu-latest # コンテナアクションなので Linux のみで実行可能
    timeout-minutes: 20
    steps:
      - name: Checkout Respository
        uses: actions/checkout@v4
      # AWS クレデンシャルの設定
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::111122223333:role/my-sample-role-name
          aws-region: ap-northeast-1
      - name: Inspector Scan
        id: inspector
        uses: aws-actions/[email protected]
        with:
          artifact_type: "repository" # repository を指定
          artifact_path: "./" # リポジトリのルートを指定
          display_vulnerability_findings: "enabled"
          critical_threshold: 1
          high_threshold: 1
          medium_threshold: 1
          low_threshold: 1
          other_threshold: 1
      # SBOM の表示(必要に応じて利用)
      - name: Display CycloneDX SBOM (JSON)
        run: cat ${{ steps.inspector.outputs.artifact_sbom }}
      # JSON 形式の検出結果を表示(必要に応じて利用)
      - name: Display Inspector vulnerability scan results (JSON)
        run: cat ${{ steps.inspector.outputs.inspector_scan_results }}
      # CSV 形式の検出結果を表示(必要に応じて利用)
      - name: Display Inspector vulnerability scan results (CSV)
        run: cat ${{ steps.inspector.outputs.inspector_scan_results_csv }}
      # Markdown 形式の検出結果を表示(必要に応じて利用)
      - name: Display Inspector vulnerability scan results (Markdown)
        run: cat ${{ steps.inspector.outputs.inspector_scan_results_markdown }}
      # CSV 形式のDockerfile検出結果を表示(必要に応じて利用)
      - name: Display Dockerfile scan results (CSV)
        run: cat ${{ steps.inspector.outputs.inspector_dockerile_scan_results_csv }}
      # Markdown 形式のDockerfile検出結果を表示(必要に応じて利用)
      - name: Display Dockerfile scan results (Markdown)
        run: cat ${{ steps.inspector.outputs.inspector_dockerile_scan_results_markdown }}
      # スキャン結果をアーティファクトにアップロード
      - name: Upload Scan Results
        uses: actions/upload-artifact@v4
        with:
          name: Inspector Vulnerability Scan Artifacts
          path: |
            ${{ steps.inspector.outputs.inspector_scan_results }}
            ${{ steps.inspector.outputs.inspector_scan_results_csv }}
            ${{ steps.inspector.outputs.artifact_sbom }}
            ${{ steps.inspector.outputs.inspector_scan_results_markdown }}
            ${{ steps.inspector.outputs.inspector_dockerile_scan_results_csv }}
            ${{ steps.inspector.outputs.inspector_dockerile_scan_results_markdown }}
      # 検出した脆弱性の数が閾値を超えた場合、ジョブを失敗ステータスにする
      - name: Fail job if vulnerability threshold is exceeded
        run: exit ${{ steps.inspector.outputs.vulnerability_threshold_exceeded }}

試してみた

以下の脆弱性のあるパッケージバージョンをインストールし、スキャンさせてみました。このうち、micromatch は devDependencies に記載したパッケージ由来のものであり、本番にビルドするコンテナイメージに含めないように構成しています。

npmパッケージ名 バージョン CVE 本番コンテナに含まれるか
next 14.1.0 CVE-2024-34351 ✅
fast-xml-parser 4.2.5 CVE-2024-41818 ✅
micromatch 14.0.5 CVE-2024-4067 ❌

また Dockerfile の設定もスキャン可能とのことなので、Dockerfile に次の一行を含めます。

USER root

実行すると、脆弱性が検出されたためジョブが失敗ステータスになりました。
ジョブ失敗

サマリページの結果

サマリページに次のような結果が表示されました。
3つのパッケージの脆弱性、および Dockerfile にて root ユーザーを使用しているとの検出結果が表示されています。
パッケージの脆弱性については、リポジトリ内の yarn.lock ファイルから検出していることがわかります。従って本番のコンテナイメージには含まれない micromatch の脆弱性も検出されています。

実行サマリ

CSV形式の検出結果

(2024/11/19)Dockerfileの検出結果について追記しました。

CSV形式の検出結果のうち、inspector_scan_ とファイル名に付くもの(outputs.inspector_scan_results_csv)は次のようになりました。
こちらはパッケージの脆弱性のみで、Dockerfile の設定については出力がありません。

"#artifact_name:./","artifact_type:directory","artifact_hash:null","build_id:null"
"#critical_vulnerabilities:0","high_vulnerabilities:1","medium_vulnerabilities:1","low_vulnerabilities:0","other_vulnerabilities:2"
"ID","Severity","Source","CVSS","Installed Package","Fixed Package","Path","EPSS","Exploit Available","Exploit Last Seen","CWEs"
"CVE-2024-4067","medium","MITRE","5.3","pkg:npm/[email protected]","4.0.8","yarn.lock","0.00045","true","2024-08-26T14:20:30Z","CWE-1333"
"CVE-2024-41818","untriaged","NVD","null","pkg:npm/[email protected]","4.4.1","yarn.lock","0.00045","null","null","null"
"CVE-2024-34351","high","MITRE","7.5","pkg:npm/[email protected]","14.1.1","yarn.lock","0.00119","true","2024-08-24T07:21:02Z","CWE-918"

inspector_dockerfile_scan_ とファイル名に付くもの(outputs.inspector_dockerile_scan_results_csv)は次のようになりました。
こちらにDockerfile の設定についての検出結果が書かれています。

ID,SEVERITY,DESCRIPTION,FILE,LINE
IN-DOCKER-003,info,Last USER is root: If a service can run without privileges use USER to change to a non-root user.,dockerfile:Dockerfile,41-41

JSON形式の検出結果

JSON形式の検出結果は次のようになりました。
脆弱性だけではなくコンポーネントが全て記載されているので大変長いのですが、sbom.vulnerabilities に脆弱性の情報が記録されていました。Dockerfile の設定についても記載されています。

JSON形式の検出結果

{
    "sbom": {
        "specVersion": "1.5",
        "metadata": {
            // (省略)
        },
        "components": [
            // (省略)
        ],
        "bomFormat": "CycloneDX",
        "vulnerabilities": [
            {
                "advisories": [
                    {
                        "url": "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1071631"
                    },
                    {
                        "url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-4067"
                    },
                    {
                        "url": "https://www.cve.org/CVERecord?id=CVE-2024-4067"
                    }
                ],
                "bom-ref": "vuln-1",
                "references": [
                    {
                        "id": "GHSA-952p-6rrq-rcjv",
                        "source": {
                            "name": "GITHUB",
                            "url": "https://github.com/advisories/GHSA-952p-6rrq-rcjv"
                        }
                    }
                ],
                "created": "2024-05-14T15:42:47Z",
                "description": "The NPM package `micromatch` is vulnerable to Regular Expression Denial of Service (ReDoS). The vulnerability occurs in `micromatch.braces()` in `index.js` because the pattern `.*` will greedily match anything. By passing a malicious payload, the pattern matching will keep backtracking to the input while it doesn't find the closing bracket. As the input size increases, the consumption time will also increase until it causes the application to hang or slow down. There was a merged fix but further testing shows the issue persists. This issue should be mitigated by using a safe pattern that won't start backtracking the regular expression due to greedy matching.",
                "affects": [
                    {
                        "ref": "comp-988"
                    }
                ],
                "source": {
                    "name": "NVD",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-4067"
                },
                "cwes": [
                    1333
                ],
                "analysis": {
                    "state": "exploitable"
                },
                "ratings": [
                    {
                        "severity": "none",
                        "score": 0.00045,
                        "method": "other",
                        "vector": "model:v2023.03.01,date:2024-08-27T00:00:00+0000",
                        "source": {
                            "name": "EPSS",
                            "url": "https://api.first.org/data/v1/epss?cve=CVE-2024-4067"
                        }
                    },
                    {
                        "severity": "medium",
                        "score": 5.3,
                        "method": "CVSSv31",
                        "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
                        "source": {
                            "name": "MITRE",
                            "url": "https://cve.org/CVERecord?id=CVE-2024-4067"
                        }
                    }
                ],
                "id": "CVE-2024-4067",
                "updated": "2024-05-22T12:15:10Z",
                "properties": [
                    {
                        "name": "amazon:inspector:sbom_scanner:priority",
                        "value": "standard"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:priority_intelligence",
                        "value": "unverified"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:exploit_available",
                        "value": "true"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:exploit_last_seen_in_public",
                        "value": "2024-08-26T14:20:30Z"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:fixed_version:comp-988",
                        "value": "4.0.8"
                    }
                ]
            },
            {
                "advisories": [
                    {
                        "url": "https://docs.docker.com/develop/develop-images/instructions/"
                    }
                ],
                "bom-ref": "vuln-2",
                "ratings": [
                    {
                        "severity": "info",
                        "method": "other",
                        "source": {
                            "name": "AMAZON_INSPECTOR",
                            "url": "https://aws.amazon.com/inspector/"
                        }
                    }
                ],
                "created": "2024-03-27T14:36:39Z",
                "description": "Last USER is root: If a service can run without privileges, use USER to change to a non-root user.",
                "affects": [
                    {
                        "ref": "comp-1106"
                    }
                ],
                "id": "IN-DOCKER-003",
                "source": {
                    "name": "AMAZON_INSPECTOR",
                    "url": "https://aws.amazon.com/inspector/"
                },
                "analysis": {
                    "state": "in_triage"
                },
                "updated": "2024-03-27T14:36:39Z"
            },
            {
                "advisories": [
                    {
                        "url": "https://access.redhat.com/errata/RHSA-2024:5054"
                    }
                ],
                "bom-ref": "vuln-3",
                "references": [
                    {
                        "id": "GHSA-mpg4-rc92-vx8v",
                        "source": {
                            "name": "GITHUB",
                            "url": "https://github.com/advisories/GHSA-mpg4-rc92-vx8v"
                        }
                    }
                ],
                "ratings": [
                    {
                        "severity": "none",
                        "score": 0.00045,
                        "method": "other",
                        "vector": "model:v2023.03.01,date:2024-08-27T00:00:00+0000",
                        "source": {
                            "name": "EPSS",
                            "url": "https://api.first.org/data/v1/epss?cve=CVE-2024-41818"
                        }
                    }
                ],
                "created": "2024-07-29T16:15:05Z",
                "description": "fast-xml-parser is an open source, pure javascript xml parser. a ReDOS exists on currency.js. This vulnerability is fixed in 4.4.1.",
                "affects": [
                    {
                        "ref": "comp-1068"
                    }
                ],
                "id": "CVE-2024-41818",
                "source": {
                    "name": "NVD",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-41818"
                },
                "analysis": {
                    "state": "in_triage"
                },
                "updated": "2024-08-02T20:17:01Z",
                "properties": [
                    {
                        "name": "amazon:inspector:sbom_scanner:priority",
                        "value": "standard"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:priority_intelligence",
                        "value": "unverified"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:fixed_version:comp-1068",
                        "value": "4.4.1"
                    }
                ]
            },
            {
                "advisories": [
                    {
                        "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-34351"
                    }
                ],
                "bom-ref": "vuln-4",
                "references": [
                    {
                        "id": "GHSA-fr5h-rqp8-mj6g",
                        "source": {
                            "name": "GITHUB",
                            "url": "https://github.com/advisories/GHSA-fr5h-rqp8-mj6g"
                        }
                    }
                ],
                "created": "2024-05-14T15:38:42Z",
                "description": "Next.js is a React framework that can provide building blocks to create web applications. A Server-Side Request Forgery (SSRF) vulnerability was identified in Next.js Server Actions. If the `Host` header is modified, and the below conditions are also met, an attacker may be able to make requests that appear to be originating from the Next.js application server itself. The required conditions are 1) Next.js is running in a self-hosted manner; 2) the Next.js application makes use of Server Actions; and 3) the Server Action performs a redirect to a relative path which starts with a `/`. This vulnerability was fixed in Next.js `14.1.1`.",
                "affects": [
                    {
                        "ref": "comp-829"
                    }
                ],
                "source": {
                    "name": "NVD",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-34351"
                },
                "cwes": [
                    918
                ],
                "analysis": {
                    "state": "exploitable"
                },
                "ratings": [
                    {
                        "severity": "none",
                        "score": 0.00119,
                        "method": "other",
                        "vector": "model:v2023.03.01,date:2024-08-27T00:00:00+0000",
                        "source": {
                            "name": "EPSS",
                            "url": "https://api.first.org/data/v1/epss?cve=CVE-2024-34351"
                        }
                    },
                    {
                        "severity": "high",
                        "score": 7.5,
                        "method": "CVSSv31",
                        "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
                        "source": {
                            "name": "MITRE",
                            "url": "https://cve.org/CVERecord?id=CVE-2024-34351"
                        }
                    }
                ],
                "id": "CVE-2024-34351",
                "updated": "2024-05-14T16:12:23Z",
                "properties": [
                    {
                        "name": "amazon:inspector:sbom_scanner:priority",
                        "value": "standard"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:priority_intelligence",
                        "value": "unverified"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:exploit_available",
                        "value": "true"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:exploit_last_seen_in_public",
                        "value": "2024-08-24T07:21:02Z"
                    },
                    {
                        "name": "amazon:inspector:sbom_scanner:fixed_version:comp-829",
                        "value": "14.1.1"
                    }
                ]
            }
        ]
    }
}

Markdown形式の検出結果

(2024/11/19)Dockerfileの検出結果について追記しました。

Markdown形式の検出結果のうち、inspector_scan_ とファイル名に付くもの(outputs.inspector_scan_results_markdown)は次のようになりました。
こちらはパッケージの脆弱性のみで、Dockerfile の設定については出力がありません。

# Amazon Inspector Scan Results
Artifact Type: repository

## Vulnerability Counts by Severity

| Severity | Count |
|----------|-------|
| Critical | 0|
| High     | 1|
| Medium   | 1|
| Low      | 0|
| Other    | 2|


## Vulnerability Findings

| ID | Severity | Source | [CVSS](https://www.first.org/cvss/) | Installed Package ([PURL](https://github.com/package-url/purl-spec/tree/master?tab=readme-ov-file#purl)) | Fixed Package | Path | [EPSS](https://www.first.org/epss/) | Exploit Available | Exploit Last Seen | CWEs |
| ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- |
| CVE-2024-34351 | high | MITRE | 7.5 | `pkg:npm/[email protected]` | `14.1.1` | `yarn.lock` | 0.00119 | true | 2024-08-24T07:21:02Z | `CWE-918` |
| CVE-2024-4067 | medium | MITRE | 5.3 | `pkg:npm/[email protected]` | `4.0.8` | `yarn.lock` | 0.00045 | true | 2024-08-26T14:20:30Z | `CWE-1333` |
| CVE-2024-41818 | untriaged | NVD |  | `pkg:npm/[email protected]` | `4.4.1` | `yarn.lock` | 0.00045 |  |  |  |

inspector_dockerfile_scan_ とファイル名に付くもの(outputs.inspector_dockerile_scan_results_markdown)は次のようになりました。
こちらにDockerfile の設定についての検出結果が書かれています。

## Dockerfile Findings
|ID|SEVERITY|DESCRIPTION|FILE|LINES|
|---|---|---|---|---|
| IN-DOCKER-003 | info | Last USER is root: If a service can run without privileges, use USER to change to a non-root user. | dockerfile:Dockerfile | 41-41 |

脆弱性が検出されなかった場合

(2024/11/18追記)本セクションに記載している問題は v1.1.4 にて解消されました。

ちなみに脆弱性が1つも検出されなかった場合、CSV形式のレポートは生成されないようで、以下のようなステップの書き方にすると、脆弱性がないにも関わらずジョブが失敗してしまいます。

      - name: Display Inspector vulnerability scan results (CSV)
        run: cat ${{ steps.inspector.outputs.inspector_scan_results_csv }}

CSV形式のレポートが生成されない
そこで cat コマンドが失敗しても異常終了させないようにしておくと良いです。

      - name: Display Inspector vulnerability scan results (CSV)
        run: cat ${{ steps.inspector.outputs.inspector_scan_results_csv }} || true

CSV形式のレポートがない場合もジョブを失敗させない

コンテナイメージをスキャンする場合

コンテナイメージをスキャンする場合のジョブの記述例です。artifact_type 入力パラメータは container を指定し、 artifact_path にはコンテナイメージ名を渡します。事前にコンテナをビルドしておく必要があります。
個人的な推奨として、コンテナイメージのスキャンの場合は lock ファイルをスキャン対象外にしておくと良いと思います。devDependencies の由来パッケージは実際には本番コンテナに含まれないようにしていることが多いと思いますが、ビルド済みのコンテナイメージをスキャンするからには、実際にコンテナに入っているパッケージのみを対象とした方が稼働環境の脆弱性がわかりやすいです。開発のみで使用しているパッケージの脆弱性を知りたい場合は、わざわざ時間をかけてコンテナをビルドせずにリポジトリ内のファイルをスキャンすれば良いのです。

      # docker/build-push-action のセットアップ
      - name: Set up docker build prereqs (QEMU)
        uses: docker/setup-qemu-action@v3
      - name: Set up docker build prereqs (Buildx)
        uses: docker/setup-buildx-action@v3
      # イメージをビルド
      - name: Build Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: false
          tags: my-container:${{ github.sha }}
          load: true      
      - name: Inspector Scan
        id: inspector
        uses: aws-actions/[email protected]
        with:
          artifact_type: "container"
          artifact_path: "my-container:${{ github.sha }}"
          display_vulnerability_findings: "enabled"
          critical_threshold: 1
          high_threshold: 1
          medium_threshold: 1
          low_threshold: 1
          other_threshold: 1
          skip_files: "/usr/app/yarn.lock" # コンテナ内のロックファイルをスキャン対象から除外

サマリページの結果

サマリページに次のような結果が表示されました。
skip_files: "/usr/app/yarn.lock" と設定したため、本番コンテナに含める2つのパッケージの脆弱性のみが検出されました。検出したソースは node_modules にインストールされたパッケージ内の package.json ファイルだとわかります。
Dockerfile についてもスキャン対象ではありますが、今回はDockerfile 自体をコンテナ内にコピーしていないため、ここでは検出されていません。
実行サマリ

リポジトリ内のファイルスキャンと、コンテナイメージスキャンの使い分けについて

2つのスキャン対象について試しましたが、使い分けについて考えてみたいと思います。

まずコンテナイメージスキャンですが、コンテナをビルドするためスキャン速度は遅く、頻繁な実行には向きません。しかし実際のコンテナビルドに使う Dockerfile を利用してビルドした結果の状態を見ることができるため、本番へデプロイするワークフローで実行するのが良いでしょう。
そのためにコンテナ内の lock ファイルはスキャン対象から除外し、開発のみで利用しているパッケージの脆弱性は出力させないことでノイズを減らします。
Dockerfile もスキャン対象ではありますが、Dockerfile 自体をコンテナ内にコピーしていない場合もあるので、別のところでスキャンさせることを考えます。

リポジトリ内のファイルスキャンですが、コンテナをビルドする必要がないのでスキャン速度は速いです。開発用ブランチへのプッシュごとに実行すると良いでしょう。 開発のみで利用しているパッケージや Dockerfile の不適切な設定もスキャン対象とし、開発中に修正するきっかけにします。

まとめ

Node.js アプリを例に GitHub Actions で Amazon Inspector を利用した脆弱性スキャンを試しました。
他の言語についても試した方がいれば、ぜひ情報をお待ちしております。

執筆:@kou.kinyo、レビュー:寺山 輝 (@terayama.akira)
(Shodoで執筆されました)