fix(providers): preserve report bytes and dictionary facts

This commit is contained in:
morluto
2026-10-26 22:01:51 +08:00
parent 4557791a2e
commit cd68181c90
10 changed files with 225 additions and 26 deletions
+59 -2
View File
@@ -1,10 +1,14 @@
import { truncate, writeFile } from "node:fs/promises";
import { readFile, truncate, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
import { createTestTempDirectory } from "../../../tests/fixtures/temporaryDirectory.js";
import { parseWakaruReports } from "./WakaruReport.js";
import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
import {
parseWakaruReports,
validateRecoverySourceMap,
} from "./WakaruReport.js";
import { RECOVERY_LIMITS } from "./WakaruRelease.js";
const reportStdout = JSON.stringify({
@@ -73,3 +77,56 @@ it("keeps a malformed provenance report distinct from the fixed report allowance
expect(JSON.stringify(projected)).not.toContain("report allowance");
expect(JSON.stringify(projected)).not.toContain("no runtime override");
});
it("rejects malformed UTF-8 provenance bytes and retains literal replacement characters", async () => {
const root = await createTestTempDirectory("rea-wakaru-provenance-utf8-");
const provenancePath = join(root, "provenance.json");
const prefix = Buffer.from('{"format":"');
const suffix = Buffer.from('","strategy":"safe","modules":{}}');
await writeFile(
provenancePath,
Buffer.concat([prefix, Buffer.from([0xff]), suffix]),
);
const context = {
execution,
provenancePath,
snapshots: new Map<string, Buffer>(),
files: [{ relativePath: "provenance.json" }],
};
const refusal: unknown = await parseWakaruReports(context).catch(
(cause: unknown) => cause,
);
expect(refusal).toBeInstanceOf(ProviderAdapterError);
if (!(refusal instanceof ProviderAdapterError))
throw new Error("Expected provider wrapper");
expect(refusal.cause).toBeInstanceOf(TypeError);
await writeFile(
provenancePath,
Buffer.concat([prefix, Buffer.from("�"), suffix]),
);
const accepted = await parseWakaruReports(context);
expect(accepted.provenance.format).toBe("�");
});
it("rejects malformed UTF-8 emitted source maps without rejecting literal replacement characters", async () => {
const root = await createTestTempDirectory("rea-wakaru-sourcemap-utf8-");
const mapPath = join(root, "module.js.map");
const prefix = Buffer.from('{"version":3,"sources":["');
const suffix = Buffer.from('"],"names":[],"mappings":""}');
await writeFile(
mapPath,
Buffer.concat([prefix, Buffer.from([0xff]), suffix]),
);
let refusal: unknown;
try {
validateRecoverySourceMap(await readFile(mapPath), "module.js");
} catch (cause: unknown) {
refusal = cause;
}
expect(refusal).toBeInstanceOf(AnalysisOutputError);
if (!(refusal instanceof AnalysisOutputError))
throw new Error("Expected source-map wrapper");
expect(refusal.cause).toBeInstanceOf(TypeError);
await writeFile(mapPath, Buffer.concat([prefix, Buffer.from("�"), suffix]));
validateRecoverySourceMap(await readFile(mapPath), "module.js");
});
+8 -7
View File
@@ -1,4 +1,5 @@
import { z } from "zod";
import { safeParseUtf8Json } from "../../domain/safeJson.js";
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
import type { ArtifactResourceScope } from "../../artifacts/ArtifactResourceScope.js";
import {
@@ -78,13 +79,11 @@ export const parseWakaruReports = async (context: ReportContext) => {
"Wakaru did not complete recovery (expected exit 0 or a reported partial exit 1)",
);
const report = reportSchema.parse(JSON.parse(execution.stdout));
const provenance = provenanceSchema.parse(
JSON.parse(
(
await readWakaruProvenanceReport(provenancePath, signal, resources)
).toString("utf8"),
),
const decoded = safeParseUtf8Json(
await readWakaruProvenanceReport(provenancePath, signal, resources),
);
if (!decoded.ok) throw decoded.cause;
const provenance = provenanceSchema.parse(decoded.value);
if (report.total !== report.modules.length || report.failed > report.total)
throw new TypeError("Reported module totals disagree");
if (
@@ -249,7 +248,9 @@ export const validateRecoverySourceMap = (
mappings: z.string(),
});
try {
schema.parse(JSON.parse(bytes.toString("utf8")));
const decoded = safeParseUtf8Json(bytes);
if (!decoded.ok) throw decoded.cause;
schema.parse(decoded.value);
} catch (cause: unknown) {
throw new AnalysisOutputError(
OPERATION,