690 lines
25 KiB
TypeScript
690 lines
25 KiB
TypeScript
import type { WasmArtifactService } from "../application/wasm/WasmArtifactService.js";
|
|
import { wasmArtifactModule } from "../composition/wasm.js";
|
|
import { registerWasmTools } from "./registerWasmTools.js";
|
|
import { snapshotEnvironment } from "../process/snapshotEnvironment.js";
|
|
import { createPeResourcesService } from "../composition/binaryDiagnostics.js";
|
|
import { registerPeResourcesTool } from "./registerPeResourcesTool.js";
|
|
import type { EvmInterfaceService } from "../application/evm/EvmInterfaceService.js";
|
|
import { createEvmInterfaceService } from "../composition/evm.js";
|
|
import { registerEvmTools } from "./registerEvmTools.js";
|
|
import { registerRecordedCrashTools } from "./registerRecordedCrashTools.js";
|
|
import { createRecordedCrashService } from "../composition/binaryDiagnostics.js";
|
|
import { createGoBinaryService } from "../composition/go.js";
|
|
import type { GoBinaryService } from "../application/go/GoBinaryService.js";
|
|
import { registerGoTools } from "./registerGoTools.js";
|
|
import type { RecordedCrashService } from "../application/binaryDiagnostics/RecordedCrashService.js";
|
|
import { registerAnalysisViewTool } from "./registerAnalysisViewTool.js";
|
|
import { registerBinaryDiagnosticsTools } from "./registerBinaryDiagnosticsTools.js";
|
|
import { FridaInstrumentationManager } from "../frida/FridaInstrumentationManager.js";
|
|
import { registerFridaTools } from "./registerFridaTools.js";
|
|
import { createBinaryLayoutService } from "../composition/binaryDiagnostics.js";
|
|
import type { BinaryLayoutService } from "../application/binaryDiagnostics/BinaryLayoutService.js";
|
|
import { EvidenceMcpServer } from "./EvidenceMcpServer.js";
|
|
import {
|
|
createToolResultDelivery,
|
|
type ToolResultDelivery,
|
|
} from "./toolResult.js";
|
|
import { parseMcpResponseBudget } from "../config/mcpResponseBudget.js";
|
|
import {
|
|
COMPACT_INPUT_SCHEMA_BUDGET_BYTES,
|
|
parseMcpInputSchemaProfile,
|
|
} from "../config/mcpInputSchemaProfile.js";
|
|
import type { CompactInputSchemaPresentation } from "./EvidenceMcpServer.js";
|
|
import { isAbsolute } from "node:path";
|
|
|
|
import type { BinarySessionPort } from "../application/binary/BinarySessionPort.js";
|
|
import type { BrowserObservationPort } from "../application/BrowserObservationPort.js";
|
|
import type { BrowserScenarioCapturePort } from "../application/BrowserScenarioCapturePort.js";
|
|
import type { ElectronActiveObservationPort } from "../application/javascript/ElectronActiveObservationPort.js";
|
|
import type { ElectronObservationPort } from "../application/javascript/ElectronObservationPort.js";
|
|
import type { JavaScriptRuntimeObservationPort } from "../application/javascript/JavaScriptRuntimeObservationPort.js";
|
|
import type { FridaInstrumentationPort } from "../application/frida/FridaInstrumentationPort.js";
|
|
import type { OptionalProviderLoadFailures } from "../application/OptionalObservationProviders.js";
|
|
import { PRODUCT_IDENTITY } from "../identity.js";
|
|
import { silentLogger } from "../logger.js";
|
|
import type { Logger } from "pino";
|
|
import { registerApplicationTools } from "./registerApplicationTools.js";
|
|
import { registerArtifactTools } from "./registerArtifactTools.js";
|
|
import { registerBrowserScenarioTool } from "./registerBrowserScenarioTool.js";
|
|
import { registerBrowserTools } from "./registerBrowserTools.js";
|
|
import { registerWebScriptTool } from "./registerWebScriptTool.js";
|
|
import { registerWebModuleTool } from "./registerWebModuleTool.js";
|
|
import type { WebModuleTraceService } from "../application/WebModuleTraceService.js";
|
|
import { createWebModuleTraceService } from "../composition/webModules.js";
|
|
import { createWebSourceLocationService } from "../composition/webSourceLocations.js";
|
|
import { registerWebSourceLocationTool } from "./registerWebSourceLocationTool.js";
|
|
import type { WebSourceLocationService } from "../application/WebSourceLocationService.js";
|
|
import type { WebRuntimeService } from "../application/WebRuntimeService.js";
|
|
import { createWebRuntimeService } from "../composition/webRuntime.js";
|
|
import { registerWebRuntimeTools } from "./registerWebRuntimeTools.js";
|
|
import type { WebNetworkCaptureService } from "../application/WebNetworkCaptureService.js";
|
|
import { createWebNetworkCaptureService } from "../composition/webNetworkCaptures.js";
|
|
import { registerWebNetworkCaptureTool } from "./registerWebNetworkCaptureTool.js";
|
|
import { registerJavaScriptRecoveryTool } from "./registerJavaScriptRecoveryTool.js";
|
|
import { JavaScriptRecoveryService } from "../application/javascript/JavaScriptRecoveryService.js";
|
|
import type { JavaScriptRecoveryPort } from "../application/javascript/JavaScriptRecoveryPort.js";
|
|
import { createJavaScriptRecoveryProvider } from "../composition/javascriptRecovery.js";
|
|
import { registerElectronTools } from "./registerElectronTools.js";
|
|
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
|
import { registerEnhancedTools } from "./registerEnhancedTools.js";
|
|
import { registerJavaScriptRuntimeObservationTools } from "./registerJavaScriptRuntimeObservationTools.js";
|
|
import { registerManagedTools } from "./registerManagedTools.js";
|
|
import { registerFirmwareTools } from "./registerFirmwareTools.js";
|
|
import { FirmwareAnalysisService } from "../application/firmware/FirmwareAnalysisService.js";
|
|
import type { FirmwareAnalysisPort } from "../application/firmware/FirmwareAnalysisPort.js";
|
|
import { createFirmwareAnalysisProvider } from "../composition/firmware.js";
|
|
import { registerAndroidTools } from "./registerAndroidTools.js";
|
|
import { registerApktoolTools } from "./registerApktoolTools.js";
|
|
import { registerJebTools } from "./registerJebTools.js";
|
|
import { registerAdbTools } from "./registerAdbTools.js";
|
|
import { AndroidAnalysisService } from "../application/android/AndroidAnalysisService.js";
|
|
import { JebAnalysisService } from "../application/jeb/JebAnalysisService.js";
|
|
import type { AndroidAnalysisPort } from "../application/android/AndroidAnalysisPort.js";
|
|
import type { ApktoolResourceAnalysisPort } from "../application/apktool/ApktoolResourceAnalysisPort.js";
|
|
import { ApktoolResourceAnalysisService } from "../application/apktool/ApktoolResourceAnalysisService.js";
|
|
import { createAndroidAnalysisProvider } from "../composition/android.js";
|
|
import { createApktoolResourceAnalysisProvider } from "../composition/apktool.js";
|
|
import type { JebAnalysisPort } from "../application/jeb/JebAnalysisPort.js";
|
|
import { createJebAnalysisProvider } from "../composition/jeb.js";
|
|
import type { AdbDeviceAnalysisPort } from "../application/adb/AdbDeviceAnalysisPort.js";
|
|
import { AdbDeviceAnalysisService } from "../application/adb/AdbDeviceAnalysisService.js";
|
|
import { createAdbDeviceAnalysisProvider } from "../composition/adb.js";
|
|
import { registerManagedWorkflowTools } from "./registerManagedWorkflowTools.js";
|
|
import { NATIVE_TOOL_CONTRACTS } from "../contracts/native/nativeToolContracts.js";
|
|
import { registerEvidenceTools } from "./registerEvidenceTools.js";
|
|
import { registerOfficialTools } from "./registerOfficialTools.js";
|
|
import { registerGuidedPrompts } from "./registerPrompts.js";
|
|
import { registerSessionTools } from "./registerSessionTools.js";
|
|
import type { AvailabilityPolicy } from "../application/CapabilityInventory.js";
|
|
import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js";
|
|
import {
|
|
withAdmittedAnalysis,
|
|
type ServerAnalysisSource,
|
|
} from "./analysisAdmission.js";
|
|
import type { WithAdmittedAnalysis } from "./analysisAdmission.js";
|
|
|
|
const TARGET_FREE_INSTRUCTIONS =
|
|
"REA provides reverse-engineering tools for local artifacts, native binaries, managed code, browser pages, and runtimes. Use the tool that directly answers the question; discover targets or inspect inventory only when needed. Tool results include inline Evidence and report their coverage and limitations.";
|
|
|
|
const ACTIVE_TARGET_INSTRUCTIONS =
|
|
"REA analyzes the active reverse-engineering target. Use the analysis tool that answers the question directly. Search or list symbols when discovery is needed; analyze_function provides a function dossier, and focused procedure tools return individual facets.";
|
|
|
|
export interface CreateServerOptions {
|
|
readonly environment?: Readonly<NodeJS.ProcessEnv>;
|
|
readonly delivery?: ToolResultDelivery;
|
|
readonly wasmArtifact?: WasmArtifactService;
|
|
readonly evmInterface?: EvmInterfaceService;
|
|
readonly logger?: Logger;
|
|
readonly binaryLayout?: BinaryLayoutService;
|
|
readonly goBinary?: GoBinaryService;
|
|
readonly recordedCrash?: RecordedCrashService;
|
|
readonly firmwareAnalysis?: FirmwareAnalysisPort;
|
|
readonly javascriptRecovery?: JavaScriptRecoveryPort;
|
|
readonly webModuleTrace?: WebModuleTraceService;
|
|
readonly webSourceLocation?: WebSourceLocationService;
|
|
readonly webRuntime?: WebRuntimeService;
|
|
readonly webNetworkCapture?: WebNetworkCaptureService;
|
|
readonly androidAnalysis?: AndroidAnalysisPort;
|
|
readonly apktoolAnalysis?: ApktoolResourceAnalysisPort;
|
|
readonly jebAnalysis?: JebAnalysisPort;
|
|
readonly adbDeviceAnalysis?: AdbDeviceAnalysisPort;
|
|
readonly browserObservation?: BrowserObservationPort;
|
|
readonly browserScenarioCapture?: BrowserScenarioCapturePort;
|
|
readonly electronObservation?: ElectronObservationPort;
|
|
readonly electronActiveObservation?: ElectronActiveObservationPort;
|
|
readonly javascriptRuntimeObservation?: JavaScriptRuntimeObservationPort;
|
|
readonly fridaInstrumentation?: FridaInstrumentationPort;
|
|
readonly availabilityPolicy?: () => AvailabilityPolicy;
|
|
readonly optionalProviderLoadFailures?: OptionalProviderLoadFailures;
|
|
}
|
|
|
|
const installSessionToolAvailability = (
|
|
session: BinarySessionPort | undefined,
|
|
options: CreateServerOptions,
|
|
environment: Readonly<NodeJS.ProcessEnv>,
|
|
fridaInstrumentation: FridaInstrumentationPort,
|
|
) => {
|
|
if (session === undefined) return undefined;
|
|
const linux = process.platform === "linux";
|
|
const linuxX64 = linux && process.arch === "x64";
|
|
const pwntools =
|
|
linuxX64 && isAbsolute(environment.REA_PWNTOOLS_PYTHON ?? "");
|
|
const basePolicy = sessionAvailabilityPolicy(options.availabilityPolicy, {
|
|
optionalProviderLoadFailures: options.optionalProviderLoadFailures,
|
|
optionalFeatures: {
|
|
wasmArtifactEnabled:
|
|
options.wasmArtifact !== undefined ||
|
|
isAbsolute(environment.REA_WABT_BIN_DIRECTORY ?? ""),
|
|
evmInterfaceEnabled: options.evmInterface !== undefined || linuxX64,
|
|
webModuleResolutionEnabled:
|
|
options.webModuleTrace !== undefined ||
|
|
isAbsolute(environment.REA_BROWSER_EXECUTABLE ?? ""),
|
|
recordedCrashEnabled: options.recordedCrash !== undefined || pwntools,
|
|
binaryLayoutEnabled: options.binaryLayout !== undefined || pwntools,
|
|
firmwareInspectionEnabled:
|
|
options.firmwareAnalysis !== undefined ||
|
|
(linux && isAbsolute(environment.REA_BINWALK_COMMAND ?? "")),
|
|
firmwareExtractionEnabled:
|
|
options.firmwareAnalysis !== undefined ||
|
|
(linux && isAbsolute(environment.REA_UNBLOB_COMMAND ?? "")),
|
|
browserObservationEnabled: options.browserObservation !== undefined,
|
|
javascriptRecoveryEnabled:
|
|
options.javascriptRecovery !== undefined ||
|
|
(linuxX64 && isAbsolute(environment.REA_WAKARU_COMMAND ?? "")),
|
|
browserScenarioEnabled: options.browserScenarioCapture !== undefined,
|
|
electronObservationEnabled: options.electronObservation !== undefined,
|
|
electronAutomationEnabled:
|
|
options.electronActiveObservation !== undefined,
|
|
v8InspectorObservationEnabled:
|
|
options.javascriptRuntimeObservation !== undefined,
|
|
},
|
|
});
|
|
const policy = (): AvailabilityPolicy => {
|
|
const value = basePolicy();
|
|
const fridaFailure =
|
|
options.optionalProviderLoadFailures?.fridaInstrumentation ??
|
|
fridaInstrumentation.availabilityFailure?.();
|
|
return fridaFailure === undefined
|
|
? value
|
|
: {
|
|
...value,
|
|
optionalProviderLoadFailures: {
|
|
...value.optionalProviderLoadFailures,
|
|
fridaInstrumentation: fridaFailure,
|
|
},
|
|
};
|
|
};
|
|
return {
|
|
policy,
|
|
};
|
|
};
|
|
|
|
const createMcpServer = (
|
|
session: BinarySessionPort | undefined,
|
|
delivery: ToolResultDelivery,
|
|
compactPresentation: CompactInputSchemaPresentation | undefined,
|
|
): EvidenceMcpServer =>
|
|
new EvidenceMcpServer(
|
|
{
|
|
name: PRODUCT_IDENTITY.mcpServerKey,
|
|
version: PRODUCT_IDENTITY.packageVersion,
|
|
},
|
|
{
|
|
capabilities: {},
|
|
instructions:
|
|
session === undefined
|
|
? ACTIVE_TARGET_INSTRUCTIONS
|
|
: TARGET_FREE_INSTRUCTIONS,
|
|
},
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
delivery,
|
|
compactPresentation,
|
|
);
|
|
|
|
/**
|
|
* Construct one MCP server without acquiring subprocess resources.
|
|
* Supplying a session adds target lifecycle tools; omitting it retains the
|
|
* fixed-target seam used by focused tests and embedders.
|
|
*/
|
|
export const createServer = (
|
|
source: ServerAnalysisSource,
|
|
options: CreateServerOptions = {},
|
|
): EvidenceMcpServer => {
|
|
const session = source.kind === "session" ? source.session : undefined;
|
|
const analysis = source.kind === "session" ? source.session : source.analysis;
|
|
const environment = snapshotEnvironment(options.environment ?? process.env);
|
|
const delivery = selectToolResultDelivery(environment, options.delivery);
|
|
const evmInterface =
|
|
options.evmInterface ?? createEvmInterfaceService(environment);
|
|
const webSourceLocation =
|
|
options.webSourceLocation ?? createWebSourceLocationService(environment);
|
|
const webModuleTrace =
|
|
options.webModuleTrace ?? createWebModuleTraceService(environment);
|
|
const javascriptRecovery =
|
|
options.javascriptRecovery ?? createJavaScriptRecoveryProvider(environment);
|
|
const artifactResources = new ArtifactResourceScope();
|
|
const firmwareAnalysis = new FirmwareAnalysisService(
|
|
options.firmwareAnalysis ?? createFirmwareAnalysisProvider(environment),
|
|
);
|
|
const selectedOptions = {
|
|
...options,
|
|
environment,
|
|
delivery,
|
|
evmInterface,
|
|
webSourceLocation,
|
|
webModuleTrace,
|
|
javascriptRecovery,
|
|
};
|
|
const startedAt = new Date().toISOString();
|
|
const logger = options.logger ?? silentLogger;
|
|
const server = createMcpServer(
|
|
session,
|
|
delivery,
|
|
selectCompactInputSchemaPresentation(environment),
|
|
);
|
|
const android =
|
|
options.androidAnalysis ?? createAndroidAnalysisProvider(environment);
|
|
const fridaInstrumentation =
|
|
options.fridaInstrumentation ?? new FridaInstrumentationManager();
|
|
const wasmArtifact = wasmArtifactModule.create(
|
|
environment,
|
|
options.wasmArtifact,
|
|
);
|
|
const apktool =
|
|
options.apktoolAnalysis ??
|
|
createApktoolResourceAnalysisProvider(environment);
|
|
const jeb = options.jebAnalysis ?? createJebAnalysisProvider(environment);
|
|
const adbDevice =
|
|
options.adbDeviceAnalysis ?? createAdbDeviceAnalysisProvider(environment);
|
|
let providerClose: Promise<void> | undefined;
|
|
const closeProviders = (): Promise<void> => {
|
|
providerClose ??= Promise.allSettled([
|
|
android.close(),
|
|
fridaInstrumentation.closeAll(),
|
|
wasmArtifact.close(),
|
|
apktool.close(),
|
|
jeb.close(),
|
|
adbDevice.close(),
|
|
evmInterface.close(),
|
|
webSourceLocation.close(),
|
|
webModuleTrace.close(),
|
|
javascriptRecovery.close?.() ?? Promise.resolve(),
|
|
artifactResources.close(),
|
|
firmwareAnalysis.close(),
|
|
])
|
|
.then((results) => {
|
|
const failures = results.flatMap((result) =>
|
|
result.status === "rejected" ? [result.reason] : [],
|
|
);
|
|
if (failures.length > 0)
|
|
throw new AggregateError(failures, "REA provider cleanup failed");
|
|
})
|
|
.catch((cause: unknown) => {
|
|
providerClose = undefined;
|
|
throw cause;
|
|
});
|
|
return providerClose;
|
|
};
|
|
const availability = installSessionToolAvailability(
|
|
session,
|
|
options,
|
|
environment,
|
|
fridaInstrumentation,
|
|
);
|
|
const toolLogger = logger.child({ layer: "server" });
|
|
const {
|
|
evidenceById,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
} = createSessionRecorders(server, session);
|
|
const analysisAdmission = withAdmittedAnalysis(source);
|
|
const toolContext: ServerToolContext = {
|
|
server,
|
|
session,
|
|
options: selectedOptions,
|
|
environment,
|
|
logger: toolLogger,
|
|
evidenceById,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
withAdmittedAnalysis: analysisAdmission,
|
|
artifactResources,
|
|
};
|
|
registerBinaryAnalysisTools(toolContext);
|
|
registerFridaTools(
|
|
server,
|
|
fridaInstrumentation,
|
|
toolLogger,
|
|
delivery,
|
|
session === undefined
|
|
? undefined
|
|
: (input) => session.recordEvidence(input),
|
|
);
|
|
const previousOnclose = server.server.onclose;
|
|
server.server.onclose = () => {
|
|
previousOnclose?.();
|
|
void closeProviders().catch((cause: unknown) => {
|
|
logger.error(
|
|
{ error: cause instanceof Error ? cause.message : String(cause) },
|
|
"Provider cleanup failed during server shutdown",
|
|
);
|
|
});
|
|
};
|
|
const closeServer = server.close.bind(server);
|
|
server.close = async () => {
|
|
const results = await Promise.allSettled([closeServer(), closeProviders()]);
|
|
for (const result of results)
|
|
if (result.status === "rejected") throw result.reason;
|
|
};
|
|
registerConfiguredAnalysisTools(
|
|
toolContext,
|
|
android,
|
|
wasmArtifact,
|
|
jeb,
|
|
firmwareAnalysis,
|
|
);
|
|
registerAdbTools(
|
|
server,
|
|
new AdbDeviceAnalysisService(adbDevice),
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
);
|
|
registerApktoolTools(
|
|
server,
|
|
new ApktoolResourceAnalysisService(apktool),
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
);
|
|
registerObservationTools(toolContext);
|
|
registerGuidedPrompts(server, analysis, session);
|
|
if (session !== undefined) {
|
|
registerSessionTools(server, session, toolLogger, {
|
|
...selectedOptions,
|
|
artifactResources,
|
|
...(availability === undefined
|
|
? {}
|
|
: { availabilityPolicy: availability.policy }),
|
|
androidAnalysisAvailability: (signal) =>
|
|
android.inspectAvailability(signal),
|
|
startedAt,
|
|
});
|
|
}
|
|
return server;
|
|
};
|
|
|
|
const selectToolResultDelivery = (
|
|
environment: Readonly<NodeJS.ProcessEnv>,
|
|
selected: ToolResultDelivery | undefined,
|
|
): ToolResultDelivery => {
|
|
if (selected !== undefined) return selected;
|
|
const configured = parseMcpResponseBudget(
|
|
environment.REA_MCP_MAX_RESPONSE_BYTES,
|
|
);
|
|
if (!configured.ok) throw configured.error;
|
|
return createToolResultDelivery(configured.value);
|
|
};
|
|
|
|
const selectCompactInputSchemaPresentation = (
|
|
environment: Readonly<NodeJS.ProcessEnv>,
|
|
): CompactInputSchemaPresentation | undefined => {
|
|
const configured = parseMcpInputSchemaProfile(
|
|
environment.REA_MCP_INPUT_SCHEMA_PROFILE,
|
|
);
|
|
if (!configured.ok) throw configured.error;
|
|
return configured.value === "compact"
|
|
? { budgetBytes: COMPACT_INPUT_SCHEMA_BUDGET_BYTES }
|
|
: undefined;
|
|
};
|
|
|
|
const registerConfiguredAnalysisTools = (
|
|
{
|
|
server,
|
|
options,
|
|
environment,
|
|
logger: toolLogger,
|
|
evidenceById,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
}: ServerToolContext,
|
|
android: AndroidAnalysisPort,
|
|
wasmArtifact: Pick<WasmArtifactService, "inspect" | "close">,
|
|
jeb: JebAnalysisPort,
|
|
firmwareAnalysis: FirmwareAnalysisService,
|
|
): void => {
|
|
registerAndroidTools(
|
|
server,
|
|
new AndroidAnalysisService(android),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerJebTools(
|
|
server,
|
|
new JebAnalysisService(jeb),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerBinaryDiagnosticsTools(
|
|
server,
|
|
options.binaryLayout ?? createBinaryLayoutService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
evidenceById,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerAnalysisViewTool(
|
|
server,
|
|
toolLogger,
|
|
evidenceById,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerPeResourcesTool(
|
|
server,
|
|
createPeResourcesService(),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerGoTools(
|
|
server,
|
|
options.goBinary ?? createGoBinaryService(),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWasmTools(
|
|
server,
|
|
wasmArtifact,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerEvmTools(
|
|
server,
|
|
options.evmInterface ?? createEvmInterfaceService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerRecordedCrashTools(
|
|
server,
|
|
options.recordedCrash ?? createRecordedCrashService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerFirmwareTools(
|
|
server,
|
|
firmwareAnalysis,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebModuleTool(
|
|
server,
|
|
options.webModuleTrace ?? createWebModuleTraceService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebSourceLocationTool(
|
|
server,
|
|
options.webSourceLocation ?? createWebSourceLocationService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebRuntimeTools(
|
|
server,
|
|
options.webRuntime ?? createWebRuntimeService(),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebNetworkCaptureTool(
|
|
server,
|
|
options.webNetworkCapture ?? createWebNetworkCaptureService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerJavaScriptRecoveryTool(
|
|
server,
|
|
new JavaScriptRecoveryService(
|
|
options.javascriptRecovery ??
|
|
createJavaScriptRecoveryProvider(environment),
|
|
),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
};
|
|
|
|
const createSessionRecorders = (
|
|
server: EvidenceMcpServer,
|
|
session: BinarySessionPort | undefined,
|
|
) => ({
|
|
evidenceById:
|
|
session === undefined
|
|
? undefined
|
|
: (evidenceId: string) =>
|
|
session.evidenceForAnalysis?.(evidenceId) ??
|
|
session.evidenceById(evidenceId),
|
|
activeTarget:
|
|
session === undefined ? undefined : () => session.activeTarget(),
|
|
recordEvidence:
|
|
session === undefined
|
|
? undefined
|
|
: (evidence: Parameters<typeof session.recordEvidence>[0]) =>
|
|
session.recordEvidence(evidence),
|
|
recordEvidenceWithUnknown:
|
|
session === undefined
|
|
? undefined
|
|
: (
|
|
evidence: Parameters<typeof session.recordEvidenceWithUnknown>[0],
|
|
input: Parameters<typeof session.recordEvidenceWithUnknown>[1],
|
|
) => session.recordEvidenceWithUnknown(evidence, input),
|
|
});
|
|
|
|
interface ServerToolContext extends ReturnType<typeof createSessionRecorders> {
|
|
readonly artifactResources: ArtifactResourceScope;
|
|
readonly server: EvidenceMcpServer;
|
|
readonly session: BinarySessionPort | undefined;
|
|
readonly options: CreateServerOptions;
|
|
readonly environment: Readonly<NodeJS.ProcessEnv>;
|
|
readonly logger: Logger;
|
|
readonly withAdmittedAnalysis: WithAdmittedAnalysis;
|
|
}
|
|
|
|
const registerBinaryAnalysisTools = ({
|
|
server,
|
|
session,
|
|
logger,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
evidenceById,
|
|
withAdmittedAnalysis,
|
|
}: ServerToolContext): void => {
|
|
const recordUnknown =
|
|
session === undefined
|
|
? undefined
|
|
: (input: Parameters<typeof session.recordUnknown>[0]) =>
|
|
session.recordUnknown(input);
|
|
const analysisOptions = {
|
|
logger,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordUnknown,
|
|
withAdmittedAnalysis,
|
|
};
|
|
registerOfficialTools(server, analysisOptions);
|
|
registerEnhancedTools(server, {
|
|
...analysisOptions,
|
|
analysisProfile:
|
|
session === undefined ? undefined : () => session.analysisProfile(),
|
|
allowsSnapshotReplay:
|
|
session === undefined
|
|
? undefined
|
|
: (operation) => session.allowsSnapshotReplay(operation),
|
|
recordWorkflowSnapshot:
|
|
session === undefined
|
|
? undefined
|
|
: (input) => session.recordWorkflowSnapshot(input),
|
|
});
|
|
const evidenceOptions = {
|
|
logger,
|
|
activeTarget,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
};
|
|
registerEvidenceTools(server, NATIVE_TOOL_CONTRACTS, evidenceOptions);
|
|
registerArtifactTools(server, { ...evidenceOptions, evidenceById });
|
|
registerManagedTools(server, evidenceOptions);
|
|
if (session !== undefined)
|
|
registerManagedWorkflowTools(server, {
|
|
logger,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
session,
|
|
withAdmittedAnalysis,
|
|
});
|
|
};
|
|
|
|
const registerObservationTools = ({
|
|
evidenceById,
|
|
server,
|
|
options,
|
|
logger,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
withAdmittedAnalysis,
|
|
artifactResources,
|
|
}: ServerToolContext): void => {
|
|
const common = { logger, recordEvidence, withAdmittedAnalysis };
|
|
registerWebScriptTool(server, { ...common, artifactResources });
|
|
registerBrowserTools(server, {
|
|
...common,
|
|
browser: options.browserObservation,
|
|
loadFailure: options.optionalProviderLoadFailures?.browserObservation,
|
|
});
|
|
registerBrowserScenarioTool(server, {
|
|
...common,
|
|
provider: options.browserScenarioCapture,
|
|
loadFailure: options.optionalProviderLoadFailures?.browserScenarioCapture,
|
|
});
|
|
registerElectronTools(server, {
|
|
...common,
|
|
artifactResources,
|
|
evidenceById,
|
|
electron: options.electronObservation,
|
|
electronActive: options.electronActiveObservation,
|
|
observationLoadFailure:
|
|
options.optionalProviderLoadFailures?.electronObservation,
|
|
activeLoadFailure:
|
|
options.optionalProviderLoadFailures?.electronActiveObservation,
|
|
});
|
|
registerJavaScriptRuntimeObservationTools(server, {
|
|
...common,
|
|
runtime: options.javascriptRuntimeObservation,
|
|
loadFailure:
|
|
options.optionalProviderLoadFailures?.javascriptRuntimeObservation,
|
|
});
|
|
registerApplicationTools(server, {
|
|
evidenceById,
|
|
logger,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
withAdmittedAnalysis,
|
|
});
|
|
};
|