Compare commits

...
Author SHA1 Message Date
morluto 3395add88d fix(coverage): validate boundary identifiers at request admission 2026-11-10 21:20:43 -05:00
4 changed files with 72 additions and 14 deletions

No files matched your search

+22 -7
View File
@@ -31,6 +31,9 @@ const evidenceIdSchema = prefixedDigestSchema("ev");
const stableIdSchema = z
.string()
.regex(/^[A-Za-z][A-Za-z0-9._:\x2f\x2d]{0,199}$/u);
export const reconstructionBoundaryIdSchema = stableIdSchema.describe(
"Exact identifier of a boundary declared in the reconstruction coverage.",
);
const boundedTextSchema = z.string().trim().min(1);
const authoritySchema = z.enum([
@@ -158,7 +161,7 @@ const packageProofSchema = z.strictObject({
});
const boundarySchema = z.strictObject({
boundary_id: stableIdSchema,
boundary_id: reconstructionBoundaryIdSchema,
title: z.string().trim().min(1),
required_surface_ids: z.array(stableIdSchema).min(1),
required_claim_ids: z.array(stableIdSchema).min(1),
@@ -204,6 +207,10 @@ export interface ParsedReconstructionCoverage {
string,
ReconstructionCoverageData["claims"][number]
>;
readonly boundaries: ReadonlyMap<
string,
ReconstructionCoverageData["boundaries"][number]
>;
readonly ownersBySurface: ReadonlyMap<
string,
readonly ReconstructionCoverageData["owners"][number][]
@@ -281,7 +288,7 @@ const closureReasonSchema = z.strictObject({
});
export const reconstructionClosureResultSchema = z.strictObject({
boundary_id: stableIdSchema,
boundary_id: reconstructionBoundaryIdSchema,
status: z.enum(["partial", "failed", "unknown", "ready"]),
summary: z.strictObject({
required_surfaces: z.int().min(0),
@@ -311,9 +318,7 @@ export const evaluateParsedReconstructionClosure = (
boundaryId: string,
nowEpochMs: number,
): Result<ReconstructionClosureResult, AnalysisInputError> => {
const boundary = parsed.coverage.boundaries.find(
({ boundary_id: id }) => id === boundaryId,
);
const boundary = parsed.boundaries.get(boundaryId);
if (boundary === undefined)
return err(
coverageInputError(
@@ -388,7 +393,10 @@ const validateCoverageReferences = (
...path,
"package_proofs",
]);
indexUnique(workspace.boundaries, "boundary_id", [...path, "boundaries"]);
const boundaries = indexUnique(workspace.boundaries, "boundary_id", [
...path,
"boundaries",
]);
for (const [index, contract] of workspace.verifier_contracts.entries()) {
const { contract_sha256: contractSha256, ...base } = contract;
if (
@@ -464,7 +472,14 @@ const validateCoverageReferences = (
["residual_unknown_ids", index],
`Coverage workspace residual unknown is missing: ${id}`,
);
return { evidenceById, artifacts, surfaces, claims, ownersBySurface };
return {
evidenceById,
artifacts,
surfaces,
claims,
boundaries,
ownersBySurface,
};
};
const indexUnique = <Item extends Record<Key, string>, Key extends keyof Item>(
+5 -2
View File
@@ -1,8 +1,11 @@
import { z } from "zod";
import { reconstructionCoverageDataSchema } from "./reconstructionCoverage.js";
import {
reconstructionBoundaryIdSchema,
reconstructionCoverageDataSchema,
} from "./reconstructionCoverage.js";
/** Named-boundary input for inline reconstruction coverage evaluation. */
export const reconstructionCoverageEvaluationInputSchema = z.strictObject({
coverage: reconstructionCoverageDataSchema,
boundary_id: z.string().min(1),
boundary_id: reconstructionBoundaryIdSchema,
});
@@ -20,19 +20,38 @@ const currentCoverage = () => {
};
};
const evaluate = async (cli: TestCli, coverage: unknown) => {
const evaluate = async (
cli: TestCli,
coverage: unknown,
boundaryId = "replacement.cli",
) => {
const root = await createTestTempDirectory("rea-coverage-cli-");
const input = join(root, "request.json");
await writeFile(
input,
JSON.stringify({ coverage, boundary_id: "replacement.cli" }),
);
await writeFile(input, JSON.stringify({ coverage, boundary_id: boundaryId }));
return cli.run({
arguments: ["evaluate-reconstruction-coverage", input, "--json"],
cwd: root,
});
};
cliTest(
"distinguishes malformed boundary IDs from unknown IDs",
async ({ cli }) => {
const coverage = currentCoverage();
for (const [boundaryId, reason] of [
["replacement cli", "invalid_format"],
["replacement.other", "invalid_value"],
] as const) {
const result = await evaluate(cli, coverage, boundaryId);
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
details: { issues: [{ path: ["boundary_id"], reason }] },
});
}
},
);
cliTest(
"evaluates complete inline coverage through the compiled CLI",
async ({ cli }) => {
@@ -19,6 +19,27 @@ import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { createServer } from "../../../src/server/createServer.js";
import { observed } from "../../fixtures/analysisExecution.js";
it("rejects malformed boundary identifiers at the MCP request boundary", async () => {
await withCoverageMcp(async ({ client }) => {
const result = await client.callTool({
name: "evaluate_reconstruction_coverage",
arguments: {
coverage: currentFixtureCoverage(),
boundary_id: "replacement cli",
},
});
expect(result.isError).toBe(true);
expect(parseMcpToolError(result)).toMatchObject({
error: {
code: "invalid_request",
details: {
issues: [{ path: ["boundary_id"], reason: "invalid_format" }],
},
},
});
});
});
it("uses the latest listed unknown revision when evaluating a boundary", async () => {
await withCoverageMcp(async ({ client }) => {
const base = currentFixtureCoverage();