GitHub Actions を静的検査するツールの紹介 (actionlint/ghalint/zizmor)
先日、 tj-actions/changed-files や reviewdog/action-* などのアクションの Git タグが書き換えられるという出来事がありました。
これにより、これらのアクションを Git タグで参照している GitHub Actions Workflow 内で悪意のあるコードが実行されてしまうという事態が発生しました。
このような事態を防ぐためには、アクションの参照には Git タグではなくコミットハッシュを使用するなどの対策が必要です。
# ❌ Git タグは書き換えられる可能性がある
- uses: actions/checkout@v4
- uses: actions/checkout@v4.2.2
# ⭕ コミットハッシュを指定しておけば Git タグが書き換えられても影響を受けない
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
しかし、 GitHub Action ではアクションのコミットハッシュ指定以外にも考慮すべき事項がたくさんあります。
それらを人力で全てチェックするのには限界があるため、以下のような静的検査ツールを利用して自動化することが重要です。
- actionlint - Static checker for GitHub Actions workflow files
- ghalint - GitHub Actions linter
- zizmor - A static analysis tool for GitHub Actions
この記事ではこれらのツールの基本的な使い方についてまとめます。
検証環境
- actionlint 1.7.7
- ghalint 1.2.3
- zizmor 1.5.1
actionlint - Static checker for GitHub Actions workflow files
actionlint でチェックできる項目
actionlint でチェックできる項目の一覧は以下の公式ドキュメントに記載されています。
主に構文チェック系の機能が充実しており、例として次のようなものがあります。
- 必須キーやキー重複のチェック
-
${{ }}
の構文チェック - shellcheck によるシェルスクリプトのチェック
- pyflakes による Python スクリプトのチェック
- etc.
actionlint のインストール
Homebrew でインストールできます。
$ brew install actionlint
その他のインストール方法については公式ドキュメントをご参照ください。
actionlint の基本的な使い方
$ actionlint --help
Usage: actionlint [FLAGS] [FILES...] [-]
actionlint is a linter for GitHub Actions workflow files.
To check all YAML files in current repository, just run actionlint without
arguments. It automatically finds the nearest '.github/workflows' directory:
$ actionlint
To check specific files, pass the file paths as arguments:
$ actionlint file1.yaml file2.yaml
To check content which is not saved in file yet (e.g. output from some
command), pass - argument. It reads stdin and checks it as workflow file:
$ actionlint -
To serialize errors into JSON, use -format option. It allows to format error
messages flexibly with Go template syntax.
$ actionlint -format '{{json .}}'
Documents:
- List of checks: https://github.com/rhysd/actionlint/tree/v1.7.7/docs/checks.md
- Usage: https://github.com/rhysd/actionlint/tree/v1.7.7/docs/usage.md
- Configuration: https://github.com/rhysd/actionlint/tree/v1.7.7/docs/config.md
Flags:
-color
Always enable colorful output. This is useful to force colorful outputs
-config-file string
File path to config file
-debug
Enable debug output (for development)
-format string
Custom template to format error messages in Go template syntax. See the usage documentation for more details
-ignore value
Regular expression matching to error messages you want to ignore. This flag is repeatable
-init-config
Generate default config file at .github/actionlint.yaml in current project
-no-color
Disable colorful output
-oneline
Use one line per one error. Useful for reading error messages from programs
-pyflakes string
Command name or file path of "pyflakes" external command. If empty, pyflakes integration will be disabled (default "pyflakes")
-shellcheck string
Command name or file path of "shellcheck" external command. If empty, shellcheck integration will be disabled (default "shellcheck")
-stdin-filename string
File name when reading input from stdin (default "<stdin>")
-verbose
Enable verbose output
-version
Show version and how this binary was installed
actionlint
を実行するだけです。
$ actionlint
.github/workflows/example.yml:4:3: "runs-on" section is missing in job "test" [syntax-check]
|
4 | test:
| ^~~~~
.github/workflows/example.yml:7:16: shell name "super-shell" is invalid. available names are "bash", "cmd", "powershell", "pwsh", "python", "sh" [shell-name]
|
7 | shell: super-shell
| ^~~~~~~~~~~
明示的にファイルパスを指定する
actionlint はデフォルトでは .github/workflows/
ディレクトリ内の Workflow ファイルをチェックしますが、引数で明示的に対象ファイルパスを指定することもできます。
$ actionlint foo.yml bar.yml
出力形式をカスタマイズする
-format
フラグを使うと Go テンプレート構文を使用して出力形式をカスタマイズできます。
様々なフォーマット例が公式ドキュメントに記載されています。
$ actionlint -format '{{json .}}'
[
{
"message": "\"runs-on\" section is missing in job \"test\"",
"filepath": ".github/workflows/example.yml",
"line": 4,
"column": 3,
"kind": "syntax-check",
"snippet": " test:\n ^~~~~",
"end_column": 7
},
{
"message": "shell name \"super-shell\" is invalid. available names are \"bash\", \"cmd\", \"powershell\", \"pwsh\", \"python\", \"sh\"",
"filepath": ".github/workflows/example.yml",
"line": 7,
"column": 16,
"kind": "shell-name",
"snippet": " shell: super-shell\n ^~~~~~~~~~~",
"end_column": 26
}
]
その他の actionlint の詳しい使い方については公式ドキュメントをご参照ください。
ghalint - GitHub Actions linter
ghalint でチェックできる項目
ghalint でチェックできる項目の一覧は以下の公式ドキュメントに記載されています。
主にセキュリティ系のチェックが充実しており、例として次のようなものがあります。
- job の permissions 指定の必須化
- コミットハッシュによるアクション参照の必須化
-
actions/checkout
アクションへのpersist-credentials: false
の設定の必須化 - etc.
ghalint のインストール
Homebrew でインストールできます。
$ brew install suzuki-shunsuke/ghalint/ghalint
その他のインストール方法については公式ドキュメントをご参照ください。
ghalint の基本的な使い方
$ ghalint --help
NAME:
ghalint - GitHub Actions linter
USAGE:
ghalint [global options] command [command options]
VERSION:
1.2.3 (2e5e757c1e8b958315fb2d24e0434ede0eaed598)
COMMANDS:
run lint GitHub Actions Workflows
run-action, act lint actions
version Show version
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--log-color value log color. auto(default)|always|never [$GHALINT_LOG_COLOR]
--log-level value log level [$GHALINT_LOG_LEVEL]
--config value, -c value configuration file path [$GHALINT_CONFIG]
--help, -h show help
--version, -v print the version
ghalint は次の 2 つのコマンドを提供しています。
-
ghalint run
: Workflow をチェックする -
ghalint run-action
: Action をチェックする
ghalint run
)
Workflow をチェックする ($ ghalint run --help
NAME:
ghalint run - lint GitHub Actions Workflows
USAGE:
ghalint run [command options]
OPTIONS:
--help, -h show help
ghalint run
は .github/workflows/
ディレクトリ内の Workflow ファイルをチェックします。
$ ghalint run
ERRO[0000] the job violates policies error="job should have permissions" job_name=test policy_name=job_permissions program=ghalint reference="https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/001.md" version=1.2.3 workflow_file_path=.github/workflows/example.yml
ERRO[0000] the step violates policies action=actions/checkout error="action ref should be full length SHA1" job_name=test policy_name=action_ref_should_be_full_length_commit_sha program=ghalint reference="https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/008.md" version=1.2.3 workflow_file_path=.github/workflows/example.yml
ERRO[0000] the step violates policies error="persist-credentials should be false" job_name=test policy_name=checkout_persist_credentials_should_be_false program=ghalint reference="https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/013.md" version=1.2.3 workflow_file_path=.github/workflows/example.yml
ghalint run-action
)
Action をチェックする ($ ghalint run-action --help
NAME:
ghalint run-action - lint actions
USAGE:
ghalint run-action [command options]
OPTIONS:
--help, -h show help
ghalint run-action
は action.yaml をチェックします。
$ ghalint run-action
$ ghalint act # alias
ERRO[0000] the step violates policies action=actions/checkout action_file_path=action.yml error="action ref should be full length SHA1" policy_name=action_ref_should_be_full_length_commit_sha program=ghalint reference="https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/008.md" version=1.2.3
ERRO[0000] the step violates policies action_file_path=action.yml error="persist-credentials should be false" policy_name=checkout_persist_credentials_should_be_false program=ghalint reference="https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/013.md" version=1.2.3
FATA[0000] ghalint failed error="some action files are invalid" program=ghalint version=1.2.3
デフォルトではカレントディレクトリの action.yml
/ action.yaml
をチェックしますが、明示的にファイルパスを指定することもできます。
$ ghalint act foo/action.yaml
# 複数指定もできる
$ ghalint act foo/action.yaml bar/action.yml
その他の ghalint の詳しい使い方については公式ドキュメントをご参照ください。
zizmor - A static analysis tool for GitHub Actions
zizmor でチェックできる項目
zizmor でチェックできる項目の一覧は以下の公式ドキュメントに記載されています。
こちらも主にセキュリティ系のチェックが充実しており、例として次のようなものがあります。
- 過剰な permissions の禁止
- なりすましコミットの検出
- テンプレート展開によるコードインジェクションの検出
- etc.
zizmor のインストール
Homebrew でインストールできます。
$ brew install zizmor
その他のインストール方法については公式ドキュメントをご参照ください。
zizmor の基本的な使い方
$ zizmor --help
Static analysis for GitHub Actions
Usage: zizmor [OPTIONS] <INPUTS>...
Arguments:
<INPUTS>...
The inputs to audit.
These can be individual workflow filenames, action definitions (typically `action.yml`), entire directories, or a `user/repo` slug for a GitHub repository. In the latter case, a `@ref` can be appended to audit the repository at a particular git reference state.
Options:
-p, --pedantic
Emit 'pedantic' findings.
This is an alias for --persona=pedantic.
--persona <PERSONA>
The persona to use while auditing
[default: regular]
Possible values:
- auditor: The "auditor" persona (false positives OK)
- pedantic: The "pedantic" persona (code smells OK)
- regular: The "regular" persona (minimal false positives)
-o, --offline
Perform only offline operations.
This disables all online audit rules, and prevents zizmor from auditing remote repositories.
[env: ZIZMOR_OFFLINE=]
--gh-token <GH_TOKEN>
The GitHub API token to use
[env: GH_TOKEN=]
--gh-hostname <GH_HOSTNAME>
The GitHub Server Hostname. Defaults to github.com
[env: GH_HOST=]
[default: github.com]
--no-online-audits
Perform only offline audits.
This is a weaker version of `--offline`: instead of completely forbidding all online operations, it only disables audits that require connectivity.
[env: ZIZMOR_NO_ONLINE_AUDITS=]
-v, --verbose...
Increase logging verbosity
-q, --quiet...
Decrease logging verbosity
--no-progress
Don't show progress bars, even if the terminal supports them
--format <FORMAT>
The output format to emit. By default, plain text will be emitted
[default: plain]
[possible values: plain, json, sarif]
--color <MODE>
Control the use of color in output
Possible values:
- auto: Use color output if the output supports it
- always: Force color output, even if the output isn't a terminal
- never: Disable color output, even if the output is a compatible terminal
-c, --config <CONFIG>
The configuration file to load. By default, any config will be discovered relative to $CWD
--no-config
Disable all configuration loading
--no-exit-codes
Disable all error codes besides success and tool failure
--min-severity <MIN_SEVERITY>
Filter all results below this severity
[possible values: unknown, informational, low, medium, high]
--min-confidence <MIN_CONFIDENCE>
Filter all results below this confidence
[possible values: unknown, low, medium, high]
--cache-dir <CACHE_DIR>
The directory to use for HTTP caching. By default, a host-appropriate user-caching directory will be used
--collect <COLLECT>
Control which kinds of inputs are collected for auditing.
By default, all workflows and composite actions are collected.
[default: default]
Possible values:
- all: Collect all possible inputs, ignoring `.gitignore` files
- default: Collect all possible inputs, respecting `.gitignore` files
- workflows-only: Collect only workflow definitions
- actions-only: Collect only action definitions (i.e. `action.yml`)
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
zizmor
に対象パスを指定するだけです。
# カレントディレクトリ内の全ての Workflow/Action をチェック
$ zizmor .
# 複数指定もできる
$ zizmor foo.yml bar/baz.yml
# GitHub リポジトリも指定できる
$ zizmor --gh-token="<GITHUB_TOKEN>" koki-develop/zenn-contents
INFO audit: zizmor: 🌈 completed ./.github/workflows/example.yml
warning[artipacked]: credential persistence through GitHub Actions artifacts
--> ./.github/workflows/example.yml:8:9
|
8 | - uses: actions/checkout@v4
| ------------------------- does not set persist-credentials: false
|
= note: audit confidence → Low
warning[excessive-permissions]: overly broad permissions
--> ./.github/workflows/example.yml:5:3
|
5 | / test:
6 | | runs-on: ubuntu-latest
7 | | steps:
8 | | - uses: actions/checkout@v4
| | -
| |__________________________________|
| this job
| default permissions used due to no permissions: block
|
= note: audit confidence → Medium
5 findings (3 suppressed): 0 unknown, 0 informational, 0 low, 2 medium, 0 high
分析の感度を調整する
--persona
フラグに分析の感度を指定できます。
指定できる値は次の通りです。
-
regular
(デフォルト) : 実用的なセキュリティの問題のみを検出する -
pedantic
: セキュリティの問題に加えて、改善が望ましい箇所も検出する -
auditor
: 誤検知の可能性も含めて全ての潜在的な問題を検出する
# regular
$ zizmor --persona=regular .
$ zizmor . # 未指定の場合は `regular` になる
# pedantic
$ zizmor --persona=pedantic .
$ zizmor -p # alias
$ zizmor --pedantic # alias
# auditor
$ zizmor --persona=auditor .
オンラインモードで実行する
一部のチェック項目は内部的に GitHub API を使用する必要があり、それらはデフォルトでは無効になっています ( offline モード) 。
これらのチェック項目を有効にする ( online モード) には GitHub Token を渡す必要があります。
$ zizmor --gh-token="<GITHUB_TOKEN>" .
# `GH_TOKEN` 環境変数経由でも指定可能
$ GH_TOKEN="<GITHUB_TOKEN>" zizmor .
その他の zizmor の詳しい使い方については公式ドキュメントをご参照ください。
どれを使うべきなのか?
それぞれチェックする項目が異なってるものも多く、且つ競合もしないので、個人的には全部併用するのが一番安心という気がしますね。
まとめ
セキュアな GitHub Actions 運用をしていきたい。
Discussion