rea-agents 1.6.0 → 2.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 +157 -66
- package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
- package/bridge/hopper_bridge.py +13 -3
- package/dist/application/AbortablePromise.js +25 -0
- package/dist/application/AnalysisProvider.js +3 -0
- package/dist/application/AnalysisProviderEvaluation.js +60 -0
- package/dist/application/AnalysisProviderRegistry.js +228 -0
- package/dist/application/AnalysisSnapshotCache.js +41 -21
- package/dist/application/AnalysisSnapshotFiles.js +5 -6
- package/dist/application/BinarySession.js +218 -101
- package/dist/application/CapabilityInventory.js +18 -0
- package/dist/application/CompositeProvider.js +20 -8
- package/dist/application/CrossVersionInvestigation.js +10 -6
- package/dist/application/DirectAnalysis.js +49 -35
- package/dist/application/Doctor.js +110 -8
- package/dist/application/ElectronBoundaryAnalysis.js +96 -0
- package/dist/application/ElectronBoundaryGraph.js +9 -0
- package/dist/application/ElectronBoundaryGraphContext.js +62 -0
- package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
- package/dist/application/ElectronBoundaryGraphNative.js +86 -0
- package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
- package/dist/application/EnhancedTools.js +39 -14
- package/dist/application/InvestigationProviders.js +41 -0
- package/dist/application/JavaScriptApplicationEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
- package/dist/application/JavaScriptApplicationService.js +74 -0
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
- package/dist/application/JavaScriptArtifactAnalysis.js +305 -0
- package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
- package/dist/application/JavaScriptArtifactFiles.js +231 -0
- package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
- package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
- package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
- package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
- package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
- package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
- package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +266 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- package/dist/application/MacHopper.js +20 -4
- package/dist/application/ManagedApplicationGraphService.js +55 -0
- package/dist/application/ManagedMemberComparisonService.js +122 -0
- package/dist/application/ManagedNativeVerificationService.js +39 -0
- package/dist/application/ManagedReconstructionService.js +58 -0
- package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
- package/dist/application/PermissionAuthority.js +1 -1
- package/dist/application/ProcessCaptureLifecycle.js +1 -1
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessSampling.js +16 -12
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/SessionProviderRouter.js +219 -0
- package/dist/application/Setup.js +84 -60
- package/dist/application/SetupClients.js +1 -1
- package/dist/application/SetupPlan.js +9 -2
- package/dist/application/SupportedClients.js +40 -16
- package/dist/application/Upgrade.js +1 -1
- package/dist/application/runtime.js +11 -6
- package/dist/artifacts/AsarArtifactReader.js +23 -3
- package/dist/browser/CdpBrowserProvider.js +4 -2
- package/dist/browser/CdpCaptureEvents.js +49 -0
- package/dist/browser/CdpElectronInspection.js +106 -162
- package/dist/browser/CdpElectronProvider.js +9 -3
- package/dist/browser/CdpElectronScriptEvents.js +69 -0
- package/dist/browser/CdpElectronScripts.js +94 -0
- package/dist/browser/CdpElectronWorkers.js +56 -0
- package/dist/browser/CdpEndpoint.js +14 -1
- package/dist/browser/CdpPageCapture.js +8 -4
- package/dist/catalogIdentity.js +6 -42
- package/dist/cli.js +446 -34
- package/dist/cliApplicationCommands.js +62 -0
- package/dist/cliBrowserAdvancedCommands.js +5 -4
- package/dist/cliBrowserCommands.js +5 -4
- package/dist/cliCommandNames.js +56 -0
- package/dist/cliElectronCommands.js +106 -3
- package/dist/cliEvidenceCommands.js +4 -3
- package/dist/cliInvestigationCommands.js +2 -1
- package/dist/cliJsonInput.js +54 -0
- package/dist/cliPolicyCommands.js +2 -1
- package/dist/cliProcessCommands.js +3 -2
- package/dist/config.js +101 -2
- package/dist/contracts/applicationToolContracts.js +91 -0
- package/dist/contracts/artifactToolContracts.js +5 -9
- package/dist/contracts/browserToolContracts.js +14 -62
- package/dist/contracts/electronToolContracts.js +45 -19
- package/dist/contracts/enhancedInputs.js +2 -2
- package/dist/contracts/errorSchemas.js +1 -0
- package/dist/contracts/investigationExamples.js +0 -6
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
- package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
- package/dist/contracts/managedToolContracts.js +153 -0
- package/dist/contracts/managedWorkflowExamples.js +346 -0
- package/dist/contracts/managedWorkflowToolContracts.js +203 -0
- package/dist/contracts/nativeToolContracts.js +2 -6
- package/dist/contracts/promptContracts.js +18 -2
- package/dist/contracts/providerSelection.js +28 -0
- package/dist/contracts/sessionLifecycleInputs.js +2 -0
- package/dist/contracts/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +69 -65
- package/dist/contracts/toolEffects.js +160 -0
- package/dist/contracts/toolOutputSchemas.js +152 -42
- package/dist/doctorRuntime.js +47 -0
- package/dist/domain/analysisProfile.js +78 -0
- package/dist/domain/analysisSnapshot.js +92 -46
- package/dist/domain/artifactComparison.js +3 -4
- package/dist/domain/artifactGraph.js +1 -0
- package/dist/domain/binaryTarget.js +4 -29
- package/dist/domain/browserObservation.js +3 -0
- package/dist/domain/bundleComparison.js +4 -4
- package/dist/domain/electronObservation.js +13 -0
- package/dist/domain/electronStaticAnalysis.js +9 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
- package/dist/domain/electronStaticAnalysisIpc.js +171 -0
- package/dist/domain/electronStaticAnalysisNative.js +164 -0
- package/dist/domain/electronStaticAnalysisTypes.js +1 -0
- package/dist/domain/electronStaticAnalysisValues.js +103 -0
- package/dist/domain/errors.js +148 -7
- package/dist/domain/evidence.js +44 -5
- package/dist/domain/evidenceBundle.js +9 -7
- package/dist/domain/functionComparison.js +9 -3
- package/dist/domain/functionComparisonNormalization.js +37 -0
- package/dist/domain/functionComparisonSchemas.js +3 -8
- package/dist/domain/functionDossierEvidence.js +4 -1
- package/dist/domain/hopperValues.js +66 -9
- package/dist/domain/inputIssueProjection.js +52 -0
- package/dist/domain/investigationWorkspace.js +2 -1
- package/dist/domain/javascriptApplicationAnalysis.js +155 -0
- package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
- package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
- package/dist/domain/javascriptApplicationGraph.js +282 -0
- package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
- package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionItems.js +221 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
- package/dist/domain/javascriptAstFingerprint.js +143 -0
- package/dist/domain/javascriptFeatureSeed.js +140 -0
- package/dist/domain/javascriptFeatureTrace.js +232 -0
- package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
- package/dist/domain/javascriptFeatureTraversal.js +88 -0
- package/dist/domain/javascriptNativeHandoff.js +85 -0
- package/dist/domain/javascriptReplay.js +361 -0
- package/dist/domain/javascriptRuntimeLoadState.js +106 -0
- package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
- package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
- package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
- package/dist/domain/javascriptStaticAnalysis.js +346 -0
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
- package/dist/domain/javascriptStaticAnalysisState.js +23 -0
- package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
- package/dist/domain/managedApplicationGraph.js +556 -0
- package/dist/domain/managedArtifact.js +444 -0
- package/dist/domain/managedMemberComparison.js +565 -0
- package/dist/domain/managedNativeVerification.js +408 -0
- package/dist/domain/managedReconstruction.js +183 -0
- package/dist/domain/managedRuntimeCorrelation.js +175 -0
- package/dist/domain/reconstructionVerificationSchemas.js +1 -3
- package/dist/dotnet/ManagedArtifactInspector.js +318 -0
- package/dist/dotnet/ManagedMemberInspector.js +887 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +368 -0
- package/dist/dotnet/ManagedMetadataLayout.js +322 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
- package/dist/dotnet/ManagedPeReader.js +157 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
- package/dist/ghidra/GhidraClient.js +400 -0
- package/dist/ghidra/GhidraClientTypes.js +1 -0
- package/dist/ghidra/GhidraDefaults.js +20 -0
- package/dist/ghidra/GhidraDiagnostics.js +43 -0
- package/dist/ghidra/GhidraDoctor.js +59 -0
- package/dist/ghidra/GhidraFunctionValues.js +264 -0
- package/dist/ghidra/GhidraInstallation.js +239 -0
- package/dist/ghidra/GhidraInventoryValues.js +217 -0
- package/dist/ghidra/GhidraLauncher.js +139 -0
- package/dist/ghidra/GhidraProvider.js +366 -0
- package/dist/ghidra/GhidraRequestQueue.js +98 -0
- package/dist/ghidra/GhidraResponseBuffer.js +31 -0
- package/dist/ghidra/GhidraResponseRouter.js +24 -0
- package/dist/ghidra/GhidraSessionError.js +39 -0
- package/dist/ghidra/GhidraSessionValues.js +62 -0
- package/dist/ghidra/GhidraSocketConnection.js +46 -0
- package/dist/ghidra/protocol.js +69 -0
- package/dist/hopper/BridgeLauncher.js +19 -54
- package/dist/hopper/HopperAnalysisProfile.js +106 -0
- package/dist/hopper/HopperClient.js +154 -162
- package/dist/hopper/HopperProvider.js +99 -7
- package/dist/hopper/HopperSessionValues.js +27 -0
- package/dist/hopper/HopperSocketConnection.js +31 -0
- package/dist/main.js +14 -0
- package/dist/native/NativeMacOSProvider.js +3 -1
- package/dist/process/PendingOperations.js +59 -0
- package/dist/process/PrivateRuntimeRoot.js +33 -0
- package/dist/process/ProviderDeadline.js +95 -0
- package/dist/process/ProviderProcess.js +275 -0
- package/dist/replay/JavaScriptReplayWorker.js +286 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/createServer.js +53 -0
- package/dist/server/promptCompletion.js +9 -3
- package/dist/server/registerApplicationTools.js +115 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +33 -8
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +45 -3
- package/dist/server/registerEnhancedTools.js +67 -19
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools.js +21 -17
- package/dist/server/registerManagedTools.js +6 -0
- package/dist/server/registerManagedWorkflowTools.js +277 -0
- package/dist/server/registerOfficialTools.js +11 -8
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionStatusTool.js +7 -7
- package/dist/server/registerSessionTools.js +57 -87
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +79 -2
- package/dist/server/toolResult.js +25 -17
- package/package.json +17 -7
- package/scripts/rea.mjs +36 -2
- package/skills/rea-analysis/SKILL.md +59 -3
- /package/dist/{application → process}/ProcessOwnership.js +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
3
|
+
import { err, ok } from "../domain/result.js";
|
|
4
|
+
const responseSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
id: z.number().int().positive(),
|
|
7
|
+
ok: z.boolean(),
|
|
8
|
+
result: jsonValueSchema.optional(),
|
|
9
|
+
error: z
|
|
10
|
+
.object({
|
|
11
|
+
code: z.string().min(1).max(128),
|
|
12
|
+
message: z.string().min(1).max(4_096),
|
|
13
|
+
})
|
|
14
|
+
.strict()
|
|
15
|
+
.optional(),
|
|
16
|
+
})
|
|
17
|
+
.strict()
|
|
18
|
+
.superRefine((value, context) => {
|
|
19
|
+
if (value.ok && value.result === undefined)
|
|
20
|
+
context.addIssue({
|
|
21
|
+
code: "custom",
|
|
22
|
+
path: ["result"],
|
|
23
|
+
message: "Successful Ghidra response requires a result",
|
|
24
|
+
});
|
|
25
|
+
if (!value.ok && value.error === undefined)
|
|
26
|
+
context.addIssue({
|
|
27
|
+
code: "custom",
|
|
28
|
+
path: ["error"],
|
|
29
|
+
message: "Failed Ghidra response requires an error",
|
|
30
|
+
});
|
|
31
|
+
if (!value.ok && value.result !== undefined)
|
|
32
|
+
context.addIssue({
|
|
33
|
+
code: "custom",
|
|
34
|
+
path: ["result"],
|
|
35
|
+
message: "Failed Ghidra response cannot include a result",
|
|
36
|
+
});
|
|
37
|
+
if (value.ok && value.error !== undefined)
|
|
38
|
+
context.addIssue({
|
|
39
|
+
code: "custom",
|
|
40
|
+
path: ["error"],
|
|
41
|
+
message: "Successful Ghidra response cannot include an error",
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
/** Expected operation failure returned by the authenticated Java bridge. */
|
|
45
|
+
export class GhidraRemoteError extends Error {
|
|
46
|
+
code;
|
|
47
|
+
constructor(code, message) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.code = code;
|
|
50
|
+
this.name = "GhidraRemoteError";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Parse one complete Java-bridge response without accepting extra fields. */
|
|
54
|
+
export const parseGhidraResponseLine = (line) => {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(line);
|
|
57
|
+
const response = responseSchema.safeParse(parsed);
|
|
58
|
+
return response.success
|
|
59
|
+
? ok(response.data)
|
|
60
|
+
: err(new Error("Ghidra bridge response has an invalid shape"));
|
|
61
|
+
}
|
|
62
|
+
catch (cause) {
|
|
63
|
+
return err(new Error("Ghidra bridge response is not valid JSON", { cause }));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
/** Project one validated response into its result or remote failure. */
|
|
67
|
+
export const ghidraResponseResult = (response) => response.ok
|
|
68
|
+
? ok(response.result ?? null)
|
|
69
|
+
: err(new GhidraRemoteError(response.error?.code ?? "bridge_error", response.error?.message ?? "Ghidra bridge request failed"));
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { execFile } from "node:child_process";
|
|
3
2
|
import { chmod, writeFile } from "node:fs/promises";
|
|
4
3
|
import { basename, dirname, extname, join } from "node:path";
|
|
5
4
|
import { promisify } from "node:util";
|
|
6
5
|
import { HopperCancelledError, HopperStartError } from "../domain/errors.js";
|
|
7
6
|
import { err, ok } from "../domain/result.js";
|
|
8
|
-
import { cleanupOwnedProcessGroup
|
|
7
|
+
import { cleanupOwnedProcessGroup } from "../process/ProcessOwnership.js";
|
|
8
|
+
import { spawnOwnedProviderProcess, } from "../process/ProviderProcess.js";
|
|
9
|
+
import { waitForAbortableDelay } from "../process/ProviderDeadline.js";
|
|
9
10
|
import writeFileAtomic from "write-file-atomic";
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
11
12
|
const HOPPER_BACKGROUND_STARTUP_MS = 5_000;
|
|
@@ -64,13 +65,14 @@ export class HopperApplicationLauncher {
|
|
|
64
65
|
return err(new HopperCancelledError());
|
|
65
66
|
const linuxDemo = linuxDemoLaunch(this.options, session, args);
|
|
66
67
|
const ownershipCommand = linuxDemo?.ownershipCommand ?? this.options.launcherPath;
|
|
67
|
-
const started = await
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
const started = await spawnOwnedProviderProcess({
|
|
69
|
+
command: linuxDemo?.command ?? this.options.launcherPath,
|
|
70
|
+
arguments: linuxDemo?.args ?? args,
|
|
71
|
+
runId: session.runId,
|
|
72
|
+
expectedCommand: ownershipCommand,
|
|
73
|
+
});
|
|
74
|
+
const child = started.process;
|
|
75
|
+
const pid = started.ownership.leaderPid;
|
|
74
76
|
const ownership = {
|
|
75
77
|
schema_version: 1,
|
|
76
78
|
run_id: session.runId,
|
|
@@ -84,18 +86,18 @@ export class HopperApplicationLauncher {
|
|
|
84
86
|
await writeFileAtomic(join(session.directory, "ownership.json"), `${JSON.stringify(ownership)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
85
87
|
}
|
|
86
88
|
catch (cause) {
|
|
87
|
-
await cleanupOwnedProcessGroup(
|
|
89
|
+
await cleanupOwnedProcessGroup(started.ownership);
|
|
88
90
|
return err(new HopperStartError({ cause }));
|
|
89
91
|
}
|
|
90
92
|
return ok({
|
|
91
93
|
process: child,
|
|
92
94
|
ownsProcessLifetime: true,
|
|
93
95
|
shutdownByCleanup: ownsProcessLifetime,
|
|
94
|
-
cleanup: () => cleanupOwnedProcessGroup(
|
|
96
|
+
cleanup: () => cleanupOwnedProcessGroup(started.ownership),
|
|
95
97
|
});
|
|
96
98
|
}
|
|
97
99
|
catch (cause) {
|
|
98
|
-
return err(
|
|
100
|
+
return err(hopperLaunchFailure(cause, options.signal));
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
}
|
|
@@ -117,34 +119,11 @@ const linuxDemoLaunch = (options, session, hopperArgs) => {
|
|
|
117
119
|
],
|
|
118
120
|
};
|
|
119
121
|
};
|
|
122
|
+
const hopperLaunchFailure = (cause, signal) => signal?.aborted === true
|
|
123
|
+
? new HopperCancelledError()
|
|
124
|
+
: new HopperStartError({ cause });
|
|
120
125
|
/** Select the version-pinned adapter from the caller's explicit launch mode. */
|
|
121
126
|
export const usesLinuxDemo = (options) => options.launchMode === "verified_linux_demo";
|
|
122
|
-
const spawnLauncher = async (command, args, runId) => {
|
|
123
|
-
const child = spawn(command, args, {
|
|
124
|
-
stdio: ["ignore", "ignore", "pipe"],
|
|
125
|
-
detached: process.platform !== "win32",
|
|
126
|
-
env: { ...process.env, REA_PROCESS_RUN_ID: runId },
|
|
127
|
-
});
|
|
128
|
-
return new Promise((resolve) => {
|
|
129
|
-
const onSpawn = () => {
|
|
130
|
-
child.off("error", onError);
|
|
131
|
-
resolve(ok(child));
|
|
132
|
-
};
|
|
133
|
-
const onError = (cause) => {
|
|
134
|
-
child.off("spawn", onSpawn);
|
|
135
|
-
resolve(err(new HopperStartError({ cause })));
|
|
136
|
-
};
|
|
137
|
-
child.once("spawn", onSpawn);
|
|
138
|
-
child.once("error", onError);
|
|
139
|
-
});
|
|
140
|
-
};
|
|
141
|
-
const ownedProcessGroup = (session, pid, launcherPath) => ({
|
|
142
|
-
runId: session.runId,
|
|
143
|
-
leaderPid: pid,
|
|
144
|
-
processGroupId: pid,
|
|
145
|
-
expectedCommand: launcherPath,
|
|
146
|
-
expectedParentPid: process.pid,
|
|
147
|
-
});
|
|
148
127
|
const prepareHopperApplication = async (launcherPath, signal) => {
|
|
149
128
|
const appBundle = hopperApplicationBundle(launcherPath);
|
|
150
129
|
if (appBundle === undefined)
|
|
@@ -160,22 +139,8 @@ const prepareHopperApplication = async (launcherPath, signal) => {
|
|
|
160
139
|
"-a",
|
|
161
140
|
appBundle,
|
|
162
141
|
]);
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
const waitForDelay = (milliseconds, signal) => {
|
|
166
|
-
if (signal?.aborted === true)
|
|
167
|
-
return Promise.resolve(false);
|
|
168
|
-
return new Promise((resolve) => {
|
|
169
|
-
const timer = setTimeout(() => {
|
|
170
|
-
signal?.removeEventListener("abort", onAbort);
|
|
171
|
-
resolve(true);
|
|
172
|
-
}, milliseconds);
|
|
173
|
-
const onAbort = () => {
|
|
174
|
-
clearTimeout(timer);
|
|
175
|
-
resolve(false);
|
|
176
|
-
};
|
|
177
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
178
|
-
});
|
|
142
|
+
return ((await waitForAbortableDelay(HOPPER_BACKGROUND_STARTUP_MS, signal)) ===
|
|
143
|
+
"elapsed");
|
|
179
144
|
};
|
|
180
145
|
const hopperApplicationBundle = (launcherPath) => {
|
|
181
146
|
if (basename(launcherPath) !== "hopper")
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { createAnalysisProfile } from "../domain/analysisProfile.js";
|
|
4
|
+
import { AnalysisCancelledError, ProviderAdapterError, } from "../domain/errors.js";
|
|
5
|
+
import { err, ok } from "../domain/result.js";
|
|
6
|
+
/** Resolve Hopper open semantics without placing its CLI flags in BinaryTarget. */
|
|
7
|
+
export const resolveHopperAnalysisProfile = async (target, options) => {
|
|
8
|
+
if (target.kind !== "executable" && target.kind !== "database")
|
|
9
|
+
return ok({ profile: null, compatibility: {} });
|
|
10
|
+
const derived = hopperLoaderArgsForTarget(target);
|
|
11
|
+
if (!derived.ok)
|
|
12
|
+
return derived;
|
|
13
|
+
const loaderArgs = options.loaderArgsOverride.length === 0
|
|
14
|
+
? derived.value
|
|
15
|
+
: [...options.loaderArgsOverride];
|
|
16
|
+
const compatibility = { loaderArgs: [...loaderArgs] };
|
|
17
|
+
const launcherDigest = await sha256File(options.launcherPath, options.signal);
|
|
18
|
+
if (!launcherDigest.ok)
|
|
19
|
+
return launcherDigest;
|
|
20
|
+
if (launcherDigest.value === undefined)
|
|
21
|
+
return ok({ profile: null, compatibility });
|
|
22
|
+
const provider = {
|
|
23
|
+
id: options.provider.id,
|
|
24
|
+
name: options.provider.name,
|
|
25
|
+
version: `launcher-sha256:${launcherDigest.value}`,
|
|
26
|
+
};
|
|
27
|
+
return ok({
|
|
28
|
+
profile: createAnalysisProfile(provider, 1, {
|
|
29
|
+
target_kind: target.kind,
|
|
30
|
+
target_format: target.format,
|
|
31
|
+
architecture: target.architecture ?? null,
|
|
32
|
+
available_architectures: [
|
|
33
|
+
...(target.availableArchitectures ?? []),
|
|
34
|
+
].sort(),
|
|
35
|
+
loader: {
|
|
36
|
+
source: options.loaderArgsOverride.length === 0
|
|
37
|
+
? "derived"
|
|
38
|
+
: "configured_override",
|
|
39
|
+
argument_count: loaderArgs.length,
|
|
40
|
+
arguments_sha256: createHash("sha256")
|
|
41
|
+
.update(JSON.stringify(loaderArgs))
|
|
42
|
+
.digest("hex"),
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
compatibility,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
/** Derive Hopper's complete non-interactive CLI loader selection. */
|
|
49
|
+
export const hopperLoaderArgsForTarget = (target) => {
|
|
50
|
+
if (target.kind === "database")
|
|
51
|
+
return ok([]);
|
|
52
|
+
const architecture = target.architecture;
|
|
53
|
+
if (architecture === undefined)
|
|
54
|
+
return err(new ProviderAdapterError("hopper", "resolve_analysis_profile"));
|
|
55
|
+
const flag = hopperArchitectureFlag(architecture);
|
|
56
|
+
switch (target.format) {
|
|
57
|
+
case "mach-o":
|
|
58
|
+
return ok((target.availableArchitectures?.length ?? 0) > 1
|
|
59
|
+
? ["-l", "FAT", flag, "-l", "Mach-O"]
|
|
60
|
+
: ["-l", "Mach-O", flag]);
|
|
61
|
+
case "elf":
|
|
62
|
+
return ok(["-l", "ELF", flag]);
|
|
63
|
+
case "pe":
|
|
64
|
+
return ok(["-l", "WinPE", flag]);
|
|
65
|
+
default:
|
|
66
|
+
return err(new ProviderAdapterError("hopper", "resolve_analysis_profile"));
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const hopperArchitectureFlag = (architecture) => {
|
|
70
|
+
switch (architecture) {
|
|
71
|
+
case "x86":
|
|
72
|
+
return "--intel-32";
|
|
73
|
+
case "x86_64":
|
|
74
|
+
return "--intel-64";
|
|
75
|
+
case "arm":
|
|
76
|
+
return "--armv7";
|
|
77
|
+
case "arm64":
|
|
78
|
+
return "--aarch64";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const sha256File = async (path, signal) => {
|
|
82
|
+
if (signalIsAborted(signal))
|
|
83
|
+
return err(new AnalysisCancelledError("open_binary"));
|
|
84
|
+
const stream = createReadStream(path);
|
|
85
|
+
const onAbort = () => {
|
|
86
|
+
stream.destroy();
|
|
87
|
+
};
|
|
88
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
89
|
+
try {
|
|
90
|
+
const hash = createHash("sha256");
|
|
91
|
+
for await (const chunk of stream)
|
|
92
|
+
hash.update(chunk);
|
|
93
|
+
return signalIsAborted(signal)
|
|
94
|
+
? err(new AnalysisCancelledError("open_binary"))
|
|
95
|
+
: ok(hash.digest("hex"));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return signalIsAborted(signal)
|
|
99
|
+
? err(new AnalysisCancelledError("open_binary"))
|
|
100
|
+
: ok(undefined);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
signal?.removeEventListener("abort", onAbort);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const signalIsAborted = (signal) => signal?.aborted === true;
|