fix(metadata): preserve canonical PowerPC subject identity

This commit is contained in:
morluto
2026-10-24 04:05:17 +08:00
parent 22d3236c31
commit e909a927c4
10 changed files with 82 additions and 40 deletions
+5
View File
@@ -200,3 +200,8 @@ those questions require a disassembler.
`npm run verify:go:binary` exercises compiler-built fixtures through the public
CLI and MCP, comparing their metadata with Go's standard `debug/buildinfo`
reader. The verification lane requires Go; normal inspection does not.
Reader architecture names remain verbatim in the report. Evidence subjects use
the recognized canonical family: `386` maps to `x86`, `amd64` to `x86_64`, and
`ppc` to `powerpc`. Unknown families such as `ppc64` remain unknown rather than
being inferred from a related spelling.
+5
View File
@@ -165,3 +165,8 @@ Package and dependency counts are checked against the minimum required JSON
representation before graph allocation or schema cloning. Oversized graphs
report the output byte budget; the compiler-path capacity reports
`maximum_rustc_hashes` in hashes, independently of byte limits.
Reader architecture names remain verbatim in the report. Evidence subjects use
the recognized canonical family: `386` maps to `x86`, `amd64` to `x86_64`, and
`ppc` to `powerpc`. Unknown families such as `ppc64` remain unknown rather than
being inferred from a related spelling.
+2 -2
View File
@@ -1,3 +1,4 @@
import { binaryMetadataArchitecture } from "../../domain/binaryMetadataArchitecture.js";
import { metadataReportSubjectFormat } from "../../domain/apple/machoTargetSelection.js";
import type { ExecutionOptions } from "../AnalysisProvider.js";
import type { GoBinaryPort } from "./GoBinaryPort.js";
@@ -8,7 +9,6 @@ import {
} from "../../domain/analysisErrorCore.js";
import { createEvidence, type Evidence } from "../../domain/evidence.js";
import {
goArchitectureForSubject,
goBinarySchema,
inspectGoBinaryInputSchema,
} from "../../domain/go/goBinary.js";
@@ -68,7 +68,7 @@ export class GoBinaryService {
),
);
const value = report.data;
const architecture = goArchitectureForSubject(value.architecture);
const architecture = binaryMetadataArchitecture(value.architecture);
const completed = completeProviderEvidence(
createEvidence(
{
+2 -2
View File
@@ -1,3 +1,4 @@
import { binaryMetadataArchitecture } from "../../domain/binaryMetadataArchitecture.js";
import { metadataReportSubjectFormat } from "../../domain/apple/machoTargetSelection.js";
import type { ExecutionOptions } from "../AnalysisProvider.js";
import type { RustBinaryPort } from "./RustBinaryPort.js";
@@ -8,7 +9,6 @@ import {
} from "../../domain/analysisErrorCore.js";
import { createEvidence, type Evidence } from "../../domain/evidence.js";
import {
rustArchitectureForSubject,
rustBinarySchema,
type InspectRustBinaryInput,
} from "../../domain/rust/rustBinary.js";
@@ -87,7 +87,7 @@ const evidenceFor = (
parameters: InspectRustBinaryInput,
value: ReturnType<typeof rustBinarySchema.parse>,
): Evidence => {
const architecture = rustArchitectureForSubject(value.architecture);
const architecture = binaryMetadataArchitecture(value.architecture);
const section = value.auditable_metadata;
return createEvidence(
{
+2 -2
View File
@@ -1,8 +1,8 @@
import { binaryMetadataArchitecture } from "../binaryMetadataArchitecture.js";
import { metadataReportSubjectFormat } from "../apple/machoTargetSelection.js";
import { z } from "zod";
import type { AnalysisError } from "../analysisErrorBase.js";
import {
goArchitectureForSubject,
goBinarySchema,
goBuildInfoSchema,
goModuleMetadataSchema,
@@ -149,7 +149,7 @@ export const projectGoBinaryView = (
parent.subject.digest.sha256 !== report.artifact.sha256 ||
parent.subject.format !== metadataReportSubjectFormat(report) ||
parent.subject.architecture !==
(goArchitectureForSubject(report.architecture) ?? null)
(binaryMetadataArchitecture(report.architecture) ?? null)
)
return err(
analysisViewInputError([
+2 -2
View File
@@ -1,8 +1,8 @@
import { binaryMetadataArchitecture } from "../binaryMetadataArchitecture.js";
import { metadataReportSubjectFormat } from "../apple/machoTargetSelection.js";
import { z } from "zod";
import type { AnalysisError } from "../analysisErrorBase.js";
import {
rustArchitectureForSubject,
rustBinarySchema,
rustAuditableMetadataSchema,
rustcCommitSchema,
@@ -159,7 +159,7 @@ export const projectRustBinaryView = (
subject.digest.sha256 !== report.artifact.sha256 ||
subject.format !== metadataReportSubjectFormat(report) ||
subject.architecture !==
(rustArchitectureForSubject(report.architecture) ?? null)
(binaryMetadataArchitecture(report.architecture) ?? null)
)
return err(
analysisViewInputError([
+17
View File
@@ -0,0 +1,17 @@
import { BINARY_ARCHITECTURES } from "./binaryTargetTypes.js";
/** Preserve reader spellings while retaining recognized Evidence CPU families. */
export const binaryMetadataArchitecture = (architecture: string) => {
switch (architecture) {
case "386":
return "x86";
case "amd64":
return "x86_64";
case "ppc":
return "powerpc";
default:
return BINARY_ARCHITECTURES.find(
(supported) => supported === architecture,
);
}
};
+2 -16
View File
@@ -1,5 +1,5 @@
import { z } from "zod";
import { BINARY_ARCHITECTURES } from "../binaryTargetTypes.js";
import { binaryMetadataArchitecture } from "../binaryMetadataArchitecture.js";
import {
machoSelectionContainsRange,
selectedSliceAgreesWithReport,
@@ -13,20 +13,6 @@ import {
} from "../localPath.js";
import { canonicalBase64Schema } from "../stringPatterns.js";
/** Map reader architecture names into Evidence identity; unknown names stay unknown. */
export const goArchitectureForSubject = (architecture: string) => {
switch (architecture) {
case "386":
return "x86";
case "amd64":
return "x86_64";
default:
return BINARY_ARCHITECTURES.find(
(supported) => supported === architecture,
);
}
};
const sourceRangeSchema = z.strictObject({
offset: z.int().nonnegative(),
bytes: z.int().nonnegative(),
@@ -105,7 +91,7 @@ export const goBinarySchema = z
(!selectedSliceAgreesWithReport(
slice,
report.format,
goArchitectureForSubject(report.architecture),
binaryMetadataArchitecture(report.architecture),
) ||
!machoSelectionContainsRange(
slice,
+2 -16
View File
@@ -4,7 +4,7 @@ import {
absoluteLocalPathStringSchema,
localPathStringSchema,
} from "../localPath.js";
import { BINARY_ARCHITECTURES } from "../binaryTargetTypes.js";
import { binaryMetadataArchitecture } from "../binaryMetadataArchitecture.js";
import {
machoSelectionContainsRange,
selectedSliceAgreesWithReport,
@@ -12,20 +12,6 @@ import {
machoTargetSelectionSchema,
} from "../apple/machoTargetSelection.js";
/** Preserve the format owner's architecture spelling separately from Evidence vocabulary. */
export const rustArchitectureForSubject = (architecture: string) => {
switch (architecture) {
case "386":
return "x86";
case "amd64":
return "x86_64";
default:
return BINARY_ARCHITECTURES.find(
(supported) => supported === architecture,
);
}
};
/** Known revisions used to interpret compatible cargo-auditable documents. */
export const RUST_AUDITABLE_FORMAT_VERSIONS = [0, 1, 8] as const;
@@ -331,7 +317,7 @@ const checkSlice = (report: RustBinary, context: z.RefinementCtx): void => {
!selectedSliceAgreesWithReport(
slice,
report.format,
rustArchitectureForSubject(report.architecture),
binaryMetadataArchitecture(report.architecture),
) ||
ranges.some(
({ offset, bytes }) =>
@@ -0,0 +1,43 @@
import { createHash } from "node:crypto";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { GoBinaryService } from "../../src/application/go/GoBinaryService.js";
import { RustBinaryService } from "../../src/application/rust/RustBinaryService.js";
import { elf } from "../../src/domain/binaryTarget.fixture.js";
import { GoBinaryProvider } from "../../src/go/GoBinaryProvider.js";
import { RustBinaryProvider } from "../../src/rust/RustBinaryProvider.js";
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
it.each([
[1, 1, 20, "ppc", "powerpc"],
[1, 2, 20, "ppc", "powerpc"],
[1, 1, 3, "386", "x86"],
[2, 1, 62, "amd64", "x86_64"],
[2, 2, 21, "ppc64", null],
] as const)(
"preserves ELF class%d byte-order%d machine%d in Go and Rust subjects",
async (bits, endian, machine, local, canonical) => {
const root = await createTestTempDirectory("rea-metadata-subject-");
const path = join(root, "header.elf");
const bytes = elf(bits, endian, machine);
await writeFile(path, bytes);
for (const service of [
new GoBinaryService(new GoBinaryProvider()),
new RustBinaryService(new RustBinaryProvider()),
]) {
const result = await service.inspect({ path });
expect(result.ok).toBe(true);
if (!result.ok) throw result.error;
expect(result.value.subject).toMatchObject({
architecture: canonical,
digest: { sha256: createHash("sha256").update(bytes).digest("hex") },
});
expect(result.value.normalized_result).toMatchObject({
architecture: local,
bits: bits === 1 ? 32 : 64,
byte_order: endian === 1 ? "little" : "big",
});
}
},
);