rea-agents 2.0.0 → 2.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.
Files changed (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -3,6 +3,7 @@ import { JavaScriptArtifactGraphAccumulator } from "./JavaScriptArtifactGraphAcc
3
3
  import { javaScriptAnalysisLimits, } from "./JavaScriptArtifactGraphContext.js";
4
4
  import { addJavaScriptHtmlRoles, addJavaScriptSourceMapOriginals, } from "./JavaScriptArtifactGraphDocuments.js";
5
5
  import { addJavaScriptStaticFindings } from "./JavaScriptArtifactGraphFindings.js";
6
+ import { addJavaScriptModuleRelationships, addJavaScriptSourceModules, } from "./JavaScriptModuleRelationships.js";
6
7
  import { completeReconstructionCoverage, partialReconstructionCoverage, } from "./JavaScriptArtifactGraphEvidence.js";
7
8
  import { addJavaScriptArtifactContainers, addJavaScriptArtifactFiles, addJavaScriptBundlerNodes, addJavaScriptPackageNodes, createJavaScriptArtifactRootNode, } from "./JavaScriptArtifactGraphStructure.js";
8
9
  import { addElectronBoundaries } from "./ElectronBoundaryGraph.js";
@@ -21,13 +22,17 @@ export const buildJavaScriptArtifactGraph = (snapshot, fileSet, analysis, input)
21
22
  filesByPath: new Map(fileSet.files.map((file) => [file.path, file])),
22
23
  fileNodes: new Map(),
23
24
  assetNodes: new Map(),
25
+ sourceModuleNodes: new Map(),
26
+ chunkNodes: new Map(),
24
27
  moduleNodes: new Map(),
25
28
  containerNodes: new Map([[snapshot.manifest.root_sha256, root]]),
26
29
  };
27
30
  addJavaScriptArtifactContainers(context);
28
31
  addJavaScriptArtifactFiles(context);
29
32
  const packageRoots = addJavaScriptPackageNodes(context);
33
+ addJavaScriptSourceModules(context);
30
34
  addJavaScriptBundlerNodes(context);
35
+ addJavaScriptModuleRelationships(context);
31
36
  addJavaScriptStaticFindings(context);
32
37
  addElectronBoundaries(context);
33
38
  addJavaScriptHtmlRoles(context);
