diagnostics: identify closed provider module rejection fences

This commit is contained in:
Dotta committed 2026-10-11 06:00:00 -05:00
1 parent 8e8999d681
commit 8f62eef8f1
5 files changed
+141 -30

No files matched your search

@@ -458,10 +458,11 @@ impl AcpxSidecarTransport {
let error = response.error.expect("failed response has validated error");
return Ok(CommandOutcome::Rejected(LocalRunnerError::invalid(
format!(
"ACPX sidecar command {} was rejected (retryable={}, classification={}){}",
"ACPX sidecar command {} was rejected (retryable={}, classification={}){}{}",
command.as_str(),
error.retryable,
response_error_classification(&error),
response_error_module_fence(&error).map(|fence| format!(" moduleFence={fence}")).unwrap_or_default(),
self.diagnostic_suffix(),
),
)));
@@ -923,7 +924,34 @@ fn stderr_diagnostic_categories(value: &str) -> BTreeSet<&'static str> {
categories
}
// Only fixed loader fence codes may cross the private provider-stderr boundary.
fn response_error_module_fence(error: &ResponseError) -> Option<&'static str> {
match error.code.as_str() {
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.RESOLUTION" => Some("resolution"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.OPEN" => Some("open"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.BOUNDS" => Some("bounds"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.ANCESTRY" => Some("ancestry"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.FORMAT" => Some("format"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.PATH_IDENTITY" => Some("path_identity"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.READ_RESULT" => Some("read_result"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.SIZE" => Some("size"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.DEVICE" => Some("device"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.INODE" => Some("inode"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.MODE" => Some("mode"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.UID" => Some("uid"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.GID" => Some("gid"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.LINK_COUNT" => Some("link_count"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.MTIME" => Some("mtime"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.BYTE_CONTINUITY" => Some("byte_continuity"),
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.REPEATED_CTIME" => Some("repeated_ctime"),
_ => None,
}
}
fn response_error_classification(error: &ResponseError) -> &'static str {
if response_error_module_fence(error).is_some() {
return "agent_startup_unverified_module";
}
match error.code.as_str() {
"ACP_MODEL_UNSUPPORTED" => return "requested_model_unsupported",
"AGENT_STARTUP_FAILED" => return "agent_startup_failed",
@@ -1179,7 +1207,7 @@ mod tests {
})
.collect();
let response = json!({ "protocolVersion": GENERATED_ACPX_SIDECAR_PROTOCOL_VERSION,
"id": 1, "ok": false, "error": { "code": "UNKNOWN_PRIVATE_CODE",
"id": 1, "ok": false, "error": { "code": "AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.REPEATED_CTIME",
"message": "/private/token-canary", "retryable": false } });
let lines = frames
.iter()
@@ -1203,9 +1231,12 @@ mod tests {
.unwrap_err()
.to_string();
assert!(
error.contains("was rejected (retryable=false, classification=unclassified)"),
error.contains(
"was rejected (retryable=false, classification=agent_startup_unverified_module)"
),
"{error}"
);
assert!(error.contains("moduleFence=repeated_ctime"), "{error}");
assert!(
error.contains("admissionStage=handshake admissionElapsedMs=24"),
"{error}"
@@ -1213,7 +1244,7 @@ mod tests {
assert!(
!error.contains("private")
&& !error.contains("canary")
&& !error.contains("UNKNOWN_PRIVATE_CODE"),
&& !error.contains("AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.REPEATED_CTIME"),
"{error}"
);
assert!(!transport.poisoned);
@@ -1361,6 +1392,48 @@ mod tests {
}
}
#[test]
fn module_fence_diagnostics_are_closed_and_keep_the_top_level_category() {
for (suffix, expected) in [
("RESOLUTION", Some("resolution")),
("OPEN", Some("open")),
("BOUNDS", Some("bounds")),
("ANCESTRY", Some("ancestry")),
("FORMAT", Some("format")),
("PATH_IDENTITY", Some("path_identity")),
("READ_RESULT", Some("read_result")),
("SIZE", Some("size")),
("DEVICE", Some("device")),
("INODE", Some("inode")),
("MODE", Some("mode")),
("UID", Some("uid")),
("GID", Some("gid")),
("LINK_COUNT", Some("link_count")),
("MTIME", Some("mtime")),
("BYTE_CONTINUITY", Some("byte_continuity")),
("REPEATED_CTIME", Some("repeated_ctime")),
("MODE private-canary", None),
("MODE.PRIVATE_CANARY", None),
("PRIVATE_CANARY", None),
("", None),
] {
let error = ResponseError {
code: format!("AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.{suffix}"),
message: "/private/token-canary MODE".to_owned(),
retryable: false,
};
assert_eq!(response_error_module_fence(&error), expected);
assert_eq!(
response_error_classification(&error),
if expected.is_some() {
"agent_startup_unverified_module"
} else {
"unclassified"
}
);
}
}
#[test]
fn candidate_auth_diagnostics_use_only_closed_codes_and_never_provider_text() {
for (code, expected) in [
@@ -126,6 +126,21 @@ describe("ACPX sidecar input sequencing", () => {
expect(acpxSidecarErrorCode(new Error("wrapped", { cause: error }))).toBe(code);
});
it.each(["RESOLUTION", "OPEN", "BOUNDS", "ANCESTRY", "FORMAT", "PATH_IDENTITY", "READ_RESULT",
"SIZE", "DEVICE", "INODE", "MODE", "UID", "GID", "LINK_COUNT", "MTIME", "BYTE_CONTINUITY", "REPEATED_CTIME"])(
"preserves only closed module fence %s", fence => {
const classify = (marker: string) => acpxSidecarErrorCode(Object.assign(new Error("private-canary"), {
detailCode: "AGENT_STARTUP_FAILED", stderrSummary: `${marker}\nprivate-canary /secret`, exitCode: 1,
}));
expect(classify(`PAPERCLIP_ACPX_IMPORT_FAILURE:UNVERIFIED_MODULE:${fence}`))
.toBe(`AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.${fence}`);
for (const suffix of ["PRIVATE_CANARY", `${fence} private-canary`, `${fence}.PRIVATE_CANARY`]) {
expect(classify(`PAPERCLIP_ACPX_IMPORT_FAILURE:UNVERIFIED_MODULE:${suffix}`))
.toBe("AGENT_STARTUP_FAILED.EXIT_NONZERO");
}
},
);
it.each(["OTHER", "UNVERIFIED_MODULE"])("classifies closed import marker %s without private stderr", category => {
const classify = (stderrSummary: string) => acpxSidecarErrorCode(Object.assign(new Error("private-canary"), {
detailCode: "AGENT_STARTUP_FAILED", stderrSummary, exitCode: 1,
@@ -90,8 +90,11 @@ function directAcpxSidecarErrorCode(error: Error): string | null {
typeof details.stderrSummary === "string" ? details.stderrSummary : "";
// Bootstrap writes this fixed marker before printing private error details and
// terminating. Never copy those details into the public classification.
const importFailure = /(?:^|\n)PAPERCLIP_ACPX_IMPORT_FAILURE:(UNVERIFIED_MODULE|OTHER)(?:\r?\n|$)/.exec(stderr);
if (importFailure?.[1] === "UNVERIFIED_MODULE") return "AGENT_STARTUP_FAILED.UNVERIFIED_MODULE";
const importFailure = /(?:^|\n)PAPERCLIP_ACPX_IMPORT_FAILURE:(UNVERIFIED_MODULE|OTHER)(?::([A-Z_]+))?(?:\r?\n|$)/.exec(stderr);
if (importFailure?.[1] === "UNVERIFIED_MODULE" && importFailure[2] === undefined) return "AGENT_STARTUP_FAILED.UNVERIFIED_MODULE";
if (importFailure?.[1] === "UNVERIFIED_MODULE" && MODULE_FENCES.has(importFailure[2])) {
return `AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.${importFailure[2]}`;
}
if (/ERR_ACPX_UNVERIFIED_MODULE/.test(stderr)) {
return "AGENT_STARTUP_FAILED.UNVERIFIED_MODULE";
}
@@ -110,7 +113,7 @@ function directAcpxSidecarErrorCode(error: Error): string | null {
if (/ERR_INVALID_ARG|invalid argument/i.test(stderr)) {
return "AGENT_STARTUP_FAILED.INVALID_ARGUMENT";
}
if (importFailure) return "AGENT_STARTUP_FAILED.OTHER";
if (importFailure?.[1] === "OTHER" && importFailure[2] === undefined) return "AGENT_STARTUP_FAILED.OTHER";
if (!stderr.trim()) return "AGENT_STARTUP_FAILED.NO_STDERR";
if (typeof details.signal === "string" && details.signal) {
return "AGENT_STARTUP_FAILED.SIGNAL";
@@ -134,6 +137,8 @@ const GENERIC_ACPX_OUTPUT_CODES = new Set([
"USAGE",
]);
const MODULE_FENCES = new Set(["RESOLUTION", "OPEN", "BOUNDS", "ANCESTRY", "FORMAT", "PATH_IDENTITY", "READ_RESULT", "SIZE", "DEVICE", "INODE", "MODE", "UID", "GID", "LINK_COUNT", "MTIME", "BYTE_CONTINUITY", "REPEATED_CTIME"]);
const STABLE_ACPX_SIDECAR_CODES = new Set([
"COPILOT_POLICY_VIOLATION",
"COPILOT_DETACHED_WORK_UNSUPPORTED",
@@ -151,6 +156,7 @@ const STABLE_ACPX_SIDECAR_CODES = new Set([
"AGENT_STARTUP_FAILED.SIGNAL",
"AGENT_STARTUP_FAILED.SYNTAX_ERROR",
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE",
...Array.from(MODULE_FENCES, fence => `AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.${fence}`),
"AUTH_REQUIRED",
"CLAUDE_ACP_SESSION_CREATE_TIMEOUT",
"SESSION_CONFIG_OPTION_REPLAY_FAILED",
@@ -1752,6 +1752,13 @@ describe("ACPX installation integrity", () => {
expect(verify).toHaveBeenCalledTimes(pass);
} else {
expect(read).toThrow();
const reasons: Record<string, string> = { recurring: "REPEATED_CTIME", changed_bytes: "BYTE_CONTINUITY",
dev: "DEVICE", ino: "INODE", mode: "MODE", uid: "UID", gid: "GID", nlink: "LINK_COUNT",
size: "SIZE", mtimeNs: "MTIME", not_regular: "BOUNDS", oversized: "BOUNDS" };
if (reasons[transition]) {
pass = 0;
expect(read).toThrow(expect.objectContaining({ code: "ERR_ACPX_UNVERIFIED_MODULE", moduleFence: reasons[transition] }));
}
expect(pass).toBeLessThanOrEqual(2);
if (transition === "snapshot_second") expect(verify).toHaveBeenCalledTimes(2);
}
@@ -1799,13 +1806,18 @@ describe("ACPX installation integrity", () => {
const closed = once(child, "close");
let stdout = "";
child.stdout?.on("data", bytes => { stdout += String(bytes); });
// Drain private stderr while keeping it out of test/report output.
child.stderr?.resume();
let stderr = "";
child.stderr?.on("data", bytes => { stderr += String(bytes); });
const timeout = setTimeout(() => child.kill("SIGKILL"), 4_000);
try {
const [code] = await closed;
expect(code).toBe(transition === "once" ? 0 : 1);
expect(stdout).toBe(transition === "once" ? "dependency-success" : "");
const fences: Record<string, string> = { recurring: "REPEATED_CTIME", bytes: "BYTE_CONTINUITY", mode: "MODE" };
const fence = fences[transition];
if (fence && !(transition === "bytes" && process.platform === "darwin")) {
expect(stderr).toContain(`PAPERCLIP_ACPX_IMPORT_FAILURE:UNVERIFIED_MODULE:${fence}\n`);
}
} finally {
clearTimeout(timeout);
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
@@ -1867,7 +1879,7 @@ describe("ACPX installation integrity", () => {
detailCode: "AGENT_STARTUP_FAILED", stderrSummary: stderr,
exitCode: child.exitCode, signal: child.signalCode,
}));
expect(code).toBe(`AGENT_STARTUP_FAILED.${category}`);
expect(code).toBe(category === "UNVERIFIED_MODULE" ? "AGENT_STARTUP_FAILED.UNVERIFIED_MODULE.ANCESTRY" : "AGENT_STARTUP_FAILED.OTHER");
expect(code).not.toMatch(/canary|\//);
expect(stderr).not.toContain("must-not-execute");
} finally {
@@ -1861,40 +1861,45 @@ export function readStableSnapshotModuleSource(input: {
verify: (bytes: Buffer) => void;
maxBytes: number;
}): Buffer {
const reject = (): never => {
const reject = (moduleFence: string): never => {
throw Object.assign(new Error("ACPX provider module changed while it was read"), {
code: "ERR_ACPX_UNVERIFIED_MODULE",
code: "ERR_ACPX_UNVERIFIED_MODULE", moduleFence,
});
};
let before = input.before;
let first: Buffer | null = null;
for (let attempt = 0; attempt < 2; attempt += 1) {
if (!before.isFile() || before.size < 0n || before.size > BigInt(input.maxBytes)) reject();
if (!before.isFile() || before.size < 0n || before.size > BigInt(input.maxBytes)) reject("BOUNDS");
const size = Number(before.size);
const buffer = Buffer.alloc(size + 1);
let offset = 0;
while (offset < buffer.length) {
const count = input.read(buffer, offset, buffer.length - offset, offset);
if (!Number.isSafeInteger(count) || count < 0 || count > buffer.length - offset) reject();
if (!Number.isSafeInteger(count) || count < 0 || count > buffer.length - offset) reject("READ_RESULT");
if (count === 0) break;
offset += count;
}
const bytes = buffer.subarray(0, offset);
const after = input.stat();
input.verify(bytes);
if (!after.isFile() || bytes.length !== size || BigInt(bytes.length) !== after.size
|| before.dev !== after.dev || before.ino !== after.ino || before.mode !== after.mode
|| before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink
|| before.size !== after.size || before.mtimeNs !== after.mtimeNs) reject();
if (first !== null && !first.equals(bytes)) reject();
if (!after.isFile()) reject("BOUNDS");
if (bytes.length !== size || BigInt(bytes.length) !== after.size || before.size !== after.size) reject("SIZE");
if (before.dev !== after.dev) reject("DEVICE");
if (before.ino !== after.ino) reject("INODE");
if (before.mode !== after.mode) reject("MODE");
if (before.uid !== after.uid) reject("UID");
if (before.gid !== after.gid) reject("GID");
if (before.nlink !== after.nlink) reject("LINK_COUNT");
if (before.mtimeNs !== after.mtimeNs) reject("MTIME");
if (first !== null && !first.equals(bytes)) reject("BYTE_CONTINUITY");
if (before.ctimeNs === after.ctimeNs) return bytes;
if (attempt !== 0) reject();
if (attempt !== 0) reject("REPEATED_CTIME");
first = bytes;
// Continue from the full final metadata, including ctime. A further drift
// between passes is therefore rejected, not granted another transition.
before = after;
}
return reject();
return reject("REPEATED_CTIME");
}
function snapshotBootstrap(format: AcpxCommandFormat, guarded = false): string {
@@ -1960,7 +1965,7 @@ function snapshotBootstrap(format: AcpxCommandFormat, guarded = false): string {
`const snapshotDescriptorResolution = ${snapshotDescriptorResolution.toString()};`,
"const dependencyAncestorIndex = (url) => { const recorded = dependencyAncestorByUrl.get(url); return recorded === undefined ? snapshotDescriptorAncestorIndex(url, directoryUrl, dependencyDirectoryUrls) : recorded; };",
`const guardSnapshotModuleResolution = ${guardSnapshotModuleResolution.toString()};`,
'const canonicalizeDescriptorResolution = (url) => { if (typeof url !== "string" || !url.startsWith("file:") || snapshotDescriptorAncestorIndex(url, directoryUrl, dependencyDirectoryUrls) < 0) return url; try { return pathToFileURL(fs.realpathSync(fileURLToPath(url))).href; } catch { const error = new Error("ACPX provider module could not be canonicalized through its retained descriptor"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; throw error; } };',
'const canonicalizeDescriptorResolution = (url) => { if (typeof url !== "string" || !url.startsWith("file:") || snapshotDescriptorAncestorIndex(url, directoryUrl, dependencyDirectoryUrls) < 0) return url; try { return pathToFileURL(fs.realpathSync(fileURLToPath(url))).href; } catch { const error = new Error("ACPX provider module could not be canonicalized through its retained descriptor"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; error.moduleFence = "RESOLUTION"; throw error; } };',
'const rememberDependencyAncestor = (specifier, resolution) => { const canonicalUrl = canonicalizeDescriptorResolution(resolution?.url); const pinned = snapshotDescriptorResolution(canonicalUrl, directoryUrl, dependencyDirectoryUrls, canonicalDirectoryUrl, canonicalDependencyDirectoryUrls); guardSnapshotModuleResolution(isBuiltin(specifier), resolution?.url, pinned !== null); if (pinned !== null && typeof resolution?.url === "string") { for (const rememberedUrl of [resolution.url, canonicalUrl, pinned.url]) { if (typeof rememberedUrl !== "string") continue; dependencyAncestorByUrl.set(rememberedUrl, pinned.ancestorIndex); if (typeof resolution.format === "string") descriptorFormatByUrl.set(rememberedUrl, resolution.format); } } return pinned === null || pinned.url === resolution?.url ? resolution : { ...resolution, url: pinned.url }; };',
`const source = fs.readFileSync(${COMMAND_SOURCE_FD});`,
"let resolvingDescriptorBare = false;",
@@ -1987,7 +1992,7 @@ function snapshotBootstrap(format: AcpxCommandFormat, guarded = false): string {
'return new URL(segments.slice(ancestorLevels).join("/") || ".", dependencyDirectoryUrls[targetAncestorIndex]);',
"};",
"const pinnedSpecifier = relativeImport ? pinRelativeSpecifier() : null;",
'if (relativeImport && pinnedSpecifier === null) { const error = new Error("ACPX provider relative module escaped its verified package"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; throw error; }',
'if (relativeImport && pinnedSpecifier === null) { const error = new Error("ACPX provider relative module escaped its verified package"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; error.moduleFence = "ANCESTRY"; throw error; }',
'const lookupSpecifier = pinnedSpecifier === null ? specifier : context.conditions?.includes("require") ? fileURLToPath(pinnedSpecifier) : pinnedSpecifier.href;',
"const snapshotImport = entryImport || parentDependencyAncestorIndex >= 0;",
'const bareImport = snapshotImport && !isBuiltin(specifier) && !specifier.startsWith("./") && !specifier.startsWith("../") && !specifier.startsWith("/") && !specifier.includes(":");',
@@ -2017,21 +2022,21 @@ function snapshotBootstrap(format: AcpxCommandFormat, guarded = false): string {
"if (!descriptorLookup) return nextLoad(url, context);",
"const canonicalRootUrl = url.startsWith(directoryUrl) ? canonicalDirectoryUrl : canonicalDependencyDirectoryUrls[dependencyDescriptorIndex];",
"let moduleFd;",
'try { moduleFd = fs.openSync(fileURLToPath(url), fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW); } catch { const error = new Error("ACPX provider module could not be opened without following its final component"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; throw error; }',
'try { moduleFd = fs.openSync(fileURLToPath(url), fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW); } catch { const error = new Error("ACPX provider module could not be opened without following its final component"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; error.moduleFence = "OPEN"; throw error; }',
"try {",
"const metadataBefore = fs.fstatSync(moduleFd, { bigint: true });",
`if (!metadataBefore.isFile() || metadataBefore.size > BigInt(${MAX_AGENT_COMMAND_BYTES})) { const error = new Error("ACPX provider module is not a bounded regular file"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; throw error; }`,
`if (!metadataBefore.isFile() || metadataBefore.size > BigInt(${MAX_AGENT_COMMAND_BYTES})) { const error = new Error("ACPX provider module is not a bounded regular file"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; error.moduleFence = "BOUNDS"; throw error; }`,
'const openedUrl = pathToFileURL(fs.realpathSync(privateSnapshot ? fileURLToPath(url) : "/proc/self/fd/" + moduleFd)).href;',
'if (typeof canonicalRootUrl !== "string" || !openedUrl.startsWith(canonicalRootUrl)) { const error = new Error("ACPX provider module escaped descriptor-pinned ancestry"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; throw error; }',
'if (typeof canonicalRootUrl !== "string" || !openedUrl.startsWith(canonicalRootUrl)) { const error = new Error("ACPX provider module escaped descriptor-pinned ancestry"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; error.moduleFence = "ANCESTRY"; throw error; }',
"const packageFormat = url.startsWith(directoryUrl) ? serverPackageFormat : dependencyAncestorFormats[dependencyDescriptorIndex];",
"const hintedFormat = descriptorFormatByUrl.get(url) || context.format;",
"const extension = extname(fileURLToPath(url));",
'const moduleFormat = extension === ".mjs" ? "module" : extension === ".cjs" ? "commonjs" : extension === ".json" ? "json" : extension === ".node" ? "addon" : extension === ".js" ? (hintedFormat === "module" || hintedFormat === "commonjs" ? hintedFormat : packageFormat) : hintedFormat;',
'if (moduleFormat !== "module" && moduleFormat !== "commonjs" && moduleFormat !== "json") { const error = new Error("ACPX provider module format is not supported by descriptor-pinned loading"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; throw error; }',
'if (moduleFormat !== "module" && moduleFormat !== "commonjs" && moduleFormat !== "json") { const error = new Error("ACPX provider module format is not supported by descriptor-pinned loading"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; error.moduleFence = "FORMAT"; throw error; }',
// Re-check both the retained descriptor's canonical path and its named
// inode after each pass; a rename is not harmless hydration just because
// its already-open descriptor still returns identical bytes.
'const statModule = () => { const currentUrl = pathToFileURL(fs.realpathSync(privateSnapshot ? fileURLToPath(url) : "/proc/self/fd/" + moduleFd)).href; const named = fs.lstatSync(fileURLToPath(url), { bigint: true }); const metadata = fs.fstatSync(moduleFd, { bigint: true }); if (currentUrl !== openedUrl || !named.isFile() || named.dev !== metadata.dev || named.ino !== metadata.ino) { const error = new Error("ACPX provider module path changed while it was read"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; throw error; } return metadata; };',
'const statModule = () => { const currentUrl = pathToFileURL(fs.realpathSync(privateSnapshot ? fileURLToPath(url) : "/proc/self/fd/" + moduleFd)).href; const named = fs.lstatSync(fileURLToPath(url), { bigint: true }); const metadata = fs.fstatSync(moduleFd, { bigint: true }); if (currentUrl !== openedUrl || !named.isFile() || named.dev !== metadata.dev || named.ino !== metadata.ino) { const error = new Error("ACPX provider module path changed while it was read"); error.code = "ERR_ACPX_UNVERIFIED_MODULE"; error.moduleFence = "PATH_IDENTITY"; throw error; } return metadata; };',
`const moduleSource = readStableSnapshotModuleSource({ before: metadataBefore, stat: statModule, read: (buffer, offset, length, position) => fs.readSync(moduleFd, buffer, offset, length, position), verify: (bytes) => verifySnapshotBytes(fileURLToPath(url), bytes), maxBytes: ${MAX_AGENT_COMMAND_BYTES} });`,
"return { format: moduleFormat, source: moduleSource, shortCircuit: true };",
"} finally { fs.closeSync(moduleFd); }",
@@ -2046,7 +2051,7 @@ function snapshotBootstrap(format: AcpxCommandFormat, guarded = false): string {
guarded
? "const exitOnImportFailure = reapOnGuardianLoss;"
: "const exitOnImportFailure = process.exit.bind(process);",
'import(target).catch((error) => { let category = "OTHER"; try { if (error?.code === "ERR_ACPX_UNVERIFIED_MODULE") category = "UNVERIFIED_MODULE"; } catch {} try { writeImportFailure(2, "PAPERCLIP_ACPX_IMPORT_FAILURE:" + category + "\\n"); console.error(error); } finally { exitOnImportFailure(1); } });',
'import(target).catch((error) => { let category = "OTHER"; try { if (error?.code === "ERR_ACPX_UNVERIFIED_MODULE") { category = "UNVERIFIED_MODULE"; const fence = error.moduleFence; if (["RESOLUTION", "OPEN", "BOUNDS", "ANCESTRY", "FORMAT", "PATH_IDENTITY", "READ_RESULT", "SIZE", "DEVICE", "INODE", "MODE", "UID", "GID", "LINK_COUNT", "MTIME", "BYTE_CONTINUITY", "REPEATED_CTIME"].includes(fence)) category += ":" + fence; } } catch {} try { writeImportFailure(2, "PAPERCLIP_ACPX_IMPORT_FAILURE:" + category + "\\n"); console.error(error); } finally { exitOnImportFailure(1); } });',
].join("");
}
@@ -2079,7 +2084,7 @@ export function guardSnapshotModuleResolution(
const error = new Error(
"ACPX provider module escaped descriptor-pinned ancestry",
);
Object.assign(error, { code: "ERR_ACPX_UNVERIFIED_MODULE" });
Object.assign(error, { code: "ERR_ACPX_UNVERIFIED_MODULE", moduleFence: "ANCESTRY" });
throw error;
}
}