在 CI 中執行 Codex Security
掃描 pull request 和 merge request 變更、保留結構化結果、上傳 SARIF,並設定嚴重性策略。
在 CI 中執行 Codex Security CLI 以檢視 pull request 或 merge request 中的確切更改,保留安全發現和覆蓋範圍,並可選擇在選定的嚴重性下使檢查失敗。從諮詢性結果開始,檢查掃描質量和執行時間,然後新增適合你的儲存庫的嚴重性策略。
本指南包含 GitHub Actions 和 GitLab CI/CD 範例。相同的掃描和匯出命令適用於其他 CI 系統。
準備工作流程
在 CI 提供商的 secret 儲存中儲存名為 CODEX_SECURITY_API_KEY 的 OpenAI API key。
將此 secret 直接對映到掃描步驟的 OPENAI_API_KEY 環境變數。只在掃描程序中提供該憑據,並使用 --auth api-key 顯式選擇它。
runner 需要:
- Node.js 22 或更高版本。
- Python 3.10 或更高版本。
- 公開發布的
@openai/codex-security軟體包;將其安裝在儲存庫 checkout 之外。 - pull request 或 merge request 的 head 與 base 歷史記錄,以便 Git 計算 merge base。
新增 GitHub Actions 工作流程
對於私有或內部儲存庫,請先啟用 GitHub 程式碼安全,再上傳 SARIF。
建立 .github/workflows/codex-security.yml。在 checkout pull request 之前,在 $RUNNER_TEMP/codex-security 下安裝 @openai/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,計算 merge base,並掃描這些 revision 之間已提交的變更。完整歷史記錄可確保目標準確。persist-credentials: false 可避免把儲存庫 token 寫入 checkout 目錄的 Git 設定。在 checkout 儲存庫前安裝 CLI,並通過絕對路徑執行,可防止儲存庫控制的執行檔接觸掃描憑據。--auth api-key 會顯式選擇作用域受限的 API key;掃描歷史記錄則儲存在儲存庫之外的可寫狀態目錄中。
--json 將一份完整的 JSON 文件寫入 stdout,因此工作流程可以直接儲存它。進度、完成摘要和錯誤保留在 stderr 上。這與 codex exec --json 不同,後者發出 JSON Lines 事件流。
匯出步驟讀取已完成的已封存的掃描並寫入 SARIF。它使 Codex 執行時和憑證保持不變。掃描產物可能包含易受攻擊的源程式碼片段、證據和補救詳細資訊。選擇適合你的儲存庫的存取控制和較短的保留視窗。
新增 GitLab CI/CD pipeline
GitLab Ultimate 19.2 或更高版本可以接收 SARIF 2.1.0 報告。執行 pipeline 前,請新增經過掩碼和隱藏處理的 CODEX_SECURITY_API_KEY CI/CD 變數。
把 security stage 和 Codex Security job 新增到根目錄的 .gitlab-ci.yml,同時保留檔案中已有的 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預設情況下,該 job 只為同一專案分支的 merge request 執行,因此 fork pipeline 不會獲得掃描憑據。在 group、project 或 pipeline 級別將 CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH 設為 "true",還可以對預設分支執行標準的完整掃描。完整掃描耗時更長且成本更高。
GIT_DEPTH: "0" 提供計算 merge request 掃描中 CI_MERGE_REQUEST_DIFF_BASE_SHA 與 CI_COMMIT_SHA 的 merge base 所需的歷史記錄。
該 job 在 /tmp 下安裝 CLI,通過絕對路徑執行它,並且只向掃描程序公開 API key。artifacts: when: always 會在掃描失敗時保留 SARIF 報告,而 artifacts:access: maintainer 會將詳細掃描結果的存取權限限制為 maintainer。
更改 .gitlab-ci.yml 可能公開 CI/CD 變數,因此請在執行 job 前審查 pipeline 變更。如果你保護 CODEX_SECURITY_API_KEY,GitLab 僅會在源分支和目標分支均受保護、且使用者能夠存取目標分支的同項目 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 中的延後處理的範圍。
使用現有結果目錄重試
為每個 CI 作業使用新的執行器目錄。對於持久或自託管執行器,請使用 --archive-existing 保留早期結果:
"$CODEX_SECURITY_BIN" scan . \
--diff origin/main \
--output-dir /path/outside/repository/results \
--archive-existing該命令存檔早期的結果並從空掃描目錄開始。
對 CI 掃描進行故障排除
- 未知的 Git revision 或意外差異: 獲取 base 與 head 歷史記錄,計算 merge base,並顯式傳入兩個 revision。
- 受保護或非空輸出目錄: 選擇外層 Git 工作樹之外的私有目錄。當目錄已包含結果時使用
--archive-existing。 - 缺少憑據: 確認
CODEX_SECURITY_API_KEY可用於受信任的 workflow 或 pipeline,並直接對映到掃描程序的OPENAI_API_KEY環境變數。 - 掃描歷史記錄錯誤: 將
CODEX_SECURITY_STATE_DIR設定為儲存庫之外的可寫目錄。 - Python 設定錯誤: 確認執行器使用 Python 3.10 或更高版本。
- 覆蓋不完整: 檢視
coverage.json中延後處理的範圍和未解決的問題,然後使用適當的目標或環境重新執行。 - SARIF 匯出錯誤: 確認掃描已完成且完整掃描目錄可用。匯出在寫入 SARIF 之前驗證已封存的產物。
- SARIF 上傳錯誤: 對於 GitHub Actions,請確認組織已為儲存庫啟用 GitHub 程式碼安全,並且 workflow 授予
actions: read、contents: read和security-events: write。對於 GitLab CI/CD,請確認專案使用 GitLab Ultimate 19.2 或更高版本,並通過artifacts:reports:sarif上傳 SARIF 2.1.0 檔案。
要檢視每個命令、flag、產物和輸出欄位,請參閱 CLI 參考。有關基於互動式外掛的 CI 審查,請參閱審查程式碼變更中的安全風險。