Files
reverse-engineering-anything/src/ida/IdaReferenceCoverage.test.ts
T
Mykola Dementiev 5f8af4ee5f feat(ida): resolve containing procedures and report the admitted program as the document (#2374)
* test(mcp): expect trace_feature to report references the provider truncated

A provider that reports a capped xrefs read makes the trace truncated with a residual unknown for that target. An explicit complete report and a provider that reports nothing leave the trace as before.

* fix(mcp): report references a provider truncated in trace_feature

Executions gain an optional flag for a capped xrefs read, set by the IDA adapter from the upstream cap. trace_feature marks the trace truncated with a residual unknown for that target only when the provider reports a truncation; a provider that reports nothing keeps its previous result. The trace_feature workflow profile is revised so earlier results are not replayed.

* refactor(ida): report reference truncation whenever the runner observed an xrefs cap

* test(mcp): cover session forwarding, per-target state, native reports and failures of the trace coverage

* test(mcp): format the session forwarding test

* test(ida): expect headless IDA to resolve containing procedures from lookups and segment facts

Cover positive and negative containment, segment boundaries including addresses above 2^53, failures of the lookup and of the segment survey, cancellation, input validation and the admitted program as the only document.

* test(ida): expect feature tracing and address context to work on headless IDA

Run trace_feature, including a capped reference read, and inspect_address_context with and without an explicit document through the IDA session client.

* test(mcp): expect trace_feature availability to follow the search operations it calls

* fix(mcp): derive trace_feature availability from the search operations it calls

* feat(ida): resolve containing procedures and report the admitted program as the document

The modern headless profile resolves an address through the function lookup and, when no function contains it, tells a mapped address from an unmapped one using the database-scoped minimal survey. A survey that is missing, truncated or inconsistent fails the read. list_documents reports the single admitted program. The attached profiles do not declare either operation.

* docs(ida): describe containing procedures, the admitted document and truncated feature traces

* test(ida): verify containing procedures, the admitted document and feature tracing on real IDA

* docs(testing): list the headless IDA workflows the real lane verifies

* test(ida): make each survey failure case isolate a single defect

* test(ida): pin large addresses, segment order, document identity, attached refusals and each trace_feature prerequisite

* test(mcp): expect a repeated reference source to be resolved once in trace_feature

* fix(mcp): resolve the containing procedure of a repeated reference source once

* test(ida): trace the fixture procedure and derive probe addresses from its facts in the real lane

* test(process): format processCaptureLifecycle.test.ts after the merge

* test(ida): declare the alignment each synthetic universal slice offset satisfies

* refactor(ida): move the real lane's feature trace assertions into a helper

* test(ida): expect the real lane's feature trace check to accept references to every matched address

* fix(ida): check each trace reference against its own matched address in the real lane

* test(ida): pin that the new operations reject a document other than the admitted program

* refactor(ida): move the real lane's mapped non-function probe into a helper

* test(ida): expect the real lane's mapped data probe to skip strings inside functions

* fix(ida): try the target's strings in order for the mapped non-function probe

* test(ida): declare the real lane helper types for the verifier tests

* test(ida): expect the real lane to reject a listed string that resolves as unmapped

* fix(ida): fail the real lane when a listed string resolves as unmapped
2026-10-13 20:56:20 +08:00

97 lines
3.0 KiB
TypeScript

import { rm } from "node:fs/promises";
import { afterEach, describe, expect, it } from "vitest";
import {
createIdaTarget,
RecordingIdaMcp,
} from "../../tests/fixtures/idaMcp.js";
import {
callGraphSession as session,
removeCallGraphSessions,
} from "../../tests/fixtures/idaCallGraph.js";
import { IdaSessionClient } from "./IdaSessionClient.js";
const attachedRoots: string[] = [];
afterEach(async () => {
await removeCallGraphSessions();
for (const root of attachedRoots.splice(0))
await rm(root, { recursive: true, force: true });
});
const xrefs = async (more: boolean | undefined) => {
const { client, producer } = await session({ callees: [] }, "x86_64");
const inner = producer.overrideCall;
producer.overrideCall = (name, args) =>
name === "xrefs_to"
? [{ xrefs: [], ...(more === undefined ? {} : { more }) }]
: inner?.(name, args);
try {
const result = await client.execute("xrefs", { address: "0x1000" });
if (!result.ok) throw result.error;
return result.value;
} finally {
expect((await client.close()).ok).toBe(true);
}
};
describe("headless IDA reference coverage", () => {
it("reports a capped xrefs read as truncated", async () => {
const execution = await xrefs(true);
expect(execution.referencesTruncated).toBe(true);
expect(
execution.limitations,
"the diagnostic limitation is still reported",
).toContain(
"Upstream xrefs_to capped results at 1000 references; additional references remain unknown.",
);
});
it("reports an explicitly uncapped xrefs read as not truncated", async () => {
expect((await xrefs(false)).referencesTruncated).toBe(false);
});
it("reports nothing when the producer does not say whether the read was capped", async () => {
expect((await xrefs(undefined)).referencesTruncated).toBeUndefined();
});
it("does not report reference coverage for other operations", async () => {
const { client } = await session({ callees: [] }, "x86_64");
try {
const result = await client.execute("procedure_callees", {
procedure: "main",
});
if (!result.ok) throw result.error;
expect(result.value.referencesTruncated).toBeUndefined();
} finally {
expect((await client.close()).ok).toBe(true);
}
});
it("reports nothing for the legacy profile, whose producer has no cap flag", async () => {
const { root, target } = await createIdaTarget("x86_64");
attachedRoots.push(root);
const client = new IdaSessionClient(
{
command: "fixture",
args: [],
env: {},
mode: "attached",
timeoutMs: 1000,
workspaceRoot: root,
},
target,
new RecordingIdaMcp(target, "attached"),
);
try {
const result = await client.execute("xrefs", { address: "0x1000" });
if (!result.ok) throw result.error;
expect(result.value.referencesTruncated).toBeUndefined();
} finally {
expect((await client.close()).ok).toBe(true);
}
});
});