rea-agents 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +103 -38
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/AndroidApplicationService.js +17 -0
  4. package/dist/application/AppleApplicationService.js +17 -0
  5. package/dist/application/ArtifactExtraction.js +1 -1
  6. package/dist/application/ArtifactInventory/classify.js +42 -0
  7. package/dist/application/ArtifactInventory/hash.js +43 -0
  8. package/dist/application/ArtifactInventory/reader.js +41 -0
  9. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  10. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  11. package/dist/application/ArtifactInventory/types.js +10 -0
  12. package/dist/application/ArtifactInventory.js +9 -351
  13. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  14. package/dist/application/BinarySession.js +47 -270
  15. package/dist/application/BinarySessionExecution.js +28 -0
  16. package/dist/application/BinarySessionOpen.js +39 -0
  17. package/dist/application/BinarySessionRecords.js +149 -0
  18. package/dist/application/BinarySessionStatus.js +75 -0
  19. package/dist/application/CapabilityInventory.js +24 -2
  20. package/dist/application/ClientRegistrationStatus.js +11 -4
  21. package/dist/application/CrossVersionInventory.js +8 -2
  22. package/dist/application/CrossVersionInvestigation.js +1 -23
  23. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  24. package/dist/application/DirectAnalysis.js +47 -44
  25. package/dist/application/Doctor.js +156 -238
  26. package/dist/application/DoctorDiagnostics.js +180 -0
  27. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  28. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  29. package/dist/application/EnhancedToolPagination.js +22 -0
  30. package/dist/application/EnhancedToolTypes.js +4 -0
  31. package/dist/application/EnhancedTools.js +20 -37
  32. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  33. package/dist/application/InventoryProjectionEvidence.js +41 -0
  34. package/dist/application/InvestigationProviders.js +12 -0
  35. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  36. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  37. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  38. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  39. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  40. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  41. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  42. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  43. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  44. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  45. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  46. package/dist/application/JavaScriptJsonModules.js +39 -0
  47. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  48. package/dist/application/JavaScriptReplayPermission.js +16 -0
  49. package/dist/application/JavaScriptReplayService.js +182 -156
  50. package/dist/application/LinuxHopper.js +10 -0
  51. package/dist/application/MacHopper.js +22 -11
  52. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  53. package/dist/application/PermissionAuthority.js +106 -63
  54. package/dist/application/ProcessCaptureCapability.js +1 -1
  55. package/dist/application/ProcessCapturePermission.js +13 -0
  56. package/dist/application/ProcessCli.js +2 -12
  57. package/dist/application/ProcessHarness.js +1 -1
  58. package/dist/application/ProcessPairedExperiment.js +72 -0
  59. package/dist/application/ProjectPermissionStore.js +48 -9
  60. package/dist/application/RealHopperAssertions.js +76 -0
  61. package/dist/application/ReconstructionCoverageService.js +69 -0
  62. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  63. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  64. package/dist/application/Setup.js +148 -260
  65. package/dist/application/SetupClientConfiguration.js +224 -0
  66. package/dist/application/SetupClients.js +24 -2
  67. package/dist/application/SetupPlan.js +124 -12
  68. package/dist/application/SetupSkill.js +54 -36
  69. package/dist/application/SupportedClients.js +1 -0
  70. package/dist/application/Uninstall.js +12 -3
  71. package/dist/application/Upgrade.js +16 -1
  72. package/dist/application/WindowsCapabilities.js +86 -0
  73. package/dist/artifacts/AsarArtifactReader.js +11 -0
  74. package/dist/browser/BrowserProcessStartup.js +88 -0
  75. package/dist/browser/CdpCaptureDocuments.js +12 -8
  76. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  77. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  78. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  79. package/dist/browser/CdpCaptureEventState.js +1 -0
  80. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  81. package/dist/browser/CdpCaptureEvents.js +29 -496
  82. package/dist/browser/CdpObservationSession.js +51 -8
  83. package/dist/browser/CdpPageCapture.js +12 -133
  84. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  85. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  86. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  87. package/dist/browser/PngVisualDiff.js +9 -2
  88. package/dist/catalogIdentity.js +4 -0
  89. package/dist/cli/artifactCommands.js +82 -0
  90. package/dist/cli/coreAnalysisCommands.js +232 -0
  91. package/dist/cli/managedCommands.js +397 -0
  92. package/dist/cli/managedProjectGraphCommand.js +24 -0
  93. package/dist/cli/options.js +9 -0
  94. package/dist/cli/setupCommands.js +113 -0
  95. package/dist/cli/types.js +1 -0
  96. package/dist/cli/utilityCommands.js +122 -0
  97. package/dist/cli.js +10 -768
  98. package/dist/cliApplicationCommands.js +118 -4
  99. package/dist/cliBrowserAdvancedCommands.js +73 -16
  100. package/dist/cliBrowserCommands.js +39 -115
  101. package/dist/cliCommandNames.js +4 -0
  102. package/dist/cliElectronCommands.js +33 -62
  103. package/dist/cliEvidenceCommands.js +13 -2
  104. package/dist/cliInvestigationCommands.js +53 -10
  105. package/dist/cliObservationOptions.js +137 -0
  106. package/dist/cliPolicyCommands.js +80 -46
  107. package/dist/cliSetup.js +306 -0
  108. package/dist/config/environment.js +107 -0
  109. package/dist/config/parseConfig.js +228 -0
  110. package/dist/config/parsers.js +68 -0
  111. package/dist/config/permissions.js +30 -0
  112. package/dist/config/types.js +1 -0
  113. package/dist/config.js +1 -375
  114. package/dist/contracts/applicationToolContracts.js +134 -0
  115. package/dist/contracts/electronToolContracts.js +51 -3
  116. package/dist/contracts/managedToolContracts.js +8 -3
  117. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  118. package/dist/contracts/providerSelection.js +2 -0
  119. package/dist/contracts/sessionStatusContract.js +26 -0
  120. package/dist/contracts/toolContractTypes.js +12 -1
  121. package/dist/contracts/toolContracts.js +5 -12
  122. package/dist/contracts/toolEffects.js +14 -0
  123. package/dist/contracts/toolOutputSchemaGroups.js +274 -0
  124. package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
  125. package/dist/contracts/toolOutputSchemas.js +2 -550
  126. package/dist/domain/analysisErrorPresentation.js +234 -0
  127. package/dist/domain/analysisErrorProjection.js +284 -0
  128. package/dist/domain/androidApplication.js +206 -0
  129. package/dist/domain/appleApplication.js +221 -0
  130. package/dist/domain/artifactComparison.js +34 -15
  131. package/dist/domain/binaryTarget.js +87 -37
  132. package/dist/domain/boundedCartesianProjection.js +15 -0
  133. package/dist/domain/browserObservation.js +1 -285
  134. package/dist/domain/browserObservationSchemas.js +286 -0
  135. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  136. package/dist/domain/errors.js +4 -486
  137. package/dist/domain/functionComparison.js +4 -165
  138. package/dist/domain/functionComparisonDimensions.js +164 -0
  139. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  140. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  141. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  142. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  143. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  144. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  145. package/dist/domain/javascriptSemanticIr.js +30 -0
  146. package/dist/domain/javascriptSemanticProjection.js +366 -0
  147. package/dist/domain/javascriptSemanticState.js +40 -0
  148. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  149. package/dist/domain/javascriptSemanticValues.js +352 -0
  150. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  151. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  152. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  153. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  154. package/dist/domain/knownPageCoverage.js +21 -0
  155. package/dist/domain/managedApplicationGraph.js +14 -364
  156. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  157. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  158. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  159. package/dist/domain/managedArtifact.js +16 -8
  160. package/dist/domain/managedMemberComparison.js +37 -391
  161. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  162. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  163. package/dist/domain/managedNativeVerification.js +27 -389
  164. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  165. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  166. package/dist/domain/managedReconstruction.js +82 -69
  167. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  168. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  169. package/dist/domain/permissionPolicy.js +2 -0
  170. package/dist/domain/processComparison.js +19 -0
  171. package/dist/domain/processPairedExperiment.js +128 -0
  172. package/dist/domain/reconstructionCoverage.js +387 -0
  173. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  174. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  175. package/dist/domain/replayMachine.js +316 -0
  176. package/dist/domain/replayMachineRuntime.js +215 -0
  177. package/dist/domain/runtimeCharacterization.js +85 -0
  178. package/dist/domain/runtimeIdentification.js +210 -0
  179. package/dist/domain/webBundleAnalyzer.js +3 -409
  180. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  181. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  182. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  183. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  184. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  185. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  186. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  187. package/dist/dotnet/ManagedMemberRows.js +120 -0
  188. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  189. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  190. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  191. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  192. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  193. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  194. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  195. package/dist/dotnet/ManagedPeReader.js +59 -28
  196. package/dist/evaluation/CodexAgentEval.js +132 -0
  197. package/dist/generatedPackageMetadata.js +2 -2
  198. package/dist/ghidra/GhidraClient.js +76 -91
  199. package/dist/ghidra/GhidraClientStartup.js +29 -0
  200. package/dist/ghidra/GhidraClientWire.js +34 -0
  201. package/dist/ghidra/GhidraDefaults.js +1 -1
  202. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  203. package/dist/ghidra/GhidraInstallation.js +20 -15
  204. package/dist/ghidra/GhidraLauncher.js +93 -29
  205. package/dist/ghidra/GhidraProvider.js +61 -161
  206. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  207. package/dist/ghidra/GhidraSessionValues.js +2 -0
  208. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  209. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  210. package/dist/ghidra/GhidraTransport.js +71 -0
  211. package/dist/identity.js +4 -2
  212. package/dist/main/messages.js +5 -0
  213. package/dist/main/reload.js +49 -0
  214. package/dist/main/shutdown.js +25 -0
  215. package/dist/main/startup.js +14 -0
  216. package/dist/main/state.js +26 -0
  217. package/dist/main/transport.js +61 -0
  218. package/dist/main/types.js +1 -0
  219. package/dist/main.js +35 -146
  220. package/dist/process/PrivateRuntimeRoot.js +3 -2
  221. package/dist/process/ProcessOwnership.js +35 -0
  222. package/dist/process/ProviderProcess.js +2 -0
  223. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  224. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  225. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  226. package/dist/replay/ReplayOutcome.js +95 -0
  227. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  228. package/dist/server/ProcessCaptureElicitation.js +157 -0
  229. package/dist/server/createServer.js +124 -56
  230. package/dist/server/javascriptApplicationResult.js +85 -0
  231. package/dist/server/registerApplicationTools/characterization.js +57 -0
  232. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  233. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  234. package/dist/server/registerApplicationTools/coverage.js +84 -0
  235. package/dist/server/registerApplicationTools/helpers.js +42 -0
  236. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  237. package/dist/server/registerApplicationTools/types.js +1 -0
  238. package/dist/server/registerApplicationTools.js +10 -112
  239. package/dist/server/registerBrowserTools.js +59 -83
  240. package/dist/server/registerElectronTools.js +48 -49
  241. package/dist/server/registerEnhancedTools.js +34 -42
  242. package/dist/server/registerEvidenceResources.js +56 -0
  243. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  244. package/dist/server/registerInvestigationTools/types.js +1 -0
  245. package/dist/server/registerInvestigationTools.js +83 -116
  246. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  247. package/dist/server/registerManagedTools.js +18 -1
  248. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  249. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  250. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  251. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  252. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  253. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  254. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  255. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  256. package/dist/server/registerManagedWorkflowTools.js +10 -274
  257. package/dist/server/registerOfficialTools.js +73 -59
  258. package/dist/server/registerSessionRecordTools.js +137 -0
  259. package/dist/server/registerSessionStatusTool.js +97 -26
  260. package/dist/server/registerSessionTools.js +60 -164
  261. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  262. package/dist/server/toolRegistrationOptions.js +101 -3
  263. package/dist/server/toolResult.js +15 -4
  264. package/package.json +11 -11
  265. package/skills/reverse-engineer-anything/SKILL.md +76 -0
  266. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  267. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  268. package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
  269. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  270. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
  271. package/scripts/prepare-node-pty.mjs +0 -35
  272. package/scripts/rebuild-native.mjs +0 -22
  273. package/skills/rea-analysis/SKILL.md +0 -222
