53## Automate reviews in CI/CD53## Automate reviews in CI/CD
54 54
55Run the same `$codex-security:security-diff-scan` skill from CI when the runner55Run the same `$codex-security:security-diff-scan` skill from CI when the runner
56can invoke the Codex CLI without interaction. The runner must already have56can invoke the Codex CLI without interaction. First install the CLI and plugin
57Codex Security installed in the `CODEX_HOME` used by `codex exec`. A fresh57without exposing the scan credential:
58runner doesn't have marketplace plugins installed by default, and
59`openai/codex-action` doesn't install the plugin.
60 58
61Before running the scan:59```bash
60npm install --global @openai/codex
61codex plugin add codex-security@openai-curated
62```
63
64Then expose an OpenAI API key from your CI secret store as
65`CODEX_SECURITY_API_KEY` only for the scan:
62 66
631. Provision Codex Security in the runner's `CODEX_HOME`.67```bash
642. Check out the exact base and head revisions with their Git history.68CODEX_API_KEY="$CODEX_SECURITY_API_KEY" codex exec \
653. Set the runner's platform temporary directory, such as `TMPDIR`, to a69 --sandbox workspace-write \
66 writable artifact location. The diff-scan workflow reviews the checkout70 "Use \$codex-security:security-diff-scan to review changes from $BASE_REVISION to $HEAD_REVISION for security regressions. Do not modify the checkout."
67 without changing it, but it writes its sealed scan bundle and final report71```
68 outside the repository.
694. Start with advisory results. Review scan quality and runtime before making
70 the job a required check.
71 72
72Then invoke the plugin explicitly:73The scan writes its output to
74`$TMPDIR/codex-security-scans/<repository>/<scan-id>/`:
75
76| File | Contents |
77| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
78| `findings.json` | Findings with stable identifiers, severity, confidence, source locations, and remediation. Use it to create pull-request comments or feed downstream tools. |
79| `scan-manifest.json` | Sealed scan receipt with the reviewed target, revisions, and artifact hashes. |
80| `coverage.json` | Reviewed and deferred surfaces, exclusions, and coverage completeness. |
81
82The [`findings.json` schema](https://github.com/openai/plugins/blob/main/plugins/codex-security/schemas/findings.schema.json)
83defines the complete structure. Some key fields are:
84
85| Field | Type | Description |
86| ------------------------- | ------ | ---------------------------------------------------------------------- |
87| `documentType` | String | Identifies the document as `codex-security.findings`. |
88| `schemaVersion` | String | Identifies the findings schema version. |
89| `scanId` | String | Identifies the scan that produced the findings. |
90| `findings` | Array | Contains zero or more finding objects. |
91| `findings[].findingId` | String | Stable finding identifier derived from the finding fingerprint. |
92| `findings[].occurrenceId` | String | Identifies this occurrence of the finding in a specific scan. |
93| `findings[].ruleId` | String | Identifies the vulnerability family. |
94| `findings[].identity` | Object | Contains the semantic anchor and optional sibling-instance identifier. |
95| `findings[].fingerprints` | Object | Contains the fingerprint algorithm and primary fingerprint. |
96| `findings[].title` | String | Provides the short finding title. |
97| `findings[].summary` | String | Summarizes the vulnerability and its impact. |
98| `findings[].severity` | Object | Contains the severity level and optional scoring details. |
99| `findings[].confidence` | Object | Contains the confidence level and rationale. |
100| `findings[].taxonomy` | Object | Contains the vulnerability category and CWE identifiers. |
101| `findings[].locations` | Array | Lists affected files, line numbers, and location roles. |
102| `findings[].remediation` | String | Describes the recommended fix. |
103| `findings[].provenance` | Object | Identifies the source of the finding. |
104
105For example, this command prints one tab-separated row per finding:
73 106
74```bash107```bash
75export CODEX_HOME=/path/to/provisioned-codex-home108jq -r '
76export TMPDIR=/path/to/writable/temp109 .findings[] |
110 [.findingId, .severity.level, .confidence.level, .locations[0].path, .locations[0].startLine, .title] |
111 @tsv
112' findings.json
113```
77 114
78codex exec \115These examples assume a trusted Linux runner with Node.js and `npm`, Git, Python
1163, `jq`, and the provider's command-line tools. The `npm` global package prefix
117must be writable.
118
119Here are examples of how to use Codex Security in common pipelines:
120
121<Tabs
122 id="codex-security-ci-examples"
123 param="ci"
124 defaultTab="github"
125 tabs={[
126 { id: "github", label: "GitHub Actions" },
127 { id: "gitlab", label: "GitLab CI/CD" },
128 { id: "azure", label: "Azure Pipelines" },
129 { id: "jenkins", label: "Jenkins" },
130 ]}
131>
132 <div slot="github">
133
134```yaml
135name: Codex Security review
136
137on:
138 pull_request:
139
140jobs:
141 security-review:
142 if: github.event.pull_request.head.repo.full_name == github.repository
143 runs-on: ubuntu-latest
144 permissions:
145 contents: read
146 pull-requests: write
147 steps:
148 - uses: actions/checkout@v5
149 with:
150 ref: ${{ github.event.pull_request.head.sha }}
151 fetch-depth: 0
152 persist-credentials: false
153
154 - name: Install Codex Security
155 env:
156 CODEX_HOME: ${{ runner.temp }}/codex-home
157 run: |
158 npm install --global @openai/codex
159 codex plugin add codex-security@openai-curated
160
161 - name: Review code changes
162 env:
163 CODEX_SECURITY_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }}
164 CODEX_HOME: ${{ runner.temp }}/codex-home
165 TMPDIR: ${{ runner.temp }}/codex-security
166 BASE_SHA: ${{ github.event.pull_request.base.sha }}
167 HEAD_REVISION: ${{ github.event.pull_request.head.sha }}
168 run: |
169 BASE_REVISION="$(git merge-base "$BASE_SHA" "$HEAD_REVISION")"
170 CODEX_API_KEY="$CODEX_SECURITY_API_KEY" codex exec \
79 --sandbox workspace-write \171 --sandbox workspace-write \
80 --output-last-message "$TMPDIR/codex-security-review.md" \172 "Use \$codex-security:security-diff-scan to review changes from $BASE_REVISION to $HEAD_REVISION for security regressions. Do not modify the checkout."
81 'Use $codex-security:security-diff-scan to review changes from <base-revision> to <head-revision> for security regressions. Do not modify the checkout. Return the final report path, findings summary, reviewed surfaces, deferred coverage, and open questions.'173
174 - name: Comment with findings
175 if: always()
176 env:
177 GH_TOKEN: ${{ github.token }}
178 PR_NUMBER: ${{ github.event.pull_request.number }}
179 run: |
180 findings="$(find "${{ runner.temp }}/codex-security/codex-security-scans" -name findings.json -print -quit 2>/dev/null || true)"
181 test -n "$findings" || exit 0
182 jq -r '
183 "## Codex Security findings",
184 "",
185 if (.findings | length) == 0 then "No findings reported."
186 else .findings[] | "- **\(.severity.level | ascii_upcase)**: \(.title) (`\(.locations[0].path):\(.locations[0].startLine)`)\n \(.summary)"
187 end
188 ' "$findings" | gh pr comment "$PR_NUMBER" --body-file -
189
190 - uses: actions/upload-artifact@v4
191 if: always()
192 with:
193 name: codex-security-review
194 path: ${{ runner.temp }}/codex-security/codex-security-scans
82```195```
83 196
84Archive the generated scan bundle and final report, then publish the Markdown197 </div>
85summary through your CI/CD system. If you use `openai/codex-action`, point its198
86`codex-home` input at the same provisioned directory and pass the skill prompt199 <div slot="gitlab">
87above. The action can install and run the Codex CLI, but plugin provisioning is200
88a separate prerequisite.201Create masked `CODEX_SECURITY_API_KEY` and `GITLAB_TOKEN` CI/CD variables. The
202GitLab token needs API access to create a merge-request note.
203
204```yaml
205codex-security-review:
206 rules:
207 - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_PROJECT_ID == $CI_PROJECT_ID'
208 variables:
209 GIT_DEPTH: "0"
210 script:
211 - |
212 codex_security_api_key="$CODEX_SECURITY_API_KEY"
213 unset CODEX_SECURITY_API_KEY GITLAB_TOKEN
214 export CODEX_HOME="/tmp/codex-home-$CI_JOB_ID"
215 export TMPDIR="/tmp/codex-security-$CI_JOB_ID"
216 export BASE_REVISION="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
217 export HEAD_REVISION="${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$CI_COMMIT_SHA}"
218 npm install --global @openai/codex
219 codex plugin add codex-security@openai-curated
220 CODEX_API_KEY="$codex_security_api_key" codex exec \
221 --sandbox workspace-write \
222 "Use \$codex-security:security-diff-scan to review changes from $BASE_REVISION to $HEAD_REVISION for security regressions. Do not modify the checkout."
223 after_script:
224 - |
225 gitlab_token="$GITLAB_TOKEN"
226 unset CODEX_SECURITY_API_KEY GITLAB_TOKEN
227 scan_root="/tmp/codex-security-$CI_JOB_ID/codex-security-scans"
228 findings="$(find "$scan_root" -name findings.json -print -quit 2>/dev/null || true)"
229 if [ -n "$findings" ]; then
230 jq -r '
231 "## Codex Security findings",
232 "",
233 if (.findings | length) == 0 then "No findings reported."
234 else .findings[] | "- **\(.severity.level | ascii_upcase)**: \(.title) (`\(.locations[0].path):\(.locations[0].startLine)`)\n \(.summary)"
235 end
236 ' "$findings" > codex-security-comment.md
237 curl --fail --request POST \
238 --header "PRIVATE-TOKEN: $gitlab_token" \
239 --form "body=<codex-security-comment.md" \
240 "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
241 fi
242 if [ -d "$scan_root" ]; then
243 tar -czf codex-security-artifacts.tar.gz -C "$scan_root" .
244 fi
245 artifacts:
246 when: always
247 paths:
248 - codex-security-artifacts.tar.gz
249```
250
251 </div>
252
253 <div slot="azure">
254
255```yaml
256trigger: none
257
258pool:
259 vmImage: ubuntu-latest
260
261steps:
262 - checkout: self
263 fetchDepth: 0
264
265 - bash: |
266 set -euo pipefail
267 export CODEX_HOME="$AGENT_TEMPDIRECTORY/codex-home"
268 npm install --global @openai/codex
269 codex plugin add codex-security@openai-curated
270 displayName: Install Codex Security
271
272 - bash: |
273 set -euo pipefail
274 export CODEX_HOME="$AGENT_TEMPDIRECTORY/codex-home"
275 export TMPDIR="$AGENT_TEMPDIRECTORY/codex-security"
276 export HEAD_REVISION="$SYSTEM_PULLREQUEST_SOURCECOMMITID"
277 export BASE_REVISION="$(git merge-base HEAD^1 "$HEAD_REVISION")"
278 CODEX_API_KEY="$CODEX_SECURITY_API_KEY" codex exec \
279 --sandbox workspace-write \
280 "Use \$codex-security:security-diff-scan to review changes from $BASE_REVISION to $HEAD_REVISION for security regressions. Do not modify the checkout."
281 displayName: Review code changes
282 condition: and(succeeded(), ne(variables['System.PullRequest.IsFork'], 'True'))
283 env:
284 CODEX_SECURITY_API_KEY: $(CODEX_SECURITY_API_KEY)
285
286 - publish: $(Agent.TempDirectory)/codex-security/codex-security-scans
287 artifact: codex-security-review
288 condition: always()
289```
290
291For Azure Repos, configure a **Build validation** branch policy to run the
292pipeline on pull requests.
293
294 </div>
295
296 <div slot="jenkins">
297
298```groovy
299pipeline {
300 agent { label 'linux' }
301 stages {
302 stage('Codex Security review') {
303 when {
304 allOf {
305 changeRequest()
306 expression { !env.CHANGE_FORK?.trim() }
307 }
308 }
309 steps {
310 sh '''#!/usr/bin/env bash
311 set -euo pipefail
312 export CODEX_HOME="/tmp/codex-home-$BUILD_TAG"
313 export TMPDIR="/tmp/codex-security-$BUILD_TAG"
314 mkdir -p "$TMPDIR"
315 git fetch --no-tags origin "$CHANGE_TARGET"
316 target="$(git rev-parse FETCH_HEAD)"
317 git fetch --no-tags origin "$CHANGE_BRANCH"
318 git rev-parse FETCH_HEAD > "$TMPDIR/head"
319 git merge-base "$target" "$(cat "$TMPDIR/head")" > "$TMPDIR/base"
320 npm install --global @openai/codex
321 codex plugin add codex-security@openai-curated
322 '''
323 withCredentials([string(credentialsId: 'codex-security-api-key', variable: 'CODEX_SECURITY_API_KEY')]) {
324 sh '''#!/usr/bin/env bash
325 set +x
326 set -euo pipefail
327 export CODEX_HOME="/tmp/codex-home-$BUILD_TAG"
328 export TMPDIR="/tmp/codex-security-$BUILD_TAG"
329 export HEAD_REVISION="$(cat "$TMPDIR/head")"
330 export BASE_REVISION="$(cat "$TMPDIR/base")"
331 CODEX_API_KEY="$CODEX_SECURITY_API_KEY" codex exec \
332 --sandbox workspace-write \
333 "Use \$codex-security:security-diff-scan to review changes from $BASE_REVISION to $HEAD_REVISION for security regressions. Do not modify the checkout."
334 '''
335 }
336 }
337 post {
338 always {
339 sh '''#!/usr/bin/env bash
340 set -euo pipefail
341 scan_root="/tmp/codex-security-$BUILD_TAG/codex-security-scans"
342 if [ -d "$scan_root" ]; then
343 tar -czf codex-security-artifacts.tar.gz -C "$scan_root" .
344 fi
345 '''
346 archiveArtifacts artifacts: 'codex-security-artifacts.tar.gz', allowEmptyArchive: true
347 }
348 }
349 }
350 }
351}
352```
353
354 </div>
355</Tabs>
356
357The examples skip forked pull requests. Run credentialed jobs only from a
358protected pipeline definition and only for contributors trusted with the scan
359credential. Archive `codex-security-scans` to keep the structured findings,
360manifest, and coverage artifacts. Start with advisory results and review
361coverage and runtime before making the job a required check.
89 362
90For API-key handling, sandbox controls, fork protections, and a GitHub Actions363For API-key handling and sandbox controls, see [Non-interactive
91workflow, see the [Codex GitHub Action guide](https://learn.chatgpt.com/docs/github-action).364mode](https://learn.chatgpt.com/docs/non-interactive-mode). If your organization permits the [Codex
365GitHub Action](https://learn.chatgpt.com/docs/github-action), it can install the CLI at runtime, but
366you must still install the plugin first and point the action's `codex-home`
367input at the same `CODEX_HOME`.