rea-agents 2.0.0 → 2.2.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 +103 -38
- package/bridge/ghidra/ReaGhidraBridge.java +96 -11
- package/dist/application/AndroidApplicationService.js +17 -0
- package/dist/application/AppleApplicationService.js +17 -0
- package/dist/application/ArtifactExtraction.js +1 -1
- package/dist/application/ArtifactInventory/classify.js +42 -0
- package/dist/application/ArtifactInventory/hash.js +43 -0
- package/dist/application/ArtifactInventory/reader.js +41 -0
- package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
- package/dist/application/ArtifactInventory/scanReader.js +124 -0
- package/dist/application/ArtifactInventory/types.js +10 -0
- package/dist/application/ArtifactInventory.js +9 -351
- package/dist/application/AuthorizedArtifactInventory.js +2 -2
- package/dist/application/BinarySession.js +47 -270
- package/dist/application/BinarySessionExecution.js +28 -0
- package/dist/application/BinarySessionOpen.js +39 -0
- package/dist/application/BinarySessionRecords.js +149 -0
- package/dist/application/BinarySessionStatus.js +75 -0
- package/dist/application/CapabilityInventory.js +24 -2
- package/dist/application/ClientRegistrationStatus.js +11 -4
- package/dist/application/CrossVersionInventory.js +8 -2
- package/dist/application/CrossVersionInvestigation.js +1 -23
- package/dist/application/CrossVersionInvestigationCollections.js +23 -0
- package/dist/application/DirectAnalysis.js +47 -44
- package/dist/application/Doctor.js +156 -238
- package/dist/application/DoctorDiagnostics.js +180 -0
- package/dist/application/ElectronBoundaryAnalysis.js +23 -7
- package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
- package/dist/application/EnhancedToolPagination.js +22 -0
- package/dist/application/EnhancedToolTypes.js +4 -0
- package/dist/application/EnhancedTools.js +20 -37
- package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
- package/dist/application/InventoryProjectionEvidence.js +41 -0
- package/dist/application/InvestigationProviders.js +12 -0
- package/dist/application/InvestigationWorkspaceStore.js +24 -10
- package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
- package/dist/application/JavaScriptArtifactFiles.js +6 -2
- package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
- package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
- package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
- package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
- package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
- package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
- package/dist/application/JavaScriptJsonModules.js +39 -0
- package/dist/application/JavaScriptModuleRelationships.js +262 -0
- package/dist/application/JavaScriptReplayPermission.js +16 -0
- package/dist/application/JavaScriptReplayService.js +182 -156
- package/dist/application/LinuxHopper.js +10 -0
- package/dist/application/MacHopper.js +22 -11
- package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
- package/dist/application/PermissionAuthority.js +106 -63
- package/dist/application/ProcessCaptureCapability.js +1 -1
- package/dist/application/ProcessCapturePermission.js +13 -0
- package/dist/application/ProcessCli.js +2 -12
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessPairedExperiment.js +72 -0
- package/dist/application/ProjectPermissionStore.js +48 -9
- package/dist/application/RealHopperAssertions.js +76 -0
- package/dist/application/ReconstructionCoverageService.js +69 -0
- package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
- package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
- package/dist/application/Setup.js +148 -260
- package/dist/application/SetupClientConfiguration.js +224 -0
- package/dist/application/SetupClients.js +24 -2
- package/dist/application/SetupPlan.js +124 -12
- package/dist/application/SetupSkill.js +54 -36
- package/dist/application/SupportedClients.js +1 -0
- package/dist/application/Uninstall.js +12 -3
- package/dist/application/Upgrade.js +16 -1
- package/dist/application/WindowsCapabilities.js +86 -0
- package/dist/artifacts/AsarArtifactReader.js +11 -0
- package/dist/browser/BrowserProcessStartup.js +88 -0
- package/dist/browser/CdpCaptureDocuments.js +12 -8
- package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
- package/dist/browser/CdpCaptureEventHandlers.js +336 -0
- package/dist/browser/CdpCaptureEventHelpers.js +71 -0
- package/dist/browser/CdpCaptureEventState.js +1 -0
- package/dist/browser/CdpCaptureEventTypes.js +1 -0
- package/dist/browser/CdpCaptureEvents.js +29 -496
- package/dist/browser/CdpObservationSession.js +51 -8
- package/dist/browser/CdpPageCapture.js +12 -133
- package/dist/browser/CdpPageCaptureScripts.js +97 -0
- package/dist/browser/CdpPageCaptureWorkers.js +46 -0
- package/dist/browser/CdpWebMcpDiscovery.js +38 -14
- package/dist/browser/PngVisualDiff.js +9 -2
- package/dist/catalogIdentity.js +4 -0
- package/dist/cli/artifactCommands.js +82 -0
- package/dist/cli/coreAnalysisCommands.js +232 -0
- package/dist/cli/managedCommands.js +397 -0
- package/dist/cli/managedProjectGraphCommand.js +24 -0
- package/dist/cli/options.js +9 -0
- package/dist/cli/setupCommands.js +113 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utilityCommands.js +122 -0
- package/dist/cli.js +10 -768
- package/dist/cliApplicationCommands.js +118 -4
- package/dist/cliBrowserAdvancedCommands.js +73 -16
- package/dist/cliBrowserCommands.js +39 -115
- package/dist/cliCommandNames.js +4 -0
- package/dist/cliElectronCommands.js +33 -62
- package/dist/cliEvidenceCommands.js +13 -2
- package/dist/cliInvestigationCommands.js +53 -10
- package/dist/cliObservationOptions.js +137 -0
- package/dist/cliPolicyCommands.js +80 -46
- package/dist/cliSetup.js +306 -0
- package/dist/config/environment.js +107 -0
- package/dist/config/parseConfig.js +228 -0
- package/dist/config/parsers.js +68 -0
- package/dist/config/permissions.js +30 -0
- package/dist/config/types.js +1 -0
- package/dist/config.js +1 -375
- package/dist/contracts/applicationToolContracts.js +134 -0
- package/dist/contracts/electronToolContracts.js +51 -3
- package/dist/contracts/managedToolContracts.js +8 -3
- package/dist/contracts/managedWorkflowToolContracts.js +3 -3
- package/dist/contracts/providerSelection.js +2 -0
- package/dist/contracts/sessionStatusContract.js +26 -0
- package/dist/contracts/toolContractTypes.js +12 -1
- package/dist/contracts/toolContracts.js +5 -12
- package/dist/contracts/toolEffects.js +14 -0
- package/dist/contracts/toolOutputSchemaGroups.js +274 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
- package/dist/contracts/toolOutputSchemas.js +2 -550
- package/dist/domain/analysisErrorPresentation.js +234 -0
- package/dist/domain/analysisErrorProjection.js +284 -0
- package/dist/domain/androidApplication.js +206 -0
- package/dist/domain/appleApplication.js +221 -0
- package/dist/domain/artifactComparison.js +34 -15
- package/dist/domain/binaryTarget.js +87 -37
- package/dist/domain/boundedCartesianProjection.js +15 -0
- package/dist/domain/browserObservation.js +1 -285
- package/dist/domain/browserObservationSchemas.js +286 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
- package/dist/domain/errors.js +4 -486
- package/dist/domain/functionComparison.js +4 -165
- package/dist/domain/functionComparisonDimensions.js +164 -0
- package/dist/domain/javascriptApplicationVersionItems.js +18 -6
- package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
- package/dist/domain/javascriptExportInstrumentation.js +97 -0
- package/dist/domain/javascriptFeatureTraversal.js +19 -7
- package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
- package/dist/domain/javascriptSemanticAnalysis.js +386 -0
- package/dist/domain/javascriptSemanticIr.js +30 -0
- package/dist/domain/javascriptSemanticProjection.js +366 -0
- package/dist/domain/javascriptSemanticState.js +40 -0
- package/dist/domain/javascriptSemanticTraversal.js +22 -0
- package/dist/domain/javascriptSemanticValues.js +352 -0
- package/dist/domain/javascriptStaticAnalysis.js +5 -221
- package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
- package/dist/domain/knownPageCoverage.js +21 -0
- package/dist/domain/managedApplicationGraph.js +14 -364
- package/dist/domain/managedApplicationGraphCoverage.js +170 -0
- package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
- package/dist/domain/managedApplicationGraphNodes.js +336 -0
- package/dist/domain/managedArtifact.js +16 -8
- package/dist/domain/managedMemberComparison.js +37 -391
- package/dist/domain/managedMemberComparisonCoverage.js +72 -0
- package/dist/domain/managedMemberComparisonMatch.js +356 -0
- package/dist/domain/managedNativeVerification.js +27 -389
- package/dist/domain/managedNativeVerificationMatch.js +272 -0
- package/dist/domain/managedNativeVerificationSchemas.js +141 -0
- package/dist/domain/managedReconstruction.js +82 -69
- package/dist/domain/managedRuntimeCorrelation.js +12 -3
- package/dist/domain/nodeRuntimeCharacterization.js +66 -0
- package/dist/domain/permissionPolicy.js +2 -0
- package/dist/domain/processComparison.js +19 -0
- package/dist/domain/processPairedExperiment.js +128 -0
- package/dist/domain/reconstructionCoverage.js +387 -0
- package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
- package/dist/domain/reconstructionVerificationSchemas.js +8 -0
- package/dist/domain/replayMachine.js +316 -0
- package/dist/domain/replayMachineRuntime.js +215 -0
- package/dist/domain/runtimeCharacterization.js +85 -0
- package/dist/domain/runtimeIdentification.js +210 -0
- package/dist/domain/webBundleAnalyzer.js +3 -409
- package/dist/domain/webBundleAnalyzerAst.js +77 -0
- package/dist/domain/webBundleAnalyzerInspection.js +307 -0
- package/dist/domain/webBundleAnalyzerResult.js +120 -0
- package/dist/dotnet/ManagedArtifactInspector.js +50 -29
- package/dist/dotnet/ManagedMemberInspector.js +51 -785
- package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
- package/dist/dotnet/ManagedMemberRows.js +120 -0
- package/dist/dotnet/ManagedMetadataInventory.js +42 -306
- package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
- package/dist/dotnet/ManagedMetadataLayout.js +48 -111
- package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
- package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
- package/dist/dotnet/ManagedPeReader.js +59 -28
- package/dist/evaluation/CodexAgentEval.js +132 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraClient.js +76 -91
- package/dist/ghidra/GhidraClientStartup.js +29 -0
- package/dist/ghidra/GhidraClientWire.js +34 -0
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraDiagnostics.js +6 -1
- package/dist/ghidra/GhidraInstallation.js +20 -15
- package/dist/ghidra/GhidraLauncher.js +93 -29
- package/dist/ghidra/GhidraProvider.js +61 -161
- package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
- package/dist/ghidra/GhidraSessionValues.js +2 -0
- package/dist/ghidra/GhidraSocketConnection.js +44 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
- package/dist/ghidra/GhidraTransport.js +71 -0
- package/dist/identity.js +4 -2
- package/dist/main/messages.js +5 -0
- package/dist/main/reload.js +49 -0
- package/dist/main/shutdown.js +25 -0
- package/dist/main/startup.js +14 -0
- package/dist/main/state.js +26 -0
- package/dist/main/transport.js +61 -0
- package/dist/main/types.js +1 -0
- package/dist/main.js +35 -146
- package/dist/process/PrivateRuntimeRoot.js +3 -2
- package/dist/process/ProcessOwnership.js +35 -0
- package/dist/process/ProviderProcess.js +2 -0
- package/dist/replay/JavaScriptReplayWorker.js +64 -50
- package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
- package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
- package/dist/replay/ReplayOutcome.js +95 -0
- package/dist/replay/ReplayProcessLifecycle.js +71 -0
- package/dist/server/ProcessCaptureElicitation.js +157 -0
- package/dist/server/createServer.js +124 -56
- package/dist/server/javascriptApplicationResult.js +85 -0
- package/dist/server/registerApplicationTools/characterization.js +57 -0
- package/dist/server/registerApplicationTools/compareVersions.js +34 -0
- package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
- package/dist/server/registerApplicationTools/coverage.js +84 -0
- package/dist/server/registerApplicationTools/helpers.js +42 -0
- package/dist/server/registerApplicationTools/traceFeature.js +26 -0
- package/dist/server/registerApplicationTools/types.js +1 -0
- package/dist/server/registerApplicationTools.js +10 -112
- package/dist/server/registerBrowserTools.js +59 -83
- package/dist/server/registerElectronTools.js +48 -49
- package/dist/server/registerEnhancedTools.js +34 -42
- package/dist/server/registerEvidenceResources.js +56 -0
- package/dist/server/registerInvestigationTools/helpers.js +74 -0
- package/dist/server/registerInvestigationTools/types.js +1 -0
- package/dist/server/registerInvestigationTools.js +83 -116
- package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
- package/dist/server/registerManagedTools.js +18 -1
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
- package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
- package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
- package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
- package/dist/server/registerManagedWorkflowTools/types.js +1 -0
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
- package/dist/server/registerManagedWorkflowTools.js +10 -274
- package/dist/server/registerOfficialTools.js +73 -59
- package/dist/server/registerSessionRecordTools.js +137 -0
- package/dist/server/registerSessionStatusTool.js +97 -26
- package/dist/server/registerSessionTools.js +60 -164
- package/dist/server/sessionAvailabilityPolicy.js +10 -0
- package/dist/server/toolRegistrationOptions.js +101 -3
- package/dist/server/toolResult.js +15 -4
- package/package.json +11 -11
- package/skills/reverse-engineer-anything/SKILL.md +76 -0
- package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
- package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
- package/scripts/prepare-node-pty.mjs +0 -35
- package/scripts/rebuild-native.mjs +0 -22
- package/skills/rea-analysis/SKILL.md +0 -222
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, mkdir, open, readFile, realpath, unlink, } from "node:fs/promises";
|
|
3
4
|
import { dirname } from "node:path";
|
|
4
5
|
import { setTimeout as delay } from "node:timers/promises";
|
|
5
6
|
import writeFileAtomic from "write-file-atomic";
|
|
@@ -22,6 +23,7 @@ const grantSchema = z.object({
|
|
|
22
23
|
"native_mount",
|
|
23
24
|
"reference_read",
|
|
24
25
|
"javascript_replay",
|
|
26
|
+
"managed_runtime",
|
|
25
27
|
]),
|
|
26
28
|
roots: z.array(z.string()),
|
|
27
29
|
executables: z.array(z.string()),
|
|
@@ -66,11 +68,17 @@ export const readProjectPermissionStore = async (path, projectRoot) => {
|
|
|
66
68
|
const project = await identifyPermissionProject(projectRoot);
|
|
67
69
|
if (!project.ok)
|
|
68
70
|
return project;
|
|
71
|
+
if (process.getuid === undefined)
|
|
72
|
+
return err(new ProjectPermissionStoreError("not_owner_only"));
|
|
73
|
+
let handle;
|
|
69
74
|
try {
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
return
|
|
73
|
-
|
|
75
|
+
const opened = await openPrivateStore(path);
|
|
76
|
+
if (!opened.ok)
|
|
77
|
+
return opened;
|
|
78
|
+
if (opened.value === null)
|
|
79
|
+
return ok(null);
|
|
80
|
+
handle = opened.value;
|
|
81
|
+
const encoded = await handle.readFile("utf8");
|
|
74
82
|
let decoded;
|
|
75
83
|
try {
|
|
76
84
|
decoded = JSON.parse(encoded);
|
|
@@ -90,6 +98,9 @@ export const readProjectPermissionStore = async (path, projectRoot) => {
|
|
|
90
98
|
return ok(null);
|
|
91
99
|
return err(new ProjectPermissionStoreError("io", { cause }));
|
|
92
100
|
}
|
|
101
|
+
finally {
|
|
102
|
+
await handle?.close().catch(() => undefined);
|
|
103
|
+
}
|
|
93
104
|
};
|
|
94
105
|
/** Atomically replace explicit project grants with owner-only permissions. */
|
|
95
106
|
export const writeProjectPermissionStore = async (path, projectRoot, grants) => {
|
|
@@ -104,16 +115,20 @@ export const writeProjectPermissionStore = async (path, projectRoot, grants) =>
|
|
|
104
115
|
});
|
|
105
116
|
if (!candidate.success)
|
|
106
117
|
return err(new ProjectPermissionStoreError("invalid", { cause: candidate.error }));
|
|
118
|
+
if (process.getuid === undefined)
|
|
119
|
+
return err(new ProjectPermissionStoreError("not_owner_only"));
|
|
107
120
|
try {
|
|
108
121
|
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
109
122
|
await writeFileAtomic(path, `${JSON.stringify(candidate.data, null, 2)}\n`, {
|
|
110
123
|
encoding: "utf8",
|
|
111
124
|
mode: 0o600,
|
|
112
125
|
});
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
return
|
|
116
|
-
|
|
126
|
+
const verified = await readProjectPermissionStore(path, projectRoot);
|
|
127
|
+
if (!verified.ok)
|
|
128
|
+
return verified;
|
|
129
|
+
if (verified.value === null ||
|
|
130
|
+
JSON.stringify(verified.value) !== JSON.stringify(candidate.data))
|
|
131
|
+
return err(new ProjectPermissionStoreError("invalid"));
|
|
117
132
|
return ok(candidate.data);
|
|
118
133
|
}
|
|
119
134
|
catch (cause) {
|
|
@@ -209,7 +224,31 @@ const releasePermissionStoreLock = async (lock) => {
|
|
|
209
224
|
await unlink(lock.path).catch(() => undefined);
|
|
210
225
|
};
|
|
211
226
|
const isNotFound = (cause) => errorCode(cause) === "ENOENT";
|
|
227
|
+
const openPrivateStore = async (path) => {
|
|
228
|
+
let handle;
|
|
229
|
+
try {
|
|
230
|
+
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
231
|
+
const metadata = await handle.stat();
|
|
232
|
+
if (!privateRegularFile(metadata)) {
|
|
233
|
+
await handle.close();
|
|
234
|
+
return err(new ProjectPermissionStoreError("not_owner_only"));
|
|
235
|
+
}
|
|
236
|
+
return ok(handle);
|
|
237
|
+
}
|
|
238
|
+
catch (cause) {
|
|
239
|
+
await handle?.close().catch(() => undefined);
|
|
240
|
+
if (isNotFound(cause))
|
|
241
|
+
return ok(null);
|
|
242
|
+
return err(new ProjectPermissionStoreError(isSymlinkRefusal(cause) ? "not_owner_only" : "io", { cause }));
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const privateRegularFile = (metadata) => metadata.isFile() &&
|
|
246
|
+
!metadata.isSymbolicLink() &&
|
|
247
|
+
(Number(metadata.mode) & 0o077) === 0 &&
|
|
248
|
+
process.getuid !== undefined &&
|
|
249
|
+
metadata.uid === process.getuid();
|
|
212
250
|
const isAlreadyExists = (cause) => errorCode(cause) === "EEXIST";
|
|
213
251
|
const errorCode = (cause) => typeof cause === "object" && cause !== null && "code" in cause
|
|
214
252
|
? cause.code
|
|
215
253
|
: undefined;
|
|
254
|
+
const isSymlinkRefusal = (cause) => errorCode(cause) === "ELOOP" || errorCode(cause) === "EMLINK";
|
|
@@ -61,6 +61,81 @@ export const requireFunctionDossier = (input, expectedAddress) => {
|
|
|
61
61
|
validateInstructionScan(value.instruction_scan);
|
|
62
62
|
return value;
|
|
63
63
|
};
|
|
64
|
+
/** Reject a structurally valid dossier that omits fixture-proven semantics. */
|
|
65
|
+
export const requireFunctionDossierOracle = (input, oracle) => {
|
|
66
|
+
const value = requireFunctionDossier(input, oracle.procedure_address);
|
|
67
|
+
if (oracle.callee_address !== undefined) {
|
|
68
|
+
const callees = z
|
|
69
|
+
.array(identitySchema)
|
|
70
|
+
.parse(collectionSchema.parse(value.callees).items);
|
|
71
|
+
if (!callees.some(({ address }) => address === oracle.callee_address))
|
|
72
|
+
throw new TypeError("analyze_function omitted the expected fixture callee");
|
|
73
|
+
const references = z
|
|
74
|
+
.array(z.object({
|
|
75
|
+
source_address: addressSchema,
|
|
76
|
+
target_address: addressSchema,
|
|
77
|
+
}))
|
|
78
|
+
.parse(collectionSchema.parse(value.outgoing_references).items);
|
|
79
|
+
if (!references.some(({ target_address }) => target_address === oracle.callee_address))
|
|
80
|
+
throw new TypeError("analyze_function omitted the expected fixture reference");
|
|
81
|
+
}
|
|
82
|
+
if (oracle.caller_address !== undefined) {
|
|
83
|
+
const callers = z
|
|
84
|
+
.array(identitySchema)
|
|
85
|
+
.parse(collectionSchema.parse(value.callers).items);
|
|
86
|
+
if (!callers.some(({ address }) => address === oracle.caller_address))
|
|
87
|
+
throw new TypeError("analyze_function omitted the expected fixture caller");
|
|
88
|
+
}
|
|
89
|
+
if (oracle.referenced_string !== undefined)
|
|
90
|
+
requireReferencedValue(value, "referenced_strings", oracle.referenced_string, (candidate) => candidate);
|
|
91
|
+
if (oracle.referenced_name !== undefined)
|
|
92
|
+
requireReferencedValue(value, "referenced_names", oracle.referenced_name, (candidate) => candidate.replace(/^_+/u, ""));
|
|
93
|
+
if (oracle.comment !== undefined) {
|
|
94
|
+
const comments = z
|
|
95
|
+
.array(z.object({
|
|
96
|
+
address: addressSchema,
|
|
97
|
+
kind: z.enum(["comment", "inline"]),
|
|
98
|
+
text: z.string(),
|
|
99
|
+
}))
|
|
100
|
+
.parse(collectionSchema.parse(value.comments).items);
|
|
101
|
+
if (!comments.some(({ address, kind, text }) => address === oracle.procedure_address &&
|
|
102
|
+
kind === "comment" &&
|
|
103
|
+
text === oracle.comment))
|
|
104
|
+
throw new TypeError("analyze_function omitted the verifier comment");
|
|
105
|
+
}
|
|
106
|
+
if (oracle.require_assembly === true &&
|
|
107
|
+
collectionSchema.parse(value.assembly).items.length === 0)
|
|
108
|
+
throw new TypeError("analyze_function omitted fixture assembly");
|
|
109
|
+
if (oracle.require_cfg_successor) {
|
|
110
|
+
const blocks = z
|
|
111
|
+
.array(z.object({ successors: z.array(addressSchema) }))
|
|
112
|
+
.parse(collectionSchema.parse(value.basic_blocks).items);
|
|
113
|
+
if (!blocks.some(({ successors }) => successors.length > 0))
|
|
114
|
+
throw new TypeError("analyze_function omitted a real CFG successor");
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
};
|
|
118
|
+
const referencedValueSchema = z.object({
|
|
119
|
+
address: addressSchema,
|
|
120
|
+
value: z.string(),
|
|
121
|
+
source_address: addressSchema,
|
|
122
|
+
});
|
|
123
|
+
const referenceEdgeSchema = z.object({
|
|
124
|
+
source_address: addressSchema,
|
|
125
|
+
target_address: addressSchema,
|
|
126
|
+
});
|
|
127
|
+
const requireReferencedValue = (dossier, field, expected, normalize) => {
|
|
128
|
+
const values = z
|
|
129
|
+
.array(referencedValueSchema)
|
|
130
|
+
.parse(collectionSchema.parse(dossier[field]).items);
|
|
131
|
+
const references = z
|
|
132
|
+
.array(referenceEdgeSchema)
|
|
133
|
+
.parse(collectionSchema.parse(dossier.outgoing_references).items);
|
|
134
|
+
const found = values.some(({ address, value, source_address: sourceAddress }) => normalize(value) === expected &&
|
|
135
|
+
references.some(({ source_address, target_address }) => source_address === sourceAddress && target_address === address));
|
|
136
|
+
if (!found)
|
|
137
|
+
throw new TypeError(`analyze_function omitted the expected fixture ${field === "referenced_strings" ? "string" : "name"}`);
|
|
138
|
+
};
|
|
64
139
|
/** Require every relationship to be an actual hexadecimal address. */
|
|
65
140
|
export const requireAddressArray = (input, operation) => {
|
|
66
141
|
const parsed = z.array(addressSchema).parse(input);
|
|
@@ -69,6 +144,7 @@ export const requireAddressArray = (input, operation) => {
|
|
|
69
144
|
return parsed;
|
|
70
145
|
};
|
|
71
146
|
const COLLECTION_FIELDS = [
|
|
147
|
+
"assembly",
|
|
72
148
|
"comments",
|
|
73
149
|
"callers",
|
|
74
150
|
"callees",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AnalysisCancelledError, AnalysisInputError, } from "../domain/errors.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
import { evaluateReconstructionClosure, reconstructionCoverageWorkspaceSchema, } from "../domain/reconstructionCoverage.js";
|
|
5
|
+
import { err, ok } from "../domain/result.js";
|
|
6
|
+
import { readReconstructionCoverageWorkspace, writeReconstructionCoverageWorkspace, } from "./ReconstructionCoverageWorkspaceStore.js";
|
|
7
|
+
const COMMIT_OPERATION = "commit_reconstruction_coverage";
|
|
8
|
+
const QUERY_OPERATION = "query_reconstruction_coverage";
|
|
9
|
+
const isAborted = (signal) => signal?.aborted === true;
|
|
10
|
+
export const reconstructionCoverageCommitInputSchema = z.strictObject({
|
|
11
|
+
approved: z.literal(true),
|
|
12
|
+
workspace_path: z.string().min(1).max(4_096),
|
|
13
|
+
expected_revision: z.number().int().min(1).nullable(),
|
|
14
|
+
workspace: reconstructionCoverageWorkspaceSchema,
|
|
15
|
+
});
|
|
16
|
+
export const reconstructionCoverageQueryInputSchema = z.strictObject({
|
|
17
|
+
workspace_path: z.string().min(1).max(4_096),
|
|
18
|
+
boundary_id: z.string().min(1).max(200),
|
|
19
|
+
});
|
|
20
|
+
export const reconstructionCoverageCommitOutputSchema = z.strictObject({
|
|
21
|
+
workspace_path: z.string(),
|
|
22
|
+
bytes: z.number().int().min(0),
|
|
23
|
+
workspace_id: z.string().regex(/^rcw_[a-f0-9]{64}$/u),
|
|
24
|
+
revision: z.number().int().min(1),
|
|
25
|
+
revision_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
26
|
+
evidence_records: z.number().int().min(0),
|
|
27
|
+
});
|
|
28
|
+
/** Commit one caller-approved canonical reconstruction coverage CAS revision. */
|
|
29
|
+
export const commitReconstructionCoverage = async (rawInput, policy, options = {}) => {
|
|
30
|
+
const parsed = reconstructionCoverageCommitInputSchema.safeParse(rawInput);
|
|
31
|
+
if (!parsed.success)
|
|
32
|
+
return err(new AnalysisInputError(COMMIT_OPERATION, { cause: parsed.error }));
|
|
33
|
+
if (isAborted(options.signal))
|
|
34
|
+
return err(new AnalysisCancelledError(COMMIT_OPERATION));
|
|
35
|
+
const written = await writeReconstructionCoverageWorkspace(parsed.data.workspace, parsed.data.workspace_path, parsed.data.expected_revision, policy);
|
|
36
|
+
if (!written.ok)
|
|
37
|
+
return written;
|
|
38
|
+
return ok(jsonValueSchema.parse({
|
|
39
|
+
workspace_path: written.value.path,
|
|
40
|
+
bytes: written.value.bytes,
|
|
41
|
+
workspace_id: parsed.data.workspace.workspace_id,
|
|
42
|
+
revision: parsed.data.workspace.revision,
|
|
43
|
+
revision_sha256: parsed.data.workspace.revision_sha256,
|
|
44
|
+
evidence_records: parsed.data.workspace.evidence_bundle.records.length,
|
|
45
|
+
}));
|
|
46
|
+
};
|
|
47
|
+
/** Read one coverage revision and evaluate a named fail-closed boundary. */
|
|
48
|
+
export const queryReconstructionCoverage = async (rawInput, policy, nowEpochMs, options = {}) => {
|
|
49
|
+
const parsed = reconstructionCoverageQueryInputSchema.safeParse(rawInput);
|
|
50
|
+
if (!parsed.success)
|
|
51
|
+
return err(new AnalysisInputError(QUERY_OPERATION, { cause: parsed.error }));
|
|
52
|
+
if (isAborted(options.signal))
|
|
53
|
+
return err(new AnalysisCancelledError(QUERY_OPERATION));
|
|
54
|
+
const loaded = await readReconstructionCoverageWorkspace(parsed.data.workspace_path, policy);
|
|
55
|
+
if (!loaded.ok)
|
|
56
|
+
return loaded;
|
|
57
|
+
if (loaded.value === null)
|
|
58
|
+
return err(new AnalysisInputError(QUERY_OPERATION, {
|
|
59
|
+
cause: new TypeError("Reconstruction coverage workspace does not exist"),
|
|
60
|
+
}));
|
|
61
|
+
if (isAborted(options.signal))
|
|
62
|
+
return err(new AnalysisCancelledError(QUERY_OPERATION));
|
|
63
|
+
try {
|
|
64
|
+
return ok(jsonValueSchema.parse(evaluateReconstructionClosure(loaded.value, parsed.data.boundary_id, nowEpochMs)));
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
return err(new AnalysisInputError(QUERY_OPERATION, { cause }));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { InvestigationWorkspaceError } from "../domain/errors.js";
|
|
2
|
+
import { parseReconstructionCoverageWorkspace, serializeReconstructionCoverageWorkspace, } from "../domain/reconstructionCoverage.js";
|
|
3
|
+
import { err, ok } from "../domain/result.js";
|
|
4
|
+
import { readRevisionedWorkspace, writeRevisionedWorkspace, } from "./InvestigationWorkspaceStore.js";
|
|
5
|
+
const codec = {
|
|
6
|
+
parse: parseReconstructionCoverageWorkspace,
|
|
7
|
+
serialize: serializeReconstructionCoverageWorkspace,
|
|
8
|
+
validateNext: (current, next, expectedRevision) => {
|
|
9
|
+
if ((current?.revision ?? null) !== expectedRevision)
|
|
10
|
+
return err(new InvestigationWorkspaceError("update", "revision-conflict"));
|
|
11
|
+
if (current === null)
|
|
12
|
+
return next.revision === 1 && next.previous_revision_sha256 === null
|
|
13
|
+
? ok(null)
|
|
14
|
+
: err(new InvestigationWorkspaceError("update", "revision-conflict"));
|
|
15
|
+
if (current.name !== next.name ||
|
|
16
|
+
current.workspace_id !== next.workspace_id)
|
|
17
|
+
return err(new InvestigationWorkspaceError("update", "name-conflict"));
|
|
18
|
+
return next.revision === current.revision + 1 &&
|
|
19
|
+
next.previous_revision_sha256 === current.revision_sha256
|
|
20
|
+
? ok(null)
|
|
21
|
+
: err(new InvestigationWorkspaceError("update", "revision-conflict"));
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
/** Read a validated reconstruction coverage workspace revision. */
|
|
25
|
+
export const readReconstructionCoverageWorkspace = (path, policy) => readRevisionedWorkspace(path, policy, codec);
|
|
26
|
+
/** Atomically append one reconstruction coverage workspace CAS revision. */
|
|
27
|
+
export const writeReconstructionCoverageWorkspace = (workspace, path, expectedRevision, policy) => writeRevisionedWorkspace(path, policy, {
|
|
28
|
+
document: workspace,
|
|
29
|
+
expectedRevision,
|
|
30
|
+
codec,
|
|
31
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { access, realpath } from "node:fs/promises";
|
|
4
|
+
import { delimiter, join } from "node:path";
|
|
5
|
+
const TOOL_NAMES = ["node", "npm", "npx"];
|
|
6
|
+
const MAX_CANDIDATES_PER_TOOL = 16;
|
|
7
|
+
const MAX_OUTPUT_BYTES = 65_536;
|
|
8
|
+
const MAX_DIAGNOSTIC_BYTES = 4_096;
|
|
9
|
+
const PROBE_CONCURRENCY = 4;
|
|
10
|
+
/** Inventory and bounded-probe Node toolchain candidates under one exact PATH. */
|
|
11
|
+
export const inspectRuntimeExecutables = async (options) => {
|
|
12
|
+
const pathEntries = unique(options.path.split(delimiter).filter(Boolean));
|
|
13
|
+
const candidates = [
|
|
14
|
+
{
|
|
15
|
+
tool: "node",
|
|
16
|
+
path: options.launcherNode,
|
|
17
|
+
pathIndex: null,
|
|
18
|
+
selection: "rea-launcher",
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
for (const tool of TOOL_NAMES) {
|
|
22
|
+
const discovered = await discoverToolCandidates(tool, pathEntries, options.platform, options.pathExtensions);
|
|
23
|
+
for (const [index, candidate] of discovered.entries())
|
|
24
|
+
candidates.push({
|
|
25
|
+
tool,
|
|
26
|
+
path: candidate.path,
|
|
27
|
+
pathIndex: candidate.pathIndex,
|
|
28
|
+
selection: index === 0 ? "path-primary" : "path-shadowed",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const seen = new Set();
|
|
32
|
+
const uniqueCandidates = [];
|
|
33
|
+
for (const candidate of candidates) {
|
|
34
|
+
const key = `${candidate.tool}\0${candidate.path}`;
|
|
35
|
+
if (seen.has(key))
|
|
36
|
+
continue;
|
|
37
|
+
seen.add(key);
|
|
38
|
+
uniqueCandidates.push(candidate);
|
|
39
|
+
}
|
|
40
|
+
const diagnostics = await mapConcurrentBounded(uniqueCandidates, PROBE_CONCURRENCY, (candidate) => probeCandidate(candidate, options.timeoutMs ?? 5_000, options.path));
|
|
41
|
+
return {
|
|
42
|
+
launcher_node: await canonicalPath(options.launcherNode),
|
|
43
|
+
candidates: diagnostics,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const discoverToolCandidates = async (tool, pathEntries, platform, configuredExtensions) => {
|
|
47
|
+
const extensions = platform === "win32"
|
|
48
|
+
? (configuredExtensions ?? [".COM", ".EXE", ".BAT", ".CMD"])
|
|
49
|
+
: [""];
|
|
50
|
+
const candidates = [];
|
|
51
|
+
for (const [pathIndex, directory] of pathEntries.entries()) {
|
|
52
|
+
for (const extension of extensions) {
|
|
53
|
+
const path = join(directory, `${tool}${extension}`);
|
|
54
|
+
if (await isExecutable(path, platform)) {
|
|
55
|
+
candidates.push({ path, pathIndex });
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (candidates.length >= MAX_CANDIDATES_PER_TOOL)
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return candidates;
|
|
63
|
+
};
|
|
64
|
+
const probeCandidate = async (candidate, timeoutMs, effectivePath) => {
|
|
65
|
+
const canonical = await canonicalPath(candidate.path);
|
|
66
|
+
const result = await executeVersion(candidate.path, timeoutMs, effectivePath);
|
|
67
|
+
return {
|
|
68
|
+
tool: candidate.tool,
|
|
69
|
+
lexical_path: candidate.path,
|
|
70
|
+
canonical_path: canonical,
|
|
71
|
+
path_index: candidate.pathIndex,
|
|
72
|
+
selection: candidate.selection,
|
|
73
|
+
version: result.ok ? result.version : null,
|
|
74
|
+
healthy: result.ok,
|
|
75
|
+
failure: result.ok ? null : result.failure,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
const executeVersion = (path, timeoutMs, effectivePath) => new Promise((resolve) => {
|
|
79
|
+
const child = execFile(path, ["--version"], {
|
|
80
|
+
encoding: "utf8",
|
|
81
|
+
timeout: timeoutMs,
|
|
82
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
83
|
+
windowsHide: true,
|
|
84
|
+
env: environmentWithPath(effectivePath),
|
|
85
|
+
}, (cause, stdout, stderr) => {
|
|
86
|
+
if (cause === null) {
|
|
87
|
+
const version = firstNonemptyLine(stdout) ?? firstNonemptyLine(stderr);
|
|
88
|
+
if (version !== undefined)
|
|
89
|
+
resolve({ ok: true, version });
|
|
90
|
+
else
|
|
91
|
+
resolve({
|
|
92
|
+
ok: false,
|
|
93
|
+
failure: failure("runtime_invalid_version_output", child, stderr),
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
resolve({
|
|
98
|
+
ok: false,
|
|
99
|
+
failure: classifyProbeFailure(cause, child, stderr),
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
const classifyProbeFailure = (cause, child, stderr) => {
|
|
104
|
+
const dependency = missingDynamicLibrary(stderr);
|
|
105
|
+
if (dependency !== null)
|
|
106
|
+
return failure("runtime_dynamic_library_missing", child, stderr, dependency);
|
|
107
|
+
if ("killed" in cause && cause.killed === true)
|
|
108
|
+
return failure("runtime_timeout", child, stderr);
|
|
109
|
+
if (child.signalCode !== null)
|
|
110
|
+
return failure("runtime_signal", child, stderr);
|
|
111
|
+
if ("code" in cause && typeof cause.code === "string")
|
|
112
|
+
return failure("runtime_spawn_failed", child, stderr);
|
|
113
|
+
return failure("runtime_nonzero_exit", child, stderr);
|
|
114
|
+
};
|
|
115
|
+
const failure = (code, child, stderr, dependency = null) => ({
|
|
116
|
+
code,
|
|
117
|
+
exit_code: child.exitCode,
|
|
118
|
+
signal: child.signalCode,
|
|
119
|
+
dependency,
|
|
120
|
+
stderr: bounded(stderr.trim()),
|
|
121
|
+
});
|
|
122
|
+
const missingDynamicLibrary = (stderr) => /(?:dyld(?:\[[^\]]+\])?: )?Library not loaded:\s*([^\r\n]+)/u
|
|
123
|
+
.exec(stderr)?.[1]
|
|
124
|
+
?.trim() ?? null;
|
|
125
|
+
const isExecutable = async (path, platform) => {
|
|
126
|
+
try {
|
|
127
|
+
await access(path, platform === "win32" ? constants.F_OK : constants.X_OK);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const canonicalPath = async (path) => {
|
|
135
|
+
try {
|
|
136
|
+
return await realpath(path);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return path;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const firstNonemptyLine = (value) => value
|
|
143
|
+
.split(/\r?\n/u)
|
|
144
|
+
.find((line) => line.trim().length > 0)
|
|
145
|
+
?.trim();
|
|
146
|
+
const bounded = (value) => value.length <= MAX_DIAGNOSTIC_BYTES
|
|
147
|
+
? value
|
|
148
|
+
: value.slice(-MAX_DIAGNOSTIC_BYTES);
|
|
149
|
+
const unique = (values) => [...new Set(values)];
|
|
150
|
+
const environmentWithPath = (path) => {
|
|
151
|
+
const environment = {};
|
|
152
|
+
for (const [name, value] of Object.entries(process.env))
|
|
153
|
+
if (name.toUpperCase() !== "PATH")
|
|
154
|
+
environment[name] = value;
|
|
155
|
+
environment.PATH = path;
|
|
156
|
+
return environment;
|
|
157
|
+
};
|
|
158
|
+
const mapConcurrentBounded = async (inputs, concurrency, operation) => {
|
|
159
|
+
const outputs = [];
|
|
160
|
+
let nextIndex = 0;
|
|
161
|
+
const workers = Array.from({ length: Math.min(concurrency, inputs.length) }, async () => {
|
|
162
|
+
while (nextIndex < inputs.length) {
|
|
163
|
+
const index = nextIndex;
|
|
164
|
+
nextIndex += 1;
|
|
165
|
+
const input = inputs[index];
|
|
166
|
+
if (input !== undefined)
|
|
167
|
+
outputs[index] = await operation(input);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
await Promise.all(workers);
|
|
171
|
+
return outputs;
|
|
172
|
+
};
|