Français

Exécuter Codex Security dans la CI

Analysez les modifications des pull requests et merge requests, conservez les résultats structurés, chargez les fichiers SARIF et définissez une politique de sévérité.

Exécutez la CLI Codex Security dans la CI pour examiner précisément les modifications d’une pull request ou d’une merge request, conserver les résultats et la couverture, et éventuellement faire échouer le contrôle à un niveau de sévérité choisi. Commencez par des résultats informatifs, examinez la qualité de l’analyse et sa durée d’exécution, puis ajoutez une politique de sévérité adaptée à votre dépôt.

Ce guide contient des exemples pour GitHub Actions et GitLab CI/CD. Les mêmes commandes d’analyse et d’exportation fonctionnent dans les autres systèmes de CI.

Préparer le workflow

Stockez une API key OpenAI dans le magasin de secrets de votre fournisseur de CI sous le nom CODEX_SECURITY_API_KEY.

Associez directement ce secret à la variable d’environnement OPENAI_API_KEY de l’étape d’analyse. Limitez les droits de l’identifiant au processus d’analyse et utilisez --auth api-key pour le sélectionner explicitement.

Exécutez le workflow uniquement pour les dépôts et les pull requests auxquels vous faites confiance. Les analyses utilisent les autorisations locales du runner et ne s’interrompent pas pour demander une approbation. Les processus d’analyse peuvent hériter de l’environnement de la tâche ; n’y placez donc pas de jetons ni d’identifiants cloud sans rapport avec l’analyse.

Le runner nécessite :

  • Node.js 22 (22.13.0 ou version ultérieure), 24 ou 26.
  • Python 3.10 ou version ultérieure.
  • Le package @openai/codex-security publié, installé en dehors du checkout du dépôt.
  • L’historique de la tête et de la base de la pull request ou merge request afin que Git puisse calculer la base de fusion.

Ajouter le workflow GitHub Actions

Pour les dépôts privés ou internes, activez GitHub Code Security avant de charger un fichier SARIF.

Créez .github/workflows/codex-security.yml. Avant d’effectuer le checkout de la pull request, installez @openai/codex-security sous $RUNNER_TEMP/codex-security afin que l’exécutable de confiance soit disponible à l’emplacement $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

Le workflow effectue le checkout de la tête de la pull request, calcule sa base de fusion et analyse les modifications validées entre ces révisions. L’historique complet garantit une cible exacte. persist-credentials: false empêche l’ajout du jeton du dépôt à la configuration Git du checkout. Installer la CLI avant le checkout et l’exécuter via son chemin absolu empêche les exécutables contrôlés par le dépôt d’accéder à l’identifiant d’analyse. --auth api-key sélectionne explicitement l’API key aux droits limités. L’analyse enregistre son historique dans un répertoire d’état accessible en écriture situé hors du dépôt.

--json écrit un document JSON complet sur stdout, ce qui permet au workflow de l’enregistrer directement. La progression, les résumés de fin et les erreurs restent sur stderr. Cela se distingue de codex exec --json, qui émet un flux d’événements JSON Lines.

L’étape d’exportation lit une analyse terminée et scellée, puis écrit le fichier SARIF. Elle ne modifie ni l’environnement d’exécution Codex ni les identifiants. Les artefacts d’analyse peuvent contenir des extraits de code source vulnérable, des preuves et des détails de correction. Choisissez des contrôles d’accès et une courte durée de conservation adaptés à votre dépôt.

Ajouter le pipeline GitLab CI/CD

Pour un workflow de production comprenant des analyses protégées de la branche par défaut, des analyses approfondies planifiées et facultatives, un contrôle des politiques SARIF distinct et la création facultative de demandes de fusion vérifiées à l’état de brouillon, consultez Exécuter Codex Security dans GitLab CI/CD.

GitLab peut ingérer des rapports SARIF 2.1.0 avec GitLab Ultimate 19.2 ou version ultérieure. Ajoutez une variable CI/CD CODEX_SECURITY_API_KEY masquée et cachée avant d’exécuter le pipeline.

L’exemple minimal suivant ajoute une tâche security dédiée à l’analyse dans le fichier .gitlab-ci.yml à la racine. Conservez toutes les phases et tâches existantes du fichier. Par défaut, elle analyse les modifications des demandes de fusion. Définissez CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH sur "true" pour analyser également l’intégralité de la branche par défaut :

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

Par défaut, la tâche s’exécute uniquement pour les merge requests provenant de branches du même projet, afin que les pipelines de forks ne reçoivent pas l’identifiant d’analyse. Définissez CODEX_SECURITY_FULL_SCAN_DEFAULT_BRANCH sur "true" au niveau du groupe, du projet ou du pipeline pour exécuter également une analyse complète standard sur la branche par défaut. Les analyses complètes sont plus longues et plus coûteuses que les analyses différentielles.

