zanadir について
zanadir は CI 設定を調べ、不足しているセキュリティ・品質管理と、既存の管理項目がビルドを止められるかを確認します。6 種類の CI プラットフォームに対応し、リポジトリの保守担当者に結果と設定案を提供します。
主な機能
- 016 種類の CI をスキャン
- 02管理項目の有効性を確認
- 03不足分のツールを提案
- 04CI 修正設定を生成
- 05表、JSON、SARIF、Markdown で出力
活用シーン
- 01リポジトリの CI セキュリティ対策を監査
- 02スキャナーがビルドを止めるか確認
- 03不足する管理項目の設定を生成
MCP 対応
zanadir は MCP サーバーとして、読み取り専用の `scan` と `fix` ツールを公開します。MCP クライアントに CI の検査結果と設定案を返し、ファイルへの書き込みは行いません。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
docker run -i --rm -e GITHUB_TOKEN docker.io/mustachecase/zanadir:0.24.2GITHUB_TOKENシークレットOptional: a token for the repo argument, used only when the server is started with --repo-tokenローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
zanadir はリポジトリの CI 設定を読み取り、不足しているセキュリティおよび 品質管理項目や、ビルドを失敗させられない設定になっている項目を報告します。
何がわかりますか
- パイプラインに不足しているものは何ですか? 依存関係の スキャンやシークレットの検出から、SBOM やワークフローの強化まで、 12 のカテゴリを確認します。各カテゴリをカバーするツールと照合し、 リポジトリの言語に適した提案を提示します。
- CI はセキュリティ上の検出結果を実際にブロックしますか?
continue-on-error、--exit-code 0、|| true、push のみを対象とするトリガー、またはallow_failureが設定されたスキャナーは存在していても、マージを止められません。 zanadir は単にカバーされているかだけでなく、各管理項目を enforcing、partial、advisory、unknown と評価します。 有効性を参照してください。 - そのチェックはマージに必須ですか?
--required-checksを指定すると、 ブランチ保護またはルールセットで必須にされている場合に限り、 管理項目を enforcing として扱います。 - 何を追加すればよいですか?
zanadir fixは不足項目に対応する、 そのまま貼り付けて使える CI 設定を出力します。--writeを指定すると、 ワークフローを書き込みます。
チェックアウトしたリポジトリ(--dir)または GitHub から直接(--repo)、
GitHub Actions、GitLab CI、CircleCI、Bitbucket Pipelines、Azure
Pipelines、Jenkins を読み取り、表、JSON、SARIF、Markdown 形式で報告します。
こうした不足はどのくらい一般的でしょうか? CI セキュリティの現状 2026 では、GitHub でスター数が最も多いリポジトリから 1,000 件をスキャンしました。 511 件は依存関係、シークレット、コードのスキャンを一切設定しておらず、 CI でスキャナーを実行しているリポジトリの 6 件に 1 件では、 ビルドを失敗させられないスキャナーが使われています。
zanadir scan --dir .
zanadir scan --repo github.com/owner/name --output json
zanadir fix --dir .
インストール
何もインストールせずに、任意の公開リポジトリで試せます。
docker run --rm mustachecase/zanadir scan --repo github.com/owner/name
インストールするには、最新リリース
から Linux、macOS、Windows 用(amd64 または arm64)のアーカイブをダウンロードしてください。
checksums.txt には各アーカイブの SHA-256 が記載されており、
すべてのアーカイブにビルド来歴のアテステーションが付属しています。
gh attestation verify zanadir_<version>_linux_amd64.tar.gz --repo MustacheCase/zanadir
または、Homebrew(tap はこのリポジトリです)か Go を使います。
brew tap --custom-remote MustacheCase/zanadir https://github.com/MustacheCase/zanadir.git
brew install mustachecase/zanadir/zanadir
go install github.com/MustacheCase/zanadir@latest
GitHub Actions では、zanadir-action を使ってください。このワークフローは、失敗させることなくすべてのプルリクエストを報告します。
# .github/workflows/zanadir.yml
name: zanadir
on: [pull_request]
permissions:
contents: read
jobs:
zanadir:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: mustachecase/[email protected]
with:
dir: .
ゲートとして使う準備ができたら、enforce: true(未カバーのカテゴリがあれば失敗)、
fail-on(指定したカテゴリのみ)、または fail-on-weakened を追加してください。
プルリクエストのコメントとしてレポートを投稿する方法は、
プルリクエストへのレポートの投稿を参照してください。
zanadir と OpenSSF Scorecard
OpenSSF Scorecard と zanadir は異なる問いに答えるツールであり、併用すると効果的です。
| OpenSSF Scorecard | zanadir | |
|---|---|---|
| 問い | このプロジェクトのセキュリティ対策はどの程度健全ですか? | どの CI コントロールが不足していて、導入済みのコントロールはビルドを失敗させる可能性がありますか? |
| 対象範囲 | プロジェクト全体にわたる 20 項目のチェック:ブランチ保護、コードレビュー、メンテナンス、依存関係のピン留め、トークンの権限、署名付きリリース、既知の脆弱性、ファジングなど | 12 カテゴリの CI コントロール。それぞれのカテゴリを、その機能を提供する多数のツールと照合します |
| 読み取るもの | フォージの API:GitHub および GitLab のリポジトリ、最近のプルリクエスト、チェック実行 | CI 設定:6 つの CI プラットフォーム、Makefile、Taskfile、tox、複合アクション、およびジョブから呼び出されるテンプレート |
| たとえば SAST | CodeQL または SonarCloud を探します。ドキュメントには、ほかのツールは低いスコアになる場合があると記載されています | Semgrep、CodeQL、gosec、Bandit、Brakeman、SonarQube などを認識し、それぞれがビルドを失敗させる可能性があるかを評価します |
| 結果 | 各チェックと全体の 0~10 のスコア | カテゴリごとの対応状況を表示し、強制、部分的、助言的、不明のいずれかで評価するとともに、修正方法を提示します |
プロジェクト全体のセキュリティ態勢を把握するには Scorecard を使用し、現在の CI が問題のある変更を実際に阻止できるか、また何を追加すべきかを確認するには zanadir を使用してください。
zanadir を AI コーディングエージェントと使用する
コーディングエージェントに変更前の CI を zanadir でチェックさせるには、リポジトリの AGENTS.md、CLAUDE.md、または同等のファイルに次の内容を追加します。
## CI changes
Before adding or changing CI configuration, run `zanadir scan --dir . --output json`
(install: https://github.com/MustacheCase/zanadir#installation). It lists the
missing CI controls and grades each present one: `enforcing` can fail a merge,
`partial`, `advisory` and `unknown` cannot or may not. Fix advisory controls before
adding tools, and use `zanadir fix --dir .` for configuration that fits this
repository. Re-run the scan to confirm the change.
MCP サーバーとして
zanadir mcp は Model Context Protocol 経由で読み取り専用の 2 つのツールを提供します。MCP に対応するエージェントであれば、CI の変更前後に評価できます。
scanは JSON レポートを返します。スコア、不足しているカテゴリと推奨ツール、判定結果付きの弱体化したコントロールが含まれます。fixは不足箇所に対応する、すぐに貼り付けて使える設定を返します。ファイルへの書き込みは行いません。
どちらも dir(ローカルのチェックアウト。デフォルトはカレントディレクトリ)または repo(github.com/owner/name[@ref]。クローンせずに読み取ります)、および excludedCategories を受け取ります。
claude mcp add zanadir -- zanadir mcp
MCP Registry には io.github.MustacheCase/zanadir として登録されており、リポジトリを読み取り専用でマウントした Docker イメージから実行されます。Cursor、VS Code、Windsurf などのほかのクライアントの場合:
{
"mcpServers": {
"zanadir": { "command": "zanadir", "args": ["mcp"] }
}
}
Claude Code プラグインとして
Claude Code では、zanadir プラグインに両方が含まれます。MCP サーバーと、CI 設定をスキャンして読み取り、優先順位順に修正を提案し、選択後にのみファイルを編集するスキルです。サーバーは、インストール済みの zanadir が 0.18.0 以降の場合はそれを実行し、それ以外の場合は Docker イメージを実行します。
/plugin marketplace add MustacheCase/zanadir
/plugin install zanadir@mustachecase
次に、Claude Code に CI のレビューまたは強化を依頼するか、/zanadir:improve-ci を実行します。
llms.txt は、リポジトリを読み取るエージェント向けに zanadir の概要をまとめたもので、サイトのルートを参照するエージェント向けには mustachecase.github.io/llms.txt で提供されています。
機能
- 📂 スキャン:セキュリティサービスやベストプラクティスなど、CI/CD の改善案についてリポジトリを分析します。
- 🔍 CI 分析:各コントロールが存在するかどうかだけでなく、実際にビルドを失敗させる可能性があるかを評価します。
- 🛠️ 修正:スキャンで不足が報告されたカテゴリについて、CI 設定を表示するか、ワークフローとして書き込みます。
- 🚀 オープンソース:Zanadir の機能向上につながる貢献を歓迎します!
サポート対象の CI アクション
Zanadir は現在、以下をサポートしています。
- GitHub Actions
- CircleCI
- GitLab
- Bitbucket Pipelines
- Azure Pipelines
- Jenkins
今後は以下のサポートを予定しています。
- Tekton
提案するカテゴリ
Zanadir は、以下のカテゴリでリポジトリを分析します。太字の名前が
--excluded-categories に渡す正確な値です(一致判定では大文字と
小文字は区別されません)。
- 🛡️ SCA:ソフトウェア構成分析
- 🔐 Secrets Detection:シークレット管理
- 📜 License Compliance:ライセンスコンプライアンス
- 🛠️ End Of Life:サポート終了ソフトウェアパッケージ
- 📊 Coverage:テストカバレッジ
- 📊 Performance Testing:テストのパフォーマンスと信頼性
- 🧑💻 Linter:コードのリント
- 🧪 Unit Tests:自動ユニットテスト
- 🔎 SAST:静的アプリケーションセキュリティテスト
- 🏗️ IaC Security:Infrastructure-as-Code の設定ミスのスキャン
- 🔗 Supply Chain:SBOM の生成、署名、来歴
- 🧱 Workflow Security:パイプライン定義自体の強化
使用例
基本的な使い方
CI/CD の改善提案を得るためにリポジトリをスキャンします。
zanadir scan --dir /path/to/your/repo
または、クローンせずに GitHub リポジトリをスキャンします。
zanadir scan --repo github.com/owner/name
zanadir scan --repo owner/[email protected] # a branch, tag or commit
zanadir fix --repo owner/name
--repo はリポジトリのアーカイブを一時ディレクトリにダウンロードし、
チェックアウトした場合とまったく同じようにスキャンしてから、そのディレクトリを削除します。内容が保持されるのは、zanadir が読み取るファイルのみです。GitHub のアーカイブでは、.gitattributes で export-ignore が指定されたファイルは除外されます。これには .github が含まれることが多いため、CI がないリポジトリとして報告しないよう、zanadir はそれらのファイルを個別に取得します。公開リポジトリではトークンは不要です。非公開リポジトリにアクセスする場合や、GitHub のレート制限を緩和する場合は、GITHUB_TOKEN(または GH_TOKEN)を設定してください。GitLab と Bitbucket のリポジトリは、引き続きクローンが必要です。
出力形式
Zanadir は、テーブル(デフォルト)、JSON、SARIF の 3 つの出力形式に対応しています。
テーブル出力(デフォルト)
zanadir scan --dir . --output table
出力例:
Coverage 10/11 - 1 category needs attention:
|--------------------------------|--------------------------------|-------------------|
| CATEGORY | DESCRIPTION | SUGGESTED TOOLS |
|--------------------------------|--------------------------------|-------------------|
| Performance and Reliability | Tools for load, stress and | k6, JMeter, |
| Testing Tools | reliability testing, to verify | Gatling, Apache |
| | a system holds up under | Bench, Artillery, |
| | expected and peak traffic. | BlazeMeter |
|--------------------------------|--------------------------------|-------------------|
不足しているものがない場合は、空のテーブルを表示せず、その旨をスキャン結果に表示します。
Coverage 11/11 - all categories are covered.
見出しは、対話型ターミナルでは太字で表示されます。出力がパイプに渡される場合や、--output-file で書き込まれる場合、また NO_COLOR が設定されている場合は、色は省略されます。
JSON 出力
zanadir scan --dir . --output json
スコア、設定が見つかった CI システム、カバーされていないカテゴリとそれらをカバーするツール、ツールがリポジトリを完全には保護できないカバー済みカテゴリ、およびスコアから除外されたカテゴリを含む 1 つのオブジェクトです。どのリストも配列であり、該当する項目がない場合は null ではなく空の配列になります。
{
"score": { "covered": 4, "total": 11 },
"platforms": ["github"],
"uncovered": [
{
"id": "Secrets Detection",
"name": "Data Leakage & Secrets Detection",
"description": "Tools that help detect sensitive information ...",
"suggestions": [
{
"name": "Gitleaks",
"repository": "https://github.com/gitleaks/gitleaks",
"description": "..."
}
]
}
],
"weakened": [
{ "category": "SCA", "verdict": "advisory", "location": ".github/dependabot.yml" }
],
"notApplicable": ["IaC Security"]
}
language は、提案されたツールが特定の言語専用の場合にのみ表示されます。また、弱体化したカテゴリの location は、リポジトリ内を指す場合にのみ表示されます。0.10.0 より前では、--output json は、現在の uncovered にあたる内容のみを含む配列を、キーを大文字で始めて出力していました。
SARIF 出力
zanadir scan --dir . --output sarif
SARIF は、静的解析結果の標準的な交換形式です。これを出力すると、カバーされていないカテゴリを、他のスキャナーの結果とともに GitHub の Security タブや、その他の SARIF 対応プラットフォームに表示できます。
カバーされていないカテゴリはそれぞれ 1 つの SARIF ルールとなり、提案されたツールを結果のヘルプテキストに記載した、重大度 warning の結果が 1 つ生成されます。結果は CI 設定ファイル、つまり不足しているツールが追加される場所に関連付けられます。不足している制御は特定の行にある欠陥ではありませんが、SARIF の利用側はすべて位置情報を必要とし、GitHub code scanning は結果に位置情報がないレポートを拒否します。
Markdown 出力
zanadir scan --dir . --output markdown
Markdown は、スキャン結果をファイルに出力するのではなく、人に見せるための形式です。ツールにはそれぞれのリポジトリへのリンクが付けられ、レポートの冒頭には非表示のマーカーが置かれます。これにより、ワークフローはプッシュのたびに新しいコメントを追加する代わりに、前回投稿したコメントを更新できます。
出力例:
### zanadir
Coverage 10/11 - 1 category needs attention:
| Category | Description | Suggested tools |
| --- | --- | --- |
| Performance and Reliability Testing Tools | Tools for load, stress and reliability testing. | [k6](https://github.com/grafana/k6), [JMeter](https://github.com/apache/jmeter) |
不足しているものがない場合は、テーブルが省略され、1 行だけが残ります。
レポートをファイルに書き込む
--output-file を使用すると、標準出力ではなく指定したパスにレポートを書き込めます。これはすべての形式で使用でき、機械可読なレポートを生成する確実な方法です。標準出力をリダイレクトするとデバッグログも一緒に取り込まれるため、--output sarif --debug > report.sarif では有効な JSON ではないファイルが生成されます。
zanadir scan --dir . --output sarif --output-file zanadir.sarif
GitHub Actions ワークフローからレポートを公開するには、次のようにします。
- name: Run zanadir
run: zanadir scan --dir . --output sarif --output-file zanadir.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: zanadir.sarif
未対応のカテゴリは、ほかのスキャナーとともにリポジトリの Security タブに表示されます。
プルリクエストにレポートを投稿する
SARIF では未対応のカテゴリが Security タブに表示されますが、そこを訪れる人は限られています。プルリクエストのコメントなら、普段から人が確認しています。
permissions:
contents: read
pull-requests: write
steps:
- name: Run zanadir
run: zanadir scan --dir . --output markdown --output-file zanadir.md
- name: Post the report
run: gh pr comment "$NUMBER" --body-file zanadir.md --edit-last --create-if-none
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUMBER: ${{ github.event.pull_request.number }}
--edit-last を使うと、プッシュのたびにコメントを追加するのではなく、1 つのコメントを更新し続けられます。同じユーザーによる最後のコメントを対象にするため、ほかの何かも github-actions としてコメントする場合は、代わりにマーカーを照合してください。
- name: Find the previous report
uses: peter-evans/find-comment@v4
id: previous
with:
issue-number: ${{ github.event.pull_request.number }}
body-includes: ""
- name: Post the report
uses: peter-evans/create-or-update-comment@v5
with:
comment-id: ${{ steps.previous.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: zanadir.md
edit-mode: replace
フォークからのプルリクエストには読み取り専用トークンが付与されるため、コメントのステップから投稿できません。pull_request_target を使って回避するのではなく、if: github.event.pull_request.head.repo.full_name == github.repository でガードしてください。pull_request_target を使うと、フォークのコードに対して書き込み権限でワークフローが実行されます。
カバレッジスコア
各スキャンはカバレッジスコアにまとめられます。リポジトリに該当するカテゴリから、未対応のカテゴリを差し引いたスコアです。
スコアの信頼性を保つため、次の 3 つのルールがあります。
-
除外されたカテゴリは分母から外れます。
--excluded-categoriesで無関係と宣言したカテゴリによって、リポジトリの評価が下がることはありません。 -
スキャン対象がないカテゴリも同様です。 Terraform、Helm チャート、Kubernetes マニフェスト、Dockerfile がないリポジトリに Checkov の追加を求めることはありません。その場合、11 ではなく 10 を満点としてスコアを算出します。スキャン結果には、その理由が示されます。
IaC Security is not applicable and is out of the score: nothing in this repository needs it.この方法で除外されるカテゴリは 2 つあります。IaC Security にはスキャン対象のインフラストラクチャが必要です。検出条件は意図的に広く設定されており、
infra、k8s、chartsという名前のディレクトリがあれば対象に含まれます。 Workflow Security には GitHub Actions が必要です。このカテゴリのツールはすべてその形式を読み取るため、GitLab だけを使うリポジトリに actionlint の追加を求めることはありません。それ以外はどのコードベースにも該当します。また、該当しない可能性があるカテゴリ、たとえばライブラリの負荷テストや、何も配布しない場合の SBOM も、該当するものと区別できません。そのため、推測で除外すると、リポジトリに必要なカテゴリまで除外してしまいます。
-
ベースラインで許容されているギャップも、未対応としてカウントされます。 ベースラインはギャップが許容されていることを記録するものであり、解消されたことを示すものではありません。ファイルをコミットしただけでスコアが上がるようでは、スコアに意味がありません。
README バッジ
--badge を使うと、スコアを shields.io のエンドポイントファイルとして書き出せます。
zanadir scan --dir . --badge zanadir-badge.json
{
"schemaVersion": 1,
"label": "zanadir",
"message": "10/11",
"color": "green"
}
そのファイルを CI から公開し、バッジの参照先に指定します。デフォルトブランチへのコミットではなく、専用のブランチに配置してください。保護されたデフォルトブランチでは、変更はプルリクエスト経由でのみ受け入れられるため、バッジをそこにプッシュするワークフローは拒否されます。

デフォルトブランチへのプッシュのたびにバッジを最新の状態に保ち、初回実行時に作成する badges ブランチに公開するジョブは次のとおりです。
badge:
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- name: Run zanadir
run: zanadir scan --dir . --badge zanadir-badge.json
- name: Publish the badge when it moved
run: |
set -euo pipefail
cp zanadir-badge.json "$RUNNER_TEMP/zanadir-badge.json"
rm -f zanadir-badge.json
git config user.name github-actions
git config user.email [email protected]
if git fetch --depth=1 origin badges 2>/dev/null; then
git switch --discard-changes -C badges FETCH_HEAD
else
git switch --orphan badges
fi
cp "$RUNNER_TEMP/zanadir-badge.json" zanadir-badge.json
git add zanadir-badge.json
if git diff --cached --quiet; then exit 0; fi
git commit -m "chore: update zanadir badge"
git push origin badges
badges ブランチには、その 1 つのファイルだけを置きます。デフォルトブランチだけでなくすべてのブランチにルールセットを適用する場合は、badges を対象から除外してください。
色はスコアに応じて変わります。80% 以上は緑、60% は黄色、40% はオレンジ、それ未満は赤で、完全にカバーされている場合のみ明るい緑になります。
有効性
存在するだけでは保護になりません。ビルドを決して失敗させないように接続されたスキャナーは、スキャナーがまったくないリポジトリよりも状況を悪化させます。誰もが、その制御があると信じてしまうからです。これらはすべて、以前は covered と報告されていました。
- uses: gitleaks/gitleaks-action@v2
continue-on-error: true # the job stays green whatever it finds
on:
schedule: [{cron: "0 3 * * *"}] # nothing is blocked at merge time
on:
push: {branches: [main]} # pull requests are never checked
スキャンでは、検出した各制御を評価し、存在から期待されるほど保護できていないものを報告するようになりました。
3 covered categories are weakened:
Linter is partial (.github/workflows/pushonly.yml)
SCA is advisory (.github/workflows/nightly.yml)
Secrets Detection is advisory (.github/workflows/ci.yml)
| 判定 | 意味 |
|---|---|
enforcing | 変更をゲートする経路で、その制御によってビルドを失敗させられます |
partial | ビルドを失敗させられますが、その経路ではありません。たとえば、プッシュ時のみです |
advisory | 実行されますが、何もブロックできません |
unknown | 判定できるほど十分に設定を読み取れません |
pull_request トリガーがゲートとして機能するのは、変更自体に対して実行され、デフォルトブランチへのプルリクエストでも実行される場合に限られます。types: [closed] はマージ後に実行されるため、プッシュと同じ判定になります。デフォルトブランチを除外する branches: または branches-ignore: フィルターがある場合、その制御は unknown になります。デフォルトブランチはリポジトリの外部で設定されます。zanadir は、git clone が設定するチェックアウトの origin/HEAD、または --repo や --required-checks を指定した場合は GitHub から読み取ります。ほとんどの CI チェックアウトのように、いずれにもデフォルトブランチがない場合は、main、master、trunk、develop を代わりに使用するか、フィルターに指定されたものを使用します。
Supply Chain は別の経路を基準に評価されます。SBOM、署名、来歴証明は、ブロックすべきものを探すのではなく、公開されるものを記述するため、アーティファクトが公開される場所に属します。リリース時、プッシュ時、タグ作成時、プルリクエスト時、またはいずれかに連鎖したワークフローで実行される場合は enforcing です。手動実行がリリースそのものである可能性はファイルから判断できないため、unknown になります。公開されるものと無関係なスケジュール実行のみの場合は advisory です。また、ここでも他と同様、continue-on-error は advisory になります。署名ステップが失敗しても通知されない場合、署名されていないアーティファクトが公開されるためです。
弱体化した制御がスキャンを失敗させるのは、そのように指定した場合だけです。--enforce と --fail-on はツールがないカテゴリにのみ作用します。弱い制御を失敗として扱うには --fail-on-weakened を指定します。
zanadir scan --dir . --fail-on-weakened
Enforcement failed: controls that cannot fail a build: Secrets Detection (advisory)
この2つは独立しています。--enforce だけでは弱体化した制御は引き続き無視されるため、アップグレードしても、以前は成功していたビルドが失敗するようにはなりません。--fail-on を指定すると、両方の対象が絞り込まれます。
ベースラインは、弱体化を受け入れた時点の判定で受け入れます。
version: 1
categories:
- Performance Testing
weakened:
Secrets Detection: partial
したがって、partial を受け入れても、後で advisory に悪化した状態まで受け入れるわけではありません。制御が記録された判定より悪くなると、スキャンは再び失敗します。--write-baseline を指定すると、判定を自動で記録します。
一部の判定は汎用的です。continue-on-error はあらゆる制御を無効にします。また、シェルで同等のことをする gitleaks detect || true、|| echo failed、ツールを if や ! の条件として実行する方法も同様です。再度失敗する || exit 1 は問題ありません。スケジュール時にしか実行されないワークフローも同様です。それ以外は特定のツールに固有で、ルールで指定できます。
- id: "tfsec-rule"
regex: "(?i)\\btfsec\\b"
weakenedWhen:
- runMatches: "--soft-fail\\b"
verdict: "advisory"
- input: "soft_fail"
equals: "true"
verdict: "advisory"
チェックは、ステップのシェルコマンドに対する正規表現、または値と比較する1つのアクション入力のいずれかです。意図的に、式言語にはしていません。現在対応しているのは、trivy --exit-code 0、gitleaks --exit-code 0、tfsec と checkov の --soft-fail/soft_fail: true、および fail-build: false を指定した anchore/scan-action です。
意図的に判定しないものが2つあります。if: 条件は決して解釈しません。任意のコンテキストを参照でき、誤って読み取るくらいなら読み取れないと認めるほうがよいためです。その場合、制御は unknown として報告されます。また、カテゴリは最も強い制御を基準に判定されるため、適切に接続されたスキャナーが1つあれば、弱いスキャナーがいくつ並んでいても、そのカテゴリについては警告されません。
必須チェック
スキャナーをすべてのプルリクエストで実行していても、そのチェックがマージに必須でなければ、何もブロックされません。この設定はリポジトリのファイルではなく、ルールセットとブランチ保護にあるため、オプトインです。
zanadir scan --dir . --required-checks
zanadir scan --repo owner/name --required-checks
zanadir は GitHub からデフォルトブランチの必須チェックを読み取ります(パブリックリポジトリではトークンは不要です。設定されている場合は GITHUB_TOKEN または GH_TOKEN が使用されます)。enforcing となる GitHub Actions のチェックは、そのジョブのチェックが必須の場合に限り、その判定を維持します。マトリックス(build (ubuntu-latest))や再利用可能なワークフロー(codeql / Analyze)の名前も含まれます。そうでない場合は partial となります。
2 covered categories are weakened:
SAST is partial (.github/workflows/codeql.yml): its check is not required to merge
Secrets Detection is partial (.github/workflows/secrets.yml): its check is not required to merge
Merge settings for main:
Required to merge: build (ubuntu-latest), build (macos-latest).
Problem: ruleset "checks must pass" requires "Run tests" but applies to no branch.
保護設定のように見えて、実際はそうではない設定も示します。たとえば、チェックを必須とするものの、対象ブランチがない、または評価のみを行うルールセットや、どのワークフロージョブも報告していない必須の GitHub Actions チェックです。DCO など、ほかのアプリが報告するチェックは判定対象になりません。設定を読み取れない場合、スキャンは警告を出し、それらを考慮せずに評価します。読み取るのは GitHub のみです。他のプラットフォームでは、マージ設定は依然として zanadir から確認できません。
パイプラインの外で宣言される制御
Dependabot と Renovate は、CI ジョブではなく設定ファイル(.github/dependabot.yml、renovate.json、および Renovate が受け付けるその他の名前)から検出されます。設定が存在するのはそこだからです。
どちらも advisory と評価されます。どちらもフォージ独自のスケジュールで実行され、プルリクエストを作成しますが、ビルドを失敗させることはできません。そのため、依存関係の制御が Dependabot だけのリポジトリは、カバーされていると同時に弱められているとも報告されます。「SCA がない」または「カバーされている」のどちらか一方よりも、こちらのほうが有用な答えです。
1 covered category is weakened:
SCA is advisory (.github/dependabot.yml)
プラットフォームのカバレッジ
GitHub Actions は完全に評価されます。GitLab CI は、そこで明確に判断できる 2 つのシグナルについて評価されます。別名の continue-on-error である allow_failure: true と、誰かが実行を要求したときだけ実行されるジョブである when: manual です。
GitLab の include: は追跡されます。ローカルファイル(local: またはパスのみの指定。ワイルドカードも使用可能)は、同じ設定の一部として読み取られます。GitLab 独自のテンプレートと CI/CD コンポーネントはリポジトリから読み取れないため、名前で認識されます。Jobs/SAST.gitlab-ci.yml または components/sast/sast@... は SAST としてカウントされ、Secret Detection、Dependency and Container Scanning、SAST-IaC、License Scanning、Code Quality、およびパフォーマンステスト用のテンプレートも同様です。project: または remote: のインクルードはスキップされます。zanadir が読み取れないジョブは、スキャンを失敗させるのではなくスキップされます。
GitLab の rules:、only:、except: は意図的に解釈されません。ブランチパイプラインがマージをブロックするかどうかは、リポジトリに含まれないプロジェクト設定によって決まるため、これらを読み取ると推測になってしまいます。弱体化を示すフィールドがない GitLab ジョブは unknown と報告されます。これは問題があるともないとも述べない判定です。CircleCI には同等のシグナルがなく、評価対象ではありません。
Bitbucket Pipelines はファイルをトリガーごとに分けているため、その大部分が評価されます。
| ツールの実行場所 | 判定 |
|---|---|
pull-requests: | enforcing |
branches:(main など、名前を指定したブランチ) | partial - マージ後に実行されます |
tags:、custom:、または trigger: manual を指定したステップやステージ | advisory |
after-script: | advisory - after-script が失敗してもステップは失敗しません |
default:、またはワイルドカードのブランチパターン | unknown |
default: はプルリクエストのソースブランチでも実行されます。また、そのビルドがマージをブロックするかどうかは、リポジトリ外で設定されたマージチェックによって決まるため、GitLab のブランチパイプラインと同じ理由で unknown のままになります。パイプは GitHub Actions と同様に読み取られます。pipe: snyk/snyk-scan は Snyk としてカウントされ、DONT_BREAK_BUILD: "true" の場合は advisory と評価されます。
Jenkinsfile(および Jenkinsfile.release など、隣に置かれたバリエーション)は、実行されるのではなく Groovy のトークンとして読み取られます。sh、bat、powershell の各ステップは run: と同様に読み取られ、snykSecurity、dependencyCheck、withSonarQubeEnv、junit などのその他のステップはアクションと同様に読み取られ、名前付き引数は入力として扱われます。どのブランチやプルリクエストをビルドするかは Jenkins ジョブで設定されるものであり、ファイル内では設定されないため、ファイル自体が制御を弱めていない限り、制御の判定は unknown になります。
| Jenkinsfile 内の記述 | 判定 |
|---|---|
catchError(buildResult: 'SUCCESS') または buildResult: null の内側 | advisory - ビルドは成功のままになります |
snykSecurity(failOnIssues: false) や waitForQualityGate abortPipeline: false などのステップオプション | advisory |
when { }、if、warnError、catchError(buildResult: 'UNSTABLE') の内側、または sh(returnStatus: true) | unknown |
mavenBuild { dependencyCheck = 'enabled' } のように、代入のみで設定される共有ライブラリのステップは読み取れません。
Azure Pipelines は、リポジトリのルートにある azure-pipelines.yml(または .yaml)から読み取られ、同じリポジトリ内のファイルへの template: および extends: 参照をたどります。別のリポジトリ内のテンプレート(path@alias)や、式で指定されたテンプレートは読み取れないため、スキップされます。
| ツールの実行場所 | 判定 |
|---|---|
pr: にブランチ名が指定されたパイプライン | enforcing |
ステップまたはジョブで continueOnError: true が指定されている | advisory |
trigger: none、pr: none が指定され、schedules: のみがある | advisory |
succeeded() 以外の condition: がある | unknown |
pr: のないパイプライン | unknown |
pr: がない場合、Azure Repos のプルリクエストはリポジトリ外のブランチポリシーによって検証されるため、パイプラインがゲートとして機能するかどうかはファイルから判断できません。succeeded() は、すべてのステップにすでに設定されているデフォルト値であるため無視されます。enabled: false が指定されたステップは実行されず、判定の対象になりません。タスクはアクションと同様に読み取られ、inputs: が入力として扱われるため、failOnIssues: false が指定された SnykSecurityScan の判定は advisory になります。
SARIF では、弱められた制御は判定の根拠となったファイルを指す note として記録され、カテゴリが見つからない場合は warning のままになります。
言語に応じた提案
Zanadir はリポジトリで使用されている言語を検出し、該当するツールのみを提案します。Go プロジェクトには ESLint が提案されず、JavaScript プロジェクトには Pylint が提案されません。エコシステムを問わず機能するツール(Trivy、Gitleaks、Codecov など)は常に提案されます。
検出はリポジトリのルートと直下のサブディレクトリにある依存関係マニフェストに基づいているため、一般的なモノレポの構成にも対応できます。
| 言語 | 検出対象 |
|---|---|
| Go | go.mod |
| Python | requirements.txt、pyproject.toml、setup.py、Pipfile |
| JavaScript | package.json |
| Ruby | Gemfile、*.gemspec |
| Java | pom.xml、build.gradle、build.gradle.kts |
| Rust | Cargo.toml |
| PHP | composer.json |
| C# | *.csproj、*.sln |
node_modules、vendor などの依存関係ディレクトリはスキップされます。これらのマニフェストは、プロジェクトが作成していないコードについて記述しているためです。言語が認識されない場合は、これまでと同様にすべてのツールが提案されます。
高度な使い方
特定のカテゴリを除外する
分析中に特定のカテゴリをスキップします。
zanadir scan --dir . --excluded-categories "SCA,Secrets Detection"
カテゴリ名は上記のリストと一致している必要があります。認識されない名前は黙って無視されず、エラーとして拒否されます。
強制モード
Zanadir には、すべての CI/CD に関する提案が満たされていることを確認するための --enforce フラグが用意されています。提案が1つでも満たされていない場合、CI パイプラインは失敗します。これにより、自動化されたワークフローでセキュリティのベストプラクティスとコンプライアンスを徹底できます。
zanadir scan --dir . --enforce
ビルドに失敗したカテゴリは stderr に出力されます。
Enforcement failed: uncovered categories: SCA, Secrets Detection
終了コード:スキャンに合格した場合は 0、強制に失敗した場合、またはスキャンを実行できない場合(読み取れないディレクトリや設定など)は 1、zanadir が認識できないコマンドラインの場合(入力ミスのフラグなど)は 2 です。そのため、入力ミスがあると、何も強制されないままビルドが通るのではなく、ビルドが失敗します。
特定のカテゴリで失敗させる
--enforce はすべてを強制するため、既存のリポジトリに導入するのは困難です。カバーされていないカテゴリが1つあるだけで、すべてのビルドが失敗します。--fail-on を使うと、対象のカテゴリだけを強制し、ほかのカテゴリは情報提供のみにできます。
zanadir scan --dir . --fail-on "SCA,Secrets Detection"
--fail-on を指定すると強制が有効になるため、併せて --enforce を指定する必要はありません。カテゴリ名は大文字と小文字を区別せずに照合されます。
ベースライン
ベースラインには、リポジトリが承知のうえで受け入れている不足項目を記録します。これらのカテゴリは引き続き報告されますが、スキャンの失敗要因にはなりません。そのため、すべてを修正する前に強制を有効にでき、新たな不足項目だけがビルドを失敗させます。
現在の状態からベースラインを生成します。
zanadir scan --dir . --write-baseline
これにより .zanadir-baseline.yaml が書き込まれ(--baseline <path> で変更できます)、正常終了します。
# zanadir baseline: categories that are uncovered but accepted.
# These are still reported; they just do not fail a scan.
# Regenerate with: zanadir scan --dir . --write-baseline
version: 1
categories:
- Coverage
- Performance Testing
次に、ベースラインを使って強制します。リポジトリの不足項目がベースラインと一致する間はスキャンに合格し、新しい不足項目が現れると失敗します。
zanadir scan --dir . --enforce --baseline .zanadir-baseline.yaml
受け入れた不足項目をレビューできるよう、ベースラインをコミットし、時間をかけて項目を減らしていきましょう。
デバッグモード
詳細なログ情報を取得します。
zanadir scan --dir . --debug
設定ファイル
--config(-c)を使うと、YAML ファイルからフラグの値を読み込めるため、チームは実行のたびに同じ値を繰り返し指定せずに済みます。各キーは、設定対象となるフラグの名前です。
# .zanadir.yml
excluded-categories: [Performance Testing]
enforce: true
fail-on-weakened: true
required-checks: true
baseline: .zanadir-baseline.yaml
badge: zanadir-badge.json
output: markdown
zanadir scan --dir . -c .zanadir.yml
zanadir scan --dir . -c .zanadir.yml --output json # the command line wins
- コマンドラインで指定したフラグは、ファイル内の同じキーより優先されます。
- 不明なキーはエラーになります。そのため、
exclude-categoriesのスペルを間違えると、除外せずに黙ってスキャンするのではなく、スキャンが失敗します。 - 相対パス(
baseline、badge、output-file)は、実行元のディレクトリではなく、設定ファイルが置かれたディレクトリを基準に解決されます。 zanadir fix -cは同じファイルを読み込み、excluded-categoriesとdebugのみを使用します。dirとwrite-baselineはコマンドラインで指定します。前者はスキャン対象を指定し、後者は設定項目ではなく一度限りの操作を指定します。
カスタムルール
zanadir が認識するのはカタログに含まれるツールのみであるため、社内スキャナーはカテゴリが欠けているものとして扱われます。設定ファイルの rules: を使って、組み込みルール と同じ形式で認識させることができます。
rules:
- id: acme-scan
applyOn: [Job.Run] # Artifact.Name, Job.Name, Job.Package, Job.Run
categories: [SAST]
regex: '\bacme-scan\b'
unless: '--list-rules' # optional: a match that does not count, e.g. a command that does not scan
weakenedWhen: # optional: configurations that cannot fail a build
- runMatches: '--report-only'
verdict: advisory # advisory or partial
Artifact.Name も照合するルールでは、ワークフローのアクションに対する正規表現を指定する unlessPackage を使えます。一致するものがある場合は、ワークフローのステップに基づいて判定され、名前はカウントされません(unlessPackage: '^acme/scan-action')。
カスタムルールはカタログに追加されるもので、組み込みルールを置き換えることはありません。そのため、すでに使用されている id や、コンパイルできない正規表現、不明なカテゴリはエラーになります。一致したルールはカバー済みとして扱われ、ほかのルールと同様に評価されるため、--fail-on-weakened は acme-scan --report-only も検出します。zanadir fix も同じルールを使って、カバーされていない項目を判定します。
完全な例
# Scan with all options
zanadir scan \
--dir /path/to/repo \
--output json \
--excluded-categories "Linter" \
--enforce \
--debug
zanadir fix
スキャンすると、不足しているものがわかります。fix は追加する設定を出力します。
zanadir fix --dir .
Infrastructure as Code Security is not covered. Add to .github/workflows/security.yml:
- name: Scan infrastructure as code
uses: bridgecrewio/checkov-action@v12
with:
directory: .
https://github.com/bridgecrewio/checkov
出力するだけで、ファイルには触れません。リポジトリから CI プラットフォームを検出し、それに応じて GitHub Actions、GitLab CI、Bitbucket Pipelines、Azure Pipelines、または Jenkins 向けのスニペットを出力します。
提案されるツールすべてにテンプレートがあるわけではありません。カテゴリ内のすべてのツールにテンプレートがない場合、中途半端なものを出力するのではなく、そのカテゴリはスキップされます。そのため、fix が対象とするカテゴリは scan が報告するカテゴリより少なくなります。テンプレートは fixer/templates.yaml にあり、アクションはメジャータグ、またはアクションがメジャータグを公開していない場合はリリースに固定され、ブランチに固定されることはありません。テンプレートの追加は、単独で完結する貢献です。
ワークフローの生成
--write を指定すると、出力の代わりに独立した .github/workflows/zanadir-suggested.yml を生成します。
zanadir fix --dir . --write
Wrote .github/workflows/zanadir-suggested.yml (9 categories).
It runs as its own job, so it repeats checkout and adds a check to pull requests.
既存のワークフローは編集されないため、作成したものが壊れることはありません。最悪の場合でも、削除できるファイルが生成されるだけです。再実行すると生成されたファイルは上書きされ、同じ結果が得られます。また、zanadir はそのパスにある、自身が生成したものではないファイルを上書きしません。
独立したジョブにすることにはトレードオフがあります。チェックアウトが繰り返され、チェックリストに独自の項目が追加されるため、既存のジョブにステップを追加するよりも煩雑になります。必要に応じて、ステップを独自のワークフローに移動してください。
--write が現在生成できるのは GitHub Actions のみです。他のプラットフォームでは、このオプションを指定せずに実行し、スニペットを貼り付けてください。
--excluded-categories は scan と同じように機能します。
コントリビューター
フィードバック、課題、プルリクエストを歓迎します。CONTRIBUTING.md をご覧ください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。