mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-12 08:19:54 +02:00
fix: retain Codex sandbox resources after npm platform installation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
f17c8c3bbf
commit
6baec5181f
7 files changed
+179
-11
No files matched your search
@@ -54,6 +54,9 @@ The existing installer checks must prove both native and legacy version selectio
|
||||
and the runtime must still verify the selected executable's version and digest.
|
||||
Direct/source runtimes retain their own dependency declarations. A CI credential
|
||||
test fixture also reserves its ports before making its unchanged denial assertion.
|
||||
Installed-consumer verification also checks the native vendor sandbox read roots
|
||||
and their rendered isolation configuration; a version probe alone does not prove
|
||||
that Codex can invoke its native resources for shell tools.
|
||||
|
||||
Reopen packaging and installed-consumer verification for this correction.
|
||||
The green checks at `36d9eb0c3c6b9ca5234432b97349dc7317cfa916` are historical;
|
||||
|
||||
@@ -73,6 +73,8 @@ belong to the published server manifest; the bundled JavaScript wrapper delegate
|
||||
platform installation there so npm can keep native and legacy versions separate.
|
||||
The wrapper code and patched ACP bridge stay unchanged. Docker images
|
||||
preinstall their qualified runtime dependencies during the image build.
|
||||
Codex sandbox read roots include the separately installed native vendor resources
|
||||
without granting access to enclosing npm directories or credential homes.
|
||||
Release assembly checks the source revision and Paperclip runner binary
|
||||
identities before publication. This packaging does not change agent defaults or
|
||||
the selected runner of an existing agent.
|
||||
|
||||
@@ -632,7 +632,7 @@ export async function verifyQualifiedAcpxInstallation(
|
||||
): Promise<VerifiedAcpxInstallation> {
|
||||
// Keep the selected package authority available when its bundled Codex
|
||||
// wrapper delegates platform installation to the published server manifest.
|
||||
if (resolvePackageJson === defaultPackageJsonResolver && process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT !== undefined) {
|
||||
if (profile.agent === "codex" && resolvePackageJson === defaultPackageJsonResolver && process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT !== undefined) {
|
||||
resolvePackageJson = createAcpxPackageJsonResolver(
|
||||
process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT,
|
||||
process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST,
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("Codex security configuration", () => {
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { resolvePinnedCodexCommand } from ${JSON.stringify(new URL("./codex-command.ts", import.meta.url).href)};
|
||||
import { codexExecutableReadOnlyRoots } from ${JSON.stringify(new URL("./codex-security-config.ts", import.meta.url).href)};
|
||||
import { codexExecutableReadOnlyRoots, createIsolatedCodexAppServerArgs } from ${JSON.stringify(new URL("./codex-security-config.ts", import.meta.url).href)};
|
||||
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperclip-pinned-codex-test-")));
|
||||
const issuer = join(root, "node_modules/@paperclipai/server/dist/vendor/paperclip-runner/drivers/codex/codex-command.js");
|
||||
const adapter = join(root, "node_modules/@paperclipai/adapter-codex-local");
|
||||
@@ -51,7 +51,9 @@ describe("Codex security configuration", () => {
|
||||
writeFileSync(join(bridge, "package.json"), JSON.stringify({ name: "@agentclientprotocol/codex-acp", version: "1.6.2" }));
|
||||
writeFileSync(join(codex, "package.json"), JSON.stringify(metadata));
|
||||
mkdirSync(vendor, { recursive: true });
|
||||
writeFileSync(join(platform, "package.json"), JSON.stringify({ name: "@openai/codex", version: "0.160.0-" + process.platform + "-" + process.arch }));
|
||||
const platformMetadata = { name: "@openai/codex", version: "0.160.0-" + process.platform + "-" + process.arch,
|
||||
os: [process.platform], cpu: [process.arch] };
|
||||
writeFileSync(join(platform, "package.json"), JSON.stringify(platformMetadata));
|
||||
writeFileSync(executable, "#!" + process.execPath + "\\nif (process.argv.slice(2).join(' ') !== '--version') process.exit(9); console.log('codex-cli 0.160.0');\\n", { mode: 0o755 });
|
||||
assert.equal(resolvePinnedCodexCommand(issuer), executable);
|
||||
const roots = codexExecutableReadOnlyRoots({ PATH: "/missing-codex-command" }, executable);
|
||||
@@ -77,6 +79,67 @@ describe("Codex security configuration", () => {
|
||||
assert.throws(() => resolvePinnedCodexCommand(issuer), /runtime unavailable/);
|
||||
rmSync(codex, { recursive: true });
|
||||
assert.throws(() => resolvePinnedCodexCommand(issuer), /runtime unavailable.*Reinstall/);
|
||||
|
||||
// npm installs the host alias declared by the published server while
|
||||
// its bundled JS wrapper deliberately omits optionalDependencies.
|
||||
const server = join(root, "node_modules/@paperclipai/server");
|
||||
const serverManifest = join(server, "package.json");
|
||||
const normalized = join(server, "node_modules/@openai/codex");
|
||||
const normalizedManifest = join(normalized, "package.json");
|
||||
const normalizedCommand = join(normalized, "bin/codex.js");
|
||||
const publishedBridgeManifest = join(server, "node_modules/@agentclientprotocol/codex-acp/package.json");
|
||||
const publishedBridgeMetadata = { name: "@agentclientprotocol/codex-acp", version: "1.6.2" };
|
||||
const serverMetadata = { name: "@paperclipai/server", dependencies: { "@agentclientprotocol/codex-acp": "1.6.2" },
|
||||
optionalDependencies: metadata.optionalDependencies };
|
||||
const normalizedMetadata = { ...metadata, optionalDependencies: undefined };
|
||||
mkdirSync(dirname(normalizedCommand), { recursive: true });
|
||||
mkdirSync(dirname(publishedBridgeManifest), { recursive: true });
|
||||
writeFileSync(publishedBridgeManifest, JSON.stringify(publishedBridgeMetadata));
|
||||
writeFileSync(serverManifest, JSON.stringify(serverMetadata));
|
||||
writeFileSync(normalizedManifest, JSON.stringify(normalizedMetadata));
|
||||
writeFileSync(normalizedCommand, "#!" + process.execPath + "\\n", { mode: 0o755 });
|
||||
const normalizedRoots = () => codexExecutableReadOnlyRoots({ PATH: "/missing-codex-command" }, normalizedCommand);
|
||||
assert.ok(normalizedRoots().includes(vendor), "The normalized published wrapper must retain its selected native sandbox resources");
|
||||
const sandbox = createIsolatedCodexAppServerArgs({ HOME: "/private-provider-home" }, normalizedRoots()).join("\\n");
|
||||
assert.ok(sandbox.includes(JSON.stringify(vendor) + '=\"read\"'), "Native sandbox resources must reach the actual read-only permission configuration");
|
||||
assert.ok(sandbox.includes('\"/private-provider-home\"=\"none\"'), "The provider home remains denied");
|
||||
for (const ancestor of [platform, dirname(platform), join(root, "node_modules"), server, root]) {
|
||||
assert.ok(!normalizedRoots().includes(ancestor), "Platform discovery must not grant enclosing npm or server roots");
|
||||
}
|
||||
for (const [path, changed] of [
|
||||
[serverManifest, { ...serverMetadata, optionalDependencies: {} }],
|
||||
[serverManifest, { ...serverMetadata, optionalDependencies: { [platformName]: "npm:@openai/codex@0.159.0-" + process.platform + "-" + process.arch } }],
|
||||
[serverManifest, { ...serverMetadata, dependencies: { "@agentclientprotocol/codex-acp": "1.5.0" } }],
|
||||
[publishedBridgeManifest, { ...publishedBridgeMetadata, version: "1.5.0" }],
|
||||
[publishedBridgeManifest, { ...publishedBridgeMetadata, name: "unqualified-bridge" }],
|
||||
[normalizedManifest, { ...normalizedMetadata, version: "0.159.0" }],
|
||||
[normalizedManifest, { ...normalizedMetadata, optionalDependencies: {} }],
|
||||
[join(platform, "package.json"), { ...platformMetadata, name: "unqualified-native" }],
|
||||
[join(platform, "package.json"), { ...platformMetadata, version: "0.159.0-" + process.platform + "-" + process.arch }],
|
||||
[join(platform, "package.json"), { ...platformMetadata, os: ["unsupported-os"] }],
|
||||
[join(platform, "package.json"), { ...platformMetadata, cpu: ["unsupported-cpu"] }],
|
||||
]) {
|
||||
writeFileSync(path, JSON.stringify(changed));
|
||||
assert.ok(!normalizedRoots().includes(vendor), "Unqualified metadata must not grant native resources");
|
||||
writeFileSync(serverManifest, JSON.stringify(serverMetadata));
|
||||
writeFileSync(publishedBridgeManifest, JSON.stringify(publishedBridgeMetadata));
|
||||
writeFileSync(normalizedManifest, JSON.stringify(normalizedMetadata));
|
||||
writeFileSync(join(platform, "package.json"), JSON.stringify(platformMetadata));
|
||||
}
|
||||
const outsideVendor = join(root, "outside-vendor");
|
||||
mkdirSync(outsideVendor);
|
||||
rmSync(vendor, { recursive: true });
|
||||
symlinkSync(outsideVendor, vendor);
|
||||
assert.ok(!normalizedRoots().includes(outsideVendor), "A vendor symlink must not escape the selected platform package");
|
||||
rmSync(vendor); mkdirSync(vendor);
|
||||
const outsidePlatform = join(root, "outside-platform");
|
||||
mkdirSync(join(outsidePlatform, "vendor"), { recursive: true });
|
||||
writeFileSync(join(outsidePlatform, "package.json"), JSON.stringify(platformMetadata));
|
||||
rmSync(platform, { recursive: true }); symlinkSync(outsidePlatform, platform);
|
||||
assert.ok(!normalizedRoots().includes(join(outsidePlatform, "vendor")), "An alias symlink must not grant a foreign package");
|
||||
rmSync(platform); mkdirSync(vendor, { recursive: true });
|
||||
writeFileSync(join(platform, "package.json"), JSON.stringify(platformMetadata));
|
||||
|
||||
} finally { rmSync(root, { recursive: true, force: true }); }
|
||||
process.stdout.write("PINNED_CODEX_ISOLATION_VERIFIED");
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { configuredEnvironmentKeys } from "../../configured-environment.js";
|
||||
import { resolve, isAbsolute, join, dirname, delimiter } from "node:path";
|
||||
import { existsSync, realpathSync, readFileSync, statSync } from "node:fs";
|
||||
import { resolve, isAbsolute, join, dirname, delimiter, relative, sep } from "node:path";
|
||||
import { closeSync, constants, existsSync, fstatSync, openSync, realpathSync, readFileSync, statSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { QUALIFIED_ACPX_PROFILES } from "../acpx/qualified-profiles.js";
|
||||
|
||||
import {
|
||||
githubCredentialEnvironmentKeys,
|
||||
@@ -54,8 +55,12 @@ export function codexExecutableReadOnlyRoots(source: NodeJS.ProcessEnv, command
|
||||
// The npm entrypoint launches the platform package's native executable,
|
||||
// which Codex invokes again inside bwrap when starting each shell command.
|
||||
const platformPackage = `@openai/codex-${process.platform}-${process.arch}`;
|
||||
if (manifest.optionalDependencies?.[platformPackage]) {
|
||||
const platformManifest = createRequire(manifestPath).resolve(`${platformPackage}/package.json`);
|
||||
const normalizedPlatformManifest = manifest.optionalDependencies === undefined
|
||||
? normalizedCodexPlatformManifest(manifestPath, platformPackage)
|
||||
: undefined;
|
||||
if (manifest.optionalDependencies?.[platformPackage] || normalizedPlatformManifest) {
|
||||
const platformManifest = normalizedPlatformManifest
|
||||
?? createRequire(manifestPath).resolve(`${platformPackage}/package.json`);
|
||||
const packageRoot = realpathSync(dirname(platformManifest));
|
||||
const vendor = realpathSync(resolve(packageRoot, "vendor"));
|
||||
if (vendor.startsWith(`${packageRoot}/`)) add(vendor);
|
||||
@@ -68,6 +73,77 @@ export function codexExecutableReadOnlyRoots(source: NodeJS.ProcessEnv, command
|
||||
return [...roots];
|
||||
}
|
||||
|
||||
/** The published server, rather than its bundled JS wrapper, declares npm's host package. */
|
||||
function normalizedCodexPlatformManifest(runtimeManifest: string, platformPackage: string): string | undefined {
|
||||
const profile = QUALIFIED_ACPX_PROFILES.codex;
|
||||
const runtime = readCodexPackageManifest(runtimeManifest);
|
||||
if (runtime.name !== profile.agentRuntimePackage || runtime.version !== profile.agentRuntimeVersion
|
||||
|| runtime.optionalDependencies !== undefined) return undefined;
|
||||
const expectedVersion = `${profile.agentRuntimeVersion}-${process.platform}-${process.arch}`;
|
||||
const expectedDeclaration = `npm:${profile.agentRuntimePackage}@${expectedVersion}`;
|
||||
for (let root = dirname(dirname(runtimeManifest)), depth = 0; depth < 24; depth += 1) {
|
||||
const serverManifest = resolve(root, "package.json");
|
||||
if (existsSync(serverManifest)) {
|
||||
const server = readCodexPackageManifest(serverManifest);
|
||||
if (server.name === "@paperclipai/server") {
|
||||
if (server.dependencies?.[profile.agentServerPackage] !== profile.agentServerVersion
|
||||
|| server.optionalDependencies?.[platformPackage] !== expectedDeclaration
|
||||
|| !codexPathInside(resolve(root, "node_modules"), runtimeManifest)) return undefined;
|
||||
const bridgeManifest = createRequire(serverManifest).resolve(`${profile.agentServerPackage}/package.json`);
|
||||
if (!codexPathInside(resolve(root, "node_modules"), bridgeManifest)
|
||||
|| realpathSync(bridgeManifest) !== bridgeManifest) return undefined;
|
||||
const bridge = readCodexPackageManifest(bridgeManifest);
|
||||
if (bridge.name !== profile.agentServerPackage || bridge.version !== profile.agentServerVersion
|
||||
|| createRequire(bridgeManifest).resolve(`${profile.agentRuntimePackage}/package.json`) !== runtimeManifest) return undefined;
|
||||
const runtimeSelection = createRequire(runtimeManifest).resolve(`${platformPackage}/package.json`);
|
||||
const selected = createRequire(serverManifest).resolve(`${platformPackage}/package.json`);
|
||||
if (runtimeSelection !== selected || realpathSync(selected) !== selected) return undefined;
|
||||
const slot = (createRequire(serverManifest).resolve.paths(platformPackage) ?? []).some(directory => {
|
||||
try {
|
||||
return (dirname(directory) === root || codexPathInside(dirname(directory), root))
|
||||
&& realpathSync(directory) === directory
|
||||
&& selected === resolve(directory, platformPackage, "package.json")
|
||||
&& realpathSync(dirname(selected)) === dirname(selected);
|
||||
} catch { return false; }
|
||||
});
|
||||
if (!slot) return undefined;
|
||||
const native = readCodexPackageManifest(selected);
|
||||
if ((native.name !== platformPackage && native.name !== profile.agentRuntimePackage)
|
||||
|| native.version !== expectedVersion
|
||||
|| !Array.isArray(native.os) || !native.os.includes(process.platform)
|
||||
|| !Array.isArray(native.cpu) || !native.cpu.includes(process.arch)) return undefined;
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
const parent = dirname(root);
|
||||
if (parent === root) break;
|
||||
root = parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function codexPathInside(root: string, path: string): boolean {
|
||||
const value = relative(root, path);
|
||||
return value !== "" && value !== ".." && !value.startsWith(`..${sep}`) && !isAbsolute(value);
|
||||
}
|
||||
|
||||
function readCodexPackageManifest(path: string): Record<string, unknown> & {
|
||||
dependencies?: Record<string, unknown>;
|
||||
optionalDependencies?: Record<string, unknown>;
|
||||
} {
|
||||
const descriptor = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
||||
try {
|
||||
const before = fstatSync(descriptor, { bigint: true });
|
||||
if (!before.isFile() || before.nlink !== 1n || before.size < 1n || before.size > 256n * 1024n) throw new Error("Unsafe Codex package manifest");
|
||||
const bytes = readFileSync(descriptor), after = fstatSync(descriptor, { bigint: true });
|
||||
if (bytes.length !== Number(before.size) || before.dev !== after.dev || before.ino !== after.ino
|
||||
|| before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs) throw new Error("Codex package manifest changed during lookup");
|
||||
const value: unknown = JSON.parse(bytes.toString("utf8"));
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid Codex package manifest");
|
||||
return value as ReturnType<typeof readCodexPackageManifest>;
|
||||
} finally { closeSync(descriptor); }
|
||||
}
|
||||
|
||||
export const CODEX_SKILLLESS_PERMISSION_PROFILE =
|
||||
"paperclip-runner-workspace-only";
|
||||
export const CODEX_PLANNING_PERMISSION_PROFILE =
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
@@ -55,12 +55,13 @@ test('verification never elevates PR-controlled provisioning or cleanup on the h
|
||||
});
|
||||
|
||||
test('the installed Codex probe exercises the public export and rejects incomplete or mismatched packages', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'installed-codex-probe-'));
|
||||
const root = realpathSync(mkdtempSync(join(tmpdir(), 'installed-codex-probe-')));
|
||||
try {
|
||||
const consumer = join(root, 'consumer'); mkdirSync(consumer);
|
||||
const target = `${process.platform}-${process.arch}`;
|
||||
const platformPath = `node_modules/@openai/codex-${target}`;
|
||||
const platform = join(consumer, platformPath); mkdirSync(platform, { recursive: true });
|
||||
const nativeVendor = join(platform, 'vendor'); mkdirSync(nativeVendor);
|
||||
const manifest = { name: '@openai/codex', version: `0.160.0-${target}`, os: [process.platform], cpu: [process.arch] };
|
||||
writeFileSync(join(platform, 'package.json'), JSON.stringify(manifest));
|
||||
const entry = { name: manifest.name, version: manifest.version,
|
||||
@@ -71,6 +72,10 @@ test('the installed Codex probe exercises the public export and rejects incomple
|
||||
const vendor = join(server, 'dist/vendor/paperclip-runner'), drivers = join(vendor, 'drivers/acpx'); mkdirSync(drivers, { recursive: true });
|
||||
writeFileSync(join(server, 'package.json'), JSON.stringify({ name: '@paperclipai/server', type: 'module' }));
|
||||
const integrity = join(drivers, 'installation-integrity.js');
|
||||
const security = join(vendor, 'drivers/codex/codex-security-config.js'); mkdirSync(join(vendor, 'drivers/codex'));
|
||||
const securitySource = `export const codexExecutableReadOnlyRoots = () => [${JSON.stringify(nativeVendor)}];
|
||||
export const createIsolatedCodexAppServerArgs = (source, roots) => roots.map(root => JSON.stringify(root) + '=\"read\"');`;
|
||||
writeFileSync(security, securitySource);
|
||||
const moduleSource = `import assert from 'node:assert/strict'; import { writeFileSync } from 'node:fs';
|
||||
export function createAcpxPackageJsonResolver(root, manifest) { assert.equal(root, ${JSON.stringify(server)}); assert.equal(manifest, root + '/package.json'); return () => 'selected-authority'; }
|
||||
export async function verifyQualifiedAcpxInstallation(profile, resolver) { assert.deepEqual(profile, { agent: 'codex', model: 'gpt-5.4' }); assert.equal(resolver(), 'selected-authority');
|
||||
@@ -91,6 +96,7 @@ test('the installed Codex probe exercises the public export and rejects incomple
|
||||
assert.equal(JSON.parse(result.stdout).codexConsumerPlatform, target);
|
||||
assert.equal(JSON.parse(result.stdout).codexQualifiedInstallationVerified, true);
|
||||
assert.equal(JSON.parse(result.stdout).codexCommandLeaseVerified, true);
|
||||
assert.equal(JSON.parse(result.stdout).codexNativeSandboxResourcesVerified, true);
|
||||
assert.equal(readFileSync(join(server, 'lease-closed'), 'utf8'), 'ok');
|
||||
assert.equal(JSON.parse(result.stdout).providerCalls, 0);
|
||||
const legacyPath = 'node_modules/legacy-codex-adapter/node_modules/@openai/codex-' + target;
|
||||
@@ -116,6 +122,13 @@ test('the installed Codex probe exercises the public export and rejects incomple
|
||||
writeFileSync(index, "export const resolvePinnedCodexCommand = () => { throw new Error('Bundled Codex runtime version mismatch'); };");
|
||||
result = run(); assert.notEqual(result.status, 0); assert.match(result.stderr, /Bundled Codex runtime version mismatch/);
|
||||
writeFileSync(index, `export const resolvePinnedCodexCommand = () => ${JSON.stringify(command)};`);
|
||||
writeFileSync(security, securitySource.replace(JSON.stringify(nativeVendor), '"/missing-vendor-root"'));
|
||||
result = run(); assert.notEqual(result.status, 0); assert.match(result.stderr, /vendor directory must be readable/);
|
||||
writeFileSync(security, securitySource.replace(JSON.stringify(nativeVendor), `${JSON.stringify(nativeVendor)}, ${JSON.stringify(consumer)}`));
|
||||
result = run(); assert.notEqual(result.status, 0); assert.match(result.stderr, /must not grant enclosing npm/);
|
||||
writeFileSync(security, securitySource.replace("'=\"read\"'", "'=\"write\"'"));
|
||||
result = run(); assert.notEqual(result.status, 0); assert.match(result.stderr, /retain the selected native executable resources/);
|
||||
writeFileSync(security, securitySource);
|
||||
const emptySlot = join(server, 'node_modules/@openai/codex-' + target);
|
||||
mkdirSync(emptySlot, { recursive: true });
|
||||
result = run(); assert.equal(result.status, 0, result.stderr);
|
||||
|
||||
@@ -126,7 +126,18 @@ export function installedCodexProbeSource(indexPath, consumerRoot, expectedVersi
|
||||
const vendor = dirname(index);
|
||||
const integrityPath = join(vendor, 'drivers/acpx/installation-integrity.js');
|
||||
const profilesPath = join(vendor, 'drivers/acpx/qualified-profiles.js');
|
||||
assert.ok(contained(integrityPath) && contained(profilesPath), 'Codex integrity modules must come from the installed consumer');
|
||||
const securityPath = join(vendor, 'drivers/codex/codex-security-config.js');
|
||||
assert.ok(contained(integrityPath) && contained(profilesPath) && contained(securityPath), 'Codex verification modules must come from the installed consumer');
|
||||
const { codexExecutableReadOnlyRoots, createIsolatedCodexAppServerArgs } = await import(pathToFileURL(securityPath).href);
|
||||
const platformRoot = realpathSync(dirname(selectedPlatformManifest));
|
||||
const nativeVendor = realpathSync(join(platformRoot, 'vendor'));
|
||||
assert.ok(contained(nativeVendor) && nativeVendor.startsWith(platformRoot + sep), 'Codex vendor resources must remain inside their installed platform package');
|
||||
const sandboxSource = { HOME: '/private-provider-home', PATH: '/usr/local/bin:/usr/bin:/bin' };
|
||||
const readRoots = codexExecutableReadOnlyRoots(sandboxSource, command);
|
||||
assert.ok(readRoots.includes(nativeVendor), 'Selected native Codex vendor directory must be readable inside its sandbox');
|
||||
assert.ok(!readRoots.includes(platformRoot) && !readRoots.includes(root) && !readRoots.includes(sandboxSource.HOME), 'Codex sandbox must not grant enclosing npm or credential-home access');
|
||||
const sandboxArgs = createIsolatedCodexAppServerArgs(sandboxSource, readRoots).join('\\n');
|
||||
assert.ok(sandboxArgs.includes(JSON.stringify(nativeVendor) + '="read"'), 'Codex sandbox configuration must retain the selected native executable resources');
|
||||
const { createAcpxPackageJsonResolver, verifyQualifiedAcpxInstallation } = await import(pathToFileURL(integrityPath).href);
|
||||
const { resolveQualifiedAcpxProfile } = await import(pathToFileURL(profilesPath).href);
|
||||
const serverRoot = resolve(vendor, '../../..');
|
||||
@@ -136,7 +147,7 @@ export function installedCodexProbeSource(indexPath, consumerRoot, expectedVersi
|
||||
const lease = await installation.openCommand();
|
||||
await lease.close();
|
||||
console.log(JSON.stringify({ pinnedCodexCommandVerified: true, pinnedCodexVersion: ${JSON.stringify(expectedVersion)},
|
||||
...provenance, codexSelectedPlatformPath: selectedPlatform.path, codexSelectedPlatformVersion: selectedMetadata.version, codexQualifiedInstallationVerified: true, codexCommandLeaseVerified: true, providerCalls: 0 }));
|
||||
...provenance, codexSelectedPlatformPath: selectedPlatform.path, codexSelectedPlatformVersion: selectedMetadata.version, codexNativeSandboxResourcesVerified: true, codexQualifiedInstallationVerified: true, codexCommandLeaseVerified: true, providerCalls: 0 }));
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user