Ejecutar Codex Security en CI
Analiza los cambios de solicitudes de incorporación y fusión, conserva los resultados estructurados, carga SARIF y establece una política de gravedad.
Ejecuta la CLI de Codex Security en CI para revisar los cambios exactos de una solicitud de incorporación o de fusión, conservar los hallazgos y la cobertura y, de manera opcional, hacer que la comprobación falle al alcanzar una gravedad determinada. Comienza con resultados informativos, revisa la calidad y el tiempo de ejecución del análisis y, después, añade una política de gravedad adecuada para tu repositorio.
Esta guía incluye ejemplos para GitHub Actions y GitLab CI/CD. Los mismos comandos de análisis y exportación funcionan en otros sistemas de CI.
Preparar el flujo de trabajo
Guarda una API key de OpenAI en el almacén de secretos de tu proveedor de CI como
CODEX_SECURITY_API_KEY.
Asigna este secreto directamente a la variable de entorno OPENAI_API_KEY del paso de
análisis. Limita la credencial al proceso de análisis y usa
--auth api-key para seleccionarla explícitamente.
El ejecutor necesita:
- Node.js 22 o posterior.
- Python 3.10 o posterior.
- El paquete publicado
@openai/codex-security, instalado fuera de la copia de trabajo del repositorio. - El historial de la cabecera y la base de la solicitud de incorporación o fusión para que Git pueda calcular la base de fusión.
Añadir el flujo de trabajo de GitHub Actions
En repositorios privados o internos, habilita GitHub Code Security antes de cargar SARIF.
Crea .github/workflows/codex-security.yml. Antes de obtener la solicitud de
incorporación, instala @openai/codex-security en
$RUNNER_TEMP/codex-security para que el ejecutable de confianza esté disponible en
$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: 7El flujo de trabajo obtiene la cabecera de la solicitud de incorporación, calcula su base de fusión y
analiza los cambios confirmados entre esas revisiones. El historial completo mantiene el
objetivo exacto. persist-credentials: false mantiene el token del repositorio fuera de
la configuración de Git obtenida. Instalar la CLI antes de obtener el código y
ejecutarla mediante su ruta absoluta mantiene los ejecutables controlados por el repositorio alejados de
la credencial de análisis. --auth api-key selecciona explícitamente la API key limitada.
El análisis guarda su historial en un directorio de estado con permisos de escritura fuera del
repositorio.
--json escribe un documento JSON completo en stdout, por lo que el flujo de trabajo puede guardarlo
directamente. El progreso, los resúmenes de finalización y los errores permanecen en stderr. Esto
es distinto de codex exec --json, que emite un flujo de eventos JSON Lines.
El paso de exportación lee un análisis completado y sellado y escribe SARIF. No modifica el tiempo de ejecución ni las credenciales de Codex. Los artefactos del análisis pueden contener fragmentos de código fuente vulnerable, evidencias y detalles de corrección. Elige controles de acceso y un periodo breve de conservación adecuados para tu repositorio.
Añadir la canalización de GitLab CI/CD
GitLab puede incorporar
informes SARIF 2.1.0
en GitLab Ultimate 19.2 o posterior. Añade una variable de CI/CD
CODEX_SECURITY_API_KEY enmascarada y oculta antes de ejecutar la canalización.
Añade la etapa security y el trabajo de Codex Security al archivo raíz .gitlab-ci.yml.
Conserva todas las etapas y los trabajos existentes en el archivo. El ejemplo analiza de forma predeterminada los
cambios de las solicitudes de fusión. Establece CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH en "true"
para analizar también la rama predeterminada completa:
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.sarifDe forma predeterminada, el trabajo solo se ejecuta para solicitudes de fusión procedentes de ramas del mismo
proyecto, por lo que las canalizaciones de bifurcaciones no reciben la credencial de análisis. Establece
CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH en "true" en el nivel de grupo, proyecto o
canalización para ejecutar también un análisis completo estándar en la rama predeterminada. Los análisis
completos tardan más y cuestan más que los análisis de diferencias.
GIT_DEPTH: "0" proporciona el historial necesario para calcular la base de fusión a partir de
CI_MERGE_REQUEST_DIFF_BASE_SHA y CI_COMMIT_SHA en los análisis de solicitudes de fusión.
El trabajo instala la CLI en /tmp, la ejecuta mediante su ruta absoluta y expone la
API key únicamente al proceso de análisis. artifacts: when: always conserva el informe
SARIF cuando el análisis falla, mientras que artifacts:access: maintainer limita el acceso
a los resultados detallados del análisis.
Los cambios en .gitlab-ci.yml pueden exponer variables de CI/CD, por lo que debes revisar los cambios de la canalización
antes de ejecutar el trabajo. Si
proteges CODEX_SECURITY_API_KEY,
GitLab lo pone a disposición únicamente de solicitudes de fusión del mismo proyecto entre
ramas protegidas y solo cuando el usuario puede acceder a la rama de destino.
Elegir una política de gravedad
Ambos ejemplos solo generan informes porque omiten --fail-on-severity. Cuando estés
listo para que los hallazgos afecten a la comprobación, añade un umbral al comando de
análisis:
"$CODEX_SECURITY_BIN" scan . \
--diff origin/main \
--output-dir /path/outside/repository/results \
--fail-on-severity highLos umbrales admitidos son critical, high, medium y low. Un
umbral incluye los hallazgos de esa gravedad y de gravedades superiores.
El paso de análisis utiliza estos códigos de salida:
| Salida | Significado |
|---|---|
0 |
El análisis finalizó con cobertura completa y se cumplió cualquier política configurada. |
1 |
El análisis completado contiene un hallazgo con una gravedad igual o superior al umbral. |
2 |
La CLI encontró un error de entrada o de ejecución, o el análisis completado tiene una cobertura incompleta. |
130 |
Ctrl-C interrumpió el análisis. |
143 |
SIGTERM finalizó el análisis. |
Un análisis con cobertura partial o unknown devuelve 2, incluso sin una política de
gravedad. La CLI sigue escribiendo los hallazgos y la cobertura disponibles. Revisa las
áreas aplazadas en coverage.json antes de considerar concluyente la comprobación.
Reintentar con un directorio de resultados existente
Usa un directorio nuevo del ejecutor para cada trabajo de CI. En un ejecutor persistente o autohospedado,
conserva un resultado anterior con --archive-existing:
"$CODEX_SECURITY_BIN" scan . \
--diff origin/main \
--output-dir /path/outside/repository/results \
--archive-existingEl comando archiva los resultados anteriores y comienza con un directorio de análisis vacío.
Solucionar problemas de un análisis de CI
- Referencia de Git desconocida o diferencia inesperada: Obtén el historial de la base y la cabecera, calcula la base de fusión y pasa ambas revisiones explícitamente.
- Directorio de salida protegido o no vacío: Elige un directorio privado
fuera del árbol de trabajo de Git contenedor. Usa
--archive-existingcuando el directorio ya contenga resultados. - Faltan credenciales: Confirma que
CODEX_SECURITY_API_KEYesté disponible para el flujo de trabajo o la canalización de confianza y se asigne directamente a la variable de entornoOPENAI_API_KEYdel proceso de análisis. - Error del historial de análisis: Establece
CODEX_SECURITY_STATE_DIRen un directorio con permisos de escritura fuera del repositorio. - Error de configuración de Python: Confirma que el ejecutor use Python 3.10 o posterior.
- Cobertura incompleta: Revisa
coverage.json, incluidas las superficies aplazadas y las preguntas abiertas, y vuelve a ejecutar el análisis con un objetivo o entorno adecuado. - Error de exportación SARIF: Confirma que el análisis haya finalizado y que el directorio completo del análisis esté disponible. La exportación valida los artefactos sellados antes de escribir SARIF.
- Error de carga de SARIF: Para GitHub Actions, confirma que tu organización
haya activado GitHub Code Security para el repositorio y que el flujo de trabajo conceda
actions: read,contents: readysecurity-events: write. Para GitLab CI/CD, confirma que el proyecto use GitLab Ultimate 19.2 o posterior y que el trabajo cargue un archivo SARIF 2.1.0 medianteartifacts:reports:sarif.
Para consultar todos los comandos, indicadores, artefactos y campos de salida, revisa la referencia de la CLI. Para realizar una revisión interactiva de CI basada en complementos, consulta Revisar cambios de código para detectar problemas de seguridad.