Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
58cac16
refactor(sdk): share Codex session setup
mldangelo-oai Aug 17, 2026
8b7c258
feat(cli): draft SECURITY.md for owner review
mldangelo-oai Aug 17, 2026
e8adefb
Merge commit 'b88216a799d4b8e99fdc739590c1860b69c44a3b' into mdangelo…
mldangelo-oai Aug 17, 2026
60acf79
Merge commit 'e8adefbbbc07c9e8f66de46ffedf01ee9e707007' into mdangelo…
mldangelo-oai Aug 17, 2026
1839c8c
fix(sdk): keep policy previews scoped and terminal-safe
mldangelo-oai Aug 17, 2026
da2ddb7
fix(cli): keep policy inputs and previews scoped
mldangelo-oai Aug 17, 2026
1fd07bb
fix(sdk): package policy helpers and handle missing links
mldangelo-oai Aug 17, 2026
b9751af
fix(sdk): validate policy paths before model access
mldangelo-oai Aug 17, 2026
8dde31c
refactor(cli): reuse scan setup across commands
mldangelo-oai Aug 17, 2026
ac17379
Merge shared command helpers and current main
mldangelo-oai Aug 17, 2026
3fe740e
fix(sdk): validate policy inputs before drafting
mldangelo-oai Aug 17, 2026
3f34dbf
refactor(policy): keep reporting checks at application
mldangelo-oai Aug 17, 2026
251c583
fix(cli): honor policy settings and cancellation
mldangelo-oai Aug 18, 2026
ae673b5
fix(sdk): preserve policy evidence and owner answers
mldangelo-oai Aug 18, 2026
8c6eacc
fix(sdk): isolate policy instructions from artifact checkouts
mldangelo-oai Aug 18, 2026
caf6d15
Merge origin/main into security-policy-drafts
mldangelo-oai Aug 22, 2026
ffa1876
fix(policy): harden draft generation boundaries
mldangelo-oai Aug 22, 2026
79a5c65
chore: merge main into security policy drafts
mldangelo-oai Aug 26, 2026
07f656b
refactor(policy): merge main and simplify draft setup
mldangelo-oai Aug 27, 2026
392ece2
test(policy): reconcile main and verify native permission fixtures
mldangelo-oai Aug 27, 2026
419e604
chore: merge final release packaging updates
mldangelo-oai Aug 27, 2026
b48d417
fix(policy): sync main and repair policy CI
mldangelo-oai Aug 28, 2026
d9f4275
chore: merge main through 0.1.23
mldangelo-oai Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Codex Security

`@openai/codex-security` is a CLI and TypeScript SDK for finding, validating, and fixing security vulnerabilities in your code.
`@openai/codex-security` is a CLI and TypeScript SDK for defining security policy and finding, validating, and fixing security vulnerabilities in your code.

