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
@@ -0,0 +1,210 @@
1
+ """REA offline adapter using mitmproxy's unchanged native tnetstring decoder.
2
+
3
+ No FlowReader migrations, HAR conversion, proxy listener, or target requests.
4
+ The containing mitmdump process and its configuration directory are REA-owned.
5
+ """
6
+
7
+ import base64
8
+ import hashlib
9
+ import json
10
+ import math
11
+ import re
12
+ import resource
13
+ from pathlib import Path
14
+
15
+ from mitmproxy import ctx, version
16
+ from mitmproxy.io import tnetstring
17
+
18
+ PROFILE = "12.2.3"
19
+ OUTPUT_BYTES = 96 * 1024 * 1024
20
+ CREDENTIALS = {b"authorization", b"proxy-authorization", b"cookie", b"set-cookie"}
21
+
22
+
23
+ class CaptureFailure(Exception):
24
+ def __init__(self, reason, message, pointer=""):
25
+ self.reason, self.pointer = reason, pointer
26
+ super().__init__(message)
27
+
28
+
29
+ def pointer(parent, key):
30
+ return parent + "/" + str(key).replace("~", "~0").replace("/", "~1")
31
+
32
+
33
+ class BoundedReader:
34
+ """Refuse malicious declared lengths before the upstream parser allocates."""
35
+ def __init__(self, handle, size):
36
+ self.handle, self.size = handle, size
37
+
38
+ def read(self, count):
39
+ if count < 0 or count > self.size - self.handle.tell():
40
+ raise CaptureFailure("format", "Native record declares bytes beyond the retained capture.")
41
+ return self.handle.read(count)
42
+
43
+
44
+ def project_record(state, secrets):
45
+ binaries, numbers, redactions = [], [], []
46
+ byte_secrets = []
47
+ for secret in secrets:
48
+ try:
49
+ byte_secrets.append(secret.encode("utf-8", errors="strict"))
50
+ except UnicodeEncodeError:
51
+ # Non-scalar JSON strings have no UTF-8 byte representation.
52
+ # Keep them in secrets for the ordinary string projection.
53
+ pass
54
+
55
+ def redact(text, path, original):
56
+ if any(secret in text or secret in original for secret in secrets):
57
+ redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
58
+ return None
59
+ return text
60
+
61
+ def transport_value(value, path, transport_url):
62
+ if not (transport_url or re.fullmatch(r"/(?:backup/)*request/(?:path|authority)", path)):
63
+ return value, False
64
+ binary = isinstance(value, bytes)
65
+ pattern = rb"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@" if binary else r"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@"
66
+ replacement = rb"\1" if binary else r"\1"
67
+ separator = b"@" if binary else "@"
68
+ safe_url = re.sub(pattern, replacement, value, flags=re.I)
69
+ if path.endswith("/authority") and separator in safe_url:
70
+ safe_url = safe_url.rsplit(separator, 1)[1]
71
+ changed = safe_url != value
72
+ if changed:
73
+ redactions.append({"pointer": path, "reason": "transport-credential"})
74
+ return safe_url, changed
75
+
76
+ def validate_omitted(value, path, depth):
77
+ before = len(binaries), len(numbers), len(redactions)
78
+ visit(value, path, depth)
79
+ del binaries[before[0]:]
80
+ del numbers[before[1]:]
81
+ del redactions[before[2]:]
82
+
83
+ def visit(value, path, depth=0, credential=False, transport_url=False):
84
+ if depth > 64:
85
+ raise CaptureFailure("input-limit", "Native capture exceeds the 64-level evidence nesting budget.", path)
86
+ if credential:
87
+ validate_omitted(value, path, depth)
88
+ redactions.append({"pointer": path, "reason": "transport-credential"})
89
+ if isinstance(value, bytes):
90
+ binaries.append({"pointer": path, "representation": "producer-bytes", "state": "redacted", "content_base64": None, "bytes": None, "sha256": None})
91
+ return None
92
+ if isinstance(value, bytes):
93
+ safe_bytes, credential_url = transport_value(value, path, transport_url)
94
+ content_base64 = base64.b64encode(value).decode("ascii")
95
+ digest = hashlib.sha256(value).hexdigest()
96
+ hidden = any(secret in value or secret in safe_bytes for secret in byte_secrets) or any(secret in content_base64 or secret in digest for secret in secrets)
97
+ safe_text = None
98
+ try:
99
+ safe_text = safe_bytes.decode("utf-8", errors="strict")
100
+ except UnicodeDecodeError:
101
+ pass
102
+ if hidden:
103
+ redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
104
+ exclude = hidden or credential_url
105
+ binaries.append({"pointer": path, "representation": "producer-bytes", "state": "redacted" if exclude else "retained", "content_base64": None if exclude else content_base64, "bytes": None if exclude else len(value), "sha256": None if exclude else digest})
106
+ if hidden:
107
+ return None
108
+ return safe_text
109
+ if isinstance(value, str):
110
+ safe_text, _ = transport_value(value, path, transport_url)
111
+ return redact(safe_text, path, value)
112
+ if isinstance(value, bool) or value is None:
113
+ return value
114
+ if isinstance(value, (int, float)):
115
+ literal = repr(value)
116
+ if any(secret in literal for secret in secrets):
117
+ redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
118
+ return None
119
+ numbers.append({"pointer": path, "producer_type": "integer" if isinstance(value, int) else "float", "literal": literal})
120
+ return value if (abs(value) <= 9007199254740991 if isinstance(value, int) else math.isfinite(value)) else None
121
+ if isinstance(value, (list, tuple)):
122
+ fields = re.fullmatch(r"/(?:backup/)*(?:request|response)/(?:headers|trailers)", path)
123
+ if fields:
124
+ result = []
125
+ for index, field in enumerate(value):
126
+ if not isinstance(field, (list, tuple)) or len(field) != 2 or not isinstance(field[0], bytes):
127
+ raise CaptureFailure("format", "Native header/trailer is not an ordered byte pair.", pointer(path, index))
128
+ result.append([visit(field[0], pointer(pointer(path, index), 0), depth + 2), visit(field[1], pointer(pointer(path, index), 1), depth + 2, field[0].lower() in CREDENTIALS, field[0].lower() in {b"location", b"referer", b"origin"})])
129
+ return result
130
+ return [visit(item, pointer(path, index), depth + 1) for index, item in enumerate(value)]
131
+ if isinstance(value, dict):
132
+ result = {}
133
+ for key, item in value.items():
134
+ if not isinstance(key, str):
135
+ raise CaptureFailure("format", "Native record has a non-string dictionary key.", path)
136
+ if key == "__proto__":
137
+ raise CaptureFailure("unsupported", "Native object contains a __proto__ member that the current JSON schema boundary cannot preserve.", path)
138
+ if any(secret in key for secret in secrets):
139
+ # Validate the omitted subtree too, then discard all of its
140
+ # coordinates rather than inventing a replacement identity.
141
+ validate_omitted(item, pointer(path, key), depth + 1)
142
+ redactions.append({"pointer": path, "reason": "explicit-sensitive-value", "scope": "property-name"})
143
+ continue
144
+ result[key] = visit(item, pointer(path, key), depth + 1)
145
+ return result
146
+ raise CaptureFailure("decoder", "Native parser returned an unexpected value type: " + type(value).__name__ + ".", path)
147
+
148
+ return {"reported": visit(state, ""), "binary_fields": binaries, "numeric_literals": numbers, "redactions": redactions}
149
+
150
+
151
+ def decode(request):
152
+ if version.VERSION != PROFILE:
153
+ raise CaptureFailure("unsupported", "Expected mitmproxy " + PROFILE + "; observed " + version.VERSION + ".")
154
+ records = []
155
+ capture = Path(request["snapshot_path"])
156
+ size = capture.stat().st_size
157
+ with capture.open("rb") as handle:
158
+ reader = BoundedReader(handle, size)
159
+ while handle.tell() < size:
160
+ start = handle.tell()
161
+ try:
162
+ state = tnetstring.load(reader)
163
+ except CaptureFailure:
164
+ raise
165
+ except RecursionError:
166
+ raise CaptureFailure("input-limit", "Native parser exceeded its recursion budget at byte offset " + str(start) + ".") from None
167
+ except (ValueError, IndexError, UnicodeError, EOFError):
168
+ raise CaptureFailure("format", "Malformed native record at byte offset " + str(start) + ".") from None
169
+ if not isinstance(state, dict):
170
+ raise CaptureFailure("format", "Native flow record must be a dictionary at byte offset " + str(start) + ".")
171
+ records.append({"ordinal": len(records), "location": {"kind": "byte-range", "offset": start, "bytes": handle.tell() - start}, **project_record(state, request["sensitive_values"]), "limitations": ["Fields are original native states without FlowReader migration. Binary fields contain exact producer bytes; reported UTF-8 strings for those fields are derived display views. Unknown flow/version extensions are retained without interpretation."]})
172
+ return {"decoder": {"id": "mitmproxy-native-tnetstring", "name": "REA offline native mitmproxy adapter", "version": PROFILE}, "container": {"reported": None, "numeric_literals": [], "redactions": [], "records_pointer": None}, "total_records": len(records), "records": records}
173
+
174
+
175
+ def failure_reply(error):
176
+ """Keep producer constraints, resource exhaustion and adapter defects distinct."""
177
+ if isinstance(error, CaptureFailure):
178
+ return {"ok": False, "reason": error.reason, "message": str(error), "pointer": error.pointer}
179
+ if isinstance(error, MemoryError):
180
+ return {"ok": False, "reason": "resource-limit", "message": "Native decoder exhausted its 768 MiB address-space budget.", "pointer": ""}
181
+ return {"ok": False, "reason": "decoder", "message": "Offline native decoder failed unexpectedly: " + type(error).__name__ + ".", "pointer": ""}
182
+
183
+
184
+ class OfflineCapture:
185
+ def load(self, loader):
186
+ loader.add_option("rea_request_path", str, "", "REA private offline capture request")
187
+
188
+ def running(self):
189
+ resource.setrlimit(resource.RLIMIT_AS, (768 * 1024 * 1024, 768 * 1024 * 1024))
190
+ resource.setrlimit(resource.RLIMIT_CPU, (30, 30))
191
+ resource.setrlimit(resource.RLIMIT_FSIZE, (OUTPUT_BYTES, OUTPUT_BYTES))
192
+ resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
193
+ request = json.loads(Path(ctx.options.rea_request_path).read_text(encoding="utf-8"))
194
+ try:
195
+ response = {"ok": True, "value": decode(request)}
196
+ except Exception as error:
197
+ response = failure_reply(error)
198
+ try:
199
+ try:
200
+ output = json.dumps(response, ensure_ascii=True, allow_nan=False, separators=(",", ":")).encode("ascii")
201
+ except Exception as error:
202
+ output = json.dumps(failure_reply(error), separators=(",", ":")).encode("ascii")
203
+ if len(output) > OUTPUT_BYTES:
204
+ output = b'{"ok":false,"reason":"limit","message":"Native reply exceeds the complete-evidence output budget.","pointer":""}'
205
+ Path(request["reply_path"]).write_bytes(output)
206
+ finally:
207
+ ctx.master.shutdown()
208
+
209
+
210
+ addons = [OfflineCapture()]
@@ -0,0 +1,303 @@
1
+ """Bounded native call observation executed inside LLDB's embedded Python.
2
+
3
+ REA imports this module into `lldb --batch` and runs `rea_trace <config.json>`.
4
+ It launches one owned process, stops at the requested functions and
5
+ Objective-C methods, records each entry from registers and memory reads only,
6
+ continues, and kills the process once it exits, the event limit is reached or
7
+ the duration elapses. No expression is evaluated and no target code runs on
8
+ the tracer's behalf. The result is written as JSON to the configured path.
9
+ """
10
+
11
+ import json
12
+ import time
13
+
14
+ import lldb
15
+
16
+ ARGUMENT_REGISTERS = {
17
+ "arm64": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"],
18
+ "arm64e": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"],
19
+ "x86_64": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
20
+ "x86_64h": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
21
+ }
22
+ MAX_SELECTOR_BYTES = 1024
23
+ MAX_REPORTED_LOCATIONS = 64
24
+ MAX_OTHER_STOPS = 256
25
+ LISTENER_NAME = "rea-native-call-observation"
26
+
27
+
28
+ def _hex(value):
29
+ return "0x%x" % value
30
+
31
+
32
+ def _location(address, target):
33
+ """Load and file address, module and symbol of one code address."""
34
+ module = address.GetModule()
35
+ symbol = address.GetSymbol()
36
+ file_address = address.GetFileAddress()
37
+ return {
38
+ "load_address": _hex(address.GetLoadAddress(target)),
39
+ "file_address": None
40
+ if file_address == lldb.LLDB_INVALID_ADDRESS
41
+ else _hex(file_address),
42
+ "module": module.GetFileSpec().GetFilename() if module.IsValid() else None,
43
+ "module_path": str(module.GetFileSpec()) if module.IsValid() else None,
44
+ "symbol": symbol.GetName() if symbol.IsValid() else None,
45
+ }
46
+
47
+
48
+ def _no_files():
49
+ return lldb.SBFileSpecList()
50
+
51
+
52
+ def _create_breakpoints(target, spec):
53
+ """LLDB breakpoints for one request; a class-qualified method may need two."""
54
+ if spec["kind"] == "function":
55
+ modules = lldb.SBFileSpecList()
56
+ if spec.get("module"):
57
+ modules.Append(lldb.SBFileSpec(spec["module"], False))
58
+ return [
59
+ target.BreakpointCreateByName(
60
+ spec["name"], lldb.eFunctionNameTypeAuto, modules, _no_files()
61
+ )
62
+ ]
63
+ class_name = spec.get("class_name")
64
+ method_type = spec.get("method_type", "any")
65
+ if class_name is None:
66
+ return [
67
+ target.BreakpointCreateByName(
68
+ spec["selector"], lldb.eFunctionNameTypeSelector, _no_files(), _no_files()
69
+ )
70
+ ]
71
+ prefixes = {"instance": ["-"], "class": ["+"], "any": ["-", "+"]}[method_type]
72
+ return [
73
+ target.BreakpointCreateByName(
74
+ "%s[%s %s]" % (prefix, class_name, spec["selector"]),
75
+ lldb.eFunctionNameTypeFull,
76
+ _no_files(),
77
+ _no_files(),
78
+ )
79
+ for prefix in prefixes
80
+ ]
81
+
82
+
83
+ def _dynamic_class(target, pointer):
84
+ """Class of an Objective-C receiver from LLDB's runtime reader, without running code."""
85
+ if pointer == 0:
86
+ return None
87
+ data = lldb.SBData.CreateDataFromUInt64Array(
88
+ target.GetByteOrder(), target.GetAddressByteSize(), [pointer]
89
+ )
90
+ value = target.CreateValueFromData(
91
+ "receiver", data, target.GetBasicType(lldb.eBasicTypeObjCID)
92
+ )
93
+ dynamic = value.GetDynamicValue(lldb.eDynamicDontRunTarget)
94
+ name = dynamic.GetTypeName() if dynamic.IsValid() else None
95
+ if not name or name in ("id", "objc_object *"):
96
+ return None
97
+ return name[:-2] if name.endswith(" *") else name
98
+
99
+
100
+ def _wanted(spec, symbol):
101
+ """Selector-only requests may admit only instance or only class methods."""
102
+ if spec["kind"] != "objc-method" or spec.get("class_name") is not None:
103
+ return True
104
+ method_type = spec.get("method_type", "any")
105
+ if method_type == "any":
106
+ return True
107
+ return symbol.startswith("-[" if method_type == "instance" else "+[")
108
+
109
+
110
+ def _record(context, thread, index, sequence):
111
+ target, process, config = context["target"], context["process"], context["config"]
112
+ frame = thread.GetFrameAtIndex(0)
113
+ location = _location(frame.GetPCAddress(), target)
114
+ symbol = location["symbol"] or ""
115
+ if not _wanted(config["breakpoints"][index], symbol):
116
+ return None
117
+ names = ARGUMENT_REGISTERS.get(config["architecture"], [])
118
+ registers = [
119
+ {"name": name, "value": _hex(frame.FindRegister(name).GetValueAsUnsigned())}
120
+ for name in names[: config["argument_registers"]]
121
+ ]
122
+ receiver_class = None
123
+ selector = None
124
+ if symbol.startswith("-[") or symbol.startswith("+["):
125
+ error = lldb.SBError()
126
+ text = process.ReadCStringFromMemory(
127
+ frame.FindRegister(names[1]).GetValueAsUnsigned(),
128
+ MAX_SELECTOR_BYTES,
129
+ error,
130
+ )
131
+ selector = text if error.Success() and text else None
132
+ if symbol.startswith("-["):
133
+ receiver_class = _dynamic_class(
134
+ target, frame.FindRegister(names[0]).GetValueAsUnsigned()
135
+ )
136
+ frames = min(thread.GetNumFrames(), 1 + config["backtrace_frames"])
137
+ record = {
138
+ "sequence": sequence,
139
+ "elapsed_ms": round((time.monotonic() - context["started"]) * 1000, 3),
140
+ "thread_id": thread.GetThreadID(),
141
+ "breakpoint_index": index,
142
+ "receiver_class": receiver_class,
143
+ "selector": selector,
144
+ "registers": registers,
145
+ "backtrace": [
146
+ _location(thread.GetFrameAtIndex(depth).GetPCAddress(), target)
147
+ for depth in range(1, frames)
148
+ ],
149
+ }
150
+ record.update(location)
151
+ return record
152
+
153
+
154
+ def _handle_stop(context, events, other_stops):
155
+ """Record breakpoint hits of one stop; return an outcome when tracing must end."""
156
+ process = context["process"]
157
+ for thread in process:
158
+ reason = thread.GetStopReason()
159
+ if reason == lldb.eStopReasonBreakpoint:
160
+ index = context["breakpoint_index"].get(thread.GetStopReasonDataAtIndex(0))
161
+ if index is None:
162
+ continue
163
+ record = _record(context, thread, index, len(events))
164
+ if record is not None:
165
+ events.append(record)
166
+ if len(events) >= context["config"]["max_events"]:
167
+ return "event-limit"
168
+ elif reason in (lldb.eStopReasonSignal, lldb.eStopReasonException):
169
+ other_stops.append(thread.GetStopDescription(256))
170
+ if len(other_stops) >= MAX_OTHER_STOPS:
171
+ return "stop-limit"
172
+ return None
173
+
174
+
175
+ def _breakpoint_report(target, created):
176
+ report = []
177
+ for index, breakpoints in enumerate(created):
178
+ locations = []
179
+ count = 0
180
+ for breakpoint in breakpoints:
181
+ count += breakpoint.GetNumLocations()
182
+ for position in range(breakpoint.GetNumLocations()):
183
+ if len(locations) < MAX_REPORTED_LOCATIONS:
184
+ locations.append(
185
+ _location(breakpoint.GetLocationAtIndex(position).GetAddress(), target)
186
+ )
187
+ report.append({"index": index, "location_count": count, "locations": locations})
188
+ return report
189
+
190
+
191
+ def _launch_info(target, config, listener):
192
+ launch = lldb.SBLaunchInfo(config["arguments"])
193
+ launch.SetListener(listener)
194
+ environment = target.GetEnvironment()
195
+ for key, value in config["environment"].items():
196
+ environment.Set(key, value, True)
197
+ launch.SetEnvironment(environment, False)
198
+ if config.get("working_directory"):
199
+ launch.SetWorkingDirectory(config["working_directory"])
200
+ launch.AddOpenFileAction(0, "/dev/null", True, False)
201
+ launch.AddOpenFileAction(1, config["stdout_path"], False, True)
202
+ launch.AddOpenFileAction(2, config["stderr_path"], False, True)
203
+ return launch
204
+
205
+
206
+ def _wait_for_exit(listener, process, seconds):
207
+ event = lldb.SBEvent()
208
+ deadline = time.monotonic() + seconds
209
+ while process.GetState() != lldb.eStateExited and time.monotonic() < deadline:
210
+ listener.WaitForEvent(1, event)
211
+
212
+
213
+ def trace(debugger, config):
214
+ debugger.SetAsync(True)
215
+ # Stop at the symbol itself, where argument registers still hold the ABI
216
+ # arguments, even when line tables would let LLDB skip the prologue.
217
+ lldb.SBDebugger.SetInternalVariable(
218
+ "target.skip-prologue", "false", debugger.GetInstanceName()
219
+ )
220
+ error = lldb.SBError()
221
+ target = debugger.CreateTarget(
222
+ config["executable"], config["architecture"], None, False, error
223
+ )
224
+ if not target.IsValid():
225
+ return {"status": "target-error", "error": error.GetCString()}
226
+ created = [_create_breakpoints(target, spec) for spec in config["breakpoints"]]
227
+ breakpoint_index = {}
228
+ for index, breakpoints in enumerate(created):
229
+ for breakpoint in breakpoints:
230
+ breakpoint_index[breakpoint.GetID()] = index
231
+ listener = lldb.SBListener(LISTENER_NAME)
232
+ started = time.monotonic()
233
+ process = target.Launch(_launch_info(target, config, listener), error)
234
+ if not error.Success() or not process.IsValid():
235
+ return {"status": "launch-error", "error": error.GetCString()}
236
+ with open(config["pid_path"], "w") as handle:
237
+ handle.write(str(process.GetProcessID()))
238
+ context = {
239
+ "target": target,
240
+ "process": process,
241
+ "config": config,
242
+ "started": started,
243
+ "breakpoint_index": breakpoint_index,
244
+ }
245
+ events = []
246
+ other_stops = []
247
+ outcome = None
248
+ deadline = started + config["duration_ms"] / 1000
249
+ event = lldb.SBEvent()
250
+ while outcome is None:
251
+ if time.monotonic() >= deadline:
252
+ outcome = "duration-elapsed"
253
+ break
254
+ if not listener.WaitForEvent(1, event):
255
+ continue
256
+ if not lldb.SBProcess.EventIsProcessEvent(event):
257
+ continue
258
+ state = lldb.SBProcess.GetStateFromEvent(event)
259
+ if state == lldb.eStateExited:
260
+ outcome = "exited"
261
+ elif state == lldb.eStateStopped and not lldb.SBProcess.GetRestartedFromEvent(event):
262
+ outcome = _handle_stop(context, events, other_stops)
263
+ if outcome is None:
264
+ process.Continue()
265
+ pid = process.GetProcessID()
266
+ exit_status = None
267
+ exit_description = None
268
+ if outcome == "exited":
269
+ exit_status = process.GetExitStatus()
270
+ exit_description = process.GetExitDescription()
271
+ else:
272
+ process.Kill()
273
+ _wait_for_exit(listener, process, 5)
274
+ return {
275
+ "status": "traced",
276
+ "version": lldb.SBDebugger.GetVersionString().splitlines()[0],
277
+ "pid": pid,
278
+ "outcome": outcome,
279
+ "exit_status": exit_status,
280
+ "exit_description": exit_description,
281
+ "killed": outcome != "exited",
282
+ "terminated": process.GetState() == lldb.eStateExited,
283
+ "elapsed_ms": round((time.monotonic() - started) * 1000, 3),
284
+ "breakpoints": _breakpoint_report(target, created),
285
+ "events": events,
286
+ "other_stops": other_stops,
287
+ }
288
+
289
+
290
+ def run(debugger, command, result, internal_dict):
291
+ """LLDB command entry point: `rea_trace <config.json>`."""
292
+ with open(command.strip()) as handle:
293
+ config = json.load(handle)
294
+ try:
295
+ output = trace(debugger, config)
296
+ except Exception as error: # Reported as a tracer failure, never re-raised into LLDB.
297
+ output = {"status": "tracer-error", "error": "%s: %s" % (type(error).__name__, error)}
298
+ with open(config["result_path"], "w") as handle:
299
+ json.dump(output, handle)
300
+
301
+
302
+ def __lldb_init_module(debugger, internal_dict):
303
+ debugger.HandleCommand("command script add -f rea_lldb_tracer.run rea_trace")