Tiếng Việt

Chạy Codex Security trong CI

Quét các thay đổi trong pull request và merge request, lưu giữ kết quả có cấu trúc, tải SARIF lên và thiết lập chính sách mức độ nghiêm trọng.

Chạy Codex Security CLI trong CI để xem xét chính xác các thay đổi trong một pull request hoặc merge request, lưu giữ các phát hiện và phạm vi bao phủ, đồng thời tùy chọn làm bước kiểm tra thất bại ở mức độ nghiêm trọng đã chọn. Hãy bắt đầu với kết quả mang tính tư vấn, xem xét chất lượng quét và thời gian chạy, sau đó thêm chính sách mức độ nghiêm trọng phù hợp với kho lưu trữ của bạn.

Hướng dẫn này bao gồm các ví dụ cho GitHub Actions và GitLab CI/CD. Các lệnh quét và xuất tương tự cũng hoạt động trong những hệ thống CI khác.

Chuẩn bị quy trình làm việc

Lưu API key của OpenAI trong kho bí mật của nhà cung cấp CI dưới dạng CODEX_SECURITY_API_KEY.

Ánh xạ trực tiếp bí mật này tới biến môi trường OPENAI_API_KEY của bước quét. Chỉ cấp thông tin xác thực trong phạm vi tiến trình quét và dùng --auth api-key để chọn thông tin đó một cách rõ ràng.

Runner cần có:

  • Node.js 22 trở lên.
  • Python 3.10 trở lên.
  • Gói @openai/codex-security đã phát hành, được cài đặt bên ngoài bản checkout của kho lưu trữ.
  • Lịch sử của phần đầu và phần gốc của pull request hoặc merge request để Git có thể tính merge base.

Thêm quy trình làm việc GitHub Actions

Đối với kho lưu trữ riêng tư hoặc nội bộ, hãy bật GitHub Code Security trước khi tải SARIF lên.

Tạo .github/workflows/codex-security.yml. Trước khi checkout pull request, hãy cài đặt @openai/codex-security trong $RUNNER_TEMP/codex-security để tệp thực thi đáng tin cậy có sẵn tại $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

Quy trình làm việc checkout phần đầu của pull request, tính merge base của nó và quét các thay đổi đã commit giữa các bản sửa đổi đó. Lịch sử đầy đủ giúp xác định chính xác mục tiêu. persist-credentials: false ngăn token của kho lưu trữ xuất hiện trong cấu hình Git đã checkout. Việc cài đặt CLI trước khi checkout và chạy CLI bằng đường dẫn tuyệt đối giúp các tệp thực thi do kho lưu trữ kiểm soát không tiếp cận thông tin xác thực dùng để quét. --auth api-key chọn rõ ràng API key đã được giới hạn phạm vi. Quá trình quét lưu lịch sử trong một thư mục trạng thái có thể ghi nằm ngoài kho lưu trữ.

--json ghi một tài liệu JSON hoàn chỉnh vào stdout, vì vậy quy trình làm việc có thể lưu tài liệu đó trực tiếp. Tiến trình, bản tóm tắt hoàn tất và lỗi vẫn nằm trên stderr. Điều này khác với codex exec --json, vốn phát luồng sự kiện JSON Lines.

Bước xuất đọc một bản quét đã hoàn tất và được niêm phong rồi ghi SARIF. Bước này không thay đổi môi trường chạy Codex và thông tin xác thực. Các artifact quét có thể chứa đoạn mã nguồn dễ bị tấn công, bằng chứng và chi tiết khắc phục. Hãy chọn biện pháp kiểm soát truy cập và khoảng thời gian lưu giữ ngắn phù hợp với kho lưu trữ của bạn.

Thêm pipeline GitLab CI/CD

GitLab có thể tiếp nhận báo cáo SARIF 2.1.0 trên GitLab Ultimate 19.2 trở lên. Hãy thêm một biến CI/CD CODEX_SECURITY_API_KEY được che và ẩn trước khi chạy pipeline.

Thêm giai đoạn security và job Codex Security vào .gitlab-ci.yml ở thư mục gốc. Giữ nguyên mọi giai đoạn và job hiện có trong tệp. Theo mặc định, ví dụ này quét các thay đổi của merge request. Đặt CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH thành "true" để cũng quét toàn bộ nhánh mặc định:

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

Theo mặc định, job chỉ chạy cho merge request từ các nhánh trong cùng dự án, vì vậy pipeline của fork không nhận được thông tin xác thực dùng để quét. Đặt CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH thành "true" ở cấp nhóm, dự án hoặc pipeline để cũng chạy một lượt quét toàn phần tiêu chuẩn trên nhánh mặc định. Quét toàn phần mất nhiều thời gian và chi phí hơn quét diff.

GIT_DEPTH: "0" cung cấp lịch sử cần thiết để tính merge base từ CI_MERGE_REQUEST_DIFF_BASE_SHACI_COMMIT_SHA cho các lượt quét merge request.

