1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

npm audit から始める段階的セキュリティ対策 ― CircleCI のスケジュール実行で依存パッケージを定期監視する

1
Posted at

Node.js プロジェクトの CI パイプラインは、通常 git push をトリガーに実行されます。つまり、コードが変更されない限り CI は動きません。しかし、依存ライブラリの脆弱性はコードの変更とは無関係に、日々新たに発見・公開されます。

2026年3月に発覚した axios のサプライチェーン攻撃では、すでに多くのプロジェクトで利用されていたバージョンに悪意あるコードが注入されました。push トリガーの CI だけでは、こうした「デプロイ済みのコードが使っているライブラリに後から脆弱性が見つかる」ケースを検知できません。

本記事では、npm に組み込まれている npm audit コマンドと CircleCI の Schedule Trigger を組み合わせて、この検知のギャップを埋める方法を紹介します。

npm audit でできること

npm audit は npm に標準で組み込まれているコマンドで、package-lock.json に記載された依存パッケージを GitHub Advisory Database と照合し、既知の脆弱性を報告します。追加のツールやアカウント登録は不要で、npm が使える環境ならすぐに実行できます。

GitHub Advisory Database は GitHub が管理するセキュリティ情報の集約データベースです。npm セキュリティアドバイザリをはじめ、National Vulnerability Database(NVD)や各エコシステムのアドバイザリを取り込んでおり、npm パッケージに関するアドバイザリも含まれています。

https://docs.github.com/ja/code-security/concepts/vulnerability-reporting-and-management/about-the-github-advisory-database

npm audit --audit-level=high

実行すると、脆弱性のあるパッケージごとに severity・Advisory へのリンク・影響範囲が出力されます。以下の例では、rollup に high severity の脆弱性があることがわかります。

$ npm audit --audit-level=high

# npm audit report

rollup  4.0.0 - 4.58.0
Severity: high
Rollup 4 has Arbitrary File Write via Path Traversal - https://github.com/advisories/GHSA-mw96-cpmx-2vgc
fix available via `npm audit fix`
node_modules/rollup

6 vulnerabilities (2 moderate, 4 high)

--audit-level は exit code の閾値を制御するオプションで、infolowmoderatehighcriticalnone の6段階から指定できます。上の例では --audit-level=high を指定しているため、high 以上の脆弱性が含まれる場合に非ゼロの exit code が返ります。CI 環境ではこれを利用して対応の必要な脆弱性が見つかった時にパイプラインを停止させたり通知を出したりします。

--audit-level は exit code の閾値を制御するオプションであり、出力内容のフィルタではありません。--audit-level=high を指定しても、出力にはすべての severity の脆弱性が表示されます。

CircleCI の Schedule Trigger

CircleCI の Schedule Trigger は、パイプラインを定期的に実行するための仕組みです。毎日、毎週、毎月といった頻度で、指定したブランチに対してパイプラインを自動的にトリガーできます。

Schedule Trigger の実装方法は、プロジェクトが使用している GitHub インテグレーションの種類によって異なります。

GitHub App GitHub OAuth / Bitbucket Cloud
Pipeline Definition プロジェクト内に複数作成可能。それぞれ独立した config ファイルを持てる プロジェクトに1つ(.circleci/config.yml 固定)
Schedule Trigger の対象 特定の Pipeline Definition を指定してスケジュール実行できる config.yml 内のすべてのワークフローが実行対象になる
push との分離方法 config ファイルを分ける(YAML の変更不要) pipeline.trigger_source で条件分岐(config.yml 内で制御)

プロジェクトがどちらのインテグレーションを使用しているかは、CircleCI Web App の Organization Settings > Overview で確認できます。Organization slug が circleci/<UID> 形式の場合は GitHub App、github/<org-name> 形式の場合は GitHub OAuth です。GitHub App と GitHub OAuth は同一組織内で共存可能であり、既存の OAuth プロジェクトに GitHub App Pipeline を追加することもできます。

npm audit を毎日スケジュール実行する方法

GitHub App を使用している場合と、GitHub OAuth / Bitbucket Cloud を使用している場合で実装方法が異なります。

設定に利用するもの

GitHub App・GitHub OAuth いずれの場合も、異なるのはスケジュールトリガーに関する設定のみで、ジョブの設定内容は共通です。

ジョブではnpm audit を利用して package-lock.json に記録されたライブラリのバージョンなどを照合します。もし問題のあるライブラリが見つかった場合は、このステップがfailし、Slack通知などのフローをトリガーします。

Slack通知はcircleci/slack Orbslack/notify ステップを利用します。event: fail を指定することで、npm audit high 以上の脆弱性が見つかった場合にのみ Slack に通知されます。

Slack Orbの設定方法や仕組みについては、以下の記事もご覧ください。

