rea-agents 2.5.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -4
- package/bridge/ghidra/ReaGhidraBridge.java +477 -4
- package/bridge/hopper_bridge.py +54 -4
- package/dist/application/AnalysisContextQueries.js +130 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
- package/dist/application/BinarySession.js +5 -4
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +79 -9
- package/dist/application/CapabilityInventory.js +16 -2
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/DirectAnalysis.js +6 -2
- package/dist/application/EnhancedTools.js +37 -7
- package/dist/application/EvidenceLedger.js +45 -16
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +4 -2
- package/dist/application/runtime.js +26 -9
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +8 -2
- package/dist/catalogIdentity.js +3 -3
- package/dist/cli/artifactCommands.js +5 -3
- package/dist/cli/coreAnalysisCommands.js +41 -0
- package/dist/cliCommandNames.js +1 -0
- package/dist/contracts/artifactToolContracts.js +8 -3
- package/dist/contracts/enhancedInputs.js +10 -0
- package/dist/contracts/functionWorkflowToolContracts.js +30 -0
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/toolContractExamples.js +3 -0
- package/dist/contracts/toolContracts.js +21 -4
- package/dist/contracts/toolEffects.js +4 -0
- package/dist/contracts/toolOutputSchemaGroups.js +50 -10
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +1 -0
- package/dist/domain/bytecodeProvider.js +179 -0
- package/dist/domain/conformancePackage.js +169 -0
- package/dist/domain/conformanceReplay.js +104 -0
- package/dist/domain/conformanceTrustGate.js +193 -0
- package/dist/domain/customProtocolCapture.js +137 -0
- package/dist/domain/eventProcessTree.js +214 -0
- package/dist/domain/evidenceBundle.js +47 -0
- package/dist/domain/hopperStartupFailure.js +1 -1
- package/dist/domain/hopperValues.js +4 -1
- package/dist/domain/inputIssueProjection.js +7 -1
- package/dist/domain/javascriptSemanticAnalysis.js +8 -12
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +8 -12
- package/dist/domain/mobileApplicationInvestigation.js +159 -0
- package/dist/domain/nativeApiBoundary.js +130 -0
- package/dist/domain/objcSwiftMetadata.js +150 -0
- package/dist/domain/packageAnalysis.js +123 -0
- package/dist/domain/peInspection.js +196 -0
- package/dist/domain/protocolCapture.js +243 -0
- package/dist/dotnet/ManagedStaticProvider.js +34 -5
- package/dist/generatedMcpToolCatalog.js +37 -0
- package/dist/generatedPackageMetadata.js +4 -4
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +27 -1
- package/dist/ghidra/GhidraProviderCapabilities.js +40 -38
- package/dist/hopper/HopperClient.js +26 -2
- package/dist/hopper/HopperProvider.js +42 -30
- package/dist/hopper/HopperRequestQueue.js +32 -4
- package/dist/hopper/HopperResponseStream.js +2 -2
- package/dist/hopper/protocol.js +63 -21
- package/dist/main/transport.js +2 -5
- package/dist/main.js +14 -12
- package/dist/mcpDoctor.js +2 -2
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/server/LazyToolCatalog.js +134 -0
- package/dist/server/createServer.js +64 -171
- package/dist/server/hydrateServerTools.js +191 -0
- package/dist/server/registerEnhancedTools.js +13 -3
- package/dist/server/registerEvidenceResources.js +50 -101
- package/dist/server/registerInvestigationTools.js +1 -2
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerReconstructionObligationLedgerResource.js +0 -7
- package/dist/server/registerReconstructionReadinessResource.js +0 -7
- package/dist/server/registerSessionRecordTools.js +82 -12
- package/dist/server/registerSessionStatusTool.js +1 -1
- package/dist/server/registerSessionTools.js +21 -1
- package/dist/server/toolRegistrationOptions.js +21 -6
- package/dist/server/toolResult.js +12 -0
- package/package.json +21 -10
- package/scripts/package-runner-bootstrap.mjs +72 -0
- package/scripts/rea.mjs +11 -1
- package/skills/reverse-engineer-anything/SKILL.md +10 -3
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { fromJsonSchema, isCallToolResult, isInputRequiredResult, } from "@modelcontextprotocol/server";
|
|
2
|
+
import { GENERATED_MCP_TOOL_CATALOG } from "../generatedMcpToolCatalog.js";
|
|
3
|
+
/**
|
|
4
|
+
* Advertise the generated MCP catalog immediately and hydrate its callbacks
|
|
5
|
+
* through the existing registrars on first execution.
|
|
6
|
+
*/
|
|
7
|
+
export class LazyToolCatalog {
|
|
8
|
+
#server;
|
|
9
|
+
#hydrateRegistrations;
|
|
10
|
+
#includeSessionTools;
|
|
11
|
+
#tools = new Map();
|
|
12
|
+
#hydrations = new Map();
|
|
13
|
+
#hydrationQueue = Promise.resolve();
|
|
14
|
+
#closed = false;
|
|
15
|
+
constructor(server, hydrateRegistrations, includeSessionTools = true) {
|
|
16
|
+
this.#server = server;
|
|
17
|
+
this.#hydrateRegistrations = hydrateRegistrations;
|
|
18
|
+
this.#includeSessionTools = includeSessionTools;
|
|
19
|
+
}
|
|
20
|
+
/** Register every generated tool with a lazy callback. */
|
|
21
|
+
register() {
|
|
22
|
+
for (const contract of GENERATED_MCP_TOOL_CATALOG) {
|
|
23
|
+
if (contract.requiresSession && !this.#includeSessionTools)
|
|
24
|
+
continue;
|
|
25
|
+
const inputSchema = advertisedSchema(contract.inputSchema);
|
|
26
|
+
const outputSchema = advertisedSchema(contract.outputSchema);
|
|
27
|
+
let registered;
|
|
28
|
+
const callback = async (input, context) => {
|
|
29
|
+
await this.#hydrate(contract.kind);
|
|
30
|
+
const handler = registered?.handler;
|
|
31
|
+
if (handler === undefined || handler === callback)
|
|
32
|
+
throw new Error(`MCP tool ${contract.name} was not hydrated`);
|
|
33
|
+
const result = await Reflect.apply(handler, undefined, [
|
|
34
|
+
input,
|
|
35
|
+
context,
|
|
36
|
+
]);
|
|
37
|
+
if (isCallToolResult(result) || isInputRequiredResult(result))
|
|
38
|
+
return result;
|
|
39
|
+
throw new TypeError(`MCP tool ${contract.name} returned an invalid result`);
|
|
40
|
+
};
|
|
41
|
+
registered = this.#server.registerTool(contract.name, {
|
|
42
|
+
title: contract.title,
|
|
43
|
+
description: contract.description,
|
|
44
|
+
inputSchema,
|
|
45
|
+
outputSchema,
|
|
46
|
+
annotations: contract.annotations,
|
|
47
|
+
}, callback);
|
|
48
|
+
this.#tools.set(contract.name, registered);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Wait for in-flight hydration and reject future first-use loading. */
|
|
52
|
+
async close() {
|
|
53
|
+
this.#closed = true;
|
|
54
|
+
await Promise.allSettled(this.#hydrations.values());
|
|
55
|
+
}
|
|
56
|
+
async #hydrate(kind) {
|
|
57
|
+
if (this.#closed)
|
|
58
|
+
throw new Error("MCP tool catalog is closed");
|
|
59
|
+
const existing = this.#hydrations.get(kind);
|
|
60
|
+
if (existing !== undefined) {
|
|
61
|
+
await existing;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const hydration = this.#hydrationQueue
|
|
65
|
+
.catch(() => undefined)
|
|
66
|
+
.then(() => this.#runHydration(kind));
|
|
67
|
+
this.#hydrationQueue = hydration;
|
|
68
|
+
this.#hydrations.set(kind, hydration);
|
|
69
|
+
try {
|
|
70
|
+
await hydration;
|
|
71
|
+
}
|
|
72
|
+
catch (cause) {
|
|
73
|
+
if (this.#hydrations.get(kind) === hydration)
|
|
74
|
+
this.#hydrations.delete(kind);
|
|
75
|
+
throw cause;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async #runHydration(kind) {
|
|
79
|
+
if (this.#closed)
|
|
80
|
+
throw new Error("MCP tool catalog is closed");
|
|
81
|
+
const expected = GENERATED_MCP_TOOL_CATALOG.filter((contract) => contract.kind === kind &&
|
|
82
|
+
(this.#includeSessionTools || !contract.requiresSession)).map((contract) => contract.name);
|
|
83
|
+
const expectedNames = new Set(expected);
|
|
84
|
+
const hydrated = new Set();
|
|
85
|
+
const originalRegisterTool = this.#server.registerTool;
|
|
86
|
+
const hydrationRegisterTool = (...args) => {
|
|
87
|
+
const [name, config, callback] = args;
|
|
88
|
+
if (typeof name !== "string" ||
|
|
89
|
+
!isRecord(config) ||
|
|
90
|
+
typeof callback !== "function")
|
|
91
|
+
throw new TypeError("Invalid lazy MCP tool registration");
|
|
92
|
+
const registered = this.#tools.get(name);
|
|
93
|
+
if (registered === undefined || !expectedNames.has(name))
|
|
94
|
+
throw new TypeError(`MCP tool ${name} is not in the ${kind} catalog family`);
|
|
95
|
+
if (hydrated.has(name))
|
|
96
|
+
throw new TypeError(`MCP tool ${name} was hydrated more than once`);
|
|
97
|
+
hydrated.add(name);
|
|
98
|
+
Reflect.apply(registered.update, registered, [
|
|
99
|
+
{
|
|
100
|
+
title: config.title,
|
|
101
|
+
description: config.description,
|
|
102
|
+
paramsSchema: config.inputSchema,
|
|
103
|
+
outputSchema: config.outputSchema,
|
|
104
|
+
annotations: config.annotations,
|
|
105
|
+
callback,
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
return registered;
|
|
109
|
+
};
|
|
110
|
+
if (!Reflect.set(this.#server, "registerTool", hydrationRegisterTool))
|
|
111
|
+
throw new TypeError("Unable to install lazy MCP tool hydration");
|
|
112
|
+
try {
|
|
113
|
+
await this.#hydrateRegistrations(kind);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
Reflect.set(this.#server, "registerTool", originalRegisterTool);
|
|
117
|
+
}
|
|
118
|
+
const missing = expected.filter((name) => !hydrated.has(name));
|
|
119
|
+
if (missing.length > 0)
|
|
120
|
+
throw new TypeError(`Generated MCP tools were not hydrated: ${missing.join(", ")}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const advertisedSchema = (jsonSchema) => fromJsonSchema(jsonSchema, DEFERRED_VALIDATION_PROVIDER);
|
|
124
|
+
class DeferredValidationProvider {
|
|
125
|
+
getValidator() {
|
|
126
|
+
return (input) => ({
|
|
127
|
+
valid: true,
|
|
128
|
+
data: input,
|
|
129
|
+
errorMessage: undefined,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const DEFERRED_VALIDATION_PROVIDER = new DeferredValidationProvider();
|
|
134
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1,30 +1,16 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { CLIENT_CAPABILITIES_META_KEY, createRequestStateCodec, McpServer, PROTOCOL_VERSION_META_KEY, } from "@modelcontextprotocol/server";
|
|
3
3
|
import { PRODUCT_IDENTITY } from "../identity.js";
|
|
4
|
-
import { registerEnhancedTools } from "./registerEnhancedTools.js";
|
|
5
|
-
import { registerOfficialTools } from "./registerOfficialTools.js";
|
|
6
|
-
import { registerSessionTools } from "./registerSessionTools.js";
|
|
7
|
-
import { registerNativeTools } from "./registerNativeTools.js";
|
|
8
|
-
import { registerArtifactTools } from "./registerArtifactTools.js";
|
|
9
|
-
import { registerManagedTools } from "./registerManagedTools.js";
|
|
10
|
-
import { registerManagedWorkflowTools } from "./registerManagedWorkflowTools.js";
|
|
11
4
|
import { silentLogger } from "../logger.js";
|
|
12
5
|
import { registerGuidedPrompts } from "./registerPrompts.js";
|
|
13
6
|
import { registerEvidenceResources } from "./registerEvidenceResources.js";
|
|
14
|
-
import { createServerIdentity } from "../serverIdentity.js";
|
|
15
|
-
import { registerBrowserTools } from "./registerBrowserTools.js";
|
|
16
|
-
import { registerBrowserScenarioTool } from "./registerBrowserScenarioTool.js";
|
|
17
|
-
import { registerElectronTools } from "./registerElectronTools.js";
|
|
18
|
-
import { registerJavaScriptRuntimeObservationTools } from "./registerJavaScriptRuntimeObservationTools.js";
|
|
19
|
-
import { registerApplicationTools } from "./registerApplicationTools.js";
|
|
20
7
|
import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js";
|
|
21
8
|
import { DENY_EVIDENCE_FILE_POLICY, DENY_PROCESS_POLICY, } from "./sessionToolPolicies.js";
|
|
22
9
|
import { installDynamicToolAvailability } from "./DynamicToolAvailability.js";
|
|
23
|
-
const TARGET_FREE_INSTRUCTIONS = "
|
|
10
|
+
const TARGET_FREE_INSTRUCTIONS = "ASAR/JavaScript -> analyze_javascript_application; archive/package -> open_binary(path), then inspect_artifact/inventory_artifact (active target); managed PE/CLI -> inspect_managed_artifact; passive browser/Electron -> list_browser_targets/list_electron_targets; approved browser interaction -> capture_browser_scenario; Node/Electron Inspector -> list_javascript_runtime_targets; native binary/database -> open_binary, then binary_overview. Start with binary_session; call only tools in tools/list. Hidden routes: binary_session detail=capabilities. Use summaries, cite Evidence IDs. Never repeat identical analysis or read full Evidence.";
|
|
24
11
|
const ACTIVE_TARGET_INSTRUCTIONS = "REA analyzes the active reverse-engineering target. Start native analysis with binary_overview, then narrow with analyze_function, literal search, callers, callees, and xrefs. Prefer summary views, never repeat an identical call, and read full Evidence only when the task requires it.";
|
|
25
|
-
import { LinuxJavaScriptReplayRunner } from "../replay/LinuxJavaScriptReplayRunner.js";
|
|
26
|
-
import { SystemJavaScriptReplayHost } from "../replay/SystemJavaScriptReplayHost.js";
|
|
27
12
|
import { PROCESS_CAPTURE_ELICITATION_POLICY, } from "./ProcessCaptureElicitation.js";
|
|
13
|
+
import { LazyToolCatalog } from "./LazyToolCatalog.js";
|
|
28
14
|
const installSessionToolAvailability = (server, session, options) => {
|
|
29
15
|
if (session === undefined)
|
|
30
16
|
return undefined;
|
|
@@ -65,7 +51,7 @@ export const createServer = (analysis, session, options = {}) => {
|
|
|
65
51
|
}, {
|
|
66
52
|
capabilities: {
|
|
67
53
|
tools: { listChanged: true },
|
|
68
|
-
resources: { listChanged: true },
|
|
54
|
+
resources: { listChanged: true, subscribe: true },
|
|
69
55
|
},
|
|
70
56
|
inputRequired: {
|
|
71
57
|
maxRounds: 3,
|
|
@@ -86,47 +72,71 @@ export const createServer = (analysis, session, options = {}) => {
|
|
|
86
72
|
registerServerIdentityResource(server, startedAt);
|
|
87
73
|
const toolLogger = logger.child({ layer: "server" });
|
|
88
74
|
const { activeTarget, recordEvidence, recordEvidenceWithUnknown } = createSessionRecorders(server, session);
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
75
|
+
const processCaptureElicitation = {
|
|
76
|
+
stateCodec: processCaptureStateCodec,
|
|
77
|
+
supported: (context) => {
|
|
78
|
+
const envelope = context.mcpReq.envelope;
|
|
79
|
+
const version = envelope?.[PROTOCOL_VERSION_META_KEY];
|
|
80
|
+
const capabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY];
|
|
81
|
+
return (typeof version === "string" &&
|
|
82
|
+
PROCESS_CAPTURE_ELICITATION_POLICY.protocolVersions.some((supported) => supported === version) &&
|
|
83
|
+
isRecord(capabilities) &&
|
|
84
|
+
isRecord(capabilities.elicitation) &&
|
|
85
|
+
capabilities.elicitation.form !== undefined);
|
|
86
|
+
},
|
|
87
|
+
now: Date.now,
|
|
88
|
+
consumedNonces: new Map(),
|
|
89
|
+
};
|
|
90
|
+
let optionalProviders;
|
|
91
|
+
const loadOptionalProviders = () => {
|
|
92
|
+
optionalProviders ??= options
|
|
93
|
+
.loadOptionalProviders?.()
|
|
94
|
+
.catch((cause) => {
|
|
95
|
+
optionalProviders = undefined;
|
|
96
|
+
throw cause;
|
|
97
|
+
});
|
|
98
|
+
return optionalProviders;
|
|
99
99
|
};
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const lazyTools = new LazyToolCatalog(server, async (kind) => {
|
|
101
|
+
const needsObservationProviders = kind === "browser-provider" ||
|
|
102
|
+
kind === "electron-provider" ||
|
|
103
|
+
kind === "runtime-provider";
|
|
104
|
+
const [{ hydrateServerToolFamily }, loadedOptionalProviders] = await Promise.all([
|
|
105
|
+
import("./hydrateServerTools.js"),
|
|
106
|
+
needsObservationProviders ? loadOptionalProviders() : undefined,
|
|
107
|
+
]);
|
|
108
|
+
const hydratedOptions = loadedOptionalProviders === undefined
|
|
109
|
+
? options
|
|
110
|
+
: { ...options, ...loadedOptionalProviders };
|
|
111
|
+
await hydrateServerToolFamily({
|
|
112
|
+
kind,
|
|
113
|
+
server,
|
|
114
|
+
analysis,
|
|
115
|
+
session,
|
|
116
|
+
options: hydratedOptions,
|
|
117
|
+
context: {
|
|
118
|
+
logger: toolLogger,
|
|
119
|
+
permissionAuthority,
|
|
120
|
+
activeTarget,
|
|
121
|
+
recordEvidence,
|
|
122
|
+
recordEvidenceWithUnknown,
|
|
123
|
+
availabilityPolicy: dynamicTools?.policy,
|
|
124
|
+
startedAt,
|
|
125
|
+
processCaptureElicitation,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}, session !== undefined);
|
|
129
|
+
lazyTools.register();
|
|
102
130
|
registerGuidedPrompts(server, analysis, session);
|
|
103
131
|
if (session !== undefined) {
|
|
104
132
|
registerEvidenceResources(server, session);
|
|
105
|
-
registerSessionTools(server, session, toolLogger, {
|
|
106
|
-
...options,
|
|
107
|
-
...(dynamicTools === undefined
|
|
108
|
-
? {}
|
|
109
|
-
: { availabilityPolicy: dynamicTools.policy }),
|
|
110
|
-
...(permissionAuthority === undefined ? {} : { permissionAuthority }),
|
|
111
|
-
startedAt,
|
|
112
|
-
processCaptureElicitation: {
|
|
113
|
-
stateCodec: processCaptureStateCodec,
|
|
114
|
-
supported: (context) => {
|
|
115
|
-
const envelope = context.mcpReq.envelope;
|
|
116
|
-
const version = envelope?.[PROTOCOL_VERSION_META_KEY];
|
|
117
|
-
const capabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY];
|
|
118
|
-
return (typeof version === "string" &&
|
|
119
|
-
PROCESS_CAPTURE_ELICITATION_POLICY.protocolVersions.some((supported) => supported === version) &&
|
|
120
|
-
isRecord(capabilities) &&
|
|
121
|
-
isRecord(capabilities.elicitation) &&
|
|
122
|
-
capabilities.elicitation.form !== undefined);
|
|
123
|
-
},
|
|
124
|
-
now: Date.now,
|
|
125
|
-
consumedNonces: new Map(),
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
133
|
}
|
|
129
134
|
dynamicTools?.controller.synchronize();
|
|
135
|
+
const close = server.close.bind(server);
|
|
136
|
+
server.close = async () => {
|
|
137
|
+
await lazyTools.close();
|
|
138
|
+
await close();
|
|
139
|
+
};
|
|
130
140
|
return server;
|
|
131
141
|
};
|
|
132
142
|
const createSessionRecorders = (server, session) => ({
|
|
@@ -135,16 +145,12 @@ const createSessionRecorders = (server, session) => ({
|
|
|
135
145
|
? undefined
|
|
136
146
|
: (evidence) => {
|
|
137
147
|
const recorded = session.recordEvidence(evidence);
|
|
138
|
-
if (recorded.ok && recorded.value === "added")
|
|
139
|
-
server.sendResourceListChanged();
|
|
140
148
|
return recorded;
|
|
141
149
|
},
|
|
142
150
|
recordEvidenceWithUnknown: session === undefined
|
|
143
151
|
? undefined
|
|
144
152
|
: (evidence, input) => {
|
|
145
153
|
const recorded = session.recordEvidenceWithUnknown(evidence, input);
|
|
146
|
-
if (recorded.ok)
|
|
147
|
-
server.sendResourceListChanged();
|
|
148
154
|
return recorded;
|
|
149
155
|
},
|
|
150
156
|
});
|
|
@@ -153,7 +159,8 @@ const registerServerIdentityResource = (server, startedAt) => {
|
|
|
153
159
|
title: "REA server identity",
|
|
154
160
|
description: "Live package, SDK, protocol, and catalog identity.",
|
|
155
161
|
mimeType: "application/json",
|
|
156
|
-
}, (uri) => {
|
|
162
|
+
}, async (uri) => {
|
|
163
|
+
const { createServerIdentity } = await import("../serverIdentity.js");
|
|
157
164
|
const client = server.server.getClientVersion();
|
|
158
165
|
const protocolVersion = server.server.getNegotiatedProtocolVersion();
|
|
159
166
|
return {
|
|
@@ -171,118 +178,4 @@ const registerServerIdentityResource = (server, startedAt) => {
|
|
|
171
178
|
};
|
|
172
179
|
});
|
|
173
180
|
};
|
|
174
|
-
const registerBinaryAnalysisTools = ({ server, analysis, session, options, logger, permissionAuthority, activeTarget, recordEvidence, recordEvidenceWithUnknown, }) => {
|
|
175
|
-
const recordUnknown = session === undefined
|
|
176
|
-
? undefined
|
|
177
|
-
: (input) => session.recordUnknown(input);
|
|
178
|
-
registerOfficialTools(server, analysis, {
|
|
179
|
-
logger,
|
|
180
|
-
activeTarget,
|
|
181
|
-
recordEvidence,
|
|
182
|
-
recordUnknown,
|
|
183
|
-
});
|
|
184
|
-
registerEnhancedTools(server, analysis, {
|
|
185
|
-
logger,
|
|
186
|
-
activeTarget,
|
|
187
|
-
analysisProfile: session === undefined ? undefined : () => session.analysisProfile(),
|
|
188
|
-
recordEvidence,
|
|
189
|
-
recordUnknown,
|
|
190
|
-
});
|
|
191
|
-
registerNativeTools(server, analysis, {
|
|
192
|
-
logger,
|
|
193
|
-
activeTarget,
|
|
194
|
-
recordEvidence,
|
|
195
|
-
});
|
|
196
|
-
registerArtifactTools(server, analysis, {
|
|
197
|
-
logger,
|
|
198
|
-
activeTarget,
|
|
199
|
-
recordEvidence,
|
|
200
|
-
...(permissionAuthority === undefined ? {} : { permissionAuthority }),
|
|
201
|
-
});
|
|
202
|
-
registerManagedTools(server, analysis, {
|
|
203
|
-
logger,
|
|
204
|
-
activeTarget,
|
|
205
|
-
recordEvidence,
|
|
206
|
-
session,
|
|
207
|
-
});
|
|
208
|
-
if (session !== undefined)
|
|
209
|
-
registerManagedWorkflowTools(server, {
|
|
210
|
-
logger,
|
|
211
|
-
recordEvidence,
|
|
212
|
-
recordEvidenceWithUnknown,
|
|
213
|
-
session,
|
|
214
|
-
runtime: {
|
|
215
|
-
policy: options.managedRuntimePolicy ?? {
|
|
216
|
-
enabled: false,
|
|
217
|
-
roots: [],
|
|
218
|
-
executablePath: "/usr/bin/dotnet",
|
|
219
|
-
},
|
|
220
|
-
authority: permissionAuthority,
|
|
221
|
-
},
|
|
222
|
-
});
|
|
223
|
-
};
|
|
224
|
-
const registerObservationTools = ({ server, session, options, logger, permissionAuthority, recordEvidence, recordEvidenceWithUnknown, }) => {
|
|
225
|
-
registerBrowserTools(server, {
|
|
226
|
-
logger,
|
|
227
|
-
browser: options.browserObservation,
|
|
228
|
-
permissionAuthority,
|
|
229
|
-
recordEvidence,
|
|
230
|
-
});
|
|
231
|
-
registerBrowserScenarioTool(server, {
|
|
232
|
-
logger,
|
|
233
|
-
provider: options.browserScenarioCapture,
|
|
234
|
-
permissionAuthority,
|
|
235
|
-
recordEvidence,
|
|
236
|
-
});
|
|
237
|
-
registerElectronTools(server, {
|
|
238
|
-
logger,
|
|
239
|
-
electron: options.electronObservation,
|
|
240
|
-
permissionAuthority,
|
|
241
|
-
recordEvidence,
|
|
242
|
-
});
|
|
243
|
-
registerJavaScriptRuntimeObservationTools(server, {
|
|
244
|
-
logger,
|
|
245
|
-
runtime: options.javascriptRuntimeObservation,
|
|
246
|
-
permissionAuthority,
|
|
247
|
-
recordEvidence,
|
|
248
|
-
});
|
|
249
|
-
registerApplicationTools(server, {
|
|
250
|
-
logger,
|
|
251
|
-
recordEvidence,
|
|
252
|
-
recordEvidenceWithUnknown,
|
|
253
|
-
evidenceLookup: session === undefined
|
|
254
|
-
? undefined
|
|
255
|
-
: (evidenceId) => session.evidenceById(evidenceId),
|
|
256
|
-
evidenceFilePolicy: options.evidenceFilePolicy ?? {
|
|
257
|
-
roots: [],
|
|
258
|
-
maxBytes: 1,
|
|
259
|
-
maxDepth: 1,
|
|
260
|
-
maxStringLength: 1,
|
|
261
|
-
maxNodes: 1,
|
|
262
|
-
},
|
|
263
|
-
permissionAuthority,
|
|
264
|
-
retainCoverageWorkspace: session === undefined
|
|
265
|
-
? undefined
|
|
266
|
-
: (workspace) => {
|
|
267
|
-
const retained = session.retainReconstructionCoverageWorkspace(workspace);
|
|
268
|
-
if (retained === "added")
|
|
269
|
-
server.sendResourceListChanged();
|
|
270
|
-
return retained;
|
|
271
|
-
},
|
|
272
|
-
replay: {
|
|
273
|
-
policy: options.javascriptReplayPolicy ?? {
|
|
274
|
-
enabled: false,
|
|
275
|
-
roots: [],
|
|
276
|
-
nodePath: process.execPath,
|
|
277
|
-
bubblewrapPath: "/usr/bin/bwrap",
|
|
278
|
-
systemdRunPath: "/usr/bin/systemd-run",
|
|
279
|
-
systemctlPath: "/usr/bin/systemctl",
|
|
280
|
-
shellPath: "/usr/bin/bash",
|
|
281
|
-
},
|
|
282
|
-
host: options.javascriptReplayHost ?? new SystemJavaScriptReplayHost(),
|
|
283
|
-
runner: options.javascriptReplayRunner ?? new LinuxJavaScriptReplayRunner(),
|
|
284
|
-
authority: permissionAuthority,
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
};
|
|
288
181
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install production callbacks on generated lazy tool registrations.
|
|
3
|
+
*/
|
|
4
|
+
export const hydrateServerToolFamily = async ({ kind, server, analysis, session, options, context, }) => {
|
|
5
|
+
const toolContext = {
|
|
6
|
+
server,
|
|
7
|
+
analysis,
|
|
8
|
+
session,
|
|
9
|
+
options,
|
|
10
|
+
logger: context.logger,
|
|
11
|
+
permissionAuthority: context.permissionAuthority,
|
|
12
|
+
activeTarget: context.activeTarget,
|
|
13
|
+
recordEvidence: context.recordEvidence,
|
|
14
|
+
recordEvidenceWithUnknown: context.recordEvidenceWithUnknown,
|
|
15
|
+
};
|
|
16
|
+
switch (kind) {
|
|
17
|
+
case "official-proxy": {
|
|
18
|
+
const { registerOfficialTools } = await import("./registerOfficialTools.js");
|
|
19
|
+
registerOfficialTools(server, analysis, analysisOptions(toolContext));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
case "enhanced": {
|
|
23
|
+
const { registerEnhancedTools } = await import("./registerEnhancedTools.js");
|
|
24
|
+
registerEnhancedTools(server, analysis, enhancedOptions(toolContext));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
case "native-provider": {
|
|
28
|
+
const { registerNativeTools } = await import("./registerNativeTools.js");
|
|
29
|
+
registerNativeTools(server, analysis, evidenceOptions(toolContext));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
case "artifact-provider": {
|
|
33
|
+
const { registerArtifactTools } = await import("./registerArtifactTools.js");
|
|
34
|
+
registerArtifactTools(server, analysis, {
|
|
35
|
+
...evidenceOptions(toolContext),
|
|
36
|
+
...(context.permissionAuthority === undefined
|
|
37
|
+
? {}
|
|
38
|
+
: { permissionAuthority: context.permissionAuthority }),
|
|
39
|
+
});
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
case "managed-provider": {
|
|
43
|
+
const { registerManagedTools } = await import("./registerManagedTools.js");
|
|
44
|
+
registerManagedTools(server, analysis, {
|
|
45
|
+
...evidenceOptions(toolContext),
|
|
46
|
+
session,
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
case "application":
|
|
51
|
+
await registerApplicationFamily(toolContext);
|
|
52
|
+
return;
|
|
53
|
+
case "browser-provider":
|
|
54
|
+
case "electron-provider":
|
|
55
|
+
case "runtime-provider": {
|
|
56
|
+
await hydrateObservationFamily(kind, toolContext);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
case "session": {
|
|
60
|
+
if (session === undefined)
|
|
61
|
+
throw new TypeError("Session tools require a binary session");
|
|
62
|
+
const { registerSessionTools } = await import("./registerSessionTools.js");
|
|
63
|
+
registerSessionTools(server, session, context.logger, {
|
|
64
|
+
...options,
|
|
65
|
+
...(context.availabilityPolicy === undefined
|
|
66
|
+
? {}
|
|
67
|
+
: { availabilityPolicy: context.availabilityPolicy }),
|
|
68
|
+
...(context.permissionAuthority === undefined
|
|
69
|
+
? {}
|
|
70
|
+
: { permissionAuthority: context.permissionAuthority }),
|
|
71
|
+
startedAt: context.startedAt,
|
|
72
|
+
processCaptureElicitation: context.processCaptureElicitation,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const hydrateObservationFamily = async (kind, context) => {
|
|
78
|
+
const common = {
|
|
79
|
+
logger: context.logger,
|
|
80
|
+
permissionAuthority: context.permissionAuthority,
|
|
81
|
+
recordEvidence: context.recordEvidence,
|
|
82
|
+
};
|
|
83
|
+
if (kind === "browser-provider") {
|
|
84
|
+
const [{ registerBrowserTools }, { registerBrowserScenarioTool }] = await Promise.all([
|
|
85
|
+
import("./registerBrowserTools.js"),
|
|
86
|
+
import("./registerBrowserScenarioTool.js"),
|
|
87
|
+
]);
|
|
88
|
+
registerBrowserTools(context.server, {
|
|
89
|
+
...common,
|
|
90
|
+
browser: context.options.browserObservation,
|
|
91
|
+
});
|
|
92
|
+
registerBrowserScenarioTool(context.server, {
|
|
93
|
+
...common,
|
|
94
|
+
provider: context.options.browserScenarioCapture,
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (kind === "electron-provider") {
|
|
99
|
+
const { registerElectronTools } = await import("./registerElectronTools.js");
|
|
100
|
+
registerElectronTools(context.server, {
|
|
101
|
+
...common,
|
|
102
|
+
electron: context.options.electronObservation,
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const { registerJavaScriptRuntimeObservationTools } = await import("./registerJavaScriptRuntimeObservationTools.js");
|
|
107
|
+
registerJavaScriptRuntimeObservationTools(context.server, {
|
|
108
|
+
...common,
|
|
109
|
+
runtime: context.options.javascriptRuntimeObservation,
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
const analysisOptions = ({ logger, activeTarget, recordEvidence, session, }) => {
|
|
113
|
+
const recordUnknown = session === undefined
|
|
114
|
+
? undefined
|
|
115
|
+
: (input) => session.recordUnknown(input);
|
|
116
|
+
return {
|
|
117
|
+
logger,
|
|
118
|
+
activeTarget,
|
|
119
|
+
recordEvidence,
|
|
120
|
+
recordUnknown,
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
const enhancedOptions = (context) => ({
|
|
124
|
+
...analysisOptions(context),
|
|
125
|
+
analysisProfile: context.session === undefined
|
|
126
|
+
? undefined
|
|
127
|
+
: () => context.session?.analysisProfile(),
|
|
128
|
+
});
|
|
129
|
+
const evidenceOptions = ({ logger, activeTarget, recordEvidence, }) => ({
|
|
130
|
+
logger,
|
|
131
|
+
activeTarget,
|
|
132
|
+
recordEvidence,
|
|
133
|
+
});
|
|
134
|
+
const registerApplicationFamily = async ({ server, session, options, logger, permissionAuthority, recordEvidence, recordEvidenceWithUnknown, }) => {
|
|
135
|
+
const [{ registerApplicationTools }, { registerManagedWorkflowTools }, { LinuxJavaScriptReplayRunner }, { SystemJavaScriptReplayHost },] = await Promise.all([
|
|
136
|
+
import("./registerApplicationTools.js"),
|
|
137
|
+
import("./registerManagedWorkflowTools.js"),
|
|
138
|
+
import("../replay/LinuxJavaScriptReplayRunner.js"),
|
|
139
|
+
import("../replay/SystemJavaScriptReplayHost.js"),
|
|
140
|
+
]);
|
|
141
|
+
if (session !== undefined)
|
|
142
|
+
registerManagedWorkflowTools(server, {
|
|
143
|
+
logger,
|
|
144
|
+
recordEvidence,
|
|
145
|
+
recordEvidenceWithUnknown,
|
|
146
|
+
session,
|
|
147
|
+
runtime: {
|
|
148
|
+
policy: options.managedRuntimePolicy ?? {
|
|
149
|
+
enabled: false,
|
|
150
|
+
roots: [],
|
|
151
|
+
executablePath: "/usr/bin/dotnet",
|
|
152
|
+
},
|
|
153
|
+
authority: permissionAuthority,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
registerApplicationTools(server, {
|
|
157
|
+
logger,
|
|
158
|
+
recordEvidence,
|
|
159
|
+
recordEvidenceWithUnknown,
|
|
160
|
+
evidenceLookup: session === undefined
|
|
161
|
+
? undefined
|
|
162
|
+
: (evidenceId) => session.evidenceById(evidenceId),
|
|
163
|
+
evidenceFilePolicy: options.evidenceFilePolicy ?? {
|
|
164
|
+
roots: [],
|
|
165
|
+
maxBytes: 1,
|
|
166
|
+
maxDepth: 1,
|
|
167
|
+
maxStringLength: 1,
|
|
168
|
+
maxNodes: 1,
|
|
169
|
+
},
|
|
170
|
+
permissionAuthority,
|
|
171
|
+
retainCoverageWorkspace: session === undefined
|
|
172
|
+
? undefined
|
|
173
|
+
: (workspace) => {
|
|
174
|
+
return session.retainReconstructionCoverageWorkspace(workspace);
|
|
175
|
+
},
|
|
176
|
+
replay: {
|
|
177
|
+
policy: options.javascriptReplayPolicy ?? {
|
|
178
|
+
enabled: false,
|
|
179
|
+
roots: [],
|
|
180
|
+
nodePath: process.execPath,
|
|
181
|
+
bubblewrapPath: "/usr/bin/bwrap",
|
|
182
|
+
systemdRunPath: "/usr/bin/systemd-run",
|
|
183
|
+
systemctlPath: "/usr/bin/systemctl",
|
|
184
|
+
shellPath: "/usr/bin/bash",
|
|
185
|
+
},
|
|
186
|
+
host: options.javascriptReplayHost ?? new SystemJavaScriptReplayHost(),
|
|
187
|
+
runner: options.javascriptReplayRunner ?? new LinuxJavaScriptReplayRunner(),
|
|
188
|
+
authority: permissionAuthority,
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
};
|
|
@@ -97,6 +97,7 @@ const ENHANCED_INPUT_PARSERS = {
|
|
|
97
97
|
find_xrefs_to_name: (input) => safeParseToolInput(enhancedInputSchemas.find_xrefs_to_name, input, "find_xrefs_to_name"),
|
|
98
98
|
binary_overview: (input) => safeParseToolInput(enhancedInputSchemas.binary_overview, input, "binary_overview"),
|
|
99
99
|
analyze_function: (input) => safeParseToolInput(enhancedInputSchemas.analyze_function, input, "analyze_function"),
|
|
100
|
+
inspect_native_api: (input) => safeParseToolInput(enhancedInputSchemas.inspect_native_api, input, "inspect_native_api"),
|
|
100
101
|
trace_feature: (input) => safeParseToolInput(enhancedInputSchemas.trace_feature, input, "trace_feature"),
|
|
101
102
|
find_code_for_string: (input) => safeParseToolInput(enhancedInputSchemas.find_code_for_string, input, "find_code_for_string"),
|
|
102
103
|
trace_call_path: (input) => safeParseToolInput(enhancedInputSchemas.trace_call_path, input, "trace_call_path"),
|
|
@@ -121,6 +122,8 @@ const parseEnhancedCall = (name, input) => {
|
|
|
121
122
|
return mapEnhancedCall(ENHANCED_INPUT_PARSERS.binary_overview(input), (value) => ({ name, input: value }));
|
|
122
123
|
case "analyze_function":
|
|
123
124
|
return mapEnhancedCall(ENHANCED_INPUT_PARSERS.analyze_function(input), (value) => ({ name, input: value }));
|
|
125
|
+
case "inspect_native_api":
|
|
126
|
+
return mapEnhancedCall(ENHANCED_INPUT_PARSERS.inspect_native_api(input), (value) => ({ name, input: value }));
|
|
124
127
|
case "trace_feature":
|
|
125
128
|
return mapEnhancedCall(ENHANCED_INPUT_PARSERS.trace_feature(input), (value) => ({ name, input: value }));
|
|
126
129
|
case "find_code_for_string":
|
|
@@ -131,7 +134,12 @@ const parseEnhancedCall = (name, input) => {
|
|
|
131
134
|
};
|
|
132
135
|
const mapEnhancedCall = (parsed, project) => parsed.ok ? ok(project(parsed.value)) : parsed;
|
|
133
136
|
const recordWorkflowUnknowns = ({ name, input, result, evidenceId, recordUnknown, }) => {
|
|
134
|
-
if (![
|
|
137
|
+
if (![
|
|
138
|
+
"trace_feature",
|
|
139
|
+
"find_code_for_string",
|
|
140
|
+
"trace_call_path",
|
|
141
|
+
"inspect_native_api",
|
|
142
|
+
].includes(name) ||
|
|
135
143
|
input.unknown_registry_approved !== true ||
|
|
136
144
|
recordUnknown === undefined ||
|
|
137
145
|
typeof result !== "object" ||
|
|
@@ -146,7 +154,7 @@ const recordWorkflowUnknowns = ({ name, input, result, evidenceId, recordUnknown
|
|
|
146
154
|
approved: true,
|
|
147
155
|
question,
|
|
148
156
|
severity: "medium",
|
|
149
|
-
domain: "control-flow",
|
|
157
|
+
domain: name === "inspect_native_api" ? "native-api" : "control-flow",
|
|
150
158
|
supporting_evidence_ids: [evidenceId],
|
|
151
159
|
contradicting_evidence_ids: [],
|
|
152
160
|
required_authority: "shipped-artifact",
|
|
@@ -155,7 +163,9 @@ const recordWorkflowUnknowns = ({ name, input, result, evidenceId, recordUnknown
|
|
|
155
163
|
recommended_probes: [
|
|
156
164
|
{
|
|
157
165
|
operation: name,
|
|
158
|
-
rationale:
|
|
166
|
+
rationale: name === "inspect_native_api"
|
|
167
|
+
? "Confirm the unsupported boundary with a capable provider or ABI probe."
|
|
168
|
+
: "Continue with a larger bounded budget.",
|
|
159
169
|
},
|
|
160
170
|
],
|
|
161
171
|
relationships: [],
|