日本語

CI で Codex Security を実行する

pull request と merge request の変更をスキャンし、構造化された結果を保持して SARIF をアップロードし、重大度ポリシーを設定します。

CI で Codex Security CLI を実行し、pull request または merge request に含まれる変更を正確にレビューし、検出結果とカバレッジを保持します。また、必要に応じて、選択した重大度でチェックを失敗させます。まずは結果の報告のみから始め、スキャンの品質と実行時間を確認してから、リポジトリに適した重大度ポリシーを追加してください。

このガイドでは、GitHub Actions と GitLab CI/CD の例を紹介します。同じスキャンコマンドとエクスポートコマンドを、ほかの CI システムでも使用できます。

ワークフローを準備する

OpenAI API key を CI プロバイダーのシークレットストアに CODEX_SECURITY_API_KEY として保存します。

このシークレットを、スキャンステップの OPENAI_API_KEY 環境変数に直接マッピングします。認証情報のスコープをスキャンプロセスに限定し、--auth api-key を使用して明示的に選択してください。

runner には以下が必要です。

  • Node.js 22 以降。
  • Python 3.10 以降。
  • 公開済みの @openai/codex-security パッケージ。リポジトリの checkout 外にインストールします。
  • Git が merge base を計算できるようにするための、pull request または merge request の head と base の履歴。

GitHub Actions ワークフローを追加する

非公開または internal リポジトリでは、SARIF をアップロードする前に GitHub Code Security を有効にしてください。

.github/workflows/codex-security.yml を作成します。pull request を checkout する前に、信頼済みの実行可能ファイルを $RUNNER_TEMP/codex-security/node_modules/.bin/codex-security で利用できるよう、@openai/codex-security$RUNNER_TEMP/codex-security 配下にインストールします。

name: Codex Security scan

on:
  pull_request:

jobs:
  codex-security:
    if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]'
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write
    steps:
      - name: Set up Node.js
        uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
        with:
          node-version: "26"

      - name: Set up Python
        uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
        with:
          python-version: "3.14"

      - name: Install Codex Security
        run: |
          set -euo pipefail
          npm install \
            --prefix "$RUNNER_TEMP/codex-security" \
            --ignore-scripts \
            --no-audit \
            --no-fund \
            @openai/codex-security

      - name: Verify Codex Security
        env:
          CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
        run: |
          set -euo pipefail
          test -x "$CODEX_SECURITY_BIN"
          "$CODEX_SECURITY_BIN" --version

      - name: Check out the pull request
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
        with:
          ref: ${{ github.event.pull_request.head.sha }}
          fetch-depth: 0
          persist-credentials: false

      - name: Scan the pull request
        env:
          OPENAI_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }}
          CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
          CODEX_SECURITY_STATE_DIR: ${{ runner.temp }}/codex-security-state
          BASE_SHA: ${{ github.event.pull_request.base.sha }}
          HEAD_SHA: ${{ github.event.pull_request.head.sha }}
          SCAN_DIR: ${{ runner.temp }}/codex-security-results
        run: |
          set -euo pipefail
          BASE_REVISION="$(git merge-base "$BASE_SHA" "$HEAD_SHA")"
          "$CODEX_SECURITY_BIN" scan . \
            --diff "$BASE_REVISION" \
            --head "$HEAD_SHA" \
            --auth api-key \
            --output-dir "$SCAN_DIR" \
            --json > "$RUNNER_TEMP/codex-security.json"

      - name: Export SARIF
        id: export-sarif
        if: always()
        env:
          CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
          SCAN_DIR: ${{ runner.temp }}/codex-security-results
          SARIF_FILE: ${{ runner.temp }}/codex-security.sarif
        run: |
          set -euo pipefail
          if test -f "$SCAN_DIR/scan-manifest.json"; then
            "$CODEX_SECURITY_BIN" export "$SCAN_DIR" \
              --export-format sarif \
              --source-root "$GITHUB_WORKSPACE" \
              --output "$SARIF_FILE"
            echo "available=true" >> "$GITHUB_OUTPUT"
          fi

      - name: Upload SARIF
        if: always() && steps.export-sarif.outputs.available == 'true'
        uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4
        with:
          sarif_file: ${{ runner.temp }}/codex-security.sarif
          ref: refs/pull/${{ github.event.pull_request.number }}/head
          sha: ${{ github.event.pull_request.head.sha }}
          category: codex-security

      - name: Preserve scan results
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
        with:
          name: codex-security-results
          path: |
            ${{ runner.temp }}/codex-security-results
            ${{ runner.temp }}/codex-security.json
          if-no-files-found: warn
          retention-days: 7

このワークフローは pull request の head を checkout し、その merge base を計算して、両リビジョン間でコミットされた変更をスキャンします。履歴全体を取得することで、対象を正確に保ちます。persist-credentials: false により、checkout した Git 設定にリポジトリトークンが残りません。checkout 前に CLI をインストールして絶対パスで実行することで、リポジトリ側で制御される実行可能ファイルをスキャン用の認証情報から隔離します。--auth api-key は、スコープが限定された API key を明示的に選択します。スキャン履歴は、リポジトリ外にある書き込み可能な状態ディレクトリへ保存されます。

