Skip to content
Closed
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
24 changes: 23 additions & 1 deletion bin/ocx.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { bootRestoreProbe, launcherUsableAfterNpmUpdate, transactionalNpmUpdate
import { manualUpdateFailureGuidance, npmUpdateFailureGuidance } from "../src/update/update-failure-guidance.mjs";
import {
CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS,
codexCliInspectionLookupSeeds,
isCodexCliUpdateInspectionArgv,
} from "../src/update/codex-cli-update-launch-policy.mjs";
import {
Expand Down Expand Up @@ -1192,25 +1193,46 @@ function createNodeLaunchContext() {
: configuredCodexCliPath;
const preBunPath = typeof process.env.PATH === "string" ? process.env.PATH : null;
const preBunPathExt = typeof process.env.PATHEXT === "string" ? process.env.PATHEXT : null;
// HOME and NODE_PATH seed the candidate Node module lookup the update
// inspection models, so they are proof-bound here for the same reason as
// PATH: the live environment could have been rewritten after selection. They
// stay in the inherited environment regardless — the child may need them for
// its own work; only the snapshot copy is optional.
const preBunHome = typeof process.env.HOME === "string" ? process.env.HOME : null;
const preBunNodePath = typeof process.env.NODE_PATH === "string" ? process.env.NODE_PATH : null;
const preBunCodexCliManagerRoots = Object.fromEntries(
CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS.flatMap(name => {
const value = process.env[name];
return typeof value === "string" && value !== "" ? [[name, value]] : [];
}),
);
const launchProof = randomBytes(32).toString("base64url");
const launchContext = JSON.stringify({
const serializeContext = lookupSeeds => JSON.stringify({
version: 1,
proof: launchProof,
anthropicEnvSlots: preBunAnthropicSlots,
codexCliInspectionEnv: codexCliUpdateInspection ? {
codexCliPath: preBunCodexCliPath,
path: preBunPath,
pathExt: preBunPathExt,
home: lookupSeeds.home,
nodePath: lookupSeeds.nodePath,
managerRoots: preBunCodexCliManagerRoots,
configDir: configDir(),
} : null,
});
let launchContext = serializeContext({ home: preBunHome, nodePath: preBunNodePath });
// The lookup seeds are optional: a Windows inspection never models the global
// Node lookup, and an oversized NODE_PATH must not push the serialized
// context past the reader's limit — that would discard the whole selection
// snapshot, while dropped seeds merely leave the fallback lane unverifiable
// and fail only the content observation closed.
const lookupSeeds = codexCliInspectionLookupSeeds(
process.platform, preBunHome, preBunNodePath, launchContext.length,
);
if (lookupSeeds.home !== preBunHome || lookupSeeds.nodePath !== preBunNodePath) {
launchContext = serializeContext(lookupSeeds);
}
// The inspection snapshot above already carries PATH, PATHEXT, and the manager-root slots as
// proof-bound values, and `inspectCodexCliInstall` reads them from that snapshot rather than
// from the live environment. Inheriting them again would spend the 32,767-character Windows
Expand Down
4 changes: 4 additions & 0 deletions src/cli/codex-cli-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ function inspectionDeps(commandDeps: CodexCliUpdateCommandDeps): CodexCliInstall
CODEX_CLI_PATH: trusted.codexCliPath ?? undefined,
PATH: trusted.path ?? undefined,
PATHEXT: trusted.pathExt ?? undefined,
// The lookup seeds the inspection models against the launch-time
// capture; ambient values are never consulted.
HOME: trusted.home ?? undefined,
NODE_PATH: trusted.nodePath ?? undefined,
},
configDir: trusted.configDir,
// The snapshot is proof-bound to the launcher, but the report may only attest
Expand Down
17 changes: 15 additions & 2 deletions src/cli/launcher-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export type TrustedNodeLaunchContext = {
codexCliPath: string | null;
path: string | null;
pathExt: string | null;
home: string | null;
nodePath: string | null;
managerRoots: CodexCliVersionManagerRoots | null;
configDir: string;
}> | null;
Expand Down Expand Up @@ -70,7 +72,7 @@ export function initializeNodeLauncherContext(
delete env.OCX_PRE_BUN_ANTHROPIC_ENV;
trustedContext = null;

if (proofArgs.length !== 1 || !raw || raw.length > 64 * 1024) return null;
if (proofArgs.length !== 1 || !raw || raw.length > NODE_LAUNCH_CONTEXT_MAX_CHARS) return null;
const proof = proofArgs[0]!;
if (!isLaunchProof(proof)) return null;

Expand Down Expand Up @@ -102,6 +104,12 @@ export function initializeNodeLauncherContext(
const value = (inspection as Record<string, unknown>)[key];
return value === null || typeof value === "string";
})
// Older launchers predate the lookup-seed slots; absent stays null so
// the fallback lane simply remains unverifiable rather than untrusted.
&& ["home", "nodePath"].every(key => {
const value = (inspection as Record<string, unknown>)[key];
return value === undefined || value === null || typeof value === "string";
})
&& typeof (inspection as Record<string, unknown>).configDir === "string"
&& (inspection as Record<string, string>).configDir.length > 0
&& (inspection as Record<string, string>).configDir.length <= 32 * 1024
Expand All @@ -110,6 +118,8 @@ export function initializeNodeLauncherContext(
codexCliPath: (inspection as Record<string, string | null>).codexCliPath ?? null,
path: (inspection as Record<string, string | null>).path ?? null,
pathExt: (inspection as Record<string, string | null>).pathExt ?? null,
home: (inspection as Record<string, string | null>).home ?? null,
nodePath: (inspection as Record<string, string | null>).nodePath ?? null,
managerRoots,
configDir: (inspection as Record<string, string>).configDir,
})
Expand All @@ -125,4 +135,7 @@ export function initializeNodeLauncherContext(
export function trustedNodeLauncherContext(): TrustedNodeLaunchContext | null {
return trustedContext;
}
import { CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS } from "../update/codex-cli-update-launch-policy.mjs";
import {
CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS,
NODE_LAUNCH_CONTEXT_MAX_CHARS,
} from "../update/codex-cli-update-launch-policy.mjs";
Loading
Loading