orca-sec-scans について
orca-sec-scans は、開発者や CI 向けの読み取り専用セキュリティスキャンスキルです。Trivy による静的検査に実リクエストでの検証と任意の類似ドメイン監視を組み合わせ、静的検査だけで安全と判断しない構成です。
主な機能
- 01依存関係の CVE、設定不備、ファイル内の秘密情報を検査
- 02JSON と HTML のレポートを生成
- 03実際の HTTP・DNS リクエストで前提を検証
- 04修正後に再スキャン
- 05任意で類似ドメインと証明書を確認
活用シーン
- 01リリースやデプロイ用ブランチのマージ前に検査
- 02CI でリポジトリを定期スキャン
- 03API 認証とドメイン公開状態を検証
- 04ブランド類似ドメインと証明書を調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rheinmir/ setup --skill orca-sec-scansインストール後、Claude Code で/orca-sec-scansを入力して読み込まれたか確認します。
詳細
スキル:orca-sec-scans
概要
目的とコンテキスト
- 目的: ローカルでもCI/Jenkinsでも使える、静的な読み取り専用のセキュリティスキャンフローです(スキャン中にTrivyがコードを変更することはありません)。対象は、依存関係のCVE(Go/Node/Pythonのロックファイル)、設定ミス(Dockerfile/compose/nginx)、ファイル内に露出したシークレットです。 この静的スキャンに加えて、開発者のデフォルトの想定を実際のリクエストで検証する動的スキャンと、外部に対するスキャン(サプライチェーン/ブランド、keyless)の2つのレイヤーがあります。
- トリガー(使用するタイミング):
- ユーザーが「セキュリティスキャン」「スキャン」「trivy」「CVEをチェック」「security scan」「脆弱性があるか」と言った場合
- リリース前/デプロイ用ブランチへのマージ前
- CIで定期的に実行する場合(
trivy fs . --config trivy.yaml --exit-code 1) - DiffがTrigger-conditionsの表(認証、ユーザー入力、シークレット、新しいエンドポイント、決済/機密データ、アプリのパブリックドメインへの公開)に該当する場合 → スキャンは必須です。ユーザーが「脆弱性があるか」「安全か」と言った場合 → 常にトリガーになります。
- 対象外: サブモジュールに未コミットの変更がある場合に一括で自動更新すること、レポートから検出結果を削除すること(受け入れる場合は
.trivyignoreに記録します)、レポートをデプロイ先サーバーにコミットすること、cti-expertのような広範な脅威調査(supply-watch keylessの範囲に限ります)。
メンタルモデル
Trivyはあるか? → 対象範囲(ロックファイル)→ HIGH/CRITICALの静的スキャン → JSON+HTMLレポート → 要約+修正案 → 再スキャンでNONE、並行して開発者の想定 → 実際のリクエスト(curl/dig/git diff)→ 検証または脆弱性の確認を行い、必要に応じてブランド名 → 決定的なバリエーション → 稼働中のDNS+CTログを実行します。静的スキャンに問題がなくても、安全とは限りません。
入力と出力の契約
| 項目 | 必須? | 意味 | |
|---|---|---|---|
| 入力 | ソースコードディレクトリ(be、feまたは.) | はい | スキャン対象。リポジトリにtrivy.yamlがあれば使用します |
| 入力 | HOST/稼働中のドメイン | 動的スキャンでは必須 | curl/digで実際の状態を検証します |
| 入力 | ブランド名 | いいえ | supply-watch.pyで使用します |
| 出力 | security/trivy-report.json + security/trivy-report.html | はい | 完全なレポート。レビュー用アーティファクトです |
| 出力 | 対象ごとのHIGH/CRITICAL一覧+修正案 | はい | 依存関係の更新、USERの非root化、シークレットのローテーション |
| 出力 | 動的スキャン結果 | はい | 各想定の正誤を、コマンド+実際のHTTP/DNSコードとともに示します |
| 出力 | 再スキャン結果 | 修正がある場合 | remaining = NONE |
| 出力 | supply-watchレポート | 任意 | rc 0は問題なし、2は検出あり。crt.shのエラー時は → スキップ |
ルールと機能
- RULE-01(必須):静的スキャン(Trivy)と動的スキャン(上記チェックリスト)は異なる2つのレイヤーです。両方を実行してください。 TrivyはCVE/設定ミス/ファイル内のシークレットを検出しますが、認証バイパス、パブリック公開、CORS/CSRFのロジック、ブランチの不一致は検出しません。Trivyレポートに問題がないことを、安全の根拠と見なさないでください。
- RULE-02(必須):開発者のデフォルトの想定を信用しないでください。「ログイン機能がある」「内部向けなので安全」「動作している」といった主張は、結論を出す前に必ず実際のリクエストで検証してください。
- RULE-03(必須):スキャン中、Trivyはコードを変更しません。いつでも安全に実行できます。
- RULE-04(必須):レビュー済みで受け入れた検出結果は、
.trivyignoreに記録してください(1行につきID 1つ+理由のコメント+日付)。レポートから削除してはいけません。 - RULE-05(必須):
security/*.json|htmlをデプロイ先サーバーにコミットしないでください。レビュー用アーティファクトに限ります。 - RULE-06(必須):bonbon-aiのデプロイ時は、サーバーにプッシュするのは
beとfe(2つのサブモジュール)のみです。ツール類(trivy.yaml、security/、スキル)はサーバーに配置しません。 - RULE-07(必須):修正の変更はすべて、コミット前にビルド/検証を行ってください(
go build ./...、pnpm iなど)。 - 機能:不足している場合はローカルにスキャンツールをインストールすること、ソースコードを読み取り専用で静的スキャンすること、プロジェクト自身のホストに実際のHTTP/DNSリクエストを送ること、修正適用が許可されている場合にマニフェスト/Dockerfileを変更すること、公開CTログ/DNS(keyless)を参照すること。
失敗時の境界
- Trivyをインストールできない → ブロック。ユーザーに報告してください。
- サブモジュールに未コミットの変更がある → 修正を提案するだけにとどめ、自動更新は行いません。
- 修正後にビルドできない(
go build ./...、pnpm i)→ 失敗。コミットしません。 - 動的スキャン用のHOSTがない → 一部のみ完了。静的スキャンのみを実施し、動的スキャンのレイヤーを実行していないことを明示します。「安全」と結論付けてはいけません。
- シークレットが検出された場合 → 直ちにキーをローテーションしてください(ファイルから削除するだけでは不十分です)。
- supply-watchからcrt.shにアクセスできない → スキップし、問題なしとは判断しません。
手順
ワークフローの概要
| 手順 | 種別 | 入力 | アクション | 出力/終了条件 | 失敗時/次の手順 |
|---|---|---|---|---|---|
| W01 | 効果 | マシン | 手順 0 — Trivy の確認とインストール(必ず最初に実行) | trivy --version の出力 | インストール失敗 → ブロック |
| W02 | 決定的 | リポジトリ | 手順 1 — ロックファイルから言語を検出し、ビルド/ベンダーディレクトリを除外 | スコープ | — |
| W03 | 決定的 | スコープ | 手順 2 — 脆弱性、設定ミス、シークレットをスキャンし、HIGH/CRITICAL を優先 | 検出結果 | — |
| W04 | 決定的 | 検出結果 | 手順 3 — JSON + HTML レポートを security/ に出力 | レポート 2 ファイル | — |
| W05 | 判断 | 検出結果 | 手順 4 — HIGH/CRITICAL をターゲットごとに要約し、修正を提案 | 表 + 提案 | サブモジュールに未コミットの変更がある場合 → 提案のみ |
| W06 | 決定的 | 修正済みターゲット | 手順 5 — 再スキャンして確認 | remaining = NONE | 残存 → W05 に戻る |
| W07 | 効果 | HOST、差分 | 動的スキャン:実際のリクエストを使って、開発環境で想定される 8 つのケースをチェック | 各ケースに証拠あり | HOST なし → 一部のみ |
| W08 | 決定的 | ブランド名 | supply-watch.py(B03)で外部スキャン | rc 0 または 2 | crt.sh エラー → スキップ |
各手順の詳細(W01〜W08 の唯一の情報源):
ワークフロー
手順 0 — Trivy の確認とインストール(必ず最初に実行)
if ! command -v trivy >/dev/null 2>&1; then
echo "Trivy chưa có → cài…"
if [[ "$(uname)" == "Darwin" ]]; then
brew install trivy
else
# Linux: dùng script chính thức Aqua (không cần root nếu set BINDIR)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
| sh -s -- -b /usr/local/bin
fi
fi
trivy --version # xác nhận; DB vuln tự cập nhật lần chạy đầu
手順 1 — 言語の検出(スキャン範囲)
ロックファイルを探してエコシステムを特定します:go.sum/go.mod(Go)、package-lock.json/pnpm-lock.yaml(Node)、requirements.txt/poetry.lock(Python)。node_modules、.next、.contentlayer、_archives、.git は除外します。
手順 2 — スキャン(迅速化のため HIGH+CRITICAL を優先)
trivy fs <dir> \
--scanners vuln,misconfig,secret \
--severity HIGH,CRITICAL \
--skip-dirs '**/node_modules' --skip-dirs '**/.next' \
--skip-dirs '**/.contentlayer' --skip-dirs '_archives' \
--no-progress
bonbon-ai リポジトリでは、be と fe を個別にスキャンします(それぞれがサブモジュールです)。リポジトリに trivy.yaml がある場合は、--config trivy.yaml を優先します。
手順 3 — 完全なレポートの出力
mkdir -p security
TPL=$(find $(brew --prefix trivy 2>/dev/null || echo /opt/homebrew) -name html.tpl 2>/dev/null | head -1)
trivy fs . <cờ skip-dirs như trên> --no-progress -f json -o security/trivy-report.json
trivy fs . <cờ skip-dirs như trên> --no-progress -f template --template "@$TPL" -o security/trivy-report.html
手順 4 — 要約と修正の提案
ターゲットごとに HIGH/CRITICAL の件数を数えて表に出力します。その後、修正を提案します(サブモジュールに未コミットの変更がある場合は、依存関係を一括で自動更新しないでください)。
- 簡単な依存関係の更新(直接依存関係で、マニフェストを直接変更):例:
requirements.txtのpython-multipart。 - 間接依存関係:
go get <pkg>@<ver>を実行し、go build ./...で検証します。go getによってgoディレクティブが引き上げられる場合があります。Dockerfile のベースイメージを超える場合は、バージョンが合うようにFROM golang:X-alpineも更新します。 - 設定ミス DS-0002(root ユーザー):Dockerfile に非 root の
USERを追加します(alpine ではadduser -D、debian ではuseradd -m、node-alpine イメージではUSER nodeを使用します。volume/data などの書き込み先ディレクトリのchownも忘れないでください)。 - シークレット:Trivy が検出した場合は、ファイルから削除するだけでなく、直ちにキーをローテーションします。
手順 5 — 再スキャンして確認
修正済みターゲットに対して手順 2 を再実行し、remaining = NONE を確認します。
外部スキャン — サプライチェーンとブランド(第 3 の軸、キー不要)
上記の 2 つは、どちらもプロジェクトの内部を確認します。外部を確認するものはありません。ユーザーをだますために、誰かがあなたの名前に似たドメインやパッケージを取得していないか、あるいはあなた自身のインストールコマンドに偽の依存関係を挿入していないかを調べます。
python3 fdk/tools/supply-watch.py <tên-thương-hiệu> # sweep mặc định 7 TLD
python3 fdk/tools/supply-watch.py <tên> --tld com,dev --json # cho CI
python3 fdk/tools/supply-watch.py --self-test
6 種類のパターン(文字の削除、文字の重複、文字の入れ替え、隣接キーの打ち間違い(QWERTY キーボード)、見た目が似た文字(o→0、l→1)、ハイフンの追加)で決定的かつオフラインでバリアントを生成し、DNS に問い合わせて稼働中のものを確認します。さらに、CT モニターが Certificate Transparency(crt.sh)を調べ、似たドメインに新たに発行された証明書がないかを確認します。
API キーは不要で、.venv も不要です。終了コード:0 は問題なし、2 は検出あり。crt.sh に接続できない場合は、問題なしと判定せず、スキップと表示します。
7onez/cti-expert(ワークフロー 08)から必要最小限の機能を取り入れています。意図的にこの部分だけを採用しています。cti-expert は、外部の脅威を調査するための 121 個の op と 78 個の MCP ツールを備え、.venv、API キー、アウトバウンドネットワークを必要とします。全体をそのまま統合すると、キー不要でオフライン実行可能なゲートに、キーやネットワークへの依存を持ち込むことになります。
動的スキャン — 開発者の思い込みを検証(Trivy を補完)
原則:Trivy(静的解析)ではアーキテクチャ/ロジック/実行時設定の不具合は検出できません。 最も深刻な脆弱性は、多くの場合、開発者の既定の思い込みに潜んでいます。実際のリクエストで検証する必要があり、言葉を信じてはいけません。「動く」≠「安全」です。
2026/06/22 のインシデントからの教訓:給与 ETL API に認証がなく(認証は Web 層にしかなく、/etl パスは nginx 経由で迂回可能)、誰でも curl で従業員 3,637 人の給与と PII を取得できました。「社内向け」のつもりでしたが、パブリックドメイン devtingting.coteccons.vn(パブリック IP)があり、インターネット全体に接続できる状態でした。Trivy では検出できず、実際に curl/dig して初めて判明しました。
「信じないこと――必ず検証する」チェックリスト(開発者の思い込み → 実態 → 検証方法)
-
「リバースプロキシの背後にある API は、Web/UI にログイン機能があるので保護されている。」 → 誤り:認証は多くの場合 Web 層にしかありません。プロキシのパス(
/etl、/apiなど)はブラウザーから直接呼び出せるため、ログインを迂回します。 → 検証:各 API ルートに直接、Cookie/トークンを付けずにcurlを実行します。401が返る必要があります。200+データが返る場合は脆弱性です。認証は API/プロキシ層(例:nginx のauth_request)に必要であり、UI だけでは不十分です。for p in data/stats/all report/export sync/status; do curl -k -o /dev/null -w "$p -> %{http_code}\n" https://HOST/etl/$p; done # mong đợi 401 -
「社内サーバー/プライベート IP(192.168.x)なので、外部からアクセスできない。」 → 誤り:パブリックドメイン、
tailscale funnel、ポートフォワーディングなどを経由して接続できる場合があります。 → 検証:dig +short <考えられるすべてのサブドメイン>を実行し、パブリック IP がないか確認します。外部ネットワークから curl を試します。「社内向け」を信じず、実際の DNS を確認してください。 -
「CORS を開放するのは脆弱性/CORS を閉じれば十分安全。」 → 認識違いです:CORS がブロックするのは、クロスオリジンでのブラウザーによる読み取りだけです。
curl/スクリプトはブロックしません。書き込み CSRF も防げません(POST のクエリパラメーター/ボディなしのリクエストは simple request であり、プリフライトなしにサーバーが実行します。CORS は関係ありません)。実際の問題は多くの場合、認証なし+ポート開放です。正面の入口(認証)を閉じる前に、CORS に手間をかけないでください。 -
「デフォルト設定をそのままにしても問題ない。」 → デフォルト値の変数が本番環境に残っていないか確認してください:
CORS_ORIGINSにlocalhost:3000が残っていないか。シークレット/フラグにサンプル値が残っていないか。BIND_HOSTが誤って0.0.0.0になっていないか。curl -k -D- -o /dev/null -H "Origin: http://localhost:3000" https://HOST/etl/health | grep -i access-control -
「どのブランチをデプロイしても同じ。」 → 誤り:CI(Jenkins)がブランチ X を clone する一方で、サーバーではブランチ Y が実行されているなど、差異が生じることがあります。マージコンフリクトによってブロック全体が落ちる場合があります(例:compose の
networks:がなくなり、undefined networkとなってデプロイに失敗)。 → 検証:CI が clone するブランチと実行中のブランチは同じですか?git diff <ci-branch> <running-branch> -- docker-compose.yml .env* deploy/を実行します。 -
「paramiko/SSH で接続できれば完了。」 →
Transport(...)/AutoAddPolicyはホストキーを検証しないため、MITM 攻撃でパスワードやファイルが盗まれる可能性があります。検証:known_hosts をピン留めし、RejectPolicyを使ってください。 -
「トークン/シークレットはどんな方法で渡してもよい。」 → URL の
?token=にトークンを入れると、nginx/uvicorn のアクセスログ、Referer、閲覧履歴に残ります。検証:シークレットはヘッダー/Cookieで渡し、クエリー文字列には含めないでください。 -
「
SELECT *のほうが便利。」 → PII(CCCD/MST/BHXH/給与)を含むすべてのカラムが返されます。また、全カラムを対象としたsearchは値を探るオラクルになります。検証:返すカラムをホワイトリストで指定し、機密カラムを検索対象に含めないでください。
トリガー条件 — 必ずスキャンする必要があるコード変更
誰かがスキルの呼び出しを覚えるのを待つ必要はありません。diff が表の条件に当てはまった時点で、スキャンが必須になります(WorldFlowAI/everything-claude-code の security-review から抽出):
| 変更内容 | 必須の対応 | 理由 |
|---|---|---|
| 認証、セッション、権限管理の追加・変更 | Trivy + 動的スキャン | トークンを localStorage に保存すると XSS の影響を受けます。また、機密性の高い操作の前にロールチェックが欠けていると、Trivy では検出できない認可の不備につながります。 |
| ユーザー入力の受け付け(フォーム、パラメーター、アップロード) | Trivy + 動的スキャン | スキーマによる入力検証がない場合や、ファイルアップロードのサイズ/タイプ/拡張子に制限がない場合、SQLi/XSS/RCE の侵入口になります。 |
| シークレット/認証情報/API キーの扱い | Trivy(シークレットスキャン) | ソースコードへのキーのハードコードや、git 履歴への混入を防ぐ必要があります。キーは環境変数とプラットフォームのシークレットに保存し、.env は gitignore に含める必要があります。 |
| 新しい API エンドポイントの公開 | Trivy + 動的スキャン | 新しいエンドポイントには通常、レート制限 + CSRF 対策 + 権限チェックが欠けています。また、エラーメッセージから内部情報やスタックトレースが漏れるおそれがあります。 |
| 決済/機密データ | Trivy + 動的スキャン + 手動レビュー | PII/金銭に関する情報をログに記録してはいけません。エラーは汎用的なものにする必要があります。脆弱性が1つあるだけでプラットフォーム全体が侵害されるおそれがあるため、慎重に判断してください。 |
| アプリをパブリックドメインに公開/リバースプロキシを変更 | 動的スキャン | その時点で、「内部」向けの認証なしの脆弱性がインターネットに公開されます(上記の開発環境を前提とするチェックリストを参照)。 |
表に該当しない場合は、リリースのタイミングでスキャンすれば十分です。自問する文言は、「この diff は上記6項目のいずれかに該当するか?」 です。1分以内に答えられます。ユーザーが「脆弱性はないか」「安全か」と尋ねた場合は、常にトリガーとなります。
分岐
| ID | 種類 | 条件 | 動作 | スキップ/失敗 | 再合流 |
|---|---|---|---|---|---|
| B01 | conditional_required | repo bonbon-ai | be と fe を個別にスキャンする(それぞれ submodule)。デプロイ時はその2つの submodule のみを push する | 別の repo の場合 → . をスキャン | W04 |
| B02 | conditional_required | repo に trivy.yaml がある | --config trivy.yaml を優先する | ない場合 → デフォルトの skip-dirs フラグ | W04 |
| B03 | user_optional | ユーザーがサプライチェーン/ブランドの調査を希望 | supply-watch.py <名前> で7つの TLD と CT モニターをスイープする | 要求がない場合 → スキップ | 終了 |
| B04 | conditional_required | 検出結果をレビューし、受け入れた | .trivyignore に記録する(1行に1 ID + 理由 + 日付)。レポートからは削除しない | — | W06 |
| B05 | conditional_required | Trivy がシークレットを検出 | ファイルから削除するだけでなく、直ちにキーをローテーションする | — | W06 |
検証と停止条件
必須事項:Trivy の終了コード/出力(再スキャンして remaining = NONE)、curl の実際の HTTP ステータスコード(トークンなしの API ルートは 401 であること)、実際の dig 結果、branch の git diff、supply-watch.py の終了コード。ビルド/検証が成功してから修正をコミットします。人による判断が必要な事項:検出結果を .trivyignore に追加することの承認、決済/機密データを扱うフローの手動レビュー。再スキャンで問題がなく、チェックリスト内のすべての前提に証拠がある時点で停止します。Trivy だけを根拠に「安全」と結論づけてはいけません。
例
- 該当例: 「repo bonbon-ai をセキュリティスキャンして」→ Trivy は導入済み →
be、feを個別にスキャン → HIGH が2件:requirements.txt内のpython-multipart+ DS-0002 root user → 依存関係を更新 +USER nodeを追加 → ビルド成功 → 再スキャンでremaining = NONE;動的スキャンで curl/etl/data/stats/allをトークンなしで実行し401を確認。 - 境界条件/失敗例: Trivy では問題がなくても、
/etl/report/exportに cookie なしで直接 curl すると200が返り、給与データが取得できる → 認証バイパスの脆弱性。静的スキャンのレポートに問題がなくても、重大な問題として報告する(RULE-01);dig +shortでサブドメインからパブリック IP が返る → 「内部向け」という前提は誤りです。 - 境界条件: crt.sh が応答しないときに
supply-watch.py acmeを実行 → CT モニターは「スキップ」と出力し、問題なしとは報告しない。
起源
- 2026-07-17 吸収(adapt_mode: dissolve, T-260717-02): トリガー条件の表は、
WorldFlowAI/everything-claude-codeのskills/security-review/SKILL.md(When to Activate + 10項目のチェックリスト)から抽出しました。scratchpad/ に depth-1 で clone し、バイト列は vendor していません。