rea-agents 5.0.0 → 6.0.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 (275) hide show
  1. package/README.md +157 -650
  2. package/bridge/mitmproxy/capture.py +210 -0
  3. package/bridge/native/rea_lldb_tracer.py +303 -0
  4. package/bridge/process/ProcessRunTokenReader.swift +263 -0
  5. package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
  6. package/bridge/pwndbg/core_context.py +101 -0
  7. package/bridge/pwndbg/launch.py +39 -0
  8. package/bridge/pwndbg/launch_impl.py +34 -0
  9. package/bridge/pwntools/decoder_runtime.py +59 -0
  10. package/bridge/pwntools/layout.py +39 -0
  11. package/bridge/pwntools/layout_impl.py +309 -0
  12. package/bridge/pwntools/recorded_crash.py +39 -0
  13. package/bridge/pwntools/recorded_crash_impl.py +143 -0
  14. package/dist/android/JadxProvider.js +1 -0
  15. package/dist/application/CapabilityInventory.js +21 -0
  16. package/dist/application/DirectAnalysis.js +97 -23
  17. package/dist/application/EvidenceBundleFiles.js +1 -1
  18. package/dist/application/ReconstructionObligationCandidates.js +5 -1
  19. package/dist/application/WebNetworkCapturePort.js +1 -0
  20. package/dist/application/WebNetworkCaptureService.js +132 -0
  21. package/dist/application/WindowsCapabilities.js +1 -1
  22. package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
  23. package/dist/application/binary/BinarySession.js +38 -10
  24. package/dist/application/binary/BinarySessionOpen.js +16 -3
  25. package/dist/application/binary/BinarySessionRecords.js +20 -0
  26. package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
  27. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
  28. package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
  29. package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
  30. package/dist/application/evm/EvmInterfacePort.js +1 -0
  31. package/dist/application/evm/EvmInterfaceService.js +62 -0
  32. package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
  33. package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
  34. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
  35. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
  36. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
  37. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
  38. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
  39. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
  40. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
  41. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
  42. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
  43. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
  44. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
  45. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
  46. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
  47. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
  48. package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
  49. package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
  50. package/dist/artifacts/ArtifactProvider.js +30 -3
  51. package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
  52. package/dist/artifacts/ZipArtifactReader.js +3 -0
  53. package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
  54. package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
  55. package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
  56. package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
  57. package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
  58. package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
  59. package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
  60. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
  61. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
  62. package/dist/artifacts/readStableArtifact.js +20 -4
  63. package/dist/browser/CdpCaptureEventHandlers.js +90 -2
  64. package/dist/browser/CdpCaptureEvents.js +9 -0
  65. package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
  66. package/dist/browser/history/CaptureFailures.js +101 -0
  67. package/dist/browser/history/CaptureFormatError.js +10 -0
  68. package/dist/browser/history/CaptureRedaction.js +65 -0
  69. package/dist/browser/history/CaptureRelease.js +12 -0
  70. package/dist/browser/history/HarCapture.js +165 -0
  71. package/dist/browser/history/HarCaptureAdapter.js +20 -0
  72. package/dist/browser/history/HarCaptureProcess.js +57 -0
  73. package/dist/browser/history/HarJson.js +91 -0
  74. package/dist/browser/history/HarSchema.js +72 -0
  75. package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
  76. package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
  77. package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
  78. package/dist/cli/artifactCommands.js +26 -1
  79. package/dist/cli/binaryDiagnosticsCommands.js +39 -0
  80. package/dist/cli/coreAnalysisCommands.js +2 -2
  81. package/dist/cli/coreBinaryCommands.js +8 -8
  82. package/dist/cli/electronCommands.js +16 -9
  83. package/dist/cli/evmCommands.js +24 -0
  84. package/dist/cli/firmwareCommands.js +6 -3
  85. package/dist/cli/javascriptApplicationAnalysis.js +16 -3
  86. package/dist/cli/jsonOutput.js +133 -0
  87. package/dist/cli/nativeCallCommands.js +35 -0
  88. package/dist/cli/processCommands.js +1 -1
  89. package/dist/cli/streamedJsonOutput.js +56 -0
  90. package/dist/cli/utilityCommands.js +2 -0
  91. package/dist/cli/webNetworkCaptureCommands.js +40 -0
  92. package/dist/cli.js +9 -3
  93. package/dist/cliBrowserScenarioCommands.js +2 -2
  94. package/dist/cliCommandNames.js +6 -0
  95. package/dist/cliJsonInput.js +30 -1
  96. package/dist/cliOutput.js +21 -6
  97. package/dist/composition/binaryDiagnostics.js +8 -0
  98. package/dist/composition/evm.js +4 -0
  99. package/dist/composition/webNetworkCaptures.js +6 -0
  100. package/dist/contracts/artifactToolContracts.js +6 -0
  101. package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
  102. package/dist/contracts/browserToolContracts.js +4 -3
  103. package/dist/contracts/errorSchemas.js +8 -1
  104. package/dist/contracts/evm/evmToolContracts.js +20 -0
  105. package/dist/contracts/investigationExamples.js +2 -2
  106. package/dist/contracts/managed/managedToolContracts.js +7 -3
  107. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
  108. package/dist/contracts/native/nativeToolContracts.js +17 -1
  109. package/dist/contracts/officialToolContracts.js +12 -9
  110. package/dist/contracts/process/processCaptureExample.js +2 -0
  111. package/dist/contracts/sessionLifecycleInputs.js +22 -5
  112. package/dist/contracts/sessionStatusContract.js +10 -2
  113. package/dist/contracts/sessionToolContracts.js +8 -1
  114. package/dist/contracts/sessionToolSchemas.js +10 -3
  115. package/dist/contracts/toolContractExamples.js +1 -1
  116. package/dist/contracts/toolContracts.js +6 -0
  117. package/dist/contracts/toolEffects.js +30 -0
  118. package/dist/contracts/toolOutputSchemaGroups.js +5 -1
  119. package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
  120. package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
  121. package/dist/domain/analysisErrorBase.js +15 -0
  122. package/dist/domain/analysisErrorCore.js +59 -5
  123. package/dist/domain/analysisErrorPresentation.js +29 -1
  124. package/dist/domain/analysisErrorProjection.js +52 -2
  125. package/dist/domain/analysisSnapshot.js +166 -2
  126. package/dist/domain/apple/dyldPaths.js +81 -0
  127. package/dist/domain/apple/dylibResolution.js +367 -0
  128. package/dist/domain/apple/dylibResolutionFindings.js +75 -0
  129. package/dist/domain/artifactPathSyntax.js +1 -1
  130. package/dist/domain/browserObservation.js +1 -1
  131. package/dist/domain/browserObservationSchemas.js +13 -0
  132. package/dist/domain/browserScenarioValues.js +9 -2
  133. package/dist/domain/callPathSchemas.js +1 -1
  134. package/dist/domain/canonicalDigest.js +13 -1
  135. package/dist/domain/changedBehavior.js +3 -3
  136. package/dist/domain/comparisonStatus.js +2 -30
  137. package/dist/domain/conformanceTrustGate.js +0 -16
  138. package/dist/domain/evidence.js +17 -5
  139. package/dist/domain/evm/evmInterface.js +63 -0
  140. package/dist/domain/explicitSensitiveFailure.js +92 -0
  141. package/dist/domain/explicitSensitiveValues.js +10 -0
  142. package/dist/domain/firmware/firmwareAnalysis.js +8 -1
  143. package/dist/domain/javascript/electronActiveObservation.js +9 -4
  144. package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
  145. package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
  146. package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
  147. package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
  148. package/dist/domain/jsonValue.js +4 -0
  149. package/dist/domain/localPath.js +2 -0
  150. package/dist/domain/native/binaryLayout.js +357 -0
  151. package/dist/domain/native/nativeCallObservation.js +164 -0
  152. package/dist/domain/native/objcSwiftMetadata.js +0 -39
  153. package/dist/domain/native/recordedCrash.js +266 -0
  154. package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
  155. package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
  156. package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
  157. package/dist/domain/process/processCaptureParsing.js +54 -0
  158. package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
  159. package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
  160. package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
  161. package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
  162. package/dist/domain/providerSelectionError.js +4 -2
  163. package/dist/domain/reconstructionVerification.js +3 -3
  164. package/dist/domain/reconstructionVerificationSchemas.js +1 -1
  165. package/dist/domain/residualUnknown.js +3 -2
  166. package/dist/domain/staticRuntimeCorrelation.js +3 -3
  167. package/dist/domain/webCaptureDiff.js +1 -0
  168. package/dist/domain/webNetworkCapture.js +118 -0
  169. package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
  170. package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
  171. package/dist/dotnet/ManagedMemberInspector.js +4 -2
  172. package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
  173. package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
  174. package/dist/dotnet/ManagedMemberRows.js +14 -3
  175. package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
  176. package/dist/dotnet/ManagedMetadataInventory.js +38 -28
  177. package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
  178. package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
  179. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
  180. package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
  181. package/dist/dotnet/ManagedPeReader.js +11 -0
  182. package/dist/evm/EvmBytecodeCarrier.js +21 -0
  183. package/dist/evm/EvmWorkerLimits.js +40 -0
  184. package/dist/evm/EvmoleFailures.js +126 -0
  185. package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
  186. package/dist/evm/EvmoleInterfaceProvider.js +273 -0
  187. package/dist/evm/EvmoleInterfaceWorker.js +87 -0
  188. package/dist/evm/EvmoleRelease.js +16 -0
  189. package/dist/firmware/FirmwareCommand.js +1 -0
  190. package/dist/generatedPackageMetadata.js +2 -2
  191. package/dist/ghidra/GhidraLauncher.js +1 -0
  192. package/dist/ghidra/GhidraProvider.js +2 -2
  193. package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
  194. package/dist/hopper/BridgeLauncher.js +2 -0
  195. package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
  196. package/dist/javascript/recovery/WakaruCommand.js +3 -0
  197. package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
  198. package/dist/native/CommandRunner.js +2 -1
  199. package/dist/native/LldbCallTracer.js +278 -0
  200. package/dist/native/NativeCallObservation.js +135 -0
  201. package/dist/native/NativeMacOSProvider.js +19 -3
  202. package/dist/native/NativeMacOSProviderMetadata.js +8 -3
  203. package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
  204. package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
  205. package/dist/native/pwntools/PwntoolsFailures.js +180 -0
  206. package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
  207. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
  208. package/dist/native/pwntools/PwntoolsRelease.js +19 -0
  209. package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
  210. package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
  211. package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
  212. package/dist/process/DarwinProcessRunTokenReader.js +294 -0
  213. package/dist/process/OwnedCommand.js +113 -0
  214. package/dist/process/ProcessOwnership.js +263 -33
  215. package/dist/process/ProcessOwnershipObservation.js +161 -24
  216. package/dist/process/ProviderProcess.js +30 -3
  217. package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
  218. package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
  219. package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
  220. package/dist/{application → process/capture}/ProcessHarness.js +195 -29
  221. package/dist/{application → process/capture}/ProcessSampling.js +2 -2
  222. package/dist/server/createServer.js +22 -0
  223. package/dist/server/promptCompletion.js +10 -3
  224. package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
  225. package/dist/server/registerEvidenceTools.js +1 -1
  226. package/dist/server/registerEvmTools.js +17 -0
  227. package/dist/server/registerProcessComparisonTool.js +1 -1
  228. package/dist/server/registerRecordedCrashTools.js +17 -0
  229. package/dist/server/registerSessionTools.js +2 -2
  230. package/dist/server/registerWebNetworkCaptureTool.js +28 -0
  231. package/dist/server/sessionAvailabilityPolicy.js +3 -0
  232. package/dist/server/sessionToolPolicies.js +1 -1
  233. package/dist/server/toolResult.js +1 -1
  234. package/native/windows/build/manifest.json +2 -2
  235. package/native/windows/build/rea-windows-x64.node +0 -0
  236. package/package.json +37 -9
  237. package/scripts/rea.mjs +11 -3
  238. package/skills/reverse-engineer-anything/SKILL.md +23 -3
  239. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
  240. package/third_party/README.md +60 -0
  241. package/third_party/evmole/LICENSE +21 -0
  242. package/third_party/evmole/README.md +22 -0
  243. package/third_party/pwndbg/LICENSE.md +21 -0
  244. package/third_party/pwndbg/README.md +23 -0
  245. package/third_party/pwntools/README.md +40 -0
  246. package/dist/application/AuthorizedArtifactInventory.js +0 -7
  247. package/dist/application/ProcessCaptureLifecycle.js +0 -299
  248. package/dist/application/StoreFileLock.js +0 -124
  249. package/dist/contracts/processCaptureExample.js +0 -2
  250. package/dist/domain/bytecodeProvider.js +0 -178
  251. package/dist/domain/customProtocolCapture.js +0 -137
  252. package/dist/domain/eventProcessTree.js +0 -225
  253. package/dist/domain/mobileApplicationInvestigation.js +0 -159
  254. package/dist/domain/packageAnalysis.js +0 -123
  255. package/dist/domain/peInspection.js +0 -197
  256. package/dist/domain/processCaptureParsing.js +0 -21
  257. package/dist/domain/protocolCapture.js +0 -217
  258. package/dist/domain/runtimeIdentification.js +0 -212
  259. package/dist/generatedMcpToolCatalog.js +0 -37
  260. /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
  261. /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
  262. /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
  263. /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
  264. /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
  265. /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
  266. /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
  267. /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
  268. /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
  269. /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
  270. /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
  271. /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
  272. /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
  273. /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
  274. /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
  275. /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
