diff --git a/server/src/services/native-runtime/native-harness-filesystem-independence.integration.test.ts b/server/src/services/native-runtime/native-harness-filesystem-independence.integration.test.ts index 799f9ea88ad..ac0b12fb8ea 100644 --- a/server/src/services/native-runtime/native-harness-filesystem-independence.integration.test.ts +++ b/server/src/services/native-runtime/native-harness-filesystem-independence.integration.test.ts @@ -16,6 +16,7 @@ import { materializeAsset } from "./runtime-context.js"; import { nativeRuntimeContextFixture } from "./runtime-context.test-fixture.js"; import { preserveRuntimeSkillAssets } from "./runtime-skill-preservation.js"; import { nativeControllerMigrationDescriptor, scopedRunnerdStateRoot } from "./native-session-executor.js"; +import * as nativeSessionExecutor from "./native-session-executor.js"; import { hasLocalNativeHarnessObligations } from "./native-harness-backup-migration.js"; import { preserveNativeHarnessFilesystemIndependence, hasValidNativeHarnessFilesystemIndependence } from "./native-harness-filesystem-independence.js"; import * as filesystemIndependence from "./native-harness-filesystem-independence.js"; @@ -39,6 +40,20 @@ import { LEGACY_WORKSPACE_RECOVERY_MIGRATION } from "../legacy-workspace-recover vi.mock("../../build-commit.js", () => ({ readBuildCommit: () => "a".repeat(40) })); +const CODEX_WEB_PROFILE_8E4 = { + providerKind: "codex", driverKind: "codex_app_server", + directories: [ + { name: "runner", location: "runner", excludeEntries: [] }, + { name: "codex-home", location: "filesystem", excludeEntries: [ + "tmp", ".tmp", "auth.json", "config.toml", ".remote-plugin-install-staging", "cache/remote_plugin_catalog", + "plugins/.remote-plugin-install-staging", "plugins/cache/openai-curated-remote", "plugins/cache/created-by-me-remote", + "plugins/cache/workspace-directory", "plugins/cache/workspace-shared-with-me", + "plugins/cache/workspace-shared-with-me-private", "plugins/cache/workspace-shared-with-me-unlisted", + ] }, + ], +} as const; +const CODEX_WEB_PROFILE_8E4_DIGEST = "6f10c4b4424e84defcdb4c7e932921d09ea73dc0f39c96e61777c8b20c5f637b"; + describeEmbeddedPostgres("owner-verified native harness filesystem independence", () => { let database: Awaited>, db: ReturnType; const roots: string[] = []; @@ -198,6 +213,60 @@ describeEmbeddedPostgres("owner-verified native harness filesystem independence" return { ...f, execution, controllerPath, raw, supervisor, supervisorPath: descriptor.supervisorPath, identity }; } + it("reads the frozen pre-auth-refresh Codex WEB proof without changing authority", async () => { + const f = await controllerFixture(true, "none"); + expect(nativeSha256(CODEX_WEB_PROFILE_8E4.directories)).toBe(CODEX_WEB_PROFILE_8E4_DIGEST); + const oldPolicy = vi.spyOn(nativeSessionExecutor, "resolveNativeHarnessPersistenceProfile").mockReturnValue(structuredClone(CODEX_WEB_PROFILE_8E4)); + try { await preserveNativeHarnessFilesystemIndependence(db, { ...f.input, mode: "web_evidence" }); } + finally { oldPolicy.mockRestore(); } + const [before] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.runId)); + expect((before!.runnerProfileJson!.nativeHarnessFilesystemIndependence as { web: { profileDigest: string } }).web.profileDigest) + .toBe(CODEX_WEB_PROFILE_8E4_DIGEST); + expect(await hasValidNativeHarnessFilesystemIndependence(db, f.input)).toBe(true); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.runId)))[0]).toEqual(before); + await db.update(nativeRunFinalizations).set({ leaseOwner: randomUUID(), leaseExpiresAt: new Date(Date.now() + 60_000) }) + .where(eq(nativeRunFinalizations.runId, f.runId)); + expect(await hasValidNativeHarnessFilesystemIndependence(db, f.input)).toBe(false); + }); + + it.each(["auth-refresh.json", "auth-refresh.json.tmp"])("refuses a legacy WEB archive that contains %s", async name => { + const f = await controllerFixture(true, "none"); + await mkdir(join(f.sourceRoot, "codex-home"), { mode: 0o700 }); + await writeFile(join(f.sourceRoot, "codex-home", name), "private credential fixture", { mode: 0o600 }); + const oldPolicy = vi.spyOn(nativeSessionExecutor, "resolveNativeHarnessPersistenceProfile").mockReturnValue(structuredClone(CODEX_WEB_PROFILE_8E4)); + try { await preserveNativeHarnessFilesystemIndependence(db, { ...f.input, mode: "web_evidence" }); } + finally { oldPolicy.mockRestore(); } + const [row] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.runId)); + const web = (row!.runnerProfileJson!.nativeHarnessFilesystemIndependence as { web: { entries: Array<{ path: string }> } }).web; + expect(web.entries.some(entry => entry.path === `web-home/codex-home/${name}`)).toBe(true); + expect(await hasValidNativeHarnessFilesystemIndependence(db, f.input)).toBe(false); + expect(await readFile(join(f.sourceRoot, "codex-home", name), "utf8")).toBe("private credential fixture"); + }); + + it.each(["extra-exclusion", "missing-existing-exclusion"])("refuses an unknown historical Codex WEB policy: %s", async change => { + const f = await controllerFixture(true, "none"); + const exclusions = change === "extra-exclusion" ? [...CODEX_WEB_PROFILE_8E4.directories[1].excludeEntries, "unrecognized-cache"] + : CODEX_WEB_PROFILE_8E4.directories[1].excludeEntries.filter(entry => entry !== "auth.json"); + const unknownPolicy = vi.spyOn(nativeSessionExecutor, "resolveNativeHarnessPersistenceProfile").mockReturnValue({ + ...CODEX_WEB_PROFILE_8E4, directories: [CODEX_WEB_PROFILE_8E4.directories[0], { ...CODEX_WEB_PROFILE_8E4.directories[1], excludeEntries: exclusions }], + }); + try { await preserveNativeHarnessFilesystemIndependence(db, { ...f.input, mode: "web_evidence" }); } + finally { unknownPolicy.mockRestore(); } + expect(await hasValidNativeHarnessFilesystemIndependence(db, f.input)).toBe(false); + }); + + it("keeps current Codex WEB writers on the current credential exclusion policy", async () => { + const f = await controllerFixture(true, "none"); + await mkdir(join(f.sourceRoot, "codex-home"), { mode: 0o700 }); + await writeFile(join(f.sourceRoot, "codex-home/auth-refresh.json"), "private credential fixture", { mode: 0o600 }); + await preserveNativeHarnessFilesystemIndependence(db, { ...f.input, mode: "web_evidence" }); + const [row] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.runId)); + const web = (row!.runnerProfileJson!.nativeHarnessFilesystemIndependence as { web: { profileDigest: string; excluded: Array<{ path: string }> } }).web; + expect(web.profileDigest).toBe("e5248e68685fb06ae32336d9517d7054e8aff785bdfb220b4481823a82ff8e78"); + expect(web.excluded.some(entry => entry.path === "codex-home/auth-refresh.json")).toBe(true); + expect(await hasValidNativeHarnessFilesystemIndependence(db, f.input)).toBe(true); + }); + it("validates retained WEB receipts with bounded parallel database proofs", async () => { const fixtures = []; for (let index = 0; index < 5; index++) { diff --git a/server/src/services/native-runtime/native-harness-filesystem-independence.ts b/server/src/services/native-runtime/native-harness-filesystem-independence.ts index 706070a1531..8d6a8a3219d 100644 --- a/server/src/services/native-runtime/native-harness-filesystem-independence.ts +++ b/server/src/services/native-runtime/native-harness-filesystem-independence.ts @@ -38,6 +38,8 @@ import { verifyDurableNativeHarnessBackup } from "./native-harness-backup-storag const KEY = "nativeHarnessFilesystemIndependence"; const SCHEMA = "paperclip.native-harness-filesystem-independence.v1"; const WEB_SCHEMA = "paperclip.native-harness-web-evidence.v1"; +const CODEX_WEB_PROFILE_BEFORE_AUTH_REFRESH = "6f10c4b4424e84defcdb4c7e932921d09ea73dc0f39c96e61777c8b20c5f637b"; +const CODEX_WEB_PROFILE_WITH_AUTH_REFRESH = "e5248e68685fb06ae32336d9517d7054e8aff785bdfb220b4481823a82ff8e78"; const terminal = ["succeeded", "failed", "cancelled", "interrupted", "timed_out"]; const sha = (bytes: string | Buffer) => createHash("sha256").update(bytes).digest("hex"); const digest = (value: unknown) => nativeSha256(JSON.parse(JSON.stringify(value))); @@ -495,8 +497,12 @@ function validWebBinding(facts: Awaited>, web: if (!web || typeof web.rootPresent !== "boolean" || typeof web.controllerAbsent !== "boolean" || !Array.isArray(web.entries) || !Array.isArray(web.skills) || !Array.isArray(web.excluded) || web.entries.length + web.excluded.length > NATIVE_CONTROLLER_WEB_LIMITS.entries - || web.profileDigest !== digest(webProfile(facts)) || digest(web.skills) !== digest(webSkills(facts))) return false; + || digest(web.skills) !== digest(webSkills(facts))) return false; const profiles = webProfile(facts), manifests = new Map(web.skills.map(skill => [skill.bundleDigest, skill.manifestDigest])); + const profileDigest = digest(profiles); + if (web.profileDigest !== profileDigest && !(resolveNativeHarnessPersistenceProfile(facts.execution).providerKind === "codex" + && profileDigest === CODEX_WEB_PROFILE_WITH_AUTH_REFRESH + && web.profileDigest === CODEX_WEB_PROFILE_BEFORE_AUTH_REFRESH)) return false; for (const [bundle, manifest] of manifests) if (!web.entries.some(entry => entry.path === `web-manifests/${bundle}.json` && entry.type === "file" && entry.sha256 === manifest)) return false; if (web.entries.some(entry => {