mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 19:23:24 +00:00
fix(managed): report comparison and native-verification coverage unknown when an input cannot tell (#11257)
compare_managed_members reported partial when a side's member inspection was unavailable or when selected-scope agreement could not be established, and verify_managed_native_boundaries reported partial when the boundary inspection was unavailable. Both now fold their inputs with joinCoverageStatus, so unknown wins beside a known gap. The status enums gain unknown (additive).
This commit is contained in:
1 parent
7aea9843bc
commit
067f4ff98b
8 files changed
+147
-27
No files matched your search
@@ -388,7 +388,11 @@ name can select different nested owners. Such disjoint selected owners cannot
|
||||
establish a member pair or added/removed status even when both inventories are
|
||||
complete. Missing nominal identities cannot establish selected membership.
|
||||
Incomplete selected inventories or missing identities leave aggregate agreement
|
||||
unknown and coverage partial; known observed member pairs remain available.
|
||||
unknown, and the comparison's `coverage.status` is then `unknown`; known
|
||||
observed member pairs remain available. A side whose member inspection is
|
||||
`unavailable` also makes the comparison `unknown`, even beside a known gap on
|
||||
the other side. `partial` is reserved for known gaps such as a different
|
||||
selected scope or an unequal method-row page.
|
||||
Complete empty selected inventories agree even if their unmatched request names
|
||||
differ. Whole-assembly and selected
|
||||
inventories remain distinct scopes, and each source retains its exact request.
|
||||
@@ -686,7 +690,9 @@ Windows name decorations are static inferred candidates;
|
||||
exact spelling still suppresses decoration and no runtime binding is proven.
|
||||
The mapping follows [Microsoft's charset guidance](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/charset).
|
||||
Partial input coverage records a residual question even when no unresolved
|
||||
individual boundary was produced. Selected literal inventories likewise remain
|
||||
individual boundary was produced. An unavailable boundary inspection read no
|
||||
P/Invoke declarations, so the verification's `coverage.status` is `unknown`,
|
||||
even beside non-exhaustive native exports. Selected literal inventories likewise remain
|
||||
partial when unknown MethodDef ownership can hide a selected body.
|
||||
|
||||
Metadata tokens are not native addresses. Native body mapping for C++/CLI,
|
||||
|
||||
@@ -214,7 +214,11 @@ export const managedMemberComparisonResultSchema = z.strictObject({
|
||||
methods: z.array(methodComparisonItemSchema),
|
||||
fields: z.array(fieldComparisonItemSchema),
|
||||
coverage: z.strictObject({
|
||||
status: z.enum(["complete-within-inputs", "partial"]),
|
||||
status: z
|
||||
.enum(["complete-within-inputs", "partial", "unknown"])
|
||||
.describe(
|
||||
"complete-within-inputs only when both observations are complete over the same selected scope and method rows; unknown when a side is unavailable or the selected scope agreement cannot be established.",
|
||||
),
|
||||
left_status: z.enum(["complete", "partial", "unavailable"]),
|
||||
right_status: z.enum(["complete", "partial", "unavailable"]),
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { joinCoverageStatus, type CoverageStatus } from "../coverage.js";
|
||||
import type {
|
||||
ManagedMemberComparisonResult,
|
||||
ManagedMemberComparisonSide,
|
||||
@@ -17,23 +18,34 @@ export const buildComparisonCoverage = ({
|
||||
right,
|
||||
selectionAgreement,
|
||||
}: ComparisonCoverageInput): ManagedMemberComparisonResult["coverage"] => {
|
||||
const leftStatus = sideCoverageStatus(left);
|
||||
const rightStatus = sideCoverageStatus(right);
|
||||
const unknownInput =
|
||||
left.coverage.state !== "complete" ||
|
||||
right.coverage.state !== "complete" ||
|
||||
selectionAgreement !== "same" ||
|
||||
!sameMethodRows(left, right);
|
||||
// An unavailable side examined nothing, and an unknown selection agreement
|
||||
// cannot tell whether both sides cover the same types: either makes the
|
||||
// comparison unknown, even beside a known gap.
|
||||
const joined = joinCoverageStatus([
|
||||
sideCoverageKnowledge(left),
|
||||
sideCoverageKnowledge(right),
|
||||
selectionAgreement === "unknown"
|
||||
? "unknown"
|
||||
: selectionAgreement === "different" || !sameMethodRows(left, right)
|
||||
? "partial"
|
||||
: "complete",
|
||||
]);
|
||||
return {
|
||||
status:
|
||||
!unknownInput && leftStatus === "complete" && rightStatus === "complete"
|
||||
? "complete-within-inputs"
|
||||
: "partial",
|
||||
left_status: leftStatus,
|
||||
right_status: rightStatus,
|
||||
status: joined === "complete" ? "complete-within-inputs" : joined,
|
||||
left_status: sideCoverageStatus(left),
|
||||
right_status: sideCoverageStatus(right),
|
||||
};
|
||||
};
|
||||
|
||||
const sideCoverageKnowledge = (
|
||||
result: ManagedMemberInspection,
|
||||
): CoverageStatus =>
|
||||
result.coverage.state === "unavailable"
|
||||
? "unknown"
|
||||
: result.coverage.state === "complete"
|
||||
? "complete"
|
||||
: "partial";
|
||||
|
||||
/** Full MethodDef coverage is required before inferring cross-build absence. */
|
||||
export const hasCompleteMethodRows = (
|
||||
result: ManagedMemberInspection,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { joinCoverageStatus } from "../coverage.js";
|
||||
import type { Evidence } from "../evidence.js";
|
||||
import { machoRuntimeSymbolName } from "../native/machoSymbolName.js";
|
||||
import type {
|
||||
@@ -566,13 +567,19 @@ export const buildVerificationResult = ({
|
||||
({ boundary_kind: kind }) =>
|
||||
kind !== "pinvoke" && kind !== "runtime-provided",
|
||||
).length;
|
||||
const coverage: ManagedNativeVerificationResult["coverage"] = {
|
||||
status:
|
||||
managed.coverage.state === "complete" &&
|
||||
native.unsupported === 0 &&
|
||||
!native.partialExports
|
||||
? "complete-within-inputs"
|
||||
// An unavailable boundary inspection read no P/Invoke declarations, so the
|
||||
// verification cannot tell what it would cover: unknown wins beside the
|
||||
// known gaps of skipped native Evidence or non-exhaustive exports.
|
||||
const status = joinCoverageStatus([
|
||||
managed.coverage.state === "unavailable"
|
||||
? "unknown"
|
||||
: managed.coverage.state === "complete"
|
||||
? "complete"
|
||||
: "partial",
|
||||
native.unsupported === 0 && !native.partialExports ? "complete" : "partial",
|
||||
]);
|
||||
const coverage: ManagedNativeVerificationResult["coverage"] = {
|
||||
status: status === "complete" ? "complete-within-inputs" : status,
|
||||
};
|
||||
return {
|
||||
algorithm: {
|
||||
|
||||
@@ -143,7 +143,11 @@ export const managedNativeVerificationRequestSchema = z
|
||||
.superRefine(distinctNativeSources);
|
||||
|
||||
const verificationCoverageSchema = z.strictObject({
|
||||
status: z.enum(["complete-within-inputs", "partial"]),
|
||||
status: z
|
||||
.enum(["complete-within-inputs", "partial", "unknown"])
|
||||
.describe(
|
||||
"unknown when the managed boundary inspection is unavailable; partial for a partial inspection, unsupported native Evidence or non-exhaustive exports.",
|
||||
),
|
||||
});
|
||||
|
||||
/** Provider-neutral managed/native verification result. */
|
||||
|
||||
@@ -138,7 +138,9 @@ cliTest(
|
||||
const report = managedMemberComparisonResultSchema.parse(
|
||||
parseEvidence(compared.structuredContent).normalized_result,
|
||||
);
|
||||
expect(report.coverage.status).toBe("partial");
|
||||
// The delta's baseline-dependent selection cannot establish its scope, so
|
||||
// the comparison's coverage is unknown rather than a known gap.
|
||||
expect(report.coverage.status).toBe("unknown");
|
||||
expect(report.summary).toMatchObject({ added: 0, removed: 0, unknown: 2 });
|
||||
expect(report.evidence_links).toEqual(
|
||||
sources.map(({ evidence_id }) => evidence_id),
|
||||
|
||||
@@ -93,7 +93,9 @@ describe("managed comparison selected nominal membership", () => {
|
||||
...source,
|
||||
result: partial,
|
||||
});
|
||||
expect(comparison.coverage.status).toBe("partial");
|
||||
// The partial side cannot establish its selected scope, so scope
|
||||
// agreement and the comparison's coverage are unknown.
|
||||
expect(comparison.coverage.status).toBe("unknown");
|
||||
expect(comparison.summary).toEqual({
|
||||
added: 0,
|
||||
removed: 0,
|
||||
@@ -135,7 +137,7 @@ describe("managed comparison selected nominal membership", () => {
|
||||
unchanged: 0,
|
||||
});
|
||||
expect(comparison.matching.unmatched).toBe(4);
|
||||
expect(comparison.coverage.status).toBe("partial");
|
||||
expect(comparison.coverage.status).toBe("unknown");
|
||||
},
|
||||
);
|
||||
|
||||
@@ -255,7 +257,9 @@ describe("managed comparison selected scope agreement", () => {
|
||||
...source,
|
||||
result: limited,
|
||||
});
|
||||
expect(comparison.coverage.status).toBe("partial");
|
||||
// Scope agreement that cannot be established makes the comparison
|
||||
// unknown, not a known partial gap.
|
||||
expect(comparison.coverage.status).toBe("unknown");
|
||||
expect(comparison.limitations).toContainEqual(
|
||||
expect.stringContaining(
|
||||
"Selected declaring-type scope agreement is unknown",
|
||||
@@ -267,6 +271,27 @@ describe("managed comparison selected scope agreement", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it("reports a whole-assembly comparison unknown when one side examined no metadata", () => {
|
||||
const source = inspect(buildManagedPeFixture(), "/fixture/whole.dll");
|
||||
const unavailable = structuredClone(source.result);
|
||||
unavailable.metadata.status = "malformed";
|
||||
unavailable.coverage.state = "unavailable";
|
||||
unavailable.types = [];
|
||||
unavailable.methods = [];
|
||||
unavailable.fields = [];
|
||||
unavailable.method_rows = null;
|
||||
const sides = [source, { ...source, result: unavailable }] as const;
|
||||
// Both selections are whole-assembly, so only the unavailable side, not
|
||||
// scope agreement, makes the result unknown; it wins beside the partial
|
||||
// method-row inventory, and in either order.
|
||||
for (const [left, right] of [sides, [sides[1], sides[0]]] as const)
|
||||
expect(compareManagedMembers(left, right).coverage).toEqual({
|
||||
status: "unknown",
|
||||
left_status: left === source ? "complete" : "unavailable",
|
||||
right_status: right === source ? "complete" : "unavailable",
|
||||
});
|
||||
});
|
||||
|
||||
it("recognizes complete empty selected scopes despite different unmatched requests", () => {
|
||||
const bytes = buildManagedPeFixture();
|
||||
const left = inspect(bytes, "/fixture/empty-left.dll", ["Absent.Left"]);
|
||||
|
||||
@@ -912,6 +912,66 @@ it("retains every observed native candidate", () => {
|
||||
expect(result.pinvoke_imports[0]?.candidates).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("reports coverage unknown when the managed boundary inspection is unavailable", () => {
|
||||
const withManagedCoverage = (
|
||||
state: "partial" | "unavailable",
|
||||
nativeObservations: ReturnType<typeof nativeEvidenceWithExports>[],
|
||||
) => {
|
||||
const input = exampleInput();
|
||||
const managed = input.managed_boundaries;
|
||||
const normalized = managedNativeBoundaryInspectionSchema.parse(
|
||||
managed.normalized_result,
|
||||
);
|
||||
return verifyManagedNativeVerificationSource(
|
||||
readFixtureSource({
|
||||
...input,
|
||||
managed_boundaries: createEvidence(undefined, managed.provider, {
|
||||
operation: managed.operation,
|
||||
parameters: managed.parameters,
|
||||
result: {
|
||||
...normalized,
|
||||
...(state === "unavailable"
|
||||
? { module_refs: [], pinvoke_imports: [] }
|
||||
: {}),
|
||||
coverage: { ...normalized.coverage, state },
|
||||
},
|
||||
rawResult: null,
|
||||
}),
|
||||
native_observations: nativeObservations,
|
||||
}),
|
||||
);
|
||||
};
|
||||
const exhaustive = nativeEvidenceWithExports(["open_native"]);
|
||||
const macho = inspectMachoSchema.parse(exhaustive.normalized_result);
|
||||
const partialExports = createEvidence(
|
||||
exhaustive.subject === null
|
||||
? undefined
|
||||
: {
|
||||
path: exhaustive.subject.local_path,
|
||||
sha256: exhaustive.subject.digest.sha256,
|
||||
format: exhaustive.subject.format,
|
||||
},
|
||||
exhaustive.provider,
|
||||
{
|
||||
operation: exhaustive.operation,
|
||||
parameters: exhaustive.parameters,
|
||||
result: { ...macho, exports: { ...macho.exports, exhaustive: false } },
|
||||
rawResult: null,
|
||||
},
|
||||
);
|
||||
|
||||
// Nothing was read on the managed side, so the result is unknown, and
|
||||
// unknown wins beside the known gap of non-exhaustive native exports.
|
||||
for (const native of [exhaustive, partialExports])
|
||||
expect(withManagedCoverage("unavailable", [native]).coverage).toEqual({
|
||||
status: "unknown",
|
||||
});
|
||||
// A partial inspection is a known gap.
|
||||
expect(withManagedCoverage("partial", [exhaustive]).coverage).toEqual({
|
||||
status: "partial",
|
||||
});
|
||||
});
|
||||
|
||||
describe("managed/native verification result algebra", () => {
|
||||
it("rejects states without their required native match", () => {
|
||||
const result = verifyManagedNativeVerificationSource(
|
||||
|
||||
Reference in new issue
Block a user