@@ -9,7 +9,7 @@ import { BrowserObservationError } from "../domain/browserObservationError.js";
9
9
  import { ProviderAdapterError } from "../domain/providerAdapterError.js";
10
10
  import { err, ok } from "../domain/result.js";
11
11
  import { cleanupOwnedProcessGroup, cleanupWindowsProcessTree, verifyNoTokenOwnedProcesses, } from "../process/ProcessOwnership.js";
12
- import { observeOwnedProcessLineage } from "../process/ProcessOwnershipObservation.js";
12
+ import { observeOwnedProcessLineage, prepareProcessOwnershipInspection, systemProcessOwnershipHost, } from "../process/ProcessOwnershipObservation.js";
13
13
  import { selectCapturedProcessGroupIds } from "../process/ProcessOwnershipProcessTree.js";
14
14
  import { runElectronActions, readApplicationState, runWithExecutionLimits, } from "./PlaywrightElectronActiveActions.js";
15
15
  const OPERATION = "capture_electron_scenario";
@@ -101,8 +101,12 @@ export class PlaywrightElectronActiveProvider {
101
101
  try {
102
102
  if (options.signal?.aborted === true)
103
103
  throw new BrowserObservationError(OPERATION, "cancelled");
104
+ await prepareProcessOwnershipInspection(options.signal);
104
105
  const startupDeadline = Date.now() + STARTUP_TIMEOUT_MS;
105
106
  const paths = await canonicalPaths(input);
107
+ const captureBaseline = await systemProcessOwnershipHost.captureBaseline?.(options.signal);
108
+ if (options.signal?.aborted)
109
+ throw new BrowserObservationError(OPERATION, "cancelled");
106
110
  application = await electron.launch({
107
111
  executablePath: paths.executable,
108
112
  cwd: paths.root,
@@ -122,13 +126,18 @@ export class PlaywrightElectronActiveProvider {
122
126
  expectedParentPid: process.pid,
123
127
  expectedCommand: paths.executable,
124
128
  sweepTokenOwnedProcesses: true,
129
+ ...(captureBaseline === undefined ? {} : { captureBaseline }),
125
130
  };
126
131
  const actions = await runElectronActions(application, input, options);
127
132
  const state = await runWithExecutionLimits(readApplicationState(application), options.signal);
128
133
  outcome = ok(createResult(paths, input, actions, state));
129
134
  }
130
135
  catch (cause) {
131
- outcome = err(providerError(cause));
136
+ outcome = err(options.signal?.aborted === true &&
137
+ (cause === options.signal.reason ||
138
+ (cause instanceof Error && cause.name === "AbortError"))
139
+ ? new BrowserObservationError(OPERATION, "cancelled", { cause })
140
+ : providerError(cause));
132
141
  }
133
142
  if (application !== undefined) {
134
143
  if (ownership !== undefined)
@@ -200,7 +209,7 @@ const cleanupElectronProcesses = async (ownership, lineage) => {
200
209
  return result;
201
210
  signaled ||= result.signaled;
202
211
  }
203
- const remaining = await verifyNoTokenOwnedProcesses(ownership.runId);
212
+ const remaining = await verifyNoTokenOwnedProcesses(ownership.runId, undefined, ownership.captureBaseline);
204
213
  if (!remaining.cleaned)
205
214
  return remaining;
206
215
  return { cleaned: true, signaled };
@@ -0,0 +1,101 @@
1
+ import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
2
+ import { AnalysisError } from "../../domain/analysisErrorBase.js";
3
+ import { AnalysisAccessDeniedError, AnalysisArtifactChangedError, AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisInputError, AnalysisOutputError, AnalysisTimeoutError, } from "../../domain/analysisErrorCore.js";
4
+ import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
5
+ import { ProviderCleanupError } from "../../domain/providerCleanupError.js";
6
+ import { WEB_NETWORK_CAPTURE_LIMITS, } from "../../domain/webNetworkCapture.js";
7
+ import { OwnedCommandFailure } from "../../process/OwnedCommand.js";
8
+ const OPERATION = "inspect_web_network_capture";
9
+ /** Preserve the origin and typed cause of a historical capture failure. */
10
+ export const historicalCaptureFailure = (input, cause, phase, options) => {
11
+ if (cause instanceof AnalysisError)
12
+ return cause;
13
+ if (cause instanceof OwnedCommandFailure) {
14
+ if (cause.cleanupFailure !== null)
15
+ return new ProviderCleanupError(input.format, cause.resources, {
16
+ reason: cause.cleanupFailure,
17
+ previous_error: {
18
+ failure_kind: cause.reason,
19
+ message: cause.message,
20
+ exit_code: cause.snapshot?.exitCode ?? null,
21
+ signal: cause.snapshot?.signal ?? null,
22
+ stdout: cause.snapshot?.stdout.text ?? null,
23
+ stderr: cause.snapshot?.stderr.text ?? null,
24
+ diagnostic_truncated: cause.snapshot?.diagnosticTruncated ?? null,
25
+ },
26
+ }, { operation: OPERATION, cause });
27
+ if (cause.reason === "cancelled")
28
+ return new AnalysisCancelledError(OPERATION);
29
+ if (cause.reason === "timeout")
30
+ return new AnalysisTimeoutError(OPERATION, WEB_NETWORK_CAPTURE_LIMITS.timeoutMs);
31
+ if (cause.reason === "output-limit")
32
+ return new AnalysisOutputError(OPERATION, cause.message);
33
+ }
34
+ if (options?.signal?.aborted)
35
+ return new AnalysisCancelledError(OPERATION);
36
+ if (phase === "capture-read" &&
37
+ cause instanceof ArtifactReaderFailure &&
38
+ cause.reason === "integrity")
39
+ return new AnalysisArtifactChangedError(OPERATION, input.capture_path, cause.message, { cause });
40
+ if (phase === "capture-read" &&
41
+ cause instanceof ArtifactReaderFailure &&
42
+ cause.reason === "cancelled")
43
+ return new AnalysisCancelledError(OPERATION);
44
+ if (phase === "capture-read" &&
45
+ cause instanceof ArtifactReaderFailure &&
46
+ (cause.reason === "limit" ||
47
+ cause.reason === "format" ||
48
+ cause.reason === "path"))
49
+ return new AnalysisInputError(OPERATION, { cause }, [
50
+ {
51
+ path: ["capture_path"],
52
+ reason: cause.reason === "limit" ? "out_of_range" : "invalid_format",
53
+ message: cause.reason === "limit"
54
+ ? `${cause.message}. Select a capture within the ${WEB_NETWORK_CAPTURE_LIMITS.inputBytes}-byte input budget.`
55
+ : cause.message,
56
+ ...(cause.reason === "limit"
57
+ ? {
58
+ expected: {
59
+ maximum_capture_bytes: WEB_NETWORK_CAPTURE_LIMITS.inputBytes,
60
+ },
61
+ }
62
+ : {}),
63
+ },
64
+ ]);
65
+ if (phase === "capture-read" &&
66
+ cause instanceof Error &&
67
+ "code" in cause &&
68
+ (cause.code === "EACCES" || cause.code === "EPERM"))
69
+ return new AnalysisAccessDeniedError(OPERATION, input.capture_path, cause.code, { cause });
70
+ if (phase === "capture-read" &&
71
+ cause instanceof Error &&
72
+ "code" in cause &&
73
+ ["ENOENT", "ENOTDIR"].includes(String(cause.code)))
74
+ return new AnalysisInputError(OPERATION, { cause }, [
75
+ {
76
+ path: ["capture_path"],
77
+ reason: "invalid_value",
78
+ message: `Selected capture could not be read (${String(cause.code)}): ${input.capture_path}.`,
79
+ },
80
+ ]);
81
+ return new ProviderAdapterError(input.format, OPERATION, {
82
+ cause,
83
+ diagnostics: {
84
+ phase,
85
+ capture_path: input.capture_path,
86
+ reason: cause instanceof Error ? cause.message : String(cause),
87
+ ...(cause instanceof ArtifactReaderFailure
88
+ ? { failure_kind: cause.reason }
89
+ : {}),
90
+ ...(cause instanceof OwnedCommandFailure && cause.snapshot !== null
91
+ ? {
92
+ exit_code: cause.snapshot.exitCode ?? null,
93
+ signal: cause.snapshot.signal ?? null,
94
+ stdout: cause.snapshot.stdout.text,
95
+ stderr: cause.snapshot.stderr.text,
96
+ diagnostic_truncated: cause.snapshot.diagnosticTruncated ?? false,
97
+ }
98
+ : {}),
99
+ },
100
+ });
101
+ };
@@ -0,0 +1,10 @@
1
+ /** Expected malformed, unsupported or oversized producer data at a historical decoder boundary. */
2
+ export class CaptureFormatError extends Error {
3
+ reason;
4
+ pointer;
5
+ constructor(reason, message, pointer = "") {
6
+ super(message);
7
+ this.reason = reason;
8
+ this.pointer = pointer;
9
+ }
10
+ }
@@ -0,0 +1,65 @@
1
+ const CREDENTIAL_HEADERS = new Set([
2
+ "authorization",
3
+ "proxy-authorization",
4
+ "cookie",
5
+ "set-cookie",
6
+ ]);
7
+ /** Literal redaction and structural authentication exclusions preserve unrelated local evidence. */
8
+ export class CaptureRedaction {
9
+ sensitiveValues;
10
+ redactions = [];
11
+ #sensitiveByteValues;
12
+ constructor(sensitiveValues) {
13
+ this.sensitiveValues = sensitiveValues;
14
+ // An unpaired surrogate has no UTF-8 representation. Buffer.from would
15
+ // invent replacement-character bytes and incorrectly discard real evidence.
16
+ this.#sensitiveByteValues = sensitiveValues.flatMap((value) => {
17
+ const encoded = Buffer.from(value, "utf8");
18
+ return encoded.toString("utf8") === value ? [encoded] : [];
19
+ });
20
+ }
21
+ /** Preserve exact text unless explicitly sensitive or known transport URL userinfo. */
22
+ text(value, pointer, transportUrl = false) {
23
+ let result = value;
24
+ if (transportUrl) {
25
+ const match = /^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?\/\/)([^/?#]*@)/i.exec(result);
26
+ if (match !== null) {
27
+ result = `${match[1]}${result.slice(match[0].length)}`;
28
+ this.redactions.push({ pointer, reason: "transport-credential" });
29
+ }
30
+ }
31
+ if (this.sensitiveValues.some((literal) => value.includes(literal) || result.includes(literal))) {
32
+ this.redactions.push({ pointer, reason: "explicit-sensitive-value" });
33
+ return null;
34
+ }
35
+ return result;
36
+ }
37
+ /** Remove a credential field's value without retaining its digest or encoded form. */
38
+ credential(pointer) {
39
+ this.redactions.push({ pointer, reason: "transport-credential" });
40
+ return null;
41
+ }
42
+ /** Known HAR credential surfaces; unknown extensions are ordinary caller-selected evidence. */
43
+ harCredential(pointer, parent) {
44
+ return ((/^\/(request|response)\/headers\/\d+\/value$/.test(pointer) &&
45
+ typeof parent.name === "string" &&
46
+ CREDENTIAL_HEADERS.has(parent.name.toLowerCase())) ||
47
+ /^\/(request|response)\/cookies\/\d+\/value$/.test(pointer));
48
+ }
49
+ /** Only actual request/redirect fields and known transport header URLs declare URL semantics. */
50
+ harTransportUrl(pointer, parent) {
51
+ return (pointer === "/request/url" ||
52
+ pointer === "/response/redirectURL" ||
53
+ (/^\/(request|response)\/headers\/\d+\/value$/.test(pointer) &&
54
+ typeof parent?.name === "string" &&
55
+ ["location", "referer", "origin"].includes(parent.name.toLowerCase())));
56
+ }
57
+ /** Preserve bytes unless an explicitly declared UTF-8 literal occurs in them. */
58
+ sensitiveBytes(bytes) {
59
+ return this.#sensitiveByteValues.some((value) => bytes.includes(value));
60
+ }
61
+ }
62
+ /** Escape a producer property for an RFC 6901 location. */
63
+ export const capturePointer = (parent, key) => `${parent}/${key.replaceAll("~", "~0").replaceAll("/", "~1")}`;
64
+ /** Structural record guard does not duck-type number/byte sidecar markers. */
65
+ export const captureObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -0,0 +1,12 @@
1
+ /** Exact upstream package profile for unchanged offline HAR validation and numeric parsing. */
2
+ export const HAR_CAPTURE_PROVIDER_IDENTITY = {
3
+ id: "har-schema/lossless-json",
4
+ name: "REA HAR capture adapter",
5
+ version: "har-schema@2.0.0;jsonc-parser@3.3.1;lossless-json@4.3.1",
6
+ };
7
+ /** Native executable profile verified with unchanged mitmproxy raw-state decoding. */
8
+ export const MITMPROXY_CAPTURE_PROVIDER_IDENTITY = {
9
+ id: "mitmproxy-native-tnetstring",
10
+ name: "REA offline native mitmproxy adapter",
11
+ version: "12.2.3",
12
+ };
@@ -0,0 +1,165 @@
1
+ import { createHash } from "node:crypto";
2
+ import { isSafeNumber, LosslessNumber } from "lossless-json";
3
+ import { parseHarJson } from "./HarJson.js";
4
+ import { WEB_NETWORK_CAPTURE_LIMITS, } from "../../domain/webNetworkCapture.js";
5
+ import { CaptureFormatError } from "./CaptureFormatError.js";
6
+ import { CaptureRedaction, captureObject, capturePointer, } from "./CaptureRedaction.js";
7
+ import { createHarValidator } from "./HarSchema.js";
8
+ import { HAR_CAPTURE_PROVIDER_IDENTITY } from "./CaptureRelease.js";
9
+ /** Decode a retained HAR with exact numeric lexemes and unchanged upstream schema validation. */
10
+ export const decodeHarCapture = (text, sensitiveValues) => {
11
+ const raw = parseHarJson(text);
12
+ const validation = projectHar(raw, [], false);
13
+ createHarValidator()(validation.reported);
14
+ if (!captureObject(raw) ||
15
+ !captureObject(raw.log) ||
16
+ !Array.isArray(raw.log.entries))
17
+ throw new CaptureFormatError("format", "HAR must contain log.entries.");
18
+ if (raw.log.version !== "1.2")
19
+ throw new CaptureFormatError("unsupported", "Only the pinned HAR 1.2 schema profile is supported.", "/log/version");
20
+ const entries = raw.log.entries;
21
+ const recordsExcluded = ["log", "entries"].some((key) => sensitiveValues.some((literal) => key.includes(literal)));
22
+ const container = projectHar({ ...raw, log: { ...raw.log, entries: null } }, sensitiveValues, true);
23
+ const records = entries.map((entry, ordinal) => {
24
+ // Validate each original record even when a structural ancestor excludes it.
25
+ const projected = projectHar(entry, sensitiveValues, true);
26
+ const projection = recordsExcluded
27
+ ? {
28
+ reported: null,
29
+ numeric_literals: [],
30
+ binary_fields: [],
31
+ redactions: [{ pointer: "", reason: "explicit-sensitive-value" }],
32
+ }
33
+ : projected;
34
+ return {
35
+ ordinal,
36
+ location: recordsExcluded
37
+ ? { kind: "unknown", reason: "explicit-sensitive-value" }
38
+ : { kind: "json-pointer", pointer: `/log/entries/${ordinal}` },
39
+ ...projection,
40
+ limitations: [
41
+ "HAR text is producer-decoded Unicode. Original body bytes remain unknown unless content explicitly uses valid base64; declared sizes are preserved independently of retained bytes. mitmproxy 12.2.3 SaveHar may report null optional postData.text; that null is retained and omitted only from the upstream validation copy.",
42
+ ...(recordsExcluded
43
+ ? [
44
+ "This record's payload and sidecars are excluded because its structural ancestor property was explicitly marked sensitive.",
45
+ ]
46
+ : []),
47
+ ],
48
+ };
49
+ });
50
+ return {
51
+ decoder: { ...HAR_CAPTURE_PROVIDER_IDENTITY },
52
+ container: {
53
+ reported: container.reported,
54
+ numeric_literals: container.numeric_literals,
55
+ redactions: container.redactions,
56
+ records_pointer: recordsExcluded ? null : "/log/entries",
57
+ },
58
+ total_records: records.length,
59
+ records,
60
+ };
61
+ };
62
+ const projectHar = (value, sensitiveValues, redact) => {
63
+ const redactor = new CaptureRedaction(sensitiveValues);
64
+ const numeric_literals = [];
65
+ const binary_fields = [];
66
+ const visit = (item, pointer, depth, parent) => {
67
+ if (depth > WEB_NETWORK_CAPTURE_LIMITS.depth)
68
+ throw new CaptureFormatError("input-limit", "HAR exceeds the 64-level complete-evidence nesting budget.", pointer);
69
+ if (redact &&
70
+ parent !== undefined &&
71
+ redactor.harCredential(pointer, parent))
72
+ return redactor.credential(pointer);
73
+ if (item instanceof LosslessNumber) {
74
+ if (redact && redactor.text(item.value, pointer) === null)
75
+ return null;
76
+ numeric_literals.push({
77
+ pointer,
78
+ producer_type: "json-number",
79
+ literal: item.value,
80
+ });
81
+ return isSafeNumber(item.value) ? Number(item.value) : null;
82
+ }
83
+ if (typeof item === "string") {
84
+ if (redact &&
85
+ pointer === "/response/content/text" &&
86
+ parent?.encoding === "base64") {
87
+ const bytes = decodeHarBase64(item, pointer);
88
+ const contentBase64 = bytes.toString("base64");
89
+ const sha256 = createHash("sha256").update(bytes).digest("hex");
90
+ const hidden = redactor.sensitiveBytes(bytes) ||
91
+ sensitiveValues.some((literal) => item.includes(literal) ||
92
+ contentBase64.includes(literal) ||
93
+ sha256.includes(literal));
94
+ binary_fields.push(hidden
95
+ ? {
96
+ pointer,
97
+ representation: "har-base64-content",
98
+ state: "redacted",
99
+ content_base64: null,
100
+ bytes: null,
101
+ sha256: null,
102
+ }
103
+ : {
104
+ pointer,
105
+ representation: "har-base64-content",
106
+ state: "retained",
107
+ content_base64: contentBase64,
108
+ bytes: bytes.length,
109
+ sha256,
110
+ });
111
+ if (hidden) {
112
+ redactor.redactions.push({
113
+ pointer,
114
+ reason: "explicit-sensitive-value",
115
+ });
116
+ return null;
117
+ }
118
+ }
119
+ return redact
120
+ ? redactor.text(item, pointer, redactor.harTransportUrl(pointer, parent))
121
+ : item;
122
+ }
123
+ if (item === null || typeof item === "boolean")
124
+ return item;
125
+ if (Array.isArray(item))
126
+ return item.map((child, index) => visit(child, capturePointer(pointer, String(index)), depth + 1));
127
+ if (captureObject(item))
128
+ return Object.fromEntries(Object.entries(item).flatMap(([key, child]) => {
129
+ if (redact &&
130
+ sensitiveValues.some((literal) => key.includes(literal))) {
131
+ const beforeNumbers = numeric_literals.length;
132
+ const beforeBinaries = binary_fields.length;
133
+ const beforeRedactions = redactor.redactions.length;
134
+ visit(child, capturePointer(pointer, key), depth + 1, item);
135
+ numeric_literals.splice(beforeNumbers);
136
+ binary_fields.splice(beforeBinaries);
137
+ redactor.redactions.splice(beforeRedactions);
138
+ redactor.redactions.push({
139
+ pointer,
140
+ reason: "explicit-sensitive-value",
141
+ scope: "property-name",
142
+ });
143
+ return [];
144
+ }
145
+ return [
146
+ [key, visit(child, capturePointer(pointer, key), depth + 1, item)],
147
+ ];
148
+ }));
149
+ throw new CaptureFormatError("format", "HAR parser returned a non-JSON value.", pointer);
150
+ };
151
+ return {
152
+ reported: visit(value, "", 0),
153
+ numeric_literals,
154
+ binary_fields,
155
+ redactions: redactor.redactions,
156
+ };
157
+ };
158
+ const decodeHarBase64 = (value, pointer) => {
159
+ const compact = value.replace(/[\t\n\r ]/g, "");
160
+ const bytes = Buffer.from(compact, "base64");
161
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(compact) ||
162
+ bytes.toString("base64") !== compact)
163
+ throw new CaptureFormatError("format", "HAR content declares base64 but its text is not valid canonical base64 (ASCII whitespace is allowed).", pointer);
164
+ return bytes;
165
+ };
@@ -0,0 +1,20 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { HAR_CAPTURE_PROVIDER_IDENTITY } from "./CaptureRelease.js";
3
+ /** HAR's parser/schema packages run in one bounded Node heap, without network or target execution. */
4
+ export class HarCaptureAdapter {
5
+ format = "har";
6
+ identity = HAR_CAPTURE_PROVIDER_IDENTITY;
7
+ async command(requestPath) {
8
+ return {
9
+ command: process.execPath,
10
+ arguments: [
11
+ "--max-old-space-size=192",
12
+ "--max-semi-space-size=8",
13
+ "--v8-pool-size=1",
14
+ "--single-threaded",
15
+ fileURLToPath(new URL("./HarCaptureProcess.js", import.meta.url)),
16
+ requestPath,
17
+ ],
18
+ };
19
+ }
20
+ }
@@ -0,0 +1,57 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { TextDecoder } from "node:util";
3
+ import { z } from "zod";
4
+ import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
5
+ import { WEB_NETWORK_CAPTURE_LIMITS } from "../../domain/webNetworkCapture.js";
6
+ import { CaptureFormatError } from "./CaptureFormatError.js";
7
+ import { decodeHarCapture } from "./HarCapture.js";
8
+ const requestSchema = z.strictObject({
9
+ snapshot_path: z.string(),
10
+ reply_path: z.string(),
11
+ sensitive_values: z.array(z.string().min(1)),
12
+ });
13
+ /** Decode untrusted HAR in an owned, bounded heap rather than the long-lived MCP process. */
14
+ const main = async () => {
15
+ const request = requestSchema.parse(JSON.parse(await readFile(process.argv[2] ?? "", "utf8")));
16
+ let reply;
17
+ try {
18
+ const snapshot = await readStableArtifact(request.snapshot_path, WEB_NETWORK_CAPTURE_LIMITS.inputBytes);
19
+ let text;
20
+ try {
21
+ text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(snapshot.bytes);
22
+ }
23
+ catch {
24
+ throw new CaptureFormatError("format", "HAR is not valid UTF-8 JSON.");
25
+ }
26
+ reply = {
27
+ ok: true,
28
+ value: decodeHarCapture(text, request.sensitive_values),
29
+ };
30
+ }
31
+ catch (cause) {
32
+ reply =
33
+ cause instanceof CaptureFormatError
34
+ ? {
35
+ ok: false,
36
+ reason: cause.reason,
37
+ message: cause.message,
38
+ pointer: cause.pointer,
39
+ }
40
+ : {
41
+ ok: false,
42
+ reason: "decoder",
43
+ message: `HAR decoder failed unexpectedly (${cause instanceof Error ? cause.name : "unknown exception"}).`,
44
+ pointer: "",
45
+ };
46
+ }
47
+ let text = JSON.stringify(reply);
48
+ if (Buffer.byteLength(text) > WEB_NETWORK_CAPTURE_LIMITS.outputBytes)
49
+ text = JSON.stringify({
50
+ ok: false,
51
+ reason: "limit",
52
+ message: "HAR reply exceeds the complete-evidence output budget.",
53
+ pointer: "",
54
+ });
55
+ await writeFile(request.reply_path, text, { flag: "wx", mode: 0o600 });
56
+ };
57
+ await main();
@@ -0,0 +1,91 @@
1
+ import { visit } from "jsonc-parser";
2
+ import { LosslessNumber } from "lossless-json";
3
+ import { WEB_NETWORK_CAPTURE_LIMITS } from "../../domain/webNetworkCapture.js";
4
+ import { CaptureFormatError } from "./CaptureFormatError.js";
5
+ /** Materialize upstream JSON events without duplicate folding, prototype setters or numeric rounding. */
6
+ export const parseHarJson = (text) => {
7
+ const frames = [];
8
+ let result;
9
+ const valuePointer = () => {
10
+ const parent = frames.at(-1);
11
+ if (parent === undefined)
12
+ return "";
13
+ if (parent.kind === "array")
14
+ return `${parent.pointer}/${parent.items.length}`;
15
+ return parent.pendingKey === undefined
16
+ ? parent.pointer
17
+ : `${parent.pointer}/${parent.pendingKey.replaceAll("~", "~0").replaceAll("/", "~1")}`;
18
+ };
19
+ const checkDepth = () => {
20
+ if (frames.length > WEB_NETWORK_CAPTURE_LIMITS.depth)
21
+ throw new CaptureFormatError("input-limit", "HAR exceeds the 64-level complete-evidence nesting budget.", valuePointer());
22
+ };
23
+ const append = (value) => {
24
+ const parent = frames.at(-1);
25
+ if (parent === undefined)
26
+ result = value;
27
+ else if (parent.kind === "array")
28
+ parent.items.push(value);
29
+ else {
30
+ if (parent.pendingKey === undefined)
31
+ throw new TypeError("JSON visitor omitted an object property identity.");
32
+ parent.members.set(parent.pendingKey, value);
33
+ delete parent.pendingKey;
34
+ }
35
+ };
36
+ visit(text, {
37
+ onObjectBegin: () => {
38
+ checkDepth();
39
+ frames.push({
40
+ kind: "object",
41
+ pointer: valuePointer(),
42
+ members: new Map(),
43
+ });
44
+ },
45
+ onObjectProperty: (key) => {
46
+ if (key === "__proto__")
47
+ throw new CaptureFormatError("unsupported", "HAR object contains a __proto__ member that the current JSON schema boundary cannot preserve.");
48
+ const frame = frames.at(-1);
49
+ if (frame?.kind !== "object")
50
+ throw new TypeError("JSON visitor emitted a property outside an object.");
51
+ if (frame.members.has(key))
52
+ throw new CaptureFormatError("format", "HAR JSON contains duplicate object members.");
53
+ frame.pendingKey = key;
54
+ },
55
+ onObjectEnd: () => {
56
+ const frame = frames.pop();
57
+ if (frame?.kind !== "object")
58
+ throw new TypeError("JSON visitor ended an unexpected object.");
59
+ append(Object.fromEntries(frame.members));
60
+ },
61
+ onArrayBegin: () => {
62
+ checkDepth();
63
+ frames.push({ kind: "array", pointer: valuePointer(), items: [] });
64
+ },
65
+ onArrayEnd: () => {
66
+ const frame = frames.pop();
67
+ if (frame?.kind !== "array")
68
+ throw new TypeError("JSON visitor ended an unexpected array.");
69
+ append(frame.items);
70
+ },
71
+ onLiteralValue: (value, offset, length) => {
72
+ checkDepth();
73
+ if (typeof value === "number")
74
+ append(new LosslessNumber(text.slice(offset, offset + length)));
75
+ else if (value === null ||
76
+ typeof value === "boolean" ||
77
+ typeof value === "string")
78
+ append(value);
79
+ else
80
+ throw new TypeError("JSON visitor emitted an unsupported literal.");
81
+ },
82
+ onError: () => {
83
+ throw new CaptureFormatError("format", "HAR is malformed JSON.");
84
+ },
85
+ }, {
86
+ disallowComments: true,
87
+ allowTrailingComma: false,
88
+ allowEmptyContent: false,
89
+ });
90
+ return result;
91
+ };
@@ -0,0 +1,72 @@
1
+ import { createRequire } from "node:module";
2
+ import { Ajv } from "ajv";
3
+ import { z } from "zod";
4
+ import { CaptureFormatError } from "./CaptureFormatError.js";
5
+ /** Validate using the unchanged, pinned upstream HAR 1.2 draft-06 schemas. */
6
+ export const createHarValidator = () => {
7
+ const require = createRequire(import.meta.url);
8
+ const schemas = z
9
+ .record(z.string(), z.record(z.string(), z.unknown()))
10
+ .parse(require("har-schema"));
11
+ const ajv = new Ajv({ strict: false, allErrors: false, ownProperties: true });
12
+ ajv.addMetaSchema(require("ajv/dist/refs/json-schema-draft-06.json"));
13
+ const plugin = require("ajv-formats");
14
+ if (typeof plugin !== "function")
15
+ throw new Error("Pinned AJV formats plugin is unavailable");
16
+ plugin(ajv);
17
+ for (const schema of Object.values(schemas))
18
+ ajv.addSchema(schema);
19
+ const validate = ajv.getSchema("har.json#");
20
+ if (validate === undefined)
21
+ throw new Error("Pinned HAR root schema is unavailable");
22
+ return (value) => {
23
+ allowMitmproxyAbsentPostData(value);
24
+ if (!validate(value)) {
25
+ const error = validate.errors?.[0];
26
+ throw new CaptureFormatError("format", `HAR schema validation failed: ${error?.keyword ?? "unknown constraint"}.`, error?.instancePath ?? "");
27
+ }
28
+ };
29
+ };
30
+ /** Observed SaveHar 12.2.3 compatibility: validate omitted optional text while retaining reported null. */
31
+ const allowMitmproxyAbsentPostData = (value) => {
32
+ const parsed = z
33
+ .object({
34
+ log: z.object({
35
+ creator: z.object({
36
+ name: z.literal("mitmproxy"),
37
+ version: z.literal("12.2.3"),
38
+ }),
39
+ entries: z.array(z.unknown()),
40
+ }),
41
+ })
42
+ .safeParse(value);
43
+ if (!parsed.success ||
44
+ typeof value !== "object" ||
45
+ value === null ||
46
+ !("log" in value))
47
+ return;
48
+ const log = value.log;
49
+ if (typeof log !== "object" ||
50
+ log === null ||
51
+ !("entries" in log) ||
52
+ !Array.isArray(log.entries))
53
+ return;
54
+ for (const entry of log.entries) {
55
+ const candidate = entry;
56
+ if (typeof candidate !== "object" ||
57
+ candidate === null ||
58
+ !("request" in candidate))
59
+ continue;
60
+ const request = candidate.request;
61
+ if (typeof request !== "object" ||
62
+ request === null ||
63
+ !("postData" in request))
64
+ continue;
65
+ const body = request.postData;
66
+ if (typeof body === "object" &&
67
+ body !== null &&
68
+ "text" in body &&
69
+ body.text === null)
70
+ delete body.text;
71
+ }
72
+ };