mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 08:17:02 +00:00
fix(snapshot): retain one-shot Ghidra pseudocode results (#8089)
* fix(snapshot): retain one-shot Ghidra pseudocode results * test(snapshot): cover evidence-only pseudocode migration --------- Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
This commit is contained in:
1 parent
4d2aff2ec6
commit
ebea0f009f
8 files changed
+243
-6
No files matched your search
@@ -777,7 +777,9 @@ describe("analysis snapshot cache policy", () => {
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("document-scoped snapshot admission", () => {
|
||||
it("retains caller observations without a document while refusing to replay them", () => {
|
||||
const descriptor: CapabilityDescriptor = {
|
||||
operation: "procedure_callers",
|
||||
@@ -829,6 +831,35 @@ describe("analysis snapshot cache policy", () => {
|
||||
document: "fixture",
|
||||
}),
|
||||
).toBe(true);
|
||||
const ghidra = {
|
||||
...pseudocode,
|
||||
provider: { ...pseudocode.provider, id: "ghidra" },
|
||||
};
|
||||
expect(
|
||||
isSnapshotCacheable("procedure_pseudo_code", ghidra, parameters),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isSnapshotCacheable(
|
||||
"procedure_pseudo_code",
|
||||
ghidra,
|
||||
parameters,
|
||||
"one-shot-target",
|
||||
),
|
||||
).toBe(true);
|
||||
for (const refused of [
|
||||
pseudocode,
|
||||
{ ...ghidra, cachePolicy: "live" as const },
|
||||
{ ...ghidra, effects: { ...ghidra.effects, mutatesArtifact: true } },
|
||||
{ ...ghidra, effects: { ...ghidra.effects, changesPermissions: true } },
|
||||
])
|
||||
expect(
|
||||
isSnapshotCacheable(
|
||||
"procedure_pseudo_code",
|
||||
refused,
|
||||
parameters,
|
||||
"one-shot-target",
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import type {
|
||||
AnalysisExecution,
|
||||
AnalysisOperation,
|
||||
CapabilityDescriptor,
|
||||
ExecutionOptions,
|
||||
} from "../AnalysisProvider.js";
|
||||
import { OFFICIAL_TOOL_CONTRACTS } from "../../contracts/officialToolContracts.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
@@ -94,6 +95,16 @@ const replayDocumentSelected = (
|
||||
!DOCUMENT_SCOPED_OPERATIONS.has(operation) ||
|
||||
typeof parameters.document === "string";
|
||||
|
||||
/** Ghidra's private Program belongs to this one-shot target; GUI defaults do not. */
|
||||
const isOneShotPseudocode = (
|
||||
operation: AnalysisOperation,
|
||||
descriptor: CapabilityDescriptor | undefined,
|
||||
context: ExecutionOptions["snapshotContext"],
|
||||
): boolean =>
|
||||
context === "one-shot-target" &&
|
||||
operation === "procedure_pseudo_code" &&
|
||||
descriptor?.provider.id === "ghidra";
|
||||
|
||||
/**
|
||||
* Caller and callee walks omit `document`. Their observations are still
|
||||
* Evidence. Other document-scoped reads, including `procedure_pseudo_code`
|
||||
@@ -112,12 +123,14 @@ export const isSourceEvidenceRetainable = (
|
||||
operation: AnalysisOperation,
|
||||
descriptor: CapabilityDescriptor | undefined,
|
||||
parameters: Readonly<Record<string, JsonValue>>,
|
||||
context?: ExecutionOptions["snapshotContext"],
|
||||
): descriptor is CapabilityDescriptor =>
|
||||
!STATEFUL_OPERATIONS.has(operation) &&
|
||||
!RESOURCE_SCOPED_OPERATIONS.has(operation) &&
|
||||
(!DOCUMENT_SCOPED_OPERATIONS.has(operation) ||
|
||||
typeof parameters.document === "string" ||
|
||||
DOCUMENTLESS_SOURCE_EVIDENCE_OPERATIONS.has(operation)) &&
|
||||
DOCUMENTLESS_SOURCE_EVIDENCE_OPERATIONS.has(operation) ||
|
||||
isOneShotPseudocode(operation, descriptor, context)) &&
|
||||
(!CURSOR_DEFAULT_OPERATIONS.has(operation) ||
|
||||
typeof parameters.address === "string") &&
|
||||
descriptor?.effects.mutatesArtifact === false &&
|
||||
@@ -130,9 +143,11 @@ export const isSnapshotCacheable = (
|
||||
operation: AnalysisOperation,
|
||||
descriptor: CapabilityDescriptor | undefined,
|
||||
parameters: Readonly<Record<string, JsonValue>>,
|
||||
context?: ExecutionOptions["snapshotContext"],
|
||||
): descriptor is CapabilityDescriptor =>
|
||||
isSourceEvidenceRetainable(operation, descriptor, parameters) &&
|
||||
replayDocumentSelected(operation, parameters) &&
|
||||
isSourceEvidenceRetainable(operation, descriptor, parameters, context) &&
|
||||
(replayDocumentSelected(operation, parameters) ||
|
||||
isOneShotPseudocode(operation, descriptor, context)) &&
|
||||
descriptor.cachePolicy !== "live";
|
||||
|
||||
/** Windows Ghidra must admit the selected source through its native snapshot owner. */
|
||||
|
||||
@@ -1418,6 +1418,7 @@ export class BinarySession
|
||||
active: this.#active,
|
||||
operation: name,
|
||||
parameters: arguments_,
|
||||
snapshotContext: options?.snapshotContext,
|
||||
nativeSourceAdmitted: this.#active?.nativeSourceAdmitted === true,
|
||||
unboundOperationError: (operation, route) =>
|
||||
this.#providerRouter.unboundOperationError(operation, route),
|
||||
@@ -1479,7 +1480,12 @@ export class BinarySession
|
||||
}
|
||||
if (
|
||||
execution.ok &&
|
||||
isSourceEvidenceRetainable(name, capability, arguments_) &&
|
||||
isSourceEvidenceRetainable(
|
||||
name,
|
||||
capability,
|
||||
arguments_,
|
||||
options?.snapshotContext,
|
||||
) &&
|
||||
profile !== undefined
|
||||
) {
|
||||
const evidence = createAnalysisExecutionEvidence(
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
AnalysisExecution,
|
||||
AnalysisOperation,
|
||||
CapabilityDescriptor,
|
||||
ExecutionOptions,
|
||||
} from "../AnalysisProvider.js";
|
||||
import {
|
||||
isSnapshotCacheable,
|
||||
@@ -41,6 +42,7 @@ interface PrepareSessionExecutionInput {
|
||||
readonly active: ActiveExecutionBinding | undefined;
|
||||
readonly operation: AnalysisOperation;
|
||||
readonly parameters: Readonly<Record<string, JsonValue>>;
|
||||
readonly snapshotContext?: ExecutionOptions["snapshotContext"];
|
||||
readonly nativeSourceAdmitted: boolean;
|
||||
readonly unboundOperationError: (
|
||||
operation: AnalysisOperation,
|
||||
@@ -94,7 +96,12 @@ export const prepareSessionExecution = (
|
||||
: undefined;
|
||||
const cacheable =
|
||||
profile !== undefined &&
|
||||
isSnapshotCacheable(operation, capability, parameters);
|
||||
isSnapshotCacheable(
|
||||
operation,
|
||||
capability,
|
||||
parameters,
|
||||
input.snapshotContext,
|
||||
);
|
||||
const cached =
|
||||
cacheable &&
|
||||
(!requiresNativeSourceAdmissionBeforeReplay(capability.provider.id) ||
|
||||
|
||||
Reference in new issue
Block a user