mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 10:43:30 +00:00
* 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
145 lines
4.3 KiB
TypeScript
145 lines
4.3 KiB
TypeScript
import { rm } from "node:fs/promises";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { inspectAddressContext } from "../../../../src/application/AnalysisContextQueries.js";
|
|
import { EnhancedTools } from "../../../../src/application/EnhancedTools.js";
|
|
import { IdaSessionClient } from "../../../../src/ida/IdaSessionClient.js";
|
|
import { createIdaTarget, RecordingIdaMcp } from "../../../fixtures/idaMcp.js";
|
|
|
|
const roots: string[] = [];
|
|
afterEach(async () => {
|
|
for (const root of roots.splice(0))
|
|
await rm(root, { recursive: true, force: true });
|
|
});
|
|
|
|
const session = async (cappedReferences = false) => {
|
|
const { root, target } = await createIdaTarget("arm64");
|
|
roots.push(root);
|
|
const producer = new RecordingIdaMcp(target, "headless");
|
|
if (cappedReferences) {
|
|
const inner = producer.overrideCall;
|
|
producer.overrideCall = (name, args) =>
|
|
name === "xrefs_to"
|
|
? [
|
|
{
|
|
xrefs: [
|
|
{
|
|
addr: "0x1004",
|
|
type: "code",
|
|
fn: { addr: "0x1000", name: "main", size: "0x20" },
|
|
},
|
|
],
|
|
more: true,
|
|
},
|
|
]
|
|
: inner?.(name, args);
|
|
}
|
|
const client = new IdaSessionClient(
|
|
{
|
|
command: "fixture",
|
|
args: [],
|
|
env: {},
|
|
mode: "headless",
|
|
timeoutMs: 1000,
|
|
workspaceRoot: root,
|
|
},
|
|
target,
|
|
producer,
|
|
);
|
|
return { client, target };
|
|
};
|
|
|
|
const MAIN = {
|
|
found: true,
|
|
procedure: { address: "0x1000", name: "main" },
|
|
};
|
|
|
|
describe("trace_feature on headless IDA", () => {
|
|
it("attributes a reference to its containing procedure", async () => {
|
|
const { client } = await session();
|
|
try {
|
|
const result = await new EnhancedTools(client).execute("trace_feature", {
|
|
query: "a.b",
|
|
});
|
|
|
|
if (!result.ok) throw result.error;
|
|
expect(result.value).toMatchObject({
|
|
matches: [{ type: "string", address: "0x3000" }],
|
|
references: [
|
|
{
|
|
target_address: "0x3000",
|
|
source_address: "0x1004",
|
|
containing_procedure: MAIN,
|
|
},
|
|
],
|
|
truncated: false,
|
|
residual_unknowns: [],
|
|
});
|
|
} finally {
|
|
expect((await client.close()).ok).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("keeps an upstream reference cap visible once containment makes the trace run", async () => {
|
|
const { client } = await session(true);
|
|
try {
|
|
const result = await new EnhancedTools(client).execute("trace_feature", {
|
|
query: "a.b",
|
|
});
|
|
|
|
if (!result.ok) throw result.error;
|
|
expect(result.value).toMatchObject({
|
|
references: [{ source_address: "0x1004", containing_procedure: MAIN }],
|
|
truncated: true,
|
|
residual_unknowns: [
|
|
"References to 0x3000 were truncated by the provider; additional references may exist.",
|
|
],
|
|
});
|
|
} finally {
|
|
expect((await client.close()).ok).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("inspect_address_context on headless IDA", () => {
|
|
it("resolves the admitted program as the document and reports the containing procedure", async () => {
|
|
const { client, target } = await session();
|
|
try {
|
|
const result = await inspectAddressContext(client, {
|
|
address: "0x1004",
|
|
});
|
|
|
|
if (!result.ok) throw result.error;
|
|
expect(result.value).toMatchObject({
|
|
address: "0x1004",
|
|
document: target.path,
|
|
procedure: { state: "available", value: MAIN },
|
|
name: { state: "unavailable" },
|
|
comment: { state: "unavailable" },
|
|
inline_comment: { state: "unavailable" },
|
|
bookmarks: { state: "unavailable" },
|
|
});
|
|
} finally {
|
|
expect((await client.close()).ok).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("answers the same way for an explicit admitted document", async () => {
|
|
const { client, target } = await session();
|
|
try {
|
|
const implicit = await inspectAddressContext(client, {
|
|
address: "0x1004",
|
|
});
|
|
const explicit = await inspectAddressContext(client, {
|
|
address: "0x1004",
|
|
document: target.path,
|
|
});
|
|
|
|
if (!implicit.ok) throw implicit.error;
|
|
if (!explicit.ok) throw explicit.error;
|
|
expect(explicit.value).toEqual(implicit.value);
|
|
} finally {
|
|
expect((await client.close()).ok).toBe(true);
|
|
}
|
|
});
|
|
});
|