GIT_DEPTH: "0" fournit l’historique nécessaire au calcul de la base de fusion à partir de CI_MERGE_REQUEST_DIFF_BASE_SHA et CI_COMMIT_SHA pour les analyses de merge requests.

La tâche installe la CLI sous /tmp, l’exécute via son chemin absolu et expose l’API key uniquement au processus d’analyse. artifacts: when: always conserve le rapport SARIF lorsque l’analyse échoue, tandis que artifacts:access: maintainer limite l’accès aux résultats d’analyse détaillés.

Les modifications apportées à .gitlab-ci.yml peuvent exposer des variables CI/CD ; examinez donc les modifications du pipeline avant d’exécuter la tâche. Si vous protégez CODEX_SECURITY_API_KEY, GitLab le rend disponible uniquement pour les merge requests du même projet entre des branches protégées, et seulement lorsque l’utilisateur peut accéder à la branche cible.

Le guide GitLab dédié développe cette tâche minimale pour en faire le workflow de production indiqué au début de cette section.

Choisir une politique de sévérité

Les deux exemples génèrent uniquement des rapports, car ils omettent --fail-on-severity. Lorsque vous êtes prêt à faire en sorte que les résultats influent sur le contrôle, ajoutez un seuil à la commande d’analyse :

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

Les seuils pris en charge sont critical, high, medium et low. Un seuil inclut les résultats de l’analyse actuelle dont la sévérité est égale ou supérieure. Les résultats ouverts antérieurs affichés dans le résumé du dépôt n’influent pas sur la politique.

L’étape d’analyse utilise les codes de sortie suivants :

Sortie Signification
0 L’analyse s’est terminée avec une couverture complète et toute politique configurée a été respectée.
1 L’analyse terminée contient un résultat dont la sévérité atteint ou dépasse le seuil.
2 La CLI a rencontré une erreur d’entrée ou d’exécution, ou la couverture de l’analyse terminée est incomplète.
130 Ctrl-C a interrompu l’analyse.
143 SIGTERM a mis fin à l’analyse.

Une analyse dont la couverture est partial ou unknown renvoie 2, même sans politique de sévérité. La CLI écrit tout de même les résultats et les informations de couverture disponibles. Examinez les zones différées dans coverage.json avant de considérer le contrôle comme concluant.

Réessayer avec un répertoire de résultats existant

Utilisez un nouveau répertoire de runner pour chaque tâche de CI. Avec un runner persistant ou auto-hébergé, conservez un résultat antérieur avec --archive-existing :

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

La commande archive les résultats antérieurs et démarre avec un répertoire d’analyse vide.

Résoudre les problèmes d’une analyse de CI

  • Référence Git inconnue ou diff inattendu : récupérez l’historique de la base et de la tête, calculez la base de fusion et transmettez explicitement les deux révisions.
  • Répertoire de sortie protégé ou non vide : choisissez un répertoire privé hors du worktree Git englobant. Utilisez --archive-existing lorsque le répertoire contient déjà des résultats.
  • Identifiants manquants : vérifiez que CODEX_SECURITY_API_KEY est accessible au workflow ou pipeline de confiance et directement associé à la variable d’environnement OPENAI_API_KEY du processus d’analyse.
  • Erreur d’historique d’analyse : définissez CODEX_SECURITY_STATE_DIR sur un répertoire accessible en écriture situé hors du dépôt.
  • Erreur de configuration de Python : vérifiez que le runner utilise Python 3.10 ou version ultérieure.
  • Couverture incomplète : examinez coverage.json, notamment les surfaces différées et les questions ouvertes, puis relancez l’analyse avec une cible ou un environnement approprié.
  • Erreur d’exportation SARIF : vérifiez que l’analyse s’est terminée et que le répertoire d’analyse complet est disponible. L’exportation valide les artefacts scellés avant d’écrire le fichier SARIF.
  • Erreur de chargement SARIF : pour GitHub Actions, vérifiez que votre organisation a activé GitHub Code Security pour le dépôt et que le workflow accorde actions: read, contents: read et security-events: write. Pour GitLab CI/CD, vérifiez que le projet utilise GitLab Ultimate 19.2 ou version ultérieure et que la tâche charge un fichier SARIF 2.1.0 via artifacts:reports:sarif.

Pour chaque commande, option, artefact et champ de sortie, consultez la référence de la CLI. Pour un examen interactif de CI reposant sur un plugin, consultez Examiner les modifications du code sous l’angle de la sécurité.