Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 22 additions & 35 deletions .github/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
<!-- release-version: 0.1.24 -->
<!-- release-version: 0.2.0 -->

## Highlights

- Start the preview findings service directly with
`codex-security serve [--port PORT]`, without Docker or an internal package
path. The command reuses the existing service, state, and shutdown behavior;
`--port` overrides `PORT`, and port `0` selects a free port. See
[running without Docker](https://github.com/openai/codex-security/blob/npm-v0.1.24/sdk/typescript/README.md#running-without-docker).
- Observe durable Deep Scan progress from the SDK with the optional
`onDeepProgress({ completed, active, maximum })` callback. Updates report
changed completed and active independent-review counts without blocking the
scan. See
[SDK scan options](https://github.com/openai/codex-security/blob/npm-v0.1.24/sdk/typescript/README.md#sdk-configuration-and-scan-options).
- Make stopped-result handling explicit and stable. Read, list, and export
operations no longer publish late retained results as a side effect; the app
reports when recovery is needed and can recover validated results on request.
See
[stopped result recovery](https://github.com/openai/codex-security/blob/npm-v0.1.24/plugins/codex-security/references/scan-contract.md#stopped-result-recovery).
- Include changed PowerShell `.ps1` files in diff-scan inventories and remove a
conflicting reporting rule so valid internal attack paths remain eligible for
review. Nested Deep Scan workers now also receive an explicitly configured
OpenAI provider credential through the plugin's existing environment boundary.
- Improve Windows reliability by preserving case-insensitive `CODEX_HOME`
entries and repository paths from ordinary PowerShell activity, and by
retrying credential snapshots when a descendant file disappears during ACL
inspection. Existing path-safety and permission failures remain fatal.
- Use one Codex Security release version across the CLI, TypeScript SDK,
bundled plugin, and MCP server. Every product release now advances the plugin
version so cached plugin installations recognize the upgrade. The findings
service and dashboards continue to ship in the same package and image.
- Require container releases to use the same source commit as the matching npm
release tag. An identical package version on a later commit no longer counts
as the same release.
- Recognize BOM-marked UTF-16 source files and PowerShell module and data files
in scan inventories.
- Improve Windows handling of long Codex executable paths, case-only renames,
and dedupe environment settings.

## Upgrade notes

- The findings API and dashboard still have no built-in authentication.
`codex-security serve` binds to loopback by default; keep it local or place it
behind an authenticated TLS proxy before sharing access. Python is still
required, and nonempty imports still require an embeddings API credential.
- Stopped-scan recovery is now explicit. App clients should check
`resultsRecoveryNeeded` and request recovery when they want validated late
results republished. Canceled scans remain immutable and cannot use this
recovery path.
- `onDeepProgress.maximum` is the configured independent-review cap, not a
percentage denominator. The SDK polls the durable projection only when the
callback is supplied.
- SDK consumers using a range such as `^0.1.24` must explicitly update to
`^0.2.0`. Existing CLI commands and public SDK version fields remain available.
- Version alignment does not change artifact schemas or database migration
versions. Saved scans keep their original producer versions, and custom
plugins continue to report their own versions. Codex runtime dependencies and
external plugin catalogs retain their independent versions.
- Publish a container from `container-v0.2.0` at the same commit as
`npm-v0.2.0`. Manual publication from `main` works only while `main` points to
that release commit. See
[container publishing](https://github.com/openai/codex-security/blob/npm-v0.2.0/docker/README.md#publishing).

The categorized list below contains the individual changes.
2 changes: 2 additions & 0 deletions .github/workflows/container-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ jobs:
exit 1
fi

sh docker/verify-container-release-source.sh "$version" "$GITHUB_SHA"

printf 'image=ghcr.io/%s\n' "${GITHUB_REPOSITORY_OWNER,,}/codex-security" >> "$GITHUB_OUTPUT"
printf 'version=%s\n' "$version" >> "$GITHUB_OUTPUT"

Expand Down
31 changes: 30 additions & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ Historical releases may contain generated notes only. The release tag and npm
package use the same stable version: `npm-vX.Y.Z` and
`@openai/codex-security@X.Y.Z`.

Starting with `0.2.0`, the CLI, TypeScript SDK, bundled plugin, and MCP server
share one release version. The findings service and dashboards ship in that
same package and container image. `sdk/typescript/package.json` is the version
source; the plugin manifest, private MCP package, and Python test-tooling
metadata are synchronized copies. The Python metadata does not represent a
separate published SDK.

Keep Codex runtime dependencies, artifact schemas, database migrations, and
test fixture versions independent. Existing public version fields remain
available, and a custom plugin still reports its own producer version. External
Codex catalogs control their own publication versions. Historical releases and
saved scan artifacts retain their original versions.

## Pull request titles and categories

Pull request titles must follow this form:
Expand Down Expand Up @@ -44,7 +57,11 @@ That manual label takes precedence over the title category.
## Prepare a release

1. Choose the next stable version and update `sdk/typescript/package.json`.
Keep the lockfile version in sync when it records the package version.
Run `pnpm --dir sdk/typescript run sync:versions` to update the component
manifests. Keep the lockfile version in sync when it records the package
version. Every product release advances the bundled plugin version, including
releases without plugin changes, so cached plugin installations refresh on
upgrade.
2. Update `.github/release-notes.md`. Its first line must be
`<!-- release-version: X.Y.Z -->` with the exact package version.
3. Summarize the changes a user will notice. Call out required migration or
Expand All @@ -56,6 +73,13 @@ That manual label takes precedence over the title category.
pull request. Do not merge until required CI, review, and public disclosure
checks pass on the current commit.

`pnpm --dir sdk/typescript run check:versions` checks synchronization without
writing files. It also runs during `prepack`, including CI and Docker builds.
Package checks verify that the bundled plugin manifest matches the package;
the installed-package smoke check verifies the public SDK exports, CLI version,
and MCP server version. Do not synchronize dependency versions or rewrite
historical fixtures as part of a release bump.

Review the summary with the same standard as product documentation. Keep it
specific, describe behavior before implementation, and do not include private
repositories, systems, people, findings, links, or issue identifiers.
Expand All @@ -81,6 +105,11 @@ do not prepare a release by editing or committing files there.
Monitor all three workflows. A version bump is not a completed release until
the npm package and GitHub release both exist and match the tag.

Container publication is a separate protected workflow. Create `container-vX.Y.Z`
at the same commit as `npm-vX.Y.Z`, or dispatch from `main` while it still points
to that exact release commit. Matching version strings on different commits are
not sufficient. See [container publishing](docker/README.md#publishing).

## Verify

Check the published state before announcing the release:
Expand Down
10 changes: 7 additions & 3 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,13 @@ and [package access settings](https://docs.github.com/en/packages/learn-github-p

## Publishing

After merging to `main`, push `container-v<version>` matching the SDK package
version or run `container-release` manually on `main`. Releases require a commit
on protected `main`; pull requests only build and test.
After the npm release tag exists, create `container-v<version>` at the same
commit as `npm-v<version>` and push the container tag. Both tags must match the
SDK package version. You can also run `container-release` manually on `main`
while it still points to that exact npm release commit. If `main` has advanced,
use the matching container tag; a later commit with the same package version is
not the same release. Releases must remain on protected `main` history; pull
requests only build and test.

If a release fails, fix the cause and rerun only failed jobs; do not overwrite
an existing stable version. `bootstrap` and
Expand Down
22 changes: 22 additions & 0 deletions docker/verify-container-release-source.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

set -eu

if [ "$#" -ne 2 ]; then
printf '%s\n' 'Usage: verify-container-release-source.sh VERSION COMMIT' >&2
exit 2
fi

version=$1
expected_commit=$2
release_tag="npm-v$version"

if ! release_commit="$(git rev-parse --verify "refs/tags/$release_tag^{commit}")"; then
printf '%s\n' "::error::Create the npm release $release_tag before publishing its container." >&2
exit 1
fi

if [ "$release_commit" != "$expected_commit" ]; then
printf '%s\n' "::error::Container version $version must use the same commit as $release_tag. Create container-v$version at that release commit if main has advanced." >&2
exit 1
fi
2 changes: 1 addition & 1 deletion plugins/codex-security/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codex-security",
"version": "0.1.79",
"version": "0.2.0",
"description": "Codex Security workflows for security scans, analysis, and investigation.",
"author": {
"name": "OpenAI"
Expand Down
2 changes: 1 addition & 1 deletion plugins/codex-security/mcp-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codex-security-mcp-app",
"version": "0.1.158",
"version": "0.2.0",
"type": "module",
"private": true,
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
Expand Down
2 changes: 1 addition & 1 deletion plugins/codex-security/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "codex-security"
version = "0.1.0"
version = "0.2.0"
requires-python = ">=3.12"

[project.optional-dependencies]
Expand Down
6 changes: 4 additions & 2 deletions sdk/typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openai/codex-security",
"version": "0.1.24",
"version": "0.2.0",
"description": "TypeScript SDK and CLI for Codex Security",
"license": "Apache-2.0",
"author": "OpenAI",
Expand Down Expand Up @@ -48,12 +48,14 @@
"build:plugin": "node scripts/build-plugin.mjs",
"check:plugin-source": "node scripts/check-plugin-source.mjs",
"check:package": "node scripts/check-package.mjs",
"check:versions": "node scripts/sync-versions.mjs --check",
"format": "prettier --check --ignore-path .gitignore --ignore-path .prettierignore \"**/*.{cjs,mjs,js,ts,tsx,json,md}\"",
"generate:models": "node scripts/generate-models.cjs",
"generate:models:check": "node scripts/generate-models.cjs --check",
"lint": "tsc --noEmit",
"prepack": "node --run build:plugin && node --run build",
"prepack": "node --run check:versions && node --run build:plugin && node --run build",
Comment thread
mldangelo-oai marked this conversation as resolved.
"start:server": "node dist/server/index.js",
"sync:versions": "node scripts/sync-versions.mjs",
"test": "node --run build:plugin && bun test --timeout 30000 ./tests-ts",
"test:ci": "node -e \"require('node:fs').mkdirSync('reports',{recursive:true})\" && pnpm run test --coverage --coverage-reporter=text --coverage-reporter=lcov --reporter=junit --reporter-outfile=reports/junit.xml",
"test:mcp": "node --run build:plugin && pnpm --dir ../../plugins/codex-security/mcp-app run test:mcp",
Expand Down
8 changes: 8 additions & 0 deletions sdk/typescript/scripts/check-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,14 @@ if (
) {
throw new Error("npm package does not contain the expected public metadata.");
}
const pluginManifest = JSON.parse(
archiveFile("package/_bundled_plugin/.codex-plugin/plugin.json").toString(
"utf8",
),
);
if (pluginManifest.version !== packageJson.version) {
throw new Error("Bundled plugin version must match the npm package version.");
}
assertExpectedGitHead(
packageJson,
process.env.CODEX_SECURITY_EXPECTED_GIT_HEAD,
Expand Down
18 changes: 16 additions & 2 deletions sdk/typescript/scripts/smoke-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,17 @@ async function smokeNestedDeepScanWorker(installedRoot, consumer) {
});
}
assert.equal(initialized.status, 0, initialized.stderr);
const serverInfo = JSON.parse(initialized.stdout.trim()).result.serverInfo;
assert.equal(
JSON.parse(initialized.stdout.trim()).result.serverInfo.name,
serverInfo.name,
"codex-security",
"The installed MCP launcher must initialize the bundled security server.",
);
assert.equal(
serverInfo.version,
packageManifest.version,
"The installed MCP server must report the package release version.",
);

const globalCodex = spawnSync("codex", ["--version"], {
cwd: consumer,
Expand Down Expand Up @@ -399,7 +405,15 @@ 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 + ".");`,
`
import assert from "node:assert/strict";
const sdk = await import(${JSON.stringify(packageManifest.name)});
for (const name of ["CodexSecurity", "publishScan", "publishScanToCustom", "checkScanPublication", "deduplicateScan"]) {
assert.equal(typeof sdk[name], "function", "The installed package must export " + name + ".");
}
assert.equal(sdk.VERSION, ${JSON.stringify(packageManifest.version)});
assert.equal(sdk.BUNDLED_PLUGIN_VERSION, sdk.VERSION);
`,
],
{ cwd: consumer },
);
Expand Down
78 changes: 78 additions & 0 deletions sdk/typescript/scripts/sync-versions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { readFile, writeFile } from "node:fs/promises";
import { join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { releaseVersion } from "./release-automation.mjs";

const repositoryRoot = fileURLToPath(new URL("../../../", import.meta.url));
const componentManifests = [
"plugins/codex-security/.codex-plugin/plugin.json",
"plugins/codex-security/mcp-app/package.json",
"plugins/codex-security/pyproject.toml",
];

export async function syncVersions({
root = repositoryRoot,
check = false,
} = {}) {
const version = releaseVersion(
JSON.parse(
await readFile(join(root, "sdk/typescript/package.json"), "utf8"),
),
);
const updates = await Promise.all(
componentManifests.map(async (path) => {
const contents = await readFile(join(root, path), "utf8");
const field = path.endsWith(".json")
? /^([ \t]*"version"[ \t]*:[ \t]*")[^"]+("[ \t]*,?)/mu
: /^(version[ \t]*=[ \t]*")[^"]+(")/mu;
if (!field.test(contents)) {
throw new Error(`${path} must declare its component version.`);
}
return {
path,
contents,
updated: contents.replace(
field,
(_match, prefix, suffix) => `${prefix}${version}${suffix}`,
),
};
}),
);
const changed = updates.filter(
({ contents, updated }) => contents !== updated,
);
if (check && changed.length > 0) {
throw new Error(
`Component versions must match ${version}:\n${changed.map(({ path }) => path).join("\n")}\nRun pnpm --dir sdk/typescript run sync:versions.`,
);
}
if (!check) {
for (const { path, updated } of changed) {
await writeFile(join(root, path), updated);
}
}
return { version, changed: changed.map(({ path }) => path) };
}

const invokedPath = process.argv[1];
if (
invokedPath !== undefined &&
pathToFileURL(resolve(invokedPath)).href === import.meta.url
) {
try {
const args = process.argv.slice(2);
if (args.length > 1 || (args.length === 1 && args[0] !== "--check")) {
throw new Error("Usage: node scripts/sync-versions.mjs [--check]");
}
const check = args[0] === "--check";
const { version, changed } = await syncVersions({ check });
console.log(
check
? `Verified component versions match ${version}.`
: `Synchronized ${changed.length} component manifests to ${version}.`,
);
} catch (error) {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
}
}
2 changes: 1 addition & 1 deletion sdk/typescript/src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const PACKAGE_VERSIONS = packageVersions(
export const VERSION = PACKAGE_VERSIONS.package;
export const CODEX_SDK_VERSION = PACKAGE_VERSIONS.sdk;
export const CODEX_EXECUTABLE_VERSION = PACKAGE_VERSIONS.executable;
export const BUNDLED_PLUGIN_VERSION = "0.1.79" as const;
export const BUNDLED_PLUGIN_VERSION = VERSION;

const PACKAGE_NAME = "@openai/codex-security";

Expand Down
Loading
Loading