fix(ghidra): clarify function annotation selectors (#8584)

* fix(ghidra): clarify function annotation selectors

* test(ghidra): compare catalog guidance with its contract

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
morlutoandCursor Agent authored and GitHub committed 2026-11-08 01:10:17 +08:00
1 parent c928bb8d80
commit 4bb53000a3
6 files changed
+31 -4

No files matched your search

+3 -1
View File
@@ -1256,7 +1256,9 @@ handle-based path admission. See [Windows Ghidra P0](windows-ghidra-p0.md) and
[issue #527](https://github.com/morluto/rea/issues/527).
On Linux and macOS, `annotate_native_function` atomically edits a function name
and entry comments in the ephemeral database, returning refreshed analysis
without changing executable bytes. Annotation text must contain no NUL or
without changing executable bytes. A Ghidra address inside a function selects
the containing function; the name and comments are changed at its entry, not
at the selected instruction. Annotation text must contain no NUL or
unpaired Unicode surrogate; a rejection identifies the field and UTF-16 index
and leaves every annotation unchanged. CRLF, supplementary Unicode characters,
and combining characters are preserved. Windows P0 remains read-only. Ghidra has no
+9
View File
@@ -13,6 +13,7 @@ import { promisify } from "node:util";
import { GHIDRA_ANNOTATIONS_RETAINED } from "../../dist/ghidra/GhidraProviderClient.js";
import Ajv from "ajv/dist/2020.js";
import addFormats from "ajv-formats";
import { OFFICIAL_TOOL_CONTRACTS } from "../../dist/contracts/officialToolContracts.js";
import { verifyLegacyGhidraReferenceSnapshot } from "./ghidra-reference-snapshot-e2e.mjs";
import { verifyGhidraSnapshotLifecycle } from "./real-ghidra-snapshot-lifecycle.mjs";
@@ -30,6 +31,14 @@ export async function verifyGhidraBoundaries(
) {
const options = { timeout: 180000 };
const catalog = await client.listTools();
const annotation = catalog.tools.find(
(tool) => tool.name === "annotate_native_function",
);
const annotationContract = OFFICIAL_TOOL_CONTRACTS.find(
(tool) => tool.name === "annotate_native_function",
);
assert.ok(annotationContract, "Annotation contract is missing");
assert.equal(annotation?.description, annotationContract.description);
const ajv = new Ajv({ strict: false });
addFormats(ajv);
const validators = new Map();
@@ -156,6 +156,15 @@ export async function verifyGhidraEntryAliases({
alias.address,
"Explicit interior addresses keep their existing containment semantics",
);
const interiorEdit = await call("annotate_native_function", {
procedure: interior.address,
comment: "Selected by interior address",
});
assert.equal(interiorEdit.annotations.address, alias.address);
assert.equal(
interiorEdit.annotations.comment,
"Selected by interior address",
);
const edited = await call("annotate_native_function", {
procedure: alias.value,
comment: "Selected by imported entry alias",
+3 -1
View File
@@ -482,7 +482,9 @@ const registerAnnotationCommand = (
path: localPathStringSchema.describe("Local executable path"),
procedure: z
.string()
.describe("Exact function entry address or unique name"),
.describe(
"Function name or address; Ghidra resolves interior addresses to the containing function and edits its entry",
),
}),
options: z.object({
name: z.string().min(1).optional().describe("Analyst function name"),
+1 -1
View File
@@ -57,7 +57,7 @@ const official = <
export const OFFICIAL_TOOL_CONTRACTS = [
official(
"annotate_native_function",
"Atomically update one function name and/or entry comments, read them back, and return a refreshed complete function dossier. Ghidra edits owned Program metadata; original executable bytes remain unchanged. Empty comment text clears that comment. With REA_GHIDRA_CHECKPOINT_DIR configured at startup, normal close saves edits for the next process; ephemeral mode discards them. CLI follows the same setting after returning its updated dossier. Check binary_session capabilities for the current mode. Invalid edits roll back the entire operation. Windows P0 and providers without this capability are unsupported.",
"Atomically update one function name and/or entry comments, read them back, and return a refreshed complete function dossier. Ghidra resolves an interior address to its containing function and applies edits at the function entry, not at the selected instruction; use procedure_address first to find that entry. Ghidra edits owned Program metadata; original executable bytes remain unchanged. Empty comment text clears that comment. With REA_GHIDRA_CHECKPOINT_DIR configured at startup, normal close saves edits for the next process; ephemeral mode discards them. CLI follows the same setting after returning its updated dossier. Check binary_session capabilities for the current mode. Invalid edits roll back the entire operation. Windows P0 and providers without this capability are unsupported.",
nativeFunctionAnnotationsInputSchema,
),
official(
@@ -4,7 +4,12 @@ import { functionDossierSchema } from "../native/functionDossier.js";
/** One explicit function's analyst-authored metadata changes. */
export const nativeFunctionAnnotationsInputSchema = z
.strictObject({
procedure: z.string().min(1),
procedure: z
.string()
.min(1)
.describe(
"Function name or address selecting a function; edits apply at its entry.",
),
name: z.string().min(1).optional(),
comment: z
.string()