Русский

Запуск Codex Security в CI

Сканируйте изменения в pull request и merge request, сохраняйте структурированные результаты, загружайте SARIF и задавайте политику уровней серьезности.

Запускайте CLI Codex Security в CI, чтобы проверять конкретные изменения в pull request или merge request, сохранять обнаруженные проблемы и данные о покрытии, а также при необходимости завершать проверку с ошибкой при выбранном уровне серьезности. Начните с результатов рекомендательного характера, оцените качество и время выполнения сканирования, а затем добавьте подходящую для вашего репозитория политику уровней серьезности.

В этом руководстве приведены примеры для GitHub Actions и GitLab CI/CD. Те же команды сканирования и экспорта работают и в других системах CI.

Подготовка рабочего процесса

Сохраните API key OpenAI в хранилище секретов вашего поставщика 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, установленный вне рабочей копии репозитория.
  • История головной и базовой ветвей pull request или merge request, позволяющая Git вычислить базу слияния.

Добавление рабочего процесса GitHub Actions

Для частных или внутренних репозиториев включите GitHub Code Security, прежде чем загружать SARIF.

Создайте .github/workflows/codex-security.yml. Перед получением рабочей копии pull request установите @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, вычисляет ее базу слияния и сканирует зафиксированные изменения между этими версиями. Полная история обеспечивает точность целевого диапазона. 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 и необязательными проверенными черновиками запросов на слияние используйте руководство Запуск Codex Security в GitLab CI/CD.

GitLab может принимать отчеты SARIF 2.1.0 в GitLab Ultimate 19.2 или более поздней версии. Перед запуском конвейера добавьте маскируемую и скрытую переменную CI/CD CODEX_SECURITY_API_KEY.

В следующем минимальном примере в корневой файл добавляется задание security, выполняющее только сканирование .gitlab-ci.yml. Сохраните в файле все существующие этапы и задания. По умолчанию оно сканирует изменения в запросах на слияние. Задайте для 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 из ветвей того же проекта, поэтому конвейеры форков не получают учетные данные сканирования. Задайте для CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH значение "true" на уровне группы, проекта или конвейера, чтобы также выполнять обычное полное сканирование ветви по умолчанию. Полное сканирование занимает больше времени и стоит дороже, чем сканирование различий.

GIT_DEPTH: "0" предоставляет историю, необходимую для вычисления базы слияния по CI_MERGE_REQUEST_DIFF_BASE_SHA и CI_COMMIT_SHA при сканировании merge request.

Задание устанавливает 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 или неожиданные различия: Получите историю базовой и головной версий, вычислите базу слияния и явно передайте обе версии.
  • Защищенный или непустой каталог вывода: Выберите частный каталог за пределами охватывающего рабочего дерева 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: read, contents: read и security-events: write. Для GitLab CI/CD убедитесь, что проект использует GitLab Ultimate 19.2 или более поздней версии и что задание загружает файл SARIF 2.1.0 через artifacts:reports:sarif.

Сведения обо всех командах, флагах, артефактах и полях вывода см. в справочнике по CLI. Сведения об интерактивной проверке CI на основе плагина см. в разделе Проверка изменений кода на наличие проблем безопасности.