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
@@ -0,0 +1,88 @@
1
+ import { readFile } from "node:fs/promises";
2
+ /** Safe diagnostics for a browser process that did not publish a CDP port. */
3
+ export class BrowserStartupError extends Error {
4
+ _tag = "BrowserStartupError";
5
+ failure;
6
+ executable;
7
+ elapsedMs;
8
+ exitCode;
9
+ signalCode;
10
+ stderr;
11
+ constructor(diagnostics, options) {
12
+ super(browserStartupMessage(diagnostics.failure, diagnostics), options);
13
+ this.failure = diagnostics.failure;
14
+ this.executable = diagnostics.executable;
15
+ this.elapsedMs = diagnostics.elapsedMs;
16
+ this.exitCode = diagnostics.exitCode;
17
+ this.signalCode = diagnostics.signalCode;
18
+ this.stderr = diagnostics.stderr;
19
+ }
20
+ }
21
+ /** Wait for Chrome's valid DevToolsActivePort or a terminal process outcome. */
22
+ export const waitForBrowserDevtoolsPort = async (options) => {
23
+ const timeoutMs = options.timeoutMs ?? 15_000;
24
+ const pollIntervalMs = options.pollIntervalMs ?? 25;
25
+ const startedAt = performance.now();
26
+ const terminal = childTermination(options, startedAt);
27
+ const deadline = startedAt + timeoutMs;
28
+ while (true) {
29
+ const terminalState = currentTerminalState(options.child);
30
+ if (terminalState !== null)
31
+ throw startupError(options, startedAt, terminalState);
32
+ const port = await readDevtoolsPort(options.activePortPath);
33
+ if (port !== null)
34
+ return port;
35
+ const remainingMs = deadline - performance.now();
36
+ if (remainingMs <= 0)
37
+ throw startupError(options, startedAt, { failure: "timeout" });
38
+ const outcome = await Promise.race([
39
+ terminal,
40
+ delay(Math.min(pollIntervalMs, remainingMs)).then(() => null),
41
+ ]);
42
+ if (outcome !== null)
43
+ throw outcome;
44
+ }
45
+ };
46
+ const childTermination = (options, startedAt) => new Promise((resolve) => {
47
+ options.child.once("error", (cause) => {
48
+ resolve(startupError(options, startedAt, {
49
+ failure: "spawn-error",
50
+ cause,
51
+ }));
52
+ });
53
+ options.child.once("exit", () => {
54
+ resolve(startupError(options, startedAt, options.child.signalCode === null
55
+ ? { failure: "exited" }
56
+ : { failure: "signalled" }));
57
+ });
58
+ });
59
+ const currentTerminalState = (child) => {
60
+ if (child.signalCode !== null)
61
+ return { failure: "signalled" };
62
+ if (child.exitCode !== null)
63
+ return { failure: "exited" };
64
+ return null;
65
+ };
66
+ const startupError = (options, startedAt, state) => new BrowserStartupError({
67
+ failure: state.failure,
68
+ executable: options.executable,
69
+ elapsedMs: Math.max(0, Math.round(performance.now() - startedAt)),
70
+ exitCode: options.child.exitCode,
71
+ signalCode: options.child.signalCode,
72
+ stderr: boundedStderr(options.stderr()),
73
+ }, state.failure === "spawn-error" ? { cause: state.cause } : undefined);
74
+ const readDevtoolsPort = async (path) => {
75
+ try {
76
+ const firstLine = (await readFile(path, "utf8")).trim().split("\n")[0];
77
+ const port = Number(firstLine);
78
+ return Number.isSafeInteger(port) && port > 0 && port <= 65_535
79
+ ? port
80
+ : null;
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ };
86
+ const boundedStderr = (stderr) => stderr.length <= 65_536 ? stderr : stderr.slice(-65_536);
87
+ const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
88
+ const browserStartupMessage = (failure, diagnostics) => `Chrome CDP startup ${failure}; executable=${diagnostics.executable}; elapsed_ms=${String(diagnostics.elapsedMs)}; exit_code=${diagnostics.exitCode === null ? "null" : String(diagnostics.exitCode)}; signal=${diagnostics.signalCode ?? "null"}; stderr=${diagnostics.stderr.length === 0 ? "<empty>" : diagnostics.stderr}`;
@@ -1,4 +1,5 @@
1
- import { allowedSanitizedUrl, boundedText, isHttpUrl, numberValue, recordValue, recordsValue, requiredRecord, stringValue, } from "./CdpCaptureValues.js";
1
+ import { allowedSanitizedUrl, boundedText, numberValue, recordValue, recordsValue, requiredRecord, stringValue, } from "./CdpCaptureValues.js";
2
+ import { exclusionReasonForUrl } from "./CdpCaptureEventHelpers.js";
2
3
  /** Normalize allowed frames while retaining a bounded prefix and total count. */
3
4
  export const captureFrames = (result, allowedOrigins, maximum, completeness) => {
4
5
  const root = recordValue(requiredRecord(result).frameTree);
@@ -121,7 +122,14 @@ export const captureDom = (result, allowedOrigins, input, completeness) => {
121
122
  .map((value) => indexedString(strings, value).slice(0, 256))
122
123
  .slice(0, 200),
123
124
  });
124
- const metadata = domMetadata(strings, attributeIndexes, documentUrl, nodeIndex, nodeName, allowedOrigins);
125
+ const metadata = domMetadata({
126
+ strings,
127
+ attributes: attributeIndexes,
128
+ documentUrl,
129
+ nodeIndex,
130
+ nodeName,
131
+ allowedOrigins,
132
+ });
125
133
  for (const url of metadata.urls) {
126
134
  if (urls.length >= input.limits.max_resources) {
127
135
  completeness?.truncate("metadata");
@@ -218,7 +226,8 @@ const numberArray = (value) => Array.isArray(value)
218
226
  return number === undefined ? [] : [number];
219
227
  })
220
228
  : [];
221
- const domMetadata = (strings, attributes, documentUrl, nodeIndex, nodeName, allowedOrigins) => {
229
+ const domMetadata = (options) => {
230
+ const { strings, attributes, documentUrl, nodeIndex, nodeName, allowedOrigins, } = options;
222
231
  const pairs = new Map();
223
232
  for (let index = 0; index + 1 < attributes.length; index += 2) {
224
233
  const name = indexedString(strings, attributes[index]).toLowerCase();
@@ -284,11 +293,6 @@ const boundedUtf8 = (value, maximumBytes) => {
284
293
  }
285
294
  return { text, bytes, truncated: true };
286
295
  };
287
- const exclusionReasonForUrl = (value) => value === undefined || value === ""
288
- ? "unattributed_origin"
289
- : isHttpUrl(value)
290
- ? "disallowed_origin"
291
- : "unsupported_url";
292
296
  const domUrlAttributes = [
293
297
  "href",
294
298
  "src",
@@ -0,0 +1,75 @@
1
+ import { inferJsonShape } from "../domain/jsonShape.js";
2
+ import { recordValue, stringValue, } from "./CdpCaptureValues.js";
3
+ import { decodeBase64, isJsonContentType } from "./CdpCaptureEventHelpers.js";
4
+ export const ingestResponseBodyShape = (state, requestId, value) => {
5
+ const result = recordValue(value);
6
+ const body = stringValue(result?.body);
7
+ if (body === undefined) {
8
+ invalidResponseBodyShape(state, requestId);
9
+ return;
10
+ }
11
+ const decoded = result?.base64Encoded === true
12
+ ? decodeBase64(body)?.toString("utf8")
13
+ : body;
14
+ if (decoded === undefined) {
15
+ invalidResponseBodyShape(state, requestId);
16
+ return;
17
+ }
18
+ const inferred = inferBodyShape(state, decoded);
19
+ updateResponseBodyShape(state, requestId, inferred.shape, inferred.truncated);
20
+ };
21
+ export const requestBodyShape = (state, request) => {
22
+ if (!state.input.include_json_body_shapes)
23
+ return { status: "not_approved", request: null, response: null };
24
+ const body = stringValue(request.postData);
25
+ if (!isJsonContentType(recordValue(request.headers)) || body === undefined)
26
+ return { status: "unavailable", request: null, response: null };
27
+ const inferred = inferBodyShape(state, body);
28
+ return {
29
+ status: inferred.truncated
30
+ ? "truncated"
31
+ : inferred.shape === null
32
+ ? "unavailable"
33
+ : "included",
34
+ request: inferred.shape,
35
+ response: null,
36
+ };
37
+ };
38
+ export const updateResponseBodyShape = (state, requestId, response, truncated) => {
39
+ const current = state.network.get(requestId);
40
+ if (current === undefined)
41
+ return;
42
+ const request = current.body_shapes.request;
43
+ const status = truncated || current.body_shapes.status === "truncated"
44
+ ? "truncated"
45
+ : response !== null
46
+ ? "included"
47
+ : request !== null
48
+ ? "partial"
49
+ : "unavailable";
50
+ state.network.set(requestId, {
51
+ ...current,
52
+ body_shapes: { status, request, response },
53
+ });
54
+ };
55
+ const inferBodyShape = (state, text) => {
56
+ const bytes = Buffer.byteLength(text);
57
+ const remaining = state.input.limits.max_total_json_body_bytes - state.jsonBodyBytes;
58
+ if (bytes > state.input.limits.max_json_body_bytes || bytes > remaining) {
59
+ state.completeness.truncate("json_body_shapes");
60
+ return { shape: null, truncated: true };
61
+ }
62
+ state.jsonBodyBytes += bytes;
63
+ const shape = inferJsonShape(text, {
64
+ maximumBytes: state.input.limits.max_json_body_bytes,
65
+ maximumNodes: state.input.limits.max_json_shape_nodes,
66
+ maximumDepth: state.input.limits.max_json_shape_depth,
67
+ });
68
+ if (shape?.truncated === true)
69
+ state.completeness.truncate("json_body_shapes");
70
+ return { shape, truncated: shape?.truncated === true };
71
+ };
72
+ const invalidResponseBodyShape = (state, requestId) => {
73
+ updateResponseBodyShape(state, requestId, null, false);
74
+ state.completeness.exclude("json_body_shapes", "invalid_protocol_value");
75
+ };
@@ -0,0 +1,336 @@
1
+ import { inferJsonShape } from "../domain/jsonShape.js";
2
+ import { safeResponseMetadata } from "./CdpSafeMetadata.js";
3
+ import { boundedSensitiveText } from "./SensitiveTextCapture.js";
4
+ import { allowedSanitizedUrl, boundedText, isHttpUrl, numberValue, recordValue, recordsValue, stringValue, } from "./CdpCaptureValues.js";
5
+ import { requestBodyShape } from "./CdpCaptureEventBodyShapes.js";
6
+ import { consolePrimitive, decodeBase64, exclusionReasonForUrl, executionContextKey, firstCallFrame, initiatorLocation, integerOrNull, } from "./CdpCaptureEventHelpers.js";
7
+ export const handleExecutionContextCreated = (state, params) => {
8
+ const context = recordValue(params.context);
9
+ const identifier = numberValue(context?.id);
10
+ const frameId = stringValue(recordValue(context?.auxData)?.frameId);
11
+ if (identifier === undefined ||
12
+ !Number.isSafeInteger(identifier) ||
13
+ frameId === undefined ||
14
+ frameId.length > 256)
15
+ return;
16
+ const key = String(identifier);
17
+ if (state.executionContextFrames.size >= state.input.limits.max_scripts &&
18
+ !state.executionContextFrames.has(key)) {
19
+ state.completeness.truncate("scripts");
20
+ return;
21
+ }
22
+ state.executionContextFrames.set(key, frameId);
23
+ };
24
+ export const handleExecutionContextDestroyed = (state, params) => {
25
+ const key = executionContextKey(params.executionContextId);
26
+ if (key !== null)
27
+ state.executionContextFrames.delete(key);
28
+ };
29
+ export const handleScriptParsed = (state, params) => {
30
+ const scriptId = stringValue(params.scriptId);
31
+ const rawUrl = stringValue(params.url) ?? "";
32
+ const sanitized = allowedSanitizedUrl(rawUrl, state.allowedOrigins);
33
+ if (scriptId === undefined || scriptId.length > 256) {
34
+ state.completeness.exclude("scripts", "invalid_protocol_value");
35
+ return;
36
+ }
37
+ if (sanitized === undefined) {
38
+ state.completeness.exclude("scripts", exclusionReasonForUrl(rawUrl));
39
+ return;
40
+ }
41
+ if (state.scripts.size >= state.input.limits.max_scripts &&
42
+ !state.scripts.has(scriptId)) {
43
+ state.completeness.drop("scripts");
44
+ return;
45
+ }
46
+ const sourceMap = sourceMapForScript(params.sourceMapURL, rawUrl, state);
47
+ const script = {
48
+ scriptId,
49
+ rawUrl,
50
+ url: sanitized.url,
51
+ origin: sanitized.origin,
52
+ hash: (stringValue(params.hash) ?? "").slice(0, 512),
53
+ length: Math.max(0, Math.min(Number.MAX_SAFE_INTEGER, Math.trunc(numberValue(params.length) ?? 0))),
54
+ isModule: params.isModule === true,
55
+ language: boundedText(params.scriptLanguage, 100),
56
+ sourceMapUrl: sourceMap.sanitized,
57
+ sourceMapRawUrl: sourceMap.raw,
58
+ executionContextKey: executionContextKey(params.executionContextId),
59
+ };
60
+ state.scripts.set(scriptId, script);
61
+ };
62
+ const sourceMapForScript = (value, scriptUrl, state) => {
63
+ const declaredUrl = stringValue(value);
64
+ if (declaredUrl === undefined || declaredUrl === "")
65
+ return { sanitized: null, raw: null };
66
+ let rawUrl;
67
+ try {
68
+ rawUrl = new URL(declaredUrl, scriptUrl).href;
69
+ }
70
+ catch {
71
+ state.completeness.exclude("source_maps", "unsupported_url");
72
+ return { sanitized: null, raw: null };
73
+ }
74
+ const sanitized = allowedSanitizedUrl(rawUrl, state.allowedOrigins);
75
+ if (sanitized !== undefined)
76
+ return { sanitized: sanitized.url, raw: rawUrl };
77
+ state.completeness.exclude("source_maps", exclusionReasonForUrl(rawUrl));
78
+ return { sanitized: null, raw: null };
79
+ };
80
+ export const handleRequestWillBeSent = (state, params) => {
81
+ const requestId = stringValue(params.requestId);
82
+ const request = recordValue(params.request);
83
+ if (requestId === undefined || requestId.length > 256) {
84
+ state.completeness.exclude("network_requests", "invalid_protocol_value");
85
+ return;
86
+ }
87
+ if (request === undefined) {
88
+ state.completeness.exclude("network_requests", "invalid_protocol_value");
89
+ state.network.delete(requestId);
90
+ return;
91
+ }
92
+ const sanitized = allowedSanitizedUrl(request.url, state.allowedOrigins);
93
+ if (sanitized === undefined) {
94
+ state.completeness.exclude("network_requests", exclusionReasonForUrl(stringValue(request.url)));
95
+ state.network.delete(requestId);
96
+ return;
97
+ }
98
+ if (state.network.size >= state.input.limits.max_network_events &&
99
+ !state.network.has(requestId)) {
100
+ state.completeness.drop("network_requests");
101
+ return;
102
+ }
103
+ const initiator = recordValue(params.initiator);
104
+ const initiatorFrame = initiatorLocation(initiator);
105
+ const rawInitiatorUrl = stringValue(initiatorFrame?.url);
106
+ const initiatorUrl = allowedSanitizedUrl(rawInitiatorUrl, state.allowedOrigins);
107
+ if (rawInitiatorUrl !== undefined && initiatorUrl === undefined)
108
+ state.completeness.exclude("network_initiators", exclusionReasonForUrl(rawInitiatorUrl));
109
+ state.network.set(requestId, {
110
+ request_id: requestId,
111
+ url: sanitized.url,
112
+ origin: sanitized.origin ?? "",
113
+ method: (stringValue(request.method) ?? "GET").slice(0, 32),
114
+ resource_type: boundedText(params.type, 100),
115
+ status: null,
116
+ mime_type: null,
117
+ encoded_data_length: null,
118
+ initiator: {
119
+ type: (stringValue(initiator?.type) ?? "other").slice(0, 100),
120
+ url: initiatorUrl?.url ?? null,
121
+ line: integerOrNull(initiatorFrame?.lineNumber),
122
+ column: integerOrNull(initiatorFrame?.columnNumber),
123
+ },
124
+ body_shapes: requestBodyShape(state, request),
125
+ });
126
+ };
127
+ export const handleResponseReceived = (state, params) => {
128
+ const requestId = stringValue(params.requestId);
129
+ if (requestId === undefined || requestId.length > 256) {
130
+ state.completeness.exclude("network_requests", "invalid_protocol_value");
131
+ return;
132
+ }
133
+ const current = state.network.get(requestId);
134
+ const response = recordValue(params.response);
135
+ if (current === undefined)
136
+ return;
137
+ const sanitized = allowedSanitizedUrl(response?.url, state.allowedOrigins);
138
+ if (response === undefined || sanitized === undefined) {
139
+ state.completeness.exclude("network_requests", response === undefined
140
+ ? "invalid_protocol_value"
141
+ : exclusionReasonForUrl(stringValue(response.url)));
142
+ state.network.delete(requestId);
143
+ return;
144
+ }
145
+ state.network.set(requestId, {
146
+ ...current,
147
+ status: numberValue(response.status) ?? null,
148
+ mime_type: boundedText(response.mimeType, 256),
149
+ });
150
+ if (state.responseMetadata.length >= state.input.limits.max_network_events) {
151
+ state.completeness.truncate("metadata");
152
+ return;
153
+ }
154
+ const metadata = safeResponseMetadata(requestId, sanitized.url, response, state.allowedOrigins);
155
+ state.responseMetadata.push(metadata.response);
156
+ state.agentHints.push(...metadata.agentHints);
157
+ };
158
+ export const handleLoadingFinished = (state, params) => {
159
+ const requestId = stringValue(params.requestId);
160
+ if (requestId === undefined || requestId.length > 256)
161
+ return;
162
+ const current = state.network.get(requestId);
163
+ if (current === undefined)
164
+ return;
165
+ state.network.set(requestId, {
166
+ ...current,
167
+ encoded_data_length: Math.max(0, numberValue(params.encodedDataLength) ?? 0),
168
+ });
169
+ };
170
+ export const handleConsoleAPICalled = (state, params) => {
171
+ const frame = firstCallFrame(recordValue(params.stackTrace));
172
+ if (frame === undefined) {
173
+ state.completeness.exclude("console_events", "unattributed_origin");
174
+ return;
175
+ }
176
+ const source = allowedSanitizedUrl(frame.url, state.allowedOrigins);
177
+ if (source === undefined) {
178
+ state.completeness.exclude("console_events", exclusionReasonForUrl(stringValue(frame.url)));
179
+ return;
180
+ }
181
+ if (state.console.length >= state.input.limits.max_console_events) {
182
+ state.completeness.drop("console_events");
183
+ return;
184
+ }
185
+ const arguments_ = recordsValue(params.args);
186
+ state.console.push({
187
+ type: (stringValue(params.type) ?? "unknown").slice(0, 100),
188
+ timestamp: numberValue(params.timestamp) ?? 0,
189
+ argument_types: arguments_
190
+ .map((argument) => (stringValue(argument.type) ?? "unknown").slice(0, 100))
191
+ .slice(0, 100),
192
+ url: source.url,
193
+ line: integerOrNull(frame.lineNumber),
194
+ column: integerOrNull(frame.columnNumber),
195
+ text_capture: captureConsoleText(state, arguments_),
196
+ });
197
+ };
198
+ const captureConsoleText = (state, arguments_) => {
199
+ if (!state.input.include_console_text)
200
+ return {
201
+ status: "not_approved",
202
+ values: [],
203
+ retained_bytes: 0,
204
+ truncated_values: 0,
205
+ };
206
+ const values = [];
207
+ let retainedBytes = 0;
208
+ let truncatedValues = 0;
209
+ for (const [argumentIndex, argument] of arguments_.entries()) {
210
+ const primitive = consolePrimitive(argument);
211
+ if (primitive === undefined)
212
+ continue;
213
+ const remaining = state.input.limits.max_total_console_text_bytes - state.consoleTextBytes;
214
+ if (remaining <= 0) {
215
+ truncatedValues += 1;
216
+ continue;
217
+ }
218
+ const bounded = boundedSensitiveText(primitive.text, Math.min(state.input.limits.max_console_text_field_bytes, remaining));
219
+ values.push({
220
+ argument_index: argumentIndex,
221
+ type: primitive.type,
222
+ text: bounded.text,
223
+ });
224
+ retainedBytes += bounded.bytes;
225
+ state.consoleTextBytes += bounded.bytes;
226
+ if (bounded.truncated)
227
+ truncatedValues += 1;
228
+ }
229
+ if (truncatedValues > 0)
230
+ state.completeness.truncate("console_text");
231
+ return {
232
+ status: truncatedValues > 0 ? "truncated" : "included",
233
+ values,
234
+ retained_bytes: retainedBytes,
235
+ truncated_values: truncatedValues,
236
+ };
237
+ };
238
+ export const handleWebSocketFrame = (state, params, direction) => {
239
+ const requestId = stringValue(params.requestId);
240
+ if (requestId === undefined ||
241
+ requestId.length > 256 ||
242
+ (!state.network.has(requestId) && !state.allowedWebSockets.has(requestId)))
243
+ return;
244
+ if (state.websockets.length >= state.input.limits.max_websocket_events) {
245
+ state.completeness.drop("websocket_frames");
246
+ return;
247
+ }
248
+ const response = recordValue(params.response);
249
+ const payload = stringValue(response?.payloadData) ?? "";
250
+ const opcode = Math.max(0, Math.trunc(numberValue(response?.opcode) ?? 0));
251
+ const decoded = opcode === 1 ? undefined : decodeBase64(payload);
252
+ if (opcode !== 1 && decoded === undefined)
253
+ state.completeness.exclude("websocket_frames", "invalid_protocol_value");
254
+ state.websockets.push({
255
+ request_id: requestId,
256
+ direction,
257
+ opcode,
258
+ payload_bytes: opcode === 1 ? Buffer.byteLength(payload) : (decoded?.byteLength ?? 0),
259
+ payload_shape: captureWebSocketShape(state, payload, opcode),
260
+ });
261
+ };
262
+ const captureWebSocketShape = (state, payload, opcode) => {
263
+ if (!state.input.include_websocket_shapes)
264
+ return null;
265
+ if (opcode !== 1)
266
+ return { format: "binary", json_shape: null, truncated: false };
267
+ const bytes = Buffer.byteLength(payload);
268
+ const remaining = state.input.limits.max_total_websocket_shape_bytes -
269
+ state.websocketShapeBytes;
270
+ if (bytes > state.input.limits.max_websocket_shape_bytes ||
271
+ bytes > remaining) {
272
+ state.completeness.truncate("websocket_shapes");
273
+ return { format: "text", json_shape: null, truncated: true };
274
+ }
275
+ state.websocketShapeBytes += bytes;
276
+ const shape = inferJsonShape(payload, {
277
+ maximumBytes: state.input.limits.max_websocket_shape_bytes,
278
+ maximumNodes: state.input.limits.max_json_shape_nodes,
279
+ maximumDepth: state.input.limits.max_json_shape_depth,
280
+ });
281
+ if (shape?.truncated === true)
282
+ state.completeness.truncate("websocket_shapes");
283
+ return {
284
+ format: shape === null ? "text" : "json",
285
+ json_shape: shape,
286
+ truncated: shape?.truncated === true,
287
+ };
288
+ };
289
+ export const handleWebSocketCreated = (state, params) => {
290
+ const requestId = stringValue(params.requestId);
291
+ const rawUrl = stringValue(params.url);
292
+ if (requestId === undefined || requestId.length > 256) {
293
+ state.completeness.exclude("websocket_connections", "invalid_protocol_value");
294
+ return;
295
+ }
296
+ if (rawUrl === undefined) {
297
+ state.completeness.exclude("websocket_connections", "invalid_protocol_value");
298
+ return;
299
+ }
300
+ let parsed;
301
+ try {
302
+ parsed = new URL(rawUrl);
303
+ }
304
+ catch {
305
+ state.completeness.exclude("websocket_connections", "unsupported_url");
306
+ return;
307
+ }
308
+ if (parsed.protocol === "ws:")
309
+ parsed.protocol = "http:";
310
+ else if (parsed.protocol === "wss:")
311
+ parsed.protocol = "https:";
312
+ else {
313
+ state.completeness.exclude("websocket_connections", "unsupported_url");
314
+ return;
315
+ }
316
+ if (!state.allowedOrigins.has(parsed.origin)) {
317
+ state.completeness.exclude("websocket_connections", "disallowed_origin");
318
+ return;
319
+ }
320
+ if (!state.allowedWebSockets.has(requestId) &&
321
+ state.allowedWebSockets.size >= state.input.limits.max_websocket_events) {
322
+ state.completeness.drop("websocket_connections");
323
+ return;
324
+ }
325
+ state.allowedWebSockets.add(requestId);
326
+ };
327
+ export const handleFrameNavigated = (state, params) => {
328
+ const frame = recordValue(params.frame);
329
+ if (stringValue(frame?.id) !== state.mainFrameId)
330
+ return;
331
+ state.navigationDuringCapture = true;
332
+ const rawUrl = stringValue(frame?.url);
333
+ if (isHttpUrl(rawUrl) &&
334
+ allowedSanitizedUrl(rawUrl, state.allowedOrigins) === undefined)
335
+ state.originViolation = true;
336
+ };
@@ -0,0 +1,71 @@
1
+ import { isHttpUrl, numberValue, recordValue, recordsValue, stringValue, } from "./CdpCaptureValues.js";
2
+ export const firstCallFrame = (value) => recordsValue(value?.callFrames)[0];
3
+ export const initiatorLocation = (initiator) => {
4
+ let stack = recordValue(initiator?.stack);
5
+ for (let depth = 0; depth < 32 && stack !== undefined; depth += 1) {
6
+ const frame = firstCallFrame(stack);
7
+ if (frame !== undefined)
8
+ return frame;
9
+ stack = recordValue(stack.parent);
10
+ }
11
+ return stringValue(initiator?.url) === undefined ? undefined : initiator;
12
+ };
13
+ export const exclusionReasonForUrl = (value) => value === undefined || value === ""
14
+ ? "unattributed_origin"
15
+ : isHttpUrl(value)
16
+ ? "disallowed_origin"
17
+ : "unsupported_url";
18
+ export const integerOrNull = (value) => {
19
+ const number = numberValue(value);
20
+ return number === undefined ? null : Math.max(0, Math.trunc(number));
21
+ };
22
+ export const executionContextKey = (value) => {
23
+ const identifier = numberValue(value);
24
+ return identifier !== undefined && Number.isSafeInteger(identifier)
25
+ ? String(identifier)
26
+ : null;
27
+ };
28
+ export const isJsonContentType = (headers) => {
29
+ if (headers === undefined)
30
+ return false;
31
+ for (const [name, value] of Object.entries(headers))
32
+ if (name.toLowerCase() === "content-type" &&
33
+ isJsonMediaType(stringValue(value)))
34
+ return true;
35
+ return false;
36
+ };
37
+ export const isJsonMediaType = (value) => {
38
+ const mediaType = value?.split(";", 1)[0]?.trim().toLowerCase();
39
+ return (mediaType === "application/json" || mediaType?.endsWith("+json") === true);
40
+ };
41
+ export const consolePrimitive = (value) => {
42
+ const type = stringValue(value.type);
43
+ switch (type) {
44
+ case "string":
45
+ return typeof value.value === "string"
46
+ ? { type, text: value.value }
47
+ : undefined;
48
+ case "boolean":
49
+ return typeof value.value === "boolean"
50
+ ? { type, text: String(value.value) }
51
+ : undefined;
52
+ case "number":
53
+ return typeof value.value === "number"
54
+ ? { type, text: String(value.value) }
55
+ : typeof value.unserializableValue === "string"
56
+ ? { type, text: value.unserializableValue }
57
+ : undefined;
58
+ case "bigint":
59
+ return typeof value.unserializableValue === "string"
60
+ ? { type, text: value.unserializableValue }
61
+ : undefined;
62
+ case "undefined":
63
+ return { type, text: "undefined" };
64
+ default:
65
+ return undefined;
66
+ }
67
+ };
68
+ const BASE64 = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/u;
69
+ export const decodeBase64 = (value) => value.length % 4 === 0 && BASE64.test(value)
70
+ ? Buffer.from(value, "base64")
71
+ : undefined;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};