fix(managed): retain qualified native candidates

This commit is contained in:
morluto committed 2026-10-27 09:15:24 +08:00
1 parent 9c9c64edfa
commit 9cad06db10
2 files changed
+83 -3

No files matched your search

@@ -356,9 +356,14 @@ const matchPinvoke = (
managed.import_name === lookupSymbolName(symbol) &&
moduleRelation(symbol) === "match",
);
const unknownModule = candidates.find(
(symbol) => moduleRelation(symbol) === "unknown",
const exactUnknownModule = candidates.find(
(symbol) =>
managed.import_name === lookupSymbolName(symbol) &&
moduleRelation(symbol) === "unknown",
);
const unknownModule =
exactUnknownModule ??
candidates.find((symbol) => moduleRelation(symbol) === "unknown");
const decorated = candidates.find(
(symbol) => moduleRelation(symbol) === "match",
);
@@ -382,7 +387,12 @@ const matchPinvoke = (
candidates,
},
};
if (unknownModule !== undefined)
// Preserve an exact-name candidate's uncertainty, but an unrelated unknown
// module must not displace an observed module match for a decorated name.
if (
unknownModule !== undefined &&
(exactUnknownModule !== undefined || decorated === undefined)
)
return {
match: {
status: "unresolved",
@@ -647,6 +647,76 @@ it("keeps empty identities unknown and compares suffix-only identities as data",
});
});
it("retains a decorated module match when another decorated candidate has unknown identity", () => {
const input = inputWithImportScope("nativehelper.dll");
const parent = input.managed_boundaries;
const normalized = managedNativeBoundaryInspectionSchema.parse(
parent.normalized_result,
);
const managed = createEvidence(undefined, parent.provider, {
operation: parent.operation,
parameters: parent.parameters,
result: {
...normalized,
pinvoke_imports: normalized.pinvoke_imports.map((item) => ({
...item,
char_set: "unicode",
no_mangle: false,
})),
},
rawResult: null,
});
const known = nativeFunctionEvidence(
"/examples/nativehelper.dll",
"pe",
"open_nativeW",
);
const subjectless = (name: string) => {
const source = nativeFunctionEvidence("/examples/unknown.dll", "pe", name);
return createEvidence(undefined, source.provider, {
operation: source.operation,
parameters: source.parameters,
result: source.normalized_result,
rawResult: null,
});
};
for (const name of ["open_nativeW", "open_native"]) {
const unknown = subjectless(name);
for (const observations of [
[known, unknown],
[unknown, known],
]) {
const result = executeManagedNativeBoundaries({
managed_boundaries: managed,
native_observations: observations,
});
expect(result.ok).toBe(true);
if (!result.ok) throw result.error;
const output = managedNativeVerificationResultSchema.parse(
result.value.evidence.normalized_result,
);
expect(output.pinvoke_imports[0]).toMatchObject(
name === "open_nativeW"
? {
status: "inferred",
basis: "decorated-name-candidate",
matched_native: { evidence_id: known.evidence_id },
}
: {
status: "unresolved",
basis: "module-identity-unknown",
matched_native: { evidence_id: unknown.evidence_id },
},
);
expect(output.pinvoke_imports[0]?.candidates).toHaveLength(2);
expect(output.evidence_links).toEqual(
expect.arrayContaining([known.evidence_id, unknown.evidence_id]),
);
expect(result.value.sourceEvidence).toEqual([managed, ...observations]);
}
}
});
it("matches versioned ELF SONAME variations without dropping their version", () => {
const result = verifyManagedNativeVerificationSource(
readFixtureSource({