Запуск Codex Security в CI
Сканируйте изменения в запросах на включение и слияние, сохраняйте структурированные результаты, загружайте SARIF и задавайте политику уровней серьёзности.
Запускайте Codex Security CLI в CI, чтобы проверять конкретные изменения в запросе на включение или слияние, сохранять обнаруженные проблемы и данные о покрытии, а также при необходимости завершать проверку с ошибкой при выбранном уровне серьёзности. Начните с результатов рекомендательного характера, оцените качество и продолжительность сканирования, а затем добавьте политику уровней серьёзности, подходящую вашему репозиторию.
В этом руководстве приведены примеры для GitHub Actions и GitLab CI/CD. Те же команды сканирования и экспорта работают и в других системах CI.
Подготовка рабочего процесса
Сохраните API key OpenAI в хранилище секретов поставщика CI под именем
CODEX_SECURITY_API_KEY.
Напрямую сопоставьте этот секрет с переменной окружения OPENAI_API_KEY
шага сканирования. Ограничьте область действия учётных данных процессом сканирования и используйте
--auth api-key, чтобы явно выбрать их.
Среде выполнения необходимы:
- Node.js 22 или более поздней версии.
- Python 3.10 или более поздней версии.
- Опубликованный пакет
@openai/codex-security, установленный вне рабочей копии репозитория. - История главной и базовой ветвей запроса на включение или слияние, чтобы 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Рабочий процесс получает рабочую копию главной ветви запроса на включение, вычисляет базу слияния и
сканирует зафиксированные изменения между этими ревизиями. Полная история обеспечивает точность
целевого диапазона. persist-credentials: false не допускает попадания токена репозитория
в конфигурацию Git полученной рабочей копии. Установка CLI до получения рабочей копии и
запуск по абсолютному пути не позволяют исполняемым файлам под управлением репозитория получить доступ к
учётным данным сканирования. --auth api-key явно выбирает API key с ограниченной областью действия.
Сканирование сохраняет историю в доступном для записи каталоге состояния за пределами
репозитория.
--json записывает в stdout один полный документ JSON, поэтому рабочий процесс может сохранить
его напрямую. Ход выполнения, итоговые сводки и ошибки остаются в stderr. Это
отличается от codex exec --json, который выводит поток событий JSON Lines.
Шаг экспорта считывает завершённое, запечатанное сканирование и записывает SARIF. Он не изменяет среду выполнения Codex и учётные данные. Артефакты сканирования могут содержать уязвимые фрагменты исходного кода, доказательства и сведения об устранении проблем. Выберите средства управления доступом и короткий срок хранения, подходящие для вашего репозитория.
Добавление конвейера GitLab CI/CD
GitLab может принимать
отчёты SARIF 2.1.0
в GitLab Ultimate 19.2 или более поздней версии. Перед запуском конвейера добавьте замаскированную и скрытую
переменную CI/CD CODEX_SECURITY_API_KEY.
Добавьте этап security и задание Codex 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
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По умолчанию задание выполняется только для запросов на слияние из ветвей того же
проекта, поэтому конвейеры форков не получают учётные данные сканирования. Задайте для
CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH значение "true" на уровне группы, проекта или
конвейера, чтобы также выполнять стандартное полное сканирование ветви по умолчанию. Полные
сканирования занимают больше времени и стоят дороже, чем сканирования различий.
GIT_DEPTH: "0" предоставляет историю, необходимую для вычисления базы слияния на основе
CI_MERGE_REQUEST_DIFF_BASE_SHA и CI_COMMIT_SHA при сканировании запросов на слияние.
Задание устанавливает CLI в /tmp, запускает его по абсолютному пути и предоставляет
API key только процессу сканирования. artifacts: when: always сохраняет отчёт SARIF
при сбое сканирования, а artifacts:access: maintainer ограничивает доступ
к подробным результатам сканирования.
Изменения в .gitlab-ci.yml могут раскрыть переменные CI/CD, поэтому проверяйте изменения
конвейера перед запуском задания. Если вы
защитите CODEX_SECURITY_API_KEY,
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 на основе плагина описана в разделе Проверка изменений кода на безопасность.