rea-agents 5.0.0 → 6.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 -650
- package/bridge/mitmproxy/capture.py +210 -0
- package/bridge/native/rea_lldb_tracer.py +303 -0
- package/bridge/process/ProcessRunTokenReader.swift +263 -0
- package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
- package/bridge/pwndbg/core_context.py +101 -0
- package/bridge/pwndbg/launch.py +39 -0
- package/bridge/pwndbg/launch_impl.py +34 -0
- package/bridge/pwntools/decoder_runtime.py +59 -0
- package/bridge/pwntools/layout.py +39 -0
- package/bridge/pwntools/layout_impl.py +309 -0
- package/bridge/pwntools/recorded_crash.py +39 -0
- package/bridge/pwntools/recorded_crash_impl.py +143 -0
- package/dist/android/JadxProvider.js +1 -0
- package/dist/application/CapabilityInventory.js +21 -0
- package/dist/application/DirectAnalysis.js +97 -23
- package/dist/application/EvidenceBundleFiles.js +1 -1
- package/dist/application/ReconstructionObligationCandidates.js +5 -1
- package/dist/application/WebNetworkCapturePort.js +1 -0
- package/dist/application/WebNetworkCaptureService.js +132 -0
- package/dist/application/WindowsCapabilities.js +1 -1
- package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
- package/dist/application/binary/BinarySession.js +38 -10
- package/dist/application/binary/BinarySessionOpen.js +16 -3
- package/dist/application/binary/BinarySessionRecords.js +20 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
- package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
- package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
- package/dist/application/evm/EvmInterfacePort.js +1 -0
- package/dist/application/evm/EvmInterfaceService.js +62 -0
- package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
- package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
- package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
- package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
- package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
- package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
- package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
- package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
- package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
- package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
- package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
- package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
- package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
- package/dist/artifacts/ArtifactProvider.js +30 -3
- package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
- package/dist/artifacts/ZipArtifactReader.js +3 -0
- package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
- package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
- package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
- package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
- package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
- package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
- package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
- package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
- package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
- package/dist/artifacts/readStableArtifact.js +20 -4
- package/dist/browser/CdpCaptureEventHandlers.js +90 -2
- package/dist/browser/CdpCaptureEvents.js +9 -0
- package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
- package/dist/browser/history/CaptureFailures.js +101 -0
- package/dist/browser/history/CaptureFormatError.js +10 -0
- package/dist/browser/history/CaptureRedaction.js +65 -0
- package/dist/browser/history/CaptureRelease.js +12 -0
- package/dist/browser/history/HarCapture.js +165 -0
- package/dist/browser/history/HarCaptureAdapter.js +20 -0
- package/dist/browser/history/HarCaptureProcess.js +57 -0
- package/dist/browser/history/HarJson.js +91 -0
- package/dist/browser/history/HarSchema.js +72 -0
- package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
- package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
- package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
- package/dist/cli/artifactCommands.js +26 -1
- package/dist/cli/binaryDiagnosticsCommands.js +39 -0
- package/dist/cli/coreAnalysisCommands.js +2 -2
- package/dist/cli/coreBinaryCommands.js +8 -8
- package/dist/cli/electronCommands.js +16 -9
- package/dist/cli/evmCommands.js +24 -0
- package/dist/cli/firmwareCommands.js +6 -3
- package/dist/cli/javascriptApplicationAnalysis.js +16 -3
- package/dist/cli/jsonOutput.js +133 -0
- package/dist/cli/nativeCallCommands.js +35 -0
- package/dist/cli/processCommands.js +1 -1
- package/dist/cli/streamedJsonOutput.js +56 -0
- package/dist/cli/utilityCommands.js +2 -0
- package/dist/cli/webNetworkCaptureCommands.js +40 -0
- package/dist/cli.js +9 -3
- package/dist/cliBrowserScenarioCommands.js +2 -2
- package/dist/cliCommandNames.js +6 -0
- package/dist/cliJsonInput.js +30 -1
- package/dist/cliOutput.js +21 -6
- package/dist/composition/binaryDiagnostics.js +8 -0
- package/dist/composition/evm.js +4 -0
- package/dist/composition/webNetworkCaptures.js +6 -0
- package/dist/contracts/artifactToolContracts.js +6 -0
- package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
- package/dist/contracts/browserToolContracts.js +4 -3
- package/dist/contracts/errorSchemas.js +8 -1
- package/dist/contracts/evm/evmToolContracts.js +20 -0
- package/dist/contracts/investigationExamples.js +2 -2
- package/dist/contracts/managed/managedToolContracts.js +7 -3
- package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
- package/dist/contracts/native/nativeToolContracts.js +17 -1
- package/dist/contracts/officialToolContracts.js +12 -9
- package/dist/contracts/process/processCaptureExample.js +2 -0
- package/dist/contracts/sessionLifecycleInputs.js +22 -5
- package/dist/contracts/sessionStatusContract.js +10 -2
- package/dist/contracts/sessionToolContracts.js +8 -1
- package/dist/contracts/sessionToolSchemas.js +10 -3
- package/dist/contracts/toolContractExamples.js +1 -1
- package/dist/contracts/toolContracts.js +6 -0
- package/dist/contracts/toolEffects.js +30 -0
- package/dist/contracts/toolOutputSchemaGroups.js +5 -1
- package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
- package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
- package/dist/domain/analysisErrorBase.js +15 -0
- package/dist/domain/analysisErrorCore.js +59 -5
- package/dist/domain/analysisErrorPresentation.js +29 -1
- package/dist/domain/analysisErrorProjection.js +52 -2
- package/dist/domain/analysisSnapshot.js +166 -2
- package/dist/domain/apple/dyldPaths.js +81 -0
- package/dist/domain/apple/dylibResolution.js +367 -0
- package/dist/domain/apple/dylibResolutionFindings.js +75 -0
- package/dist/domain/artifactPathSyntax.js +1 -1
- package/dist/domain/browserObservation.js +1 -1
- package/dist/domain/browserObservationSchemas.js +13 -0
- package/dist/domain/browserScenarioValues.js +9 -2
- package/dist/domain/callPathSchemas.js +1 -1
- package/dist/domain/canonicalDigest.js +13 -1
- package/dist/domain/changedBehavior.js +3 -3
- package/dist/domain/comparisonStatus.js +2 -30
- package/dist/domain/conformanceTrustGate.js +0 -16
- package/dist/domain/evidence.js +17 -5
- package/dist/domain/evm/evmInterface.js +63 -0
- package/dist/domain/explicitSensitiveFailure.js +92 -0
- package/dist/domain/explicitSensitiveValues.js +10 -0
- package/dist/domain/firmware/firmwareAnalysis.js +8 -1
- package/dist/domain/javascript/electronActiveObservation.js +9 -4
- package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
- package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
- package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
- package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
- package/dist/domain/jsonValue.js +4 -0
- package/dist/domain/localPath.js +2 -0
- package/dist/domain/native/binaryLayout.js +357 -0
- package/dist/domain/native/nativeCallObservation.js +164 -0
- package/dist/domain/native/objcSwiftMetadata.js +0 -39
- package/dist/domain/native/recordedCrash.js +266 -0
- package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
- package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
- package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
- package/dist/domain/process/processCaptureParsing.js +54 -0
- package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
- package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
- package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
- package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
- package/dist/domain/providerSelectionError.js +4 -2
- package/dist/domain/reconstructionVerification.js +3 -3
- package/dist/domain/reconstructionVerificationSchemas.js +1 -1
- package/dist/domain/residualUnknown.js +3 -2
- package/dist/domain/staticRuntimeCorrelation.js +3 -3
- package/dist/domain/webCaptureDiff.js +1 -0
- package/dist/domain/webNetworkCapture.js +118 -0
- package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
- package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
- package/dist/dotnet/ManagedMemberInspector.js +4 -2
- package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
- package/dist/dotnet/ManagedMemberRows.js +14 -3
- package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
- package/dist/dotnet/ManagedMetadataInventory.js +38 -28
- package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
- package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
- package/dist/dotnet/ManagedPeReader.js +11 -0
- package/dist/evm/EvmBytecodeCarrier.js +21 -0
- package/dist/evm/EvmWorkerLimits.js +40 -0
- package/dist/evm/EvmoleFailures.js +126 -0
- package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
- package/dist/evm/EvmoleInterfaceProvider.js +273 -0
- package/dist/evm/EvmoleInterfaceWorker.js +87 -0
- package/dist/evm/EvmoleRelease.js +16 -0
- package/dist/firmware/FirmwareCommand.js +1 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraLauncher.js +1 -0
- package/dist/ghidra/GhidraProvider.js +2 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
- package/dist/hopper/BridgeLauncher.js +2 -0
- package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
- package/dist/javascript/recovery/WakaruCommand.js +3 -0
- package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
- package/dist/native/CommandRunner.js +2 -1
- package/dist/native/LldbCallTracer.js +278 -0
- package/dist/native/NativeCallObservation.js +135 -0
- package/dist/native/NativeMacOSProvider.js +19 -3
- package/dist/native/NativeMacOSProviderMetadata.js +8 -3
- package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
- package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
- package/dist/native/pwntools/PwntoolsFailures.js +180 -0
- package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
- package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
- package/dist/native/pwntools/PwntoolsRelease.js +19 -0
- package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
- package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
- package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
- package/dist/process/DarwinProcessRunTokenReader.js +294 -0
- package/dist/process/OwnedCommand.js +113 -0
- package/dist/process/ProcessOwnership.js +263 -33
- package/dist/process/ProcessOwnershipObservation.js +161 -24
- package/dist/process/ProviderProcess.js +30 -3
- package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
- package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
- package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
- package/dist/{application → process/capture}/ProcessHarness.js +195 -29
- package/dist/{application → process/capture}/ProcessSampling.js +2 -2
- package/dist/server/createServer.js +22 -0
- package/dist/server/promptCompletion.js +10 -3
- package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
- package/dist/server/registerEvidenceTools.js +1 -1
- package/dist/server/registerEvmTools.js +17 -0
- package/dist/server/registerProcessComparisonTool.js +1 -1
- package/dist/server/registerRecordedCrashTools.js +17 -0
- package/dist/server/registerSessionTools.js +2 -2
- package/dist/server/registerWebNetworkCaptureTool.js +28 -0
- package/dist/server/sessionAvailabilityPolicy.js +3 -0
- package/dist/server/sessionToolPolicies.js +1 -1
- package/dist/server/toolResult.js +1 -1
- package/native/windows/build/manifest.json +2 -2
- package/native/windows/build/rea-windows-x64.node +0 -0
- package/package.json +37 -9
- package/scripts/rea.mjs +11 -3
- package/skills/reverse-engineer-anything/SKILL.md +23 -3
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
- package/third_party/README.md +60 -0
- package/third_party/evmole/LICENSE +21 -0
- package/third_party/evmole/README.md +22 -0
- package/third_party/pwndbg/LICENSE.md +21 -0
- package/third_party/pwndbg/README.md +23 -0
- package/third_party/pwntools/README.md +40 -0
- package/dist/application/AuthorizedArtifactInventory.js +0 -7
- package/dist/application/ProcessCaptureLifecycle.js +0 -299
- package/dist/application/StoreFileLock.js +0 -124
- package/dist/contracts/processCaptureExample.js +0 -2
- package/dist/domain/bytecodeProvider.js +0 -178
- package/dist/domain/customProtocolCapture.js +0 -137
- package/dist/domain/eventProcessTree.js +0 -225
- package/dist/domain/mobileApplicationInvestigation.js +0 -159
- package/dist/domain/packageAnalysis.js +0 -123
- package/dist/domain/peInspection.js +0 -197
- package/dist/domain/processCaptureParsing.js +0 -21
- package/dist/domain/protocolCapture.js +0 -217
- package/dist/domain/runtimeIdentification.js +0 -212
- package/dist/generatedMcpToolCatalog.js +0 -37
- /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
- /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
- /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
- /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
- /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
- /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
- /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
- /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
- /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
- /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
- /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
- /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
- /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
- /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
- /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
- /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
|
@@ -11,6 +11,7 @@ import { access } from "node:fs/promises";
|
|
|
11
11
|
import { readAnalysisSnapshot, writeAnalysisSnapshot, } from "./binary/AnalysisSnapshotFiles.js";
|
|
12
12
|
import { parseBinaryTarget } from "./BinaryTargetResolver.js";
|
|
13
13
|
import { snapshotEvidenceForQuery, snapshotMatchesTarget, } from "../domain/analysisSnapshot.js";
|
|
14
|
+
import { analysisProfileSchema, committedProviderSchema, } from "../domain/analysisProfile.js";
|
|
14
15
|
import { err, ok } from "../domain/result.js";
|
|
15
16
|
import { REA_WORKFLOW_PROVIDER, workflowAnalysisProfile, } from "./InvestigationProviders.js";
|
|
16
17
|
import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
|
|
@@ -82,7 +83,42 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
|
|
|
82
83
|
if (!prepared.ok)
|
|
83
84
|
return cliError(prepared.error);
|
|
84
85
|
const { snapshot } = prepared.value;
|
|
85
|
-
|
|
86
|
+
let resolvedTarget;
|
|
87
|
+
if (snapshot !== undefined && prepared.value.target !== undefined) {
|
|
88
|
+
const preview = await session.previewTarget(prepared.value.target, {
|
|
89
|
+
signal,
|
|
90
|
+
snapshot,
|
|
91
|
+
...(options.formatHint === undefined
|
|
92
|
+
? {}
|
|
93
|
+
: { formatHint: options.formatHint }),
|
|
94
|
+
...(options.providerId === undefined
|
|
95
|
+
? {}
|
|
96
|
+
: { providerId: options.providerId }),
|
|
97
|
+
});
|
|
98
|
+
if (!preview.ok)
|
|
99
|
+
return cliError(preview.error);
|
|
100
|
+
resolvedTarget = preview.value;
|
|
101
|
+
const route = preview.value.route;
|
|
102
|
+
const bindingProfile = route.profile ?? undefined;
|
|
103
|
+
const evidenceProfile = analysisProfileForRoute(route, tool);
|
|
104
|
+
if (bindingProfile !== undefined &&
|
|
105
|
+
evidenceProfile !== undefined &&
|
|
106
|
+
allowsSnapshotReplay(route, tool)) {
|
|
107
|
+
const cached = snapshotEvidenceForQuery(snapshot, {
|
|
108
|
+
target: preview.value.target,
|
|
109
|
+
bindingProfile,
|
|
110
|
+
operation: tool,
|
|
111
|
+
parameters: arguments_,
|
|
112
|
+
provider: isWorkflowEvidenceTool(tool)
|
|
113
|
+
? REA_WORKFLOW_PROVIDER
|
|
114
|
+
: providerIdentityForRoute(route, tool),
|
|
115
|
+
evidenceProfile,
|
|
116
|
+
});
|
|
117
|
+
if (cached !== undefined)
|
|
118
|
+
return cached;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const openOptions = {
|
|
86
122
|
signal,
|
|
87
123
|
...(options.formatHint === undefined
|
|
88
124
|
? {}
|
|
@@ -91,26 +127,13 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
|
|
|
91
127
|
...(options.providerId === undefined
|
|
92
128
|
? {}
|
|
93
129
|
: { providerId: options.providerId }),
|
|
94
|
-
}
|
|
130
|
+
};
|
|
131
|
+
const opened = resolvedTarget === undefined
|
|
132
|
+
? await session.open(path, openOptions)
|
|
133
|
+
: await session.openResolvedTarget(resolvedTarget, openOptions);
|
|
95
134
|
if (!opened.ok)
|
|
96
135
|
return cliError(opened.error);
|
|
97
136
|
const evidenceProfile = analysisProfileForEvidence(session, tool);
|
|
98
|
-
const bindingProfile = session.analysisProfile();
|
|
99
|
-
if (canReplayDirectTool(session, tool) &&
|
|
100
|
-
snapshot !== undefined &&
|
|
101
|
-
evidenceProfile !== undefined &&
|
|
102
|
-
bindingProfile !== undefined) {
|
|
103
|
-
const cached = snapshotEvidenceForQuery(snapshot, {
|
|
104
|
-
target: opened.value,
|
|
105
|
-
bindingProfile,
|
|
106
|
-
operation: tool,
|
|
107
|
-
parameters: arguments_,
|
|
108
|
-
provider: replayProviderFor(session, tool),
|
|
109
|
-
evidenceProfile,
|
|
110
|
-
});
|
|
111
|
-
if (cached !== undefined)
|
|
112
|
-
return cached;
|
|
113
|
-
}
|
|
114
137
|
const { output, evidence } = await executeAnalysisTool({
|
|
115
138
|
session,
|
|
116
139
|
openedTarget: opened.value,
|
|
@@ -121,6 +144,37 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
|
|
|
121
144
|
});
|
|
122
145
|
if (evidence !== undefined)
|
|
123
146
|
session.recordEvidence(evidence);
|
|
147
|
+
if (isWorkflowEvidenceTool(tool) &&
|
|
148
|
+
tool !== "trace_native_ui_action" &&
|
|
149
|
+
snapshotPath !== undefined &&
|
|
150
|
+
evidence !== undefined &&
|
|
151
|
+
"analysis_profile" in evidence &&
|
|
152
|
+
session.allowsSnapshotReplay(tool)) {
|
|
153
|
+
const recorded = session.recordWorkflowSnapshot({
|
|
154
|
+
operation: tool,
|
|
155
|
+
parameters: arguments_,
|
|
156
|
+
execution: {
|
|
157
|
+
result: evidence.normalized_result,
|
|
158
|
+
rawResult: evidence.raw_result,
|
|
159
|
+
provider: committedProviderSchema.parse(evidence.provider),
|
|
160
|
+
analysisProfile: analysisProfileSchema.parse(evidence.analysis_profile),
|
|
161
|
+
limitations: evidence.limitations,
|
|
162
|
+
locations: evidence.locations,
|
|
163
|
+
subject: evidence.subject === null
|
|
164
|
+
? null
|
|
165
|
+
: {
|
|
166
|
+
path: evidence.subject.local_path,
|
|
167
|
+
sha256: evidence.subject.digest.sha256,
|
|
168
|
+
format: evidence.subject.format,
|
|
169
|
+
...(evidence.subject.architecture === null
|
|
170
|
+
? {}
|
|
171
|
+
: { architecture: evidence.subject.architecture }),
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
if (!recorded.ok)
|
|
176
|
+
return cliError(recorded.error);
|
|
177
|
+
}
|
|
124
178
|
if (tool !== "trace_native_ui_action" &&
|
|
125
179
|
snapshotPath !== undefined &&
|
|
126
180
|
evidence !== undefined) {
|
|
@@ -137,7 +191,6 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
|
|
|
137
191
|
await session.close();
|
|
138
192
|
}
|
|
139
193
|
};
|
|
140
|
-
const canReplayDirectTool = (session, tool) => tool !== "trace_native_ui_action" && session.allowsSnapshotReplay(tool);
|
|
141
194
|
const executeAnalysisTool = async (input) => {
|
|
142
195
|
const { session, tool, signal, evidenceProfile } = input;
|
|
143
196
|
if (tool === "analyze_function") {
|
|
@@ -212,11 +265,32 @@ const prepareSnapshot = async (options) => {
|
|
|
212
265
|
return target;
|
|
213
266
|
if (!snapshotMatchesTarget(loaded.value.target, target.value))
|
|
214
267
|
return err(new EvidenceIntegrityError("Analysis snapshot target does not match the requested binary"));
|
|
215
|
-
return ok({ snapshot: loaded.value });
|
|
268
|
+
return ok({ snapshot: loaded.value, target: target.value });
|
|
269
|
+
};
|
|
270
|
+
const allowsSnapshotReplay = (route, tool) => {
|
|
271
|
+
if (tool === "trace_native_ui_action")
|
|
272
|
+
return false;
|
|
273
|
+
const descriptor = route.capabilities?.get(tool);
|
|
274
|
+
return descriptor === undefined
|
|
275
|
+
? ![...(route.capabilities?.values() ?? [])].some(({ cachePolicy }) => cachePolicy === "live")
|
|
276
|
+
: descriptor.cachePolicy !== "live";
|
|
277
|
+
};
|
|
278
|
+
const analysisProfileForRoute = (route, tool) => {
|
|
279
|
+
const profile = route.profile;
|
|
280
|
+
if (profile === null || profile === undefined)
|
|
281
|
+
return undefined;
|
|
282
|
+
if (isWorkflowEvidenceTool(tool))
|
|
283
|
+
return workflowAnalysisProfile(profile);
|
|
284
|
+
const provider = providerIdentityForRoute(route, tool);
|
|
285
|
+
return provider.id === profile.provider.id ? profile : undefined;
|
|
286
|
+
};
|
|
287
|
+
/** Mirror BinarySession.providerIdentity for a route not opened yet. */
|
|
288
|
+
const providerIdentityForRoute = (route, operation) => {
|
|
289
|
+
const selected = route.capabilities?.get(operation)?.provider ?? route.identity;
|
|
290
|
+
return route.profile?.provider.id === selected.id
|
|
291
|
+
? route.profile.provider
|
|
292
|
+
: selected;
|
|
216
293
|
};
|
|
217
|
-
const replayProviderFor = (session, tool) => isWorkflowEvidenceTool(tool)
|
|
218
|
-
? REA_WORKFLOW_PROVIDER
|
|
219
|
-
: session.providerIdentity(tool);
|
|
220
294
|
const analysisProfileForEvidence = (session, tool) => {
|
|
221
295
|
if (!isWorkflowEvidenceTool(tool))
|
|
222
296
|
return session.analysisProfile(tool);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseEvidenceBundle, serializeEvidenceBundle, } from "../domain/evidenceBundle.js";
|
|
2
2
|
import { EvidenceFileError, EvidenceIntegrityError, } from "../domain/evidenceErrors.js";
|
|
3
3
|
import { err, ok } from "../domain/result.js";
|
|
4
|
-
import { parseProcessCapture } from "../domain/processCapture.js";
|
|
4
|
+
import { parseProcessCapture } from "../domain/process/processCapture.js";
|
|
5
5
|
import { readJsonFile, writeTextFile } from "./JsonFiles.js";
|
|
6
6
|
/** Read and validate an evidence bundle at the caller-supplied path. */
|
|
7
7
|
export const readEvidenceBundle = async (path) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import canonicalize from "canonicalize";
|
|
3
3
|
import { parseEvidenceBundle, } from "../domain/evidenceBundle.js";
|
|
4
|
-
import { parseProcessCapture } from "../domain/processCapture.js";
|
|
4
|
+
import { parseProcessCapture } from "../domain/process/processCapture.js";
|
|
5
5
|
import { parseApplicationGraphEvidence } from "./javascript/JavaScriptApplicationEvidenceGraph.js";
|
|
6
6
|
import { applicationObligationPolicy, semanticObligationPolicy, } from "./ReconstructionObligationPolicies.js";
|
|
7
7
|
/** Derive conservative candidates from graph, process, and reviewed inputs. */
|
|
@@ -101,6 +101,10 @@ const deriveProcessCandidate = (evidence, candidates, limitations) => {
|
|
|
101
101
|
]
|
|
102
102
|
: []),
|
|
103
103
|
];
|
|
104
|
+
if (capture.manifest.executable_sha256 === null) {
|
|
105
|
+
limitations.add(`Process capture ${evidence.evidence_id} has no validated executable artifact identity; packaged-process lifecycle obligation was not bound to an artifact.`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
104
108
|
addCandidate(candidates, {
|
|
105
109
|
obligation_id: obligationId(`process:${capture.manifest.executable_sha256}:${capture.manifest.full_scenario_sha256}`),
|
|
106
110
|
obligation_version: 1,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { redactExplicitFailure } from "../domain/explicitSensitiveFailure.js";
|
|
2
|
+
import { excludeCaptureCoordinates } from "../domain/webNetworkCaptureCoordinates.js";
|
|
3
|
+
import { redactExplicitText } from "../domain/explicitSensitiveValues.js";
|
|
4
|
+
import { isAbsolute } from "node:path";
|
|
5
|
+
import { AnalysisCancelledError, AnalysisInputError, AnalysisOutputError, } from "../domain/analysisErrorCore.js";
|
|
6
|
+
import { createEvidence } from "../domain/evidence.js";
|
|
7
|
+
import { projectInputIssues } from "../domain/inputIssueProjection.js";
|
|
8
|
+
import { jsonObjectSchema } from "../domain/jsonValue.js";
|
|
9
|
+
import { err, ok } from "../domain/result.js";
|
|
10
|
+
import { inspectWebNetworkCaptureInputSchema, webNetworkCaptureSchema, } from "../domain/webNetworkCapture.js";
|
|
11
|
+
const OPERATION = "inspect_web_network_capture";
|
|
12
|
+
/** Shared CLI/MCP historical inspection preserves caller selection and producer record identity. */
|
|
13
|
+
export class WebNetworkCaptureService {
|
|
14
|
+
port;
|
|
15
|
+
constructor(port) {
|
|
16
|
+
this.port = port;
|
|
17
|
+
}
|
|
18
|
+
/** Return selected complete records inline, without inventing live browser attribution. */
|
|
19
|
+
async inspect(rawInput, options) {
|
|
20
|
+
if (options?.signal?.aborted)
|
|
21
|
+
return err(new AnalysisCancelledError(OPERATION));
|
|
22
|
+
const declarations = inspectWebNetworkCaptureInputSchema.shape.sensitive_values.safeParse(typeof rawInput === "object" &&
|
|
23
|
+
rawInput !== null &&
|
|
24
|
+
"sensitive_values" in rawInput
|
|
25
|
+
? rawInput.sensitive_values
|
|
26
|
+
: undefined);
|
|
27
|
+
const failure = (error) => err(redactExplicitFailure(error, declarations.success ? declarations.data : []));
|
|
28
|
+
const parsed = inspectWebNetworkCaptureInputSchema.safeParse(rawInput);
|
|
29
|
+
if (!parsed.success)
|
|
30
|
+
return failure(new AnalysisInputError(OPERATION, { cause: parsed.error }, projectInputIssues(parsed.error.issues, rawInput)));
|
|
31
|
+
const input = parsed.data;
|
|
32
|
+
if (!isAbsolute(input.capture_path))
|
|
33
|
+
return failure(new AnalysisInputError(OPERATION, undefined, [
|
|
34
|
+
{
|
|
35
|
+
path: ["capture_path"],
|
|
36
|
+
reason: "invalid_format",
|
|
37
|
+
message: "Expected an absolute filesystem path on this host.",
|
|
38
|
+
},
|
|
39
|
+
]));
|
|
40
|
+
if (input.record_ordinals !== undefined &&
|
|
41
|
+
new Set(input.record_ordinals).size !== input.record_ordinals.length)
|
|
42
|
+
return failure(new AnalysisInputError(OPERATION, undefined, [
|
|
43
|
+
{
|
|
44
|
+
path: ["record_ordinals"],
|
|
45
|
+
reason: "invalid_format",
|
|
46
|
+
message: "Each requested producer ordinal must occur once.",
|
|
47
|
+
},
|
|
48
|
+
]));
|
|
49
|
+
const loaded = await this.port.inspect(input, options);
|
|
50
|
+
if (!loaded.ok)
|
|
51
|
+
return failure(loaded.error);
|
|
52
|
+
if (options?.signal?.aborted)
|
|
53
|
+
return failure(new AnalysisCancelledError(OPERATION));
|
|
54
|
+
const report = webNetworkCaptureSchema.safeParse(loaded.value);
|
|
55
|
+
if (!report.success)
|
|
56
|
+
return failure(new AnalysisOutputError(OPERATION, "Capture adapter returned malformed historical evidence."));
|
|
57
|
+
const value = report.data;
|
|
58
|
+
const artifactSha256 = value.artifact.sha256;
|
|
59
|
+
if (artifactSha256 === null)
|
|
60
|
+
return failure(new AnalysisOutputError(OPERATION, "Capture adapter omitted its observed artifact digest before explicit projection."));
|
|
61
|
+
if (value.artifact.path !== input.capture_path ||
|
|
62
|
+
value.format !== input.format ||
|
|
63
|
+
value.total_records !== value.records.length ||
|
|
64
|
+
value.records.some((record, index) => record.ordinal !== index))
|
|
65
|
+
return failure(new AnalysisOutputError(OPERATION, "Capture adapter changed the selected artifact, format or original record sequence."));
|
|
66
|
+
const safeValue = excludeCaptureCoordinates(value, input.sensitive_values);
|
|
67
|
+
const redactText = (text) => redactExplicitText(text, input.sensitive_values);
|
|
68
|
+
const selected = input.record_ordinals ?? value.records.map(({ ordinal }) => ordinal);
|
|
69
|
+
const records = [];
|
|
70
|
+
for (const ordinal of selected) {
|
|
71
|
+
const record = safeValue.records[ordinal];
|
|
72
|
+
if (record === undefined)
|
|
73
|
+
return failure(new AnalysisInputError(OPERATION, undefined, [
|
|
74
|
+
{
|
|
75
|
+
path: ["record_ordinals"],
|
|
76
|
+
reason: "invalid_format",
|
|
77
|
+
message: `Producer ordinal ${ordinal} is outside the retained capture (${value.total_records} records).`,
|
|
78
|
+
},
|
|
79
|
+
]));
|
|
80
|
+
records.push({
|
|
81
|
+
...record,
|
|
82
|
+
limitations: record.limitations.map(redactText),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const safePath = input.sensitive_values.some((literal) => input.capture_path.includes(literal))
|
|
86
|
+
? ""
|
|
87
|
+
: input.capture_path;
|
|
88
|
+
const safeSha256 = input.sensitive_values.some((literal) => artifactSha256.includes(literal))
|
|
89
|
+
? null
|
|
90
|
+
: artifactSha256;
|
|
91
|
+
const subjectUnavailableReason = redactText("The explicitly sensitive artifact digest is excluded; its cryptographic identity is unknown in this Evidence.");
|
|
92
|
+
const limitations = [
|
|
93
|
+
...safeValue.limitations,
|
|
94
|
+
...(safePath === ""
|
|
95
|
+
? [
|
|
96
|
+
"The explicitly sensitive artifact path is excluded; its size remains available.",
|
|
97
|
+
]
|
|
98
|
+
: []),
|
|
99
|
+
].map(redactText);
|
|
100
|
+
const parameters = jsonObjectSchema.parse({
|
|
101
|
+
capture_path: safePath,
|
|
102
|
+
...(input.sensitive_values.some((literal) => input.format.includes(literal))
|
|
103
|
+
? {}
|
|
104
|
+
: { format: input.format }),
|
|
105
|
+
record_ordinals: selected,
|
|
106
|
+
sensitive_value_count: input.sensitive_values.length,
|
|
107
|
+
});
|
|
108
|
+
return ok(createEvidence(safeSha256 === null
|
|
109
|
+
? undefined
|
|
110
|
+
: {
|
|
111
|
+
path: safePath,
|
|
112
|
+
sha256: safeSha256,
|
|
113
|
+
format: "file",
|
|
114
|
+
}, value.decoder, {
|
|
115
|
+
operation: OPERATION,
|
|
116
|
+
parameters,
|
|
117
|
+
result: {
|
|
118
|
+
...safeValue,
|
|
119
|
+
artifact: { ...value.artifact, path: safePath, sha256: safeSha256 },
|
|
120
|
+
records,
|
|
121
|
+
limitations: safeSha256 === null
|
|
122
|
+
? [...limitations, subjectUnavailableReason]
|
|
123
|
+
: limitations,
|
|
124
|
+
},
|
|
125
|
+
confidence: "observed",
|
|
126
|
+
authority: "historical-reference",
|
|
127
|
+
limitations,
|
|
128
|
+
...(safeSha256 === null ? { subjectUnavailableReason } : {}),
|
|
129
|
+
locations: safePath === "" ? [] : [{ kind: "artifact-path", path: safePath }],
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -3,7 +3,7 @@ import { mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { windowsNativeCapabilities, } from "../process/WindowsAuthority.js";
|
|
6
|
-
import { probeProcessCaptureCapability, } from "
|
|
6
|
+
import { probeProcessCaptureCapability, } from "../process/capture/ProcessCaptureCapability.js";
|
|
7
7
|
/** Probe named prerequisites without converting missing controls into support. */
|
|
8
8
|
export const probeWindowsCapabilities = async (dependencies = systemDependencies()) => {
|
|
9
9
|
const [symlinkCreation, pty] = await Promise.all([
|
|
@@ -2,7 +2,7 @@ import { EvidenceIntegrityError } from "../../domain/evidenceErrors.js";
|
|
|
2
2
|
import { NoBinaryOpenError } from "../../domain/configurationErrors.js";
|
|
3
3
|
import {} from "../../domain/analysisErrorBase.js";
|
|
4
4
|
import { err, ok } from "../../domain/result.js";
|
|
5
|
-
import { analysisQueryId, createAnalysisSnapshotEntry, snapshotBinding, snapshotMatchesProfile, snapshotMatchesTarget, snapshotTarget, parseAnalysisSnapshot, } from "../../domain/analysisSnapshot.js";
|
|
5
|
+
import { analysisQueryId, createAnalysisSnapshotEntry, createAnalysisSnapshotWorkflowEntry, snapshotBinding, snapshotMatchesProfile, snapshotMatchesTarget, snapshotTarget, parseAnalysisSnapshot, } from "../../domain/analysisSnapshot.js";
|
|
6
6
|
import { OFFICIAL_TOOL_CONTRACTS } from "../../contracts/officialToolContracts.js";
|
|
7
7
|
const STATEFUL_OPERATIONS = new Set([
|
|
8
8
|
"health",
|
|
@@ -24,6 +24,8 @@ const CURSOR_DEFAULT_OPERATIONS = new Set([
|
|
|
24
24
|
const DOCUMENT_SCOPED_OPERATIONS = new Set(OFFICIAL_TOOL_CONTRACTS.map(({ name }) => name).filter((name) => name !== "current_document" &&
|
|
25
25
|
name !== "list_documents" &&
|
|
26
26
|
name !== "set_current_document"));
|
|
27
|
+
/** Maximum duplicated result bindings retained across direct and workflow queries. */
|
|
28
|
+
export const SNAPSHOT_CACHE_ENTRY_CEILING = 10_000;
|
|
27
29
|
/** Whether an operation is immutable and independent of provider UI state. */
|
|
28
30
|
export const isSnapshotCacheable = (operation, descriptor, parameters) => !STATEFUL_OPERATIONS.has(operation) &&
|
|
29
31
|
(!DOCUMENT_SCOPED_OPERATIONS.has(operation) ||
|
|
@@ -37,6 +39,7 @@ export const isSnapshotCacheable = (operation, descriptor, parameters) => !STATE
|
|
|
37
39
|
/** Bounded in-memory cache for one immutable binary identity. */
|
|
38
40
|
export class AnalysisSnapshotCache {
|
|
39
41
|
#entries = new Map();
|
|
42
|
+
#workflowEntries = new Map();
|
|
40
43
|
#target;
|
|
41
44
|
#binding;
|
|
42
45
|
/** Whether staged entries belong to the supplied target and profile. */
|
|
@@ -51,7 +54,7 @@ export class AnalysisSnapshotCache {
|
|
|
51
54
|
/** Replace cache state when the target or selected profile changes. */
|
|
52
55
|
select(target, profile) {
|
|
53
56
|
if (!this.matches(target, profile))
|
|
54
|
-
this.#
|
|
57
|
+
this.#clearEntries();
|
|
55
58
|
this.#target = snapshotTarget(target);
|
|
56
59
|
this.#binding = snapshotBinding(profile);
|
|
57
60
|
}
|
|
@@ -61,15 +64,22 @@ export class AnalysisSnapshotCache {
|
|
|
61
64
|
JSON.stringify(this.#target) !== JSON.stringify(snapshot.target)) ||
|
|
62
65
|
(this.#binding !== undefined &&
|
|
63
66
|
!snapshotMatchesProfile(this.#binding, snapshot.binding.analysis_profile)))
|
|
64
|
-
this.#
|
|
67
|
+
this.#clearEntries();
|
|
65
68
|
this.#target = structuredClone(snapshot.target);
|
|
66
69
|
this.#binding = structuredClone(snapshot.binding);
|
|
67
70
|
let imported = 0;
|
|
68
71
|
for (const entry of snapshot.entries) {
|
|
72
|
+
if (!this.#entries.has(entry.query_id) && !this.#hasCapacity())
|
|
73
|
+
continue;
|
|
69
74
|
if (!this.#entries.has(entry.query_id))
|
|
70
75
|
imported += 1;
|
|
71
76
|
this.#entries.set(entry.query_id, structuredClone(entry));
|
|
72
77
|
}
|
|
78
|
+
for (const entry of snapshot.workflow_entries ?? []) {
|
|
79
|
+
if (!this.#workflowEntries.has(entry.query_id) && !this.#hasCapacity())
|
|
80
|
+
continue;
|
|
81
|
+
this.#workflowEntries.set(entry.query_id, structuredClone(entry));
|
|
82
|
+
}
|
|
73
83
|
return imported;
|
|
74
84
|
}
|
|
75
85
|
/** Build a complete snapshot for an active target. */
|
|
@@ -84,6 +94,9 @@ export class AnalysisSnapshotCache {
|
|
|
84
94
|
target: snapshotTargetIdentity,
|
|
85
95
|
binding,
|
|
86
96
|
entries: this.entries(),
|
|
97
|
+
...(this.workflowEntries().length === 0
|
|
98
|
+
? {}
|
|
99
|
+
: { workflow_entries: this.workflowEntries() }),
|
|
87
100
|
evidence_bundle: retainedEvidence,
|
|
88
101
|
}));
|
|
89
102
|
}
|
|
@@ -117,6 +130,27 @@ export class AnalysisSnapshotCache {
|
|
|
117
130
|
.sort((left, right) => left.query_id.localeCompare(right.query_id))
|
|
118
131
|
.map((entry) => structuredClone(entry));
|
|
119
132
|
}
|
|
133
|
+
/** Return canonical composed-workflow entries for persistence. */
|
|
134
|
+
workflowEntries() {
|
|
135
|
+
return [...this.#workflowEntries.values()]
|
|
136
|
+
.sort((left, right) => left.query_id.localeCompare(right.query_id))
|
|
137
|
+
.map((entry) => structuredClone(entry));
|
|
138
|
+
}
|
|
139
|
+
/** Record one exact derived workflow result unless the shared cache is full. */
|
|
140
|
+
recordWorkflow(input) {
|
|
141
|
+
this.select(input.target, input.profile);
|
|
142
|
+
const queryId = analysisQueryId(snapshotTarget(input.target), snapshotBinding(input.profile), input.operation, input.parameters);
|
|
143
|
+
if (!this.#workflowEntries.has(queryId) && !this.#hasCapacity())
|
|
144
|
+
return;
|
|
145
|
+
const entry = createAnalysisSnapshotWorkflowEntry({
|
|
146
|
+
target: snapshotTarget(input.target),
|
|
147
|
+
binding: snapshotBinding(input.profile),
|
|
148
|
+
operation: input.operation,
|
|
149
|
+
parameters: input.parameters,
|
|
150
|
+
execution: input.execution,
|
|
151
|
+
});
|
|
152
|
+
this.#workflowEntries.set(entry.query_id, entry);
|
|
153
|
+
}
|
|
120
154
|
/** Replay an exact provider-specific query, marking its cached provenance. */
|
|
121
155
|
lookup(target, profile, operation, parameters) {
|
|
122
156
|
const queryId = analysisQueryId(snapshotTarget(target), snapshotBinding(profile), operation, parameters);
|
|
@@ -153,9 +187,10 @@ export class AnalysisSnapshotCache {
|
|
|
153
187
|
/** Record one successful immutable call unless the cache is full. */
|
|
154
188
|
record(input) {
|
|
155
189
|
const { target, profile, operation, parameters, execution } = input;
|
|
156
|
-
if (this.#entries.size >= 10_000)
|
|
157
|
-
return;
|
|
158
190
|
this.select(target, profile);
|
|
191
|
+
const queryId = analysisQueryId(snapshotTarget(target), snapshotBinding(profile), operation, parameters);
|
|
192
|
+
if (!this.#entries.has(queryId) && !this.#hasCapacity())
|
|
193
|
+
return;
|
|
159
194
|
const entry = createAnalysisSnapshotEntry({
|
|
160
195
|
target: snapshotTarget(target),
|
|
161
196
|
binding: snapshotBinding(profile),
|
|
@@ -167,8 +202,16 @@ export class AnalysisSnapshotCache {
|
|
|
167
202
|
}
|
|
168
203
|
/** Forget all target-bound entries. */
|
|
169
204
|
clear() {
|
|
170
|
-
this.#
|
|
205
|
+
this.#clearEntries();
|
|
171
206
|
this.#target = undefined;
|
|
172
207
|
this.#binding = undefined;
|
|
173
208
|
}
|
|
209
|
+
#clearEntries() {
|
|
210
|
+
this.#entries.clear();
|
|
211
|
+
this.#workflowEntries.clear();
|
|
212
|
+
}
|
|
213
|
+
#hasCapacity() {
|
|
214
|
+
return (this.#entries.size + this.#workflowEntries.size <
|
|
215
|
+
SNAPSHOT_CACHE_ENTRY_CEILING);
|
|
216
|
+
}
|
|
174
217
|
}
|
|
@@ -10,7 +10,7 @@ import { SessionProviderRouter, } from "./SessionProviderRouter.js";
|
|
|
10
10
|
import { InvestigationRecords } from "../investigation/InvestigationRecords.js";
|
|
11
11
|
import { BinarySessionRecords } from "./BinarySessionRecords.js";
|
|
12
12
|
import { binarySessionStatus } from "./BinarySessionStatus.js";
|
|
13
|
-
import { resolveSessionOpen, } from "./BinarySessionOpen.js";
|
|
13
|
+
import { resolveSessionOpen, resolveSessionTarget, validateResolvedSessionTarget, } from "./BinarySessionOpen.js";
|
|
14
14
|
import { bindExecutionTarget, commitExecutionProfile, prepareSessionExecution, } from "./BinarySessionExecution.js";
|
|
15
15
|
import { closeAnalysisClient } from "./AnalysisClientCleanup.js";
|
|
16
16
|
const OFFICIAL_OPERATIONS = new Set(OFFICIAL_TOOL_CONTRACTS.map(({ name }) => name));
|
|
@@ -88,21 +88,47 @@ export class BinarySession extends BinarySessionRecords {
|
|
|
88
88
|
this.#availabilityListeners.add(listener);
|
|
89
89
|
return () => this.#availabilityListeners.delete(listener);
|
|
90
90
|
}
|
|
91
|
+
/** Resolve a target and provider profile without creating a provider client. */
|
|
92
|
+
previewTarget(target, options = {}) {
|
|
93
|
+
if (isAborted(options.signal))
|
|
94
|
+
return Promise.resolve(err(new AnalysisCancelledError("open_binary")));
|
|
95
|
+
return resolveSessionTarget({
|
|
96
|
+
router: this.#providerRouter,
|
|
97
|
+
current: this.#active,
|
|
98
|
+
target,
|
|
99
|
+
options,
|
|
100
|
+
stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
|
|
101
|
+
}).then((resolved) => isAborted(options.signal)
|
|
102
|
+
? err(new AnalysisCancelledError("open_binary"))
|
|
103
|
+
: resolved);
|
|
104
|
+
}
|
|
91
105
|
/**
|
|
92
106
|
* Open or switch targets after draining calls against the current target.
|
|
93
107
|
* Returns the switch failure even if best-effort restoration also fails.
|
|
94
108
|
*/
|
|
95
109
|
open(path, options = {}) {
|
|
110
|
+
return this.#open(() => resolveSessionOpen({
|
|
111
|
+
router: this.#providerRouter,
|
|
112
|
+
current: this.#active,
|
|
113
|
+
path,
|
|
114
|
+
options,
|
|
115
|
+
stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
|
|
116
|
+
}), options);
|
|
117
|
+
}
|
|
118
|
+
/** Open a previewed target without repeating parsing or provider profile discovery. */
|
|
119
|
+
openResolvedTarget(resolved, options = {}) {
|
|
120
|
+
return this.#open(() => Promise.resolve(validateResolvedSessionTarget({
|
|
121
|
+
...resolved,
|
|
122
|
+
current: this.#active,
|
|
123
|
+
options,
|
|
124
|
+
stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
|
|
125
|
+
})), options);
|
|
126
|
+
}
|
|
127
|
+
#open(resolve, options) {
|
|
96
128
|
return this.#serialize(async () => {
|
|
97
129
|
if (isAborted(options.signal))
|
|
98
130
|
return err(new AnalysisCancelledError("open_binary"));
|
|
99
|
-
const resolved = await
|
|
100
|
-
router: this.#providerRouter,
|
|
101
|
-
current: this.#active,
|
|
102
|
-
path,
|
|
103
|
-
options,
|
|
104
|
-
stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
|
|
105
|
-
});
|
|
131
|
+
const resolved = await resolve();
|
|
106
132
|
if (!resolved.ok)
|
|
107
133
|
return resolved;
|
|
108
134
|
const { target, route, sameTarget } = resolved.value;
|
|
@@ -232,7 +258,8 @@ export class BinarySession extends BinarySessionRecords {
|
|
|
232
258
|
unboundOperationError: (operation, route) => this.#providerRouter.unboundOperationError(operation, route),
|
|
233
259
|
lookupSnapshot: (target, profile, operation, parameters) => operation === "decode_interface_builder" ||
|
|
234
260
|
operation === "inspect_asset_catalog" ||
|
|
235
|
-
operation === "inspect_keyed_archive"
|
|
261
|
+
operation === "inspect_keyed_archive" ||
|
|
262
|
+
operation === "trace_dylib_resolution"
|
|
236
263
|
? undefined
|
|
237
264
|
: this.lookupSnapshot(target, profile, operation, parameters),
|
|
238
265
|
});
|
|
@@ -252,7 +279,8 @@ export class BinarySession extends BinarySessionRecords {
|
|
|
252
279
|
profile !== undefined &&
|
|
253
280
|
name !== "decode_interface_builder" &&
|
|
254
281
|
name !== "inspect_asset_catalog" &&
|
|
255
|
-
name !== "inspect_keyed_archive"
|
|
282
|
+
name !== "inspect_keyed_archive" &&
|
|
283
|
+
name !== "trace_dylib_resolution") {
|
|
256
284
|
const evidence = createEvidence(profiled.value.subject ?? active.target, profiled.value.provider, {
|
|
257
285
|
operation: name,
|
|
258
286
|
parameters: arguments_,
|
|
@@ -7,11 +7,15 @@ import { err } from "../../domain/result.js";
|
|
|
7
7
|
import { SessionProviderRouter } from "./SessionProviderRouter.js";
|
|
8
8
|
/** Parse a target, resolve its provider route, and validate snapshot binding. */
|
|
9
9
|
export const resolveSessionOpen = async (input) => {
|
|
10
|
-
const {
|
|
10
|
+
const { path, options } = input;
|
|
11
11
|
const parsed = await parseBinaryTarget(path, process.cwd(), process.arch, options.targetKind, options.formatHint);
|
|
12
12
|
if (!parsed.ok)
|
|
13
13
|
return parsed;
|
|
14
|
-
|
|
14
|
+
return resolveSessionTarget({ ...input, target: parsed.value });
|
|
15
|
+
};
|
|
16
|
+
/** Resolve a known target's provider route and validate snapshot binding. */
|
|
17
|
+
export const resolveSessionTarget = async (input) => {
|
|
18
|
+
const { router, current, target, options } = input;
|
|
15
19
|
const sameTarget = current?.target.path === target.path &&
|
|
16
20
|
snapshotMatchesTarget(snapshotTarget(current.target), target);
|
|
17
21
|
const resolvedRoute = sameTarget && options.providerId === undefined && current !== undefined
|
|
@@ -19,7 +23,16 @@ export const resolveSessionOpen = async (input) => {
|
|
|
19
23
|
: await router.resolve(target, options.providerId, options.signal);
|
|
20
24
|
if (!resolvedRoute.ok)
|
|
21
25
|
return resolvedRoute;
|
|
22
|
-
|
|
26
|
+
return validateResolvedSessionTarget({
|
|
27
|
+
...input,
|
|
28
|
+
route: resolvedRoute.value,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
/** Revalidate a previewed route against the current session and snapshot binding. */
|
|
32
|
+
export const validateResolvedSessionTarget = (input) => {
|
|
33
|
+
const { current, target, route, options, stagedSnapshotMatches } = input;
|
|
34
|
+
const sameTarget = current?.target.path === target.path &&
|
|
35
|
+
snapshotMatchesTarget(snapshotTarget(current.target), target);
|
|
23
36
|
const snapshotError = validateSnapshot({
|
|
24
37
|
snapshot: options.snapshot,
|
|
25
38
|
target,
|
|
@@ -84,6 +84,26 @@ export class BinarySessionRecords {
|
|
|
84
84
|
this.#snapshot.record(input);
|
|
85
85
|
this.#emitSnapshotChanged();
|
|
86
86
|
}
|
|
87
|
+
/** Retain one derived workflow result alongside its provider cache entries. */
|
|
88
|
+
recordWorkflowSnapshot(input) {
|
|
89
|
+
const active = this.activeAnalysisBinding();
|
|
90
|
+
if (active === undefined || active.profile === null)
|
|
91
|
+
return err(new EvidenceIntegrityError("Workflow snapshot entries require an active concrete provider profile"));
|
|
92
|
+
try {
|
|
93
|
+
this.#snapshot.recordWorkflow({
|
|
94
|
+
target: active.target,
|
|
95
|
+
profile: active.profile,
|
|
96
|
+
...input,
|
|
97
|
+
});
|
|
98
|
+
this.#emitSnapshotChanged();
|
|
99
|
+
return ok(null);
|
|
100
|
+
}
|
|
101
|
+
catch (cause) {
|
|
102
|
+
return err(new EvidenceIntegrityError(cause instanceof Error
|
|
103
|
+
? cause.message
|
|
104
|
+
: "Workflow snapshot entry validation failed", { cause }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
87
107
|
invalidateSnapshot() {
|
|
88
108
|
this.#snapshot.clear();
|
|
89
109
|
this.#snapshotInvalidated = true;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|