@@ -0,0 +1,206 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
5
+ import { projectBoundedCartesian } from "./boundedCartesianProjection.js";
6
+ import { evidenceSchema } from "./evidence.js";
7
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
8
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
9
+ const pathSchema = z.string().min(1).max(4_096);
10
+ const componentSchema = z.strictObject({
11
+ path: pathSchema,
12
+ artifact_id: z.string().regex(/^art_[a-f0-9]{64}$/u),
13
+ sha256: digestSchema,
14
+ format: z.string().min(1).max(100),
15
+ });
16
+ /** Authenticated APK inventory pages projected as one Android application. */
17
+ export const androidApplicationProjectionInputSchema = z.strictObject({
18
+ inventory_evidence: z.array(evidenceSchema).min(1).max(100),
19
+ limits: z
20
+ .strictObject({
21
+ max_components: z.number().int().min(1).max(10_000).default(1_000),
22
+ })
23
+ .default({ max_components: 1_000 }),
24
+ });
25
+ /** Deterministic, execution-free Android application inventory projection. */
26
+ export const androidApplicationProjectionResultSchema = z.strictObject({
27
+ schema_version: z.literal(1),
28
+ projection_id: z.string().regex(/^adp_[a-f0-9]{64}$/u),
29
+ root_sha256: digestSchema,
30
+ root_format: z.literal("apk"),
31
+ source_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
32
+ components: z.strictObject({
33
+ manifests: z.array(componentSchema),
34
+ resources: z.array(componentSchema),
35
+ dex: z.array(componentSchema),
36
+ jvm_classes: z.array(componentSchema),
37
+ native_libraries: z.array(componentSchema),
38
+ javascript: z.array(componentSchema),
39
+ signing: z.array(componentSchema),
40
+ }),
41
+ runtime_families: z.array(z.enum([
42
+ "dalvik-art",
43
+ "java-kotlin",
44
+ "native",
45
+ "javascript",
46
+ "react-native",
47
+ "flutter",
48
+ "unity",
49
+ ])),
50
+ bridge_candidates: z.array(z.strictObject({
51
+ managed_path: pathSchema,
52
+ native_path: pathSchema,
53
+ basis: z.enum([
54
+ "managed-and-native-content",
55
+ "jni-library-convention",
56
+ "react-native-convention",
57
+ "flutter-convention",
58
+ "unity-convention",
59
+ ]),
60
+ })),
61
+ coverage: z.strictObject({
62
+ status: z.enum(["complete-within-inventory", "partial", "truncated"]),
63
+ inventory_complete: z.boolean(),
64
+ omitted_components: z.number().int().min(0),
65
+ omitted_bridge_candidates: z.number().int().min(0),
66
+ }),
67
+ limitations: z.array(z.string().min(1).max(4_096)).max(100),
68
+ });
69
+ /** Project exact APK paths and hashes without decoding or executing target code. */
70
+ export const projectAndroidApplication = (input) => {
71
+ const parsed = androidApplicationProjectionInputSchema.parse(input);
72
+ const { evidence, inventory } = parseArtifactInventoryEvidence(parsed.inventory_evidence);
73
+ if (inventory.manifest.root_format !== "apk")
74
+ throw new TypeError("Android application projection requires APK Evidence");
75
+ const nodes = new Map(inventory.nodes.map((node) => [node.artifact_id, node]));
76
+ const all = inventory.occurrences
77
+ .filter((occurrence) => occurrence.artifact_id !== null && occurrence.logical_path !== ".")
78
+ .map((occurrence) => {
79
+ const node = nodes.get(occurrence.artifact_id ?? "");
80
+ if (node === undefined)
81
+ throw new TypeError("Android application occurrence has no artifact node");
82
+ return {
83
+ path: occurrence.logical_path,
84
+ artifact_id: node.artifact_id,
85
+ sha256: node.sha256,
86
+ format: node.format,
87
+ };
88
+ });
89
+ const classified = classify(all);
90
+ const components = retain(classified, parsed.limits.max_components);
91
+ const omittedComponents = count(classified) - count(components);
92
+ const bridgeProjection = bridgeCandidates([...components.dex, ...components.jvm_classes], components.native_libraries, parsed.limits.max_components);
93
+ const limitations = [
94
+ ...(!inventory.complete
95
+ ? ["Source inventory pages are incomplete; absence is unknown."]
96
+ : []),
97
+ ...(omittedComponents > 0
98
+ ? [
99
+ `${String(omittedComponents)} component observations were omitted by the projection limit.`,
100
+ ]
101
+ : []),
102
+ ...(bridgeProjection.omitted > 0
103
+ ? [
104
+ `${String(bridgeProjection.omitted)} bridge hypotheses were omitted by the projection limit.`,
105
+ ]
106
+ : []),
107
+ "Manifest, resource, signing, and bytecode semantics require a dedicated Android provider; this projection reports exact inventory paths and hashes only.",
108
+ "Bridge candidates are path-based hypotheses, not decoded JNI declarations or observed runtime calls.",
109
+ ];
110
+ const withoutId = {
111
+ schema_version: 1,
112
+ root_sha256: inventory.manifest.root_sha256,
113
+ root_format: "apk",
114
+ source_evidence_ids: evidence
115
+ .map(({ evidence_id: id }) => id)
116
+ .sort(compare),
117
+ components,
118
+ runtime_families: runtimeFamilies(all),
119
+ bridge_candidates: bridgeProjection.candidates,
120
+ coverage: {
121
+ status: omittedComponents > 0 || bridgeProjection.omitted > 0
122
+ ? "truncated"
123
+ : inventory.complete
124
+ ? "complete-within-inventory"
125
+ : "partial",
126
+ inventory_complete: inventory.complete,
127
+ omitted_components: omittedComponents,
128
+ omitted_bridge_candidates: bridgeProjection.omitted,
129
+ },
130
+ limitations,
131
+ };
132
+ return androidApplicationProjectionResultSchema.parse({
133
+ ...withoutId,
134
+ projection_id: `adp_${digest(withoutId)}`,
135
+ });
136
+ };
137
+ const classify = (all) => ({
138
+ manifests: all.filter(({ path, format }) => format === "android-manifest" ||
139
+ /(?:^|\/)AndroidManifest\.xml$/u.test(path)),
140
+ resources: all.filter(({ path, format }) => format === "android-resources" || /(?:^|\/)resources\.arsc$/u.test(path)),
141
+ dex: all.filter(({ path, format }) => format === "dex" || /(?:^|\/)[^/]+\.dex$/iu.test(path)),
142
+ jvm_classes: all.filter(({ path, format }) => format === "jvm-class" || /(?:^|\/)[^/]+\.class$/iu.test(path)),
143
+ native_libraries: all.filter(({ path, format }) => format === "elf" || /(?:^|\/)lib\/[^/]+\/[^/]+\.so$/iu.test(path)),
144
+ javascript: all.filter(({ format }) => format === "javascript-bundle"),
145
+ signing: all.filter(({ path }) => /^META-INF\/[^/]+\.(?:MF|RSA|DSA|EC|SF)$/iu.test(path)),
146
+ });
147
+ const retain = (groups, maximum) => {
148
+ let remaining = maximum;
149
+ return Object.fromEntries(Object.entries(groups).map(([key, values]) => {
150
+ const selected = [...values]
151
+ .sort((left, right) => compare(left.path, right.path))
152
+ .slice(0, remaining);
153
+ remaining -= selected.length;
154
+ return [key, selected];
155
+ }));
156
+ };
157
+ const count = (groups) => Object.values(groups).reduce((total, values) => total + values.length, 0);
158
+ const runtimeFamilies = (all) => {
159
+ const paths = all.map(({ path }) => path.toLowerCase());
160
+ const families = new Set();
161
+ if (all.some(({ path, format }) => format === "dex" || path.endsWith(".dex")))
162
+ families.add("dalvik-art");
163
+ if (all.some(({ path, format }) => format === "jvm-class" || path.endsWith(".class")))
164
+ families.add("java-kotlin");
165
+ if (all.some(({ format }) => format === "elf"))
166
+ families.add("native");
167
+ if (all.some(({ format }) => format === "javascript-bundle"))
168
+ families.add("javascript");
169
+ if (paths.some((path) => path.includes("reactnative") || path.includes("hermes")))
170
+ families.add("react-native");
171
+ if (paths.some((path) => path.includes("libflutter.so")))
172
+ families.add("flutter");
173
+ if (paths.some((path) => path.includes("libunity.so")))
174
+ families.add("unity");
175
+ return [...families].sort(compare);
176
+ };
177
+ const bridgeCandidates = (managed, native, maximum) => {
178
+ const projection = projectBoundedCartesian(managed, native, maximum, (source, target) => ({
179
+ managed_path: source.path,
180
+ native_path: target.path,
181
+ basis: bridgeBasis(target.path),
182
+ }));
183
+ return {
184
+ candidates: projection.values,
185
+ omitted: projection.omitted,
186
+ };
187
+ };
188
+ const bridgeBasis = (path) => {
189
+ const lower = path.toLowerCase();
190
+ if (lower.includes("react") || lower.includes("hermes"))
191
+ return "react-native-convention";
192
+ if (lower.includes("flutter"))
193
+ return "flutter-convention";
194
+ if (lower.includes("unity"))
195
+ return "unity-convention";
196
+ if (/lib\/[^/]+\/lib[^/]+\.so$/u.test(lower))
197
+ return "jni-library-convention";
198
+ return "managed-and-native-content";
199
+ };
200
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
201
+ const digest = (value) => {
202
+ const encoded = canonicalize(value);
203
+ if (encoded === undefined)
204
+ throw new TypeError("Android application projection is not canonical JSON");
205
+ return createHash("sha256").update(encoded).digest("hex");
206
+ };
@@ -0,0 +1,221 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
5
+ import { projectBoundedCartesian } from "./boundedCartesianProjection.js";
6
+ import { evidenceSchema } from "./evidence.js";
7
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
8
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
9
+ const boundedPathSchema = z.string().min(1).max(4_096);
10
+ const componentSchema = z.strictObject({
11
+ path: boundedPathSchema,
12
+ artifact_id: z.string().regex(/^art_[a-f0-9]{64}$/u),
13
+ sha256: digestSchema,
14
+ format: z.string().min(1).max(100),
15
+ });
16
+ /** Authenticated artifact inventory pages projected as one Apple application. */
17
+ export const appleApplicationProjectionInputSchema = z.strictObject({
18
+ inventory_evidence: z.array(evidenceSchema).min(1).max(100),
19
+ limits: z
20
+ .strictObject({
21
+ max_components: z.number().int().min(1).max(10_000).default(1_000),
22
+ })
23
+ .default({ max_components: 1_000 }),
24
+ });
25
+ /** Deterministic, execution-free Apple application inventory projection. */
26
+ export const appleApplicationProjectionResultSchema = z.strictObject({
27
+ schema_version: z.literal(1),
28
+ projection_id: z.string().regex(/^aap_[a-f0-9]{64}$/u),
29
+ root_sha256: digestSchema,
30
+ root_format: z.literal("ipa"),
31
+ source_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
32
+ application_roots: z.array(boundedPathSchema),
33
+ components: z.strictObject({
34
+ bundle_metadata: z.array(componentSchema),
35
+ executables: z.array(componentSchema),
36
+ frameworks: z.array(componentSchema),
37
+ native_libraries: z.array(componentSchema),
38
+ javascript: z.array(componentSchema),
39
+ signing: z.array(componentSchema),
40
+ }),
41
+ runtime_families: z.array(z.enum([
42
+ "native",
43
+ "swift-objective-c",
44
+ "javascript",
45
+ "react-native",
46
+ "flutter",
47
+ "unity",
48
+ ])),
49
+ bridge_candidates: z.array(z.strictObject({
50
+ source_path: boundedPathSchema,
51
+ native_path: boundedPathSchema,
52
+ basis: z.enum([
53
+ "javascript-and-native-content",
54
+ "react-native-convention",
55
+ "flutter-convention",
56
+ "unity-convention",
57
+ ]),
58
+ })),
59
+ coverage: z.strictObject({
60
+ status: z.enum(["complete-within-inventory", "partial", "truncated"]),
61
+ inventory_complete: z.boolean(),
62
+ omitted_components: z.number().int().min(0),
63
+ omitted_bridge_candidates: z.number().int().min(0),
64
+ }),
65
+ limitations: z.array(z.string().min(1).max(4_096)).max(100),
66
+ });
67
+ /** Project exact IPA paths and hashes without parsing or executing target code. */
68
+ export const projectAppleApplication = (input) => {
69
+ const parsed = appleApplicationProjectionInputSchema.parse(input);
70
+ const { evidence, inventory } = parseArtifactInventoryEvidence(parsed.inventory_evidence);
71
+ if (inventory.manifest.root_format !== "ipa")
72
+ throw new TypeError("Apple application projection requires IPA Evidence");
73
+ const nodes = new Map(inventory.nodes.map((node) => [node.artifact_id, node]));
74
+ const all = inventory.occurrences
75
+ .filter((occurrence) => occurrence.artifact_id !== null && occurrence.logical_path !== ".")
76
+ .map((occurrence) => {
77
+ const node = nodes.get(occurrence.artifact_id ?? "");
78
+ if (node === undefined)
79
+ throw new TypeError("Apple application occurrence has no artifact node");
80
+ return {
81
+ path: occurrence.logical_path,
82
+ artifact_id: node.artifact_id,
83
+ sha256: node.sha256,
84
+ format: node.format,
85
+ };
86
+ });
87
+ const roots = [
88
+ ...new Set(inventory.occurrences.flatMap(({ logical_path: path }) => {
89
+ const match = /^(Payload\/[^/]+\.app)(?:\/|$)/u.exec(path);
90
+ return match?.[1] === undefined ? [] : [match[1]];
91
+ })),
92
+ ].sort(compare);
93
+ const classified = classifyComponents(all, roots);
94
+ const retained = retainComponents(classified, parsed.limits.max_components);
95
+ const omitted = componentCount(classified) - componentCount(retained);
96
+ const runtimeFamilies = identifyRuntimeFamilies(all);
97
+ const bridgeProjection = identifyBridgeCandidates(retained.javascript, deduplicateComponents([
98
+ ...retained.executables,
99
+ ...retained.native_libraries,
100
+ ]), parsed.limits.max_components);
101
+ const limitations = projectionLimitations(inventory.complete, omitted, bridgeProjection.omitted, roots);
102
+ const withoutId = {
103
+ schema_version: 1,
104
+ root_sha256: inventory.manifest.root_sha256,
105
+ root_format: "ipa",
106
+ source_evidence_ids: evidence
107
+ .map(({ evidence_id: id }) => id)
108
+ .sort(compare),
109
+ application_roots: roots,
110
+ components: retained,
111
+ runtime_families: runtimeFamilies,
112
+ bridge_candidates: bridgeProjection.candidates,
113
+ coverage: {
114
+ status: omitted > 0 || bridgeProjection.omitted > 0
115
+ ? "truncated"
116
+ : inventory.complete
117
+ ? "complete-within-inventory"
118
+ : "partial",
119
+ inventory_complete: inventory.complete,
120
+ omitted_components: omitted,
121
+ omitted_bridge_candidates: bridgeProjection.omitted,
122
+ },
123
+ limitations,
124
+ };
125
+ return appleApplicationProjectionResultSchema.parse({
126
+ ...withoutId,
127
+ projection_id: `aap_${digest(withoutId)}`,
128
+ });
129
+ };
130
+ const classifyComponents = (all, roots) => {
131
+ const withinApp = (path) => roots.some((root) => path.startsWith(`${root}/`));
132
+ return {
133
+ bundle_metadata: all.filter(({ path }) => /(?:^|\/)Info\.plist$/u.test(path)),
134
+ executables: all.filter(({ path, format }) => withinApp(path) && ["mach-o", "mach-o-universal"].includes(format)),
135
+ frameworks: all.filter(({ path }) => /\.framework\//u.test(path)),
136
+ native_libraries: all.filter(({ path }) => /\.(?:dylib|so)$/iu.test(path)),
137
+ javascript: all.filter(({ format }) => format === "javascript-bundle"),
138
+ signing: all.filter(({ path }) => /(?:^|\/)(?:embedded\.mobileprovision|_CodeSignature\/CodeResources)$/u.test(path)),
139
+ };
140
+ };
141
+ const retainComponents = (groups, maximum) => {
142
+ let remaining = maximum;
143
+ return Object.fromEntries(Object.entries(groups).map(([key, values]) => {
144
+ const retained = [...values]
145
+ .sort((left, right) => compare(left.path, right.path))
146
+ .slice(0, remaining);
147
+ remaining -= retained.length;
148
+ return [key, retained];
149
+ }));
150
+ };
151
+ const componentCount = (groups) => Object.values(groups).reduce((total, values) => total + values.length, 0);
152
+ const deduplicateComponents = (values) => [...new Map(values.map((value) => [value.path, value])).values()].sort((left, right) => compare(left.path, right.path));
153
+ const identifyRuntimeFamilies = (all) => {
154
+ const paths = all.map(({ path }) => path.toLowerCase());
155
+ const families = new Set();
156
+ if (all.some(({ format }) => ["mach-o", "mach-o-universal"].includes(format)))
157
+ families.add("native");
158
+ if (paths.some((path) => /\.(?:dylib|framework\/[^/]+)$/u.test(path)))
159
+ families.add("swift-objective-c");
160
+ if (all.some(({ format }) => format === "javascript-bundle"))
161
+ families.add("javascript");
162
+ if (paths.some((path) => path.includes("reactnative") ||
163
+ path.includes("react.framework") ||
164
+ path.includes("hermes")))
165
+ families.add("react-native");
166
+ if (paths.some((path) => path.includes("flutter.framework") || path.includes("app.framework")))
167
+ families.add("flutter");
168
+ if (paths.some((path) => path.includes("unityframework.framework")))
169
+ families.add("unity");
170
+ return [...families].sort(compare);
171
+ };
172
+ const identifyBridgeCandidates = (scripts, native, maximum) => {
173
+ const projection = projectBoundedCartesian(scripts, native, maximum, (script, item) => ({
174
+ source_path: script.path,
175
+ native_path: item.path,
176
+ basis: bridgeBasis(item.path),
177
+ }));
178
+ return {
179
+ candidates: projection.values,
180
+ omitted: projection.omitted,
181
+ };
182
+ };
183
+ const bridgeBasis = (path) => {
184
+ const lower = path.toLowerCase();
185
+ if (lower.includes("react"))
186
+ return "react-native-convention";
187
+ if (lower.includes("flutter") || lower.includes("app.framework"))
188
+ return "flutter-convention";
189
+ if (lower.includes("unity"))
190
+ return "unity-convention";
191
+ return "javascript-and-native-content";
192
+ };
193
+ const projectionLimitations = (complete, omitted, omittedBridges, roots) => [
194
+ ...(!complete
195
+ ? ["Source inventory pages are incomplete; absence is unknown."]
196
+ : []),
197
+ ...(omitted > 0
198
+ ? [
199
+ `${String(omitted)} component observations were omitted by the projection limit.`,
200
+ ]
201
+ : []),
202
+ ...(omittedBridges > 0
203
+ ? [
204
+ `${String(omittedBridges)} bridge hypotheses were omitted by the projection limit.`,
205
+ ]
206
+ : []),
207
+ ...(roots.length === 0
208
+ ? [
209
+ "No Payload/*.app directory was present in the supplied inventory pages.",
210
+ ]
211
+ : []),
212
+ "Bundle identifiers and signing claims require dedicated plist and CMS parsing; this projection reports only exact paths and hashes.",
213
+ "Bridge candidates are path-based hypotheses, not observed runtime calls.",
214
+ ];
215
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
216
+ const digest = (value) => {
217
+ const encoded = canonicalize(value);
218
+ if (encoded === undefined)
219
+ throw new TypeError("Apple application projection is not canonical JSON");
220
+ return createHash("sha256").update(encoded).digest("hex");
221
+ };
@@ -161,39 +161,58 @@ const compareOccurrences = (left, right, evidenceLinks, complete) => {
161
161
  }
162
162
  return output;
163
163
  };
