Repository navigation
feat(codex): verify Windows CLI archive and shim layouts #7619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
89 commits
Select commit
Hold shift + click to select a range
d5e5b41
fix(codex): keep npm Node cache within inspection lifetime
luvs01 840ca43
feat(codex): add confirmed CLI update apply candidate
luvs01 e9fdc4b
fix(codex): preserve canonical staging and complete install preimages
luvs01 12b7c59
fix(codex): await archive descriptor release before readback
luvs01 09bf422
feat(codex): stage durable CLI update job management
luvs01 26a070e
fix(codex): bind management launches and preserve uncertain job lifet…
luvs01 b06e809
test(codex): align POSIX CLI job consumer contracts
luvs01 3df99b8
fix(codex): initialize confirmed CLI update jobs safely
luvs01 8e0eeaa
test(codex): preserve job DTO evidence during matching
luvs01 224e797
test(codex): isolate npm wrapper module scope
luvs01 7299e4b
docs(codex): describe POSIX update apply and job consumers
luvs01 40a1075
fix(cli): document Codex updater resource parity
luvs01 13cf987
fix(codex): preserve update job results across store failures
luvs01 d617ec4
fix(api): advertise retry delay for CLI job admission contention
luvs01 3c25ae9
fix(codex): recover owned job locks and signal apply failures
luvs01 d67b167
test(codex): isolate admission lock replacement identity guards
luvs01 4c22308
test(codex): align unapplied CLI consumer exit expectation
luvs01 72f8ea9
feat(codex): prove explicit Windows npm installations for read-only p…
luvs01 670824f
Merge dev into POSIX CLI update recovery
luvs01 76b4ee0
Merge latest dev layout fix into POSIX CLI update recovery
luvs01 bd31dba
Merge recovered POSIX parent into Windows CLI planning
luvs01 c7dcd65
fix(codex): preserve CLI results after persistence failure
luvs01 ec3cd76
feat(codex): verify independent Windows CLI artifact layout
luvs01 d9e2be4
merge: align Windows CLI plan with POSIX result persistence fix
luvs01 0b474d5
test(codex): bind shim fixture to verified npm artifact
luvs01 7a43b5e
Merge published CLI plan parent into Windows artifact layout
luvs01 6aac61f
Merge commit 'c4d4bb2da2162e23c314befafc5ec104292902a0' into codex/cl…
luvs01 9fd7f6d
Merge commit '6aac61f6480bdea9875f3e125a83aa5373614f2e' into codex/wi…
luvs01 00684c3
Merge updated Windows plan parent into artifact layout
luvs01 9e5a917
fix(test): promote Bun 1.4.4 with independent runner role verification
luvs01 1deb67d
docs: align Docker Bun comments with unified role defaults
luvs01 41f08ff
test: use a draining TLS origin for early upload replies
luvs01 a9c3d12
test: confine draining origin to buffered HTTP/1 upload
luvs01 f011a94
merge: align test-runner role branch with dev 2.93.0 dependency baseline
luvs01 1523ba3
fix(codex): run cli-update registry queries and session scans async
luvs01 5bb78cc
fix(codex): keep bare Windows check content-observation opt-in
luvs01 4183b12
docs(cli): align localized update-check wording with Windows x64 support
luvs01 d36b804
fix(codex): settle cli-update metadata on the child's terminal event
luvs01 9ea78e4
Merge commit 'd36b8041770ff75c6841f7379da2fcc8a1db076e' into codex/wi…
luvs01 fd24f16
fix(codex): report windows_apply_disabled for direct Windows apply re…
luvs01 8caa528
test(codex): hold Windows toolchain through async metadata resolution
luvs01 320781b
Merge current dev into Codex CLI async metadata integration
luvs01 9fa70f4
Merge updated parent into Windows CLI read-only plan integration
luvs01 6c5f467
merge: integrate verified parent into Windows CLI artifact layout
luvs01 7e7c553
test(codex): pin gated registry fixtures to x64
luvs01 5985f70
Merge 7596 fixture architecture fix
luvs01 aa39670
Merge 7619 fixture architecture fix
luvs01 f3ed578
Merge current dev into Codex CLI update parent
luvs01 cd582f4
Merge shared Bun 1.4.4 baseline into Codex CLI update parent
luvs01 5c2caaf
Merge refreshed Bun 1.4.4 parent into Windows CLI provenance
luvs01 6a1ca08
Merge refreshed provenance parent into Windows CLI artifact layout
luvs01 bebc5a9
ci: record measured Codex CLI apply batch weight
luvs01 f7e55ab
Merge measured CLI apply weight into PR 7596 candidate
luvs01 29f3d2c
Merge measured CLI apply weight into PR 7619 candidate
luvs01 abc2643
fix(codex): release apply lease when version verifier setup fails bef…
luvs01 b0e2d8d
test(codex): cover terminal pre-child verifier setup on every platform
luvs01 f250ad7
fix(codex): run CLI update plan/apply hashing in a dedicated worker
luvs01 d352a8e
fix(codex): drain CLI update workers on Bun 1.4.4
luvs01 a8743e1
test(codex): prove worker imports preserve fresh main handler
luvs01 b52f502
test(codex): align route planning fixture with worker dispatch
luvs01 d10c5c9
Merge CLI update worker parent into Windows planning and cover native…
luvs01 b433252
Merge commit 'd10c5c9d38e5026b9f7f4825687e6e3644929994' into codex/76…
luvs01 f6b238b
test(codex): isolate selector fixtures from runtime memo expiry
luvs01 e68067e
Merge dev c2bcb529 into POSIX Codex CLI update stack
luvs01 cf22d6c
Merge commit '323a5e11772c38c15b271d23c7696c5947bb97da' into codex/75…
luvs01 486768f
Merge commit 'cf22d6c15156d98b3a42c408ac0cc96877545531' into codex/75…
luvs01 eec0d14
Merge dev6672 into POSIX CLI update stack
luvs01 d662a41
Merge commit 'eec0d143d990eff3c43d5673cad30832711cf47e' into codex/75…
luvs01 4d3d0d0
Merge commit 'd662a41ed4e97d60d22a48d504921d22643a1237' into codex/76…
luvs01 ef61edb
Merge origin/dev into codex/cli-update-stack-posix-20261023
lidge-jun 834db4f
Refine Windows CLI installation checks
lidge-jun 52ca9e4
fix(codex): validate update paths and retain installation admission
lidge-jun eee0e35
Merge remote-tracking branch 'origin/dev' into fix/pr7586-install-bou…
lidge-jun c71a858
fix(codex): recover update admission after terminal evidence
lidge-jun 0b55267
fix(codex): preserve recovery across admission and storage failures
lidge-jun 3877dd6
fix(codex): preserve recovery admission across publication and home a…
lidge-jun 8e32f89
fix(codex): recognize interrupted terminal recovery writes
lidge-jun dda17d8
Merge reviewed CLI update admission into Windows provenance
lidge-jun 715e1b9
test(codex): use canonical homes for recovery fixtures
lidge-jun efa2121
Merge remote-tracking branch 'origin/dev' into fix/cli-update-macos-r…
lidge-jun c97614f
Merge remote-tracking branch 'origin/dev' into HEAD
lidge-jun c061f78
Merge origin/dev into codex/cli-update-stack-posix-20261023
lidge-jun 188954e
Merge codex/cli-update-stack-posix-20261023 into codex/windows-cli-pr…
lidge-jun ff7534e
Merge dev (before #7586 squash) into codex/windows-cli-provenance-sta…
lidge-jun 4d978f4
Merge #7586 squash (4118b90ba) into codex/windows-cli-provenance-stac…
lidge-jun e405822
Merge origin/dev into codex/windows-cli-provenance-stack-20261023
lidge-jun 70143cb
Merge origin/dev into codex/windows-cli-provenance-stack-20261023
lidge-jun 14b585c
Merge current Windows provenance parent into archive layout PR
luvs01 1bbd1c9
Merge landed Windows planning safeguards from dev
luvs01 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| import { createHash } from "node:crypto"; | ||
| import { backingReference, codexCliArtifactSet, codexCliArtifactSetDigest, verifyCodexCliArchive, | ||
| type CodexArchiveFiles, type CodexCliArtifactSet } from "./cli-update-artifacts"; | ||
| import { WINDOWS_CODEX_HEADER_BYTES, WINDOWS_CODEX_NATIVE_MEMBER } from "./cli-update-windows-paths"; | ||
|
|
||
| export interface WindowsCodexExpectedFile { readonly relativePath: string; readonly size: number; readonly digest: string } | ||
| export interface WindowsCodexArchiveLayout { | ||
| readonly schemaVersion: 1; | ||
| readonly applicable: false; | ||
| readonly mutationAuthority: false; | ||
| readonly platform: "win32"; | ||
| readonly arch: "x64"; | ||
| readonly nativeHeader: "PE32+-AMD64"; | ||
| readonly shimContract: "npm-cmd-shim-8-node"; | ||
| readonly mainRoot: "node_modules/@openai/codex"; | ||
| readonly backingRoot: "node_modules/@openai/codex-win32-x64"; | ||
| readonly nativePath: string; | ||
| readonly artifactsDigest: string; | ||
| readonly contentDigest: string; | ||
| readonly files: readonly WindowsCodexExpectedFile[]; | ||
| } | ||
| const MAIN = "node_modules/@openai/codex", BACKING = "node_modules/@openai/codex-win32-x64"; | ||
| const PACKAGE = "@openai/codex-win32-x64"; | ||
| // Exact cmd-shim@8.0.0 (ISC) output for the supported node shebang/global-prefix linkage. | ||
| // Independent captured outputs live in tests/fixtures/codex-windows-npm-shims.json. | ||
| // This is a readback contract, not a shim writer or a claim about every npm version. | ||
| const SHIMS: readonly WindowsCodexExpectedFile[] = [ | ||
| { relativePath: "codex", size: 421, digest: "e4cfdce20603991a0cc30e79b7080277f934100a317f9c8b2779c0a2f8213ab2" }, | ||
| { relativePath: "codex.cmd", size: 341, digest: "0ea5e45d176e31ed39d5b9aad97b58afd6b41f40023508ae5362e151335db803" }, | ||
| { relativePath: "codex.ps1", size: 869, digest: "1f82c9b7d42cb3293d459d7304e67f9da76297d1cc33230a3f786c297ee0cc6a" }, | ||
| ]; | ||
| const object = (value: unknown): value is Record<string, any> => !!value && typeof value === "object" && !Array.isArray(value); | ||
| function manifest(files: CodexArchiveFiles): Record<string, any> { | ||
| const bytes = files.get("package.json")?.bytes; | ||
| const value: unknown = bytes && JSON.parse(bytes.toString("utf8")); | ||
| if (!object(value)) throw new Error("artifact_manifest_unverified"); | ||
| return value; | ||
| } | ||
| function validateNative(files: CodexArchiveFiles): void { | ||
| const native = files.get(WINDOWS_CODEX_NATIVE_MEMBER), prefix = native?.nativePrefix; | ||
| if (!native || !prefix || prefix.length !== Math.min(native.size, WINDOWS_CODEX_HEADER_BYTES) | ||
| || prefix.length < 64 || prefix.toString("ascii", 0, 2) !== "MZ") throw new Error("artifact_windows_native_unverified"); | ||
| const pe = prefix.readUInt32LE(60); | ||
| if (pe < 64 || pe > prefix.length - 26 || prefix.toString("ascii", pe, pe + 4) !== "PE\0\0" | ||
| || prefix.readUInt16LE(pe + 4) !== 0x8664 || !prefix.readUInt16LE(pe + 6) | ||
| || prefix.readUInt16LE(pe + 20) < 2 || pe + 24 + prefix.readUInt16LE(pe + 20) > native.size | ||
| || !(prefix.readUInt16LE(pe + 22) & 2) || (prefix.readUInt16LE(pe + 22) & 0x2000) | ||
| || prefix.readUInt16LE(pe + 24) !== 0x20b) throw new Error("artifact_windows_native_unverified"); | ||
| } | ||
|
|
||
| /** Read-only artifact verification. Relative paths never select a live prefix or grant write authority. | ||
| * Uses the existing Windows package/Node lookup contract's sibling optional-package lane only. | ||
| * PE header evidence is not a signature, complete image validation or execution proof. | ||
| */ | ||
| export async function verifyWindowsCodexCliLayout(set: CodexCliArtifactSet, mainSource: Buffer | string, | ||
| backingSource: Buffer | string): Promise<WindowsCodexArchiveLayout> { | ||
| if (set.platform !== "win32" || set.arch !== "x64" || set.main.name !== "@openai/codex" | ||
| || set.main.installName !== "@openai/codex" || set.backing.installName !== PACKAGE) throw new Error("artifact_platform_unverified"); | ||
| const main = await verifyCodexCliArchive(mainSource, set.main, { windowsX64: true }); | ||
| const backing = await verifyCodexCliArchive(backingSource, set.backing, { windowsX64: true }); | ||
| const pkg = manifest(main), platform = manifest(backing); | ||
| // Reuse metadata origin/version and exact direct/alias linkage checks, rather than trusting a structural DTO. | ||
| codexCliArtifactSet({ ...pkg, dist: { integrity: set.main.integrity, tarball: set.main.tarball } }, | ||
| () => ({ ...platform, dist: { integrity: set.backing.integrity, tarball: set.backing.tarball } }), "win32", "x64"); | ||
| for (const name of Object.keys(pkg.optionalDependencies ?? {})) { | ||
| if (!/^@openai\/codex-(linux|darwin|win32)-(x64|arm64)$/.test(name)) throw new Error("artifact_dependency_unverified"); | ||
| backingReference(pkg, name); | ||
| } | ||
| const bin = typeof pkg.bin === "string" ? pkg.bin | ||
| : object(pkg.bin) && Object.keys(pkg.bin).length === 1 ? pkg.bin.codex : null; | ||
| const entry = main.get("bin/codex.js")?.bytes; | ||
| if (typeof bin !== "string" || bin.replace(/^\.\//, "") !== "bin/codex.js" || !entry | ||
| || entry.toString("utf8").split(/\r?\n/, 1)[0] !== "#!/usr/bin/env node" | ||
| || platform.exports !== undefined || platform.bin !== undefined || Object.keys(platform.optionalDependencies ?? {}).length | ||
| || (platform.os !== undefined && (!Array.isArray(platform.os) || platform.os.length !== 1 || platform.os[0] !== "win32")) | ||
| || (platform.cpu !== undefined && (!Array.isArray(platform.cpu) || platform.cpu.length !== 1 || platform.cpu[0] !== "x64"))) { | ||
| throw new Error("artifact_backing_unverified"); | ||
| } | ||
| validateNative(backing); | ||
| const files: WindowsCodexExpectedFile[] = SHIMS.map(file => ({ ...file })); | ||
| for (const [root, archive] of [[MAIN, main], [BACKING, backing]] as const) { | ||
| for (const [relative, proof] of archive) { | ||
| // npm aliases are installed as explicit sibling packages. No embedded package may shadow that lookup. | ||
| if (relative.split("/").some(part => part.toLowerCase() === "node_modules")) throw new Error("artifact_backing_unverified"); | ||
| files.push({ relativePath: `${root}/${relative}`, size: proof.size, digest: proof.digest }); | ||
| } | ||
| } | ||
| files.sort((a, b) => a.relativePath < b.relativePath ? -1 : a.relativePath > b.relativePath ? 1 : 0); | ||
| const artifactsDigest = codexCliArtifactSetDigest(set); | ||
| const contentDigest = createHash("sha256").update("codex-cli-windows-layout-v1\0") | ||
| .update(JSON.stringify([artifactsDigest, files])).digest("hex"); | ||
| return Object.freeze({ schemaVersion: 1, applicable: false, mutationAuthority: false, platform: "win32", arch: "x64", | ||
| nativeHeader: "PE32+-AMD64", shimContract: "npm-cmd-shim-8-node", mainRoot: MAIN, backingRoot: BACKING, | ||
| nativePath: `${BACKING}/${WINDOWS_CODEX_NATIVE_MEMBER}`, artifactsDigest, contentDigest, | ||
| files: Object.freeze(files.map(file => Object.freeze(file))) }); | ||
| } | ||
|
|
||
| /** Compare the two package roots and three shim paths, not unrelated files in the global prefix. | ||
| * No filesystem reads, mode guesses or partial matches. | ||
| * The caller still owes held-file/linkage/selection proof; equal content is not mutation authority. | ||
| */ | ||
| export function matchesWindowsCodexCliLayout(expected: WindowsCodexArchiveLayout, | ||
| observed: readonly WindowsCodexExpectedFile[]): boolean { | ||
| if (expected.files.length !== observed.length) return false; | ||
| const actual = new Map(observed.map(file => [file.relativePath, file])); | ||
| return actual.size === observed.length && expected.files.every(file => { | ||
| const other = actual.get(file.relativePath); | ||
| return other?.size === file.size && other.digest === file.digest; | ||
| }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /** Archive names only, never filesystem resolution or installation authority. */ | ||
| export const WINDOWS_CODEX_NATIVE_MEMBER = "vendor/x86_64-pc-windows-msvc/bin/codex.exe"; | ||
| export const WINDOWS_CODEX_HEADER_BYTES = 4096; | ||
|
|
||
| /** ASCII is the supported archive namespace. Refuse unknown Windows case folding. */ | ||
| export function windowsArchivePathValidator(): (name: string, directory: boolean) => void { | ||
| const seen = new Map<string, { spelling: string; directory: boolean; explicit: boolean }>(); | ||
| return (name, directory) => { | ||
| const relative = directory && name.endsWith("/") ? name.slice(0, -1) : name; | ||
| const parts = relative.split("/"); | ||
| if (relative.length > 8192 || parts.length > 64 || parts.some(part => !part || part === "." || part === ".." | ||
| || /[^\x20-\x7e]|[<>:"\\|?*]/.test(part) || /[. ]$/.test(part) | ||
| || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)|^(?:conin\$|conout\$)$/i.test(part))) { | ||
| throw new Error("artifact_windows_path_unverified"); | ||
| } | ||
| for (let i = 0; i < parts.length; i++) { | ||
| const spelling = parts.slice(0, i + 1).join("/"), key = spelling.toLowerCase(); | ||
| const isDirectory = directory || i < parts.length - 1, explicit = i === parts.length - 1; | ||
| const prior = seen.get(key); | ||
| if (prior && (prior.spelling !== spelling || prior.directory !== isDirectory || prior.explicit && explicit)) { | ||
| throw new Error("artifact_windows_path_collision"); | ||
| } | ||
| seen.set(key, { spelling, directory: isDirectory, explicit: explicit || !!prior?.explicit }); | ||
| } | ||
| }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify the compound modifier.
Replace
live held file identitieswithlive-held file identitiesoridentities of files currently held by the live installation. The current wording is grammatically ambiguous.LanguageTool
[grammar] ~126-~126: Use a hyphen to join words.
Context: ...ture consumer must separately prove live held file identities, package lookup, se...
(QB_NEW_EN_HYPHEN)
Source: Linters/SAST tools