GitHub App の場合は専用のYAMLファイルを作成する

GitHub App では、プロジェクト内に複数の Pipeline Definition を作成できます。push トリガーで使用する既存の .circleci/config.yml とは別に、audit 専用の config ファイル(例: .circleci/audit.yml)を作成し、Schedule Trigger をその Pipeline Definition に紐付けます。既存の config.yml を一切変更する必要がありません。

audit 専用の config ファイルを作成します。

# .circleci/audit.yml
version: 2.1

orbs:
  slack: circleci/slack@5.1.1

jobs:
  audit:
    docker:
      - image: cimg/node:22.0
    resource_class: small
    steps:
      - checkout
      - run:
          name: Run npm audit
          command: npm audit --audit-level=high
      - slack/notify:
          event: fail
          template: basic_fail_1
          channel: your-security-channel-id  # Slack チャンネル ID に置き換え

workflows:
  nightly-audit:
    jobs:
      - audit:
          context:
            - slack-notifications  # Slack Webhook URL を格納した Context

この config ファイルには npm audit のワークフローしか含まれないため、pipeline.trigger_source による条件分岐は不要です。Schedule Trigger が audit.yml の Pipeline Definition を直接指定するため、push 時のビルド・テスト・デプロイワークフローが誤って実行されることはありません。

次に、CircleCI ダッシュボードで Pipeline Definition と Schedule Trigger を設定します。

1.Project Settings > Project Setup に移動
2.Add Pipeline を選択し、以下を設定

  • Integration: GitHub App
  • Config File Path: .circleci/audit.yml

Pipeline Definition 追加画面(名前: Audit、Config filepath: .circleci/audit.yml)

3.作成した Pipeline の Schedule を選択し、スケジュール実行内容を設定

  • Name: nightly-audit
  • Branch: main
  • Actor: Scheduling system

Schedule Trigger 設定画面(Name・Branch・Pipeline attribution)

npm audit のジョブではクレデンシャルは不要なため、Actor は Scheduling system で問題ありません。Slack 通知で Restricted Context を使用する場合は、その Context にアクセス権を持つユーザーを Actor に設定してください。

4.Schedule0 18 * * *(UTC 18:00 = JST 翌3:00、毎日実行)など、業務時間外の時刻を指定

cron 式の入力フィールドと WRITE WITH AI ボタン

GitHub App の Schedule Trigger は cron 構文で設定します。WRITE WITH AI ボタンを使うと自然言語から cron 式を生成できます。たとえば「毎週平日夜6時(JST)」と入力すると 0 9 * * 1-5 が生成されます。

WRITE WITH AI に「毎週平日夜6時(JST)」と入力した状態

AI 変換結果: 0 9 * * 1-5 / At 09:00 AM, Monday through Friday (times in UTC)

最後に設定を保存して有効化しましょう。

設定後の動作確認:Custom Webhook で手動トリガーする

Schedule Trigger を設定した後、スケジュールの時間まで待たずに動作確認したい場合は、Custom Webhook トリガーを一時的に追加します。Webhook URL に curl でリクエストを送るだけで、任意のタイミングでパイプラインを起動できます。

Pipeline の "Trigger on..." セクションで Custom webhook + を選択し、Event name と Event source を入力して保存します。

Custom Webhook 設定画面(Event name: manual trigger、Event source: curl)

保存後に表示される Complete webhook setup ダイアログに、Secret と Webhook URL が表示されます。Secret はこのダイアログを閉じると二度と表示されないため、すぐにコピーしてください。

Complete webhook setup ダイアログ(Secret と Webhook URL)

Webhook URL と Secret を取得したら、以下の curl コマンドで手動トリガーできます。

curl "https://internal.circleci.com/private/soc/e/xxxxxx-xxxxxx?secret=xxxxxxxxx" -XPOST
{"message":"webhook successfully ingested"}

動作確認が完了したら、Custom Webhook トリガーは削除し、Schedule Trigger のみを残しましょう。

GitHub OAuth / Bitbucket Cloud の場合:pipeline.trigger_source で分離する

GitHub OAuth / Bitbucket Cloud では、プロジェクトに1つの Pipeline Definition(.circleci/config.yml)しか持てません。Schedule Trigger は config.yml 内のすべてのワークフローを実行対象とするため、pipeline.trigger_source を使って push トリガーのワークフローとスケジュール実行のワークフローを分離する必要があります。

既存の config.yml に audit ジョブとワークフロー分離の条件を追加します。

# .circleci/config.yml
version: 2.1

orbs:
  slack: circleci/slack@5.1.1

