rea-agents 6.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 +23 -17
- 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 +0 -2
- package/bridge/native/rea_lldb_tracer.py +630 -94
- package/bridge/process/ProcessRunTokenReader.swift +39 -5
- package/bridge/pwntools/recorded_crash_impl.py +4 -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 +19 -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 +6 -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 +27 -37
- 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 +23 -11
- 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/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/WebNetworkCaptureService.js +4 -2
- package/dist/application/WebRuntimeService.js +5 -3
- package/dist/application/WebScriptExportService.js +2 -2
- package/dist/application/WebSourceLocationService.js +4 -2
- 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 +3 -6
- package/dist/application/binary/BinarySession.js +95 -68
- package/dist/application/binary/BinarySessionOpen.js +3 -3
- package/dist/application/binary/BinarySessionRecords.js +25 -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/BinaryLayoutService.js +4 -2
- package/dist/application/binaryDiagnostics/RecordedCrashService.js +4 -2
- package/dist/application/evm/EvmInterfaceService.js +4 -2
- 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 +24 -3
- package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +19 -2
- package/dist/application/javascript/JavaScriptApplicationService.js +22 -15
- package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +5 -5
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +5 -4
- package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +39 -7
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +6 -3
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +11 -3
- 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/JavaScriptSemanticGraphBuilder.js +63 -61
- package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +23 -1
- 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/process/ProcessCli.js +14 -6
- package/dist/application/process/ProcessEvidence.js +4 -3
- package/dist/application/workflowInputError.js +18 -0
- package/dist/artifacts/ArtifactProvider.js +12 -2
- package/dist/artifacts/MachOSliceArtifactReader.js +98 -14
- package/dist/artifacts/NativeDmgArtifactReader.js +13 -6
- package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +22 -3
- 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 +92 -31
- 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/artifacts/inventory/ArtifactGraphConstruction.js +13 -8
- package/dist/artifacts/inventory/scanCanonical.js +11 -14
- package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +6 -2
- package/dist/browser/CdpCaptureCompleteness.js +2 -0
- package/dist/browser/CdpCaptureEventHandlers.js +76 -11
- package/dist/browser/CdpCaptureEventTypes.js +5 -1
- package/dist/browser/CdpCaptureEvents.js +25 -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 +123 -124
- package/dist/browser/WebSourceMapFetcher.js +426 -72
- package/dist/browser/assets/LocalWebScriptArtifacts.js +2 -2
- package/dist/browser/history/CaptureFailures.js +37 -1
- package/dist/browser/history/CaptureRelease.js +5 -0
- package/dist/browser/history/HarCaptureAdapter.js +3 -3
- package/dist/browser/history/HarJson.js +3 -2
- package/dist/browser/history/HistoricalCaptureDecoder.js +4 -2
- package/dist/cli/analysisSelector.js +23 -0
- package/dist/cli/applicationCommands.js +2 -3
- package/dist/cli/coreBinaryCommands.js +70 -12
- package/dist/cli/electronCommands.js +7 -6
- package/dist/cli/javascriptApplicationAnalysis.js +7 -3
- package/dist/cli/jsonOutput.js +4 -99
- package/dist/cli/nativeCallCommands.js +2 -3
- package/dist/cli/webRuntimeCommands.js +8 -8
- package/dist/cli.js +2 -1
- package/dist/cliCommandNames.js +0 -1
- package/dist/cliJsonInput.js +9 -4
- 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/enhancedInputs.js +5 -3
- package/dist/contracts/enhancedToolContracts.js +3 -3
- package/dist/contracts/hopperMemoryContracts.js +2 -5
- package/dist/contracts/javascript/electronToolContracts.js +1 -1
- package/dist/contracts/managed/managedToolContracts.js +2 -4
- package/dist/contracts/native/nativeToolContracts.js +4 -2
- package/dist/contracts/officialToolContracts.js +14 -15
- package/dist/contracts/promptContracts.js +3 -3
- package/dist/contracts/sessionLifecycleInputs.js +3 -7
- package/dist/contracts/sessionStatusContract.js +2 -4
- package/dist/contracts/sessionToolContracts.js +4 -6
- package/dist/contracts/sessionToolSchemas.js +2 -5
- package/dist/contracts/toolContractExamples.js +0 -1
- package/dist/contracts/toolContractHelpers.js +4 -1
- package/dist/contracts/toolEffects.js +0 -1
- package/dist/contracts/toolOutputSchemaGroups.js +28 -27
- package/dist/contracts/toolOutputSchemaPrimitives.js +18 -0
- package/dist/contracts/toolSchemaMetadata.js +69 -10
- package/dist/contracts/webNetworkCaptureToolContracts.js +1 -1
- package/dist/domain/analysisErrorBase.js +8 -3
- package/dist/domain/analysisErrorCore.js +3 -1
- package/dist/domain/analysisErrorPresentation.js +72 -17
- package/dist/domain/analysisErrorProjection.js +106 -27
- 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/dylibResolution.js +120 -9
- package/dist/domain/apple/dylibResolutionFindings.js +9 -7
- 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/bridgeCandidateProjection.js +71 -0
- package/dist/domain/browserCompleteness.js +1 -0
- package/dist/domain/browserObservationError.js +24 -5
- package/dist/domain/browserScenarioValues.js +2 -6
- package/dist/domain/bundleComparison.js +3 -2
- package/dist/domain/canonicalJsonDigestSteps.js +133 -0
- package/dist/domain/canonicalOrdering.js +9 -5
- package/dist/domain/changedBehavior.js +6 -6
- package/dist/domain/comparisonSourceEvidence.js +40 -0
- package/dist/domain/configurationErrors.js +1 -1
- package/dist/domain/evidence.js +64 -9
- package/dist/domain/evidenceBundle.js +42 -1
- package/dist/domain/evidenceErrors.js +12 -1
- package/dist/domain/explicitSensitiveFailure.js +17 -1
- package/dist/domain/firmware/firmwareAnalysis.js +6 -7
- 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 +30 -4
- package/dist/domain/javascript/electronStaticAnalysisValues.js +6 -12
- 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/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 +13 -0
- 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 +90 -6
- package/dist/domain/localPath.js +6 -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/fileOffsetPartialObservation.js +1 -0
- package/dist/domain/native/nativeCallObservation.js +33 -11
- package/dist/domain/native/nativeCallPartialObservation.js +33 -0
- package/dist/domain/process/processCapture.js +8 -4
- package/dist/domain/process/processComparison.js +38 -4
- package/dist/domain/process/processScenario.js +11 -10
- 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/reconstructionVerification.js +6 -43
- package/dist/domain/reconstructionVerificationSchemas.js +2 -2
- package/dist/domain/staticRuntimeCorrelation.js +6 -43
- package/dist/domain/webBundleAnalysis.js +20 -8
- package/dist/domain/webBundleAnalyzerInspection.js +13 -8
- package/dist/domain/webBundleAnalyzerResult.js +41 -2
- package/dist/domain/webRuntime.js +19 -5
- package/dist/domain/webSourceLocation.js +1 -0
- package/dist/firmware/FirmwareFiles.js +1 -1
- package/dist/generatedPackageMetadata.js +1 -1
- 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 +33 -16
- package/dist/ghidra/GhidraProviderCapabilities.js +14 -2
- 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 +31 -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/inspector/JavaScriptRuntimeScope.js +9 -1
- package/dist/inspector/V8InspectorCaptureProjection.js +8 -3
- package/dist/inspector/V8InspectorProvider.js +47 -11
- package/dist/javascript/sourceMaps/DecodedSourceMap.js +47 -0
- package/dist/javascript/sourceMaps/SourceMapCommand.js +16 -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 +81 -84
- package/dist/native/LldbCallTracer.js +365 -102
- package/dist/native/LldbRetainedObservations.js +178 -0
- package/dist/native/NativeCallObservation.js +62 -3
- package/dist/native/NativeMacOSProvider.js +98 -17
- package/dist/native/SignatureTargetBinding.js +155 -0
- package/dist/native/parsers/lipo.js +71 -1
- package/dist/native/pwndbg/PwndbgCoreContext.js +25 -5
- package/dist/native/pwntools/RecordedCrashStructureBindings.js +2 -0
- package/dist/process/DarwinProcessRunTokenReader.js +8 -9
- package/dist/process/ExecFileOutput.js +20 -0
- package/dist/process/OwnedCommand.js +7 -2
- package/dist/process/ProcessOwnership.js +203 -53
- package/dist/process/ProcessOwnershipObservation.js +49 -4
- package/dist/process/ProviderProcess.js +38 -7
- package/dist/process/capture/ProcessCaptureError.js +2 -2
- package/dist/process/capture/ProcessCaptureLifecycle.js +100 -34
- package/dist/server/EvidenceMcpServer.js +146 -0
- package/dist/server/createServer.js +15 -8
- 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 +2 -4
- 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 +2 -4
- package/dist/server/registerEvmTools.js +2 -4
- 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 +71 -45
- package/dist/server/registerRecordedCrashTools.js +2 -4
- package/dist/server/registerSessionRecordTools.js +5 -6
- package/dist/server/registerSessionStatusTool.js +17 -3
- package/dist/server/registerSessionTools.js +11 -3
- package/dist/server/registerWebModuleTool.js +2 -4
- package/dist/server/registerWebNetworkCaptureTool.js +2 -4
- 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 +0 -1
- package/dist/server/toolRegistrationOptions.js +2 -2
- package/dist/server/toolResult.js +62 -10
- package/dist/server/toolResultEncoding.js +33 -0
- package/install.sh +1 -1
- package/native/windows/build/manifest.json +1 -1
- package/package.json +8 -3
- package/scripts/electron-active-hook-boundaries.cjs +18 -20
- package/scripts/electron-active-hook.cjs +241 -37
- package/skills/reverse-engineer-anything/SKILL.md +11 -3
- package/skills/reverse-engineer-anything/references/javascript-applications.md +8 -1
- package/dist/ghidra/GhidraDefaults.js +0 -2
|
@@ -6,6 +6,7 @@ import { parseArtifactInventoryEvidence } from "../artifactInventoryEvidence.js"
|
|
|
6
6
|
import { evidenceSchema } from "../evidence.js";
|
|
7
7
|
import { digestSchema } from "../digests.js";
|
|
8
8
|
import { prefixedDigestSchema } from "../digests.js";
|
|
9
|
+
import { bridgeCandidateCoverageSchema, projectCartesianCandidates, } from "../bridgeCandidateProjection.js";
|
|
9
10
|
const evidenceIdSchema = prefixedDigestSchema("ev");
|
|
10
11
|
const pathSchema = z.string().min(1);
|
|
11
12
|
const APPLE_ROOT_FORMATS = ["ipa", "directory", "zip", "dmg"];
|
|
@@ -52,6 +53,7 @@ export const appleApplicationProjectionResultSchema = z.strictObject({
|
|
|
52
53
|
"unity-convention",
|
|
53
54
|
]),
|
|
54
55
|
})),
|
|
56
|
+
bridge_candidate_coverage: bridgeCandidateCoverageSchema,
|
|
55
57
|
coverage: z.strictObject({
|
|
56
58
|
status: z.enum(["complete-within-inventory", "partial"]),
|
|
57
59
|
inventory_complete: z.boolean(),
|
|
@@ -110,6 +112,7 @@ export const projectAppleApplication = (input) => {
|
|
|
110
112
|
sidecars: entries.some(({ path }) => isSidecar(path)),
|
|
111
113
|
symlinks: entries.some(({ kind }) => kind === "symlink"),
|
|
112
114
|
unattributed,
|
|
115
|
+
bridgeCandidateCoverage: bridgeProjection.coverage,
|
|
113
116
|
});
|
|
114
117
|
const withoutId = {
|
|
115
118
|
root_sha256: inventory.manifest.root_sha256,
|
|
@@ -127,8 +130,9 @@ export const projectAppleApplication = (input) => {
|
|
|
127
130
|
.sort(compare),
|
|
128
131
|
runtime_families: runtimeFamilies,
|
|
129
132
|
bridge_candidates: bridgeProjection.candidates,
|
|
133
|
+
bridge_candidate_coverage: bridgeProjection.coverage,
|
|
130
134
|
coverage: {
|
|
131
|
-
status: complete
|
|
135
|
+
status: complete && bridgeProjection.coverage.status === "complete"
|
|
132
136
|
? "complete-within-inventory"
|
|
133
137
|
: "partial",
|
|
134
138
|
inventory_complete: inventory.complete,
|
|
@@ -137,9 +141,11 @@ export const projectAppleApplication = (input) => {
|
|
|
137
141
|
};
|
|
138
142
|
return appleApplicationProjectionResultSchema.parse({
|
|
139
143
|
...withoutId,
|
|
140
|
-
projection_id:
|
|
144
|
+
projection_id: appleProjectionId(withoutId),
|
|
141
145
|
});
|
|
142
146
|
};
|
|
147
|
+
/** Identify the complete projection, including source Evidence provenance. */
|
|
148
|
+
export const appleProjectionId = (projection) => `aap_${digest(projection)}`;
|
|
143
149
|
/**
|
|
144
150
|
* IPA projection keeps every archive component, as before. Directory, ZIP,
|
|
145
151
|
* and DMG inventories can hold installers and other siblings, so only
|
|
@@ -204,21 +210,37 @@ const identifyRuntimeFamilies = (all) => {
|
|
|
204
210
|
families.add("unity");
|
|
205
211
|
return [...families].sort(compare);
|
|
206
212
|
};
|
|
207
|
-
/** Pair scripts and native code
|
|
213
|
+
/** Pair scripts and native code within matching application-root groups. */
|
|
208
214
|
const identifyBridgeCandidates = (scripts, native, roots) => {
|
|
209
215
|
const owner = (path) => roots.find((root) => isWithin(path, root)) ?? null;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
const scriptGroups = [];
|
|
217
|
+
for (const script of scripts) {
|
|
218
|
+
const root = owner(script.path);
|
|
219
|
+
const last = scriptGroups.at(-1);
|
|
220
|
+
if (last?.root === root)
|
|
221
|
+
last.left.push(script);
|
|
222
|
+
else
|
|
223
|
+
scriptGroups.push({ root, left: [script] });
|
|
224
|
+
}
|
|
225
|
+
const nativeByRoot = new Map();
|
|
226
|
+
for (const item of native) {
|
|
227
|
+
const root = owner(item.path);
|
|
228
|
+
const scoped = nativeByRoot.get(root) ?? [];
|
|
229
|
+
scoped.push(item);
|
|
230
|
+
nativeByRoot.set(root, scoped);
|
|
231
|
+
}
|
|
232
|
+
const groups = scriptGroups.map(({ root, left }) => ({
|
|
233
|
+
left,
|
|
234
|
+
right: nativeByRoot.get(root) ?? [],
|
|
235
|
+
}));
|
|
236
|
+
return projectCartesianCandidates({
|
|
237
|
+
groups,
|
|
238
|
+
createCandidate: (source_path, item) => ({
|
|
239
|
+
source_path,
|
|
240
|
+
native_path: item.path,
|
|
241
|
+
basis: bridgeBasis(item.path),
|
|
220
242
|
}),
|
|
221
|
-
};
|
|
243
|
+
});
|
|
222
244
|
};
|
|
223
245
|
const bridgeBasis = (path) => {
|
|
224
246
|
const lower = path.toLowerCase();
|
|
@@ -264,6 +286,11 @@ const projectionLimitations = (facts) => [
|
|
|
264
286
|
"AppleDouble sidecar entries (._* files and __MACOSX/) describe neighbouring files and are excluded from bundle roles.",
|
|
265
287
|
]
|
|
266
288
|
: []),
|
|
289
|
+
...(facts.bridgeCandidateCoverage.status === "partial"
|
|
290
|
+
? [
|
|
291
|
+
`Bridge candidate pairs exceeded the projection safety budget; ${facts.bridgeCandidateCoverage.omitted_candidates} hypotheses are omitted. Component arrays still include every component from the supplied inventory pages.`,
|
|
292
|
+
]
|
|
293
|
+
: []),
|
|
267
294
|
"Bundle roles follow path conventions. Read each info_plist_path with inspect_plist for CFBundleExecutable, identifiers, and declared services.",
|
|
268
295
|
"Bundle identifiers and signing claims require dedicated plist and CMS parsing; this projection reports only exact paths and hashes.",
|
|
269
296
|
"Bridge candidates are path-based hypotheses, not observed runtime calls.",
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Retain recognized image-selection settings separately from diagnostic observations. */
|
|
2
|
+
export const embeddedDyldOverrides = (environment, platforms) => {
|
|
3
|
+
const overrides = [];
|
|
4
|
+
for (const setting of environment) {
|
|
5
|
+
const delimiter = setting.indexOf("=");
|
|
6
|
+
if (delimiter <= 0)
|
|
7
|
+
continue;
|
|
8
|
+
const variable = setting.slice(0, delimiter);
|
|
9
|
+
const value = setting.slice(delimiter + 1);
|
|
10
|
+
const effect = overrideEffect(variable, value);
|
|
11
|
+
if (effect === undefined)
|
|
12
|
+
continue;
|
|
13
|
+
// ROOT_PATH is accepted only for simulator processes. Missing or unknown
|
|
14
|
+
// platform metadata cannot establish that dyld ignores it.
|
|
15
|
+
if (variable === "DYLD_ROOT_PATH" &&
|
|
16
|
+
platforms.length > 0 &&
|
|
17
|
+
platforms.every((platform) => platform >= 1 && platform <= 12) &&
|
|
18
|
+
!platforms.some((platform) => [7, 8, 9, 12].includes(platform)))
|
|
19
|
+
continue;
|
|
20
|
+
overrides.push({ variable, value, ...effect });
|
|
21
|
+
}
|
|
22
|
+
return overrides;
|
|
23
|
+
};
|
|
24
|
+
const overrideEffect = (variable, value) => {
|
|
25
|
+
switch (variable) {
|
|
26
|
+
// Empty directory components still produce /leaf candidates in dyld.
|
|
27
|
+
// In particular, an empty fallback value is not a no-op.
|
|
28
|
+
case "DYLD_LIBRARY_PATH":
|
|
29
|
+
return { phase: "override", scope: "library" };
|
|
30
|
+
case "DYLD_FRAMEWORK_PATH":
|
|
31
|
+
return { phase: "override", scope: "framework" };
|
|
32
|
+
case "DYLD_FALLBACK_LIBRARY_PATH":
|
|
33
|
+
return { phase: "fallback", scope: "library" };
|
|
34
|
+
case "DYLD_FALLBACK_FRAMEWORK_PATH":
|
|
35
|
+
return { phase: "fallback", scope: "framework" };
|
|
36
|
+
// Versioned paths map discovered images to their reported install names,
|
|
37
|
+
// so the directory's kind does not establish the replaced image's kind.
|
|
38
|
+
case "DYLD_VERSIONED_LIBRARY_PATH":
|
|
39
|
+
case "DYLD_VERSIONED_FRAMEWORK_PATH":
|
|
40
|
+
case "DYLD_IMAGE_SUFFIX":
|
|
41
|
+
// Versioned paths scan directories; suffixes create path variants.
|
|
42
|
+
// Empty directories cannot be scanned and empty suffixes repeat a path.
|
|
43
|
+
return /[^:]/u.test(value)
|
|
44
|
+
? { phase: "override", scope: "all" }
|
|
45
|
+
: undefined;
|
|
46
|
+
case "DYLD_INSERT_LIBRARIES":
|
|
47
|
+
return value === "" ? undefined : { phase: "override", scope: "all" };
|
|
48
|
+
case "DYLD_ROOT_PATH":
|
|
49
|
+
case "DYLD_OVERLAY_PATH":
|
|
50
|
+
return value === "" ? undefined : { phase: "override", scope: "prefix" };
|
|
51
|
+
default:
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const isFrameworkInstallName = (name, overrides) => {
|
|
56
|
+
const separator = name.lastIndexOf(".framework/");
|
|
57
|
+
if (separator < 0)
|
|
58
|
+
return false;
|
|
59
|
+
const framework = name.slice(0, separator).split("/").at(-1);
|
|
60
|
+
const leaf = name.slice(name.lastIndexOf("/") + 1);
|
|
61
|
+
return (leaf === framework ||
|
|
62
|
+
overrides.some(({ variable, value }) => variable === "DYLD_IMAGE_SUFFIX" && leaf === `${framework}${value}`));
|
|
63
|
+
};
|
|
64
|
+
/** Select only settings that can affect this dependency at its reached search phase. */
|
|
65
|
+
export const hasApplicableDyldOverrides = (overrides, installName, ordinarySearchFoundImage) => {
|
|
66
|
+
const kind = isFrameworkInstallName(installName, overrides)
|
|
67
|
+
? "framework"
|
|
68
|
+
: "library";
|
|
69
|
+
return overrides.some(({ phase, scope }) => (phase === "override" || !ordinarySearchFoundImage) &&
|
|
70
|
+
(scope === "all" ||
|
|
71
|
+
scope === kind ||
|
|
72
|
+
(scope === "prefix" && !installName.startsWith("@"))));
|
|
73
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { compatibleSlice, directoryOf, expandPrefix, joinPath, resolveTreePath, } from "./dyldPaths.js";
|
|
3
3
|
import { digestSchema } from "../digests.js";
|
|
4
|
+
import { hasApplicableDyldOverrides, embeddedDyldOverrides, } from "./dyldEnvironment.js";
|
|
4
5
|
import { DYLIB_RESOLUTION_LIMITATIONS, deriveFindings, } from "./dylibResolutionFindings.js";
|
|
5
6
|
/** Normalized path below the analyzed root: no `.`/`..` or empty segments. */
|
|
6
7
|
const ROOT_RELATIVE_PATH = /^(?!\/)(?!(?:.*\/)?\.{1,2}(?:\/|$))(?!.*\/\/)(?!.*\/$)[^\\\0]+$/u;
|
|
@@ -31,7 +32,20 @@ const dependencySchema = z.strictObject({
|
|
|
31
32
|
/** Observed dylib-loading facts of one Mach-O slice. */
|
|
32
33
|
export const machoSliceSchema = z.strictObject({
|
|
33
34
|
architecture: z.string().min(1),
|
|
35
|
+
/** Physical identity observed in the thin header and, for FAT, its table. */
|
|
36
|
+
slice_offset: z.number().int().nonnegative(),
|
|
37
|
+
slice_size: z.number().int().positive(),
|
|
38
|
+
cpu_type: z.number().int().nonnegative(),
|
|
39
|
+
cpu_subtype: z.number().int().nonnegative(),
|
|
40
|
+
fat_cpu_type: z.number().int().nonnegative().nullable(),
|
|
41
|
+
fat_cpu_subtype: z.number().int().nonnegative().nullable(),
|
|
42
|
+
fat_alignment_exponent: z.number().int().nonnegative().nullable(),
|
|
34
43
|
file_type: z.enum(["execute", "dylib", "bundle", "other"]),
|
|
44
|
+
file_type_code: z.number().int().nonnegative(),
|
|
45
|
+
/** Raw LC_BUILD_VERSION platform number; null means no such command was observed. */
|
|
46
|
+
platform: z.number().int().nonnegative().nullable(),
|
|
47
|
+
/** Every distinct build platform declared by LC_BUILD_VERSION/version-min commands. */
|
|
48
|
+
platforms: z.array(z.number().int().nonnegative()),
|
|
35
49
|
install_name: z.string().nullable(),
|
|
36
50
|
dependencies: z.array(dependencySchema),
|
|
37
51
|
rpaths: z.array(z.string()),
|
|
@@ -56,6 +70,8 @@ const candidateSchema = z.strictObject({
|
|
|
56
70
|
"malformed",
|
|
57
71
|
"unsupported",
|
|
58
72
|
"architecture-missing",
|
|
73
|
+
"not-loadable",
|
|
74
|
+
"platform-mismatch",
|
|
59
75
|
"outside-target",
|
|
60
76
|
"escapes-target",
|
|
61
77
|
"undetermined",
|
|
@@ -168,13 +184,65 @@ const evaluateCandidate = async (template, context) => {
|
|
|
168
184
|
if (lookup.kind !== "file")
|
|
169
185
|
return { ...base, outcome: "absent", resolved_path: null };
|
|
170
186
|
const facts = await imageFacts(context, lookup.path);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
187
|
+
let outcome;
|
|
188
|
+
if (facts.status !== "parsed")
|
|
189
|
+
outcome = facts.status;
|
|
190
|
+
else {
|
|
191
|
+
const slice = compatibleSlice(facts.slices, context.architecture);
|
|
192
|
+
if (slice === undefined)
|
|
193
|
+
outcome = "architecture-missing";
|
|
194
|
+
// Dependency commands require a dylib; generic dyld image loadability
|
|
195
|
+
// also admits main executables and bundles for different operations.
|
|
196
|
+
else if (slice.file_type_code !== 6)
|
|
197
|
+
outcome = "not-loadable";
|
|
198
|
+
else {
|
|
199
|
+
const platformResult = platformLoadability(context.platforms, slice.platforms, slice.file_type_code, context.architecture);
|
|
200
|
+
outcome =
|
|
201
|
+
platformResult === "compatible"
|
|
202
|
+
? "resolved"
|
|
203
|
+
: platformResult === "incompatible"
|
|
204
|
+
? "platform-mismatch"
|
|
205
|
+
: "undetermined";
|
|
206
|
+
}
|
|
207
|
+
}
|
|
176
208
|
return { ...base, outcome, resolved_path: lookup.path };
|
|
177
209
|
};
|
|
210
|
+
/** Apply dyld's documented cross-platform load cases and retain uncertain host cases. */
|
|
211
|
+
const platformLoadability = (processPlatforms, imagePlatforms, fileType, architecture) => {
|
|
212
|
+
if (processPlatforms.length === 0 || imagePlatforms.length === 0)
|
|
213
|
+
return "unknown";
|
|
214
|
+
const outcomes = processPlatforms.map((processPlatform) => imageLoadabilityForPlatform(processPlatform, imagePlatforms, fileType, architecture));
|
|
215
|
+
if (outcomes.every((outcome) => outcome === "compatible"))
|
|
216
|
+
return "compatible";
|
|
217
|
+
if (outcomes.every((outcome) => outcome === "incompatible"))
|
|
218
|
+
return "incompatible";
|
|
219
|
+
return "unknown";
|
|
220
|
+
};
|
|
221
|
+
const imageLoadabilityForPlatform = (processPlatform, imagePlatforms, fileType, architecture) => {
|
|
222
|
+
if (imagePlatforms.includes(processPlatform))
|
|
223
|
+
return "compatible";
|
|
224
|
+
// These are explicit dyld loadableIntoProcess cross-platform cases.
|
|
225
|
+
if (processPlatform === 6 && imagePlatforms.includes(1))
|
|
226
|
+
return "compatible"; // macOS dylibs in Mac Catalyst processes
|
|
227
|
+
if (processPlatform === 2 && imagePlatforms.includes(6))
|
|
228
|
+
return "compatible"; // Catalyst dylibs in iOS processes
|
|
229
|
+
if (processPlatform === 2 && imagePlatforms.includes(11))
|
|
230
|
+
return "compatible"; // visionOS dylibs in iOS processes
|
|
231
|
+
if (processPlatform === 7 && imagePlatforms.includes(12))
|
|
232
|
+
return "compatible"; // visionOS simulator dylibs in iOS simulator processes
|
|
233
|
+
if (processPlatform === 1 && fileType === 2 && imagePlatforms.includes(6))
|
|
234
|
+
return "compatible"; // Catalyst main executables run on macOS
|
|
235
|
+
// dyld's remaining exceptions depend on host architecture or a special path.
|
|
236
|
+
if ((processPlatform === 2 &&
|
|
237
|
+
imagePlatforms.includes(1) &&
|
|
238
|
+
architecture.startsWith("arm64")) ||
|
|
239
|
+
([7, 8, 9].includes(processPlatform) && imagePlatforms.includes(1)))
|
|
240
|
+
return "unknown";
|
|
241
|
+
if (isKnownPlatform(processPlatform) && imagePlatforms.every(isKnownPlatform))
|
|
242
|
+
return "incompatible";
|
|
243
|
+
return "unknown";
|
|
244
|
+
};
|
|
245
|
+
const isKnownPlatform = (platform) => platform >= 1 && platform <= 12;
|
|
178
246
|
const imageFacts = async (context, path) => {
|
|
179
247
|
const known = context.images.get(path);
|
|
180
248
|
if (known !== undefined)
|
|
@@ -222,7 +290,19 @@ const resolveDependency = async (dependency, loader, context) => {
|
|
|
222
290
|
if (candidate.outcome === "resolved")
|
|
223
291
|
break;
|
|
224
292
|
}
|
|
225
|
-
const
|
|
293
|
+
const modeled = resolution(candidates);
|
|
294
|
+
const searchUnknown = loaded === undefined &&
|
|
295
|
+
hasApplicableDyldOverrides(context.searchOverrides, dependency.install_name, modeled.image !== null);
|
|
296
|
+
if (searchUnknown)
|
|
297
|
+
context.environmentRoots.add(context.root);
|
|
298
|
+
// Fallbacks cannot preempt a modeled hit, and framework paths do not
|
|
299
|
+
// override ordinary dylibs. Reuse inherits uncertainty from the first load.
|
|
300
|
+
const searched = searchUnknown
|
|
301
|
+
? {
|
|
302
|
+
...modeled,
|
|
303
|
+
status: modeled.image === null ? "undetermined" : "conditional",
|
|
304
|
+
}
|
|
305
|
+
: modeled;
|
|
226
306
|
// Reusing an image that itself loads only conditionally is conditional too.
|
|
227
307
|
const resolved = loaded !== undefined &&
|
|
228
308
|
context.loaded.get(loaded)?.conditional === true &&
|
|
@@ -306,6 +386,7 @@ export const traceDylibLoading = async (view, request) => {
|
|
|
306
386
|
const roots = [];
|
|
307
387
|
const edges = [];
|
|
308
388
|
const withoutArchitecture = [];
|
|
389
|
+
const environmentRoots = new Set();
|
|
309
390
|
for (const root of request.roots) {
|
|
310
391
|
const facts = await view.image(root);
|
|
311
392
|
images.set(root, facts);
|
|
@@ -316,12 +397,23 @@ export const traceDylibLoading = async (view, request) => {
|
|
|
316
397
|
if (slices.length === 0)
|
|
317
398
|
withoutArchitecture.push(root);
|
|
318
399
|
for (const slice of slices) {
|
|
400
|
+
const executable = slice.file_type === "execute" ? root : null;
|
|
401
|
+
// Only the main executable supplies the process's embedded environment.
|
|
402
|
+
const searchOverrides = executable === null
|
|
403
|
+
? []
|
|
404
|
+
: embeddedDyldOverrides(slice.dyld_environment, slice.platforms);
|
|
405
|
+
// Inserted libraries introduce unexamined images even without dependencies.
|
|
406
|
+
if (searchOverrides.some(({ variable, value }) => variable === "DYLD_INSERT_LIBRARIES" && value !== ""))
|
|
407
|
+
environmentRoots.add(root);
|
|
319
408
|
roots.push({ image: root, architecture: slice.architecture });
|
|
320
409
|
edges.push(...(await traceProcess({
|
|
321
410
|
view,
|
|
322
411
|
root,
|
|
323
412
|
architecture: slice.architecture,
|
|
324
|
-
|
|
413
|
+
searchOverrides,
|
|
414
|
+
environmentRoots,
|
|
415
|
+
platforms: slice.platforms,
|
|
416
|
+
executable,
|
|
325
417
|
loaded: new Map(),
|
|
326
418
|
byInstallName: new Map(),
|
|
327
419
|
images,
|
|
@@ -347,18 +439,37 @@ export const traceDylibLoading = async (view, request) => {
|
|
|
347
439
|
edges,
|
|
348
440
|
findings: deriveFindings(edges, roots, images),
|
|
349
441
|
coverage: {
|
|
350
|
-
status: unparsed.length === 0 &&
|
|
442
|
+
status: unparsed.length === 0 &&
|
|
443
|
+
withoutArchitecture.length === 0 &&
|
|
444
|
+
environmentRoots.size === 0
|
|
351
445
|
? "complete"
|
|
352
446
|
: "partial",
|
|
353
447
|
unparsed_images: unparsed,
|
|
354
448
|
roots_without_architecture: withoutArchitecture,
|
|
355
449
|
},
|
|
356
|
-
limitations:
|
|
450
|
+
limitations: [
|
|
451
|
+
...DYLIB_RESOLUTION_LIMITATIONS,
|
|
452
|
+
...(environmentRoots.size === 0
|
|
453
|
+
? []
|
|
454
|
+
: [
|
|
455
|
+
`Roots ${[...environmentRoots].join(", ")} request applicable image-selection inputs through LC_DYLD_ENVIRONMENT that are not modeled; affected dependencies or inserted images remain uncertain.`,
|
|
456
|
+
]),
|
|
457
|
+
],
|
|
357
458
|
};
|
|
358
459
|
};
|
|
359
460
|
const withoutDependencies = (slice) => ({
|
|
360
461
|
architecture: slice.architecture,
|
|
462
|
+
slice_offset: slice.slice_offset,
|
|
463
|
+
slice_size: slice.slice_size,
|
|
464
|
+
cpu_type: slice.cpu_type,
|
|
465
|
+
cpu_subtype: slice.cpu_subtype,
|
|
466
|
+
fat_cpu_type: slice.fat_cpu_type,
|
|
467
|
+
fat_cpu_subtype: slice.fat_cpu_subtype,
|
|
468
|
+
fat_alignment_exponent: slice.fat_alignment_exponent,
|
|
361
469
|
file_type: slice.file_type,
|
|
470
|
+
file_type_code: slice.file_type_code,
|
|
471
|
+
platform: slice.platform,
|
|
472
|
+
platforms: slice.platforms,
|
|
362
473
|
install_name: slice.install_name,
|
|
363
474
|
rpaths: slice.rpaths,
|
|
364
475
|
dyld_environment: slice.dyld_environment,
|
|
@@ -14,22 +14,24 @@ export const deriveFindings = (edges, roots, images) => {
|
|
|
14
14
|
edge_index: index,
|
|
15
15
|
image: edge.loader,
|
|
16
16
|
basis: "derived",
|
|
17
|
-
explanation: `dyld
|
|
17
|
+
explanation: `The modeled dyld search checks ${earlier.join(", ")} before ${edge.resolution.image ?? edge.install_name}. A compatible Mach-O placed at an earlier modeled path could take precedence over that fallback, subject to unmodeled search inputs and code-signing library validation; library validation is not evaluated here (see inspect_signature).${edge.loader_conditional ? " This loader loads only conditionally; if it never loads, this search never occurs." : ""}${edge.resolution.status === "conditional" ? " Unmodeled environment overrides or earlier unknown candidates may take precedence; placing an image at this rpath does not establish which image dyld will load." : ""}`,
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
20
|
for (const { image, architecture } of roots) {
|
|
21
21
|
const facts = images.get(image);
|
|
22
|
-
const
|
|
23
|
-
?
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const slice = facts?.status === "parsed"
|
|
23
|
+
? facts.slices.find((candidate) => candidate.architecture === architecture)
|
|
24
|
+
: undefined;
|
|
25
|
+
const environment = slice?.dyld_environment ?? [];
|
|
26
26
|
if (environment.length > 0)
|
|
27
27
|
findings.push({
|
|
28
28
|
kind: "dyld-environment-present",
|
|
29
29
|
edge_index: null,
|
|
30
30
|
image,
|
|
31
31
|
basis: "derived",
|
|
32
|
-
explanation:
|
|
32
|
+
explanation: slice?.file_type === "execute"
|
|
33
|
+
? `${image} (${architecture}) sets dyld environment variables through LC_DYLD_ENVIRONMENT (${environment.join(", ")}); any image-selection overrides they request are not modeled. Diagnostic settings alone do not alter resolution.`
|
|
34
|
+
: `${image} (${architecture}) contains LC_DYLD_ENVIRONMENT (${environment.join(", ")}). Dyld reads these commands only from the main executable; entries in this non-executable root are observations and do not alter resolution.`,
|
|
33
35
|
});
|
|
34
36
|
}
|
|
35
37
|
return findings;
|
|
@@ -60,7 +62,7 @@ const unresolvedFinding = (edge, index) => {
|
|
|
60
62
|
: {
|
|
61
63
|
...base,
|
|
62
64
|
kind: "required-load-unresolved",
|
|
63
|
-
explanation: `${missing}${when}dyld would fail to
|
|
65
|
+
explanation: `${missing}${when}dyld would fail to load this required dependency for ${edge.root} (${edge.architecture}) unless the image is supplied elsewhere.`,
|
|
64
66
|
};
|
|
65
67
|
};
|
|
66
68
|
export const DYLIB_RESOLUTION_LIMITATIONS = [
|
|
@@ -28,6 +28,7 @@ export const buildInterfaceBuilderAnalysis = (input) => {
|
|
|
28
28
|
let omittedObjects = 0;
|
|
29
29
|
let omittedConnections = 0;
|
|
30
30
|
let omittedGraphNodes = 0;
|
|
31
|
+
let omittedHierarchyReferences = 0;
|
|
31
32
|
for (const document of input.documents.slice(0, input.limits.max_documents)) {
|
|
32
33
|
const parsed = parseInterfaceBuilderRecords(document.raw);
|
|
33
34
|
const prefix = `ib:${document.relativePath}:`;
|
|
@@ -276,6 +277,8 @@ export const buildInterfaceBuilderAnalysis = (input) => {
|
|
|
276
277
|
}
|
|
277
278
|
}
|
|
278
279
|
const objectsTruncated = parsed.omittedObjects > 0 || parsed.objects.length > documentObjectCount;
|
|
280
|
+
const parserOmittedHierarchyReferences = parsed.omittedHierarchyReferences;
|
|
281
|
+
omittedHierarchyReferences += parserOmittedHierarchyReferences;
|
|
279
282
|
const parserOmittedConnections = parsed.omittedConnections;
|
|
280
283
|
const connectionsTruncated = parserOmittedConnections > 0 ||
|
|
281
284
|
parsed.connections.length > input.limits.max_connections;
|
|
@@ -291,7 +294,9 @@ export const buildInterfaceBuilderAnalysis = (input) => {
|
|
|
291
294
|
document_kind: document.documentKind,
|
|
292
295
|
object_count: parsed.objectCount,
|
|
293
296
|
connection_count: parsed.connections.length + parserOmittedConnections,
|
|
294
|
-
hierarchy_complete: parsed.hierarchy.length > 0 &&
|
|
297
|
+
hierarchy_complete: parsed.hierarchy.length > 0 &&
|
|
298
|
+
!truncated &&
|
|
299
|
+
parserOmittedHierarchyReferences === 0,
|
|
295
300
|
});
|
|
296
301
|
coverage.push({
|
|
297
302
|
facet: `objects:${document.relativePath}`,
|
|
@@ -314,18 +319,22 @@ export const buildInterfaceBuilderAnalysis = (input) => {
|
|
|
314
319
|
parserOmittedConnections,
|
|
315
320
|
}, {
|
|
316
321
|
facet: `hierarchy:${document.relativePath}`,
|
|
317
|
-
status:
|
|
318
|
-
? "
|
|
319
|
-
:
|
|
320
|
-
? "
|
|
321
|
-
:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
322
|
+
status: parserOmittedHierarchyReferences > 0
|
|
323
|
+
? "partial"
|
|
324
|
+
: parsed.hierarchy.length === 0
|
|
325
|
+
? "unsupported"
|
|
326
|
+
: hierarchyTruncated || documentOmittedHierarchyEdges > 0
|
|
327
|
+
? "partial"
|
|
328
|
+
: "complete",
|
|
329
|
+
reason: parserOmittedHierarchyReferences > 0
|
|
330
|
+
? "archive_hierarchy_references_missing"
|
|
331
|
+
: parsed.hierarchy.length === 0
|
|
332
|
+
? "archive_hierarchy_not_decoded"
|
|
333
|
+
: hierarchyTruncated || documentOmittedHierarchyEdges > 0
|
|
334
|
+
? "graph_limit_reached"
|
|
335
|
+
: null,
|
|
327
336
|
examined: hierarchyCount,
|
|
328
|
-
omitted: documentOmittedHierarchyEdges,
|
|
337
|
+
omitted: documentOmittedHierarchyEdges + parserOmittedHierarchyReferences,
|
|
329
338
|
}, {
|
|
330
339
|
facet: `classes:${document.relativePath}`,
|
|
331
340
|
status: Object.keys(parsed.classes).length > 0 ? "complete" : "unsupported",
|
|
@@ -367,7 +376,10 @@ export const buildInterfaceBuilderAnalysis = (input) => {
|
|
|
367
376
|
nodes: [...nodes.values()],
|
|
368
377
|
edges,
|
|
369
378
|
coverage,
|
|
370
|
-
truncated: truncated ||
|
|
379
|
+
truncated: truncated ||
|
|
380
|
+
omittedObjects > 0 ||
|
|
381
|
+
omittedConnections > 0 ||
|
|
382
|
+
omittedHierarchyReferences > 0,
|
|
371
383
|
},
|
|
372
384
|
limitations: [
|
|
373
385
|
"The graph reflects recognized Interface Builder archive fields and connections; omitted private or unrecognized archive fields remain unknown.",
|
|
@@ -82,6 +82,7 @@ export const parseInterfaceBuilderRecords = (value) => {
|
|
|
82
82
|
objectCount: objectEntries.length,
|
|
83
83
|
omittedObjects: Math.max(0, objectEntries.length - 20_000),
|
|
84
84
|
omittedConnections,
|
|
85
|
+
omittedHierarchyReferences: 0,
|
|
85
86
|
};
|
|
86
87
|
};
|
|
87
88
|
/**
|
|
@@ -164,6 +165,7 @@ const keyedArchiveTable = (objectTable) => {
|
|
|
164
165
|
return output;
|
|
165
166
|
};
|
|
166
167
|
const hierarchy = (start) => {
|
|
168
|
+
let omittedReferences = 0;
|
|
167
169
|
const visited = new Set();
|
|
168
170
|
const root = [];
|
|
169
171
|
const pending = [
|
|
@@ -190,10 +192,17 @@ const keyedArchiveTable = (objectTable) => {
|
|
|
190
192
|
const { value, output } = entry;
|
|
191
193
|
const uid = archiveUid(value);
|
|
192
194
|
if (uid !== undefined) {
|
|
195
|
+
// UID0 is archived nil, not a missing hierarchy object.
|
|
196
|
+
if (uid === 0)
|
|
197
|
+
continue;
|
|
198
|
+
const target = objectTable[uid];
|
|
199
|
+
if (target === undefined) {
|
|
200
|
+
omittedReferences += 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
193
203
|
if (visited.has(uid))
|
|
194
204
|
continue;
|
|
195
205
|
visited.add(uid);
|
|
196
|
-
const target = objectTable[uid];
|
|
197
206
|
if (Array.isArray(target)) {
|
|
198
207
|
for (let index = target.length - 1; index >= 0; index -= 1) {
|
|
199
208
|
const child = target[index];
|
|
@@ -229,7 +238,7 @@ const keyedArchiveTable = (objectTable) => {
|
|
|
229
238
|
appendNode(record(value), undefined, output);
|
|
230
239
|
}
|
|
231
240
|
}
|
|
232
|
-
return root;
|
|
241
|
+
return { items: root, omittedReferences };
|
|
233
242
|
};
|
|
234
243
|
const archivedArrayClass = (classReference) => {
|
|
235
244
|
const uid = archiveUid(classReference);
|
|
@@ -317,11 +326,12 @@ const parseKeyedArchive = (root) => {
|
|
|
317
326
|
return {
|
|
318
327
|
objects,
|
|
319
328
|
connections,
|
|
320
|
-
hierarchy,
|
|
329
|
+
hierarchy: hierarchy.items,
|
|
321
330
|
classes,
|
|
322
331
|
objectCount,
|
|
323
332
|
omittedObjects: Math.max(0, objectCount - 20_000),
|
|
324
333
|
omittedConnections,
|
|
334
|
+
omittedHierarchyReferences: hierarchy.omittedReferences,
|
|
325
335
|
};
|
|
326
336
|
};
|
|
327
337
|
/** Coerce unknown values to a string-keyed record; non-objects become `{}`. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { AnalysisInputError } from "../analysisErrorCore.js";
|
|
2
3
|
import { jsonValueSchema } from "../jsonValue.js";
|
|
3
4
|
/** Select one archive within the active bundle and optionally one named root. */
|
|
4
5
|
export const keyedArchiveInputSchema = z.strictObject({
|
|
@@ -72,7 +73,14 @@ export const projectKeyedArchive = (value, selection) => {
|
|
|
72
73
|
if (top === undefined || Object.keys(top).length === 0)
|
|
73
74
|
throw new TypeError("Keyed archive has no $top roots");
|
|
74
75
|
if (selection.root !== undefined && !Object.hasOwn(top, selection.root))
|
|
75
|
-
throw new
|
|
76
|
+
throw new AnalysisInputError("inspect_keyed_archive", undefined, [
|
|
77
|
+
{
|
|
78
|
+
path: ["root"],
|
|
79
|
+
reason: "invalid_value",
|
|
80
|
+
message: `Archive root does not exist: ${selection.root}. Select one of the archive's $top roots or omit root.`,
|
|
81
|
+
expected: Object.keys(top),
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
76
84
|
const roots = selection.root === undefined
|
|
77
85
|
? top
|
|
78
86
|
: { [selection.root]: top[selection.root] ?? null };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Decode FAT table entries once so artifact and native readers share identity facts. */
|
|
2
|
+
export const readFatSliceDeclarations = (tableBytes, options) => {
|
|
3
|
+
const { count, wide, littleEndian } = options;
|
|
4
|
+
const entrySize = wide ? 32 : 20;
|
|
5
|
+
if (tableBytes.byteLength < count * entrySize)
|
|
6
|
+
return { status: "malformed", reason: "FAT table is truncated" };
|
|
7
|
+
const view = new DataView(tableBytes.buffer, tableBytes.byteOffset, tableBytes.byteLength);
|
|
8
|
+
const slices = [];
|
|
9
|
+
for (let index = 0; index < count; index++) {
|
|
10
|
+
const base = index * entrySize;
|
|
11
|
+
const offset = wide
|
|
12
|
+
? safeNumber(view.getBigUint64(base + 8, littleEndian))
|
|
13
|
+
: view.getUint32(base + 8, littleEndian);
|
|
14
|
+
const size = wide
|
|
15
|
+
? safeNumber(view.getBigUint64(base + 16, littleEndian))
|
|
16
|
+
: view.getUint32(base + 12, littleEndian);
|
|
17
|
+
if (offset === null || size === null)
|
|
18
|
+
return {
|
|
19
|
+
status: "malformed",
|
|
20
|
+
reason: "FAT offset or size exceeds the safe range",
|
|
21
|
+
};
|
|
22
|
+
slices.push({
|
|
23
|
+
index,
|
|
24
|
+
offset,
|
|
25
|
+
size,
|
|
26
|
+
alignmentExponent: view.getUint32(base + (wide ? 24 : 16), littleEndian),
|
|
27
|
+
cpuType: view.getUint32(base, littleEndian),
|
|
28
|
+
cpuSubtype: view.getUint32(base + 4, littleEndian),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return { status: "parsed", slices };
|
|
32
|
+
};
|
|
33
|
+
/** Validate byte-range and table/header identity facts shared by Mach-O readers. */
|
|
34
|
+
export const validateFatSlice = (declaration, containerSize, tableEnd, embedded) => {
|
|
35
|
+
const { index, offset, size, alignmentExponent, cpuType, cpuSubtype } = declaration;
|
|
36
|
+
if (!Number.isSafeInteger(offset) ||
|
|
37
|
+
!Number.isSafeInteger(size) ||
|
|
38
|
+
size <= 0 ||
|
|
39
|
+
offset < tableEnd)
|
|
40
|
+
return `FAT architecture ${index} has an invalid slice range`;
|
|
41
|
+
if (offset > containerSize || size > containerSize - offset)
|
|
42
|
+
return `FAT architecture ${index} slice range extends beyond the file`;
|
|
43
|
+
if (!Number.isInteger(alignmentExponent) ||
|
|
44
|
+
alignmentExponent < 0 ||
|
|
45
|
+
alignmentExponent > 52)
|
|
46
|
+
return `FAT architecture ${index} alignment exponent ${alignmentExponent} is unsupported`;
|
|
47
|
+
if (offset % 2 ** alignmentExponent !== 0)
|
|
48
|
+
return `FAT architecture ${index} offset is not aligned to 2^${alignmentExponent}`;
|
|
49
|
+
if (embedded !== undefined &&
|
|
50
|
+
(embedded.cpuType !== cpuType || embedded.cpuSubtype !== cpuSubtype))
|
|
51
|
+
return `FAT architecture ${index} CPU type/subtype disagrees with its Mach-O header`;
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
54
|
+
const safeNumber = (value) => value > BigInt(Number.MAX_SAFE_INTEGER) ? null : Number(value);
|