@@ -50,6 +55,7 @@ const graphCoverage = (context) => {
50
55
  context.accumulator.omittedObservations();
51
56
  const sourceMapPolicyGap = context.analysis.source_maps.some(({ status }) => status === "not-approved");
52
57
  const malformedStructuredData = context.analysis.packages.some(({ status }) => status !== "included") ||
58
+ context.analysis.json_modules.some(({ status }) => status !== "included") ||
53
59
  context.analysis.source_maps.some(({ status }) => status === "invalid");
54
60
  const partialJavaScript = context.analysis.files.some(({ javascript }) => javascript !== null && javascript.parse_status === "partial");
55
61
  const unknownGap = context.analysis.parse_failures > 0 ||
@@ -58,13 +64,28 @@ const graphCoverage = (context) => {
58
64
  malformedStructuredData ||
59
65
  partialJavaScript;
60
66
  if (context.analysis.truncated_scopes > 0)
61
- return partialReconstructionCoverage(reconstructionLimits(context.input), null, true);
67
+ return partialReconstructionCoverage(reconstructionLimits(context.input), truncationOmittedCount(context), true);
62
68
  if (exactLimitOmissions > 0)
63
69
  return partialReconstructionCoverage(reconstructionLimits(context.input), exactLimitOmissions, true);
64
70
  if (unknownGap)
65
71
  return partialReconstructionCoverage(reconstructionLimits(context.input), null, false);
66
72
  return completeReconstructionCoverage(reconstructionLimits(context.input));
67
73
  };
74
+ const truncationOmittedCount = (context) => {
75
+ const staticTruncations = context.analysis.files.filter(({ javascript }) => javascript?.parse_status === "truncated");
76
+ if (staticTruncations.length > 0)
77
+ return null;
78
+ const semanticTruncations = context.analysis.files.flatMap(({ semantic }) => semantic?.ir.coverage.status === "truncated" ? [semantic.ir.coverage] : []);
79
+ const sourceMapTruncations = context.analysis.source_maps.filter(({ status }) => status === "truncated");
80
+ if (sourceMapTruncations.length > 0)
81
+ return null;
82
+ if (semanticTruncations.length !== context.analysis.truncated_scopes)
83
+ return null;
84
+ const omissions = semanticTruncations.map(({ omittedCount }) => omittedCount);
85
+ return omissions.some((omitted) => omitted === null)
86
+ ? null
87
+ : omissions.reduce((total, omitted) => total + (omitted ?? 0), 0);
88
+ };
68
89
  const graphLimitations = (context, coverage) => {
69
90
  const ipc = collectElectronIpcRecords(context.analysis);
70
91
  const pairings = classifyElectronIpcPairings(ipc);
@@ -79,6 +100,7 @@ const graphLimitations = (context, coverage) => {
79
100
  javascript.electron.native_addon_bindings.length), 0);
80
101
  return [
81
102
  ...context.analysis.limitations,
103
+ "CommonJS and ESM binding relationships were recovered from inert syntax and resolved only within the inventoried artifact container.",
82
104
  "Webpack/Rspack factories were recovered from AST literals; REA did not invoke push handlers or bundle bootstrap code.",
83
105
  "Static imports, entrypoints, workers, endpoints, and storage relationships do not prove runtime execution.",
84
106
  ...(electronFindings === 0
@@ -0,0 +1,225 @@
1
+ import { addAstContainsEdge, artifactLocalIdentity, chunkLookupKey, javascriptAnalysisCoverage, moduleLookupKey, } from "./JavaScriptArtifactGraphContext.js";
2
+ import { astObservationEvidence, staticInferenceEvidence, } from "./JavaScriptArtifactGraphEvidence.js";
3
+ /** Project Webpack/Rspack chunk and factory literals recovered from AST. */
4
+ export const addJavaScriptBundlerNodes = (context) => {
5
+ for (const analyzed of context.analysis.files) {
6
+ const { file, javascript } = analyzed;
7
+ const asset = context.assetNodes.get(file.path);
8
+ if (javascript === null || asset === undefined)
9
+ continue;
10
+ const coverage = javascriptAnalysisCoverage(javascript, context.input);
11
+ const records = [];
12
+ for (const registration of javascript.bundler_registrations) {
13
+ const input = {
14
+ file,
15
+ registration,
16
+ coverage,
17
+ limitations: javascript.limitations,
18
+ };
19
+ const chunk = createBundlerChunkNode(context, input);
20
+ addAstContainsEdge(context, {
21
+ source: asset,
22
+ target: chunk,
23
+ file,
24
+ range: registration.location,
25
+ coverage,
26
+ properties: { bundler: registration.bundler },
27
+ });
28
+ for (const key of registration.chunk_keys)
29
+ context.chunkNodes.set(chunkLookupKey(file.path, registration.runtime, key), chunk);
30
+ records.push({ file, registration, chunk, coverage });
31
+ addBundlerModuleNodes(context, { ...input, chunk });
32
+ }
33
+ for (const record of records)
34
+ addBundlerRuntimeEdges(context, record);
35
+ }
36
+ };
37
+ const createBundlerChunkNode = (context, input) => {
38
+ const { file, registration, coverage, limitations } = input;
39
+ const chunkKey = `${registration.runtime}:${registration.chunk_keys.join(",")}`;
40
+ return context.accumulator.addNode({
41
+ kind: "javascript-chunk",
42
+ identity: artifactLocalIdentity(file.sha256, "bundler-chunk", chunkKey),
43
+ observations: [
44
+ {
45
+ label: chunkKey,
46
+ properties: {
47
+ path: file.path,
48
+ bundler: registration.bundler,
49
+ runtime: registration.runtime,
50
+ chunk_keys: registration.chunk_keys,
51
+ omitted_chunk_keys: registration.omitted_chunk_keys,
52
+ unknown_chunk_keys: registration.unknown_chunk_keys,
53
+ runtime_require_name: registration.runtime_require_name,
54
+ runtime_module_cache_status: registration.runtime_module_cache_status,
55
+ entry_module_keys: registration.entry_module_keys,
56
+ omitted_entry_module_keys: registration.omitted_entry_module_keys,
57
+ unknown_entry_module_keys: registration.unknown_entry_module_keys,
58
+ async_chunk_keys: registration.async_chunk_keys,
59
+ omitted_async_chunk_keys: registration.omitted_async_chunk_keys,
60
+ unknown_async_chunk_keys: registration.unknown_async_chunk_keys,
61
+ module_count: registration.modules.length,
62
+ },
63
+ evidence: astObservationEvidence({
64
+ sha256: file.sha256,
65
+ path: file.path,
66
+ range: registration.location,
67
+ operation: "extract-bundler-registration",
68
+ coverage,
69
+ limitations,
70
+ }),
71
+ },
72
+ ],
73
+ });
74
+ };
75
+ const addBundlerModuleNodes = (context, input) => {
76
+ const { file, registration, chunk, coverage, limitations } = input;
77
+ for (const moduleValue of registration.modules) {
78
+ const module = context.accumulator.addNode({
79
+ kind: "javascript-module",
80
+ identity: artifactLocalIdentity(file.sha256, `${registration.runtime}:module`, moduleValue.module_key),
81
+ observations: [
82
+ {
83
+ label: moduleValue.module_key,
84
+ properties: {
85
+ path: file.path,
86
+ bundler: registration.bundler,
87
+ runtime: registration.runtime,
88
+ chunk_keys: registration.chunk_keys,
89
+ module_key: moduleValue.module_key,
90
+ factory_require_name: moduleValue.factory_require_name,
91
+ runtime_entry: registration.entry_module_keys.includes(moduleValue.module_key),
92
+ source_sha256: moduleValue.source_sha256,
93
+ structural_fingerprint_sha256: moduleValue.structural_fingerprint_sha256,
94
+ structural_fingerprint_algorithm: moduleValue.structural_fingerprint_algorithm,
95
+ structural_fingerprint_status: moduleValue.structural_fingerprint_status,
96
+ exports: moduleValue.exports,
97
+ exports_truncated: moduleValue.exports_truncated,
98
+ },
99
+ evidence: astObservationEvidence({
100
+ sha256: file.sha256,
101
+ path: file.path,
102
+ range: moduleValue.location,
103
+ operation: "extract-bundler-module",
104
+ coverage,
105
+ limitations,
106
+ }),
107
+ },
108
+ ],
109
+ });
110
+ context.moduleNodes.set(moduleLookupKey(file.path, moduleValue.module_key), module);
111
+ addAstContainsEdge(context, {
112
+ source: chunk,
113
+ target: module,
114
+ file,
115
+ range: moduleValue.location,
116
+ coverage,
117
+ properties: {
118
+ bundler: registration.bundler,
119
+ runtime: registration.runtime,
120
+ chunk_keys: registration.chunk_keys,
121
+ module_key: moduleValue.module_key,
122
+ },
123
+ });
124
+ }
125
+ };
126
+ const addBundlerRuntimeEdges = (context, record) => {
127
+ const { file, registration, chunk, coverage } = record;
128
+ for (const moduleKey of registration.entry_module_keys) {
129
+ const resolved = context.moduleNodes.get(moduleLookupKey(file.path, moduleKey));
130
+ const target = resolved ?? unresolvedBundlerModuleNode(context, record, moduleKey);
131
+ context.accumulator.addEdge({
132
+ source_node_id: chunk.node_id,
133
+ target_node_id: target.node_id,
134
+ relation: "loads",
135
+ properties: {
136
+ kind: "bundler-entry-module",
137
+ bundler: registration.bundler,
138
+ runtime: registration.runtime,
139
+ module_key: moduleKey,
140
+ resolution_status: resolved === undefined ? "not-found" : "resolved",
141
+ },
142
+ evidence: staticInferenceEvidence({
143
+ sha256: file.sha256,
144
+ path: file.path,
145
+ range: registration.location,
146
+ operation: "resolve-bundler-entry-module",
147
+ coverage,
148
+ confidence: resolved === undefined ? "low" : "high",
149
+ }),
150
+ });
151
+ }
152
+ for (const chunkKey of registration.async_chunk_keys) {
153
+ const lookup = chunkLookupKey(file.path, registration.runtime, chunkKey);
154
+ const resolved = context.chunkNodes.get(lookup);
155
+ const target = resolved ?? unresolvedBundlerChunkNode(context, record, chunkKey);
156
+ context.accumulator.addEdge({
157
+ source_node_id: chunk.node_id,
158
+ target_node_id: target.node_id,
159
+ relation: "imports",
160
+ properties: {
161
+ kind: "bundler-async-chunk",
162
+ bundler: registration.bundler,
163
+ runtime: registration.runtime,
164
+ chunk_key: chunkKey,
165
+ resolution_status: resolved === undefined ? "not-found" : "resolved",
166
+ },
167
+ evidence: staticInferenceEvidence({
168
+ sha256: file.sha256,
169
+ path: file.path,
170
+ range: registration.location,
171
+ operation: "resolve-bundler-async-chunk",
172
+ coverage,
173
+ confidence: resolved === undefined ? "low" : "high",
174
+ }),
175
+ });
176
+ }
177
+ };
178
+ const unresolvedBundlerModuleNode = (context, record, moduleKey) => context.accumulator.addNode({
179
+ kind: "javascript-module",
180
+ identity: artifactLocalIdentity(record.file.sha256, `${record.registration.runtime}:unresolved-entry-module`, moduleKey),
181
+ observations: [
182
+ {
183
+ label: moduleKey,
184
+ properties: {
185
+ semantic_role: "bundler-module-reference",
186
+ bundler: record.registration.bundler,
187
+ runtime: record.registration.runtime,
188
+ module_key: moduleKey,
189
+ resolution_status: "not-found",
190
+ },
191
+ evidence: staticInferenceEvidence({
192
+ sha256: record.file.sha256,
193
+ path: record.file.path,
194
+ range: record.registration.location,
195
+ operation: "retain-unresolved-bundler-entry-module",
196
+ coverage: record.coverage,
197
+ confidence: "low",
198
+ }),
199
+ },
200
+ ],
201
+ });
202
+ const unresolvedBundlerChunkNode = (context, record, chunkKey) => context.accumulator.addNode({
203
+ kind: "javascript-chunk",
204
+ identity: artifactLocalIdentity(record.file.sha256, `${record.registration.runtime}:unresolved-async-chunk`, chunkKey),
205
+ observations: [
206
+ {
207
+ label: chunkKey,
208
+ properties: {
209
+ semantic_role: "bundler-chunk-reference",
210
+ bundler: record.registration.bundler,
211
+ runtime: record.registration.runtime,
212
+ chunk_key: chunkKey,
213
+ resolution_status: "not-found",
214
+ },
215
+ evidence: staticInferenceEvidence({
216
+ sha256: record.file.sha256,
217
+ path: record.file.path,
218
+ range: record.registration.location,
219
+ operation: "retain-unresolved-bundler-async-chunk",
220
+ coverage: record.coverage,
221
+ confidence: "low",
222
+ }),
223
+ },
224
+ ],
225
+ });
@@ -1,6 +1,6 @@
1
- import { posix } from "node:path";
2
1
  import { JavaScriptArtifactGraphAccumulator } from "./JavaScriptArtifactGraphAccumulator.js";
3
2
  import { artifactObservationEvidence, astObservationEvidence, completeReconstructionCoverage, partialReconstructionCoverage, staticInferenceEvidence, unavailableAstEvidence, } from "./JavaScriptArtifactGraphEvidence.js";
3
+ import { resolveArtifactPathByContext, } from "./JavaScriptArtifactPathResolution.js";
4
4
  /** Add a direct artifact-containment edge. */
5
5
  export const addArtifactContainsEdge = (context, input) => {
6
6
  context.accumulator.addEdge({
@@ -103,16 +103,27 @@ export const artifactLocalIdentity = (sha256, namespace, key) => ({
103
103
  namespace: namespace.slice(0, 512),
104
104
  key: key.slice(0, 4_096),
105
105
  });
106
+ /** Select the graph node kind for one inventoried relevant file. */
107
+ export const artifactFileNodeKind = (kind) => kind === "javascript"
108
+ ? "javascript-asset"
109
+ : kind === "source-map"
110
+ ? "source-map"
111
+ : kind === "native-addon"
112
+ ? "native-addon"
113
+ : "artifact";
106
114
  /** Create or merge an Electron role inferred from static syntax or metadata. */
107
115
  export const createElectronRoleNode = (context, input) => context.accumulator.addNode({
108
116
  kind: input.kind,
109
- identity: artifactLocalIdentity(input.anchor.sha256, "electron-role", `${input.kind}:${input.resolvedPath ?? input.declaredPath}`),
117
+ identity: artifactLocalIdentity(input.anchor.sha256, "electron-role", `${input.kind}:${input.resolution.resolution_context}:${input.resolution.resolved_path ?? input.resolution.declared_path}`),
110
118
  observations: [
111
119
  {
112
- label: input.resolvedPath ?? input.declaredPath,
120
+ label: input.resolution.resolved_path ?? input.resolution.declared_path,
113
121
  properties: {
114
- declared_path: input.declaredPath,
115
- resolved_path: input.resolvedPath,
122
+ declared_path: input.resolution.declared_path,
123
+ resolution_context: input.resolution.resolution_context,
124
+ resolved_path: input.resolution.resolved_path,
125
+ resolution_status: input.resolution.resolution_status,
126
+ limitations: input.resolution.limitations,
116
127
  mechanism: input.mechanism,
117
128
  },
118
129
  evidence: staticInferenceEvidence({
@@ -120,6 +131,7 @@ export const createElectronRoleNode = (context, input) => context.accumulator.ad
120
131
  path: input.anchor.path,
121
132
  operation: "discover-electron-entrypoint",
122
133
  coverage: input.coverage ?? completeReconstructionCoverage(),
134
+ limitations: input.resolution.limitations,
123
135
  ...(input.range === undefined ? {} : { range: input.range }),
124
136
  }),
125
137
  },
@@ -127,59 +139,48 @@ export const createElectronRoleNode = (context, input) => context.accumulator.ad
127
139
  });
128
140
  /** Resolve an Electron role to an inventoried asset when available. */
129
141
  export const linkElectronRoleToAsset = (context, input) => {
130
- if (input.path === null)
142
+ const path = input.resolution.resolved_path;
143
+ if (path === null)
131
144
  return;
132
- const asset = context.assetNodes.get(input.path) ?? context.fileNodes.get(input.path);
145
+ const asset = context.assetNodes.get(path) ?? context.fileNodes.get(path);
133
146
  if (asset === undefined)
134
147
  return;
135
148
  context.accumulator.addEdge({
136
149
  source_node_id: input.role.node_id,
137
150
  target_node_id: asset.node_id,
138
151
  relation: "maps_to",
139
- properties: { resolved_path: input.path },
152
+ properties: {
153
+ declared_path: input.resolution.declared_path,
154
+ resolution_context: input.resolution.resolution_context,
155
+ resolved_path: path,
156
+ resolution_status: input.resolution.resolution_status,
157
+ },
140
158
  evidence: staticInferenceEvidence({
141
159
  sha256: input.anchor.sha256,
142
160
  path: input.anchor.path,
143
161
  operation: "resolve-electron-entrypoint",
144
162
  coverage: input.coverage ?? completeReconstructionCoverage(),
163
+ limitations: input.resolution.limitations,
145
164
  ...(input.range === undefined ? {} : { range: input.range }),
146
165
  }),
147
166
  });
148
167
  };
149
168
  /** Deterministic key for one recovered module inside a bundle asset. */
150
169
  export const moduleLookupKey = (path, moduleKey) => `${path}\0${moduleKey}`;
170
+ /** Deterministic key for one recovered bundler chunk inside a bundle asset. */
171
+ export const chunkLookupKey = (path, runtime, chunkKey) => `${path}\0${runtime}\0${chunkKey}`;
151
172
  /** Resolve a relative static specifier without escaping the artifact root. */
152
- export const resolveArtifactPath = (specifier, sourcePath, files) => {
153
- const clean = specifier.split("#", 1)[0]?.split("?", 1)[0] ?? "";
154
- if (clean === "" || /^[A-Za-z][A-Za-z+.-]*:/u.test(clean))
155
- return null;
156
- if (!clean.startsWith(".") && !clean.startsWith("/"))
157
- return null;
158
- const candidate = posix.normalize(clean.startsWith("/")
159
- ? clean.slice(1)
160
- : posix.join(posix.dirname(sourcePath), clean));
161
- if (candidate === ".." || candidate.startsWith("../"))
162
- return null;
163
- for (const path of pathCandidates(candidate))
164
- if (files.has(path))
165
- return path;
166
- return null;
167
- };
173
+ export const resolveArtifactPath = (specifier, sourcePath, files, moduleKind) => resolveArtifactPathByContext({
174
+ declaredPath: specifier,
175
+ sourcePath,
176
+ context: "module-specifier",
177
+ files,
178
+ ...(moduleKind === undefined ? {} : { moduleKind }),
179
+ }).resolved_path;
168
180
  /** Locate the recovered module that owns a static finding. */
169
181
  export const sourceNodeFor = (context, path, moduleKey) => moduleKey === null
170
- ? undefined
182
+ ? context.sourceModuleNodes.get(path)
171
183
  : context.moduleNodes.get(moduleLookupKey(path, moduleKey));
172
- const pathCandidates = (path) => [
173
- path,
174
- `${path}.js`,
175
- `${path}.mjs`,
176
- `${path}.cjs`,
177
- `${path}.ts`,
178
- `${path}.tsx`,
179
- `${path}.node`,
180
- posix.join(path, "index.js"),
181
- posix.join(path, "index.ts"),
182
- ];
183
184
  /** Named AST budgets committed to graph evidence coverage. */
184
185
  export const javaScriptAnalysisLimits = (input) => [
185
186
  {
@@ -1,5 +1,6 @@
1
- import { addArtifactContainsEdge, addStaticInferenceEdge, createElectronRoleNode, resolveArtifactPath, } from "./JavaScriptArtifactGraphContext.js";
1
+ import { addArtifactContainsEdge, addStaticInferenceEdge, createElectronRoleNode, linkElectronRoleToAsset, } from "./JavaScriptArtifactGraphContext.js";
2
2
  import { artifactObservationEvidence, completeReconstructionCoverage, partialReconstructionCoverage, } from "./JavaScriptArtifactGraphEvidence.js";
3
+ import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
3
4
  /** Project HTML renderer entrypoints and their local script assets. */
4
5
  export const addJavaScriptHtmlRoles = (context) => {
5
6
  const byHtml = new Map();
@@ -11,11 +12,17 @@ export const addJavaScriptHtmlRoles = (context) => {
11
12
  const html = context.filesByPath.get(htmlPath);
12
13
  if (html === undefined)
13
14
  continue;
15
+ const roleResolution = {
16
+ declared_path: html.path,
17
+ resolution_context: "html-reference",
18
+ resolved_path: html.path,
19
+ resolution_status: "resolved",
20
+ limitations: [],
21
+ };
14
22
  const role = createElectronRoleNode(context, {
15
23
  kind: "electron-renderer",
16
24
  anchor: html,
17
- declaredPath: html.path,
18
- resolvedPath: html.path,
25
+ resolution: roleResolution,
19
26
  mechanism: "html-script-entrypoint",
20
27
  });
21
28
  addArtifactContainsEdge(context, {
@@ -24,8 +31,20 @@ export const addJavaScriptHtmlRoles = (context) => {
24
31
  file: html,
25
32
  operation: "discover-html-renderer",
26
33
  });
34
+ linkElectronRoleToAsset(context, {
35
+ role,
36
+ anchor: html,
37
+ resolution: roleResolution,
38
+ });
27
39
  for (const script of scripts) {
28
- const resolvedPath = resolveArtifactPath(script.script_path, html.path, context.filesByPath);
40
+ const resolution = resolveArtifactPathByContext({
41
+ declaredPath: script.script_path,
42
+ sourcePath: html.path,
43
+ context: "html-reference",
44
+ files: context.filesByPath,
45
+ htmlBaseHref: script.base_href,
46
+ });
47
+ const resolvedPath = resolution.resolved_path;
29
48
  if (resolvedPath === null)
30
49
  continue;
31
50
  const resolved = context.fileNodes.get(resolvedPath);
@@ -40,7 +59,11 @@ export const addJavaScriptHtmlRoles = (context) => {
40
59
  relation: "loads",
41
60
  properties: {
42
61
  script_path: script.script_path,
62
+ base_href: script.base_href,
63
+ resolution_context: resolution.resolution_context,
43
64
  resolved_path: resolvedPath,
65
+ resolution_status: resolution.resolution_status,
66
+ limitations: resolution.limitations,
44
67
  },
45
68
  });
46
69
  }
@@ -1,5 +1,6 @@
1
- import { addStaticInferenceEdge, artifactLocalIdentity, createElectronRoleNode, javascriptAnalysisCoverage, linkElectronRoleToAsset, moduleLookupKey, resolveArtifactPath, sourceNodeFor, } from "./JavaScriptArtifactGraphContext.js";
1
+ import { addStaticInferenceEdge, artifactLocalIdentity, chunkLookupKey, createElectronRoleNode, javascriptAnalysisCoverage, linkElectronRoleToAsset, moduleLookupKey, resolveArtifactPath, sourceNodeFor, } from "./JavaScriptArtifactGraphContext.js";
2
2
  import { astObservationEvidence, staticInferenceEvidence, } from "./JavaScriptArtifactGraphEvidence.js";
3
+ import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
3
4
  /** Project imports, workers, roles, endpoints, storage, and source-map links. */
4
5
  export const addJavaScriptStaticFindings = (context) => {
5
6
  for (const analyzed of context.analysis.files) {
@@ -25,7 +26,7 @@ const addReference = (context, input) => {
25
26
  input.asset;
26
27
  const resolved = input.value.specifier === null
27
28
  ? null
28
- : resolveReference(context, input.file.path, input.value.specifier);
29
+ : resolveReference(context, input.file.path, input.value);
29
30
  if (input.value.kind === "worker" || input.value.kind === "service-worker") {
30
31
  const worker = context.accumulator.addNode({
31
32
  kind: input.value.kind === "worker" ? "worker" : "service-worker",
@@ -155,12 +156,16 @@ const addStorage = (context, input) => {
155
156
  });
156
157
  };
157
158
  const addDiscoveredRole = (context, input) => {
158
- const resolvedPath = resolveArtifactPath(input.value.path, input.file.path, context.filesByPath);
159
+ const resolution = resolveArtifactPathByContext({
160
+ declaredPath: input.value.path,
161
+ sourcePath: input.file.path,
162
+ context: input.value.resolution_context,
163
+ files: context.filesByPath,
164
+ });
159
165
  const role = createElectronRoleNode(context, {
160
166
  kind: input.value.role === "preload" ? "electron-preload" : "electron-renderer",
161
167
  anchor: input.file,
162
- declaredPath: input.value.path,
163
- resolvedPath,
168
+ resolution,
164
169
  mechanism: input.value.mechanism,
165
170
  range: input.value.location,
166
171
  coverage: input.coverage,
@@ -179,7 +184,7 @@ const addDiscoveredRole = (context, input) => {
179
184
  linkElectronRoleToAsset(context, {
180
185
  role,
181
186
  anchor: input.file,
182
- path: resolvedPath,
187
+ resolution,
183
188
  range: input.value.location,
184
189
  coverage: input.coverage,
185
190
  });
@@ -228,7 +233,13 @@ const addSourceMapEdge = (context, input) => {
228
233
  },
229
234
  });
230
235
  };
231
- const resolveReference = (context, sourcePath, specifier) => {
236
+ const resolveReference = (context, sourcePath, reference) => {
237
+ const specifier = reference.specifier;
238
+ if (specifier === null)
239
+ return null;
240
+ const chunk = resolveBundlerChunkReference(context, sourcePath, reference);
241
+ if (chunk !== null)
242
+ return chunk;
232
243
  const module = context.moduleNodes.get(moduleLookupKey(sourcePath, specifier));
233
244
  if (module !== undefined) {
234
245
  const file = context.filesByPath.get(sourcePath);
@@ -236,13 +247,48 @@ const resolveReference = (context, sourcePath, specifier) => {
236
247
  ? null
237
248
  : { path: `${sourcePath}#module:${specifier}`, file, node: module };
238
249
  }
239
- const path = resolveArtifactPath(specifier, sourcePath, context.filesByPath);
250
+ const path = resolveArtifactPath(specifier, sourcePath, context.filesByPath, reference.kind === "require"
251
+ ? "require"
252
+ : reference.kind === "static-import" ||
253
+ reference.kind === "dynamic-import"
254
+ ? "import"
255
+ : undefined);
240
256
  if (path === null)
241
257
  return null;
242
258
  const file = context.filesByPath.get(path);
243
259
  const node = context.fileNodes.get(path);
244
260
  return file === undefined || node === undefined ? null : { path, file, node };
245
261
  };
262
+ const resolveBundlerChunkReference = (context, sourcePath, reference) => {
263
+ const key = reference.specifier?.startsWith("chunk:")
264
+ ? reference.specifier.slice("chunk:".length)
265
+ : null;
266
+ if (key === null)
267
+ return null;
268
+ const file = context.filesByPath.get(sourcePath);
269
+ if (file === undefined)
270
+ return null;
271
+ const registrations = context.analysis.files
272
+ .find(({ file: analyzedFile }) => analyzedFile.path === sourcePath)
273
+ ?.javascript?.bundler_registrations.filter(({ modules }) => reference.module_key === null
274
+ ? true
275
+ : modules.some(({ module_key: moduleKey }) => moduleKey === reference.module_key));
276
+ if (registrations === undefined)
277
+ return null;
278
+ const candidates = registrations.flatMap(({ runtime }) => {
279
+ const node = context.chunkNodes.get(chunkLookupKey(sourcePath, runtime, key));
280
+ return node === undefined ? [] : [node];
281
+ });
282
+ const unique = [
283
+ ...new Map(candidates.map((node) => [node.node_id, node])).values(),
284
+ ];
285
+ if (unique.length !== 1)
286
+ return null;
287
+ const node = unique[0];
288
+ return node === undefined
289
+ ? null
290
+ : { path: `${sourcePath}#chunk:${key}`, file, node };
291
+ };
246
292
  const unresolvedReferenceNode = (context, file, reference, coverage) => context.accumulator.addNode({
247
293
  kind: "javascript-module",
248
294
  identity: artifactLocalIdentity(file.sha256, "unresolved-reference", `${reference.kind}:${reference.specifier ?? reference.expression ?? "[dynamic]"}`),