1# Run Codex Security in CI1# Codex Security CI/CD overview
2 2
3> For the complete documentation index, see [llms.txt](https://learn.chatgpt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.3> For the complete documentation index, see [llms.txt](https://learn.chatgpt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.
4 4
5Run the Codex Security CLI in CI to review the exact changes in a pull request5Run Codex Security in CI to review pull-request or merge-request changes, scan
6or merge request, keep findings and coverage, and optionally fail the check at6your default branch, and preserve findings and coverage. See your CI
7a chosen severity. Start with advisory results, review scan quality and7provider's guide for the workflow, report handling, and enforcement options.
8runtime, then add a severity policy that fits your repository.
9 8
10Install the public `@openai/codex-security` package. Running scans still9## Prepare the workflow
11 requires Codex Security access.
12 10
13This page provides a GitHub Actions workflow. For GitLab, use the11Running scans requires Codex Security access. Store an OpenAI API key in your
14[GitLab CI/CD guide](https://learn.chatgpt.com/docs/security/cli/ci/gitlab). The same scan and export12CI provider's secret store as `CODEX_SECURITY_API_KEY`, then map it to the scan
15commands work in other CI systems.13process's `OPENAI_API_KEY` environment variable.
16 14
17## Prepare the workflow15Run scans only for repositories and changes you trust. Scans use the runner's
16local permissions and don't pause for approval. Keep unrelated credentials and
17deployment steps in separate jobs.
18
19### Use an approved Daybreak model
20
21For Daybreak Blue access through [Trusted Access for
22Cyber](https://learn.chatgpt.com/docs/cyber-safety#trusted-access-for-cyber), set the GitHub Action's
23`model` input to `gpt-6-sol` and `cyber-access-program` to
24`daybreak_blue`. For direct CLI scans, use `--model gpt-6-sol`
25and `--cyber-access-program daybreak_blue`.
26
27Use an API key from a project that has Daybreak Blue enabled and access to the
28selected model. Setting the model or program does not grant access. See
29[Configure optional Daybreak access](https://learn.chatgpt.com/docs/security/cli/ci/github#configure-optional-daybreak-access)
30for the Action settings.
31
32## Choose an integration
33
34### Add the GitHub Actions workflow
35
36Use the [GitHub Actions guide](https://learn.chatgpt.com/docs/security/cli/ci/github) for pull-request
37checks and scheduled repository scans. The Codex Security Action installs the CLI,
38saves reports, and adds a job summary and source annotations. The guide includes
39runner setup, optional Daybreak access, SARIF upload, and severity enforcement.
40
41### Add the GitLab CI/CD pipeline
42
43Use the [GitLab CI/CD guide](https://learn.chatgpt.com/docs/security/cli/ci/gitlab) for merge-request,
44default-branch, and scheduled scans. Start with a scan-only pipeline, then
45adopt report handling, policy enforcement, and optional verified remediation.
46
47### Use another CI system
48
49Install the public `@openai/codex-security` package and run the CLI directly.
50Use `--auth api-key`, choose a repository or diff scan, and save the output in a
51private directory outside the Git worktree. Your runner must provide the
52dependencies described in the [CLI quickstart](https://learn.chatgpt.com/docs/security/cli#check-the-prerequisites).
18 53
19Store an OpenAI API key in your CI provider's secret store as54See the [CLI reference](https://learn.chatgpt.com/docs/security/cli/reference) for scan options, diff
20`CODEX_SECURITY_API_KEY`.55revisions, report formats, state and output directories, and retry behavior.
21
22Map this secret directly to the scan step's `OPENAI_API_KEY` environment
23variable. Keep the credential scoped to the scan process and use
24`--auth api-key` to select it explicitly.
25
26Run the workflow only for repositories and pull requests you trust. Scans use
27the runner's local permissions and don't pause for approval. Scan processes
28can inherit the job environment, so keep unrelated tokens and cloud
29credentials out of it.
30
31The runner needs:
32
33- Node.js 24 or 26, or Node.js 22 starting with 22.13.0.
34- Python 3.10 or later.
35- The published `@openai/codex-security` package, installed outside the
36 repository checkout.
37- The pull-request or merge-request head and base history so Git can calculate
38 the merge base.
39
40## Add the GitHub Actions workflow
41
42For private or internal repositories, enable [GitHub Code Security](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github)
43before you upload SARIF.
44
45Create `.github/workflows/codex-security.yml`. Before checking out the pull
46request, install `@openai/codex-security` under
47`$RUNNER_TEMP/codex-security` so the trusted executable is available at
48`$RUNNER_TEMP/codex-security/node_modules/.bin/codex-security`:
49
50```yaml
51name: Codex Security scan
52
53on:
54 pull_request:
55
56jobs:
57 codex-security:
58 if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]'
59 runs-on: ubuntu-latest
60 permissions:
61 actions: read
62 contents: read
63 security-events: write
64 steps:
65 - name: Set up Node.js
66 uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
67 with:
68 node-version: "26"
69
70 - name: Set up Python
71 uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
72 with:
73 python-version: "3.14"
74
75 - name: Install Codex Security
76 run: |
77 set -euo pipefail
78 npm install \
79 --prefix "$RUNNER_TEMP/codex-security" \
80 --ignore-scripts \
81 --no-audit \
82 --no-fund \
83 @openai/codex-security
84
85 - name: Verify Codex Security
86 env:
87 CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
88 run: |
89 set -euo pipefail
90 test -x "$CODEX_SECURITY_BIN"
91 "$CODEX_SECURITY_BIN" --version
92
93 - name: Check out the pull request
94 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
95 with:
96 ref: ${{ github.event.pull_request.head.sha }}
97 fetch-depth: 0
98 persist-credentials: false
99
100 - name: Scan the pull request
101 env:
102 OPENAI_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }}
103 CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
104 CODEX_SECURITY_STATE_DIR: ${{ runner.temp }}/codex-security-state
105 BASE_SHA: ${{ github.event.pull_request.base.sha }}
106 HEAD_SHA: ${{ github.event.pull_request.head.sha }}
107 SCAN_DIR: ${{ runner.temp }}/codex-security-results
108 run: |
109 set -euo pipefail
110 BASE_REVISION="$(git merge-base "$BASE_SHA" "$HEAD_SHA")"
111 "$CODEX_SECURITY_BIN" scan . \
112 --diff "$BASE_REVISION" \
113 --head "$HEAD_SHA" \
114 --auth api-key \
115 --output-dir "$SCAN_DIR" \
116 --json > "$RUNNER_TEMP/codex-security.json"
117
118 - name: Export SARIF
119 id: export-sarif
120 if: always()
121 env:
122 CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
123 SCAN_DIR: ${{ runner.temp }}/codex-security-results
124 SARIF_FILE: ${{ runner.temp }}/codex-security.sarif
125 run: |
126 set -euo pipefail
127 if test -f "$SCAN_DIR/scan-manifest.json"; then
128 "$CODEX_SECURITY_BIN" export "$SCAN_DIR" \
129 --export-format sarif \
130 --source-root "$GITHUB_WORKSPACE" \
131 --output "$SARIF_FILE"
132 echo "available=true" >> "$GITHUB_OUTPUT"
133 fi
134
135 - name: Upload SARIF
136 if: always() && steps.export-sarif.outputs.available == 'true'
137 uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4
138 with:
139 sarif_file: ${{ runner.temp }}/codex-security.sarif
140 ref: refs/pull/${{ github.event.pull_request.number }}/head
141 sha: ${{ github.event.pull_request.head.sha }}
142 category: codex-security
143
144 - name: Preserve scan results
145 if: always()
146 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
147 with:
148 name: codex-security-results
149 path: |
150 ${{ runner.temp }}/codex-security-results
151 ${{ runner.temp }}/codex-security.json
152 if-no-files-found: warn
153 retention-days: 7
154```
155
156The workflow checks out the pull-request head, calculates its merge base, and
157scans the committed changes between those revisions. Full history keeps the
158target exact. `persist-credentials: false` keeps the repository token out of
159the checked-out Git configuration. Installing the CLI before checkout and
160running its absolute path keeps repository-controlled executables away from
161the scan credential. `--auth api-key` explicitly selects the scoped API key.
162The scan saves its history in a writable state directory outside the
163repository.
164
165`--json` writes one complete JSON document to stdout, so the workflow can save
166it directly. Progress, completion summaries, and errors remain on stderr. This
167differs from `codex exec --json`, which emits a JSON Lines event stream.
168
169The export step reads a completed, sealed scan and writes SARIF. It leaves the
170Codex runtime and credentials untouched. Scan artifacts can contain vulnerable
171source snippets, evidence, and remediation details. Set who can read them and
172how long to keep them.
173
174## Add the GitLab CI/CD pipeline
175
176For a production workflow with protected default-branch scans, opt-in scheduled
177deep scans, separate SARIF policy gating, and optional verified draft merge
178requests, use [Run Codex Security in GitLab
179CI/CD](https://learn.chatgpt.com/docs/security/cli/ci/gitlab).
180
181The GitLab guide includes a scan-only starting point and the complete pipeline.
182It covers protected variables, artifact access, and a separate policy gate so
183GitLab can ingest findings even when the scanner returns a nonzero exit code.
184 56
185## Choose a severity policy57## Choose a severity policy
186 58
187The GitHub example is report-only because it omits `--fail-on-severity`. Once you59Start with advisory results. Review representative findings, scan coverage,
188are ready to make findings affect the check, add a threshold to the scan60cost, and runtime before deciding which outcomes should block a merge.
189command:61
190 62Evaluate both findings and scan completeness. The severity policy evaluates
191```bash63findings from the current scan at or above the configured threshold. Earlier
192"$CODEX_SECURITY_BIN" scan . \64open findings in the repository summary don't affect that policy. An
193 --diff origin/main \65incomplete scan can leave areas without a review even when it reports no
194 --output-dir /path/outside/repository/results \66findings.
195 --fail-on-severity high67
196```68In the Action release `action-v0.3.0`, a completed scan means the configured analysis
197 69produced a valid result. Scope exclusions, deferred investigations, and other
198The supported thresholds are `critical`, `high`, `medium`, and `low`. A70coverage notes do not bypass the severity policy. Scanner and required
199threshold includes findings from the current scan at that severity and above.71reporting errors fail the job. CLI `0.3.0` returns exit code `2` for input,
200Earlier open findings shown in the repository summary don't affect the policy.72execution, result-integrity, or required-output failures. Review `coverage.json` when assessing the result; completion does
201 73not certify that every vulnerability was found.
202The scan step uses these exit codes:74
203 75Configure enforcement in the [GitHub Actions
204| Exit | Meaning |76guide](https://learn.chatgpt.com/docs/security/cli/ci/github#choose-a-severity-policy) or the [GitLab
205| ----- | --------------------------------------------------------------------------- |77CI/CD guide](https://learn.chatgpt.com/docs/security/cli/ci/gitlab#separate-reporting-from-policy-enforcement).
206| `0` | The scan completed, and any configured severity policy passed. |78For direct CLI integrations, see [severity enforcement and exit
207| `1` | The completed scan contains a finding at or above the threshold. |79codes](https://learn.chatgpt.com/docs/security/cli/reference#exit-codes-and-signals).
208| `2` | An input, execution, result-integrity, or required-output failure occurred. |
209| `130` | Ctrl-C interrupted the scan. |
210| `143` | SIGTERM terminated the scan. |
211
212Coverage gaps don't change a completed scan's exit code or bypass its severity
213policy. Exit `0` doesn't mean every file was reviewed. Review excluded paths,
214deferred work, and open questions in `coverage.json` when assessing the result.
215
216## Retry with an existing result directory
217
218Use a fresh runner directory for each CI job. For a persistent or self-hosted
219runner, preserve an earlier result with `--archive-existing`:
220
221```bash
222"$CODEX_SECURITY_BIN" scan . \
223 --diff origin/main \
224 --output-dir /path/outside/repository/results \
225 --archive-existing
226```
227
228The command archives the earlier results and starts with an empty scan directory.
229
230## Troubleshoot a CI scan
231
232- **Unknown Git ref or unexpected diff:** Fetch the base and head history,
233 calculate the merge base, and pass both revisions explicitly.
234- **Protected or non-empty output directory:** Choose a private directory
235 outside the enclosing Git worktree. Use `--archive-existing` when the
236 directory already contains results.
237- **Missing credentials:** Confirm that `CODEX_SECURITY_API_KEY` is available to
238 the trusted workflow or pipeline and mapped directly to the scan process's
239 `OPENAI_API_KEY` environment variable.
240- **Scan history error:** Set `CODEX_SECURITY_STATE_DIR` to a writable
241 directory outside the repository.
242- **Python setup error:** Confirm that the runner uses Python 3.10 or later.
243- **Incomplete coverage:** Review `coverage.json`, including deferred surfaces
244 and open questions. Resolve the reported gaps before rerunning the scan.
245- **SARIF export error:** Confirm that the scan completed and the full scan
246 directory is available. Export validates the sealed artifacts before writing
247 SARIF.
248- **SARIF upload error:** For GitHub Actions, confirm that your organization
249 turned on GitHub Code Security for the repository and the workflow grants
250 `actions: read`, `contents: read`, and `security-events: write`. For GitLab
251 CI/CD, confirm that the project uses GitLab Ultimate 19.2 or later and that
252 the job uploads a SARIF 2.1.0 file through `artifacts:reports:sarif`.
253
254For every command, flag, artifact, and output field, see the [CLI
255reference](https://learn.chatgpt.com/docs/security/cli/reference). To invoke the plugin through `codex exec`, see [Review code changes for security](https://learn.chatgpt.com/docs/security/plugin/code-changes#automate-reviews-in-cicd).