Codex Security CLIを見つけたとき、最初に迷うのは「既存のCodex Security pluginと何が違うのか」「CIに入れてよいのか」「exit code 1や2をどう扱うのか」です。security scanは導入しただけでは安全になりません。むしろ、coverageが不完全なのにgreen扱いする、CI runnerのsecretをscan processへ渡しすぎる、SARIFだけ保存して判断根拠を失う、といった事故が起きやすくなります。
この記事では、Codex Security CLIをterminalとCIのsecurity gateとして使う設計に限定します。Codex内でSecurity plugin skillを呼び、review laneを分ける話は Codex Security plugin 実践ガイド が担当します。CIでcodex execを安全に回す一般論は Codex CI / non-interactive 設計ガイド、Codex自体の実行権限は Codex sandbox / approvals 設計ガイド、scan結果をmerge前に受け入れる判断は Codex レビュー完了ゲート設計ガイド に分けます。
今回の論点は、local scan、PR差分scan、SARIF export、severity policy、coverage、bulk scanを、同じ「セキュリティ確認」という言葉へ潰さないことです。
先に結論: CLIはscanの実行面、pluginはCodex内のreview lane
Codex Security CLIとCodex Security pluginは近い名前ですが、運用上の入口が違います。
| 目的 | 向いている入口 | 成果物 | 注意点 |
|---|---|---|---|
| 手元でrepositoryをscanする | Codex Security CLI | report.md、findings.json、coverage.json | local OS権限と環境変数を継承しうる |
| PR差分をCIで確認する | Codex Security CLI | JSON、SARIF、coverage、exit code | 最初はadvisory運用にする |
| Codex作業中にsecurity laneを呼ぶ | Codex Security plugin | plugin workflowの結果 | terminal / CI gateとは分ける |
| 通常のPR reviewerを回す | Codex code review | review comment | security scanの代替にしない |
私なら、CLIは「scanを再現可能な成果物として残す道具」、pluginは「Codex内のsecurity review laneを明示する道具」として分けます。この分け方にすると、CI gate、ローカル調査、Codex上の修正依頼が混線しません。
導入前に3つだけ確認する
公式docsでは、@openai/codex-security packageはpublicですが、scan実行にはCodex Security accessが必要です。さらにCLIはNode.js 22.13.0以降の22系、24、26と、scan / export / history用にPython 3.10以降を要求します。
導入前の確認は次で十分です。
npx @openai/codex-security --version
npx @openai/codex-security info --json
npx @openai/codex-security --help
local利用ではChatGPT sign-inを使えます。CIやheadless runnerではAPI keyをscan stepだけに渡し、--auth api-keyで明示します。
npx @openai/codex-security login
npx @openai/codex-security scan . --auth chatgpt
CIで使うcredentialは、build、deploy、cloud adminのtokenと同じjob環境へ混ぜない方がよいです。scan processはlocal permissionでrepositoryを読み、環境変数を継承しうるため、不要なsecretを持ったrunnerで動かすほど結果の扱いが難しくなります。
local scanは結果ディレクトリをrepo外に置く
最初のscanでやるべきことは、脆弱性を全部見つけることではありません。repositoryをどう見たか、何を見られなかったか、どの成果物をreviewに渡せるかを確認することです。
REPOSITORY=/path/to/repository
SCAN_DIR=/path/outside/repository/codex-security-results
npx @openai/codex-security scan "$REPOSITORY" \
--output-dir "$SCAN_DIR" \
--dry-run
npx @openai/codex-security scan "$REPOSITORY" \
--output-dir "$SCAN_DIR"
--dry-runは、repository、target、knowledge base、output pathなどの入力を確認します。長いscanを始める前に、出力先がrepo内になっていないか、対象pathが想定どおりか、credentialを使う段階まで進まない形で見るために使います。
scan結果はprivateなrepo外ディレクトリへ置きます。結果にはsource excerpt、finding detail、validation logが入ることがあるため、dist/やPR差分へ混ぜるものではありません。
artifactsはSARIFだけで終わらせない
CLIのcompleted scanは、読み物としてのreport.mdと構造化artifactをまとめて保存します。
codex-security-results/
scan-manifest.json
findings.json
coverage.json
report.md
artifacts/
exports/
results.sarif
それぞれ役割が違います。
| ファイル | 使い道 |
|---|---|
scan-manifest.json | scanの対象、状態、producer、artifact record |
findings.json | finding ID、severity、confidence、location、evidence、validation |
coverage.json | 見たsurface、除外、deferred work、open question、coverage completeness |
report.md | 人間が読むsummary |
results.sarif | GitHub code scanningなどへの連携 |
SARIFはtriage UIに載せるには便利ですが、scanの根拠全部ではありません。特にcoverage、deferred surface、open questionはcoverage.jsonを見ないと失われます。CIでSARIFだけuploadして「security checked」とするのは弱いです。
exit codeはmerge判定の語彙に変換する
Codex Security CLIのexit codeは、そのままmerge可否にせず、チームの語彙へ変換します。
| exit code | 意味 | CIでの扱い |
|---|---|---|
0 | 完了し、coverage completeでseverity policyも通過 | pass候補 |
1 | 完了scanでpolicy以上のfindingがある | security findingあり |
2 | 入力、runtime、export、bulk error、またはpartial / unknown coverage | 判定不能としてfail |
130 / 143 | interrupt / SIGTERM | 判定不能としてfail |
重要なのは2です。partial coverageやunknown coverageは「高severity findingがなかった」ではありません。見切れていない、またはscanとして信頼できない状態です。coverage.jsonでdeferred surfaceとopen questionを確認し、必要ならtargetを絞って再scanします。
PR CIは最初からblockしない
CIではPR差分を対象にします。repository全体のdeep scanを毎回PRで回すと、既存負債や未確認surfaceが混ざり、merge判断が遅くなります。
BASE_REVISION="$(git merge-base "$BASE_SHA" "$HEAD_SHA")"
codex-security scan . \
--diff "$BASE_REVISION" \
--head "$HEAD_SHA" \
--auth api-key \
--output-dir "$SCAN_DIR" \
--json > "$RUNNER_TEMP/codex-security.json"
最初の2週間は、私ならadvisoryにします。
| 段階 | CI挙動 | 人間の見るもの |
|---|---|---|
| 導入週 | 結果を保存してsummaryだけcomment | report.md、coverage、scan時間 |
| 安定後 | --fail-on-severity highを設定 | high以上、coverage gap |
| 成熟後 | SARIF uploadとowner routingを追加 | finding owner、期限、再検証 |
いきなり--fail-on-severity mediumのように広く止めると、toolの信頼性検証より先に運用が詰まります。まずcomplete coverageが安定して出るか、findingsがreview可能な粒度か、runtimeとcostがCIの予算内に収まるかを見ます。
severity policyはcoverageとセットで読む
--fail-on-severity highを設定すると、complete scanでhigh以上のfindingが出たときにexit code 1になります。
npx @openai/codex-security scan . \
--diff origin/main \
--fail-on-severity high
ただし、severity policyはcoverageの代わりではありません。scanがpartialなら、policy以上のfindingがなくてもexit code 2です。この仕様はむしろ良い設計です。不完全なscanをgreenへ落とすと、セキュリティ確認ではなく「未確認を隠すCI」になります。
CI commentでは、severityだけでなく次を必ず出します。
{
"security_gate": "needs_review",
"coverage": "partial",
"policy": "fail_on_high",
"confirmed_findings": 0,
"blocking_reason": "coverage_not_complete",
"next_action": "Review coverage.json deferred surfaces and rerun targeted scan"
}
この形なら、findingがないのか、scanが見切れていないのかをreviewerが区別できます。
CI runnerのsecretを最小化する
CI guideでは、API keyをCI secret storeに置き、scan stepのOPENAI_API_KEYへ直接mapする構成が示されています。このとき、同じjobにdeploy key、cloud admin credential、package publish tokenを置かない方が安全です。
最小構成は次です。
permissions:
actions: read
contents: read
security-events: write
env:
OPENAI_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }}
CODEX_SECURITY_STATE_DIR: ${{ runner.temp }}/codex-security-state
actions: read、contents: read、security-events: writeはSARIF uploadまで含めた最小permissionです。checkoutではpersist-credentials: falseを使い、scan packageはrepository checkoutの外、たとえば$RUNNER_TEMP/codex-securityへ入れます。scanがrepository内のnode_modulesやpackage scriptへ依存しない形にするためです。
local developer hookは軽く使う
FAQには、staged / unstaged change向けのpre-commit checkとしてinstall-hookが示されています。
npx @openai/codex-security install-hook
ただし、hookは開発者の手元の速度を壊しやすいので、最初は重要repositoryやsecurity-sensitive directoryに限定します。全員のpre-commitでdeep scanを走らせるのは過剰です。
私なら次の分担にします。
| 場所 | 役割 |
|---|---|
| pre-commit | 明らかなPR前事故を早めに見る |
| PR CI | merge前の差分gate |
| scheduled full scan | repository全体の変化と既存findingの再確認 |
| bulk scan | 複数repoの棚卸し |
developer hookはCIの代替ではありません。手元でpassしても、CIのbase / head / credential / OS / dependency状態は違います。
bulk scanはinventoryを固定してから使う
複数repositoryを見たい場合、Codex Security CLIにはbulk scan導線があります。ここで避けたいのは、organization全体をいきなり広くscanして、結果を誰も読めない状態にすることです。
bulk scan前に、inventoryへ最低限これを持たせます。
id,repository,revision,scope,mode,prompt
api,https://github.com/example/api.git,0123456789abcdef0123456789abcdef01234567,src,standard,Review authorization boundaries.
admin,https://github.com/example/admin.git,fedcba9876543210fedcba9876543210fedcba98,.,deep,Review privileged admin workflows.
必須列はid、repository、revisionです。特にrevisionはfull commit hashで固定します。「先週のscanと今週のscanで何が変わったか」を見るには、同じbranch名ではなく、対象commitを記録する必要があります。scan結果を比較するときも、同じ範囲がcoverageされているかを先に確認します。
fix確認はscan比較だけで終わらせない
findingを直したあと、元scanをrerunし、before / afterをcompareできます。
npx @openai/codex-security scans rerun BEFORE_SCAN_ID
npx @openai/codex-security scans compare BEFORE_SCAN_ID AFTER_SCAN_ID
ただし、FAQが強調しているように、findingが消えたことやscan comparisonだけでは修正証明になりません。元のpathがcoverageされているかを確認し、重要findingはcurrent checkoutに対して直接recheckします。
npx @openai/codex-security validate /path/to/original/findings.json \
"Recheck the authorization bypass in src/billing.ts against the current code"
PR本文には、次を残します。
codex_security_evidence:
before_scan: "scan_..."
after_scan: "scan_..."
coverage: "complete"
finding_status: "resolved"
direct_recheck: "passed"
regression_test: "pnpm test authz"
remaining_gap: "runtime tenant policy not exercised in this scan"
これで、AI scanの結果をそのまま信じるのではなく、review可能な証拠として扱えます。
TypeScript SDKはproduct組み込み用に残す
Codex SecurityにはTypeScript SDKもあります。SDKは、アプリケーションや社内developer toolからscanを起動し、typed findings、coverage、artifact path、progress、cost control、cancellationを扱うための入口です。
ただし、最初からSDKへ行く必要はありません。
| やりたいこと | 入口 |
|---|---|
| 手元でscanしたい | CLI |
| GitHub ActionsでPR差分を見る | CLI |
| GitHub code scanningへSARIFを送る | CLI |
| 社内portalからscan lifecycleを管理する | TypeScript SDK |
| Codex task内のsecurity review laneを使う | Security plugin |
SDKを使う場合も、local OS permission、環境変数継承、repo外のprivate result directory、coverage completenessの確認はCLIと同じです。
最小運用テンプレート
小さなチームなら、最初はこのくらいで十分です。
codex_security_cli_policy:
local_scan:
output_dir: "repo外のprivate directory"
auth: "chatgpt or api-keyを明示"
share:
- report.md
- findings.json
- coverage.json
pull_request_ci:
target: "merge-base..head"
mode: "standard"
initial_behavior: "advisory"
later_policy: "fail-on-severity high"
fail_if:
- coverage is partial
- coverage is unknown
- exit code is 2
artifacts:
keep:
- scan-manifest.json
- findings.json
- coverage.json
- report.md
- results.sarif
retention_days: 30
secrets:
provide_only:
- CODEX_SECURITY_API_KEY
forbid:
- deploy credentials
- cloud admin tokens
- production database URLs
よくある失敗
1. exit 2をwarningにする
exit 2は不完全なscanやruntime/export errorを含みます。warningへ落とすと、coverage gapを見落とします。CIでは判定不能としてfailさせ、coverage.jsonを読む方がよいです。
2. SARIF uploadだけで完了にする
SARIFは結果の連携形式です。scanの対象、coverage、deferred work、open question、artifact provenanceまで残すにはscan-manifest.json、coverage.json、report.mdも必要です。
3. plugin記事の設定をCLIに流用する
$codex-security:security-diff-scanはCodex plugin laneの入口です。CLIではnpx @openai/codex-security scan . --diff ...のように、terminal / CIの再現性を中心に考えます。
4. scan jobに余計なsecretを渡す
scanはlocal permissionで動き、環境を継承しうる作業です。security scanだから強いcredentialを渡す、ではなく、security scanだからcredentialを減らします。
5. fix後に「findingが消えた」だけで閉じる
scan comparisonは便利ですが、coverageが変わっていれば意味が弱くなります。direct recheck、regression test、owner reviewを合わせて修正完了にします。
参考
- Codex Security CLI quickstart
- Run Codex Security in CI
- Codex Security CLI reference
- Codex Security CLI FAQ
- Codex Security TypeScript SDK
- openai/codex-security
Codex Security CLIは「AIが見たから安心」の道具ではありません。scan対象、coverage、finding、artifact、exit code、credential境界をそろえて、初めてCIのsecurity gateとして使えます。
最初はlocal scanとadvisory CIで十分です。coverageが安定し、findingsをreviewできるようになってからseverity policyでmergeを止める。この順番なら、セキュリティ確認を形だけのcheckではなく、チームが読める証拠にできます。