Job cài đặt CLI trong /tmp, chạy CLI bằng đường dẫn tuyệt đối và chỉ cung cấp API key cho tiến trình quét. artifacts: when: always giữ lại báo cáo SARIF khi quá trình quét thất bại, còn artifacts:access: maintainer giới hạn quyền truy cập vào kết quả quét chi tiết.

Các thay đổi đối với .gitlab-ci.yml có thể làm lộ biến CI/CD, vì vậy hãy xem xét các thay đổi của pipeline trước khi chạy job. Nếu bạn bảo vệ CODEX_SECURITY_API_KEY, GitLab chỉ cung cấp biến này cho các merge request cùng dự án giữa các nhánh được bảo vệ và chỉ khi người dùng có thể truy cập nhánh đích.

Chọn chính sách mức độ nghiêm trọng

Cả hai ví dụ đều chỉ tạo báo cáo vì chúng bỏ qua --fail-on-severity. Khi bạn đã sẵn sàng để các phát hiện ảnh hưởng đến bước kiểm tra, hãy thêm một ngưỡng vào lệnh quét:

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

Các ngưỡng được hỗ trợ là critical, high, mediumlow. Một ngưỡng bao gồm các phát hiện ở mức độ nghiêm trọng đó trở lên.

Bước quét sử dụng các mã thoát sau:

Mã thoát Ý nghĩa
0 Quá trình quét hoàn tất với phạm vi bao phủ đầy đủ và mọi chính sách đã cấu hình đều đạt yêu cầu.
1 Bản quét đã hoàn tất chứa một phát hiện ở ngưỡng hoặc cao hơn.
2 CLI phát hiện lỗi đầu vào hoặc lỗi khi chạy, hoặc bản quét đã hoàn tất có phạm vi bao phủ không đầy đủ.
130 Ctrl-C đã ngắt quá trình quét.
143 SIGTERM đã chấm dứt quá trình quét.

Một bản quét có phạm vi bao phủ partial hoặc unknown sẽ trả về 2, ngay cả khi không có chính sách mức độ nghiêm trọng. CLI vẫn ghi các phát hiện và thông tin phạm vi bao phủ hiện có. Hãy xem xét các khu vực bị hoãn trong coverage.json trước khi coi bước kiểm tra là có tính kết luận.

Thử lại với thư mục kết quả hiện có

Sử dụng một thư mục runner mới cho mỗi job CI. Đối với runner lâu dài hoặc tự lưu trữ, hãy lưu giữ kết quả trước đó bằng --archive-existing:

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

Lệnh này lưu trữ các kết quả trước đó và bắt đầu với một thư mục quét trống.

Khắc phục sự cố quét CI

  • Git ref không xác định hoặc diff không mong đợi: Tải lịch sử của phần gốc và phần đầu, tính merge base rồi truyền rõ ràng cả hai bản sửa đổi.
  • Thư mục đầu ra được bảo vệ hoặc không trống: Chọn một thư mục riêng tư bên ngoài Git worktree bao quanh. Dùng --archive-existing khi thư mục đã chứa kết quả.
  • Thiếu thông tin xác thực: Xác nhận rằng CODEX_SECURITY_API_KEY có sẵn cho quy trình làm việc hoặc pipeline đáng tin cậy và được ánh xạ trực tiếp tới biến môi trường OPENAI_API_KEY của tiến trình quét.
  • Lỗi lịch sử quét: Đặt CODEX_SECURITY_STATE_DIR thành một thư mục có thể ghi nằm ngoài kho lưu trữ.
  • Lỗi thiết lập Python: Xác nhận runner sử dụng Python 3.10 trở lên.
  • Phạm vi bao phủ không đầy đủ: Xem xét coverage.json, bao gồm các khu vực bị hoãn và câu hỏi chưa giải quyết, sau đó chạy lại với mục tiêu hoặc môi trường phù hợp.
  • Lỗi xuất SARIF: Xác nhận rằng quá trình quét đã hoàn tất và toàn bộ thư mục quét đang có sẵn. Quá trình xuất xác thực các artifact đã niêm phong trước khi ghi SARIF.
  • Lỗi tải SARIF lên: Đối với GitHub Actions, hãy xác nhận tổ chức của bạn đã bật GitHub Code Security cho kho lưu trữ và quy trình làm việc cấp actions: read, contents: readsecurity-events: write. Đối với GitLab CI/CD, hãy xác nhận dự án sử dụng GitLab Ultimate 19.2 trở lên và job tải lên một tệp SARIF 2.1.0 thông qua artifacts:reports:sarif.

Để biết mọi lệnh, cờ, artifact và trường đầu ra, hãy xem tài liệu tham chiếu CLI. Để thực hiện quy trình xem xét CI tương tác dựa trên plugin, hãy xem Xem xét các thay đổi mã về bảo mật.