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.
- package/README.md +157 -650
- package/bridge/mitmproxy/capture.py +210 -0
- package/bridge/native/rea_lldb_tracer.py +303 -0
- package/bridge/process/ProcessRunTokenReader.swift +263 -0
- package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
- package/bridge/pwndbg/core_context.py +101 -0
- package/bridge/pwndbg/launch.py +39 -0
- package/bridge/pwndbg/launch_impl.py +34 -0
- package/bridge/pwntools/decoder_runtime.py +59 -0
- package/bridge/pwntools/layout.py +39 -0
- package/bridge/pwntools/layout_impl.py +309 -0
- package/bridge/pwntools/recorded_crash.py +39 -0
- package/bridge/pwntools/recorded_crash_impl.py +143 -0
- package/dist/android/JadxProvider.js +1 -0
- package/dist/application/CapabilityInventory.js +21 -0
- package/dist/application/DirectAnalysis.js +97 -23
- package/dist/application/EvidenceBundleFiles.js +1 -1
- package/dist/application/ReconstructionObligationCandidates.js +5 -1
- package/dist/application/WebNetworkCapturePort.js +1 -0
- package/dist/application/WebNetworkCaptureService.js +132 -0
- package/dist/application/WindowsCapabilities.js +1 -1
- package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
- package/dist/application/binary/BinarySession.js +38 -10
- package/dist/application/binary/BinarySessionOpen.js +16 -3
- package/dist/application/binary/BinarySessionRecords.js +20 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
- package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
- package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
- package/dist/application/evm/EvmInterfacePort.js +1 -0
- package/dist/application/evm/EvmInterfaceService.js +62 -0
- package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
- package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
- package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
- package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
- package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
- package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
- package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
- package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
- package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
- package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
- package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
- package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
- package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
- package/dist/artifacts/ArtifactProvider.js +30 -3
- package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
- package/dist/artifacts/ZipArtifactReader.js +3 -0
- package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
- package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
- package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
- package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
- package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
- package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
- package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
- package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
- package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
- package/dist/artifacts/readStableArtifact.js +20 -4
- package/dist/browser/CdpCaptureEventHandlers.js +90 -2
- package/dist/browser/CdpCaptureEvents.js +9 -0
- package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
- package/dist/browser/history/CaptureFailures.js +101 -0
- package/dist/browser/history/CaptureFormatError.js +10 -0
- package/dist/browser/history/CaptureRedaction.js +65 -0
- package/dist/browser/history/CaptureRelease.js +12 -0
- package/dist/browser/history/HarCapture.js +165 -0
- package/dist/browser/history/HarCaptureAdapter.js +20 -0
- package/dist/browser/history/HarCaptureProcess.js +57 -0
- package/dist/browser/history/HarJson.js +91 -0
- package/dist/browser/history/HarSchema.js +72 -0
- package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
- package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
- package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
- package/dist/cli/artifactCommands.js +26 -1
- package/dist/cli/binaryDiagnosticsCommands.js +39 -0
- package/dist/cli/coreAnalysisCommands.js +2 -2
- package/dist/cli/coreBinaryCommands.js +8 -8
- package/dist/cli/electronCommands.js +16 -9
- package/dist/cli/evmCommands.js +24 -0
- package/dist/cli/firmwareCommands.js +6 -3
- package/dist/cli/javascriptApplicationAnalysis.js +16 -3
- package/dist/cli/jsonOutput.js +133 -0
- package/dist/cli/nativeCallCommands.js +35 -0
- package/dist/cli/processCommands.js +1 -1
- package/dist/cli/streamedJsonOutput.js +56 -0
- package/dist/cli/utilityCommands.js +2 -0
- package/dist/cli/webNetworkCaptureCommands.js +40 -0
- package/dist/cli.js +9 -3
- package/dist/cliBrowserScenarioCommands.js +2 -2
- package/dist/cliCommandNames.js +6 -0
- package/dist/cliJsonInput.js +30 -1
- package/dist/cliOutput.js +21 -6
- package/dist/composition/binaryDiagnostics.js +8 -0
- package/dist/composition/evm.js +4 -0
- package/dist/composition/webNetworkCaptures.js +6 -0
- package/dist/contracts/artifactToolContracts.js +6 -0
- package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
- package/dist/contracts/browserToolContracts.js +4 -3
- package/dist/contracts/errorSchemas.js +8 -1
- package/dist/contracts/evm/evmToolContracts.js +20 -0
- package/dist/contracts/investigationExamples.js +2 -2
- package/dist/contracts/managed/managedToolContracts.js +7 -3
- package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
- package/dist/contracts/native/nativeToolContracts.js +17 -1
- package/dist/contracts/officialToolContracts.js +12 -9
- package/dist/contracts/process/processCaptureExample.js +2 -0
- package/dist/contracts/sessionLifecycleInputs.js +22 -5
- package/dist/contracts/sessionStatusContract.js +10 -2
- package/dist/contracts/sessionToolContracts.js +8 -1
- package/dist/contracts/sessionToolSchemas.js +10 -3
- package/dist/contracts/toolContractExamples.js +1 -1
- package/dist/contracts/toolContracts.js +6 -0
- package/dist/contracts/toolEffects.js +30 -0
- package/dist/contracts/toolOutputSchemaGroups.js +5 -1
- package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
- package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
- package/dist/domain/analysisErrorBase.js +15 -0
- package/dist/domain/analysisErrorCore.js +59 -5
- package/dist/domain/analysisErrorPresentation.js +29 -1
- package/dist/domain/analysisErrorProjection.js +52 -2
- package/dist/domain/analysisSnapshot.js +166 -2
- package/dist/domain/apple/dyldPaths.js +81 -0
- package/dist/domain/apple/dylibResolution.js +367 -0
- package/dist/domain/apple/dylibResolutionFindings.js +75 -0
- package/dist/domain/artifactPathSyntax.js +1 -1
- package/dist/domain/browserObservation.js +1 -1
- package/dist/domain/browserObservationSchemas.js +13 -0
- package/dist/domain/browserScenarioValues.js +9 -2
- package/dist/domain/callPathSchemas.js +1 -1
- package/dist/domain/canonicalDigest.js +13 -1
- package/dist/domain/changedBehavior.js +3 -3
- package/dist/domain/comparisonStatus.js +2 -30
- package/dist/domain/conformanceTrustGate.js +0 -16
- package/dist/domain/evidence.js +17 -5
- package/dist/domain/evm/evmInterface.js +63 -0
- package/dist/domain/explicitSensitiveFailure.js +92 -0
- package/dist/domain/explicitSensitiveValues.js +10 -0
- package/dist/domain/firmware/firmwareAnalysis.js +8 -1
- package/dist/domain/javascript/electronActiveObservation.js +9 -4
- package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
- package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
- package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
- package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
- package/dist/domain/jsonValue.js +4 -0
- package/dist/domain/localPath.js +2 -0
- package/dist/domain/native/binaryLayout.js +357 -0
- package/dist/domain/native/nativeCallObservation.js +164 -0
- package/dist/domain/native/objcSwiftMetadata.js +0 -39
- package/dist/domain/native/recordedCrash.js +266 -0
- package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
- package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
- package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
- package/dist/domain/process/processCaptureParsing.js +54 -0
- package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
- package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
- package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
- package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
- package/dist/domain/providerSelectionError.js +4 -2
- package/dist/domain/reconstructionVerification.js +3 -3
- package/dist/domain/reconstructionVerificationSchemas.js +1 -1
- package/dist/domain/residualUnknown.js +3 -2
- package/dist/domain/staticRuntimeCorrelation.js +3 -3
- package/dist/domain/webCaptureDiff.js +1 -0
- package/dist/domain/webNetworkCapture.js +118 -0
- package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
- package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
- package/dist/dotnet/ManagedMemberInspector.js +4 -2
- package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
- package/dist/dotnet/ManagedMemberRows.js +14 -3
- package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
- package/dist/dotnet/ManagedMetadataInventory.js +38 -28
- package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
- package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
- package/dist/dotnet/ManagedPeReader.js +11 -0
- package/dist/evm/EvmBytecodeCarrier.js +21 -0
- package/dist/evm/EvmWorkerLimits.js +40 -0
- package/dist/evm/EvmoleFailures.js +126 -0
- package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
- package/dist/evm/EvmoleInterfaceProvider.js +273 -0
- package/dist/evm/EvmoleInterfaceWorker.js +87 -0
- package/dist/evm/EvmoleRelease.js +16 -0
- package/dist/firmware/FirmwareCommand.js +1 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraLauncher.js +1 -0
- package/dist/ghidra/GhidraProvider.js +2 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
- package/dist/hopper/BridgeLauncher.js +2 -0
- package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
- package/dist/javascript/recovery/WakaruCommand.js +3 -0
- package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
- package/dist/native/CommandRunner.js +2 -1
- package/dist/native/LldbCallTracer.js +278 -0
- package/dist/native/NativeCallObservation.js +135 -0
- package/dist/native/NativeMacOSProvider.js +19 -3
- package/dist/native/NativeMacOSProviderMetadata.js +8 -3
- package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
- package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
- package/dist/native/pwntools/PwntoolsFailures.js +180 -0
- package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
- package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
- package/dist/native/pwntools/PwntoolsRelease.js +19 -0
- package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
- package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
- package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
- package/dist/process/DarwinProcessRunTokenReader.js +294 -0
- package/dist/process/OwnedCommand.js +113 -0
- package/dist/process/ProcessOwnership.js +263 -33
- package/dist/process/ProcessOwnershipObservation.js +161 -24
- package/dist/process/ProviderProcess.js +30 -3
- package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
- package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
- package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
- package/dist/{application → process/capture}/ProcessHarness.js +195 -29
- package/dist/{application → process/capture}/ProcessSampling.js +2 -2
- package/dist/server/createServer.js +22 -0
- package/dist/server/promptCompletion.js +10 -3
- package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
- package/dist/server/registerEvidenceTools.js +1 -1
- package/dist/server/registerEvmTools.js +17 -0
- package/dist/server/registerProcessComparisonTool.js +1 -1
- package/dist/server/registerRecordedCrashTools.js +17 -0
- package/dist/server/registerSessionTools.js +2 -2
- package/dist/server/registerWebNetworkCaptureTool.js +28 -0
- package/dist/server/sessionAvailabilityPolicy.js +3 -0
- package/dist/server/sessionToolPolicies.js +1 -1
- package/dist/server/toolResult.js +1 -1
- package/native/windows/build/manifest.json +2 -2
- package/native/windows/build/rea-windows-x64.node +0 -0
- package/package.json +37 -9
- package/scripts/rea.mjs +11 -3
- package/skills/reverse-engineer-anything/SKILL.md +23 -3
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
- package/third_party/README.md +60 -0
- package/third_party/evmole/LICENSE +21 -0
- package/third_party/evmole/README.md +22 -0
- package/third_party/pwndbg/LICENSE.md +21 -0
- package/third_party/pwndbg/README.md +23 -0
- package/third_party/pwntools/README.md +40 -0
- package/dist/application/AuthorizedArtifactInventory.js +0 -7
- package/dist/application/ProcessCaptureLifecycle.js +0 -299
- package/dist/application/StoreFileLock.js +0 -124
- package/dist/contracts/processCaptureExample.js +0 -2
- package/dist/domain/bytecodeProvider.js +0 -178
- package/dist/domain/customProtocolCapture.js +0 -137
- package/dist/domain/eventProcessTree.js +0 -225
- package/dist/domain/mobileApplicationInvestigation.js +0 -159
- package/dist/domain/packageAnalysis.js +0 -123
- package/dist/domain/peInspection.js +0 -197
- package/dist/domain/processCaptureParsing.js +0 -21
- package/dist/domain/protocolCapture.js +0 -217
- package/dist/domain/runtimeIdentification.js +0 -212
- package/dist/generatedMcpToolCatalog.js +0 -37
- /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
- /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
- /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
- /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
- /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
- /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
- /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
- /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
- /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
- /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
- /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
- /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
- /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
- /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
- /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
- /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
|
|
2
|
+
// Exact amd64/core enum projections in the pinned pyelftools 0.33 profile.
|
|
3
|
+
const segmentKinds = new Map([
|
|
4
|
+
[0, "PT_NULL"],
|
|
5
|
+
[1, "PT_LOAD"],
|
|
6
|
+
[2, "PT_DYNAMIC"],
|
|
7
|
+
[3, "PT_INTERP"],
|
|
8
|
+
[4, "PT_NOTE"],
|
|
9
|
+
[5, "PT_SHLIB"],
|
|
10
|
+
[6, "PT_PHDR"],
|
|
11
|
+
[7, "PT_TLS"],
|
|
12
|
+
[0x60000000, "PT_LOOS"],
|
|
13
|
+
[0x6fffffff, "PT_HIOS"],
|
|
14
|
+
[0x6474e550, "PT_GNU_EH_FRAME"],
|
|
15
|
+
[0x6474e551, "PT_GNU_STACK"],
|
|
16
|
+
[0x6474e552, "PT_GNU_RELRO"],
|
|
17
|
+
[0x6474e553, "PT_GNU_PROPERTY"],
|
|
18
|
+
]);
|
|
19
|
+
const noteKinds = new Map([
|
|
20
|
+
[1, "NT_PRSTATUS"],
|
|
21
|
+
[2, "NT_FPREGSET"],
|
|
22
|
+
[3, "NT_PRPSINFO"],
|
|
23
|
+
[4, "NT_TASKSTRUCT"],
|
|
24
|
+
[6, "NT_AUXV"],
|
|
25
|
+
[0x53494749, "NT_SIGINFO"],
|
|
26
|
+
[0x46494c45, "NT_FILE"],
|
|
27
|
+
]);
|
|
28
|
+
const matchesKind = (raw, reported, kinds) => {
|
|
29
|
+
const known = kinds.get(raw);
|
|
30
|
+
return typeof reported === "string"
|
|
31
|
+
? reported === known
|
|
32
|
+
: known === undefined && reported === raw;
|
|
33
|
+
};
|
|
34
|
+
const segmentMatches = (segment, raw) => {
|
|
35
|
+
const type = raw.readUInt32LE(0);
|
|
36
|
+
return (matchesKind(type, segment.type, segmentKinds) &&
|
|
37
|
+
BigInt(segment.flags) === BigInt(raw.readUInt32LE(4)) &&
|
|
38
|
+
segment.file_backing ===
|
|
39
|
+
(type === 0 || raw.readBigUInt64LE(32) === 0n ? "none" : "file") &&
|
|
40
|
+
[
|
|
41
|
+
[segment.offset, 8],
|
|
42
|
+
[segment.virtual_address, 16],
|
|
43
|
+
[segment.physical_address, 24],
|
|
44
|
+
[segment.file_size, 32],
|
|
45
|
+
[segment.memory_size, 40],
|
|
46
|
+
[segment.alignment, 48],
|
|
47
|
+
].every(([field, relative]) => BigInt(field) === raw.readBigUInt64LE(relative)));
|
|
48
|
+
};
|
|
49
|
+
/** Bind coverage-bearing ELF and note headers to the same snapshot as their payloads. */
|
|
50
|
+
export function validateRecordedCrashStructure(report, snapshot, diagnostics) {
|
|
51
|
+
const fail = () => {
|
|
52
|
+
throw new AnalysisOutputError("inspect_recorded_crash", "Reported core structure differs from its original ELF/note headers.", { capturedOutput: diagnostics });
|
|
53
|
+
};
|
|
54
|
+
const bytesAt = (offset, bytes) => {
|
|
55
|
+
if (offset < 0n || offset > BigInt(snapshot.length - bytes))
|
|
56
|
+
return fail();
|
|
57
|
+
return snapshot.subarray(Number(offset), Number(offset) + bytes);
|
|
58
|
+
};
|
|
59
|
+
const header = bytesAt(0n, 64);
|
|
60
|
+
if (!header
|
|
61
|
+
.subarray(0, 6)
|
|
62
|
+
.equals(Buffer.from([0x7f, 0x45, 0x4c, 0x46, 2, 1])) ||
|
|
63
|
+
![0, 3].includes(header[7] ?? -1) ||
|
|
64
|
+
header[8] !== 0 ||
|
|
65
|
+
header.readUInt16LE(16) !== 4 ||
|
|
66
|
+
header.readUInt16LE(18) !== 62)
|
|
67
|
+
fail();
|
|
68
|
+
const table = header.readBigUInt64LE(32);
|
|
69
|
+
const entryBytes = header.readUInt16LE(54);
|
|
70
|
+
let count = header.readUInt16LE(56);
|
|
71
|
+
if (count === 0xffff) {
|
|
72
|
+
if (header.readUInt16LE(58) < 64)
|
|
73
|
+
return fail();
|
|
74
|
+
count = bytesAt(header.readBigUInt64LE(40), 64).readUInt32LE(44);
|
|
75
|
+
}
|
|
76
|
+
if (count !== report.segments.length || (count > 0 && entryBytes < 56))
|
|
77
|
+
fail();
|
|
78
|
+
for (const segment of report.segments) {
|
|
79
|
+
const offset = table + BigInt(segment.index) * BigInt(entryBytes);
|
|
80
|
+
if (BigInt(segment.header_location.offset) !== offset ||
|
|
81
|
+
BigInt(segment.header_location.bytes) !== BigInt(entryBytes))
|
|
82
|
+
fail();
|
|
83
|
+
const raw = bytesAt(offset, entryBytes);
|
|
84
|
+
if (!segmentMatches(segment, raw))
|
|
85
|
+
fail();
|
|
86
|
+
}
|
|
87
|
+
for (const note of report.notes) {
|
|
88
|
+
const raw = bytesAt(BigInt(note.location.offset), 12);
|
|
89
|
+
if (BigInt(note.owner_location.bytes) !== BigInt(raw.readUInt32LE(0)) ||
|
|
90
|
+
BigInt(note.descriptor_location.bytes) !== BigInt(raw.readUInt32LE(4)) ||
|
|
91
|
+
!matchesKind(raw.readUInt32LE(8), note.type, noteKinds))
|
|
92
|
+
fail();
|
|
93
|
+
const owner = bytesAt(BigInt(note.owner_location.offset), raw.readUInt32LE(0));
|
|
94
|
+
const terminator = owner.indexOf(0);
|
|
95
|
+
const display = owner.length === 0
|
|
96
|
+
? null
|
|
97
|
+
: owner.subarray(0, terminator).toString("utf8");
|
|
98
|
+
if ((owner.length > 0 && terminator < 0) || note.owner_display !== display)
|
|
99
|
+
fail();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { safeParseJson } from "../domain/safeJson.js";
|
|
8
|
+
import { execFileOutput } from "./ExecFileOutput.js";
|
|
9
|
+
const responseSchema = z.strictObject({
|
|
10
|
+
results: z.array(z.discriminatedUnion("state", [
|
|
11
|
+
z.strictObject({
|
|
12
|
+
pid: z.number().int().positive(),
|
|
13
|
+
state: z.literal("readable"),
|
|
14
|
+
run_id: z.string().optional(),
|
|
15
|
+
reason: z.null().optional(),
|
|
16
|
+
}),
|
|
17
|
+
z.strictObject({
|
|
18
|
+
pid: z.number().int().positive(),
|
|
19
|
+
state: z.literal("unavailable"),
|
|
20
|
+
run_id: z.union([z.string(), z.null()]).optional(),
|
|
21
|
+
reason: z.string().min(1),
|
|
22
|
+
}),
|
|
23
|
+
])),
|
|
24
|
+
});
|
|
25
|
+
const identityResponseSchema = z.strictObject({
|
|
26
|
+
results: z.array(z.discriminatedUnion("state", [
|
|
27
|
+
z.strictObject({
|
|
28
|
+
pid: z.number().int().positive(),
|
|
29
|
+
state: z.literal("readable"),
|
|
30
|
+
identity: z.string().min(1),
|
|
31
|
+
reason: z.null().optional(),
|
|
32
|
+
}),
|
|
33
|
+
z.strictObject({
|
|
34
|
+
pid: z.number().int().positive(),
|
|
35
|
+
state: z.literal("unavailable"),
|
|
36
|
+
identity: z.null().optional(),
|
|
37
|
+
reason: z.string().min(1),
|
|
38
|
+
}),
|
|
39
|
+
])),
|
|
40
|
+
});
|
|
41
|
+
const sourceFiles = [
|
|
42
|
+
fileURLToPath(new URL("../../bridge/process/ProcessRunTokenReader.swift", import.meta.url)),
|
|
43
|
+
fileURLToPath(new URL("../../bridge/process/ProcessRunTokenReaderMain.swift", import.meta.url)),
|
|
44
|
+
];
|
|
45
|
+
/** The native Darwin process-ownership reader could not be prepared. */
|
|
46
|
+
export class DarwinProcessOwnershipInspectionError extends Error {
|
|
47
|
+
constructor(message, options) {
|
|
48
|
+
super(message, options);
|
|
49
|
+
this.name = "DarwinProcessOwnershipInspectionError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const abortReason = (signal) => {
|
|
53
|
+
if (signal.reason !== undefined)
|
|
54
|
+
return signal.reason;
|
|
55
|
+
const error = new Error("The operation was aborted");
|
|
56
|
+
error.name = "AbortError";
|
|
57
|
+
return error;
|
|
58
|
+
};
|
|
59
|
+
const isExpectedAbort = (cause, signal) => signal.aborted &&
|
|
60
|
+
(cause === signal.reason ||
|
|
61
|
+
(cause instanceof Error && cause.name === "AbortError"));
|
|
62
|
+
const waitForCompilation = async (promise, signal) => {
|
|
63
|
+
if (signal === undefined)
|
|
64
|
+
return promise;
|
|
65
|
+
if (signal.aborted)
|
|
66
|
+
throw abortReason(signal);
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
let completed = false;
|
|
69
|
+
const finish = (callback) => {
|
|
70
|
+
if (completed)
|
|
71
|
+
return;
|
|
72
|
+
completed = true;
|
|
73
|
+
signal.removeEventListener("abort", onAbort);
|
|
74
|
+
callback();
|
|
75
|
+
};
|
|
76
|
+
const onAbort = () => finish(() => reject(abortReason(signal)));
|
|
77
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
78
|
+
promise.then((value) => finish(() => resolve(value)), (cause) => finish(() => reject(cause)));
|
|
79
|
+
if (signal.aborted)
|
|
80
|
+
onAbort();
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
/** Compile and retain the narrow Darwin sysctl reader for this REA process. */
|
|
84
|
+
export const createDarwinProcessRunTokenReader = (options = {}) => {
|
|
85
|
+
let root;
|
|
86
|
+
let executable;
|
|
87
|
+
let compilation;
|
|
88
|
+
let closePromise;
|
|
89
|
+
let closed = false;
|
|
90
|
+
let exitCleanupInstalled = false;
|
|
91
|
+
const cleanupAtExit = () => {
|
|
92
|
+
if (root !== undefined)
|
|
93
|
+
rmSync(root, { recursive: true, force: true });
|
|
94
|
+
};
|
|
95
|
+
const cleanupRoot = async (target = root) => {
|
|
96
|
+
if (target === undefined)
|
|
97
|
+
return;
|
|
98
|
+
await rm(target, { recursive: true, force: true });
|
|
99
|
+
if (root !== target)
|
|
100
|
+
return;
|
|
101
|
+
root = undefined;
|
|
102
|
+
executable = undefined;
|
|
103
|
+
if (exitCleanupInstalled)
|
|
104
|
+
process.removeListener("exit", cleanupAtExit);
|
|
105
|
+
exitCleanupInstalled = false;
|
|
106
|
+
};
|
|
107
|
+
const runCompilation = async (signal) => {
|
|
108
|
+
let operationRoot;
|
|
109
|
+
try {
|
|
110
|
+
operationRoot = await mkdtemp(join(tmpdir(), "rea-process-token-"));
|
|
111
|
+
root = operationRoot;
|
|
112
|
+
process.once("exit", cleanupAtExit);
|
|
113
|
+
exitCleanupInstalled = true;
|
|
114
|
+
if (signal.aborted || closed)
|
|
115
|
+
throw abortReason(signal);
|
|
116
|
+
const output = join(operationRoot, "reader");
|
|
117
|
+
try {
|
|
118
|
+
await execFileOutput(options.xcrun ?? "/usr/bin/xcrun", [
|
|
119
|
+
"swiftc",
|
|
120
|
+
"-module-cache-path",
|
|
121
|
+
join(operationRoot, "modules"),
|
|
122
|
+
...sourceFiles,
|
|
123
|
+
"-o",
|
|
124
|
+
output,
|
|
125
|
+
], {
|
|
126
|
+
timeout: 60_000,
|
|
127
|
+
maxBuffer: 1024 * 1024,
|
|
128
|
+
signal,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (cause) {
|
|
132
|
+
if (isExpectedAbort(cause, signal))
|
|
133
|
+
throw cause;
|
|
134
|
+
const code = cause instanceof Error && "code" in cause
|
|
135
|
+
? String(cause.code)
|
|
136
|
+
: "unknown";
|
|
137
|
+
throw new DarwinProcessOwnershipInspectionError(`macOS process ownership inspection requires the Apple Swift compiler via xcrun (compiler result: ${code})`, { cause });
|
|
138
|
+
}
|
|
139
|
+
if (signal.aborted || closed)
|
|
140
|
+
throw abortReason(signal);
|
|
141
|
+
executable = output;
|
|
142
|
+
return output;
|
|
143
|
+
}
|
|
144
|
+
catch (cause) {
|
|
145
|
+
const ownsRoot = operationRoot !== undefined && root === operationRoot;
|
|
146
|
+
if (operationRoot !== undefined) {
|
|
147
|
+
if (ownsRoot)
|
|
148
|
+
await cleanupRoot(operationRoot);
|
|
149
|
+
else
|
|
150
|
+
await rm(operationRoot, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
throw cause;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const beginCompilation = () => {
|
|
156
|
+
const controller = new AbortController();
|
|
157
|
+
const operation = {
|
|
158
|
+
controller,
|
|
159
|
+
promise: runCompilation(controller.signal).finally(() => {
|
|
160
|
+
operation.settled = true;
|
|
161
|
+
if (compilation === operation)
|
|
162
|
+
compilation = undefined;
|
|
163
|
+
}),
|
|
164
|
+
waiters: 0,
|
|
165
|
+
aborting: false,
|
|
166
|
+
settled: false,
|
|
167
|
+
};
|
|
168
|
+
compilation = operation;
|
|
169
|
+
return operation;
|
|
170
|
+
};
|
|
171
|
+
const compile = async (signal) => {
|
|
172
|
+
if (closed)
|
|
173
|
+
throw new Error("Darwin process token reader is closed");
|
|
174
|
+
signal?.throwIfAborted();
|
|
175
|
+
while (true) {
|
|
176
|
+
if (closed)
|
|
177
|
+
throw new Error("Darwin process token reader is closed");
|
|
178
|
+
signal?.throwIfAborted();
|
|
179
|
+
if (executable !== undefined)
|
|
180
|
+
return executable;
|
|
181
|
+
if (compilation === undefined && root !== undefined) {
|
|
182
|
+
await cleanupRoot();
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
let operation = compilation;
|
|
186
|
+
if (operation?.aborting === true) {
|
|
187
|
+
try {
|
|
188
|
+
await waitForCompilation(operation.promise, signal);
|
|
189
|
+
}
|
|
190
|
+
catch (cause) {
|
|
191
|
+
if (signal !== undefined && isExpectedAbort(cause, signal))
|
|
192
|
+
throw cause;
|
|
193
|
+
}
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
operation ??= beginCompilation();
|
|
197
|
+
operation.waiters += 1;
|
|
198
|
+
try {
|
|
199
|
+
return await waitForCompilation(operation.promise, signal);
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
operation.waiters -= 1;
|
|
203
|
+
if (operation.waiters === 0 &&
|
|
204
|
+
!operation.settled &&
|
|
205
|
+
!operation.aborting) {
|
|
206
|
+
operation.aborting = true;
|
|
207
|
+
operation.controller.abort();
|
|
208
|
+
await operation.promise.catch(() => undefined);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
const read = async (processes) => {
|
|
214
|
+
if (processes.length === 0)
|
|
215
|
+
return new Map();
|
|
216
|
+
const binary = await compile();
|
|
217
|
+
const pids = processes.map(({ pid }) => String(pid));
|
|
218
|
+
const { stdout } = await execFileOutput(binary, pids, { timeout: 15_000 });
|
|
219
|
+
const decoded = safeParseJson(stdout);
|
|
220
|
+
if (!decoded.ok)
|
|
221
|
+
throw new Error(`macOS process token helper returned invalid JSON: ${decoded.error}`, { cause: decoded.cause });
|
|
222
|
+
const parsed = responseSchema.safeParse(decoded.value);
|
|
223
|
+
if (!parsed.success)
|
|
224
|
+
throw new Error("macOS process token helper returned an invalid response", {
|
|
225
|
+
cause: parsed.error,
|
|
226
|
+
});
|
|
227
|
+
const requested = new Set(processes.map(({ pid }) => pid));
|
|
228
|
+
const observations = new Map();
|
|
229
|
+
for (const result of parsed.data.results) {
|
|
230
|
+
if (!requested.has(result.pid) || observations.has(result.pid))
|
|
231
|
+
throw new Error("macOS process token helper returned an unexpected PID set");
|
|
232
|
+
observations.set(result.pid, result.state === "readable"
|
|
233
|
+
? { state: "readable", runId: result.run_id }
|
|
234
|
+
: {
|
|
235
|
+
state: "unavailable",
|
|
236
|
+
reason: result.reason ?? "process token observation is unavailable",
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (observations.size !== requested.size)
|
|
240
|
+
throw new Error("macOS process token helper returned an incomplete PID set");
|
|
241
|
+
return observations;
|
|
242
|
+
};
|
|
243
|
+
const identities = async (processes, signal) => {
|
|
244
|
+
if (processes.length === 0)
|
|
245
|
+
return new Map();
|
|
246
|
+
const binary = await compile(signal);
|
|
247
|
+
const pids = processes.map(({ pid }) => String(pid));
|
|
248
|
+
const { stdout } = await execFileOutput(binary, ["--identities", ...pids], {
|
|
249
|
+
timeout: 15_000,
|
|
250
|
+
...(signal === undefined ? {} : { signal }),
|
|
251
|
+
});
|
|
252
|
+
const decoded = safeParseJson(stdout);
|
|
253
|
+
if (!decoded.ok)
|
|
254
|
+
throw new Error(`macOS process identity helper returned invalid JSON: ${decoded.error}`, { cause: decoded.cause });
|
|
255
|
+
const parsed = identityResponseSchema.safeParse(decoded.value);
|
|
256
|
+
if (!parsed.success)
|
|
257
|
+
throw new Error("macOS process identity helper returned an invalid response", {
|
|
258
|
+
cause: parsed.error,
|
|
259
|
+
});
|
|
260
|
+
const requested = new Set(processes.map(({ pid }) => pid));
|
|
261
|
+
const observations = new Map();
|
|
262
|
+
for (const result of parsed.data.results) {
|
|
263
|
+
if (!requested.has(result.pid) || observations.has(result.pid))
|
|
264
|
+
throw new Error("macOS process identity helper returned an unexpected PID set");
|
|
265
|
+
observations.set(result.pid, result.state === "readable"
|
|
266
|
+
? { state: "readable", identity: result.identity }
|
|
267
|
+
: { state: "unavailable", reason: result.reason });
|
|
268
|
+
}
|
|
269
|
+
if (observations.size !== requested.size)
|
|
270
|
+
throw new Error("macOS process identity helper returned an incomplete PID set");
|
|
271
|
+
return observations;
|
|
272
|
+
};
|
|
273
|
+
const close = () => {
|
|
274
|
+
if (closePromise !== undefined)
|
|
275
|
+
return closePromise;
|
|
276
|
+
closed = true;
|
|
277
|
+
const pendingClose = (async () => {
|
|
278
|
+
const operation = compilation;
|
|
279
|
+
if (operation !== undefined && !operation.settled) {
|
|
280
|
+
operation.aborting = true;
|
|
281
|
+
operation.controller.abort();
|
|
282
|
+
await operation.promise.catch(() => undefined);
|
|
283
|
+
}
|
|
284
|
+
await cleanupRoot();
|
|
285
|
+
})().catch((cause) => {
|
|
286
|
+
if (closePromise === pendingClose)
|
|
287
|
+
closePromise = undefined;
|
|
288
|
+
throw cause;
|
|
289
|
+
});
|
|
290
|
+
closePromise = pendingClose;
|
|
291
|
+
return closePromise;
|
|
292
|
+
};
|
|
293
|
+
return { read, identities, prepare: compile, close };
|
|
294
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ProviderProcessSupervisor, spawnOwnedProviderProcess, } from "./ProviderProcess.js";
|
|
2
|
+
import { cleanupOwnedProcessGroup } from "./ProcessOwnership.js";
|
|
3
|
+
import { ProviderStartupDeadline, waitForAbortableDelay, } from "./ProviderDeadline.js";
|
|
4
|
+
/** A short-lived command failed its lifecycle or complete-output contract. */
|
|
5
|
+
export class OwnedCommandFailure extends Error {
|
|
6
|
+
reason;
|
|
7
|
+
snapshot;
|
|
8
|
+
cleanupFailure;
|
|
9
|
+
resources;
|
|
10
|
+
constructor(reason, message, snapshot = null, cleanupFailure = null, options, resources = []) {
|
|
11
|
+
super(message, options);
|
|
12
|
+
this.reason = reason;
|
|
13
|
+
this.snapshot = snapshot;
|
|
14
|
+
this.cleanupFailure = cleanupFailure;
|
|
15
|
+
this.resources = resources;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Execute a single owned process with independent cleanup and bounded diagnostic retention. */
|
|
19
|
+
export const runOwnedCommand = async (spawn, limits, options = {}) => {
|
|
20
|
+
const callerSignal = options.signal ?? spawn.signal;
|
|
21
|
+
if (callerSignal?.aborted)
|
|
22
|
+
throw new OwnedCommandFailure("cancelled", "Command cancelled before launch.");
|
|
23
|
+
const deadline = new ProviderStartupDeadline(limits.timeoutMs, callerSignal);
|
|
24
|
+
try {
|
|
25
|
+
return await collectOwnedCommand(spawn, limits, options.launcher ?? spawnOwnedProviderProcess, deadline, callerSignal);
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
deadline.dispose();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const collectOwnedCommand = async (spawn, limits, launcher, deadline, callerSignal) => {
|
|
32
|
+
const signal = deadline.signal;
|
|
33
|
+
let launched;
|
|
34
|
+
try {
|
|
35
|
+
launched = await launcher({
|
|
36
|
+
...spawn,
|
|
37
|
+
signal,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (cause) {
|
|
41
|
+
const interruption = deadline.interruption;
|
|
42
|
+
if (interruption !== undefined)
|
|
43
|
+
throw new OwnedCommandFailure(interruption, interruption === "cancelled"
|
|
44
|
+
? "Command cancelled during ownership preparation."
|
|
45
|
+
: "Command deadline elapsed during ownership preparation.", null, null, { cause });
|
|
46
|
+
throw cause;
|
|
47
|
+
}
|
|
48
|
+
let exceeded = false;
|
|
49
|
+
let processFailure;
|
|
50
|
+
const supervisor = new ProviderProcessSupervisor({
|
|
51
|
+
...launched,
|
|
52
|
+
ownsProcessLifetime: true,
|
|
53
|
+
cleanup: launched.cleanup ??
|
|
54
|
+
(() => cleanupOwnedProcessGroup(launched.ownership)),
|
|
55
|
+
}, {
|
|
56
|
+
maxDiagnosticBytes: limits.diagnosticBytes,
|
|
57
|
+
onDiagnostic: (event) => {
|
|
58
|
+
if (event.type === "output" && event.truncated === true)
|
|
59
|
+
exceeded = true;
|
|
60
|
+
if (event.type === "error")
|
|
61
|
+
processFailure = event.message;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
let failure;
|
|
65
|
+
try {
|
|
66
|
+
while (!(await supervisor.waitForOutputClose(10))) {
|
|
67
|
+
const interruption = deadline.interruption;
|
|
68
|
+
if (interruption !== undefined)
|
|
69
|
+
throw new OwnedCommandFailure(interruption, interruption === "cancelled"
|
|
70
|
+
? "Command cancelled."
|
|
71
|
+
: "Command deadline elapsed.");
|
|
72
|
+
if (exceeded)
|
|
73
|
+
throw new OwnedCommandFailure("output-limit", "Command diagnostic output exceeded its complete-output budget.");
|
|
74
|
+
if (processFailure !== undefined)
|
|
75
|
+
throw new OwnedCommandFailure("process", processFailure);
|
|
76
|
+
await waitForAbortableDelay(25, signal);
|
|
77
|
+
}
|
|
78
|
+
const snapshot = supervisor.snapshot();
|
|
79
|
+
const interruption = deadline.interruption;
|
|
80
|
+
if (interruption !== undefined)
|
|
81
|
+
throw new OwnedCommandFailure(interruption, interruption === "cancelled"
|
|
82
|
+
? "Command cancelled."
|
|
83
|
+
: "Command deadline elapsed.", snapshot);
|
|
84
|
+
if (exceeded)
|
|
85
|
+
throw new OwnedCommandFailure("output-limit", "Command diagnostic output exceeded its complete-output budget.", snapshot);
|
|
86
|
+
if (processFailure !== undefined ||
|
|
87
|
+
snapshot.exitCode !== 0 ||
|
|
88
|
+
snapshot.signal !== null)
|
|
89
|
+
throw new OwnedCommandFailure("process", processFailure ??
|
|
90
|
+
"Command failed; inspect its exit status and diagnostics.", snapshot);
|
|
91
|
+
}
|
|
92
|
+
catch (cause) {
|
|
93
|
+
failure =
|
|
94
|
+
cause instanceof OwnedCommandFailure
|
|
95
|
+
? cause
|
|
96
|
+
: new OwnedCommandFailure("process", "Command collection failed.", null, null, { cause });
|
|
97
|
+
}
|
|
98
|
+
const stopped = await supervisor.stop();
|
|
99
|
+
const snapshot = supervisor.snapshot();
|
|
100
|
+
supervisor.dispose();
|
|
101
|
+
if (stopped.status === "incomplete")
|
|
102
|
+
throw new OwnedCommandFailure(failure?.reason ?? "process", failure?.message ?? "Command cleanup failed.", snapshot, stopped.reason, failure === undefined ? undefined : { cause: failure }, [
|
|
103
|
+
spawn.runId,
|
|
104
|
+
...(spawn.cwd === undefined ? [] : [spawn.cwd]),
|
|
105
|
+
`pid:${launched.ownership.leaderPid}`,
|
|
106
|
+
`process-group:${launched.ownership.processGroupId}`,
|
|
107
|
+
]);
|
|
108
|
+
if (failure !== undefined)
|
|
109
|
+
throw new OwnedCommandFailure(failure.reason, failure.message, snapshot, failure.cleanupFailure, { cause: failure }, failure.resources);
|
|
110
|
+
if (callerSignal?.aborted)
|
|
111
|
+
throw new OwnedCommandFailure("cancelled", "Command cancelled.", snapshot);
|
|
112
|
+
return snapshot;
|
|
113
|
+
};
|