mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 19:23:24 +00:00
fix(managed): report a managed graph over an unavailable source as unknown (#11280)
This commit is contained in:
1 parent
ac9064b394
commit
c931ba43ab
6 files changed
+128
-34
No files matched your search
@@ -724,6 +724,11 @@ type selection. Limitations name each counted class, and graph coverage is
|
||||
incomplete. A missing `calls` edge is therefore not evidence that a method is
|
||||
never called.
|
||||
|
||||
A supplied inspection whose parser coverage is `unavailable` examined nothing,
|
||||
so the graph cannot tell what it omits: the result's `coverage.status` and the
|
||||
graph coverage are then `unknown`, even beside a known gap in another source.
|
||||
`partial` is reserved for known gaps.
|
||||
|
||||
## Evidence record shape
|
||||
|
||||
Member comparison, application graph projection, and managed/native verification
|
||||
|
||||
@@ -458,7 +458,7 @@
|
||||
"src/domain/javascript/sourceToBundleComparisonSchemas.ts": 1,
|
||||
"src/domain/javascript/staticObservedEndpointSchemas.ts": 1,
|
||||
"src/domain/jeb/jebAnalysis.ts": 1,
|
||||
"src/domain/managed/managedArtifact.ts": 6,
|
||||
"src/domain/managed/managedArtifact.ts": 4,
|
||||
"src/domain/managed/managedMemberComparison.ts": 3,
|
||||
"src/domain/managed/managedNativeVerificationSchemas.ts": 2,
|
||||
"src/domain/managed/managedReconstruction.ts": 1,
|
||||
|
||||
@@ -118,7 +118,11 @@ export const managedApplicationGraphResultSchema = z
|
||||
}),
|
||||
graph: javascriptApplicationGraphSchema,
|
||||
coverage: z.strictObject({
|
||||
status: z.enum(["complete-within-inputs", "partial"]),
|
||||
status: z
|
||||
.enum(["complete-within-inputs", "partial", "unknown"])
|
||||
.describe(
|
||||
"complete-within-inputs when every supplied managed Evidence record is complete and nothing was omitted; unknown when a supplied record is unavailable.",
|
||||
),
|
||||
}),
|
||||
evidence_links: z.array(evidenceIdSchema).min(1).max(3),
|
||||
limitations: z.array(nonEmptyTextSchema),
|
||||
@@ -428,9 +432,14 @@ const projectionLimitations = (
|
||||
"Managed native-boundary Evidence was not supplied; P/Invoke and native-implementation nodes are absent.",
|
||||
]
|
||||
: []),
|
||||
...(omissions.partialInput
|
||||
...(omissions.inputStates.includes("partial")
|
||||
? ["At least one supplied managed Evidence record has partial coverage."]
|
||||
: []),
|
||||
...(omissions.inputStates.includes("unavailable")
|
||||
? [
|
||||
"At least one supplied managed Evidence record is unavailable; what the graph omits is unknown.",
|
||||
]
|
||||
: []),
|
||||
...(omissions.selectedTypes === null
|
||||
? []
|
||||
: [
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { joinCoverageStatus, type CoverageStatus } from "../coverage.js";
|
||||
import type { ApplicationGraphEvidence } from "../javascript/javascriptApplicationEvidenceSchemas.js";
|
||||
import type {
|
||||
ManagedArtifactInspection,
|
||||
ManagedMemberInspection,
|
||||
ManagedNativeBoundaryInspection,
|
||||
import {
|
||||
managedInspectionCoverage,
|
||||
type ManagedArtifactInspection,
|
||||
type ManagedInspectionState,
|
||||
type ManagedMemberInspection,
|
||||
type ManagedNativeBoundaryInspection,
|
||||
} from "./managedArtifact.js";
|
||||
import { managedTokenScopesMatch } from "./managedInspectionEvidence.js";
|
||||
|
||||
type ManagedCoverageState = "complete" | "partial" | "unavailable";
|
||||
|
||||
/** Why a call site did not become a `calls` edge. */
|
||||
export type OmittedCallReason =
|
||||
| "member-ref"
|
||||
@@ -44,9 +45,10 @@ export interface ManagedGraphCoverageSources {
|
||||
readonly boundaries: ManagedNativeBoundaryInspection | null;
|
||||
}
|
||||
|
||||
/** Whether any supplied source has incomplete coverage. */
|
||||
/** What the supplied sources and the projection left out. */
|
||||
export interface ManagedGraphProjectionOmissions {
|
||||
readonly partialInput: boolean;
|
||||
/** Parser states of the supplied sources; absent sources are not listed. */
|
||||
readonly inputStates: readonly ManagedInspectionState[];
|
||||
readonly unscopedTokenRelationships: boolean;
|
||||
/** Declaring types the member Evidence selected; null when it inspected every type. */
|
||||
readonly selectedTypes: readonly string[] | null;
|
||||
@@ -56,9 +58,21 @@ export interface ManagedGraphProjectionOmissions {
|
||||
readonly relations: ManagedRelationOmissions | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coverage the supplied sources allow: an unavailable source examined nothing,
|
||||
* so the graph cannot tell what it omits, even beside a known gap.
|
||||
*/
|
||||
const inputCoverage = (
|
||||
omissions: ManagedGraphProjectionOmissions,
|
||||
): CoverageStatus =>
|
||||
joinCoverageStatus([
|
||||
"complete",
|
||||
...omissions.inputStates.map(managedInspectionCoverage),
|
||||
]);
|
||||
|
||||
/** Omissions whose size is unknown, unlike counted relation sites. */
|
||||
const omitsUncounted = (omissions: ManagedGraphProjectionOmissions): boolean =>
|
||||
omissions.partialInput ||
|
||||
inputCoverage(omissions) !== "complete" ||
|
||||
omissions.unscopedTokenRelationships ||
|
||||
omissions.selectedTypes !== null ||
|
||||
omissions.methodRows !== undefined;
|
||||
@@ -75,7 +89,7 @@ const completeCoverage = (): ApplicationGraphEvidence["coverage"] => ({
|
||||
|
||||
/** Preserve the source parser's coverage state on projected observations. */
|
||||
export const managedSourceCoverage = (
|
||||
state: ManagedCoverageState,
|
||||
state: ManagedInspectionState,
|
||||
): ApplicationGraphEvidence["coverage"] =>
|
||||
state === "complete"
|
||||
? completeCoverage()
|
||||
@@ -103,11 +117,9 @@ export const assessManagedGraphOmissions = (
|
||||
sources.members.identity_scope.requires_mvid,
|
||||
sources.boundaries.identity_scope.requires_mvid,
|
||||
),
|
||||
partialInput: [
|
||||
sources.members?.coverage.state,
|
||||
sources.boundaries?.coverage.state,
|
||||
sources.artifact?.coverage.state,
|
||||
].some((state) => state !== undefined && state !== "complete"),
|
||||
inputStates: [sources.members, sources.boundaries, sources.artifact].flatMap(
|
||||
(source) => (source === null ? [] : [source.coverage.state]),
|
||||
),
|
||||
selectedTypes: sources.members?.selection?.type_names ?? null,
|
||||
methodRows:
|
||||
sources.members === null ||
|
||||
@@ -122,22 +134,27 @@ export const assessManagedGraphOmissions = (
|
||||
export const managedGraphEvidenceCoverage = (
|
||||
omissions: ManagedGraphProjectionOmissions,
|
||||
): ApplicationGraphEvidence["coverage"] =>
|
||||
omitsAny(omissions)
|
||||
? {
|
||||
status: "partial",
|
||||
truncated: false,
|
||||
omitted_count: omitsUncounted(omissions)
|
||||
? null
|
||||
: omittedRelationSites(omissions.relations),
|
||||
limits: [],
|
||||
}
|
||||
: completeCoverage();
|
||||
inputCoverage(omissions) === "unknown"
|
||||
? { status: "unknown", truncated: false, omitted_count: null, limits: [] }
|
||||
: omitsAny(omissions)
|
||||
? {
|
||||
status: "partial",
|
||||
truncated: false,
|
||||
omitted_count: omitsUncounted(omissions)
|
||||
? null
|
||||
: omittedRelationSites(omissions.relations),
|
||||
limits: [],
|
||||
}
|
||||
: completeCoverage();
|
||||
|
||||
/** Report whether all supplied source evidence was complete. */
|
||||
export const managedGraphResultCoverage = (
|
||||
omissions: ManagedGraphProjectionOmissions,
|
||||
) => ({
|
||||
status: omitsAny(omissions)
|
||||
? ("partial" as const)
|
||||
: ("complete-within-inputs" as const),
|
||||
status:
|
||||
inputCoverage(omissions) === "unknown"
|
||||
? ("unknown" as const)
|
||||
: omitsAny(omissions)
|
||||
? ("partial" as const)
|
||||
: ("complete-within-inputs" as const),
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import type { CoverageStatus } from "../coverage.js";
|
||||
import { digestSchema } from "../digests.js";
|
||||
import { managedFieldConstantSchema } from "./managedFieldConstant.js";
|
||||
|
||||
@@ -6,6 +7,28 @@ import { managedFieldConstantSchema } from "./managedFieldConstant.js";
|
||||
export const managedMetadataTokenSchema = z.string().regex(/^0x[0-9a-f]{8}$/u);
|
||||
const offsetSchema = z.int().min(0);
|
||||
|
||||
/**
|
||||
* Parser coverage of one managed inspection. `unavailable` means the parser
|
||||
* examined nothing of that kind, unlike `partial`, which records a known gap.
|
||||
*/
|
||||
export const managedInspectionStateSchema = z.enum([
|
||||
"complete",
|
||||
"partial",
|
||||
"unavailable",
|
||||
]);
|
||||
export type ManagedInspectionState = z.infer<
|
||||
typeof managedInspectionStateSchema
|
||||
>;
|
||||
|
||||
/**
|
||||
* What a result derived from one managed inspection can claim about its
|
||||
* coverage: an unavailable inspection examined nothing, so what it would cover
|
||||
* is unknown rather than partially known.
|
||||
*/
|
||||
export const managedInspectionCoverage = (
|
||||
state: ManagedInspectionState,
|
||||
): CoverageStatus => (state === "unavailable" ? "unknown" : state);
|
||||
|
||||
/** A 16-byte CLI metadata GUID without RFC 4122 version/variant restrictions. */
|
||||
export const cliMetadataGuidSchema = z
|
||||
.string()
|
||||
@@ -510,7 +533,7 @@ export const managedArtifactInspectionSchema = z.object({
|
||||
resources: z.array(managedResourceSchema),
|
||||
attributes: z.array(customAttributeSchema),
|
||||
coverage: z.object({
|
||||
state: z.enum(["complete", "partial", "unavailable"]),
|
||||
state: managedInspectionStateSchema,
|
||||
issues: z.array(managedParseIssueSchema),
|
||||
}),
|
||||
limitations: z.array(z.string()),
|
||||
@@ -582,7 +605,7 @@ export const managedMemberInspectionSchema = z
|
||||
"Examined zero-based MethodDef row interval before declaring-type selection; null means the row scope was not recorded. Row coordinates are build-local and cannot prove cross-build member absence for a page.",
|
||||
),
|
||||
coverage: z.object({
|
||||
state: z.enum(["complete", "partial", "unavailable"]),
|
||||
state: managedInspectionStateSchema,
|
||||
issues: z.array(managedParseIssueSchema),
|
||||
}),
|
||||
limitations: z.array(z.string()),
|
||||
@@ -629,7 +652,7 @@ export const managedNativeBoundaryInspectionSchema = z.object({
|
||||
mixed_mode_or_native_header: z.boolean().nullable(),
|
||||
}),
|
||||
coverage: z.object({
|
||||
state: z.enum(["complete", "partial", "unavailable"]),
|
||||
state: managedInspectionStateSchema,
|
||||
issues: z.array(managedParseIssueSchema),
|
||||
}),
|
||||
limitations: z.array(z.string()),
|
||||
|
||||
@@ -921,6 +921,46 @@ describe("managed application graph coverage", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("reports an unavailable source as unknown, not partial", () => {
|
||||
const bytes = buildManagedPeFixture();
|
||||
const binary = managedPeFixtureTarget(bytes, "/fixture/ManagedInterop.exe");
|
||||
const members = inspectManagedMembersBytes(bytes, binary);
|
||||
const evidence = (state: "partial" | "unavailable") =>
|
||||
createEvidence(binary, MANAGED_STATIC_PROVIDER, {
|
||||
operation: "inspect_managed_members",
|
||||
parameters: {},
|
||||
result: { ...members, coverage: { state, issues: [] } },
|
||||
rawResult: null,
|
||||
limitations: members.limitations,
|
||||
});
|
||||
const project = (state: "partial" | "unavailable") => {
|
||||
const projection = projectManagedApplicationGraphEvidence({
|
||||
managed_members: evidence(state),
|
||||
});
|
||||
if (!projection.ok) throw new Error("projection failed");
|
||||
return managedApplicationGraphResultSchema.parse(
|
||||
parseEvidence(projection.value).normalized_result,
|
||||
);
|
||||
};
|
||||
|
||||
const unavailable = project("unavailable");
|
||||
expect(unavailable.coverage).toEqual({ status: "unknown" });
|
||||
expect(unavailable.graph.coverage).toEqual({
|
||||
status: "unknown",
|
||||
truncated: false,
|
||||
omitted_count: null,
|
||||
limits: [],
|
||||
});
|
||||
expect(unavailable.limitations).toContainEqual(
|
||||
expect.stringMatching(/managed Evidence record is unavailable/iu),
|
||||
);
|
||||
expect(unavailable.limitations).not.toContainEqual(
|
||||
expect.stringMatching(/partial coverage/iu),
|
||||
);
|
||||
// A known gap alone stays partial.
|
||||
expect(project("partial").coverage).toEqual({ status: "partial" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("managed application graph type selection", () => {
|
||||
|
||||
Reference in new issue
Block a user