パイプラインを失敗させるべき時と、警告だけで良い時

CIパイプラインにセキュリティスキャナを追加したとしよう。最初のスキャンが実行され、47件の問題が見つかった。3件がCritical、12件がHigh、残りはMediumとLowだ。ここで判断を迫られる:すべての検出結果でパイプラインを失敗させるべきか、それともすべてを通過させてレポートを収集するだけにするべきか。

すべてで失敗させると、パイプラインは成功より失敗の方が多くなる。開発者はスキャナを無視し始めるか、さらに悪いことにバイパスする方法を見つけ出すだろう。決してパイプラインを失敗させなければ、スキャナはノイズと化す。誰もレポートを読まず、同じ脆弱性がコードベースに何ヶ月も居座り続ける。

どちらの極端も機能しない。答えは深刻度の閾値だ。

深刻度が重要事項を示す

どのセキュリティスキャナも、検出結果を深刻度でグループ化する。ラベルはツールによって多少異なるが、パターンは一貫している:Critical、High、Medium、Low、そして場合によってはInfoやUnknownだ。これらのラベルは飾りではない。誰かがその脆弱性を悪用した場合に、事態がどれほど悪化するかを推定している。

ユーザー認証を処理するライブラリのCriticalな検出結果は、セットアップ時に一度だけ実行されその後二度と使われないコードのLowな検出結果とは異なる。これらを同じように扱うのは間違いだ。

シンプルなルール:CriticalとHighの検出結果ではパイプラインを失敗させる。MediumとLowの検出結果は警告とともに通過させる。

以下の決定木がこのルールを要約している:

flowchart TD A[Scanner Finding] --> B{Critical or High?} B -->|Yes| C[Fail Pipeline] B -->|No| D{Medium or Low?} D -->|Yes| E[Warn, Pass Pipeline] D -->|No| F[Ignore / Info]

これが実際に機能する理由

CriticalとHighで失敗させることで、最も危険な問題が本番環境に到達するのを防ぐ。これらは攻撃者が積極的に狙う脆弱性だ。データ漏洩、サービス乗っ取り、コンプライアンス違反を引き起こすものだ。

MediumとLowで警告を出すことで、パイプラインの流れを維持する。開発者は作業を続けられる。実際には問題にならないかもしれない問題でチームがブロックされることはない。ただし警告は記録され、後で誰かがレビューする必要がある。

このバランスにより、パイプラインをボトルネックにすることなく有用に保つことができる。

例外は存在するが、ルールが必要

シンプルなルールは良い出発点だが、実際のプロジェクトにはエッジケースがある。

時にはHighの検出結果にまだパッチが利用できないことがある。ベンダーが修正をリリースしておらず、その依存関係がアプリケーションの中核であるため削除できない。その場合、毎回パイプラインを失敗させても誰の役にも立たない。ベンダーの修正を追跡している間、一時的にその検出結果を受け入れる方法が必要だ。

時にはMediumの検出結果が暗号化や認証のような機密コンポーネントにあることがある。スキャナがMediumと評価しても、その場所のためにリスクはより高い。CriticalやHighでなくても、その特定の検出結果に対してパイプラインを失敗させたい場合がある。

ここで、単純な件数ベースの品質ゲートではなく、リスクベースの品質ゲートが有用になる。

ゼロ検出ルールではなく、品質ゲートを構築する

よくある間違いは、「検出結果が1つでもあればパイプライン失敗」というルールを設定することだ。それは厳格で安全に聞こえるが、実際には問題を生み出す。スキャナは誤検出を出す。特定のセットアップでは実際には悪用できないものをフラグする。すべてで失敗させると、開発者はスキャナを無視するか、すべてに例外を要求することを覚える。ゲートは意味を失う。

より良い品質ゲートは、検出結果がリスク閾値に違反しているかどうかを評価する。例:

  • 承認された例外なしにCriticalまたはHighの検出結果がある場合、パイプライン失敗。
  • 認証モジュールにMediumの検出結果がある場合、パイプライン失敗。
  • 同じ検出結果が30日以上アクションなしで開かれている場合、パイプライン失敗。

これらのルールは「ゼロ検出」よりも現実的だ。管理可能な脆弱性がある一方で、即座にブロックしなければならない脆弱性もあることを認識している。

閾値の設定方法

まず、スキャナが既に提供しているものから始める。ほとんどのスキャナは、深刻度に基づいて終了コードを設定できる。CriticalとHighはビルドを失敗させるように設定する。MediumとLowは通過させるが、警告をログに記録する。

次に通知を追加する。MediumとLowの検出結果が現れたら、チームのチャットチャンネルにメッセージを送信するか、課題トラッカーにチケットを作成する。チームはこれらの検出結果を定期的に、おそらくスプリントごとや主要リリース前にレビューするべきだ。これにより、検出結果が失われることはなく、日々の作業をブロックすることもない。

基本的な閾値を実行した後は、その動作を観察する。パイプラインが失敗しすぎる場合は、検出結果が本物か誤検出かを確認する。パイプラインがほとんど失敗しない場合は、スキャナが正しく設定され、適切なものをスキャンしているかを確認する。

ゲートが実際に行うこと

パイプラインのセキュリティゲートは、アプリケーションが完全に安全であることを保証するものではない。自動化されたスキャナですべての脆弱性を見つけられるわけではない。パイプラインのルールですべてのミスを防げるわけではない。

ゲートが行うのは、最も危険な問題が自動的に本番環境に到達するのを防ぐことだ。デプロイされたら夜も眠れなくなるようなものをキャッチする。それ以外については、手動レビュー、計画的な修正、そして優れたエンジニアリングプラクティスに依存する。

これは、ゼロ検出を目指すよりも現実的で持続可能だ。ゼロ検出を目指すチームはしばしば燃え尽きるか、システムをゲームし始める。最悪の問題をブロックし、残りを管理することを目指すチームは、長期間にわたって効果的であり続ける傾向がある。

実践的なチェックリスト

  • CriticalとHighの検出結果でパイプラインを失敗させるように設定する。
  • MediumとLowの検出結果は警告とともに通過させるように設定する。
  • MediumとLowの検出結果に対する通知を設定し、チームが後でレビューできるようにする。
  • 検出結果をすぐに修正できない場合に例外を承認するプロセスを作成する。
  • 例外リクエストは個人ではなく少人数のグループでレビューする。
  • 例外に有効期限を設定し、永久に開かれたままにならないようにする。
  • 実際の経験に基づいて調整するため、数ヶ月ごとに閾値を見直す。

まとめ

パイプラインゲートはすべてをキャッチする必要はない。最も重要なものをキャッチする必要がある。CriticalとHighをハードブロックとして始めよう。MediumとLowはチームがスケジュールに従ってレビューする警告とする。すぐに修正が不可能な場合のためのシンプルな例外プロセスを構築する。その組み合わせにより、パイプラインの流れを維持し、すべてをブロックしようとするよりも本番環境をより安全に保つことができる。