rea-agents 2.4.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 +91 -19
- package/bridge/ghidra/ReaGhidraBridge.java +524 -4
- package/bridge/hopper_bridge.py +313 -28
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/AnalysisContextQueries.js +130 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
- package/dist/application/BinarySession.js +20 -8
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +79 -9
- package/dist/application/BinarySessionStatus.js +31 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +84 -14
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +13 -4
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +71 -127
- package/dist/application/EvidenceLedger.js +45 -16
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/ProcessReactiveEffects.js +6 -0
- package/dist/application/ProcessReactiveHarness.js +16 -1
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +4 -2
- package/dist/application/runtime.js +26 -9
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +259 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +11 -3
- package/dist/cli/artifactCommands.js +44 -3
- package/dist/cli/coreAnalysisCommands.js +78 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +9 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +29 -2
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +35 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/functionWorkflowToolContracts.js +30 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +8 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +34 -25
- package/dist/contracts/toolEffects.js +25 -0
- package/dist/contracts/toolOutputSchemaGroups.js +120 -30
- package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +9 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -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 +14 -1
- package/dist/domain/inputIssueProjection.js +7 -1
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +13 -16
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +12 -15
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- 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/processCapture.js +8 -2
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +9 -4
- package/dist/domain/protocolCapture.js +243 -0
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- 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 +40 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +47 -38
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +93 -99
- package/dist/hopper/HopperProvider.js +61 -30
- package/dist/hopper/HopperRequestQueue.js +215 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +63 -20
- package/dist/main/transport.js +24 -27
- package/dist/main.js +14 -6
- package/dist/mcpDoctor.js +27 -4
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/LazyToolCatalog.js +134 -0
- package/dist/server/createServer.js +92 -154
- package/dist/server/hydrateServerTools.js +191 -0
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +28 -7
- package/dist/server/registerEvidenceResources.js +54 -101
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerInvestigationTools.js +1 -2
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +32 -0
- package/dist/server/registerReconstructionReadinessResource.js +32 -0
- package/dist/server/registerSessionRecordTools.js +82 -12
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +32 -58
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/dist/server/toolRegistrationOptions.js +21 -6
- package/dist/server/toolResult.js +12 -0
- package/package.json +28 -11
- 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,160 @@
|
|
|
1
|
+
import { projectAnalysisError } from "../domain/errors.js";
|
|
2
|
+
import { parseRelatedAddresses } from "../domain/hopperValues.js";
|
|
3
|
+
import { ok } from "../domain/result.js";
|
|
4
|
+
/** Trace one deterministic bounded direct caller/callee path. */
|
|
5
|
+
export const traceCallPath = async (call, input, signal) => {
|
|
6
|
+
const state = createTraceState(input);
|
|
7
|
+
while (state.queueIndex < state.queue.length &&
|
|
8
|
+
state.traversalPath.length === 0) {
|
|
9
|
+
const address = state.queue[state.queueIndex++];
|
|
10
|
+
if (address === undefined)
|
|
11
|
+
break;
|
|
12
|
+
const current = state.visited.get(address);
|
|
13
|
+
if (current === undefined)
|
|
14
|
+
continue;
|
|
15
|
+
if (current.depth >= input.max_depth) {
|
|
16
|
+
state.frontier.add(address);
|
|
17
|
+
state.residual.add("Call-path traversal reached the depth limit.");
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (state.operations >= input.max_operations) {
|
|
21
|
+
recordPendingFrontier(state, input);
|
|
22
|
+
state.residual.add("Call-path traversal reached the operation budget.");
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
state.operations += 1;
|
|
26
|
+
const found = await expandAddress(call, {
|
|
27
|
+
input,
|
|
28
|
+
state,
|
|
29
|
+
address,
|
|
30
|
+
current,
|
|
31
|
+
...(signal === undefined ? {} : { signal }),
|
|
32
|
+
});
|
|
33
|
+
if (found)
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
return ok(projectTrace(input, state));
|
|
37
|
+
};
|
|
38
|
+
const createTraceState = (input) => ({
|
|
39
|
+
visited: new Map([[input.start, { depth: 0, path: [input.start] }]]),
|
|
40
|
+
queue: [input.start],
|
|
41
|
+
edges: new Map(),
|
|
42
|
+
failures: [],
|
|
43
|
+
frontier: new Set(),
|
|
44
|
+
residual: new Set(),
|
|
45
|
+
operations: 0,
|
|
46
|
+
queueIndex: 0,
|
|
47
|
+
traversalPath: input.goal === input.start ? [input.start] : [],
|
|
48
|
+
});
|
|
49
|
+
const expandAddress = async (call, expansion) => {
|
|
50
|
+
const { input, state, address, current, signal } = expansion;
|
|
51
|
+
const relation = input.direction === "forward" ? "callees" : "callers";
|
|
52
|
+
const tool = input.direction === "forward" ? "procedure_callees" : "procedure_callers";
|
|
53
|
+
const result = await call(tool, { procedure: address }, signal);
|
|
54
|
+
if (!result.ok) {
|
|
55
|
+
state.failures.push({
|
|
56
|
+
address,
|
|
57
|
+
error: projectAnalysisError(result.error),
|
|
58
|
+
});
|
|
59
|
+
state.residual.add(`Call relationships were unavailable for ${address}.`);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const related = parseRelatedAddresses(result.value, relation);
|
|
63
|
+
if (!related.ok) {
|
|
64
|
+
state.failures.push({
|
|
65
|
+
address,
|
|
66
|
+
error: projectAnalysisError(related.error),
|
|
67
|
+
});
|
|
68
|
+
state.residual.add(`Call relationships were unreadable for ${address}.`);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
for (const relatedAddress of sortedUniqueAddresses(related.value)) {
|
|
72
|
+
recordEdge(input, state, {
|
|
73
|
+
address,
|
|
74
|
+
relatedAddress,
|
|
75
|
+
discoveryDepth: current.depth + 1,
|
|
76
|
+
});
|
|
77
|
+
const path = state.visited.get(relatedAddress)?.path ?? [
|
|
78
|
+
...current.path,
|
|
79
|
+
relatedAddress,
|
|
80
|
+
];
|
|
81
|
+
if (state.visited.has(relatedAddress)) {
|
|
82
|
+
if (relatedAddress === input.goal) {
|
|
83
|
+
state.traversalPath = path;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (state.visited.size >= input.max_nodes) {
|
|
89
|
+
if (state.frontier.size < input.max_nodes)
|
|
90
|
+
state.frontier.add(relatedAddress);
|
|
91
|
+
state.residual.add("Call-path traversal reached the node limit.");
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
state.visited.set(relatedAddress, {
|
|
95
|
+
depth: current.depth + 1,
|
|
96
|
+
path,
|
|
97
|
+
});
|
|
98
|
+
if (relatedAddress === input.goal) {
|
|
99
|
+
state.traversalPath = path;
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
state.queue.push(relatedAddress);
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
const recordEdge = (input, state, edge) => {
|
|
107
|
+
const sourceAddress = input.direction === "forward" ? edge.address : edge.relatedAddress;
|
|
108
|
+
const targetAddress = input.direction === "forward" ? edge.relatedAddress : edge.address;
|
|
109
|
+
const edgeKey = `${sourceAddress}\u0000${targetAddress}`;
|
|
110
|
+
if (state.edges.has(edgeKey))
|
|
111
|
+
return;
|
|
112
|
+
if (state.edges.size >= input.max_nodes) {
|
|
113
|
+
state.residual.add("Call-path traversal reached the edge limit.");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
state.edges.set(edgeKey, {
|
|
117
|
+
source_address: sourceAddress,
|
|
118
|
+
target_address: targetAddress,
|
|
119
|
+
discovery_depth: edge.discoveryDepth,
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
const recordPendingFrontier = (state, input) => {
|
|
123
|
+
for (const pending of state.queue.slice(state.queueIndex - 1))
|
|
124
|
+
if (state.frontier.size < input.max_nodes)
|
|
125
|
+
state.frontier.add(pending);
|
|
126
|
+
};
|
|
127
|
+
const projectTrace = (input, state) => ({
|
|
128
|
+
start: input.start,
|
|
129
|
+
goal: input.goal ?? null,
|
|
130
|
+
direction: input.direction,
|
|
131
|
+
goal_status: input.goal === undefined
|
|
132
|
+
? "not_requested"
|
|
133
|
+
: state.traversalPath.length > 0
|
|
134
|
+
? "reached"
|
|
135
|
+
: "not_reached",
|
|
136
|
+
nodes: [...state.visited.entries()]
|
|
137
|
+
.map(([address, { depth }]) => ({ address, depth }))
|
|
138
|
+
.sort((left, right) => left.depth - right.depth || compareAddress(left.address, right.address)),
|
|
139
|
+
edges: [...state.edges.values()].sort((left, right) => left.discovery_depth - right.discovery_depth ||
|
|
140
|
+
compareAddress(left.source_address, right.source_address) ||
|
|
141
|
+
compareAddress(left.target_address, right.target_address)),
|
|
142
|
+
traversal_path: state.traversalPath,
|
|
143
|
+
failures: state.failures,
|
|
144
|
+
frontier: [...state.frontier].sort(compareAddress),
|
|
145
|
+
limits: {
|
|
146
|
+
max_depth: input.max_depth,
|
|
147
|
+
max_nodes: input.max_nodes,
|
|
148
|
+
max_operations: input.max_operations,
|
|
149
|
+
nodes_visited: state.visited.size,
|
|
150
|
+
operations_used: state.operations,
|
|
151
|
+
},
|
|
152
|
+
truncated: state.residual.size > 0,
|
|
153
|
+
residual_unknowns: [...state.residual],
|
|
154
|
+
limitations: [
|
|
155
|
+
"Direct provider call relationships may omit unresolved indirect calls.",
|
|
156
|
+
"A not_reached goal is only absent from this bounded traversal, not from the complete program.",
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
const compareAddress = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
160
|
+
const sortedUniqueAddresses = (addresses) => [...new Set(addresses)].sort(compareAddress);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { GENERATED_MCP_TOOL_CATALOG } from "../generatedMcpToolCatalog.js";
|
|
3
3
|
const statusSchema = z.object({
|
|
4
4
|
open: z.boolean(),
|
|
5
5
|
kind: z.enum(["executable", "database", "archive", "artifact"]).optional(),
|
|
@@ -11,6 +11,18 @@ const statusSchema = z.object({
|
|
|
11
11
|
availability_code: z.string().nullable().optional(),
|
|
12
12
|
})),
|
|
13
13
|
});
|
|
14
|
+
const NO_CLIENT_FEATURES = {
|
|
15
|
+
elicitation_form: false,
|
|
16
|
+
elicitation_url: false,
|
|
17
|
+
roots: false,
|
|
18
|
+
sampling: false,
|
|
19
|
+
};
|
|
20
|
+
const CLIENT_FEATURE_REQUIREMENTS = {
|
|
21
|
+
capture_process_scenario: {
|
|
22
|
+
required: [],
|
|
23
|
+
optional: ["elicitation_form"],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
14
26
|
const ENHANCED_REQUIREMENTS = {
|
|
15
27
|
swift_classes: ["list_procedures"],
|
|
16
28
|
get_objc_classes: ["list_names"],
|
|
@@ -26,6 +38,7 @@ const ENHANCED_REQUIREMENTS = {
|
|
|
26
38
|
"list_strings",
|
|
27
39
|
],
|
|
28
40
|
analyze_function: ["analyze_function"],
|
|
41
|
+
inspect_native_api: ["analyze_function"],
|
|
29
42
|
trace_feature: [
|
|
30
43
|
"list_strings",
|
|
31
44
|
"list_procedures",
|
|
@@ -33,8 +46,15 @@ const ENHANCED_REQUIREMENTS = {
|
|
|
33
46
|
"resolve_containing_procedure",
|
|
34
47
|
],
|
|
35
48
|
};
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
const SESSION_COMPOSITION_REQUIREMENTS = {
|
|
50
|
+
get_navigation_context: [
|
|
51
|
+
"current_document",
|
|
52
|
+
"current_address",
|
|
53
|
+
"resolve_containing_procedure",
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
/** Build stable per-operation availability for discovery and tool visibility. */
|
|
57
|
+
export const buildCapabilityInventory = (sessionStatus, policy, clientFeatures = NO_CLIENT_FEATURES) => {
|
|
38
58
|
const status = statusSchema.parse(sessionStatus);
|
|
39
59
|
const descriptors = new Map(status.capabilities.map((descriptor) => [
|
|
40
60
|
descriptor.operation,
|
|
@@ -44,7 +64,7 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
|
|
|
44
64
|
availability_code: descriptor.availability_code ?? null,
|
|
45
65
|
},
|
|
46
66
|
]));
|
|
47
|
-
return
|
|
67
|
+
return GENERATED_MCP_TOOL_CATALOG.map((contract) => {
|
|
48
68
|
const availability = availabilityFor({
|
|
49
69
|
name: contract.name,
|
|
50
70
|
kind: contract.kind,
|
|
@@ -53,12 +73,19 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
|
|
|
53
73
|
descriptors,
|
|
54
74
|
policy,
|
|
55
75
|
});
|
|
76
|
+
const clientRequirements = clientRequirementsFor(contract.name, clientFeatures);
|
|
77
|
+
const clientBlocked = clientRequirements.missing_required.length > 0;
|
|
56
78
|
return {
|
|
57
79
|
name: contract.name,
|
|
58
80
|
surface: contract.kind,
|
|
59
|
-
available: availability.reason === "available",
|
|
60
|
-
reason:
|
|
61
|
-
|
|
81
|
+
available: availability.reason === "available" && !clientBlocked,
|
|
82
|
+
reason: clientBlocked
|
|
83
|
+
? "client_capability_missing"
|
|
84
|
+
: availability.reason,
|
|
85
|
+
remediation: clientBlocked
|
|
86
|
+
? `Use an MCP client that supports: ${clientRequirements.missing_required.join(", ")}.`
|
|
87
|
+
: availability.remediation,
|
|
88
|
+
client_requirements: clientRequirements,
|
|
62
89
|
effects: { ...contract.effects },
|
|
63
90
|
annotations: {
|
|
64
91
|
read_only: contract.annotations.readOnlyHint ?? false,
|
|
@@ -69,7 +96,22 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
|
|
|
69
96
|
};
|
|
70
97
|
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
71
98
|
};
|
|
99
|
+
const clientRequirementsFor = (name, clientFeatures) => {
|
|
100
|
+
const requirements = CLIENT_FEATURE_REQUIREMENTS[name] ?? {
|
|
101
|
+
required: [],
|
|
102
|
+
optional: [],
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
required: [...requirements.required],
|
|
106
|
+
optional: [...requirements.optional],
|
|
107
|
+
missing_required: requirements.required.filter((feature) => !clientFeatures[feature]),
|
|
108
|
+
missing_optional: requirements.optional.filter((feature) => !clientFeatures[feature]),
|
|
109
|
+
};
|
|
110
|
+
};
|
|
72
111
|
const availabilityFor = (context) => {
|
|
112
|
+
const sessionComposition = SESSION_COMPOSITION_REQUIREMENTS[context.name];
|
|
113
|
+
if (sessionComposition !== undefined)
|
|
114
|
+
return composedAvailabilityFor(sessionComposition, context.descriptors);
|
|
73
115
|
const javascriptApplication = javascriptApplicationAvailability(context);
|
|
74
116
|
if (javascriptApplication !== null)
|
|
75
117
|
return javascriptApplication;
|
|
@@ -94,6 +136,9 @@ const javascriptApplicationAvailability = ({ name, policy, }) => {
|
|
|
94
136
|
: null;
|
|
95
137
|
};
|
|
96
138
|
const policyAvailability = ({ name, kind, policy, }) => {
|
|
139
|
+
const browser = browserPolicyAvailability(name, kind, policy);
|
|
140
|
+
if (browser !== null)
|
|
141
|
+
return browser;
|
|
97
142
|
if (name === "capture_process_scenario" && !policy.processCaptureEnabled)
|
|
98
143
|
return {
|
|
99
144
|
reason: "policy_disabled",
|
|
@@ -121,26 +166,43 @@ const policyAvailability = ({ name, kind, policy, }) => {
|
|
|
121
166
|
reason: "policy_disabled",
|
|
122
167
|
remediation: "Configure or grant an evidence_read root.",
|
|
123
168
|
};
|
|
124
|
-
if (kind === "
|
|
125
|
-
return policy.
|
|
169
|
+
if (kind === "electron-provider")
|
|
170
|
+
return policy.electronObservationEnabled === true
|
|
126
171
|
? { reason: "available", remediation: null }
|
|
127
172
|
: {
|
|
128
173
|
reason: "policy_disabled",
|
|
129
|
-
remediation: "Enable
|
|
174
|
+
remediation: "Enable Electron observation and configure a loopback CDP endpoint and canonical file roots.",
|
|
130
175
|
};
|
|
131
|
-
if (kind === "
|
|
132
|
-
return policy.
|
|
176
|
+
if (kind === "runtime-provider")
|
|
177
|
+
return policy.v8InspectorObservationEnabled === true
|
|
133
178
|
? { reason: "available", remediation: null }
|
|
134
179
|
: {
|
|
135
180
|
reason: "policy_disabled",
|
|
136
|
-
remediation: "Enable
|
|
181
|
+
remediation: "Enable V8 Inspector observation and configure exact loopback endpoints plus canonical file roots or exact origins.",
|
|
137
182
|
};
|
|
138
183
|
if (kind === "session")
|
|
139
184
|
return { reason: "available", remediation: null };
|
|
140
185
|
return null;
|
|
141
186
|
};
|
|
187
|
+
const browserPolicyAvailability = (name, kind, policy) => {
|
|
188
|
+
if (name === "capture_browser_scenario")
|
|
189
|
+
return policy.browserScenarioEnabled === true
|
|
190
|
+
? { reason: "available", remediation: null }
|
|
191
|
+
: {
|
|
192
|
+
reason: "policy_disabled",
|
|
193
|
+
remediation: "Enable browser scenarios and configure exact executable roots or loopback CDP endpoints, page origins, and environment secret names.",
|
|
194
|
+
};
|
|
195
|
+
if (kind !== "browser-provider")
|
|
196
|
+
return null;
|
|
197
|
+
return policy.browserObservationEnabled === true
|
|
198
|
+
? { reason: "available", remediation: null }
|
|
199
|
+
: {
|
|
200
|
+
reason: "policy_disabled",
|
|
201
|
+
remediation: "Enable browser observation and configure exact CDP endpoint and page origins.",
|
|
202
|
+
};
|
|
203
|
+
};
|
|
142
204
|
const targetAvailability = ({ kind, targetKind, targetOpen, }) => {
|
|
143
|
-
if (!targetOpen)
|
|
205
|
+
if (!targetOpen && (kind === "official-proxy" || kind === "enhanced"))
|
|
144
206
|
return {
|
|
145
207
|
reason: "target_required",
|
|
146
208
|
remediation: "Call open_binary with a supported local target.",
|
|
@@ -159,6 +221,11 @@ const providerAvailability = ({ descriptors, kind, name, }) => {
|
|
|
159
221
|
if (kind === "enhanced")
|
|
160
222
|
return composedAvailability(name, descriptors);
|
|
161
223
|
const descriptor = descriptors.get(name);
|
|
224
|
+
if (descriptor === undefined &&
|
|
225
|
+
(kind === "artifact-provider" ||
|
|
226
|
+
kind === "managed-provider" ||
|
|
227
|
+
kind === "native-provider"))
|
|
228
|
+
return { reason: "available", remediation: null };
|
|
162
229
|
if (descriptor === undefined)
|
|
163
230
|
return {
|
|
164
231
|
reason: "provider_missing",
|
|
@@ -184,6 +251,9 @@ const composedAvailability = (name, descriptors) => {
|
|
|
184
251
|
reason: "provider_missing",
|
|
185
252
|
remediation: "No provider composition is declared for this operation.",
|
|
186
253
|
};
|
|
254
|
+
return composedAvailabilityFor(requirements, descriptors);
|
|
255
|
+
};
|
|
256
|
+
const composedAvailabilityFor = (requirements, descriptors) => {
|
|
187
257
|
const missing = requirements.find((operation) => !descriptors.has(operation));
|
|
188
258
|
if (missing !== undefined)
|
|
189
259
|
return {
|
|
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { parse as parseToml } from "smol-toml";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { PRODUCT_IDENTITY } from "../identity.js";
|
|
6
|
+
import { MCP_STARTUP_POLICY } from "../mcpStartupPolicy.js";
|
|
6
7
|
import { supportedClients } from "./SupportedClients.js";
|
|
7
8
|
const objectSchema = z.record(z.string(), z.unknown());
|
|
8
9
|
const registrationSchema = z
|
|
@@ -41,7 +42,9 @@ export const readClientRegistrationStatuses = async (home, currentCommandPath =
|
|
|
41
42
|
};
|
|
42
43
|
const registrationAligned = (registration, client, currentCommandPath) => {
|
|
43
44
|
const command = [registration.command, ...registration.args];
|
|
44
|
-
if (client === "codex" &&
|
|
45
|
+
if (client === "codex" &&
|
|
46
|
+
registration.startup_timeout_sec !==
|
|
47
|
+
MCP_STARTUP_POLICY.codexStartupTimeoutSeconds)
|
|
45
48
|
return false;
|
|
46
49
|
if (command.length === 4 &&
|
|
47
50
|
command[0] === "npx" &&
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AnalysisCapabilityUnavailableError } from "../domain/errors.js";
|
|
2
2
|
import { err, ok } from "../domain/result.js";
|
|
3
|
+
import { closeAnalysisClient } from "./AnalysisClientCleanup.js";
|
|
3
4
|
/** Synthetic compatibility identity for a deterministic provider set. */
|
|
4
5
|
export const compositeProviderIdentity = (identities) => ({
|
|
5
6
|
id: `composite:${identities
|
|
@@ -61,6 +62,10 @@ export class CompositeProvider {
|
|
|
61
62
|
clients.set(provider, created);
|
|
62
63
|
return created;
|
|
63
64
|
};
|
|
65
|
+
const closeWithOutcome = async (options) => {
|
|
66
|
+
const outcomes = await Promise.all([...clients.entries()].map(([provider, client]) => closeAnalysisClient(client, provider.identity().id, options)));
|
|
67
|
+
return outcomes.find((outcome) => !outcome.ok) ?? ok(null);
|
|
68
|
+
};
|
|
64
69
|
return {
|
|
65
70
|
execute: (operation, parameters, options) => {
|
|
66
71
|
if (operation === "health")
|
|
@@ -80,8 +85,12 @@ export class CompositeProvider {
|
|
|
80
85
|
runtimeLineageSnapshots: () => [...clients.values()]
|
|
81
86
|
.flatMap((client) => client.runtimeLineageSnapshots?.() ?? [])
|
|
82
87
|
.sort((left, right) => left.provider.id.localeCompare(right.provider.id)),
|
|
88
|
+
requestActivitySnapshots: () => [...clients.values()]
|
|
89
|
+
.flatMap((client) => client.requestActivitySnapshots?.() ?? [])
|
|
90
|
+
.sort((left, right) => left.provider.id.localeCompare(right.provider.id)),
|
|
91
|
+
closeWithOutcome,
|
|
83
92
|
close: async () => {
|
|
84
|
-
await
|
|
93
|
+
await closeWithOutcome();
|
|
85
94
|
},
|
|
86
95
|
};
|
|
87
96
|
}
|
|
@@ -12,6 +12,7 @@ import { err, ok } from "../domain/result.js";
|
|
|
12
12
|
import { loadConfiguredPermissionAuthority } from "./PermissionConfiguration.js";
|
|
13
13
|
import { authorizeFileReadWithDeferredWrite, } from "./DeferredFileAuthorization.js";
|
|
14
14
|
import { REA_WORKFLOW_PROVIDER, workflowAnalysisProfile, } from "./InvestigationProviders.js";
|
|
15
|
+
import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
|
|
15
16
|
export { runCapabilityStatus, runProviderStatus, } from "./DirectAnalysisStatus.js";
|
|
16
17
|
/**
|
|
17
18
|
* Open one binary, execute one tool, and always release provider resources.
|
|
@@ -60,7 +61,7 @@ const authorizeAnalysis = async (authority, tool, arguments_) => {
|
|
|
60
61
|
cause: result.error,
|
|
61
62
|
}));
|
|
62
63
|
}
|
|
63
|
-
if (
|
|
64
|
+
if (["inventory_artifact", "inspect_artifact"].includes(tool) &&
|
|
64
65
|
arguments_.native_mount_approved === true) {
|
|
65
66
|
const result = await authority.authorize({
|
|
66
67
|
capability: "native_mount",
|
|
@@ -69,7 +70,7 @@ const authorizeAnalysis = async (authority, tool, arguments_) => {
|
|
|
69
70
|
environment_names: [],
|
|
70
71
|
network: "none",
|
|
71
72
|
mount: true,
|
|
72
|
-
operation_identity:
|
|
73
|
+
operation_identity: `${tool}:native_mount`,
|
|
73
74
|
}, "read");
|
|
74
75
|
if (!result.ok)
|
|
75
76
|
return err(result.error instanceof PermissionRequiredError
|
|
@@ -181,6 +182,7 @@ const executeAnalysisTool = async (input) => {
|
|
|
181
182
|
const { session, tool, signal, evidenceProfile } = input;
|
|
182
183
|
if (tool === "binary_overview" ||
|
|
183
184
|
tool === "analyze_function" ||
|
|
185
|
+
tool === "inspect_native_api" ||
|
|
184
186
|
tool === "trace_feature") {
|
|
185
187
|
const result = await new EnhancedTools(session).execute(tool, input.arguments, signal);
|
|
186
188
|
if (!result.ok)
|
|
@@ -212,6 +214,10 @@ const executeAnalysisTool = async (input) => {
|
|
|
212
214
|
rawResult: result.value.rawResult,
|
|
213
215
|
limitations: result.value.limitations,
|
|
214
216
|
locations: result.value.locations,
|
|
217
|
+
evidenceLinks: tool === "inspect_artifact"
|
|
218
|
+
? artifactInspectionResultSchema.parse(result.value.result)
|
|
219
|
+
.evidence_links
|
|
220
|
+
: [],
|
|
215
221
|
});
|
|
216
222
|
return { output: evidence, evidence };
|
|
217
223
|
};
|
|
@@ -242,15 +248,18 @@ const prepareSnapshot = async (options) => {
|
|
|
242
248
|
return err(new EvidenceIntegrityError("Analysis snapshot target does not match the requested binary"));
|
|
243
249
|
return ok({ snapshot: loaded.value });
|
|
244
250
|
};
|
|
245
|
-
const replayProviderFor = (session, tool) => tool
|
|
251
|
+
const replayProviderFor = (session, tool) => isWorkflowEvidenceTool(tool)
|
|
246
252
|
? REA_WORKFLOW_PROVIDER
|
|
247
253
|
: session.providerIdentity(tool);
|
|
248
254
|
const analysisProfileForEvidence = (session, tool) => {
|
|
249
|
-
if (tool
|
|
255
|
+
if (!isWorkflowEvidenceTool(tool))
|
|
250
256
|
return session.analysisProfile(tool);
|
|
251
257
|
const upstream = session.analysisProfile();
|
|
252
258
|
return upstream === undefined ? undefined : workflowAnalysisProfile(upstream);
|
|
253
259
|
};
|
|
260
|
+
const isWorkflowEvidenceTool = (tool) => tool === "binary_overview" ||
|
|
261
|
+
tool === "inspect_native_api" ||
|
|
262
|
+
tool === "trace_feature";
|
|
254
263
|
const fileExists = async (path) => {
|
|
255
264
|
try {
|
|
256
265
|
await access(path);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { AnalysisOutputError } from "../domain/errors.js";
|
|
2
|
+
import { parseAddressedPage } from "../domain/hopperValues.js";
|
|
3
|
+
import { err, ok } from "../domain/result.js";
|
|
4
|
+
const FEATURE_SEARCH_SOURCES = [
|
|
5
|
+
["list_strings", "string"],
|
|
6
|
+
["list_procedures", "procedure"],
|
|
7
|
+
];
|
|
8
|
+
const STRING_SEARCH_SOURCES = [
|
|
9
|
+
["list_strings", "string"],
|
|
10
|
+
];
|
|
11
|
+
/** Trace a bounded feature or string literal through provider xrefs. */
|
|
12
|
+
export const traceLiteralFeature = async (call, input, scope, signal) => {
|
|
13
|
+
const sources = scope === "feature" ? FEATURE_SEARCH_SOURCES : STRING_SEARCH_SOURCES;
|
|
14
|
+
const searched = await literalMatches(call, input, sources, signal);
|
|
15
|
+
if (!searched.ok)
|
|
16
|
+
return searched;
|
|
17
|
+
const residual = new Set();
|
|
18
|
+
const traced = await traceReferences(call, {
|
|
19
|
+
matches: searched.value.matches,
|
|
20
|
+
limit: input.limit,
|
|
21
|
+
operationBudget: input.max_operations - searched.value.operations,
|
|
22
|
+
...(signal === undefined ? {} : { signal }),
|
|
23
|
+
});
|
|
24
|
+
if (!traced.ok)
|
|
25
|
+
return traced;
|
|
26
|
+
for (const reason of traced.value.residual)
|
|
27
|
+
residual.add(reason);
|
|
28
|
+
const operations = searched.value.operations + traced.value.operations;
|
|
29
|
+
if (operations >= input.max_operations)
|
|
30
|
+
residual.add("Investigation reached the operation budget.");
|
|
31
|
+
if (searched.value.matches.length >= input.limit)
|
|
32
|
+
residual.add("Literal matches reached the configured limit.");
|
|
33
|
+
return ok({
|
|
34
|
+
query: input.query,
|
|
35
|
+
search_mode: "literal",
|
|
36
|
+
operations_used: operations,
|
|
37
|
+
operation_budget: input.max_operations,
|
|
38
|
+
matches: searched.value.matches,
|
|
39
|
+
references: traced.value.references,
|
|
40
|
+
truncated: residual.size > 0,
|
|
41
|
+
residual_unknowns: [...residual],
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const literalMatches = async (call, input, sources, signal) => {
|
|
45
|
+
let operations = 0;
|
|
46
|
+
const matches = [];
|
|
47
|
+
const needle = input.case_sensitive ? input.query : input.query.toLowerCase();
|
|
48
|
+
for (const [tool, type] of sources) {
|
|
49
|
+
let offset = 0;
|
|
50
|
+
while (operations < input.max_operations && matches.length < input.limit) {
|
|
51
|
+
operations += 1;
|
|
52
|
+
const result = await call(tool, { offset, limit: 500 }, signal);
|
|
53
|
+
if (!result.ok)
|
|
54
|
+
return result;
|
|
55
|
+
const page = parseAddressedPage(result.value);
|
|
56
|
+
if (!page.ok)
|
|
57
|
+
return page;
|
|
58
|
+
for (const item of page.value.items) {
|
|
59
|
+
const haystack = input.case_sensitive
|
|
60
|
+
? item.name
|
|
61
|
+
: item.name.toLowerCase();
|
|
62
|
+
if (haystack.includes(needle))
|
|
63
|
+
matches.push({ type, address: item.address, value: item.name });
|
|
64
|
+
if (matches.length >= input.limit)
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (!page.value.hasMore || page.value.nextOffset === null)
|
|
68
|
+
break;
|
|
69
|
+
offset = page.value.nextOffset;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return ok({ matches, operations });
|
|
73
|
+
};
|
|
74
|
+
const traceReferences = async (call, request) => {
|
|
75
|
+
let operations = 0;
|
|
76
|
+
const references = [];
|
|
77
|
+
const residual = new Set();
|
|
78
|
+
for (const match of request.matches) {
|
|
79
|
+
if (operations >= request.operationBudget) {
|
|
80
|
+
residual.add("Reference traversal stopped at the operation budget.");
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
operations += 1;
|
|
84
|
+
const xrefs = await call("xrefs", { address: match.address }, request.signal);
|
|
85
|
+
if (!xrefs.ok)
|
|
86
|
+
return xrefs;
|
|
87
|
+
if (!Array.isArray(xrefs.value))
|
|
88
|
+
return err(new AnalysisOutputError("xrefs", "provider returned a non-array result"));
|
|
89
|
+
for (const source of xrefs.value) {
|
|
90
|
+
if (typeof source !== "string")
|
|
91
|
+
return err(new AnalysisOutputError("xrefs", "provider returned a non-address value"));
|
|
92
|
+
if (operations >= request.operationBudget) {
|
|
93
|
+
residual.add("Containing-procedure resolution stopped at the operation budget.");
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
operations += 1;
|
|
97
|
+
const resolved = await call("resolve_containing_procedure", { address: source }, request.signal);
|
|
98
|
+
if (!resolved.ok)
|
|
99
|
+
return resolved;
|
|
100
|
+
references.push({
|
|
101
|
+
target_address: match.address,
|
|
102
|
+
source_address: source,
|
|
103
|
+
containing_procedure: resolved.value,
|
|
104
|
+
});
|
|
105
|
+
if (references.length >= request.limit) {
|
|
106
|
+
residual.add("Reference results reached the configured limit.");
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (references.length >= request.limit)
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
return ok({
|
|
114
|
+
references,
|
|
115
|
+
operations,
|
|
116
|
+
residual: [...residual],
|
|
117
|
+
});
|
|
118
|
+
};
|