164
- const classifyPath = (input) => {
165
- const base = {
166
- logical_path: input.path,
167
- left_occurrence_id: input.leftOccurrence?.occurrence_id ?? null,
168
- right_occurrence_id: input.rightOccurrence?.occurrence_id ?? null,
169
- left_artifact_id: input.leftOccurrence?.artifact_id ?? null,
170
- right_artifact_id: input.rightOccurrence?.artifact_id ?? null,
171
- evidence_links: [...input.evidenceLinks],
172
- };
164
+ const buildArtifactChangeBase = (input) => ({
165
+ logical_path: input.path,
166
+ left_occurrence_id: input.leftOccurrence?.occurrence_id ?? null,
167
+ right_occurrence_id: input.rightOccurrence?.occurrence_id ?? null,
168
+ left_artifact_id: input.leftOccurrence?.artifact_id ?? null,
169
+ right_artifact_id: input.rightOccurrence?.artifact_id ?? null,
170
+ evidence_links: [...input.evidenceLinks],
171
+ });
172
+ const classifyPresence = (input) => {
173
173
  if (input.leftOccurrence === undefined)
174
174
  return {
175
- ...base,
175
+ ...buildArtifactChangeBase(input),
176
176
  classification: input.complete ? "added" : "unknown",
177
177
  dimensions: [input.complete ? "content" : "availability"],
178
178
  };
179
179
  if (input.rightOccurrence === undefined)
180
180
  return {
181
- ...base,
181
+ ...buildArtifactChangeBase(input),
182
182
  classification: input.complete ? "removed" : "unknown",
183
183
  dimensions: [input.complete ? "content" : "availability"],
184
184
  };
185
- if (input.leftOccurrence.hash_status === "mismatched" ||
186
- input.rightOccurrence.hash_status === "mismatched")
185
+ return null;
186
+ };
187
+ const classifyIntegrity = (input, base) => {
188
+ if (input.leftOccurrence?.hash_status === "mismatched" ||
189
+ input.rightOccurrence?.hash_status === "mismatched")
187
190
  return {
188
191
  ...base,
189
192
  classification: "contradiction",
190
193
  dimensions: ["integrity"],
191
194
  };
192
- if (input.leftOccurrence.hash_status !== "verified" ||
193
- input.rightOccurrence.hash_status !== "verified" ||
195
+ return null;
196
+ };
197
+ const classifyAvailability = (input, base) => {
198
+ if (input.leftOccurrence?.hash_status !== "verified" ||
199
+ input.rightOccurrence?.hash_status !== "verified" ||
194
200
  input.leftNode === undefined ||
195
201
  input.rightNode === undefined)
196
202
  return { ...base, classification: "unknown", dimensions: ["availability"] };
203
+ return null;
204
+ };
205
+ const classifyPath = (input) => {
206
+ const presence = classifyPresence(input);
207
+ if (presence !== null)
208
+ return presence;
209
+ const base = buildArtifactChangeBase(input);
210
+ const integrity = classifyIntegrity(input, base);
211
+ if (integrity !== null)
212
+ return integrity;
213
+ const availability = classifyAvailability(input, base);
214
+ if (availability !== null)
215
+ return availability;
197
216
  const dimensions = changedDimensions(input);
198
217
  return dimensions.length === 0
199
218
  ? null