Files
rea/scripts/lib/ida-feature-trace.mjs
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

53 lines
2.0 KiB
JavaScript

import assert from "node:assert/strict";
const NEGATIVE_REASONS = new Set(["not_in_procedure", "outside_segments"]);
/**
* Assert the `trace_feature` result of the real IDA lane for one selected
* procedure. The query is a substring search, so the trace may also match other
* procedures and strings; each reference is checked against its own matched
* target, and a source outside every function (a data reference) is valid.
*/
export function assertFeatureTrace(trace, procedure) {
assert.ok(
trace.matches.some(
({ type, address }) =>
type === "procedure" && address === procedure.address,
),
"The trace omitted the selected procedure's match.",
);
assert.equal(typeof trace.truncated, "boolean");
const matched = new Set(trace.matches.map(({ address }) => address));
for (const reference of trace.references) {
assert.ok(
matched.has(reference.target_address),
`A reference targets ${reference.target_address}, which is not a match.`,
);
const { containing_procedure: containing } = reference;
if (containing.found)
assert.equal(typeof containing.procedure.address, "string");
else assert.ok(NEGATIVE_REASONS.has(containing.reason));
}
}
/**
* Find a string that IDA maps outside every function. A string inside a
* function chunk (inline data in code) is valid and skipped, so the strings are
* tried in order. Any other answer must be "not in a procedure": IDA lists only
* mapped strings, so an unmapped answer is a containment defect and fails the
* lane. Returns the address, or undefined when every string is inside a function.
*/
export async function findMappedNonFunction(strings, resolveAt) {
for (const { address } of strings) {
const resolved = await resolveAt(address);
if (resolved.found) continue;
assert.equal(
resolved.reason,
"not_in_procedure",
`The listed string at ${address} resolved as ${resolved.reason}.`,
);
return address;
}
return undefined;
}