fix(rust): retain compatible cargo-auditable revisions

Admit documented compatible format revisions, preserve the original uint32 revision and disclose unverified fallback for unknown revisions. Keep malformed package and compressed-input rejection.

Co-authored-by: Dragolone <810170966qq@gmail.com>
This commit is contained in:
morlutoandDragolone committed 2026-10-14 14:47:26 +08:00
1 parent 66536d0d2e
commit 36c91428fc
6 files changed
+108 -20

No files matched your search

+5
View File
@@ -55,6 +55,11 @@ dependency sections are typed failures. A cycle is reported and is not walked.
A missing section returns success with `auditable_metadata: null` and a
limitation. Absence of `.dep-v0` does not mean the binary has no dependencies.
Unknown embedded format revisions are retained in `format_version` and
interpreted using the highest known preceding revision, following the
cargo-auditable schema. The output includes a limitation that identifies this
fallback and notes that the particular revision has not been independently
verified. An omitted revision remains `null` and follows revision 0 semantics.
Embedded names and versions are file content. They do not authenticate the
packages or the compiler.
+4 -2
View File
@@ -2,7 +2,8 @@ import { z } from "zod";
import { digestSchema } from "../digests.js";
import { localPathStringSchema } from "../localPath.js";
export const RUST_AUDITABLE_FORMAT_VERSION = 1;
/** Known revisions used to interpret compatible cargo-auditable documents. */
export const RUST_AUDITABLE_FORMAT_VERSIONS = [0, 1, 8] as const;
const index = z.number().int().nonnegative();
const rustcHash = z.string().regex(/^[0-9a-f]{40}$/u);
@@ -29,7 +30,8 @@ export const rustAuditableMetadataSchema = z.strictObject({
section_bytes: z.number().int().positive(),
compressed_bytes: z.number().int().positive(),
uncompressed_bytes: z.number().int().positive(),
format_version: z.literal(RUST_AUDITABLE_FORMAT_VERSION).nullable(),
/** Original embedded revision; null means the source omitted the field. */
format_version: z.number().int().nonnegative().max(0xffff_ffff).nullable(),
packages: z.array(rustPackageSchema),
root_package_index: index.nullable(),
dependency_edges: z.array(dependencyEdgeSchema),
+47 -6
View File
@@ -1,5 +1,6 @@
import { deflateSync } from "node:zlib";
import { expect, it } from "vitest";
import { rustAuditableMetadataSchema } from "../domain/rust/rustBinary.js";
import {
RUST_SECTION_BYTES,
readAuditableSection,
@@ -153,14 +154,54 @@ it("stops an inflate that exceeds the section ratio", () => {
).toThrow(/1 MiB/);
});
it.each([{ packages: [] }, { packages: [{ future_package_field: true }] }])(
"rejects an unknown format before interpreting its packages: %j",
({ packages }) => {
expect(() => section({ format: 99, packages })).toThrow(
it.each([0, 8])("reads documented cargo-auditable revision %i", (format) => {
const parsed = section({
format,
packages: [
{ name: "sample", version: "0.1.0", source: "local", root: true },
],
});
expect(parsed).toMatchObject({
format_version: format,
root_package_index: 0,
});
expect(rustAuditableMetadataSchema.parse(parsed)).toEqual(parsed);
});
it.each([{ source: 4 }, { source: 9 }, { source: 99 }])(
"preserves unknown revision $source for the documented preceding-revision interpretation",
({ source }) => {
const parsed = section({
format: source,
packages: [
{ name: "sample", version: "0.1.0", source: "local", root: true },
],
});
expect(parsed).toMatchObject({
format_version: source,
});
expect(rustAuditableMetadataSchema.parse(parsed)).toEqual(parsed);
},
);
it("rejects unknown package fields even for a forward-compatible revision", () => {
expect(() =>
section({
format: 99,
packages: [
{ name: "sample", version: "0.1.0", source: "local", future: true },
],
}),
).toThrow(/package schema/);
});
it.each([-1, 1.5, 0x1_0000_0000])(
"rejects invalid cargo-auditable format value %s",
(format) => {
expect(() => section({ format, packages: [] })).toThrow(
expect.objectContaining({
name: "RustAuditableFormatFailure",
kind: "unsupported",
message: "Unsupported cargo-auditable format version 99",
kind: "malformed",
}),
);
},
+1 -11
View File
@@ -1,7 +1,6 @@
import { deflateSync, inflateSync } from "node:zlib";
import { z } from "zod";
import {
RUST_AUDITABLE_FORMAT_VERSION,
cyclicDependencyIndices,
type RustAuditableMetadata,
type RustPackage,
@@ -28,11 +27,10 @@ const packageSchema = z.strictObject({
root: z.boolean().optional(),
});
const documentVersionSchema = z.object({
format: z.number().int().nonnegative().optional(),
format: z.number().int().nonnegative().max(0xffff_ffff).optional(),
});
const documentSchema = documentVersionSchema
.extend({
format: z.literal(RUST_AUDITABLE_FORMAT_VERSION).optional(),
packages: z.array(packageSchema),
})
.strict();
@@ -239,14 +237,6 @@ const parseDocument = (bytes: Buffer): z.infer<typeof documentSchema> => {
"Dependency JSON does not match the format schema",
{ cause: version.error },
);
if (
version.data.format !== undefined &&
version.data.format !== RUST_AUDITABLE_FORMAT_VERSION
)
throw new RustAuditableFormatFailure(
"unsupported",
`Unsupported cargo-auditable format version ${version.data.format}`,
);
admitGraphExpansion(value);
// One malformed package must fail before Zod retains an issue for every field.
rejectMalformedPackages(value);
+16
View File
@@ -13,6 +13,7 @@ import {
AnalysisUnsupportedTargetError,
} from "../domain/analysisErrorCore.js";
import {
RUST_AUDITABLE_FORMAT_VERSIONS,
rustBinarySchema,
type InspectRustBinaryInput,
type RustBinary,
@@ -97,6 +98,7 @@ const limitationsFor = (
"No .dep-v0 section was found. Absence of .dep-v0 does not mean the binary has no dependencies.",
]
: []),
...unknownFormatLimitation(image.auditable_metadata?.format_version),
...(image.auditable_metadata?.packages.some((pkg) => pkg.kind === null)
? [
"An omitted package kind is left null. The embedded format treats an omitted kind as runtime, and that default was not stored in the section.",
@@ -111,6 +113,20 @@ const limitationsFor = (
...rustcLimitation(image.rustc_commit),
];
const unknownFormatLimitation = (
format: number | null | undefined,
): string[] => {
if (format === null || format === undefined) return [];
let preceding = 0;
for (const known of RUST_AUDITABLE_FORMAT_VERSIONS) {
if (known === format) return [];
if (known < format) preceding = known;
}
return [
`cargo-auditable format revision ${format} is unknown. Its schema directs readers to interpret it using the highest known preceding revision (${preceding}); this specific revision has not been independently verified.`,
];
};
const rustcLimitation = (
commit: ReturnType<typeof readRustBinaryImage>["rustc_commit"],
): string[] => {
+35 -1
View File
@@ -3,7 +3,10 @@ import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { toolContract } from "../../src/contracts/toolContracts.js";
import { createRustImage } from "../fixtures/rust/image.js";
import {
createRustImage,
rustDependencyPayload,
} from "../fixtures/rust/image.js";
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
import { cliTest } from "../support/cli/cliFixture.js";
@@ -49,6 +52,37 @@ cliTest("returns null metadata when .dep-v0 is absent", async ({ cli }) => {
);
});
cliTest(
"preserves an unknown cargo-auditable revision and reports its fallback",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-rust-cli-unknown-format-");
const path = join(root, "sample");
const payload = rustDependencyPayload({
format: 4,
packages: [
{ name: "sample", version: "0.1.0", source: "local", root: true },
],
});
await writeFile(path, createRustImage({ format: "elf", payload }));
const result = await cli.run({
arguments: ["inspect-rust-binary", path, "--json"],
});
expect(result.exitCode, result.stderr).toBe(0);
const evidence = toolContract("inspect_rust_binary").outputSchema.parse(
result.json,
);
expect(evidence.normalized_result.auditable_metadata?.format_version).toBe(
4,
);
expect(evidence.normalized_result.limitations.join("\n")).toContain(
"highest known preceding revision (1)",
);
expect(evidence.normalized_result.limitations.join("\n")).toContain(
"this specific revision has not been independently verified",
);
},
);
cliTest("reports malformed zlib as invalid input", async ({ cli }) => {
const root = await createTestTempDirectory("rea-rust-cli-zlib-");
const path = join(root, "broken.elf");