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.yml に security 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_SHA と CI_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サポートされるしきい値は critical、high、medium、low です。しきい値には、その重大度以上の検出結果が含まれます。
スキャンステップでは、以下の終了コードを使用します。
| 終了コード | 意味 |
|---|---|
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: read、contents: read、security-events: writeが付与されていることを確認してください。GitLab CI/CD では、project が GitLab Ultimate 19.2 以降を使用し、job がartifacts:reports:sarifを介して SARIF 2.1.0 ファイルをアップロードしていることを確認してください。
すべてのコマンド、flag、artifact、出力 field については、CLI リファレンスを参照してください。plugin を使用する対話型の CI レビューについては、コード変更のセキュリティをレビューするを参照してください。