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 cho bước kiểm tra thất bại tại một 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 một OpenAI API key trong kho bí mật của nhà cung cấp CI dưới tên 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 cho quy trình quét và dùng --auth api-key để chọn rõ ràng thông tin đó.

Chỉ chạy quy trình làm việc cho những kho lưu trữ và pull request mà bạn tin cậy. Các lượt quét sử dụng quyền cục bộ của runner và không tạm dừng để xin phê duyệt. Quy trình quét có thể kế thừa môi trường của job, vì vậy đừng đưa các token và thông tin xác thực đám mây không liên quan vào môi trường đó.

Runner cần:

  • Node.js 22 (22.13.0 trở lên), 24 hoặc 26.
  • 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ử head và base của pull request hoặc merge request để Git có thể tính toán 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 head của pull request, tính toán merge base và quét các thay đổi đã commit giữa những 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 tách các tệp thực thi do kho lưu trữ kiểm soát khỏi 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. Lượt quét lưu lịch sử trong một thư mục trạng thái có thể ghi nằm bên ngoài kho lưu trữ.

--json ghi một tài liệu JSON hoàn chỉnh vào stdout để 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 được ghi vào stderr. Cách này khác với codex exec --json, vốn phát ra một luồng sự kiện JSON Lines.

Bước xuất đọc một lượt quét đã hoàn tất và được niêm phong rồi ghi SARIF. Bước này không tác động đến Codex runtime 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à thời hạn lưu giữ ngắn phù hợp với kho lưu trữ của bạn.

Thêm pipeline GitLab CI/CD

Để triển khai quy trình sản xuất có chức năng quét nhánh mặc định được bảo vệ, quét sâu theo lịch và yêu cầu chọn tham gia, kiểm soát chính sách SARIF riêng biệt cùng tùy chọn tạo yêu cầu hợp nhất nháp đã xác minh, hãy xem Chạy Codex Security trong GitLab CI/CD.

GitLab có thể nhập 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.

Ví dụ tối thiểu sau đây thêm một tác vụ security chỉ quét vào tệp .gitlab-ci.yml ở thư mục gốc. Giữ nguyên mọi giai đoạn và tác vụ hiện có trong tệp. Theo mặc định, tác vụ này quét các thay đổi của yêu cầu hợp nhất. Đặt CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH thành "true" để quét cả 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@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

Theo mặc định, job chỉ chạy cho merge request từ các nhánh trong cùng một 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 đầy đủ tiêu chuẩn trên nhánh mặc định. Quét đầy đủ mất nhiều thời gian và chi phí hơn quét phần thay đổi.

GIT_DEPTH: "0" cung cấp lịch sử cần thiết để tính toán 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 quy trình quét. artifacts: when: always lưu giữ báo cáo SARIF khi lượt 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 thay đổi 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 đó cho merge request trong 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.

Hướng dẫn riêng về GitLab mở rộng tác vụ tối thiểu này thành quy trình sản xuất được liên kết ở đầu phần này.

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

Cả hai ví dụ 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 từ lượt quét hiện tại ở mức độ nghiêm trọng đó trở lên. Các phát hiện mở trước đó xuất hiện trong bản tóm tắt kho lưu trữ không ảnh hưởng đến chính sách.

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

Mã thoát Ý nghĩa
0 Lượt 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 Lượt quét đã hoàn tất chứa một phát hiện ở mức ngưỡng hoặc cao hơn.
2 CLI phát hiện lỗi đầu vào hoặc runtime, hoặc lượt quét đã hoàn tất có phạm vi bao phủ chưa đầy đủ.
130 Ctrl-C đã ngắt lượt quét.
143 SIGTERM đã chấm dứt lượt quét.

Một lượt 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à dữ liệu 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 bằng 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ữ 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 phần thay đổi không như mong đợi: Tìm nạp lịch sử base và head, tính toán 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 quy 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 bên ngoài kho lưu trữ.
  • Lỗi thiết lập Python: Xác nhận rằng runner sử dụng Python 3.10 trở lên.
  • Phạm vi bao phủ chưa đầy đủ: Xem xét coverage.json, bao gồm các bề mặt bị hoãn và câu hỏi còn bỏ ngỏ, 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 lượt quét đã hoàn tất và có sẵn toàn bộ thư mục quét. 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 rằng 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 rằng 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. Để xem xét CI tương tác dựa trên plugin, hãy xem Xem xét các thay đổi mã nguồn về bảo mật.