feat(analysis): make resource budgets recoverable across CLI and MCP (#9706)

* refactor(analysis): centralize resource budgets and recovery facts

* feat(analysis): expose ASAR and Ghidra runtime admission controls

* feat(artifacts): expose reader admission and preserve limited recognition

* feat(javascript): expose recoverable semantic precision budgets

* feat(web): add recoverable source-map budgets and compact retained views

* feat(web): make historical capture decoding budgets recoverable

* fix(desktop): enforce selected view policies across capture and export

* feat(runtime): make Frida and LLDB retention budgets selectable

* feat(native): expose diagnostic budgets and preserve exhaustion causes

* feat(managed): expose recoverable snapshot and representation budgets

* feat(metadata): make Go and Rust inspection budgets recoverable

* feat(wasm): expose representation and producer admissions

* feat(evm): expose per-call worker resource budgets

* feat(android): make resource and string-pool omissions recoverable

* feat(producers): expose command and recorded-capture budgets

* docs(analysis): record recovery contracts and integrated validation

* fix(docs): link calibration oracle from published verification pages

* fix(docs): restore desktop task anchors and receipt links

* test(analysis): align fixtures with selectable budget contracts

* fix(analysis): remove source-location projector dependency cycle

* test(analysis): refresh budget recovery and verifier regressions

* test(analysis): preserve format and publication recovery expectations

* test(windows): align conformance with owned runtime admission

* fix(desktop): preserve selected alias kinds in exported state

* test(analysis): align provider fixtures with recoverable budgets

* test(desktop): validate emitted alias metadata structurally

* fix(ghidra): classify bounded Windows endpoint refusals

* test(cli): assert selectable artifact admission recovery

* fix(cli): preserve capture provenance in environment builds
This commit is contained in:
morluto authored and GitHub committed 2026-11-10 05:11:20 -05:00
1 parent 55b6d2ca20
commit cb4e5cfbff
515 files changed
+21066 -3637

No files matched your search

+5 -6
View File
@@ -850,13 +850,12 @@ export const createServer = (
recordEvidence,
analysisAdmission,
);
registerWebSourceLocationTool(
server,
webSourceLocation,
toolLogger,
registerWebSourceLocationTool(server, webSourceLocation, {
logger: toolLogger,
recordEvidence,
analysisAdmission,
);
withAdmittedAnalysis: analysisAdmission,
evidenceById,
});
registerJavaScriptRecoveryTool(
server,
new JavaScriptRecoveryService(javascriptRecovery),
+11
View File
@@ -1,3 +1,4 @@
import type { FridaCapturePolicy } from "../domain/fridaCapturePolicy.js";
import type { McpServer, ServerContext } from "@modelcontextprotocol/server";
import type { Logger } from "pino";
@@ -67,6 +68,9 @@ export const registerFridaTools = (
async (input, context: ServerContext) => {
const remote = remoteConnection(input.remote);
const selected = {
...(input.capture_limits === undefined
? {}
: { captureLimits: input.capture_limits }),
...(input.device_id === undefined ? {} : { deviceId: input.device_id }),
...(remote === undefined ? {} : { remote }),
};
@@ -222,6 +226,9 @@ export const registerFridaTools = (
async (input, context: ServerContext) => {
const remote = remoteConnection(input.remote);
const selected = {
...(input.capture_limits === undefined
? {}
: { captureLimits: input.capture_limits }),
...(input.device_id === undefined ? {} : { deviceId: input.device_id }),
...(remote === undefined ? {} : { remote }),
};
@@ -310,6 +317,7 @@ const remoteConnection = (
};
const sessionOutput = (session: {
readonly captureLimits?: FridaCapturePolicy;
readonly sessionId: string;
readonly deviceId: string;
readonly target: string;
@@ -318,6 +326,9 @@ const sessionOutput = (session: {
readonly state: "paused" | "running" | "detached";
}) => ({
session_id: session.sessionId,
...(session.captureLimits === undefined
? {}
: { capture_limits: session.captureLimits }),
device_id: session.deviceId,
target: session.target,
pid: session.pid,
+14 -7
View File
@@ -1,3 +1,4 @@
import type { EvidenceLookup } from "../application/EvidenceInputResolver.js";
import type { EvidenceMcpServer } from "./EvidenceMcpServer.js";
import type { WebSourceLocationService } from "../application/WebSourceLocationService.js";
@@ -5,28 +6,34 @@ import type { EvidenceWriter } from "../application/investigation/InvestigationR
import { toolContract } from "../contracts/toolContracts.js";
import type { Logger } from "pino";
import type { WithAdmittedAnalysis } from "./analysisAdmission.js";
import { createEvidenceToolHandler } from "./contractToolHandler.js";
import { createRetainedSummaryToolHandler } from "./contractToolHandler.js";
import { applicationToolRegistrationOptions } from "./toolRegistrationOptions.js";
/** Bind source point tracing to its exact named contract and session Evidence owner. */
export const registerWebSourceLocationTool = (
server: EvidenceMcpServer,
service: WebSourceLocationService,
logger: Logger,
recordEvidence?: EvidenceWriter["recordEvidence"],
withAdmittedAnalysis?: WithAdmittedAnalysis,
options: {
readonly logger: Logger;
readonly recordEvidence: EvidenceWriter["recordEvidence"] | undefined;
readonly withAdmittedAnalysis: WithAdmittedAnalysis | undefined;
readonly evidenceById: EvidenceLookup | undefined;
},
): void => {
const { logger, recordEvidence, withAdmittedAnalysis, evidenceById } =
options;
const contract = toolContract("trace_web_source_location");
const handler = createEvidenceToolHandler(
const handler = createRetainedSummaryToolHandler(
server,
contract,
(input, options) => service.trace(input, options),
logger,
recordEvidence,
{ recordEvidence, evidenceById },
withAdmittedAnalysis,
);
server.registerTool(
contract.name,
applicationToolRegistrationOptions(contract),
handler(contract),
handler,
);
};
+26 -24
View File
@@ -78,27 +78,29 @@ const retainedEvidenceContinuation = (
/** The first page an owner offers when its complete retained result does not fit. */
const ownerFirstPage = (evidence: Evidence): JsonValue | undefined =>
evidence.operation === "analyze_function" ||
evidence.operation === "procedure_pseudo_code"
? { kind: "native", facet: "pseudocode", offset: 0 }
: evidence.operation === "inspect_native_instruction"
? {
kind: "native-instruction-references",
collection: "incoming_references",
offset: 0,
limit: 64,
}
: evidence.operation === "trace_application_feature"
? { kind: "feature-trace", collection: "paths", offset: 0, limit: 64 }
: evidence.operation === "batch_decompile"
? {
kind: "batch-decompile",
collection: "items",
offset: 0,
limit: 64,
}
: isNativeInventoryOperation(evidence.operation)
? { kind: "native-inventory", offset: 0, limit: 64 }
: isNativeObservationOperation(evidence.operation)
? { kind: "native-observation", offset: 0, limit: 64 }
: undefined;
evidence.operation === "trace_web_source_location"
? { kind: "source-location", collection: "matches", offset: 0 }
: evidence.operation === "analyze_function" ||
evidence.operation === "procedure_pseudo_code"
? { kind: "native", facet: "pseudocode", offset: 0 }
: evidence.operation === "inspect_native_instruction"
? {
kind: "native-instruction-references",
collection: "incoming_references",
offset: 0,
limit: 64,
}
: evidence.operation === "trace_application_feature"
? { kind: "feature-trace", collection: "paths", offset: 0, limit: 64 }
: evidence.operation === "batch_decompile"
? {
kind: "batch-decompile",
collection: "items",
offset: 0,
limit: 64,
}
: isNativeInventoryOperation(evidence.operation)
? { kind: "native-inventory", offset: 0, limit: 64 }
: isNativeObservationOperation(evidence.operation)
? { kind: "native-observation", offset: 0, limit: 64 }
: undefined;