從寫程式碼,到創作下一幕

探索 字節跳動 - 火山方舟 的 AI 程式設計與影片創作活動。

Agent Plan & Coding Plan

一站體驗多款熱門模型,為 AI 程式設計與智能體開發提供更多選擇。新使用者可聯絡(微信: goo_lvyouyou)免費體驗 9.9 agent plan。

Seedance 2.5

讓創意,躍然成片。探索 30 秒影片、多模態參考與局部編輯,把腦海中的畫面變成下一支作品。

繁體中文

在 CI 中執行 Codex Security

掃描 PR 和合並請求中的更改,保留結構化結果,上傳 SARIF,並設定嚴重性策略。

在 CI 中執行 Codex Security CLI,以審查 PR 或合並請求中的確切更改、保留髮現和覆蓋範圍,並可選擇在達到指定嚴重性時使檢查失敗。建議先使用僅供參考的結果,評估掃描質量和執行時間,然後新增適合你儲存庫的嚴重性策略。

本指南包含 GitHub Actions 和 GitLab CI/CD 範例。同樣的掃描和匯出命令也適用於其他 CI 系統。

準備工作流程

將 OpenAI API key 作為 CODEX_SECURITY_API_KEY 儲存在 CI 供應商的金鑰儲存中。

將此金鑰直接對映到掃描步驟的 OPENAI_API_KEY 環境 變數。將憑據的作用域限制在掃描程序內,並使用 --auth api-key 顯式選擇該憑據。

僅對你信任的儲存庫和 PR 執行工作流程。掃描會使用 執行器的本機權限,且不會暫停以等待核准。掃描程序 可能繼承作業環境,因此不要在其中放置無關的令牌和雲服務 憑據。

執行器需要:

  • Node.js 22(22.13.0 或更高版本)、24 或 26。
  • Python 3.10 或更高版本。
  • 已發布的 @openai/codex-security 軟體包,且安裝在 儲存庫檢出目錄之外。
  • PR 或合並請求的頭部和基準歷史記錄,以便 Git 計算 合並基準。

新增 GitHub Actions 工作流程

對於私有或內部儲存庫,請先啟用 GitHub Code Security, 再上傳 SARIF。

建立 .github/workflows/codex-security.yml。在檢出拉取 請求之前,將 @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

該工作流程會檢出 PR 頭部,計算其合並基準,並 掃描這些修訂版本之間已提交的更改。完整歷史記錄可確保 目標準確。persist-credentials: false 可避免儲存庫令牌進入 已檢出的 Git 設定。在檢出前安裝 CLI 並 通過絕對路徑執行它,可防止儲存庫控制的執行檔接觸 掃描憑據。--auth api-key 會顯式選擇限定作用域的 API key。 掃描會將歷史記錄儲存在儲存庫之外可寫的狀態目錄中。

--json 會向 stdout 寫入一份完整的 JSON 文件,因此工作流程可以直接 儲存它。進度、完成摘要和錯誤仍會寫入 stderr。這 不同於 codex exec --json,後者會發出 JSON Lines 事件流。

匯出步驟會讀取已完成且已封存的掃描並寫入 SARIF。它不會改動 Codex 執行時和憑據。掃描工件可能包含易受攻擊的 源程式碼片段、證據和修復詳情。請根據你的儲存庫選擇適當的存取控制和 較短的保留期限。

新增 GitLab CI/CD 流水線

如需包含受保護預設分支掃描、可選擇啟用的定時 深度掃描、獨立的 SARIF 策略門控以及可選的已驗證草稿合並 請求的生產工作流程,請參閱在 GitLab CI/CD 中執行 Codex Security

GitLab Ultimate 19.2 或更高版本可以接收 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

預設情況下,該作業僅針對同一專案內分支發起的合並請求執行, 因此來自 fork 儲存庫的流水線不會收到掃描憑據。在組、專案或 流水線層級將 CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH 設定為 "true", 還可在預設分支上執行標準完整掃描。完整 掃描比差異掃描耗時更長、成本更高。

GIT_DEPTH: "0" 提供從 CI_MERGE_REQUEST_DIFF_BASE_SHACI_COMMIT_SHA 計算合並請求掃描所需合並基準的歷史記錄。

該作業會將 CLI 安裝到 /tmp 下,通過絕對路徑執行它,並僅向 掃描程序提供 API key。artifacts: when: always 會在掃描失敗時保留 SARIF 報告,而 artifacts:access: maintainer 會限制對詳細掃描結果的存取。

.gitlab-ci.yml 的更改可能暴露 CI/CD 變數,因此請在執行作業前 審查流水線更改。如果你 保護 CODEX_SECURITY_API_KEY, GitLab 只會為受保護分支之間、同一專案內的合並請求提供該變數, 並且僅限能夠存取目標分支的使用者使用。

專門的 GitLab 指南將此最小作業擴充套件為本節開頭所連結的 生產工作流程。

選擇嚴重性策略

兩個範例都省略了 --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 終止了掃描。

覆蓋範圍為 partialunknown 的掃描會傳回 2,即使未設定嚴重性 策略也是如此。CLI 仍會寫入可用的發現和覆蓋範圍。在將該檢查視為結論性結果之前, 請審查 coverage.json 中推遲處理的區域。

使用現有結果目錄重試

每個 CI 作業都應使用新的執行器目錄。對於持久化或自託管 執行器,請使用 --archive-existing 保留先前的結果:

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

該命令會歸檔先前的結果,並從空的掃描目錄開始。

排查 CI 掃描問題

  • 未知 Git 引用或意外的差異: 取得基準和頭部歷史記錄, 計算合並基準,並顯式傳入兩個修訂版本。
  • 受保護或非空的輸出目錄: 選擇外圍 Git 工作樹 之外的私有目錄。當目錄中已有結果時,使用 --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: readcontents: readsecurity-events: write。對於 GitLab CI/CD,請確認專案使用 GitLab Ultimate 19.2 或更高版本,並且 作業通過 artifacts:reports:sarif 上傳 SARIF 2.1.0 檔案。

有關每個命令、標誌、工件和輸出欄位的資訊,請參閱 CLI 參考。有關基於外掛的互動式 CI 審查,請參閱審查程式碼更改中的安全問題