jobs:
  # 既存のジョブ(build, test, deploy 等)はそのまま維持
  # ...

  audit:
    docker:
      - image: cimg/node:22.0
    resource_class: small
    steps:
      - checkout
      - run:
          name: Run npm audit
          command: npm audit --audit-level=high
      - slack/notify:
          event: fail
          template: basic_fail_1
          channel: your-security-channel-id  # Slack チャンネル ID に置き換え

workflows:
  # push トリガー時のみ実行(スケジュール実行時は実行しない)
  build-test-deploy:
    when:
      not:
        equal: [scheduled_pipeline, << pipeline.trigger_source >>]
    jobs:
      - build
      - test
      - deploy

  # スケジュール実行時のみ実行
  nightly-audit:
    when:
      equal: [scheduled_pipeline, << pipeline.trigger_source >>]
    jobs:
      - audit:
          context:
            - slack-notifications

pipeline.trigger_source は、パイプラインがどのように起動されたかを示すパイプライン値です。Schedule Trigger から起動された場合は scheduled_pipeline が設定されます。既存のワークフロー(build-test-deploy)に when: not: 条件を追加することで、スケジュール実行時にビルド・テスト・デプロイが走ることを防ぎます。

CircleCI ダッシュボードでで Schedule Trigger を設定します。

  1. Project Settings > Triggers に移動
  2. Add Trigger を選択し、以下を設定
    • Trigger Name: nightly-audit
    • Repeats: Weekly
    • Repeats on these days: 全曜日にチェック(毎日実行)
    • Repeats on these months: 全月にチェック

GitHub OAuth の Schedule Trigger 設定画面(上部)

フォームが縦長ですので、画像を区切りました。スクロールして次の情報も入力しましょう。

  • Start Time (UTC): 業務時間外の時刻(例: 18時 = JST 翌3:00)
  • Repeats Per Hour: Once(1時間に1回)
  • Branch or Tag Name: main
  • Attribution: Scheduled Actor(Scheduling System)(Slack で Restricted Context を使用する場合は Me を選択)

GitHub OAuth の Schedule Trigger 設定画面(下部:Attribution)

最後にSave して有効化します。

auditで問題が見つかった場合

Slack Orbを設定していれば、ワークフローが失敗した場合に通知が飛びます。

IMG_9054.jpg

「View Job」をクリックすると、CircleCIのCI実行ログから対象のライブラリをチェックすることができます。

スクリーンショット 2026-04-07 11.48.55.png

Chunkを有効化している場合、[Fix error (Beta)]ボタンを押して、そのまま修正のPRを作成できます。

スクリーンショット 2026-04-07 13.19.10.png

ChunkとCIエラー修正機能については、以下の記事も併せてご覧ください。

npm audit で検知できる範囲と更なる検知に向けて

npm auditを定期実行することで、簡単かつゼロコストでnpmライブラリを利用しているアプリケーションの脆弱性をチェックできます。ただしnpm auditで検知できる範囲には限度があることにはご注意ください。

npm audit で検知できるのは、GitHub Advisory Database に登録された既知の脆弱性に限られます。GitHub Advisory Database は NVD や各エコシステムのアドバイザリを集約したデータベースですが、登録されていない脆弱性には対応できません。そのため、package-lock.json に記載されないライブラリ、例えば OS / システムミドルウェアレベルの問題やコンテナイメージに関するもの、そしてゼロデイ攻撃などへの対処には使えません。

Snyk でスキャン範囲を広げる

Snyk は依存パッケージの脆弱性に加え、コンテナイメージやライセンス違反まで対象とした脆弱性スキャンが可能なプラットフォームです。CircleCI では Snyk 社が公式に提供する snyk/snyk Orb を使用して導入できます。

本記事の audit ワークフローに Snyk のジョブを追加すれば、同じ Schedule Trigger で npm audit と Snyk を並行実行できます。

jobs:
  snyk-scan:
    docker:
      - image: cimg/node:22.0
    resource_class: small
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: npm ci --ignore-scripts
      - snyk/scan:
          severity-threshold: high
          fail-on-issues: true

なお、導入には Snyk アカウントの作成と CircleCI Context への SNYK_TOKEN 登録が必要です。

まとめ

npm audit は npm に組み込まれたコマンドであり、CircleCI の Schedule Trigger と組み合わせるだけで、毎日自動的に既知の脆弱性をチェックする仕組みを構築できます。

とはいえ GitHub Advisory Database に登録された情報をもとに検知するという仕組み上、これだけでサプライチェーン攻撃などの問題に対処できるわけではありません。Snyk や SonarCube などの SCA / SAST ツールなども併用して複数の視点・角度から検知を行うようにしましょう。

なお、検知の仕組みだけでなく「通知が来た後に誰が、いつまでに対応するか」のトリアージルールをチーム内で事前に合意しておくことを推奨します。

関連リソース

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?