fix: report provider admission failures as typed codes (#8066)

* fix: report provider admission failures as typed codes

* fix(evm): keep limiter path on admission diagnostics

* fix: assert WABT and CBOR admission codes

* fix: keep admission details on returned failures

* fix(evm): detect repeated CBOR keys in the decoder

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
morlutoandCursor Agent authored and GitHub committed 2026-10-30 06:48:01 +08:00
1 parent 3b647132c6
commit 317bf3fe23
63 files changed
+1021 -432

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
const EVM_ADMISSION_CODES = [
"prlimit_not_absolute",
"prlimit_not_file",
"cleanup_incomplete",
"cleanup_unconfirmed",
"marker_mismatch",
"request_path_missing",
"tag_or_float",
"text_bytes_unavailable",
"collection_exceeds_bound",
"nesting_exceeds_profile",
"value_outside_profile",
"repeated_map_key",
"root_not_map",
] as const;
export type EvmAdmissionCode = (typeof EVM_ADMISSION_CODES)[number];
/** EVM limiter, worker, or metadata admission failure. `code` is the stable identity. */
export class EvmAdmissionError extends Error {
readonly _tag = "EvmAdmissionError";
constructor(
readonly code: EvmAdmissionCode,
readonly detail?: Readonly<Record<string, string | number>>,
options?: ErrorOptions,
) {
super(code, options);
this.name = "EvmAdmissionError";
}
}
+26 -13
View File
@@ -1,4 +1,6 @@
import { EvmAdmissionError } from "./EvmAdmissionError.js";
import { snapshotEnvironment } from "../process/snapshotEnvironment.js";
import type { JsonValue } from "../domain/jsonValue.js";
import { safeParseUtf8Json } from "../domain/safeJson.js";
import { createHash, randomUUID } from "node:crypto";
import { access, readFile, stat, writeFile } from "node:fs/promises";
@@ -110,7 +112,7 @@ export interface EvmLimiterProbeOptions {
type EvmLimiterSelection = {
readonly path: string;
readonly diagnostics: Readonly<Record<string, string>>;
readonly diagnostics: Readonly<Record<string, JsonValue>>;
};
type EvmLimiterUnavailable = EvmLimiterSelection & {
readonly reason: string;
@@ -142,20 +144,29 @@ const selectEvmLimiter = async (
const diagnostics = { limiter_path: path, platform, arch };
try {
if (!isAbsolute(path))
throw new Error(
"REA_EVM_PRLIMIT_COMMAND must be an absolute executable path.",
);
throw new EvmAdmissionError("prlimit_not_absolute", {
setting: "REA_EVM_PRLIMIT_COMMAND",
});
const fs = probes?.fs ?? { access, stat };
await fs.access(path, constants.X_OK);
throwIfAborted();
if (!(await fs.stat(path)).isFile())
throw new Error(
"Configured prlimit must be a regular executable file, not a directory.",
);
throw new EvmAdmissionError("prlimit_not_file");
throwIfAborted();
return ok({ path, diagnostics });
} catch (cause: unknown) {
throwIfAborted();
if (cause instanceof EvmAdmissionError)
return err({
path,
diagnostics: {
...diagnostics,
code: cause.code,
...cause.detail,
},
reason: cause.code,
cause,
});
const reason = `Offline EVM interface inspection requires caller-supplied util-linux prlimit: ${path}; ${cause instanceof Error ? cause.message : String(cause)}`;
return err({ path, diagnostics, reason, cause });
}
@@ -452,10 +463,13 @@ export class EvmoleInterfaceProvider implements EvmInterfacePort {
stopped.status === "incomplete" ||
stopped.status === "not-owned"
)
throw new Error(
throw new EvmAdmissionError(
stopped.status === "incomplete"
? stopped.reason
: "Owned EVM worker cleanup did not confirm ownership.",
? "cleanup_incomplete"
: "cleanup_unconfirmed",
stopped.status === "incomplete"
? { reason: stopped.reason }
: undefined,
{ cause: stopped },
);
},
@@ -511,6 +525,7 @@ export class EvmoleInterfaceProvider implements EvmInterfacePort {
selectedLimiter.error.reason,
{
cause: selectedLimiter.error.cause,
diagnostics: selectedLimiter.error.diagnostics,
userMessage: selectedLimiter.error.reason,
},
);
@@ -813,9 +828,7 @@ export class EvmoleInterfaceProvider implements EvmInterfacePort {
marker = completed;
}
if (!marker.bytes.equals(Buffer.from("F")))
throw new Error(
"Private worker failure marker does not match the observed exit status.",
);
throw new EvmAdmissionError("marker_mismatch");
fileFailure = { verified: true, failure: null };
} catch (markerFailure: unknown) {
fileFailure = {
+2 -1
View File
@@ -1,3 +1,4 @@
import { EvmAdmissionError } from "./EvmAdmissionError.js";
import { createHash } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
import { z } from "zod";
@@ -23,7 +24,7 @@ class UnsupportedContainer extends Error {}
const main = async (): Promise<void> => {
const requestPath = process.argv[2];
if (requestPath === undefined)
throw new Error("Owned worker request path missing.");
throw new EvmAdmissionError("request_path_missing");
const request = requestSchema.parse(
JSON.parse(await readFile(requestPath, "utf8")),
);
+18 -1
View File
@@ -163,11 +163,28 @@ it("rejects repeated deeply nested container keys promptly", () => {
inspectSolidityMetadata(bytecode("a2" + key + "01" + key + "02")),
).toMatchObject({
status: "not-decoded",
diagnostic: expect.stringContaining("found repeat map key"),
diagnostic: "repeated_map_key",
admission: {
code: "repeated_map_key",
detail: { key_length: expect.any(Number) },
},
});
expect(performance.now() - started).toBeLessThan(1000);
});
it("records the profile depth when CBOR nesting is refused", () => {
expect(
inspectSolidityMetadata(bytecode("a16178" + "81".repeat(65) + "00")),
).toMatchObject({
status: "not-decoded",
diagnostic: "nesting_exceeds_profile",
admission: {
code: "nesting_exceeds_profile",
detail: { max_depth: 64 },
},
});
});
it.each(["", "60006000f3", "000000", "00010001"])(
"does not claim absent Solidity metadata for an unavailable trailer: %s",
(hex) =>
+67 -23
View File
@@ -1,3 +1,4 @@
import { EvmAdmissionError } from "./EvmAdmissionError.js";
import { decode, Tokenizer, Type, type Token } from "cborg";
import type {
SolidityMetadata,
@@ -14,39 +15,52 @@ const decodeOptions = {
allowUndefined: false,
allowInfinity: false,
allowNaN: false,
rejectDuplicateMapKeys: true,
rejectDuplicateMapKeys: false,
retainStringBytes: true,
} as const;
/** Bound recursive decoder entry before collection interpretation/allocation. */
class BoundedMetadataTokenizer extends Tokenizer {
readonly remaining: number[] = [];
readonly #mapKeys: Array<Set<string> | undefined> = [];
constructor(bytes: Uint8Array) {
super(bytes, decodeOptions);
}
override next(): Token {
while (this.remaining.at(-1) === 0) this.remaining.pop();
while (this.remaining.at(-1) === 0) {
this.remaining.pop();
this.#mapKeys.pop();
}
const token = super.next();
if (
Type.equals(token.type, Type.tag) ||
Type.equals(token.type, Type.float)
)
throw new Error(
"CBOR tags and floating-point values are outside this metadata profile.",
);
throw new EvmAdmissionError("tag_or_float");
if (Type.equals(token.type, Type.string)) {
if (token.byteValue !== undefined)
token.value = utf8.decode(token.byteValue);
// cborg answers the shortest-form empty text (0x60) with a shared token that has no
// bytes; it can only mean "", which is already its value.
else if (token.value !== "" || token.encodedLength !== 1)
throw new Error(
"CBOR text bytes were unavailable for strict UTF-8 validation.",
);
throw new EvmAdmissionError("text_bytes_unavailable");
}
const parent = this.remaining.length - 1;
const mapKeys = parent >= 0 ? this.#mapKeys[parent] : undefined;
const onKey =
mapKeys !== undefined && parent >= 0 && this.remaining[parent]! % 2 === 0;
if (parent >= 0) this.remaining[parent]!--;
if (
onKey &&
mapKeys !== undefined &&
!Type.equals(token.type, Type.map) &&
!Type.equals(token.type, Type.array)
) {
const identity = keyIdentity(token.value);
if (mapKeys.has(identity)) throw repeatedMapKey(identity);
mapKeys.add(identity);
}
if (
Type.equals(token.type, Type.map) ||
Type.equals(token.type, Type.array)
@@ -61,11 +75,14 @@ class BoundedMetadataTokenizer extends Tokenizer {
children < 0 ||
children > this.data.length
)
throw new Error(
"Declared CBOR collection exceeds the encoded item-byte bound.",
);
throw new EvmAdmissionError("collection_exceeds_bound");
if (this.remaining.length >= MAX_DEPTH)
throw new Error("CBOR nesting exceeds the 64-level metadata profile.");
throw new EvmAdmissionError("nesting_exceeds_profile", {
max_depth: MAX_DEPTH,
});
this.#mapKeys.push(
Type.equals(token.type, Type.map) ? new Set() : undefined,
);
this.remaining.push(children);
}
return token;
@@ -85,9 +102,7 @@ const scalar = (value: unknown): SolidityMetadataScalar => {
if (value instanceof Map) return { kind: "uninterpreted", container: "map" };
if (Array.isArray(value))
return { kind: "uninterpreted", container: "array" };
throw new Error(
"CBOR decoder returned a value outside the admitted profile.",
);
throw new EvmAdmissionError("value_outside_profile");
};
/** Value identity for duplicate-key checks; map entry order is not significant. */
@@ -101,14 +116,21 @@ const keyIdentity = (value: unknown): string => {
return JSON.stringify(scalar(value));
};
const repeatedMapKey = (identity: string): EvmAdmissionError =>
new EvmAdmissionError(
"repeated_map_key",
identity.length <= 128
? { key: identity }
: { key_length: identity.length },
);
/** Check every map boundary, even when nested values remain uninterpreted. */
const validateMapKeys = (value: unknown): void => {
if (value instanceof Map) {
const seen = new Set<string>();
for (const [key, item] of value) {
const identity = keyIdentity(key);
if (seen.has(identity))
throw new Error(`CBOR decode error: found repeat map key ${identity}`);
if (seen.has(identity)) throw repeatedMapKey(identity);
seen.add(identity);
validateMapKeys(key);
validateMapKeys(item);
@@ -118,6 +140,32 @@ const validateMapKeys = (value: unknown): void => {
}
};
/** Keep the typed code and its detail; map only the decoder's duplicate-key throw. */
const solidityFailure = (
cause: unknown,
): {
readonly diagnostic: string;
readonly admission?: {
readonly code: string;
readonly detail?: Readonly<Record<string, string | number>>;
};
} => {
if (cause instanceof EvmAdmissionError)
return {
diagnostic: cause.code,
admission: {
code: cause.code,
...(cause.detail === undefined ? {} : { detail: cause.detail }),
},
};
return {
diagnostic: (cause instanceof Error ? cause.message : String(cause)).slice(
0,
512,
),
};
};
/** Parse only a plausible terminal root-map candidate; never establish absence. */
export const inspectSolidityMetadata = (
bytes: Uint8Array,
@@ -148,8 +196,7 @@ export const inspectSolidityMetadata = (
...decodeOptions,
tokenizer: new BoundedMetadataTokenizer(selected),
});
if (!(raw instanceof Map))
throw new Error("Terminal CBOR candidate did not decode to a root map.");
if (!(raw instanceof Map)) throw new EvmAdmissionError("root_not_map");
validateMapKeys(raw);
// cborg compares Map keys by identity, so repeated byte-string or container
// keys (each its own object) pass its duplicate check; compare by value at
@@ -185,10 +232,7 @@ export const inspectSolidityMetadata = (
status: "not-decoded",
entries: [],
compiler_version: null,
diagnostic: (cause instanceof Error
? cause.message
: String(cause)
).slice(0, 512),
...solidityFailure(cause),
};
}
};