--json は完全な JSON ドキュメントを 1 つ stdout に書き込むため、ワークフローで直接保存できます。進行状況、完了時の概要、エラーは stderr に出力されます。これは、JSON Lines のイベントストリームを出力する codex exec --json とは異なります。

エクスポートステップは、完了して sealed 状態になったスキャンを読み取り、SARIF を書き出します。Codex ランタイムと認証情報には手を加えません。スキャンの artifact には、脆弱なソースのスニペット、証拠、修正の詳細が含まれる可能性があります。リポジトリに適したアクセス制御と短い保持期間を選択してください。

GitLab CI/CD パイプラインを追加する

GitLab Ultimate 19.2 以降では、GitLab に SARIF 2.1.0 レポート を取り込めます。パイプラインを実行する前に、masked かつ hidden の CODEX_SECURITY_API_KEY CI/CD 変数を追加してください。

ルートの .gitlab-ci.ymlsecurity stage と Codex Security job を追加します。ファイル内の既存の stage と job は維持してください。この例では、デフォルトで merge request の変更をスキャンします。デフォルトブランチ全体もスキャンするには、CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH"true" に設定します。

variables:
  CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH: "false"

stages:
  - test
  - security

codex-security:
  stage: security
  image: node:26-bookworm-slim
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_PROJECT_ID == $CI_PROJECT_ID'
      variables:
        CODEX_SECURITY_SCAN_SCOPE: "diff"
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH == "true"'
      variables:
        CODEX_SECURITY_SCAN_SCOPE: "full"
  variables:
    GIT_DEPTH: "0"
    CODEX_SECURITY_CLI_DIR: "/tmp/codex-security-cli"
  before_script:
    - |
      set -eu
      apt-get update -qq
      apt-get install -y -qq --no-install-recommends \
        ca-certificates \
        git \
        python3 \
        ripgrep
      npm install \
        --prefix "$CODEX_SECURITY_CLI_DIR" \
        --ignore-scripts \
        --no-audit \
        --no-fund \
        @openai/codex-security
      export CODEX_SECURITY_BIN="$CODEX_SECURITY_CLI_DIR/node_modules/.bin/codex-security"
      test -x "$CODEX_SECURITY_BIN"
      "$CODEX_SECURITY_BIN" --version
  script:
    - |
      set -eu
      if test -z "${CODEX_SECURITY_API_KEY:-}"; then
        echo "Set the CODEX_SECURITY_API_KEY CI/CD variable." >&2
        exit 2
      fi

      codex_security_api_key="$CODEX_SECURITY_API_KEY"
      unset CODEX_SECURITY_API_KEY

      case "${CODEX_SECURITY_SCAN_SCOPE:-}" in
        diff)
          BASE_SHA="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
          HEAD_SHA="$CI_COMMIT_SHA"
          BASE_REVISION="$(git merge-base "$BASE_SHA" "$HEAD_SHA")"
          set -- --diff "$BASE_REVISION" --head "$HEAD_SHA"
          echo "Scanning committed changes from $BASE_REVISION to $HEAD_SHA."
          ;;
        full)
          set -- --mode standard
          echo "Scanning the complete default branch at $CI_COMMIT_SHA."
          ;;
        *)
          echo "Unsupported Codex Security scan scope: ${CODEX_SECURITY_SCAN_SCOPE:-unset}" >&2
          exit 2
          ;;
      esac

      export CODEX_SECURITY_STATE_DIR="/tmp/codex-security-state-$CI_JOB_ID"
      SCAN_DIR="/tmp/codex-security-results-$CI_JOB_ID"
      JSON_FILE="/tmp/codex-security-$CI_JOB_ID.json"
      SARIF_FILE="/tmp/codex-security-$CI_JOB_ID.sarif"

      install -d -m 700 "$CODEX_SECURITY_STATE_DIR" "$SCAN_DIR"

      set +e
      OPENAI_API_KEY="$codex_security_api_key" \
        "$CODEX_SECURITY_BIN" scan . \
          "$@" \
          --auth api-key \
          --output-dir "$SCAN_DIR" \
          --json > "$JSON_FILE"
      scan_exit="$?"
      set -e
      unset codex_security_api_key

      install -d -m 700 codex-security-artifacts/results
      cp -R "$SCAN_DIR"/. codex-security-artifacts/results/
      if test -s "$JSON_FILE"; then
        cp "$JSON_FILE" codex-security-artifacts/codex-security.json
      fi
      printf '%s\n' "$scan_exit" > codex-security-artifacts/scan-exit-code.txt

      export_exit=0
      if test -f "$SCAN_DIR/scan-manifest.json"; then
        set +e
        "$CODEX_SECURITY_BIN" export "$SCAN_DIR" \
          --export-format sarif \
          --source-root "$CI_PROJECT_DIR" \
          --output "$SARIF_FILE"
        export_exit="$?"
        set -e
        if test -s "$SARIF_FILE"; then
          cp "$SARIF_FILE" codex-security-artifacts/codex-security.sarif
        fi
      fi

      if test "$scan_exit" -ne 0; then
        exit "$scan_exit"
      fi
      exit "$export_exit"
  artifacts:
    when: always
    access: maintainer
    expire_in: 7 days
    paths:
      - codex-security-artifacts/
    reports:
      sarif: codex-security-artifacts/codex-security.sarif

