rea-agents 5.0.0 → 6.1.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 +168 -655
- package/bridge/android/ReaJadxBridge.java +14 -5
- package/bridge/ghidra/ReaGhidraBridge.java +173 -47
- package/bridge/hopper_bridge.py +310 -128
- package/bridge/mitmproxy/capture.py +208 -0
- package/bridge/native/rea_lldb_tracer.py +839 -0
- package/bridge/process/ProcessRunTokenReader.swift +297 -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 +147 -0
- package/dist/android/JadxConfiguration.js +196 -33
- package/dist/android/JadxMcpTransport.js +4 -0
- package/dist/android/JadxProtocol.js +6 -0
- package/dist/android/JadxProvider.js +20 -2
- package/dist/android/JadxSession.js +23 -4
- package/dist/application/AnalysisContextQueries.js +8 -35
- package/dist/application/AnalysisDocument.js +32 -0
- package/dist/application/AppBundleExecutable.js +67 -21
- package/dist/application/CallPathTracing.js +19 -3
- package/dist/application/CapabilityInventory.js +27 -3
- package/dist/application/ClientConfigurationDocument.js +871 -6
- package/dist/application/ClientRegistrationIdentity.js +26 -5
- package/dist/application/ClientRegistrationStatus.js +29 -14
- package/dist/application/DirectAnalysis.js +97 -33
- package/dist/application/Doctor.js +4 -2
- package/dist/application/DoctorScope.js +25 -13
- package/dist/application/EnhancedLiteralTracing.js +22 -25
- package/dist/application/EnhancedTools.js +13 -11
- package/dist/application/EvidenceBundleFiles.js +24 -12
- package/dist/application/FunctionAnalysisEvidence.js +2 -3
- package/dist/application/InventoryProjectionEvidence.js +4 -3
- package/dist/application/InvestigationProviders.js +3 -2
- package/dist/application/JsonFiles.js +50 -28
- package/dist/application/ProcedureAddressResolution.js +11 -0
- package/dist/application/ReconstructionCoverageService.js +7 -4
- package/dist/application/ReconstructionObligationCandidates.js +5 -1
- package/dist/application/ReconstructionObligationLedgerService.js +4 -3
- package/dist/application/Setup.js +13 -1
- package/dist/application/SetupClientConfiguration.js +10 -15
- package/dist/application/SetupHost.js +5 -2
- package/dist/application/SetupPlan.js +5 -3
- package/dist/application/SupportedClients.js +34 -1
- package/dist/application/Uninstall.js +4 -3
- package/dist/application/Update.js +18 -3
- package/dist/application/UpdateRuntime.js +46 -25
- package/dist/application/WebModuleTraceService.js +4 -2
- package/dist/application/WebNetworkCapturePort.js +1 -0
- package/dist/application/WebNetworkCaptureService.js +134 -0
- package/dist/application/WebRuntimeService.js +5 -3
- package/dist/application/WebScriptExportService.js +2 -2
- package/dist/application/WebSourceLocationService.js +4 -2
- package/dist/application/WindowsCapabilities.js +1 -1
- package/dist/application/WorkflowEvidence.js +82 -0
- package/dist/application/binary/AnalysisProviderEvaluation.js +21 -5
- package/dist/application/binary/AnalysisProviderRegistry.js +2 -0
- package/dist/application/binary/AnalysisSnapshotCache.js +52 -12
- package/dist/application/binary/BinarySession.js +131 -76
- package/dist/application/binary/BinarySessionOpen.js +19 -6
- package/dist/application/binary/BinarySessionRecords.js +45 -3
- package/dist/application/binary/BinarySessionStatus.js +17 -1
- package/dist/application/binary/CompositeProvider.js +13 -0
- package/dist/application/binary/LazyAnalysisProvider.js +3 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutService.js +59 -0
- package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
- package/dist/application/binaryDiagnostics/RecordedCrashService.js +62 -0
- package/dist/application/evm/EvmInterfacePort.js +1 -0
- package/dist/application/evm/EvmInterfaceService.js +64 -0
- package/dist/application/investigation/EvidenceLedger.js +21 -7
- package/dist/application/investigation/InvestigationRecords.js +8 -0
- package/dist/application/javascript/JavaScriptAnalysisControl.js +34 -0
- package/dist/application/javascript/JavaScriptApplicationEvidence.js +25 -5
- package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +19 -2
- package/dist/application/javascript/JavaScriptApplicationService.js +34 -14
- package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +5 -5
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +47 -27
- package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +39 -7
- package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +80 -27
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +33 -12
- package/dist/application/javascript/JavaScriptModuleRelationships.js +37 -8
- package/dist/application/javascript/JavaScriptRecoveryService.js +4 -2
- package/dist/application/javascript/JavaScriptRuntimeObservationService.js +3 -1
- package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
- package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +149 -72
- 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 +27 -5
- package/dist/application/managed/ManagedApplicationGraphService.js +4 -3
- package/dist/application/managed/ManagedMemberComparisonService.js +7 -4
- package/dist/application/managed/ManagedNativeVerificationService.js +4 -3
- package/dist/application/managed/ManagedReconstructionService.js +4 -3
- package/dist/application/{ProcessCli.js → process/ProcessCli.js} +23 -15
- package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +8 -7
- package/dist/application/workflowInputError.js +18 -0
- package/dist/artifacts/ArtifactProvider.js +42 -5
- package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
- package/dist/artifacts/MachOSliceArtifactReader.js +98 -14
- package/dist/artifacts/NativeDmgArtifactReader.js +13 -6
- package/dist/artifacts/ZipArtifactReader.js +3 -0
- package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +22 -3
- package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
- package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +176 -56
- package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +195 -0
- package/dist/artifacts/apple/KeyedArchiveReader.js +36 -22
- package/dist/artifacts/apple/MachoLoadCommandReader.js +355 -0
- package/dist/artifacts/apple/NibArchive.js +81 -14
- package/dist/artifacts/apple/NibViewHierarchy.js +10 -3
- package/dist/artifacts/apple/PlistNumberLiterals.js +164 -0
- package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
- package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +16 -11
- 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 +14 -17
- package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
- package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +6 -2
- package/dist/artifacts/readStableArtifact.js +20 -4
- package/dist/browser/CdpCaptureCompleteness.js +2 -0
- package/dist/browser/CdpCaptureEventHandlers.js +165 -12
- package/dist/browser/CdpCaptureEventTypes.js +5 -1
- package/dist/browser/CdpCaptureEvents.js +34 -0
- package/dist/browser/CdpConnection.js +30 -11
- package/dist/browser/CdpOptionalCommand.js +2 -3
- package/dist/browser/CdpPageCapture.js +23 -1
- package/dist/browser/PlaywrightElectronActiveActions.js +32 -23
- package/dist/browser/PlaywrightElectronActiveProjection.js +141 -0
- package/dist/browser/PlaywrightElectronActiveProvider.js +130 -122
- package/dist/browser/WebSourceMapFetcher.js +426 -72
- package/dist/browser/assets/LocalWebScriptArtifacts.js +2 -2
- package/dist/browser/history/CaptureFailures.js +137 -0
- package/dist/browser/history/CaptureFormatError.js +10 -0
- package/dist/browser/history/CaptureRedaction.js +65 -0
- package/dist/browser/history/CaptureRelease.js +17 -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 +92 -0
- package/dist/browser/history/HarSchema.js +72 -0
- package/dist/browser/history/HistoricalCaptureDecoder.js +188 -0
- package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
- package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
- package/dist/cli/analysisSelector.js +23 -0
- package/dist/cli/applicationCommands.js +2 -3
- 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 +78 -20
- package/dist/cli/electronCommands.js +21 -13
- package/dist/cli/evmCommands.js +24 -0
- package/dist/cli/firmwareCommands.js +6 -3
- package/dist/cli/javascriptApplicationAnalysis.js +20 -3
- package/dist/cli/jsonOutput.js +38 -0
- package/dist/cli/nativeCallCommands.js +34 -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/webRuntimeCommands.js +8 -8
- package/dist/cli.js +11 -4
- package/dist/cliBrowserScenarioCommands.js +2 -2
- package/dist/cliCommandNames.js +6 -1
- package/dist/cliJsonInput.js +35 -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/config/environment.js +4 -0
- package/dist/config/ghidraStartupTimeout.js +17 -0
- package/dist/config/mcpResponseBudget.js +22 -0
- package/dist/config/parseConfig.js +2 -1
- package/dist/contracts/artifactToolContracts.js +6 -0
- package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
- package/dist/contracts/browserToolContracts.js +4 -3
- package/dist/contracts/enhancedInputs.js +5 -3
- package/dist/contracts/enhancedToolContracts.js +3 -3
- package/dist/contracts/errorSchemas.js +8 -1
- package/dist/contracts/evm/evmToolContracts.js +20 -0
- package/dist/contracts/hopperMemoryContracts.js +2 -5
- package/dist/contracts/investigationExamples.js +2 -2
- package/dist/contracts/javascript/electronToolContracts.js +1 -1
- package/dist/contracts/managed/managedToolContracts.js +8 -6
- package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
- package/dist/contracts/native/nativeToolContracts.js +21 -3
- package/dist/contracts/officialToolContracts.js +26 -24
- package/dist/contracts/process/processCaptureExample.js +2 -0
- package/dist/contracts/promptContracts.js +3 -3
- package/dist/contracts/sessionLifecycleInputs.js +18 -5
- package/dist/contracts/sessionStatusContract.js +8 -2
- package/dist/contracts/sessionToolContracts.js +8 -3
- package/dist/contracts/sessionToolSchemas.js +8 -4
- package/dist/contracts/toolContractExamples.js +1 -2
- package/dist/contracts/toolContractHelpers.js +4 -1
- package/dist/contracts/toolContracts.js +6 -0
- package/dist/contracts/toolEffects.js +30 -1
- package/dist/contracts/toolOutputSchemaGroups.js +33 -28
- package/dist/contracts/toolOutputSchemaPrimitives.js +19 -1
- package/dist/contracts/toolSchemaMetadata.js +69 -10
- package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
- package/dist/domain/analysisErrorBase.js +22 -2
- package/dist/domain/analysisErrorCore.js +61 -5
- package/dist/domain/analysisErrorPresentation.js +95 -12
- package/dist/domain/analysisErrorProjection.js +148 -19
- package/dist/domain/analysisSnapshot.js +166 -2
- package/dist/domain/android/androidAnalysis.js +2 -4
- package/dist/domain/android/androidApplication.js +17 -9
- package/dist/domain/apple/appleApplication.js +41 -14
- package/dist/domain/apple/dyldEnvironment.js +73 -0
- package/dist/domain/apple/dyldPaths.js +81 -0
- package/dist/domain/apple/dylibResolution.js +478 -0
- package/dist/domain/apple/dylibResolutionFindings.js +77 -0
- package/dist/domain/apple/interfaceBuilderGraph.js +25 -13
- package/dist/domain/apple/interfaceBuilderKeyedArchive.js +13 -3
- package/dist/domain/apple/keyedArchive.js +9 -1
- package/dist/domain/apple/machoContainer.js +54 -0
- package/dist/domain/apple/plistNumbers.js +76 -0
- package/dist/domain/artifactIdentity.js +29 -0
- package/dist/domain/artifactInventoryEvidence.js +19 -24
- package/dist/domain/artifactPathSyntax.js +1 -1
- package/dist/domain/bridgeCandidateProjection.js +71 -0
- package/dist/domain/browserCompleteness.js +1 -0
- package/dist/domain/browserObservation.js +1 -1
- package/dist/domain/browserObservationError.js +24 -5
- package/dist/domain/browserObservationSchemas.js +13 -0
- package/dist/domain/browserScenarioValues.js +5 -2
- package/dist/domain/bundleComparison.js +3 -2
- package/dist/domain/callPathSchemas.js +1 -1
- package/dist/domain/canonicalDigest.js +13 -1
- package/dist/domain/canonicalJsonDigestSteps.js +133 -0
- package/dist/domain/canonicalOrdering.js +9 -5
- package/dist/domain/changedBehavior.js +9 -9
- package/dist/domain/comparisonSourceEvidence.js +40 -0
- package/dist/domain/comparisonStatus.js +2 -30
- package/dist/domain/configurationErrors.js +1 -1
- package/dist/domain/conformanceTrustGate.js +0 -16
- package/dist/domain/evidence.js +78 -11
- package/dist/domain/evidenceBundle.js +42 -1
- package/dist/domain/evidenceErrors.js +12 -1
- package/dist/domain/evm/evmInterface.js +63 -0
- package/dist/domain/explicitSensitiveFailure.js +108 -0
- package/dist/domain/explicitSensitiveValues.js +10 -0
- package/dist/domain/firmware/firmwareAnalysis.js +10 -4
- package/dist/domain/hopperErrors.js +23 -2
- package/dist/domain/hopperValues.js +51 -8
- package/dist/domain/immutableJson.js +114 -0
- package/dist/domain/inputIssueProjection.js +3 -0
- package/dist/domain/javascript/electronActiveObservation.js +37 -6
- package/dist/domain/javascript/electronStaticAnalysisValues.js +6 -12
- package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
- package/dist/domain/javascript/javascriptApplicationAnalysis.js +71 -23
- package/dist/domain/javascript/javascriptApplicationGraph.js +13 -0
- package/dist/domain/javascript/javascriptAstValues.js +20 -0
- package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
- package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
- package/dist/domain/javascript/javascriptRecovery.js +3 -8
- package/dist/domain/javascript/javascriptSemanticAnalysis.js +97 -2
- package/dist/domain/javascript/javascriptSemanticCoverage.js +6 -5
- package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +9 -17
- package/dist/domain/javascript/javascriptSemanticDataEffects.js +22 -5
- package/dist/domain/javascript/javascriptSemanticGraph.js +34 -5
- package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +1 -0
- package/dist/domain/javascript/javascriptSemanticPrimitives.js +25 -17
- package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +8 -4
- package/dist/domain/javascript/javascriptSemanticResourceLimits.js +77 -0
- package/dist/domain/javascript/javascriptSemanticValues.js +83 -11
- package/dist/domain/javascript/javascriptSourceParser.js +6 -2
- package/dist/domain/javascript/javascriptStaticAnalysis.js +22 -17
- package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +5 -3
- package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +14 -4
- package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +66 -7
- package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +28 -16
- package/dist/domain/javascript/javascriptStaticAnalysisState.js +3 -2
- package/dist/domain/jsonSerialization.js +109 -0
- package/dist/domain/jsonValue.js +91 -3
- package/dist/domain/localPath.js +8 -0
- package/dist/domain/managed/managedApplicationGraph.js +10 -10
- package/dist/domain/managed/managedInspectionEvidence.js +16 -0
- package/dist/domain/managed/managedMemberComparisonMatch.js +5 -9
- package/dist/domain/managed/managedNativeVerification.js +3 -9
- package/dist/domain/native/binaryLayout.js +357 -0
- package/dist/domain/native/fileOffsetPartialObservation.js +1 -0
- package/dist/domain/native/nativeCallObservation.js +186 -0
- package/dist/domain/native/nativeCallPartialObservation.js +33 -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} +143 -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} +39 -5
- package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
- package/dist/domain/{processScenario.js → process/processScenario.js} +11 -10
- package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
- package/dist/domain/propertyListKeys.js +1 -1
- package/dist/domain/propertyListXmlText.js +19 -0
- package/dist/domain/providerAdapterError.js +1 -1
- package/dist/domain/providerOperationHealth.js +54 -0
- package/dist/domain/providerSelectionError.js +4 -2
- package/dist/domain/reconstructionVerification.js +9 -46
- package/dist/domain/reconstructionVerificationSchemas.js +3 -3
- package/dist/domain/residualUnknown.js +3 -2
- package/dist/domain/staticRuntimeCorrelation.js +9 -46
- package/dist/domain/webBundleAnalysis.js +20 -8
- package/dist/domain/webBundleAnalyzerInspection.js +13 -8
- package/dist/domain/webBundleAnalyzerResult.js +41 -2
- package/dist/domain/webCaptureDiff.js +1 -0
- package/dist/domain/webNetworkCapture.js +118 -0
- package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
- package/dist/domain/webRuntime.js +19 -5
- package/dist/domain/webSourceLocation.js +1 -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/firmware/FirmwareFiles.js +1 -1
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraAnalysisProfile.js +5 -0
- package/dist/ghidra/GhidraClient.js +47 -14
- package/dist/ghidra/GhidraDiagnostics.js +7 -7
- package/dist/ghidra/GhidraFunctionValues.js +10 -7
- package/dist/ghidra/GhidraInstallation.js +18 -21
- package/dist/ghidra/GhidraInventoryValues.js +14 -4
- package/dist/ghidra/GhidraJavaLaunch.js +62 -0
- package/dist/ghidra/GhidraLauncher.js +34 -16
- package/dist/ghidra/GhidraProvider.js +2 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +15 -3
- package/dist/ghidra/GhidraProviderClient.js +23 -12
- package/dist/ghidra/GhidraSessionError.js +11 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +47 -12
- package/dist/ghidra/GhidraTransport.js +20 -0
- package/dist/hopper/BridgeLauncher.js +33 -11
- package/dist/hopper/HopperAnalysisProfile.js +62 -4
- package/dist/hopper/HopperCleanup.js +15 -3
- package/dist/hopper/HopperClient.js +222 -14
- package/dist/hopper/HopperFileOffset.js +237 -0
- package/dist/hopper/HopperMachOImage.js +196 -0
- package/dist/hopper/HopperOperationStage.js +6 -0
- package/dist/hopper/HopperProvider.js +63 -9
- package/dist/hopper/HopperProviderCapabilities.js +19 -1
- package/dist/hopper/HopperRegexSearch.js +137 -0
- package/dist/hopper/HopperRegexWorker.js +27 -0
- package/dist/hopper/HopperRequestQueue.js +68 -19
- package/dist/hopper/HopperTargetLease.js +3 -2
- package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
- package/dist/inspector/JavaScriptRuntimeScope.js +9 -1
- package/dist/inspector/V8InspectorCaptureProjection.js +8 -3
- package/dist/inspector/V8InspectorProvider.js +47 -11
- package/dist/javascript/recovery/WakaruCommand.js +3 -0
- package/dist/javascript/sourceMaps/DecodedSourceMap.js +47 -0
- package/dist/javascript/sourceMaps/SourceMapCommand.js +17 -0
- package/dist/javascript/sourceMaps/SourceMapFormat.js +79 -31
- package/dist/javascript/sourceMaps/TraceSourceMap.js +14 -28
- package/dist/mcpDoctor.js +62 -2
- package/dist/native/AppleDispatchCoverage.js +47 -6
- package/dist/native/AppleDispatchDecodeFacts.js +6 -0
- package/dist/native/AppleDispatchMetadata.js +6 -3
- package/dist/native/AppleMachoFixups.js +48 -24
- package/dist/native/AppleMachoSelection.js +32 -16
- package/dist/native/AppleObjcCategories.js +6 -2
- package/dist/native/AppleObjcDispatchFacets.js +5 -2
- package/dist/native/AppleObjcProperties.js +2 -1
- package/dist/native/AppleSwiftDispatchFacets.js +27 -21
- package/dist/native/AppleSwiftVtables.js +21 -15
- package/dist/native/CommandRunner.js +82 -84
- package/dist/native/LldbCallTracer.js +541 -0
- package/dist/native/LldbRetainedObservations.js +178 -0
- package/dist/native/NativeCallObservation.js +194 -0
- package/dist/native/NativeMacOSProvider.js +117 -20
- package/dist/native/NativeMacOSProviderMetadata.js +8 -3
- package/dist/native/SignatureTargetBinding.js +155 -0
- package/dist/native/parsers/lipo.js +71 -1
- package/dist/native/pwndbg/PwndbgCoreContext.js +180 -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 +103 -0
- package/dist/process/DarwinProcessRunTokenReader.js +293 -0
- package/dist/process/ExecFileOutput.js +20 -0
- package/dist/process/OwnedCommand.js +118 -0
- package/dist/process/ProcessOwnership.js +431 -51
- package/dist/process/ProcessOwnershipObservation.js +209 -27
- package/dist/process/ProviderProcess.js +68 -10
- package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
- package/dist/{application → process/capture}/ProcessCaptureError.js +18 -2
- package/dist/process/capture/ProcessCaptureLifecycle.js +681 -0
- package/dist/{application → process/capture}/ProcessHarness.js +195 -29
- package/dist/{application → process/capture}/ProcessSampling.js +2 -2
- package/dist/server/EvidenceMcpServer.js +146 -0
- package/dist/server/createServer.js +37 -8
- package/dist/server/promptCompletion.js +10 -3
- package/dist/server/registerAndroidTools.js +2 -4
- package/dist/server/registerApplicationTools/helpers.js +2 -4
- package/dist/server/registerArtifactComparisonTool.js +11 -7
- package/dist/server/registerBinaryDiagnosticsTools.js +15 -0
- package/dist/server/registerBrowserScenarioTool.js +2 -4
- package/dist/server/registerBrowserTools.js +2 -4
- package/dist/server/registerBundleComparisonTool.js +2 -3
- package/dist/server/registerCloseLifecycleTool.js +8 -18
- package/dist/server/registerElectronTools.js +2 -4
- package/dist/server/registerEnhancedTools.js +18 -59
- package/dist/server/registerEvidenceTools.js +3 -5
- package/dist/server/registerEvmTools.js +15 -0
- package/dist/server/registerFirmwareTools.js +2 -4
- package/dist/server/registerFunctionComparisonTool.js +10 -6
- package/dist/server/registerInvestigationTools.js +7 -7
- package/dist/server/registerJavaScriptRecoveryTool.js +2 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +2 -4
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +2 -4
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +2 -4
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +2 -4
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +2 -4
- package/dist/server/registerOfficialTools.js +2 -4
- package/dist/server/registerProcessComparisonTool.js +72 -46
- package/dist/server/registerRecordedCrashTools.js +15 -0
- package/dist/server/registerSessionRecordTools.js +5 -6
- package/dist/server/registerSessionStatusTool.js +17 -3
- package/dist/server/registerSessionTools.js +13 -5
- package/dist/server/registerWebModuleTool.js +2 -4
- package/dist/server/registerWebNetworkCaptureTool.js +26 -0
- package/dist/server/registerWebRuntimeTools.js +3 -7
- package/dist/server/registerWebScriptTool.js +2 -4
- package/dist/server/registerWebSourceLocationTool.js +2 -4
- package/dist/server/runDerivedOperation.js +15 -3
- package/dist/server/sessionAvailabilityPolicy.js +3 -1
- package/dist/server/sessionToolPolicies.js +1 -1
- package/dist/server/toolRegistrationOptions.js +2 -2
- package/dist/server/toolResult.js +63 -11
- package/dist/server/toolResultEncoding.js +33 -0
- package/install.sh +1 -1
- package/native/windows/build/manifest.json +2 -2
- package/native/windows/build/rea-windows-x64.node +0 -0
- package/package.json +42 -9
- package/scripts/electron-active-hook-boundaries.cjs +18 -20
- package/scripts/electron-active-hook.cjs +241 -37
- package/scripts/rea.mjs +11 -3
- package/skills/reverse-engineer-anything/SKILL.md +32 -4
- package/skills/reverse-engineer-anything/references/javascript-applications.md +8 -1
- 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/ghidra/GhidraDefaults.js +0 -2
- /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/{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
|
@@ -1,49 +1,55 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { TextDecoder } from "node:util";
|
|
3
2
|
import { parseBinary } from "plist";
|
|
4
3
|
import { z } from "zod";
|
|
4
|
+
import { AnalysisInputError } from "../../domain/analysisErrorCore.js";
|
|
5
5
|
import { projectPlistValue } from "../../domain/apple/plistValue.js";
|
|
6
|
+
import { createPlistNumberProjection } from "../../domain/apple/plistNumbers.js";
|
|
7
|
+
import { binaryPlistNumberLiterals, xmlPlistNumberLiterals, } from "./PlistNumberLiterals.js";
|
|
6
8
|
import { omittedPrototypeKeysLimitation, parseXmlPropertyList, } from "../../domain/propertyListKeys.js";
|
|
7
9
|
import { keyedArchiveInputSchema, keyedArchiveResultSchema, projectKeyedArchive, } from "../../domain/apple/keyedArchive.js";
|
|
8
10
|
import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
|
|
9
11
|
import { ArtifactReaderFailure } from "../ArtifactReader.js";
|
|
12
|
+
import { decodeXmlPlistText } from "../../domain/propertyListXmlText.js";
|
|
10
13
|
const MAX_BYTES = 64 * 1024 * 1024;
|
|
11
14
|
/** Parse inert plist bytes, preserving data and dates with explicit typed values. */
|
|
12
15
|
export const decodeKeyedArchiveBytes = (bytes, selection) => {
|
|
13
16
|
if (bytes.length > MAX_BYTES)
|
|
14
17
|
throw new RangeError("Keyed archive exceeds 64 MiB");
|
|
18
|
+
if (bytes.subarray(0, 10).toString("ascii") === "NIBArchive")
|
|
19
|
+
throw new TypeError("Selected file is a compiled NIBArchive, not a Foundation plist archive; decode it with decode_interface_builder");
|
|
15
20
|
const binary = bytes.subarray(0, 8).toString("ascii") === "bplist00";
|
|
21
|
+
const xmlText = binary ? undefined : decodeXmlPlistText(bytes);
|
|
16
22
|
const parsed = binary
|
|
17
23
|
? { value: parseBinary(bytes), omittedPrototypeKeys: 0 }
|
|
18
|
-
: parseXmlPropertyList(
|
|
24
|
+
: parseXmlPropertyList(xmlText ?? "");
|
|
19
25
|
const graph = projectKeyedArchive(normalizePlist(parsed.value), selection);
|
|
26
|
+
const numbers = createPlistNumberProjection(binary
|
|
27
|
+
? binaryPlistNumberLiterals(bytes)
|
|
28
|
+
: xmlPlistNumberLiterals(xmlText ?? ""));
|
|
29
|
+
// Classify the serialized graph before projecting values to typed decimals.
|
|
30
|
+
// Numeric IDs, node kinds, references and pagination retain their original meaning.
|
|
31
|
+
const roots = Object.fromEntries(Object.entries(graph.roots).map(([name, value]) => [
|
|
32
|
+
name,
|
|
33
|
+
numbers.project(value),
|
|
34
|
+
]));
|
|
35
|
+
const objects = graph.objects.map((object) => ({
|
|
36
|
+
...object,
|
|
37
|
+
value: numbers.project(object.value),
|
|
38
|
+
}));
|
|
20
39
|
return {
|
|
21
40
|
archive_format: binary ? "binary-plist" : "xml-plist",
|
|
22
41
|
...graph,
|
|
42
|
+
roots,
|
|
43
|
+
objects,
|
|
23
44
|
limitations: [
|
|
24
45
|
...graph.limitations,
|
|
46
|
+
...numbers.limitations(),
|
|
25
47
|
...(parsed.omittedPrototypeKeys === 0
|
|
26
48
|
? []
|
|
27
49
|
: [omittedPrototypeKeysLimitation(parsed.omittedPrototypeKeys)]),
|
|
28
50
|
],
|
|
29
51
|
};
|
|
30
52
|
};
|
|
31
|
-
const decodeXmlPlistText = (bytes) => {
|
|
32
|
-
const encoding = bytes[0] === 0xff && bytes[1] === 0xfe
|
|
33
|
-
? "utf-16le"
|
|
34
|
-
: bytes[0] === 0xfe && bytes[1] === 0xff
|
|
35
|
-
? "utf-16be"
|
|
36
|
-
: "utf-8";
|
|
37
|
-
const text = new TextDecoder(encoding, { fatal: true }).decode(bytes);
|
|
38
|
-
const declared = /^<\?xml\s[^?]*\bencoding\s*=\s*["']([^"']+)["']/u
|
|
39
|
-
.exec(text)?.[1]
|
|
40
|
-
?.toLowerCase();
|
|
41
|
-
if (declared !== undefined &&
|
|
42
|
-
((encoding !== "utf-8" && declared !== encoding && declared !== "utf-16") ||
|
|
43
|
-
(encoding === "utf-8" && declared.startsWith("utf-16"))))
|
|
44
|
-
throw new TypeError("XML encoding declaration disagrees with its bytes");
|
|
45
|
-
return text;
|
|
46
|
-
};
|
|
47
53
|
const normalizePlist = (value) => {
|
|
48
54
|
const projected = projectPlistValue(value);
|
|
49
55
|
if (projected.unknownRealCount > 0)
|
|
@@ -57,14 +63,16 @@ export const inspectBundleKeyedArchive = async (input) => {
|
|
|
57
63
|
selected.path
|
|
58
64
|
.split("/")
|
|
59
65
|
.some((part) => part === ".." || part === "." || part.length === 0))
|
|
60
|
-
throw
|
|
66
|
+
throw archivePathError("invalid_format", selected.path === "."
|
|
67
|
+
? "Select a keyed archive path relative to the active app bundle."
|
|
68
|
+
: `Archive path must be a canonical relative bundle path without empty, '.', or '..' segments: ${selected.path}`);
|
|
61
69
|
const reader = new DirectoryArtifactReader(input.bundlePath);
|
|
62
70
|
try {
|
|
63
71
|
for await (const entry of reader.entries(input.signal)) {
|
|
64
72
|
if (entry.path !== selected.path)
|
|
65
73
|
continue;
|
|
66
74
|
if (entry.kind !== "file")
|
|
67
|
-
throw
|
|
75
|
+
throw archivePathError("invalid_value", `Archive path selects a ${entry.kind}, not a regular file: ${selected.path}`);
|
|
68
76
|
if ((entry.declaredSize ?? 0) > MAX_BYTES)
|
|
69
77
|
throw new ArtifactReaderFailure("limit", "Keyed archive exceeds 64 MiB");
|
|
70
78
|
const stream = await reader.open(entry, input.signal);
|
|
@@ -93,12 +101,18 @@ export const inspectBundleKeyedArchive = async (input) => {
|
|
|
93
101
|
});
|
|
94
102
|
}
|
|
95
103
|
catch (cause) {
|
|
96
|
-
|
|
104
|
+
if (cause instanceof AnalysisInputError)
|
|
105
|
+
throw cause;
|
|
106
|
+
throw new ArtifactReaderFailure(cause instanceof RangeError ? "limit" : "format", `Cannot decode selected Foundation keyed archive: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
|
-
throw
|
|
109
|
+
throw archivePathError("invalid_value", `No regular file exists at ${selected.path} in the active app bundle.`);
|
|
100
110
|
}
|
|
101
111
|
finally {
|
|
102
112
|
await reader.close();
|
|
103
113
|
}
|
|
104
114
|
};
|
|
115
|
+
/** A caller-selected archive path, not the artifact, failed its constraint. */
|
|
116
|
+
const archivePathError = (reason, message) => new AnalysisInputError("inspect_keyed_archive", undefined, [
|
|
117
|
+
{ path: ["path"], reason, message },
|
|
118
|
+
]);
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { readFatSliceDeclarations, validateFatSlice, } from "../../domain/apple/machoContainer.js";
|
|
2
|
+
/** Load commands larger than this are refused instead of buffered. */
|
|
3
|
+
export const MAX_LOAD_COMMAND_BYTES = 16 * 1024 * 1024;
|
|
4
|
+
/** Target opening accepts up to 128 FAT records; Java class files share the FAT magic. */
|
|
5
|
+
const MAX_FAT_ARCHITECTURES = 128;
|
|
6
|
+
/** CPU types of real Mach-O slices (x86, x86_64, arm, arm64, arm64_32, ppc, ppc64). */
|
|
7
|
+
const KNOWN_CPU_TYPES = [
|
|
8
|
+
7, 0x01000007, 12, 0x0100000c, 0x0200000c, 18, 0x01000012,
|
|
9
|
+
];
|
|
10
|
+
const MH_MAGIC = 0xfeedface;
|
|
11
|
+
const MH_MAGIC_64 = 0xfeedfacf;
|
|
12
|
+
const MH_CIGAM = 0xcefaedfe;
|
|
13
|
+
const MH_CIGAM_64 = 0xcffaedfe;
|
|
14
|
+
const FAT_MAGIC = 0xcafebabe;
|
|
15
|
+
const FAT_MAGIC_64 = 0xcafebabf;
|
|
16
|
+
const FAT_CIGAM = 0xbebafeca;
|
|
17
|
+
const FAT_CIGAM_64 = 0xbfbafeca;
|
|
18
|
+
const LC_ID_DYLIB = 0xd;
|
|
19
|
+
const LC_LOAD_DYLIB = 0xc;
|
|
20
|
+
const LC_LOAD_WEAK_DYLIB = 0x80000018;
|
|
21
|
+
const LC_REEXPORT_DYLIB = 0x8000001f;
|
|
22
|
+
const LC_LAZY_LOAD_DYLIB = 0x20;
|
|
23
|
+
const LC_LOAD_UPWARD_DYLIB = 0x80000023;
|
|
24
|
+
const LC_RPATH = 0x8000001c;
|
|
25
|
+
const LC_DYLD_ENVIRONMENT = 0x27;
|
|
26
|
+
const LC_CODE_SIGNATURE = 0x1d;
|
|
27
|
+
const LC_VERSION_MIN_MACOSX = 0x24;
|
|
28
|
+
const LC_VERSION_MIN_IPHONEOS = 0x25;
|
|
29
|
+
const LC_VERSION_MIN_TVOS = 0x2f;
|
|
30
|
+
const LC_VERSION_MIN_WATCHOS = 0x30;
|
|
31
|
+
const LC_BUILD_VERSION = 0x32;
|
|
32
|
+
/** `mach-o/loader.h` `dylib_use_command`: `nameoff == 28` and this marker. */
|
|
33
|
+
const DYLIB_USE_MARKER = 0x1a741800;
|
|
34
|
+
const DYLIB_USE_WEAK_LINK = 0x1;
|
|
35
|
+
const DYLIB_USE_REEXPORT = 0x2;
|
|
36
|
+
const DYLIB_USE_UPWARD = 0x4;
|
|
37
|
+
const DYLIB_USE_DELAYED_INIT = 0x8;
|
|
38
|
+
const DEPENDENCY_COMMANDS = new Map([
|
|
39
|
+
[LC_LOAD_DYLIB, "LC_LOAD_DYLIB"],
|
|
40
|
+
[LC_LOAD_WEAK_DYLIB, "LC_LOAD_WEAK_DYLIB"],
|
|
41
|
+
[LC_REEXPORT_DYLIB, "LC_REEXPORT_DYLIB"],
|
|
42
|
+
[LC_LAZY_LOAD_DYLIB, "LC_LAZY_LOAD_DYLIB"],
|
|
43
|
+
[LC_LOAD_UPWARD_DYLIB, "LC_LOAD_UPWARD_DYLIB"],
|
|
44
|
+
]);
|
|
45
|
+
/** Malformed or unsupported structure found while decoding one image. */
|
|
46
|
+
class MachoFormatIssue extends Error {
|
|
47
|
+
status;
|
|
48
|
+
constructor(status, message) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.status = status;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Report whether the first bytes carry a Mach-O or FAT magic number. */
|
|
54
|
+
export const hasMachoMagic = (head) => {
|
|
55
|
+
if (head.byteLength < 4)
|
|
56
|
+
return false;
|
|
57
|
+
const magic = new DataView(head.buffer, head.byteOffset, head.byteLength).getUint32(0, false);
|
|
58
|
+
return [
|
|
59
|
+
MH_MAGIC,
|
|
60
|
+
MH_MAGIC_64,
|
|
61
|
+
MH_CIGAM,
|
|
62
|
+
MH_CIGAM_64,
|
|
63
|
+
FAT_MAGIC,
|
|
64
|
+
FAT_MAGIC_64,
|
|
65
|
+
FAT_CIGAM,
|
|
66
|
+
FAT_CIGAM_64,
|
|
67
|
+
].includes(magic);
|
|
68
|
+
};
|
|
69
|
+
/** Decode every slice's dylib-loading load commands without reading code. */
|
|
70
|
+
export const readMachoImage = async (readAt, size) => {
|
|
71
|
+
try {
|
|
72
|
+
const head = await readAt(0, Math.min(size, 8));
|
|
73
|
+
if (head.byteLength < 8)
|
|
74
|
+
// A recognized magic with no room for a header is a truncated image.
|
|
75
|
+
return hasMachoMagic(head)
|
|
76
|
+
? { status: "malformed", reason: "Mach-O header is truncated" }
|
|
77
|
+
: { status: "not-mach-o" };
|
|
78
|
+
const view = viewOf(head);
|
|
79
|
+
const magic = view.getUint32(0, false);
|
|
80
|
+
if ([FAT_MAGIC, FAT_MAGIC_64, FAT_CIGAM, FAT_CIGAM_64].includes(magic)) {
|
|
81
|
+
// FAT headers are big-endian; the swapped magics store them little-endian.
|
|
82
|
+
const littleEndian = magic === FAT_CIGAM || magic === FAT_CIGAM_64;
|
|
83
|
+
const count = view.getUint32(4, littleEndian);
|
|
84
|
+
const wide = magic === FAT_MAGIC_64 || magic === FAT_CIGAM_64;
|
|
85
|
+
// A Java class file's nonzero major version reads as the count, so an
|
|
86
|
+
// empty architecture table can only be a malformed universal header.
|
|
87
|
+
if (count === 0)
|
|
88
|
+
return {
|
|
89
|
+
status: "malformed",
|
|
90
|
+
reason: "FAT header declares no architectures",
|
|
91
|
+
};
|
|
92
|
+
if (count > MAX_FAT_ARCHITECTURES || 8 + count * (wide ? 32 : 20) > size)
|
|
93
|
+
return { status: "not-mach-o" };
|
|
94
|
+
const tableBytes = await readExact(readAt, 8, count * (wide ? 32 : 20), "FAT table");
|
|
95
|
+
const declarations = readFatSliceDeclarations(tableBytes, {
|
|
96
|
+
count,
|
|
97
|
+
wide,
|
|
98
|
+
littleEndian,
|
|
99
|
+
});
|
|
100
|
+
if (declarations.status !== "parsed")
|
|
101
|
+
throw new MachoFormatIssue("malformed", declarations.reason);
|
|
102
|
+
// A Java class file's version fields read as a FAT count; its "table" names no CPU.
|
|
103
|
+
if (!declarations.slices.some(({ cpuType }) => KNOWN_CPU_TYPES.includes(cpuType))) {
|
|
104
|
+
let hasMachOSliceHeader = false;
|
|
105
|
+
for (const declaration of declarations.slices) {
|
|
106
|
+
if (validateFatSlice(declaration, size, 8 + count * (wide ? 32 : 20)) !== null)
|
|
107
|
+
continue;
|
|
108
|
+
const bytes = await readAt(declaration.offset, 4);
|
|
109
|
+
if (bytes.byteLength < 4)
|
|
110
|
+
continue;
|
|
111
|
+
const sliceMagic = viewOf(bytes).getUint32(0, false);
|
|
112
|
+
if ([MH_MAGIC, MH_MAGIC_64, MH_CIGAM, MH_CIGAM_64].includes(sliceMagic)) {
|
|
113
|
+
hasMachOSliceHeader = true;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!hasMachOSliceHeader)
|
|
118
|
+
return { status: "not-mach-o" };
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
status: "parsed",
|
|
122
|
+
slices: await readFatSlices(readAt, size, 8 + count * (wide ? 32 : 20), declarations.slices),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (magic === MH_CIGAM || magic === MH_CIGAM_64)
|
|
126
|
+
return { status: "parsed", slices: [await readSlice(readAt, 0, size)] };
|
|
127
|
+
if (magic === MH_MAGIC || magic === MH_MAGIC_64)
|
|
128
|
+
throw new MachoFormatIssue("unsupported", "big-endian Mach-O images are not supported");
|
|
129
|
+
return { status: "not-mach-o" };
|
|
130
|
+
}
|
|
131
|
+
catch (cause) {
|
|
132
|
+
if (cause instanceof MachoFormatIssue)
|
|
133
|
+
return { status: cause.status, reason: cause.message };
|
|
134
|
+
throw cause;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const readFatSlices = async (readAt, size, tableEnd, declarations) => {
|
|
138
|
+
const slices = [];
|
|
139
|
+
for (const declaration of declarations) {
|
|
140
|
+
const { index, offset, size: length, cpuType, cpuSubtype } = declaration;
|
|
141
|
+
const rangeIssue = validateFatSlice(declaration, size, tableEnd);
|
|
142
|
+
if (rangeIssue !== null)
|
|
143
|
+
throw new MachoFormatIssue(rangeIssue.includes("unsupported") ? "unsupported" : "malformed", rangeIssue);
|
|
144
|
+
const slice = await readSlice(readAt, offset, length, {
|
|
145
|
+
fatCpuType: cpuType,
|
|
146
|
+
fatCpuSubtype: cpuSubtype,
|
|
147
|
+
alignmentExponent: declaration.alignmentExponent,
|
|
148
|
+
});
|
|
149
|
+
const identityIssue = validateFatSlice(declaration, size, tableEnd, {
|
|
150
|
+
cpuType: slice.cpu_type,
|
|
151
|
+
cpuSubtype: slice.cpu_subtype,
|
|
152
|
+
});
|
|
153
|
+
if (identityIssue !== null)
|
|
154
|
+
throw new MachoFormatIssue("malformed", identityIssue);
|
|
155
|
+
slices.push(slice);
|
|
156
|
+
}
|
|
157
|
+
return slices;
|
|
158
|
+
};
|
|
159
|
+
const readSlice = async (readAt, base, length, fat = null) => {
|
|
160
|
+
const header = viewOf(await readExact(readAt, base, 28, "Mach-O header"));
|
|
161
|
+
const magic = header.getUint32(0, true);
|
|
162
|
+
if (magic === MH_MAGIC_64 || magic === MH_MAGIC) {
|
|
163
|
+
const wide = magic === MH_MAGIC_64;
|
|
164
|
+
const headerSize = wide ? 32 : 28;
|
|
165
|
+
const commandCount = header.getUint32(16, true);
|
|
166
|
+
const commandBytes = header.getUint32(20, true);
|
|
167
|
+
if (commandBytes > MAX_LOAD_COMMAND_BYTES)
|
|
168
|
+
throw new MachoFormatIssue("unsupported", `load commands occupy ${commandBytes} bytes, above the ${MAX_LOAD_COMMAND_BYTES}-byte limit`);
|
|
169
|
+
if (headerSize + commandBytes > length)
|
|
170
|
+
throw new MachoFormatIssue("malformed", "load commands extend beyond the Mach-O slice");
|
|
171
|
+
const commands = await readExact(readAt, base + headerSize, commandBytes, "load commands");
|
|
172
|
+
const slice = decodeCommands(commands, {
|
|
173
|
+
wide,
|
|
174
|
+
cpuType: header.getUint32(4, true),
|
|
175
|
+
cpuSubtype: header.getUint32(8, true),
|
|
176
|
+
fileType: header.getUint32(12, true),
|
|
177
|
+
commandCount,
|
|
178
|
+
sliceOffset: base,
|
|
179
|
+
sliceSize: length,
|
|
180
|
+
fatCpuType: fat?.fatCpuType ?? null,
|
|
181
|
+
fatCpuSubtype: fat?.fatCpuSubtype ?? null,
|
|
182
|
+
fatAlignmentExponent: fat?.alignmentExponent ?? null,
|
|
183
|
+
});
|
|
184
|
+
const distinctPlatforms = [...new Set(slice.platforms)];
|
|
185
|
+
slice.platform =
|
|
186
|
+
distinctPlatforms.length === 1 ? (distinctPlatforms[0] ?? null) : null;
|
|
187
|
+
return slice;
|
|
188
|
+
}
|
|
189
|
+
throw new MachoFormatIssue(magic === swap(MH_MAGIC) || magic === swap(MH_MAGIC_64)
|
|
190
|
+
? "unsupported"
|
|
191
|
+
: "malformed", magic === swap(MH_MAGIC) || magic === swap(MH_MAGIC_64)
|
|
192
|
+
? "big-endian Mach-O slices are not supported"
|
|
193
|
+
: `slice at offset ${base} has no Mach-O header`);
|
|
194
|
+
};
|
|
195
|
+
const decodeCommands = (bytes, header) => {
|
|
196
|
+
// loader.h: load commands are 8-byte aligned in 64-bit images, 4 in 32-bit.
|
|
197
|
+
const alignment = header.wide ? 8 : 4;
|
|
198
|
+
const view = viewOf(bytes);
|
|
199
|
+
const slice = {
|
|
200
|
+
architecture: architectureName(header.cpuType, header.cpuSubtype),
|
|
201
|
+
slice_offset: header.sliceOffset,
|
|
202
|
+
slice_size: header.sliceSize,
|
|
203
|
+
cpu_type: header.cpuType,
|
|
204
|
+
cpu_subtype: header.cpuSubtype,
|
|
205
|
+
fat_cpu_type: header.fatCpuType,
|
|
206
|
+
fat_cpu_subtype: header.fatCpuSubtype,
|
|
207
|
+
fat_alignment_exponent: header.fatAlignmentExponent,
|
|
208
|
+
file_type: fileTypeName(header.fileType),
|
|
209
|
+
file_type_code: header.fileType,
|
|
210
|
+
platform: null,
|
|
211
|
+
platforms: [],
|
|
212
|
+
install_name: null,
|
|
213
|
+
dependencies: [],
|
|
214
|
+
rpaths: [],
|
|
215
|
+
dyld_environment: [],
|
|
216
|
+
code_signature_present: false,
|
|
217
|
+
};
|
|
218
|
+
let offset = 0;
|
|
219
|
+
for (let index = 0; index < header.commandCount; index++) {
|
|
220
|
+
if (offset + 8 > bytes.byteLength)
|
|
221
|
+
throw new MachoFormatIssue("malformed", `load command ${index} starts beyond sizeofcmds`);
|
|
222
|
+
const command = view.getUint32(offset, true);
|
|
223
|
+
const size = view.getUint32(offset + 4, true);
|
|
224
|
+
if (size < 8 || size % alignment !== 0 || offset + size > bytes.byteLength)
|
|
225
|
+
throw new MachoFormatIssue("malformed", `load command ${index} at offset ${offset} has invalid cmdsize ${size}`);
|
|
226
|
+
const body = bytes.subarray(offset, offset + size);
|
|
227
|
+
decodeCommand(command, body, index, slice);
|
|
228
|
+
offset += size;
|
|
229
|
+
}
|
|
230
|
+
if (offset !== bytes.byteLength)
|
|
231
|
+
throw new MachoFormatIssue("malformed", `load commands declare ${header.commandCount} commands but sizeofcmds holds ${bytes.byteLength} bytes`);
|
|
232
|
+
return slice;
|
|
233
|
+
};
|
|
234
|
+
const decodeCommand = (command, body, index, slice) => {
|
|
235
|
+
if (command === LC_ID_DYLIB) {
|
|
236
|
+
slice.install_name = commandString(body, 24, index);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (command === LC_RPATH) {
|
|
240
|
+
slice.rpaths.push(commandString(body, 12, index));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (command === LC_DYLD_ENVIRONMENT) {
|
|
244
|
+
slice.dyld_environment.push(commandString(body, 12, index));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (command === LC_CODE_SIGNATURE) {
|
|
248
|
+
// linkedit_data_command: cmd, cmdsize, dataoff, datasize.
|
|
249
|
+
if (body.byteLength < 16)
|
|
250
|
+
throw new MachoFormatIssue("malformed", `load command ${index} is too short for LC_CODE_SIGNATURE`);
|
|
251
|
+
slice.code_signature_present = true;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (command === LC_BUILD_VERSION) {
|
|
255
|
+
if (body.byteLength < 24)
|
|
256
|
+
throw new MachoFormatIssue("malformed", `load command ${index} is too short for LC_BUILD_VERSION`);
|
|
257
|
+
const platform = viewOf(body).getUint32(8, true);
|
|
258
|
+
slice.platforms.push(platform);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const legacyPlatform = new Map([
|
|
262
|
+
[LC_VERSION_MIN_MACOSX, 1],
|
|
263
|
+
[LC_VERSION_MIN_IPHONEOS, 2],
|
|
264
|
+
[LC_VERSION_MIN_TVOS, 3],
|
|
265
|
+
[LC_VERSION_MIN_WATCHOS, 4],
|
|
266
|
+
]).get(command);
|
|
267
|
+
if (legacyPlatform !== undefined) {
|
|
268
|
+
if (body.byteLength < 16)
|
|
269
|
+
throw new MachoFormatIssue("malformed", `load command ${index} is too short for a version-min command`);
|
|
270
|
+
slice.platforms.push(legacyPlatform);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const name = DEPENDENCY_COMMANDS.get(command);
|
|
274
|
+
if (name !== undefined)
|
|
275
|
+
slice.dependencies.push(decodeDependency(name, body, index));
|
|
276
|
+
};
|
|
277
|
+
const decodeDependency = (command, body, index) => {
|
|
278
|
+
if (body.byteLength < 24)
|
|
279
|
+
throw new MachoFormatIssue("malformed", `load command ${index} is too short for a dylib command`);
|
|
280
|
+
const view = viewOf(body);
|
|
281
|
+
const nameOffset = view.getUint32(8, true);
|
|
282
|
+
const usesFlags = nameOffset === 28 &&
|
|
283
|
+
body.byteLength >= 28 &&
|
|
284
|
+
view.getUint32(12, true) === DYLIB_USE_MARKER;
|
|
285
|
+
const flags = usesFlags ? view.getUint32(24, true) : 0;
|
|
286
|
+
return {
|
|
287
|
+
command,
|
|
288
|
+
encoding: usesFlags ? "dylib_use_command" : "dylib_command",
|
|
289
|
+
install_name: commandString(body, usesFlags ? 28 : 24, index),
|
|
290
|
+
weak: command === "LC_LOAD_WEAK_DYLIB" || (flags & DYLIB_USE_WEAK_LINK) !== 0,
|
|
291
|
+
upward: command === "LC_LOAD_UPWARD_DYLIB" || (flags & DYLIB_USE_UPWARD) !== 0,
|
|
292
|
+
reexport: command === "LC_REEXPORT_DYLIB" || (flags & DYLIB_USE_REEXPORT) !== 0,
|
|
293
|
+
delayed_init: (flags & DYLIB_USE_DELAYED_INIT) !== 0,
|
|
294
|
+
current_version: version(view.getUint32(16, true)),
|
|
295
|
+
compatibility_version: version(view.getUint32(20, true)),
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
/** NUL-terminated UTF-8 string at a `lc_str` offset inside one command. */
|
|
299
|
+
const commandString = (body, minimumOffset, index) => {
|
|
300
|
+
if (body.byteLength < 12)
|
|
301
|
+
throw new MachoFormatIssue("malformed", `load command ${index} is too short to hold a string offset`);
|
|
302
|
+
const offset = viewOf(body).getUint32(8, true);
|
|
303
|
+
if (offset < minimumOffset || offset >= body.byteLength)
|
|
304
|
+
throw new MachoFormatIssue("malformed", `load command ${index} has string offset ${offset} outside the command`);
|
|
305
|
+
const end = body.indexOf(0, offset);
|
|
306
|
+
if (end < 0)
|
|
307
|
+
throw new MachoFormatIssue("malformed", `load command ${index} has an unterminated string`);
|
|
308
|
+
try {
|
|
309
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(body.subarray(offset, end));
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
throw new MachoFormatIssue("malformed", `load command ${index} has a string that is not UTF-8`);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
const readExact = async (readAt, offset, length, label) => {
|
|
316
|
+
const bytes = await readAt(offset, length);
|
|
317
|
+
if (bytes.byteLength < length)
|
|
318
|
+
throw new MachoFormatIssue("malformed", `${label} is truncated`);
|
|
319
|
+
return bytes;
|
|
320
|
+
};
|
|
321
|
+
const CPU_ARCH_ABI64 = 0x01000000;
|
|
322
|
+
const CPU_ARCH_ABI64_32 = 0x02000000;
|
|
323
|
+
const CPU_TYPE_X86 = 7;
|
|
324
|
+
const CPU_TYPE_ARM = 12;
|
|
325
|
+
const CPU_SUBTYPE_MASK = 0xff000000;
|
|
326
|
+
const CPU_SUBTYPE_ARM64E = 2;
|
|
327
|
+
const CPU_SUBTYPE_X86_64_H = 8;
|
|
328
|
+
const architectureName = (cpuType, cpuSubtype) => {
|
|
329
|
+
const subtype = cpuSubtype & ~CPU_SUBTYPE_MASK;
|
|
330
|
+
if (cpuType === (CPU_TYPE_ARM | CPU_ARCH_ABI64))
|
|
331
|
+
return subtype === CPU_SUBTYPE_ARM64E ? "arm64e" : "arm64";
|
|
332
|
+
if (cpuType === (CPU_TYPE_ARM | CPU_ARCH_ABI64_32))
|
|
333
|
+
return "arm64_32";
|
|
334
|
+
if (cpuType === (CPU_TYPE_X86 | CPU_ARCH_ABI64))
|
|
335
|
+
return subtype === CPU_SUBTYPE_X86_64_H ? "x86_64h" : "x86_64";
|
|
336
|
+
if (cpuType === CPU_TYPE_X86)
|
|
337
|
+
return "i386";
|
|
338
|
+
if (cpuType === CPU_TYPE_ARM)
|
|
339
|
+
return "arm";
|
|
340
|
+
return `cpu-${cpuType.toString(16)}`;
|
|
341
|
+
};
|
|
342
|
+
const fileTypeName = (fileType) => fileType === 2
|
|
343
|
+
? "execute"
|
|
344
|
+
: fileType === 6
|
|
345
|
+
? "dylib"
|
|
346
|
+
: fileType === 8
|
|
347
|
+
? "bundle"
|
|
348
|
+
: "other";
|
|
349
|
+
const version = (value) => `${value >>> 16}.${(value >>> 8) & 0xff}.${value & 0xff}`;
|
|
350
|
+
const swap = (value) => (((value & 0xff) << 24) |
|
|
351
|
+
((value & 0xff00) << 8) |
|
|
352
|
+
((value >>> 8) & 0xff00) |
|
|
353
|
+
(value >>> 24)) >>>
|
|
354
|
+
0;
|
|
355
|
+
const viewOf = (bytes) => new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { TextDecoder } from "node:util";
|
|
2
|
+
import { InterfaceBuilderDecodeBudget, InterfaceBuilderDecodeBudgetExceeded, } from "./InterfaceBuilderDecodeBudget.js";
|
|
2
3
|
const MAGIC = Buffer.from("NIBArchive", "ascii");
|
|
3
|
-
const
|
|
4
|
+
const DEFAULT_DECODE_BUDGET_BYTES = 128 * 1024 * 1024;
|
|
4
5
|
/** Decode Apple's length-prefixed NIBArchive representation without loading UI classes. */
|
|
5
|
-
export const decodeNibArchive = (bytes) => {
|
|
6
|
+
export const decodeNibArchive = (bytes, budget = new InterfaceBuilderDecodeBudget(DEFAULT_DECODE_BUDGET_BYTES)) => {
|
|
6
7
|
if (bytes.length < 50 || !bytes.subarray(0, MAGIC.length).equals(MAGIC))
|
|
7
8
|
throw new TypeError("NIBArchive magic or header is invalid");
|
|
8
9
|
const formatVersion = readU32(bytes, 10);
|
|
@@ -23,11 +24,13 @@ export const decodeNibArchive = (bytes) => {
|
|
|
23
24
|
valuesOffset,
|
|
24
25
|
classesOffset,
|
|
25
26
|
]);
|
|
27
|
+
const projectedFieldCount = preflightProjectedFieldCount(bytes, objectsOffset, keysOffset, objectCount, valueCount, classCount, budget.remainingBytes);
|
|
28
|
+
budget.reserve(objectCount * 96 + projectedFieldCount * 96, "NIBArchive object and projected field counts exceed the aggregate Interface Builder decode budget");
|
|
26
29
|
const objectRecords = parseObjects(bytes, objectsOffset, keysOffset, objectCount, valueCount, classCount);
|
|
27
|
-
const keys = parseStrings(bytes, keysOffset, valuesOffset, keyCount, "key");
|
|
28
|
-
const classes = parseClasses(bytes, classesOffset, bytes.length, classCount);
|
|
30
|
+
const keys = parseStrings(bytes, keysOffset, valuesOffset, keyCount, "key", budget);
|
|
31
|
+
const classes = parseClasses(bytes, classesOffset, bytes.length, classCount, budget);
|
|
29
32
|
const neededValues = valueIndexes(objectRecords, valueCount);
|
|
30
|
-
const values = parseValues(bytes, valuesOffset, classesOffset, valueCount, keyCount, neededValues);
|
|
33
|
+
const values = parseValues(bytes, valuesOffset, classesOffset, valueCount, keyCount, neededValues, budget);
|
|
31
34
|
for (const { value } of values.values()) {
|
|
32
35
|
if (typeof value === "object" &&
|
|
33
36
|
value !== null &&
|
|
@@ -89,19 +92,40 @@ const parseObjects = (bytes, start, end, count, valueCount, classCount) => {
|
|
|
89
92
|
throw new TypeError("NIBArchive object table overlaps the next table");
|
|
90
93
|
return output;
|
|
91
94
|
};
|
|
92
|
-
const
|
|
95
|
+
const preflightProjectedFieldCount = (bytes, start, end, objectCount, valueCount, classCount, maxBytes) => {
|
|
96
|
+
if (objectCount * 96 > maxBytes)
|
|
97
|
+
throw new InterfaceBuilderDecodeBudgetExceeded("aggregate_decode_budget_exhausted", "NIBArchive object count exceeds the aggregate Interface Builder decode budget");
|
|
98
|
+
const cursor = { offset: start };
|
|
99
|
+
let projectedFields = 0;
|
|
100
|
+
for (let index = 0; index < objectCount; index += 1) {
|
|
101
|
+
const classIndex = readVarint(bytes, cursor, end);
|
|
102
|
+
const valueStart = readVarint(bytes, cursor, end);
|
|
103
|
+
const fieldCount = readVarint(bytes, cursor, end);
|
|
104
|
+
if (classIndex >= classCount ||
|
|
105
|
+
valueStart > valueCount ||
|
|
106
|
+
fieldCount > valueCount - valueStart)
|
|
107
|
+
throw new TypeError(`NIBArchive object ${index} references an invalid table range`);
|
|
108
|
+
projectedFields += fieldCount;
|
|
109
|
+
if (!Number.isSafeInteger(projectedFields) ||
|
|
110
|
+
projectedFields * 96 > maxBytes - objectCount * 96)
|
|
111
|
+
throw new InterfaceBuilderDecodeBudgetExceeded("aggregate_decode_budget_exhausted", "NIBArchive projected field ranges exceed the aggregate Interface Builder decode budget");
|
|
112
|
+
}
|
|
113
|
+
return projectedFields;
|
|
114
|
+
};
|
|
115
|
+
const parseStrings = (bytes, start, end, count, label, budget) => {
|
|
93
116
|
const cursor = { offset: start };
|
|
94
117
|
const output = [];
|
|
95
118
|
for (let index = 0; index < count; index += 1) {
|
|
96
119
|
const length = readVarint(bytes, cursor, end);
|
|
97
120
|
const next = checkedEnd(cursor.offset, length, end, `${label} ${index}`);
|
|
121
|
+
budget.reserve(32 + length * 2, `NIBArchive ${label} strings exceed the aggregate Interface Builder decode budget`);
|
|
98
122
|
const value = decodeUtf8(bytes, cursor.offset, next, `${label} ${index}`);
|
|
99
123
|
cursor.offset = next;
|
|
100
124
|
output.push(value);
|
|
101
125
|
}
|
|
102
126
|
return output;
|
|
103
127
|
};
|
|
104
|
-
const parseClasses = (bytes, start, end, count) => {
|
|
128
|
+
const parseClasses = (bytes, start, end, count, budget) => {
|
|
105
129
|
const cursor = { offset: start };
|
|
106
130
|
const output = [];
|
|
107
131
|
for (let index = 0; index < count; index += 1) {
|
|
@@ -112,13 +136,14 @@ const parseClasses = (bytes, start, end, count) => {
|
|
|
112
136
|
ensureRange(cursor.offset, extraCount * 4, end, `class ${index} extensions`);
|
|
113
137
|
cursor.offset += extraCount * 4;
|
|
114
138
|
const next = checkedEnd(cursor.offset, length, end, `class ${index}`);
|
|
139
|
+
budget.reserve(32 + length * 2, "NIBArchive class names exceed the aggregate Interface Builder decode budget");
|
|
115
140
|
const name = decodeUtf8(bytes, cursor.offset, next, `class ${index}`).replace(/\0+$/u, "");
|
|
116
141
|
cursor.offset = next;
|
|
117
142
|
output.push(name);
|
|
118
143
|
}
|
|
119
144
|
return output;
|
|
120
145
|
};
|
|
121
|
-
const parseValues = (bytes, start, end, count, keyCount, needed) => {
|
|
146
|
+
const parseValues = (bytes, start, end, count, keyCount, needed, budget) => {
|
|
122
147
|
const cursor = { offset: start };
|
|
123
148
|
const output = new Map();
|
|
124
149
|
for (let index = 0; index < count; index += 1) {
|
|
@@ -127,15 +152,19 @@ const parseValues = (bytes, start, end, count, keyCount, needed) => {
|
|
|
127
152
|
throw new TypeError(`NIBArchive value ${index} references an invalid key`);
|
|
128
153
|
ensureRange(cursor.offset, 1, end, `value ${index} type`);
|
|
129
154
|
const type = bytes[cursor.offset++] ?? -1;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
155
|
+
if (!needed.has(index)) {
|
|
156
|
+
skipValue(bytes, cursor, end, type, index);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
budget.reserve(64, "NIBArchive decoded values exceed the aggregate Interface Builder decode budget");
|
|
160
|
+
const value = readValue(bytes, cursor, end, type, index, budget);
|
|
161
|
+
output.set(index, { key_index: keyIndex, value });
|
|
133
162
|
}
|
|
134
163
|
if (cursor.offset > end)
|
|
135
164
|
throw new TypeError("NIBArchive value table overlaps the next table");
|
|
136
165
|
return output;
|
|
137
166
|
};
|
|
138
|
-
const readValue = (bytes, cursor, end, type, index) => {
|
|
167
|
+
const readValue = (bytes, cursor, end, type, index, budget) => {
|
|
139
168
|
switch (type) {
|
|
140
169
|
case 0:
|
|
141
170
|
ensureRange(cursor.offset, 1, end, `value ${index}`);
|
|
@@ -185,6 +214,7 @@ const readValue = (bytes, cursor, end, type, index) => {
|
|
|
185
214
|
case 8: {
|
|
186
215
|
const length = readVarint(bytes, cursor, end);
|
|
187
216
|
const next = checkedEnd(cursor.offset, length, end, `value ${index} data`);
|
|
217
|
+
budget.reserve(32 + 4 * Math.ceil(length / 3), `NIBArchive data value ${index} exceeds the aggregate Interface Builder decode budget`);
|
|
188
218
|
const encoded = bytes.subarray(cursor.offset, next).toString("base64");
|
|
189
219
|
cursor.offset = next;
|
|
190
220
|
return { $nib_data_base64: encoded };
|
|
@@ -202,6 +232,43 @@ const readValue = (bytes, cursor, end, type, index) => {
|
|
|
202
232
|
throw new TypeError(`NIBArchive value ${index} has unsupported type ${type}`);
|
|
203
233
|
}
|
|
204
234
|
};
|
|
235
|
+
const skipValue = (bytes, cursor, end, type, index) => {
|
|
236
|
+
switch (type) {
|
|
237
|
+
case 0:
|
|
238
|
+
ensureRange(cursor.offset, 1, end, `value ${index}`);
|
|
239
|
+
cursor.offset += 1;
|
|
240
|
+
return;
|
|
241
|
+
case 1:
|
|
242
|
+
ensureRange(cursor.offset, 2, end, `value ${index}`);
|
|
243
|
+
cursor.offset += 2;
|
|
244
|
+
return;
|
|
245
|
+
case 2:
|
|
246
|
+
case 10:
|
|
247
|
+
ensureRange(cursor.offset, 4, end, `value ${index}`);
|
|
248
|
+
cursor.offset += 4;
|
|
249
|
+
return;
|
|
250
|
+
case 3:
|
|
251
|
+
case 7:
|
|
252
|
+
ensureRange(cursor.offset, 8, end, `value ${index}`);
|
|
253
|
+
cursor.offset += 8;
|
|
254
|
+
return;
|
|
255
|
+
case 6:
|
|
256
|
+
ensureRange(cursor.offset, 4, end, `value ${index}`);
|
|
257
|
+
cursor.offset += 4;
|
|
258
|
+
return;
|
|
259
|
+
case 4:
|
|
260
|
+
case 5:
|
|
261
|
+
case 9:
|
|
262
|
+
return;
|
|
263
|
+
case 8: {
|
|
264
|
+
const length = readVarint(bytes, cursor, end);
|
|
265
|
+
cursor.offset = checkedEnd(cursor.offset, length, end, `value ${index} data`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
default:
|
|
269
|
+
throw new TypeError(`NIBArchive value ${index} has unsupported type ${type}`);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
205
272
|
const valueIndexes = (objects, total) => {
|
|
206
273
|
const indexes = new Set();
|
|
207
274
|
for (const object of objects)
|
|
@@ -213,8 +280,8 @@ const valueIndexes = (objects, total) => {
|
|
|
213
280
|
};
|
|
214
281
|
const readCount = (bytes, offset, label) => {
|
|
215
282
|
const count = readU32(bytes, offset);
|
|
216
|
-
if (count >
|
|
217
|
-
throw new TypeError(`NIBArchive ${label} count exceeds the
|
|
283
|
+
if (count > bytes.length)
|
|
284
|
+
throw new TypeError(`NIBArchive ${label} count exceeds the archive length`);
|
|
218
285
|
return count;
|
|
219
286
|
};
|
|
220
287
|
const readOffset = (bytes, offset, fileLength, label) => {
|