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 を使用して明示的に選択してください。
信頼できるリポジトリと pull request に対してのみワークフローを実行してください。スキャンは ランナーのローカル権限を使用し、承認のために一時停止することはありません。スキャンプロセスは ジョブ環境を継承できるため、無関係なトークンやクラウドの 認証情報を環境に含めないでください。
ランナーには以下が必要です。
- Node.js 22(22.13.0 以降)、24、または 26。
- Python 3.10 以降。
- 公開済みの
@openai/codex-securityパッケージ。リポジトリの checkout 外にインストールします。 - Git が merge base を計算できるようにするための、pull request または merge request の head と base の履歴。
GitHub Actions ワークフローを追加する
非公開または内部リポジトリでは、SARIF をアップロードする前に GitHub Code Security を有効にしてください。
.github/workflows/codex-security.yml を作成します。pull request を checkout する前に、
@openai/codex-security を $RUNNER_TEMP/codex-security 配下に
インストールし、信頼できる実行可能ファイルを
$RUNNER_TEMP/codex-security/node_modules/.bin/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 を計算して、
それらのリビジョン間で commit 済みの変更をスキャンします。完全な履歴を取得することで、
対象を正確に保ちます。persist-credentials: false により、リポジトリトークンが
checkout 済みの Git 設定に保存されるのを防ぎます。checkout の前に CLI をインストールし、
その絶対パスを実行することで、リポジトリ側で制御される実行可能ファイルを
スキャン用認証情報から隔離します。--auth api-key は、スコープを限定した API key を明示的に選択します。
スキャン履歴は、リポジトリ外にある書き込み可能な状態ディレクトリに保存されます。
--json は完全な JSON ドキュメントを 1 つ stdout に書き込むため、ワークフローで
そのまま保存できます。進行状況、完了時の概要、エラーは stderr に出力されます。これは、
JSON Lines のイベントストリームを出力する codex exec --json とは異なります。
エクスポートステップは、完了して封印されたスキャンを読み取り、SARIF を書き出します。 Codex ランタイムや認証情報には手を加えません。スキャンのアーティファクトには、脆弱性のある ソースの抜粋、証拠、修正の詳細が含まれる場合があります。リポジトリに適した アクセス制御と短い保持期間を選択してください。
GitLab CI/CD パイプラインを追加する
保護されたデフォルトブランチのスキャン、オプトイン方式のスケジュール済み ディープスキャン、独立した SARIF ポリシーゲート、検証済みのドラフトマージ リクエスト(任意)を備えた本番ワークフローについては、GitLab CI/CD で Codex Security を実行するを参照してください。
GitLab Ultimate 19.2 以降では、GitLab に
SARIF 2.1.0 レポート
を取り込めます。パイプラインを実行する前に、マスクおよび非表示に設定した
CODEX_SECURITY_API_KEY CI/CD 変数を追加してください。
次の最小構成の例では、ルートの
.gitlab-ci.yml にスキャン専用の security ジョブを追加します。ファイル内の既存のステージとジョブは残してください。デフォルトでは
マージリクエストの変更をスキャンします。デフォルトブランチ全体もスキャンするには、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@0.1.20
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デフォルトでは、同じプロジェクト内のブランチから作成された merge request に対してのみジョブが
実行されるため、fork のパイプラインにはスキャン用認証情報が渡されません。デフォルトブランチでも
通常のフルスキャンを実行するには、グループ、プロジェクト、または
パイプラインレベルで CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH を "true" に設定します。フルスキャンは
差分スキャンよりも時間とコストがかかります。
GIT_DEPTH: "0" は、merge request のスキャンで CI_MERGE_REQUEST_DIFF_BASE_SHA と
CI_COMMIT_SHA から merge base を計算するために必要な履歴を提供します。
ジョブは CLI を /tmp 配下にインストールし、絶対パスで実行して、
API key をスキャンプロセスにのみ公開します。artifacts: when: always はスキャンが失敗した場合でも
SARIF レポートを保持し、artifacts:access: maintainer は詳細なスキャン結果への
アクセスを制限します。
.gitlab-ci.yml を変更すると CI/CD 変数が公開される可能性があるため、ジョブを
実行する前にパイプラインの変更をレビューしてください。
CODEX_SECURITY_API_KEY を保護
すると、GitLab は、保護されたブランチ間の同一プロジェクト内 merge request で、
かつユーザーが対象ブランチにアクセスできる場合にのみ、この変数を利用可能にします。
専用の GitLab ガイドでは、この最小構成のジョブを、このセクションの冒頭でリンクした本番 ワークフローへと拡張しています。
重大度ポリシーを選択する
どちらの例も --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 の保留領域を確認してください。
既存の結果ディレクトリを使用して再試行する
CI ジョブごとに新しいランナーディレクトリを使用してください。永続的なランナーまたはセルフホストランナーでは、
--archive-existing を使用して以前の結果を保持します。
"$CODEX_SECURITY_BIN" scan . \
--diff origin/main \
--output-dir /path/outside/repository/results \
--archive-existingこのコマンドは以前の結果をアーカイブし、空のスキャンディレクトリで開始します。
CI スキャンのトラブルシューティング
- 不明な Git ref または想定外の差分: base と head の履歴を取得し、 merge base を計算して、両方のリビジョンを明示的に渡します。
- 保護されているか、空でない出力ディレクトリ: 対象の Git worktree の外部にある
非公開ディレクトリを選択します。ディレクトリに結果がすでに含まれている場合は、
--archive-existingを使用します。 - 認証情報がない:
CODEX_SECURITY_API_KEYが信頼できるワークフローまたは パイプラインで利用でき、スキャンプロセスのOPENAI_API_KEY環境変数に 直接割り当てられていることを確認します。 - スキャン履歴エラー:
CODEX_SECURITY_STATE_DIRを、リポジトリ外にある 書き込み可能なディレクトリに設定します。 - Python セットアップエラー: ランナーが Python 3.10 以降を使用していることを確認します。
- 不完全なカバレッジ: 保留されている領域や未解決の質問を含めて
coverage.jsonを確認し、 適切な対象または環境で再実行します。 - SARIF エクスポートエラー: スキャンが完了しており、スキャンディレクトリ全体を 利用できることを確認します。エクスポート処理は、SARIF を書き出す前に封印済みのアーティファクトを検証します。
- SARIF アップロードエラー: GitHub Actions の場合は、組織で対象リポジトリの
GitHub Code Security が有効になっており、ワークフローに
actions: read、contents: read、security-events: writeが付与されていることを確認します。GitLab CI/CD の場合は、プロジェクトで GitLab Ultimate 19.2 以降を使用しており、 ジョブがartifacts:reports:sarifを通じて SARIF 2.1.0 ファイルをアップロードすることを確認します。
すべてのコマンド、フラグ、アーティファクト、出力フィールドについては、CLI リファレンスを参照してください。プラグインを使用した対話型の CI レビューについては、セキュリティの観点からコード変更をレビューするを参照してください。