デフォルトでは、同じ project 内のブランチから作成された merge request に対してのみ job が実行されるため、fork のパイプラインにはスキャン用の認証情報が渡りません。デフォルトブランチでも標準のフルスキャンを実行するには、group、project、または pipeline レベルで CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH"true" に設定します。フルスキャンは diff スキャンより時間とコストがかかります。

GIT_DEPTH: "0" は、merge request のスキャンで CI_MERGE_REQUEST_DIFF_BASE_SHACI_COMMIT_SHA から merge base を計算するために必要な履歴を提供します。

job は CLI を /tmp 配下にインストールし、絶対パスで実行します。また、API key はスキャンプロセスにだけ公開します。artifacts: when: always はスキャンに失敗した場合も SARIF レポートを保持し、artifacts:access: maintainer は詳細なスキャン結果へのアクセスを制限します。

.gitlab-ci.yml を変更すると CI/CD 変数が公開される可能性があるため、job を実行する前にパイプラインの変更をレビューしてください。 CODEX_SECURITY_API_KEY を保護すると、GitLab は、保護されたブランチ間の同一 project 内 merge request で、かつユーザーが対象ブランチにアクセスできる場合にのみ、その変数を利用可能にします。

重大度ポリシーを選択する

どちらの例も --fail-on-severity を省略しているため、レポートのみを生成します。検出結果をチェックの成否に反映する準備ができたら、スキャンコマンドにしきい値を追加します。

"$CODEX_SECURITY_BIN" scan . \
  --diff origin/main \
  --output-dir /path/outside/repository/results \
  --fail-on-severity high

サポートされるしきい値は criticalhighmediumlow です。しきい値には、その重大度以上の検出結果が含まれます。

スキャンステップでは、以下の終了コードを使用します。

終了コード 意味
0 スキャンが完全なカバレッジで完了し、設定されたポリシーがすべて満たされました。
1 完了したスキャンに、しきい値以上の重大度の検出結果が含まれています。
2 CLI が入力エラーまたはランタイムエラーを検出したか、完了したスキャンのカバレッジが不完全です。
130 Ctrl-C によってスキャンが中断されました。
143 SIGTERM によってスキャンが終了されました。

カバレッジが partial または unknown のスキャンは、重大度ポリシーがなくても 2 を返します。CLI は、利用可能な検出結果とカバレッジを引き続き書き出します。チェックを決定的なものとして扱う前に、coverage.json にある deferred 領域を確認してください。

既存の結果ディレクトリを使用して再試行する

CI job ごとに新しい runner ディレクトリを使用してください。永続的な runner または self-hosted runner では、--archive-existing を使用して以前の結果を保持します。

"$CODEX_SECURITY_BIN" scan . \
  --diff origin/main \
  --output-dir /path/outside/repository/results \
  --archive-existing

このコマンドは以前の結果を archive し、空のスキャンディレクトリから開始します。

CI スキャンのトラブルシューティング

  • 不明な Git ref または想定外の diff: base と head の履歴を fetch し、merge base を計算して、両方のリビジョンを明示的に渡してください。
  • 保護されている、または空でない出力ディレクトリ: それを囲む Git worktree の外にある非公開ディレクトリを選択してください。ディレクトリに結果がすでに含まれている場合は --archive-existing を使用します。
  • 認証情報がない: 信頼済みの workflow または pipeline で CODEX_SECURITY_API_KEY を利用でき、スキャンプロセスの OPENAI_API_KEY 環境変数に直接マッピングされていることを確認してください。
  • スキャン履歴エラー: CODEX_SECURITY_STATE_DIR を、リポジトリ外にある書き込み可能なディレクトリに設定してください。
  • Python セットアップエラー: runner が Python 3.10 以降を使用していることを確認してください。
  • 不完全なカバレッジ: deferred surfaces と open questions を含む coverage.json を確認し、適切な対象または環境で再実行してください。
  • SARIF エクスポートエラー: スキャンが完了し、スキャンディレクトリ全体を利用できることを確認してください。エクスポートでは、SARIF を書き出す前に sealed artifact が検証されます。
  • SARIF アップロードエラー: GitHub Actions では、organization がリポジトリの GitHub Code Security を有効にしており、workflow に actions: readcontents: readsecurity-events: write が付与されていることを確認してください。GitLab CI/CD では、project が GitLab Ultimate 19.2 以降を使用し、job が artifacts:reports:sarif を介して SARIF 2.1.0 ファイルをアップロードしていることを確認してください。

すべてのコマンド、flag、artifact、出力 field については、CLI リファレンスを参照してください。plugin を使用する対話型の CI レビューについては、コード変更のセキュリティをレビューするを参照してください。