rea-agents 6.2.0 → 6.3.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 +18 -20
- package/bridge/hopper_bridge.py +2 -0
- package/dist/android/JadxProvider.js +8 -2
- package/dist/android/JadxSession.js +51 -9
- package/dist/application/BinaryTargetResolver.js +63 -17
- package/dist/application/BrowserScenarioEvidence.js +2 -2
- package/dist/application/ClientConfigurationDocument.js +28 -0
- package/dist/application/ClientRegistrationStatus.js +19 -6
- package/dist/application/DirectAnalysis.js +12 -6
- package/dist/application/DirectAnalysisCleanup.js +0 -1
- package/dist/application/DirectAnalysisStatus.js +2 -5
- package/dist/application/Doctor.js +25 -34
- package/dist/application/DoctorDiagnostics.js +1 -1
- package/dist/application/EvidenceBundleFiles.js +1 -1
- package/dist/application/InvestigationProviders.js +4 -4
- package/dist/application/JsonFiles.js +15 -15
- package/dist/application/LinuxHopper.js +2 -1
- package/dist/application/ReconstructionObligationCandidates.js +9 -2
- package/dist/application/ReconstructionObligationLedgerProjection.js +3 -1
- package/dist/application/RegularFileRead.js +17 -0
- package/dist/application/Setup.js +18 -7
- package/dist/application/SetupClientConfiguration.js +18 -7
- package/dist/application/SetupHost.js +6 -4
- package/dist/application/SetupPlan.js +19 -15
- package/dist/application/SetupSkill.js +57 -8
- package/dist/application/SupportedClients.js +43 -0
- package/dist/application/Uninstall.js +18 -2
- package/dist/application/UpdateMaintenance.js +6 -2
- package/dist/application/Utf8JsonInput.js +64 -0
- package/dist/application/WebScriptExportService.js +13 -6
- package/dist/application/android/AndroidAnalysisService.js +1 -1
- package/dist/application/binary/AnalysisSnapshotCache.js +5 -4
- package/dist/application/binary/BinarySession.js +16 -0
- package/dist/application/binary/BinarySessionOpen.js +9 -1
- package/dist/application/javascript/ElectronActiveEvidence.js +2 -2
- package/dist/application/javascript/ElectronBoundaryAnalysis.js +2 -2
- package/dist/application/javascript/ElectronBoundaryGraphNative.js +5 -1
- package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -0
- package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +107 -46
- package/dist/application/javascript/JavaScriptApplicationService.js +1 -0
- package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +59 -45
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +0 -6
- package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +8 -18
- package/dist/application/javascript/JavaScriptArtifactGraphFindings.js +34 -26
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +44 -12
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +6 -3
- package/dist/application/javascript/JavaScriptArtifactReconstructionInput.js +1 -0
- package/dist/application/javascript/JavaScriptJsonModules.js +2 -2
- package/dist/application/javascript/JavaScriptModuleRelationships.js +5 -17
- package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +11 -27
- package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +2 -2
- package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +8 -6
- package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +1 -1
- package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +7 -5
- package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +1 -1
- package/dist/application/javascript/JavaScriptSemanticGraphEvidence.js +1 -1
- package/dist/application/javascript/JavaScriptSemanticGraphFingerprintProjection.js +2 -2
- package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +2 -2
- package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +1 -1
- package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +4 -4
- package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +1 -1
- package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +10 -7
- package/dist/application/javascript/JavaScriptSemanticTraceService.js +15 -10
- package/dist/application/native/NativeCallRoutes.js +2 -4
- package/dist/artifacts/ArtifactProvider.js +14 -16
- package/dist/artifacts/ArtifactProviderMetadata.js +1 -4
- package/dist/artifacts/AsarArtifactReader.js +81 -17
- package/dist/artifacts/DirectoryArtifactReader.js +2 -2
- package/dist/artifacts/NativeDmgArtifactReader.js +152 -67
- package/dist/artifacts/NativeDmgOwnership.js +109 -0
- package/dist/artifacts/apple/KeyedArchiveReader.js +54 -46
- package/dist/artifacts/apple/MachoLoadCommandReader.js +36 -6
- package/dist/artifacts/extraction/ArtifactExtraction.js +29 -11
- package/dist/artifacts/inventory/ArtifactGraphConstruction.js +7 -8
- package/dist/artifacts/inventory/classify.js +33 -14
- package/dist/artifacts/inventory/hashStableRootArtifact.js +37 -0
- package/dist/artifacts/inventory/scanCanonical.js +28 -22
- package/dist/artifacts/inventory/scanReader.js +7 -11
- package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +65 -27
- package/dist/browser/CdpBrowserProvider.js +4 -3
- package/dist/browser/CdpCaptureStorageFingerprints.js +3 -3
- package/dist/browser/CdpElectronInspection.js +2 -2
- package/dist/browser/CdpElectronProvider.js +4 -3
- package/dist/browser/CdpElectronScripts.js +2 -2
- package/dist/browser/CdpWebMcpDiscovery.js +16 -13
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +6 -6
- package/dist/browser/PlaywrightScenarioBrowser.js +145 -42
- package/dist/browser/PlaywrightScenarioSession.js +3 -11
- package/dist/browser/WebSourceMapFetcher.js +32 -6
- package/dist/browser/assets/ScenarioScriptCapture.js +8 -12
- package/dist/cli/coreAnalysisCommands.js +2 -2
- package/dist/cli/coreBinaryCommands.js +13 -9
- package/dist/cli/electronCommands.js +9 -10
- package/dist/cli/javascriptApplicationAnalysis.js +2 -12
- package/dist/cli/jsonOutput.js +3 -2
- package/dist/cli/streamedJsonOutput.js +83 -4
- package/dist/cli/utilityCommands.js +2 -4
- package/dist/cli.js +5 -2
- package/dist/cliJsonFile.js +179 -0
- package/dist/cliJsonInput.js +47 -34
- package/dist/cliObservationOptions.js +4 -0
- package/dist/composition/auxiliaryAnalysisProviders.js +5 -4
- package/dist/composition/browserScenario.js +2 -2
- package/dist/composition/directAnalysis.js +2 -3
- package/dist/contracts/artifactComparisonExample.js +5 -5
- package/dist/contracts/errorSchemas.js +3 -1
- package/dist/contracts/functionComparisonExample.js +8 -0
- package/dist/contracts/inputSchemaPresentation.js +94 -3
- package/dist/contracts/javascript/electronToolContracts.js +2 -1
- package/dist/contracts/javascript/javascriptApplicationWorkflowExamples.js +3 -2
- package/dist/contracts/javascript/javascriptExportShapeComparisonExample.js +6 -3
- package/dist/contracts/javascript/javascriptRuntimeReconciliationExample.js +8 -7
- package/dist/contracts/managed/managedToolContracts.js +3 -2
- package/dist/contracts/mobileApplicationGraphExamples.js +5 -5
- package/dist/contracts/native/nativeToolContracts.js +1 -1
- package/dist/contracts/sessionLifecycleInputs.js +4 -3
- package/dist/contracts/sessionStatusContract.js +3 -2
- package/dist/contracts/sessionToolContracts.js +3 -2
- package/dist/contracts/sessionToolSchemas.js +3 -2
- package/dist/contracts/toolOutputSchemaGroups.js +2 -2
- package/dist/contracts/toolOutputSchemaPrimitives.js +2 -5
- package/dist/domain/analysisErrorBase.js +5 -0
- package/dist/domain/analysisErrorPresentation.js +4 -2
- package/dist/domain/analysisSnapshot.js +3 -1
- package/dist/domain/analysisView/analysisView.js +1 -3
- package/dist/domain/android/androidPartialObservation.js +36 -0
- package/dist/domain/apple/dylibResolution.js +1 -1
- package/dist/domain/artifactComparison.js +4 -4
- package/dist/domain/artifactGraph.js +1 -1
- package/dist/domain/artifactIdentity.js +8 -8
- package/dist/domain/artifactInspection.js +8 -8
- package/dist/domain/artifactInventoryEvidence.js +5 -5
- package/dist/domain/browserCompleteness.js +3 -2
- package/dist/domain/browserScenarioCaptureValues.js +2 -2
- package/dist/domain/browserScenarioNormalization.js +3 -3
- package/dist/domain/browserScenarioValues.js +3 -2
- package/dist/domain/callPath.js +8 -10
- package/dist/domain/canonicalDigest.js +7 -1
- package/dist/domain/canonicalOrdering.js +1 -13
- package/dist/domain/changedBehavior.js +2 -3
- package/dist/domain/comparisonSemantics.js +0 -5
- package/dist/domain/digests.js +0 -3
- package/dist/domain/evidence.js +10 -5
- package/dist/domain/evidenceBundle.js +51 -33
- package/dist/domain/firmware/firmwareAnalysis.js +4 -3
- package/dist/domain/functionComparison.js +2 -1
- package/dist/domain/functionComparisonNormalization.js +4 -3
- package/dist/domain/functionComparisonResults.js +2 -2
- package/dist/domain/functionDossierEvidence.js +2 -1
- package/dist/domain/hopperValues.js +11 -15
- package/dist/domain/javascript/electronActiveObservation.js +4 -3
- package/dist/domain/javascript/electronStaticAnalysisBrowser.js +2 -2
- package/dist/domain/javascript/electronStaticAnalysisIpc.js +9 -1
- package/dist/domain/javascript/electronStaticAnalysisNative.js +5 -3
- package/dist/domain/javascript/electronStaticAnalysisValues.js +8 -4
- package/dist/domain/javascript/javascriptApplicationAnalysis.js +3 -1
- package/dist/domain/javascript/javascriptApplicationChangeGraph.js +12 -8
- package/dist/domain/javascript/javascriptApplicationEvidenceSchemas.js +4 -0
- package/dist/domain/javascript/javascriptApplicationGraph.js +15 -13
- package/dist/domain/javascript/javascriptApplicationVersionComparison.js +4 -4
- package/dist/domain/javascript/javascriptApplicationVersionComparisonSchemas.js +37 -22
- package/dist/domain/javascript/javascriptApplicationVersionItems.js +45 -34
- package/dist/domain/javascript/javascriptApplicationVersionKeys.js +7 -7
- package/dist/domain/javascript/javascriptAstFingerprint.js +2 -2
- package/dist/domain/javascript/javascriptAstValues.js +22 -0
- package/dist/domain/javascript/javascriptExportShapeComparison.js +3 -2
- package/dist/domain/javascript/javascriptExportShapeComparisonSchemas.js +31 -23
- package/dist/domain/javascript/javascriptExportShapeSelection.js +2 -2
- package/dist/domain/javascript/javascriptExportShapeVariants.js +3 -2
- package/dist/domain/javascript/javascriptFeatureSeed.js +5 -3
- package/dist/domain/javascript/javascriptFeatureTrace.js +7 -6
- package/dist/domain/javascript/javascriptFeatureTraceSchemas.js +17 -9
- package/dist/domain/javascript/javascriptFeatureTraversal.js +4 -4
- package/dist/domain/javascript/javascriptNativeHandoff.js +6 -6
- package/dist/domain/javascript/javascriptRuntimeObservation.js +8 -5
- package/dist/domain/javascript/javascriptRuntimeReconciliation.js +12 -6
- package/dist/domain/javascript/javascriptRuntimeReconciliationActive.js +9 -8
- package/dist/domain/javascript/javascriptRuntimeReconciliationCaptureParsing.js +212 -0
- package/dist/domain/javascript/javascriptRuntimeReconciliationGraph.js +5 -4
- package/dist/domain/javascript/javascriptRuntimeReconciliationInputValidation.js +28 -0
- package/dist/domain/javascript/javascriptRuntimeReconciliationMatching.js +5 -5
- package/dist/domain/javascript/javascriptRuntimeReconciliationParsing.js +12 -223
- package/dist/domain/javascript/javascriptRuntimeReconciliationResult.js +2 -2
- package/dist/domain/javascript/javascriptRuntimeReconciliationRuntime.js +15 -4
- package/dist/domain/javascript/javascriptRuntimeReconciliationSchemas.js +2 -2
- package/dist/domain/javascript/javascriptRuntimeStaticCandidates.js +7 -6
- package/dist/domain/javascript/javascriptSemanticAnalysis.js +200 -8
- package/dist/domain/javascript/javascriptSemanticCallResolution.js +9 -7
- package/dist/domain/javascript/javascriptSemanticChildProcesses.js +2 -2
- package/dist/domain/javascript/javascriptSemanticCoverage.js +20 -0
- package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +7 -9
- package/dist/domain/javascript/javascriptSemanticDataEffects.js +4 -11
- package/dist/domain/javascript/javascriptSemanticFingerprints.js +2 -2
- package/dist/domain/javascript/javascriptSemanticGraph.js +142 -35
- package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +19 -1
- package/dist/domain/javascript/javascriptSemanticIr.js +0 -4
- package/dist/domain/javascript/javascriptSemanticIterationSources.js +106 -0
- package/dist/domain/javascript/javascriptSemanticMemberMutations.js +497 -113
- package/dist/domain/javascript/javascriptSemanticMutationInitializers.js +190 -0
- package/dist/domain/javascript/javascriptSemanticMutationValues.js +41 -27
- package/dist/domain/javascript/javascriptSemanticObjects.js +2 -6
- package/dist/domain/javascript/javascriptSemanticPrimitives.js +7 -2
- package/dist/domain/javascript/javascriptSemanticProjection.js +20 -14
- package/dist/domain/javascript/javascriptSemanticPromises.js +15 -21
- package/dist/domain/javascript/javascriptSemanticPropertyPaths.js +23 -0
- package/dist/domain/javascript/javascriptSemanticProvenance.js +3 -3
- package/dist/domain/javascript/javascriptSemanticQuery.js +8 -2
- package/dist/domain/javascript/javascriptSemanticQueryIdentity.js +3 -2
- package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +8 -9
- package/dist/domain/javascript/javascriptSemanticRequireOrigin.js +16 -10
- package/dist/domain/javascript/javascriptSemanticReturns.js +23 -27
- package/dist/domain/javascript/javascriptSemanticTraversal.js +6 -1
- package/dist/domain/javascript/javascriptSemanticValues.js +135 -24
- package/dist/domain/javascript/javascriptSourceMapPaths.js +11 -1
- package/dist/domain/javascript/javascriptSourceParser.js +34 -9
- package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +5 -6
- package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +26 -15
- package/dist/domain/javascript/sourceToBundleComparison.js +7 -5
- package/dist/domain/javascript/sourceToBundleSignals.js +7 -7
- package/dist/domain/jsonSerialization.js +109 -55
- package/dist/domain/jsonShape.js +46 -19
- package/dist/domain/localPath.js +10 -4
- package/dist/domain/managed/managedApplicationGraph.js +10 -6
- package/dist/domain/managed/managedApplicationGraphNodes.js +82 -57
- package/dist/domain/managed/managedMemberComparisonMatch.js +6 -7
- package/dist/domain/native/nativeCallObservation.js +2 -4
- package/dist/domain/native/nativeValueFlow.js +5 -11
- package/dist/domain/native/objcSwiftMetadata.js +15 -17
- package/dist/domain/process/processCapture.js +11 -10
- package/dist/domain/process/processCaptureCoverage.js +8 -5
- package/dist/domain/process/processCaptureExample.js +43 -0
- package/dist/domain/process/processCaptureValidation.js +4 -3
- package/dist/domain/process/processComparison.js +0 -23
- package/dist/domain/process/processScenario.js +3 -3
- package/dist/domain/process/processTraceEvaluation.js +4 -4
- package/dist/domain/process/processTraceSpecification.js +1 -3
- package/dist/domain/providerCleanupError.js +6 -2
- package/dist/domain/reconstructionCoverage.js +2 -1
- package/dist/domain/reconstructionUnknowns.js +4 -3
- package/dist/domain/reconstructionVerification.js +3 -2
- package/dist/domain/referenceSourceGraph.js +7 -7
- package/dist/domain/referenceSourceImportParsing.js +34 -45
- package/dist/domain/residualUnknown.js +3 -2
- package/dist/domain/staticRuntimeCorrelation.js +2 -1
- package/dist/domain/swiftSymbolClassification.js +6 -1
- package/dist/domain/symbolAnalysis.js +17 -2
- package/dist/domain/unicodeCodePointOrder.js +6 -0
- package/dist/domain/webBundleAnalyzerInspection.js +9 -1
- package/dist/domain/webCaptureDiff.js +71 -29
- package/dist/domain/webCaptureDiffSchemas.js +2 -8
- package/dist/domain/webInventory.js +3 -2
- package/dist/domain/webMcpDiscovery.js +4 -3
- package/dist/domain/webScriptExport.js +1 -1
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +1 -2
- package/dist/dotnet/ManagedStaticProvider.js +10 -7
- package/dist/dotnet/ManagedStaticProviderMetadata.js +1 -4
- package/dist/filesystem/RegularFile.js +41 -0
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraClient.js +14 -4
- package/dist/ghidra/GhidraInstallation.js +111 -10
- package/dist/ghidra/GhidraLauncher.js +43 -19
- package/dist/hopper/HopperCleanup.js +1 -1
- package/dist/ida/IdaOperationRunner.js +2 -0
- package/dist/ida/IdaSessionClient.js +4 -2
- package/dist/inspector/JavaScriptRuntimeScope.js +9 -5
- package/dist/inspector/V8InspectorCaptureProjection.js +46 -13
- package/dist/inspector/V8InspectorProvider.js +60 -8
- package/dist/main/shutdown.js +76 -32
- package/dist/main/transport.js +1 -1
- package/dist/native/AppleObjcDispatchFacets.js +3 -3
- package/dist/native/AppleObjcProperties.js +17 -7
- package/dist/native/LldbCallTracer.js +241 -75
- package/dist/native/NativeMacOSProvider.js +10 -5
- package/dist/native/NativeMacOSProviderMetadata.js +1 -2
- package/dist/process/ProcessOwnershipObservation.js +9 -13
- package/dist/process/capture/FilesystemSnapshot.js +88 -18
- package/dist/process/capture/ProcessCaptureLifecycle.js +13 -27
- package/dist/process/capture/ProcessFilesystemEffects.js +19 -4
- package/dist/process/capture/ProcessHarness.js +8 -21
- package/dist/server/analysisAdmission.js +8 -0
- package/dist/server/createServer.js +22 -23
- package/dist/server/registerApplicationTools/compareExportShapes.js +2 -1
- package/dist/server/registerApplicationTools/helpers.js +3 -2
- package/dist/server/registerArtifactTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +6 -4
- package/dist/server/registerEnhancedTools.js +78 -73
- package/dist/server/registerEvidenceTools.js +54 -46
- package/dist/server/registerManagedTools.js +7 -4
- package/dist/server/registerOfficialTools.js +31 -25
- package/dist/server/registerSessionTools.js +9 -3
- package/native/windows/build/manifest.json +1 -1
- package/package.json +6 -3
- package/skills/reverse-engineer-anything/SKILL.md +5 -5
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +9 -0
- package/dist/artifacts/inventory/policy.js +0 -2
- package/dist/domain/managed/managedApplicationGraphNodeCoverage.js +0 -6
- package/dist/server/registerNativeTools.js +0 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { digestCanonicalValue } from "./canonicalDigest.js";
|
|
2
|
+
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
|
2
3
|
import { webCaptureDiffSchema, } from "./webCaptureDiffSchemas.js";
|
|
3
4
|
/** Compare normalized observations without treating incomplete absence as proof. */
|
|
4
5
|
export const compareWebCaptures = (input) => {
|
|
@@ -41,8 +42,8 @@ const compareIdentities = (before, after) => {
|
|
|
41
42
|
for (const identity of after.keys())
|
|
42
43
|
if (!before.has(identity))
|
|
43
44
|
changes.push({ identity, change: "added" });
|
|
44
|
-
return changes.sort((left, right) => left.identity
|
|
45
|
-
left.change
|
|
45
|
+
return changes.sort((left, right) => compareUnicodeCodePoints(left.identity, right.identity) ||
|
|
46
|
+
compareUnicodeCodePoints(left.change, right.change));
|
|
46
47
|
};
|
|
47
48
|
const compareDimension = (before, after, complete, reason) => {
|
|
48
49
|
const all = compareIdentities(before, after).filter(({ change }) => complete || change === "modified");
|
|
@@ -72,7 +73,7 @@ const accessibilityDimension = (before, after) => {
|
|
|
72
73
|
const afterAccess = accessibilityComparable(after);
|
|
73
74
|
const textComparable = beforeAccess.text && afterAccess.text;
|
|
74
75
|
const nodesComparable = beforeAccess.nodes && afterAccess.nodes;
|
|
75
|
-
return compareDimension(singleton("accessibility_tree",
|
|
76
|
+
return compareDimension(singleton("accessibility_tree", digestCanonicalValue(accessibilityProjection(before.accessibility, textComparable, nodesComparable))), singleton("accessibility_tree", digestCanonicalValue(accessibilityProjection(after.accessibility, textComparable, nodesComparable))), beforeAccess.complete && afterAccess.complete, "Accessibility tree or text capture was incomplete in at least one observation.");
|
|
76
77
|
};
|
|
77
78
|
const storageDimension = (before, after) => {
|
|
78
79
|
const keysComplete = storageKeysComparable(before) && storageKeysComparable(after);
|
|
@@ -86,32 +87,65 @@ const storageDimension = (before, after) => {
|
|
|
86
87
|
const compareWebCaptureDimensions = (input) => {
|
|
87
88
|
const before = input.before.inspection;
|
|
88
89
|
const after = input.after.inspection;
|
|
89
|
-
const dimension = compareDimension;
|
|
90
90
|
return {
|
|
91
|
-
dom_structure:
|
|
91
|
+
dom_structure: compareDimension(singleton("document", digestCanonicalValue(domProjection(before))), singleton("document", digestCanonicalValue(domProjection(after))), sectionsComplete(before, ["frames", "dom"]) &&
|
|
92
92
|
sectionsComplete(after, ["frames", "dom"]), "DOM or frame capture was incomplete in at least one observation."),
|
|
93
|
-
scripts:
|
|
93
|
+
scripts: compareDimension(keyed(before.scripts.items.map(scriptProjection), (item) => item.script_key), keyed(after.scripts.items.map(scriptProjection), (item) => item.script_key), sectionsComplete(before, ["scripts"]) &&
|
|
94
94
|
sectionsComplete(after, ["scripts"]), "Script inventory was incomplete in at least one observation."),
|
|
95
|
-
resources:
|
|
95
|
+
resources: compareDimension(keyed(before.resources, (item) => item.resource_key), keyed(after.resources, (item) => item.resource_key), sectionsComplete(before, ["resources"]) &&
|
|
96
96
|
sectionsComplete(after, ["resources"]), "Resource inventory was incomplete in at least one observation."),
|
|
97
|
-
network:
|
|
98
|
-
|
|
99
|
-
metadata: dimension(singleton("metadata", digest(metadataProjection(before))), singleton("metadata", digest(metadataProjection(after))), sectionsComplete(before, ["metadata"]) &&
|
|
97
|
+
network: networkDimension(before, after),
|
|
98
|
+
metadata: compareDimension(singleton("metadata", digestCanonicalValue(metadataProjection(before))), singleton("metadata", digestCanonicalValue(metadataProjection(after))), sectionsComplete(before, ["metadata"]) &&
|
|
100
99
|
sectionsComplete(after, ["metadata"]), "Safe metadata capture was incomplete in at least one observation."),
|
|
101
|
-
webmcp:
|
|
100
|
+
webmcp: compareDimension(webMcpMap(input.before.webmcp), webMcpMap(input.after.webmcp), webMcpComplete(input.before.webmcp) && webMcpComplete(input.after.webmcp), "WebMCP discovery was unavailable or incomplete in at least one capture."),
|
|
102
101
|
accessibility: accessibilityDimension(before, after),
|
|
103
102
|
storage: storageDimension(before, after),
|
|
104
103
|
};
|
|
105
104
|
};
|
|
106
|
-
const keyed = (values, identity) => new Map(values.map((value) => [identity(value),
|
|
107
|
-
const
|
|
105
|
+
const keyed = (values, identity) => new Map(values.map((value) => [identity(value), digestCanonicalValue(value)]));
|
|
106
|
+
const bodyShapesSelected = (inspection) => !inspection.completeness.excluded.some(({ section, reason }) => section === "json_body_shapes" && reason === "not_approved") &&
|
|
107
|
+
(inspection.network.requests.length === 0 ||
|
|
108
|
+
inspection.network.requests.some(({ body_shapes }) => body_shapes.status !== "not_approved"));
|
|
109
|
+
const networkIdentity = (request) => `net_${digestCanonicalValue({ method: request.method, url: request.url, resource_type: request.resource_type })}`;
|
|
110
|
+
const networkDimension = (before, after) => {
|
|
111
|
+
const selected = bodyShapesSelected(before) && bodyShapesSelected(after);
|
|
112
|
+
const sources = new Map();
|
|
113
|
+
if (selected) {
|
|
114
|
+
const observedSources = (inspection) => {
|
|
115
|
+
const result = new Map();
|
|
116
|
+
for (const request of inspection.network.requests) {
|
|
117
|
+
const identity = networkIdentity(request);
|
|
118
|
+
const previous = result.get(identity);
|
|
119
|
+
result.set(identity, {
|
|
120
|
+
request: previous?.request !== false && request.body_shapes.request !== null,
|
|
121
|
+
response: previous?.response !== false &&
|
|
122
|
+
request.body_shapes.response !== null,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
};
|
|
127
|
+
const left = observedSources(before);
|
|
128
|
+
const right = observedSources(after);
|
|
129
|
+
for (const [identity, coverage] of left) {
|
|
130
|
+
const next = right.get(identity);
|
|
131
|
+
sources.set(identity, {
|
|
132
|
+
request: coverage.request && next?.request === true,
|
|
133
|
+
response: coverage.response && next?.response === true,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const shapesComplete = !selected ||
|
|
138
|
+
[before, after].every((inspection) => sectionsComplete(inspection, ["json_body_shapes"]) &&
|
|
139
|
+
inspection.network.requests.every(({ body_shapes }) => body_shapes.status === "included"));
|
|
140
|
+
return compareDimension(networkMap(before, sources), networkMap(after, sources), sectionsComplete(before, ["network_requests"]) &&
|
|
141
|
+
sectionsComplete(after, ["network_requests"]) &&
|
|
142
|
+
shapesComplete, "Network capture or selected JSON body-shape coverage is attach-window limited, unavailable, or incomplete.");
|
|
143
|
+
};
|
|
144
|
+
const networkMap = (inspection, sources) => {
|
|
108
145
|
const grouped = new Map();
|
|
109
146
|
for (const request of inspection.network.requests) {
|
|
110
|
-
const identity =
|
|
111
|
-
|
|
112
|
-
url: request.url,
|
|
113
|
-
resource_type: request.resource_type,
|
|
114
|
-
})}`;
|
|
147
|
+
const identity = networkIdentity(request);
|
|
148
|
+
const shapes = sources.get(identity);
|
|
115
149
|
const values = grouped.get(identity) ?? [];
|
|
116
150
|
values.push({
|
|
117
151
|
status: request.status,
|
|
@@ -119,13 +153,18 @@ const networkMap = (inspection) => {
|
|
|
119
153
|
encoded_data_length: request.encoded_data_length,
|
|
120
154
|
redirects: (request.redirects ?? []).map(({ request_timestamp: _requestTimestamp, redirect_event_timestamp: _redirectEventTimestamp, ...redirect }) => redirect),
|
|
121
155
|
initiator: request.initiator,
|
|
122
|
-
|
|
156
|
+
...(shapes?.request === true
|
|
157
|
+
? { request_shape: request.body_shapes.request }
|
|
158
|
+
: {}),
|
|
159
|
+
...(shapes?.response === true
|
|
160
|
+
? { response_shape: request.body_shapes.response }
|
|
161
|
+
: {}),
|
|
123
162
|
});
|
|
124
163
|
grouped.set(identity, values);
|
|
125
164
|
}
|
|
126
165
|
return new Map([...grouped].map(([identity, values]) => [
|
|
127
166
|
identity,
|
|
128
|
-
|
|
167
|
+
digestCanonicalValue(values.map((value) => digestCanonicalValue(value)).sort()),
|
|
129
168
|
]));
|
|
130
169
|
};
|
|
131
170
|
const webMcpMap = (discovery) => discovery === null
|
|
@@ -147,7 +186,9 @@ const incompleteSections = (completeness) => new Set([
|
|
|
147
186
|
const domProjection = (inspection) => ({
|
|
148
187
|
frames: inspection.frames
|
|
149
188
|
.map(({ url, origin }) => ({ url, origin }))
|
|
150
|
-
.sort((left, right) => left.url
|
|
189
|
+
.sort((left, right) => compareUnicodeCodePoints(left.url, right.url) ||
|
|
190
|
+
compareUnicodeCodePoints(left.origin ?? "", right.origin ?? "") ||
|
|
191
|
+
Number(left.origin !== null) - Number(right.origin !== null)),
|
|
151
192
|
nodes: inspection.dom.nodes.map(({ index: _index, ...node }) => node),
|
|
152
193
|
});
|
|
153
194
|
const scriptProjection = (script) => ({
|
|
@@ -162,17 +203,18 @@ const scriptProjection = (script) => ({
|
|
|
162
203
|
const metadataProjection = (inspection) => ({
|
|
163
204
|
responses: inspection.metadata.responses
|
|
164
205
|
.map(({ request_id: _requestId, ...response }) => response)
|
|
165
|
-
.map((value) =>
|
|
206
|
+
.map((value) => digestCanonicalValue(value))
|
|
207
|
+
.sort(),
|
|
208
|
+
dom_urls: inspection.metadata.dom_urls
|
|
209
|
+
.map((value) => digestCanonicalValue(value))
|
|
166
210
|
.sort(),
|
|
167
|
-
dom_urls: inspection.metadata.dom_urls.map((value) => digest(value)).sort(),
|
|
168
211
|
agent_hints: inspection.metadata.agent_hints
|
|
169
|
-
.map((value) =>
|
|
212
|
+
.map((value) => digestCanonicalValue(value))
|
|
170
213
|
.sort(),
|
|
171
214
|
excluded_dom_urls: inspection.metadata.excluded_dom_urls,
|
|
172
215
|
headers_allowlisted: inspection.metadata.headers_allowlisted,
|
|
173
216
|
});
|
|
174
217
|
const singleton = (key, value) => new Map([[key, value]]);
|
|
175
|
-
const digest = (value) => canonicalDigest(value);
|
|
176
218
|
const accessibilityComparable = (inspection) => {
|
|
177
219
|
const text = inspection.accessibility.text_capture.status === "included" &&
|
|
178
220
|
inspection.accessibility.text_capture.excluded_fields === 0;
|
|
@@ -209,7 +251,7 @@ const storageMap = (storage, includeKeys, includeUsage, fingerprintIdentities) =
|
|
|
209
251
|
const map = new Map([
|
|
210
252
|
[
|
|
211
253
|
"storage:summary",
|
|
212
|
-
|
|
254
|
+
digestCanonicalValue({
|
|
213
255
|
origin: storage.origin,
|
|
214
256
|
values_redacted: storage.values_redacted,
|
|
215
257
|
...(includeUsage
|
|
@@ -225,7 +267,7 @@ const storageMap = (storage, includeKeys, includeUsage, fingerprintIdentities) =
|
|
|
225
267
|
return map;
|
|
226
268
|
const add = (kind, keys) => {
|
|
227
269
|
for (const key of keys) {
|
|
228
|
-
map.set(`storage:${kind}:${key}`,
|
|
270
|
+
map.set(`storage:${kind}:${key}`, digestCanonicalValue(key));
|
|
229
271
|
}
|
|
230
272
|
};
|
|
231
273
|
add("local_storage", storage.local_storage_keys);
|
|
@@ -236,7 +278,7 @@ const storageMap = (storage, includeKeys, includeUsage, fingerprintIdentities) =
|
|
|
236
278
|
const identity = `${fingerprint.scope}:${fingerprint.identity_sha256}`;
|
|
237
279
|
if (!fingerprintIdentities.has(identity))
|
|
238
280
|
continue;
|
|
239
|
-
map.set(`storage:content:${identity}`,
|
|
281
|
+
map.set(`storage:content:${identity}`, digestCanonicalValue(fingerprint.value_sha256));
|
|
240
282
|
}
|
|
241
283
|
return map;
|
|
242
284
|
};
|
|
@@ -50,12 +50,6 @@ const dimensionSchema = z.union([
|
|
|
50
50
|
reason: z.string(),
|
|
51
51
|
}),
|
|
52
52
|
]);
|
|
53
|
-
const legacyUnknownDimension = {
|
|
54
|
-
status: "unknown",
|
|
55
|
-
total_changes: 0,
|
|
56
|
-
changes: [],
|
|
57
|
-
reason: "Dimension was not recorded by this version 1 capture diff.",
|
|
58
|
-
};
|
|
59
53
|
/** Completeness-aware changes across stable browser evidence dimensions. */
|
|
60
54
|
export const webCaptureDiffSchema = z
|
|
61
55
|
.object({
|
|
@@ -69,8 +63,8 @@ export const webCaptureDiffSchema = z
|
|
|
69
63
|
network: dimensionSchema,
|
|
70
64
|
metadata: dimensionSchema,
|
|
71
65
|
webmcp: dimensionSchema,
|
|
72
|
-
accessibility: dimensionSchema
|
|
73
|
-
storage: dimensionSchema
|
|
66
|
+
accessibility: dimensionSchema,
|
|
67
|
+
storage: dimensionSchema,
|
|
74
68
|
}),
|
|
75
69
|
limitations: z.array(z.string()),
|
|
76
70
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import canonicalize from "canonicalize";
|
|
3
|
+
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
|
3
4
|
/** Assign deterministic identities to a bounded resource inventory. */
|
|
4
5
|
export const stableWebResources = (resources) => {
|
|
5
6
|
const keyed = resources.map((resource) => ({
|
|
@@ -8,8 +9,8 @@ export const stableWebResources = (resources) => {
|
|
|
8
9
|
}));
|
|
9
10
|
return [
|
|
10
11
|
...new Map(keyed.map((resource) => [resource.resource_key, resource])).values(),
|
|
11
|
-
].sort((left, right) => left.resource_key
|
|
12
|
-
left.url
|
|
12
|
+
].sort((left, right) => compareUnicodeCodePoints(left.resource_key, right.resource_key) ||
|
|
13
|
+
compareUnicodeCodePoints(left.url, right.url));
|
|
13
14
|
};
|
|
14
15
|
/** Reconcile one script with resource-tree observations without fuzzy matching. */
|
|
15
16
|
export const reconcileWebScript = (script, resources) => {
|
|
@@ -2,8 +2,8 @@ import { z } from "zod";
|
|
|
2
2
|
import { browserCompletenessSchema } from "./browserCompleteness.js";
|
|
3
3
|
import { browserAllowedOriginsSchema, browserEndpointSchema, } from "./browserObservation.js";
|
|
4
4
|
import { browserVersionSchema } from "./browserObservationSchemas.js";
|
|
5
|
-
import {
|
|
6
|
-
import { prefixedDigestSchema } from "./../domain/digests.js";
|
|
5
|
+
import { jsonValueSchema } from "./jsonValue.js";
|
|
6
|
+
import { digestSchema, prefixedDigestSchema } from "./../domain/digests.js";
|
|
7
7
|
/** Input for passive discovery of page-registered WebMCP tools. */
|
|
8
8
|
export const discoverWebMcpToolsInputSchema = z.strictObject({
|
|
9
9
|
cdp_endpoint: browserEndpointSchema,
|
|
@@ -19,7 +19,8 @@ const webMcpToolSchema = z.object({
|
|
|
19
19
|
frame_url: z.string(),
|
|
20
20
|
owner_origin: z.string(),
|
|
21
21
|
declaration_kind: z.enum(["declarative", "imperative"]),
|
|
22
|
-
|
|
22
|
+
input_schema: jsonValueSchema.nullable(),
|
|
23
|
+
input_schema_sha256: digestSchema.nullable(),
|
|
23
24
|
annotations: z.object({
|
|
24
25
|
read_only: z.boolean().nullable(),
|
|
25
26
|
untrusted_content: z.boolean().nullable(),
|
|
@@ -24,7 +24,7 @@ const sourceSchema = z.discriminatedUnion("kind", [
|
|
|
24
24
|
}),
|
|
25
25
|
z.strictObject({
|
|
26
26
|
kind: z.literal("scenario-response"),
|
|
27
|
-
transaction_id: z.string().
|
|
27
|
+
transaction_id: z.string().min(1),
|
|
28
28
|
request_sequence: z.number().int().min(1),
|
|
29
29
|
response_sequence: z.number().int().min(1).nullable(),
|
|
30
30
|
status: z.number().nullable(),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const opcode = (code) => OPCODES.get(code);
|
|
2
1
|
const PREFIX_OPCODES = new Set([
|
|
3
2
|
"unaligned.",
|
|
4
3
|
"volatile.",
|
|
@@ -361,7 +360,7 @@ export const decodeInstructions = (il) => {
|
|
|
361
360
|
const first = il.readUInt8(offset);
|
|
362
361
|
offset += 1;
|
|
363
362
|
const code = first === 0xfe ? 0xfe00 + il.readUInt8(offset++) : first;
|
|
364
|
-
const descriptor =
|
|
363
|
+
const descriptor = OPCODES.get(code);
|
|
365
364
|
if (descriptor === undefined) {
|
|
366
365
|
issue = `Unsupported CIL opcode 0x${code.toString(16)} at IL offset ${String(start)}`;
|
|
367
366
|
break;
|
|
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { createAnalysisExecution, } from "../application/AnalysisProvider.js";
|
|
5
|
-
import {
|
|
5
|
+
import { managedStaticCapabilities } from "./ManagedStaticProviderMetadata.js";
|
|
6
|
+
import { MANAGED_STATIC_PROVIDER } from "../application/InvestigationProviders.js";
|
|
6
7
|
import { MANAGED_TOOL_CONTRACTS, managedTargetInputSchema, } from "../contracts/managed/managedToolContracts.js";
|
|
7
8
|
import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, } from "../domain/analysisErrorCore.js";
|
|
8
9
|
import { EvidenceIntegrityError } from "../domain/evidenceErrors.js";
|
|
@@ -15,7 +16,7 @@ import { inspectManagedNativeBoundariesBytes } from "./ManagedNativeBoundaryInsp
|
|
|
15
16
|
export class ManagedStaticProvider {
|
|
16
17
|
#capabilities = managedStaticCapabilities();
|
|
17
18
|
identity() {
|
|
18
|
-
return
|
|
19
|
+
return MANAGED_STATIC_PROVIDER;
|
|
19
20
|
}
|
|
20
21
|
capabilities() {
|
|
21
22
|
return this.#capabilities;
|
|
@@ -32,11 +33,11 @@ class ManagedStaticClient {
|
|
|
32
33
|
}
|
|
33
34
|
async execute(operation, parameters, options) {
|
|
34
35
|
if (operation === "health")
|
|
35
|
-
return ok(createAnalysisExecution(null,
|
|
36
|
+
return ok(createAnalysisExecution(null, MANAGED_STATIC_PROVIDER));
|
|
36
37
|
if (!isManagedOperation(operation))
|
|
37
|
-
return err(new AnalysisCapabilityUnavailableError(
|
|
38
|
+
return err(new AnalysisCapabilityUnavailableError(MANAGED_STATIC_PROVIDER.id, operation, "Operation is not implemented by the managed static provider."));
|
|
38
39
|
if (this.target.format !== "pe")
|
|
39
|
-
return err(new AnalysisCapabilityUnavailableError(
|
|
40
|
+
return err(new AnalysisCapabilityUnavailableError(MANAGED_STATIC_PROVIDER.id, operation, `Managed static triage requires a PE target; observed ${this.target.format}.`));
|
|
40
41
|
try {
|
|
41
42
|
if (options?.signal?.aborted === true)
|
|
42
43
|
return err(new AnalysisCancelledError(operation));
|
|
@@ -51,7 +52,7 @@ class ManagedStaticClient {
|
|
|
51
52
|
throw new TypeError("Managed snapshot bytes are unavailable");
|
|
52
53
|
this.#snapshotBytes = bytes;
|
|
53
54
|
const result = inspectManagedOperation(operation, parameters, bytes, this.target);
|
|
54
|
-
return ok(createAnalysisExecution(result,
|
|
55
|
+
return ok(createAnalysisExecution(result, MANAGED_STATIC_PROVIDER, {
|
|
55
56
|
rawResult: null,
|
|
56
57
|
limitations: result.limitations,
|
|
57
58
|
subject: this.target,
|
|
@@ -61,7 +62,9 @@ class ManagedStaticClient {
|
|
|
61
62
|
catch (cause) {
|
|
62
63
|
if (options?.signal?.aborted === true)
|
|
63
64
|
return err(new AnalysisCancelledError(operation));
|
|
64
|
-
return err(new ProviderAdapterError(
|
|
65
|
+
return err(new ProviderAdapterError(MANAGED_STATIC_PROVIDER.id, operation, {
|
|
66
|
+
cause,
|
|
67
|
+
}));
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
close() {
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { MANAGED_STATIC_PROVIDER } from "../application/InvestigationProviders.js";
|
|
2
2
|
import { MANAGED_TOOL_CONTRACTS, } from "../contracts/managed/managedToolContracts.js";
|
|
3
|
-
/** Identity of execution-free managed metadata observations. */
|
|
4
|
-
export const MANAGED_STATIC_PROVIDER_IDENTITY = Object.freeze(MANAGED_STATIC_PROVIDER);
|
|
5
|
-
const IDENTITY = MANAGED_STATIC_PROVIDER_IDENTITY;
|
|
6
3
|
/** Declare managed metadata coverage without opening or loading an assembly. */
|
|
7
4
|
export const managedStaticCapabilities = () => Object.freeze(MANAGED_TOOL_CONTRACTS.map((contract) => Object.freeze({
|
|
8
|
-
provider:
|
|
5
|
+
provider: MANAGED_STATIC_PROVIDER,
|
|
9
6
|
operation: contract.name,
|
|
10
7
|
available: true,
|
|
11
8
|
reason: null,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { open } from "node:fs/promises";
|
|
3
|
+
/** Identifies a selected path that cannot be read as one regular file. */
|
|
4
|
+
export class NonRegularFileReadError extends Error {
|
|
5
|
+
path;
|
|
6
|
+
code;
|
|
7
|
+
constructor(path, directory) {
|
|
8
|
+
super(`Selected input must be a regular file: ${path}`);
|
|
9
|
+
this.path = path;
|
|
10
|
+
this.name = "NonRegularFileReadError";
|
|
11
|
+
this.code = directory ? "EISDIR" : "ENOTFILE";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Open and verify the selected file descriptor without blocking on pipes. */
|
|
15
|
+
export const openRegularFile = async (path, options) => {
|
|
16
|
+
options.signal?.throwIfAborted();
|
|
17
|
+
const handle = await open(path, constants.O_RDONLY |
|
|
18
|
+
(constants.O_NONBLOCK ?? 0) |
|
|
19
|
+
(constants.O_NOCTTY ?? 0) |
|
|
20
|
+
(options.symlinks === "reject" ? (constants.O_NOFOLLOW ?? 0) : 0));
|
|
21
|
+
try {
|
|
22
|
+
options.signal?.throwIfAborted();
|
|
23
|
+
const metadata = await handle.stat();
|
|
24
|
+
options.signal?.throwIfAborted();
|
|
25
|
+
if (!metadata.isFile())
|
|
26
|
+
throw new NonRegularFileReadError(path, metadata.isDirectory());
|
|
27
|
+
return handle;
|
|
28
|
+
}
|
|
29
|
+
catch (cause) {
|
|
30
|
+
await handle.close().catch(() => undefined);
|
|
31
|
+
throw cause;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/** Compare one regular file's identity and mutable metadata across a read. */
|
|
35
|
+
export const sameRegularFileState = (expected, observed) => observed.isFile() &&
|
|
36
|
+
expected.dev === observed.dev &&
|
|
37
|
+
expected.ino === observed.ino &&
|
|
38
|
+
expected.size === observed.size &&
|
|
39
|
+
expected.mode === observed.mode &&
|
|
40
|
+
expected.mtimeMs === observed.mtimeMs &&
|
|
41
|
+
expected.ctimeMs === observed.ctimeMs;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Generated from package.json and package-lock.json; do not edit. */
|
|
2
2
|
export const PACKAGE_METADATA = {
|
|
3
3
|
name: "rea-agents",
|
|
4
|
-
version: "6.
|
|
4
|
+
version: "6.3.0", // x-release-please-version
|
|
5
5
|
serverSdkVersion: "2.3.1",
|
|
6
6
|
clientSdkVersion: "2.3.1",
|
|
7
7
|
coreSdkVersion: "2.3.1",
|
|
@@ -10,6 +10,7 @@ import { ProviderRunLineage } from "../process/ProviderRunLineage.js";
|
|
|
10
10
|
import { ProviderProcessSupervisor, } from "../process/ProviderProcess.js";
|
|
11
11
|
import { DEFAULT_GHIDRA_STARTUP_TIMEOUT_MS } from "../config/ghidraStartupTimeout.js";
|
|
12
12
|
import { createGhidraDiagnostics } from "./GhidraDiagnostics.js";
|
|
13
|
+
import { GhidraLaunchError } from "./GhidraLauncher.js";
|
|
13
14
|
import { GhidraResponseBuffer } from "./GhidraResponseBuffer.js";
|
|
14
15
|
import { GhidraResponseRouter } from "./GhidraResponseRouter.js";
|
|
15
16
|
import { GhidraRequestQueue } from "./GhidraRequestQueue.js";
|
|
@@ -290,8 +291,17 @@ export class GhidraClient {
|
|
|
290
291
|
}, { signal: deadline.signal })
|
|
291
292
|
.catch((cause) => err(this.#failure("start", "Ghidra launcher failed", cause)));
|
|
292
293
|
if (!launched.ok) {
|
|
294
|
+
const partialLaunch = launched.error instanceof GhidraLaunchError
|
|
295
|
+
? launched.error.partialLaunch
|
|
296
|
+
: undefined;
|
|
297
|
+
if (partialLaunch !== undefined) {
|
|
298
|
+
this.#launch = partialLaunch;
|
|
299
|
+
this.#process = new ProviderProcessSupervisor(partialLaunch, {
|
|
300
|
+
onDiagnostic: (event) => this.#onProcessDiagnostic(event),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
293
303
|
const failure = deadline.signal.aborted
|
|
294
|
-
? this.#interruptionFailure(deadline)
|
|
304
|
+
? this.#interruptionFailure(deadline, launched.error)
|
|
295
305
|
: launched.error instanceof GhidraSessionError
|
|
296
306
|
? launched.error
|
|
297
307
|
: this.#failure("start", launched.error.message, launched.error);
|
|
@@ -519,10 +529,10 @@ export class GhidraClient {
|
|
|
519
529
|
const failure = this.#interruptionFailure(deadline);
|
|
520
530
|
return this.#startupFailure(failure);
|
|
521
531
|
}
|
|
522
|
-
#interruptionFailure(deadline) {
|
|
532
|
+
#interruptionFailure(deadline, cause) {
|
|
523
533
|
return deadline.interruption === "cancelled"
|
|
524
|
-
? this.#failure("cancelled", "Ghidra startup was cancelled")
|
|
525
|
-
: this.#failure("timeout", "Ghidra startup deadline elapsed",
|
|
534
|
+
? this.#failure("cancelled", "Ghidra startup was cancelled", cause)
|
|
535
|
+
: this.#failure("timeout", "Ghidra startup deadline elapsed", cause, {
|
|
526
536
|
timeoutMs: deadline.timeoutMs,
|
|
527
537
|
});
|
|
528
538
|
}
|
|
@@ -1,14 +1,84 @@
|
|
|
1
1
|
import { snapshotEnvironment } from "../process/snapshotEnvironment.js";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { constants } from "node:fs";
|
|
4
|
-
import { accessSync, readFileSync } from "node:fs";
|
|
4
|
+
import { accessSync, closeSync, fstatSync, openSync, readFileSync, readSync, statSync, } from "node:fs";
|
|
5
5
|
import { posix, win32 } from "node:path";
|
|
6
|
+
import { parseExecutableHeader, } from "../domain/binaryTarget.js";
|
|
6
7
|
import { ghidraApplicationProperties, ghidraJavaMajorAccepted, ghidraJavaRangeLabel, ghidraJavaRequirement, ghidraReleaseAccepted, ghidraReleaseCompatibility, ghidraReleaseLine, ghidraVersionDetail, SUPPORTED_GHIDRA_JAVA_MAJOR, SUPPORTED_GHIDRA_VERSION, } from "./GhidraInstallationPolicy.js";
|
|
7
8
|
const NATIVE_PLATFORMS = {
|
|
8
9
|
linux: { x64: "linux_x86_64", arm64: "linux_arm_64" },
|
|
9
10
|
darwin: { x64: "mac_x86_64", arm64: "mac_arm_64" },
|
|
10
11
|
win32: { x64: "win_x86_64" },
|
|
11
12
|
};
|
|
13
|
+
/** Executable format each supported host runs its own Ghidra native tools as. */
|
|
14
|
+
const NATIVE_TOOL_FORMATS = {
|
|
15
|
+
linux: "elf",
|
|
16
|
+
darwin: "mach-o",
|
|
17
|
+
win32: "pe",
|
|
18
|
+
};
|
|
19
|
+
/** Node architecture identifiers as the executable-header parser reports them. */
|
|
20
|
+
const NATIVE_ARCHITECTURES = [
|
|
21
|
+
{ node: "x64", binary: "x86_64" },
|
|
22
|
+
{ node: "ia32", binary: "x86" },
|
|
23
|
+
{ node: "arm", binary: "arm" },
|
|
24
|
+
{ node: "arm64", binary: "arm64" },
|
|
25
|
+
];
|
|
26
|
+
/** Bound installation discovery reads; larger headers remain unknown. */
|
|
27
|
+
const EXECUTABLE_HEADER_PROBE_BYTES = 4096;
|
|
28
|
+
/**
|
|
29
|
+
* Judge a native decompiler by its own executable header instead of the
|
|
30
|
+
* directory name that admits it.
|
|
31
|
+
*
|
|
32
|
+
* Only a header that positively contradicts the host is `incompatible`. An
|
|
33
|
+
* absent, unreadable, truncated, or unrecognized header stays `unknown`, so a
|
|
34
|
+
* file the other checks already accept is never refused on missing evidence.
|
|
35
|
+
*/
|
|
36
|
+
const assessNativeDecompiler = (path, platform, architecture, probe) => {
|
|
37
|
+
const expectedFormat = NATIVE_TOOL_FORMATS[platform];
|
|
38
|
+
const expectedArchitecture = NATIVE_ARCHITECTURES.find(({ node }) => node === architecture)?.binary;
|
|
39
|
+
if (expectedFormat === undefined || expectedArchitecture === undefined)
|
|
40
|
+
return {
|
|
41
|
+
status: "unknown",
|
|
42
|
+
detail: `${path} is executable, but no native tool format is defined for ${platform}/${architecture}`,
|
|
43
|
+
};
|
|
44
|
+
let parsed = probe === undefined
|
|
45
|
+
? undefined
|
|
46
|
+
: parseExecutableHeader(probe.bytes, architecture, probe.size);
|
|
47
|
+
if (probe !== undefined && parsed !== undefined && !parsed.ok) {
|
|
48
|
+
// FAT parsing selects a host slice. A validated foreign slice can still
|
|
49
|
+
// prove that its complete architecture table excludes this host. If the
|
|
50
|
+
// host is listed but its slice is malformed, preserve the unknown outcome.
|
|
51
|
+
for (const { node } of NATIVE_ARCHITECTURES) {
|
|
52
|
+
if (node === architecture)
|
|
53
|
+
continue;
|
|
54
|
+
const alternative = parseExecutableHeader(probe.bytes, node, probe.size);
|
|
55
|
+
if (alternative.ok &&
|
|
56
|
+
!alternative.value.availableArchitectures.includes(expectedArchitecture)) {
|
|
57
|
+
parsed = alternative;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (parsed === undefined || !parsed.ok)
|
|
63
|
+
return {
|
|
64
|
+
status: "unknown",
|
|
65
|
+
detail: `${path} is executable, but its executable format for ${platform}/${architecture} could not be established`,
|
|
66
|
+
};
|
|
67
|
+
const observed = `${parsed.value.format} ${parsed.value.architecture}`;
|
|
68
|
+
const expected = `${expectedFormat} ${expectedArchitecture}`;
|
|
69
|
+
if (observed === expected)
|
|
70
|
+
return {
|
|
71
|
+
status: "compatible",
|
|
72
|
+
detail: `${path} reports ${observed}, which matches ${platform}/${architecture}`,
|
|
73
|
+
};
|
|
74
|
+
const nativeDirectory = NATIVE_PLATFORMS[platform]?.[architecture];
|
|
75
|
+
const executable = platform === "win32" ? "decompile.exe" : "decompile";
|
|
76
|
+
return {
|
|
77
|
+
status: "incompatible",
|
|
78
|
+
detail: `${path} reports ${observed}, but ${platform}/${architecture} requires ${expected}`,
|
|
79
|
+
remediation: `Provide an executable Ghidra/Features/Decompiler/os/${nativeDirectory ?? "<platform>"}/${executable} built for ${expected}, or its build/os equivalent. REA does not build or install native tools.`,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
12
82
|
/** Inspect an installation without importing a target or modifying Ghidra. */
|
|
13
83
|
export const inspectGhidraInstallation = (options, host = systemGhidraInstallationHost()) => {
|
|
14
84
|
const platform = options.platform ?? process.platform;
|
|
@@ -137,14 +207,7 @@ const installationChecks = ({ coordinates, platform, architecture, javaHome, hos
|
|
|
137
207
|
detail: coordinates.analyzeHeadlessPath ?? "analyzeHeadless unavailable",
|
|
138
208
|
remediation: "Restore support/analyzeHeadless or support/analyzeHeadless.bat from the official Ghidra release.",
|
|
139
209
|
}),
|
|
140
|
-
|
|
141
|
-
name: "native_decompiler",
|
|
142
|
-
passed: coordinates.nativeDecompilerPath !== null,
|
|
143
|
-
code: "executable_missing",
|
|
144
|
-
detail: coordinates.nativeDecompilerPath ??
|
|
145
|
-
`Matching native decompiler for ${platform}/${architecture} was not found`,
|
|
146
|
-
remediation: `Provide an executable Ghidra/Features/Decompiler/os/${NATIVE_PLATFORMS[platform]?.[architecture] ?? "<platform>"}/${platform === "win32" ? "decompile.exe" : "decompile"}, or its build/os equivalent. REA does not build or install native tools.`,
|
|
147
|
-
}),
|
|
210
|
+
nativeDecompilerCheck({ coordinates, platform, architecture, host }),
|
|
148
211
|
javaCheck(coordinates.java, coordinates.javaCommand, javaHome, coordinates.properties),
|
|
149
212
|
];
|
|
150
213
|
/**
|
|
@@ -226,6 +289,23 @@ const installationCheck = (options) => options.passed
|
|
|
226
289
|
detail: options.detail,
|
|
227
290
|
remediation: options.remediation,
|
|
228
291
|
};
|
|
292
|
+
/** Require a present native decompiler without positively incompatible bytes. */
|
|
293
|
+
const nativeDecompilerCheck = ({ coordinates, platform, architecture, host, }) => {
|
|
294
|
+
const path = coordinates.nativeDecompilerPath;
|
|
295
|
+
const compatibility = path === null
|
|
296
|
+
? undefined
|
|
297
|
+
: assessNativeDecompiler(path, platform, architecture, host.executableHeader?.(path));
|
|
298
|
+
return installationCheck({
|
|
299
|
+
name: "native_decompiler",
|
|
300
|
+
passed: path !== null && compatibility?.status !== "incompatible",
|
|
301
|
+
code: "executable_missing",
|
|
302
|
+
detail: compatibility?.detail ??
|
|
303
|
+
`Matching native decompiler for ${platform}/${architecture} was not found`,
|
|
304
|
+
remediation: compatibility?.status === "incompatible"
|
|
305
|
+
? compatibility.remediation
|
|
306
|
+
: `Provide an executable Ghidra/Features/Decompiler/os/${NATIVE_PLATFORMS[platform]?.[architecture] ?? "<platform>"}/${platform === "win32" ? "decompile.exe" : "decompile"}, or its build/os equivalent. REA does not build or install native tools.`,
|
|
307
|
+
});
|
|
308
|
+
};
|
|
229
309
|
const versionCheck = (properties) => {
|
|
230
310
|
const raw = properties?.providerVersion;
|
|
231
311
|
return installationCheck({
|
|
@@ -280,10 +360,31 @@ const systemGhidraInstallationHost = () => ({
|
|
|
280
360
|
return undefined;
|
|
281
361
|
}
|
|
282
362
|
},
|
|
363
|
+
executableHeader(path) {
|
|
364
|
+
let handle;
|
|
365
|
+
try {
|
|
366
|
+
handle = openSync(path, constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOCTTY);
|
|
367
|
+
const metadata = fstatSync(handle);
|
|
368
|
+
if (!metadata.isFile())
|
|
369
|
+
return undefined;
|
|
370
|
+
const bytes = Buffer.alloc(EXECUTABLE_HEADER_PROBE_BYTES);
|
|
371
|
+
const read = readSync(handle, bytes, 0, bytes.length, 0);
|
|
372
|
+
return { bytes: bytes.subarray(0, read), size: metadata.size };
|
|
373
|
+
}
|
|
374
|
+
catch (cause) {
|
|
375
|
+
// best-effort cleanup: a header that cannot be read stays unknown.
|
|
376
|
+
void cause;
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
379
|
+
finally {
|
|
380
|
+
if (handle !== undefined)
|
|
381
|
+
closeSync(handle);
|
|
382
|
+
}
|
|
383
|
+
},
|
|
283
384
|
executable(path) {
|
|
284
385
|
try {
|
|
285
386
|
accessSync(path, constants.X_OK);
|
|
286
|
-
return
|
|
387
|
+
return statSync(path).isFile();
|
|
287
388
|
}
|
|
288
389
|
catch (cause) {
|
|
289
390
|
// best-effort cleanup: optional executable probing; failure means missing.
|