* 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
53 lines
2.0 KiB
JavaScript
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;
|
|
}
|