fix(ghidra): preserve omitted PDB identity uncertainty

This commit is contained in:
N0zoM1z0 2026-10-14 08:23:55 +08:00
parent c8ba745c92
commit 1941d343b8
5 changed files with 78 additions and 4 deletions

View File

@ -218,7 +218,7 @@ export const resolveGhidraAnalysisProfile = async (
].sort(),
import_mode: "ephemeral-source-immutable",
...(target.format === "pe"
? { pdb_observations: "program-info-v4" }
? { pdb_observations: "program-info-v5" }
: {}),
annotation_policy: "atomic-function-entry-metadata-v1",
load_image_observations: "source-mappings-entry-context-v2",

View File

@ -168,6 +168,67 @@ it("reports NB10 declared age without inventing a GUID", () => {
expect(caveats[0]).not.toContain("GUID");
});
it.each([false, null, true])(
"keeps an omitted NB10 GUID unknown with loaded = %s",
(loaded) => {
const pdbPath = "a".repeat(4070) + "\\legacy.pdb";
const legacy = {
...target,
codeView: {
pdbPath,
pdbFileName: "legacy.pdb",
guid: null,
age: 2,
},
};
const pdb = {
...absentAttributes,
file: pdbPath,
age: "2",
signature: "01234567",
version: "NB10",
loaded,
};
expect(ghidraPePdbLimitations(legacy, pdb)).toEqual(
ghidraPdbLimitations(pdb),
);
const omitted: GhidraSessionInfo["pdb"] = {
...pdb,
resource_limit: { omitted_attributes: ["guid"], utf16_budget: 4096 },
};
const caveats = ghidraPePdbLimitations(legacy, omitted);
expect(caveats).toEqual([
...ghidraPdbLimitations(omitted),
expect.stringContaining('PDB "legacy.pdb" (age (decimal) 2)'),
]);
expect(caveats.at(-1)).toContain("equivalence is not established");
},
);
it.each(["signature", "version"] as const)(
"keeps matching NB10 identity when only %s is omitted",
(attribute) => {
const legacy = {
...target,
codeView: {
pdbPath: "legacy.pdb",
pdbFileName: "legacy.pdb",
guid: null,
age: 2,
},
};
const pdb: GhidraSessionInfo["pdb"] = {
...absentAttributes,
file: "legacy.pdb",
age: "2",
resource_limit: { omitted_attributes: [attribute], utf16_budget: 4096 },
};
expect(ghidraPePdbLimitations(legacy, pdb)).toEqual(
ghidraPdbLimitations(pdb),
);
},
);
it("retains missing-report uncertainty without a declared CodeView record", () => {
const { codeView: _, ...withoutReference } = target;
expect(ghidraPePdbLimitations(withoutReference, undefined)).toEqual(

View File

@ -15,6 +15,10 @@ const reportedPdbMatchesDeclaration = (
reported: NonNullable<GhidraSessionInfo["pdb"]>,
): boolean => {
if (
reported.resource_limit?.omitted_attributes.some(
(attribute) =>
attribute === "file" || attribute === "guid" || attribute === "age",
) ||
(reported.file !== declared.pdbPath &&
reported.file !== declared.pdbFileName) ||
reported.age === null ||

View File

@ -85,7 +85,12 @@ const createElfClient = async (ghidra: GhidraProvider) => {
return ghidra.createClient(target, resolved.value.profile);
};
it.each(["program-info-v1", "program-info-v2", "program-info-v3"])(
it.each([
"program-info-v1",
"program-info-v2",
"program-info-v3",
"program-info-v4",
])(
"retains historical PE PDB Evidence for %s without replaying its old projection",
async (pdbObservations) => {
const target = peTarget("x86_64");
@ -99,7 +104,11 @@ it.each(["program-info-v1", "program-info-v2", "program-info-v3"])(
});
const operation = "search_procedures";
const parameters = { pattern: "wmain" };
const limitations = ["Legacy projection collapsed unknown PDB load state."];
const limitations = [
pdbObservations === "program-info-v4"
? "Legacy projection discarded NB10 declaration when the reported GUID was omitted."
: "Legacy projection collapsed unknown PDB load state.",
];
const cache = new AnalysisSnapshotCache();
cache.record({
target,

View File

@ -315,7 +315,7 @@ it("retains missing PDB coverage in empty PE name-search Evidence and snapshot r
const evidence = sessionEvidence(harness.session, reply.structuredContent);
expect(evidence.normalized_result).toEqual([]);
expect(evidence.analysis_profile?.parameters.pdb_observations).toBe(
"program-info-v4",
"program-info-v5",
);
const limitation = evidence.limitations.find((value) =>
value.includes(pdb.file),