**👉👉 See the [Codex Security documentation](https://learn.chatgpt.com/docs/security/cli)** for full documentation.

Expand All @@ -20,6 +20,20 @@ codex-security scan /path/to/directory

For CI, set `OPENAI_API_KEY` instead of signing in.

## Generate SECURITY.md

Draft a repository-wide or component-scoped security policy without changing the checkout:

```bash
codex-security policy .
codex-security policy . --path services/api --knowledge-base architecture.md
```

Review the proposed diff before copying the policy. Supporting architecture,
threat-model, and review documents stay outside the repository and may contain
sensitive details. See the [SDK policy guide](sdk/typescript/README.md#generate-a-security-policy)
for headless generation, saved artifacts, and SDK usage.

## TypeScript SDK

Codex Security is a Javascript package:
Expand Down
101 changes: 100 additions & 1 deletion sdk/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ npx @openai/codex-security --version
```

Use Node.js 22.13.0+ (22.x), 24.x, or 26.x on macOS, Linux, or Windows.
Scans, exports, scan history, and saved findings also need Python 3.10+
Policy drafting, scans, exports, scan history, and saved findings also need Python 3.10+
(plus `tomli` on Python 3.10).

## Run a scan from TypeScript
Expand Down Expand Up @@ -248,9 +248,108 @@ Some cybersecurity requests and protected findings require Trusted Access for
Cyber approval. Apply or check your access at
[chatgpt.com/cyber](https://chatgpt.com/cyber).

## Generate a security policy

`policy` drafts a source-backed `SECURITY.md` without changing the checkout or
creating a scan record. It uses the scan runtime and authentication, with
read-only access to the selected repository and required tools. Network access,
web search, apps, and MCP servers are disabled. Drafts stay outside the checkout.
Git metadata outside the selected checkout is inspected only by the host.

```bash
npx @openai/codex-security policy .
npx @openai/codex-security policy . --path services/api
npx @openai/codex-security policy . --knowledge-base architecture.md --model gpt-5.6-terra --effort high
npx @openai/codex-security policy . --dry-run --json
```

The repository defaults to the current directory. `--path` selects a component,
which inherits policies from its Git root, with the closest policy taking
precedence. Linked worktrees and initialized submodules use their own roots.
Targets and policy links must stay in the selected checkout, outside Git
metadata; ancestor links cannot widen a component policy's scope.

For an intentional separate Git directory, set `core.worktree` to the checkout's
absolute path. Use `git worktree repair` for moved linked worktrees.

Generation describes the system, builds a threat model, then drafts the policy.
In a terminal, it asks about facts the source cannot establish and shows the
exact diff. If both ChatGPT and API-key credentials are available, it asks which
to use; `--auth chatgpt` or `--auth api-key` selects one explicitly.

### Review the draft

Review the saved `SECURITY.md` before copying it to the reported target. Check
links from `.github/SECURITY.md` or `docs/SECURITY.md`: copying can change their
guidance too. Preserve reporting instructions and obtain owner approval for
exclusions, accepted risks, and severity decisions. Later scans read this policy.

Preview rejects changes to the selected or inherited policies. Other source
files are not frozen; regenerate if relevant source or neighboring policies change.

Use `--headless` or an explicit output format to skip questions. Unanswered
questions remain in the review notes. Drafts default to the Codex Security state
directory; `--output-dir` selects an empty directory outside every enclosing
Git checkout and its Git metadata.

```bash
npx @openai/codex-security policy . --path services/api \
--headless --output-dir /path/outside/repository/api-policy --json
```

The artifact directory contains:

| File | Purpose |
| ---------------------- | --------------------------------------------------------- |
| `SECURITY.md` | Editable policy draft. |
| `THREAT_MODEL.md` | Detailed threat model with source references. |
| `project-spec.md` | System description and security boundaries. |
| `previous-SECURITY.md` | Original policy used for the diff. |
| `policy-draft.json` | Target, policy hashes, revision, model, and review notes. |

Keep supporting documents private until reviewed for disclosure. A generated
threat scenario is neither owner approval nor a confirmed vulnerability.

`--format md` writes the draft to stdout. `--json` returns paths, review notes,
status, and estimated cost. Global filters and token options work with these
formats. Progress goes to stderr. `--full-output` reports failures with
`ok: false`. `--max-cost` applies to the whole generation. If a stage cannot
inspect required source evidence, generation stops and preserves completed
documents. Fix the reported problem and use a new output directory to retry.

### Generate a policy from TypeScript

```ts
import { CodexSecurity } from "@openai/codex-security";

const security = new CodexSecurity();
try {
const draft = await security.generatePolicy("/path/to/repository", {
path: "services/api",
knowledgeBasePaths: ["/path/to/architecture.md"],
onStage: (stage) => console.error(stage),
});

console.log(await security.previewPolicy(draft));
// Open draft.draftPath in an editor to review the saved policy.
} finally {
await security.close();
}
```

`preflightPolicy()` checks local inputs without starting Codex.
`previewPolicy()` uses the client's Python setting and makes terminal control
characters visible. The standalone `securityPolicyDiff()` returns a raw diff
for files or other non-terminal uses; pass an interpreter explicitly if needed.
`generatePolicy()` accepts `auth`, `path`, `knowledgeBasePaths`, `outputDir`,
`maxCostUsd`, `signal`, and progress and cost callbacks. An optional
`answerQuestions` callback receives each group of up to three owner questions
and a cancellation signal. Without it, the questions remain unresolved.

## CLI

```bash
npx @openai/codex-security policy . --path services/api
npx @openai/codex-security scan .
npx @openai/codex-security scan /path/to/repository --path src --path tests
npx @openai/codex-security scan /path/to/repository --diff origin/main --json
Expand Down
2 changes: 2 additions & 0 deletions sdk/typescript/scripts/check-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ const distFiles = new Set(
"scan-dashboard",
"scan-history-renderer",
"scan-logs",
"security-policy",
"security-policy-cli",
"scan-sessions",
"server/index",
"deduplication/codex-review",
Expand Down
45 changes: 44 additions & 1 deletion sdk/typescript/scripts/smoke-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
mkdir,
mkdtemp,
readFile,
realpath,
readdir,
rm,
stat,
Expand Down Expand Up @@ -399,7 +400,13 @@ try {
[
"--input-type=module",
"--eval",
`const sdk = await import(${JSON.stringify(packageManifest.name)}); for (const name of ["CodexSecurity", "publishScan", "publishScanToCustom", "checkScanPublication", "deduplicateScan"]) if (typeof sdk[name] !== "function") throw new Error("The installed package does not export " + name + ".");`,
[
`const sdk = await import(${JSON.stringify(packageManifest.name)});`,
`for (const name of ${JSON.stringify(["CodexSecurity", "publishScan", "publishScanToCustom", "checkScanPublication", "deduplicateScan", "securityPolicyDiff"])}) {`,
' if (typeof sdk[name] !== "function") throw new Error(`The installed package does not export ${name}.`);',
"}",
'if (typeof sdk.CodexSecurity.prototype.generatePolicy !== "function") throw new Error("The installed package does not export generatePolicy.");',
].join("\n"),
],
{ cwd: consumer },
);
Expand Down Expand Up @@ -478,6 +485,42 @@ try {
assert.match(help, /Usage: codex-security\b/u);
assert.match(help, /\bpublish\b/u);
assert.match(help, /\bdedupe\b/u);
assert.match(help, /\bpolicy\b/u);
const policyHelp = run(process.execPath, [launcher, "policy", "--help"], {
cwd: consumer,
capture: true,
});
assert.match(policyHelp, /SECURITY\.md/u);
const policyTarget = join(consumer, "policy-target");
await mkdir(policyTarget);
const policyPreflight = JSON.parse(
run(
process.execPath,
[
launcher,
"policy",
policyTarget,
"--auth",
"chatgpt",
"--dry-run",
"--json",
],
{
cwd: consumer,
capture: true,
env: {
...process.env,
CODEX_SECURITY_STATE_DIR: join(consumer, "policy-state"),
},
},
),
);
assert.equal(
policyPreflight.targetPath,
join(await realpath(policyTarget), "SECURITY.md"),
);
assert.equal(policyPreflight.dryRun, true);
assert.deepEqual(await readdir(policyTarget), []);

const publicationScan = join(consumer, "publication-scan");
await cp(
Expand Down
Loading
Loading