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,266 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { recordedNoteCoverageIssues } from "./recordedCrashNoteCoverage.js";
|
|
3
|
+
const hex = z.string().regex(/^0x(?:0|[1-9a-f][0-9a-f]{0,15})$/);
|
|
4
|
+
const index = z.number().int().nonnegative();
|
|
5
|
+
const signed32 = z.number().int().min(-2147483648).max(2147483647);
|
|
6
|
+
const base64 = z
|
|
7
|
+
.string()
|
|
8
|
+
.regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/][AQgw]==|[A-Za-z0-9+/]{2}[AEIMQUYcgkosw048]=)?$/);
|
|
9
|
+
const range = z.strictObject({ offset: hex, bytes: hex });
|
|
10
|
+
const diagnostics = z.strictObject({
|
|
11
|
+
stdout: z.string(),
|
|
12
|
+
stderr: z.string(),
|
|
13
|
+
truncated: z.boolean(),
|
|
14
|
+
});
|
|
15
|
+
// Exact b"CORE\0"; a lossy display label cannot establish the Linux note ABI.
|
|
16
|
+
const LINUX_CORE_OWNER_BASE64 = "Q09SRQA=";
|
|
17
|
+
/** Complete register identities in the supported Linux amd64 PRSTATUS ABI order. */
|
|
18
|
+
export const RECORDED_LINUX_AMD64_REGISTERS = [
|
|
19
|
+
"r15",
|
|
20
|
+
"r14",
|
|
21
|
+
"r13",
|
|
22
|
+
"r12",
|
|
23
|
+
"rbp",
|
|
24
|
+
"rbx",
|
|
25
|
+
"r11",
|
|
26
|
+
"r10",
|
|
27
|
+
"r9",
|
|
28
|
+
"r8",
|
|
29
|
+
"rax",
|
|
30
|
+
"rcx",
|
|
31
|
+
"rdx",
|
|
32
|
+
"rsi",
|
|
33
|
+
"rdi",
|
|
34
|
+
"orig_rax",
|
|
35
|
+
"rip",
|
|
36
|
+
"cs",
|
|
37
|
+
"eflags",
|
|
38
|
+
"rsp",
|
|
39
|
+
"ss",
|
|
40
|
+
"fs_base",
|
|
41
|
+
"gs_base",
|
|
42
|
+
"ds",
|
|
43
|
+
"es",
|
|
44
|
+
"fs",
|
|
45
|
+
"gs",
|
|
46
|
+
];
|
|
47
|
+
const supportedRegisters = new Set(RECORDED_LINUX_AMD64_REGISTERS);
|
|
48
|
+
/** Select an explicit recording and optionally request debugger-derived mapping context. */
|
|
49
|
+
export const inspectRecordedCrashInputSchema = z.strictObject({
|
|
50
|
+
path: z
|
|
51
|
+
.string()
|
|
52
|
+
.min(1)
|
|
53
|
+
.describe("Absolute filesystem path to a supplied Linux ELF core"),
|
|
54
|
+
include_debugger_context: z
|
|
55
|
+
.boolean()
|
|
56
|
+
.default(false)
|
|
57
|
+
.describe("Add core-only mapping candidates through caller-supplied GDB/pwndbg"),
|
|
58
|
+
});
|
|
59
|
+
/** Debugger map candidates preserve reported flags; zero does not establish absent permissions. */
|
|
60
|
+
export const recordedCrashDebuggerSchema = z.discriminatedUnion("status", [
|
|
61
|
+
z.strictObject({ status: z.literal("not-requested") }),
|
|
62
|
+
z.strictObject({
|
|
63
|
+
status: z.literal("available"),
|
|
64
|
+
gdb_version: z.string().min(1),
|
|
65
|
+
pwndbg_version: z.string().min(1),
|
|
66
|
+
connection: z.literal("core"),
|
|
67
|
+
executable: z.null(),
|
|
68
|
+
historical_pid: signed32,
|
|
69
|
+
reported_limits: z.strictObject({
|
|
70
|
+
address_space_bytes: index,
|
|
71
|
+
cpu_seconds: index,
|
|
72
|
+
file_size_bytes: index,
|
|
73
|
+
}),
|
|
74
|
+
maps: z.array(z.strictObject({
|
|
75
|
+
start: hex,
|
|
76
|
+
end: hex,
|
|
77
|
+
offset: hex,
|
|
78
|
+
reported_flags: index,
|
|
79
|
+
permissions: z
|
|
80
|
+
.strictObject({
|
|
81
|
+
read: z.boolean(),
|
|
82
|
+
write: z.boolean(),
|
|
83
|
+
execute: z.boolean(),
|
|
84
|
+
})
|
|
85
|
+
.nullable(),
|
|
86
|
+
pathname_display: z.string(),
|
|
87
|
+
current_file_identity: z.literal("unknown"),
|
|
88
|
+
})),
|
|
89
|
+
confidence: z.literal("derived"),
|
|
90
|
+
completeness: z.literal("unknown"),
|
|
91
|
+
diagnostics,
|
|
92
|
+
}),
|
|
93
|
+
]);
|
|
94
|
+
/** Portable recorded observations; registers and source coordinates remain lossless. */
|
|
95
|
+
const recordedCrashObjectSchema = z.strictObject({
|
|
96
|
+
artifact: z.strictObject({
|
|
97
|
+
path: z.string().min(1),
|
|
98
|
+
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
99
|
+
bytes: index,
|
|
100
|
+
}),
|
|
101
|
+
format: z.literal("elf-core"),
|
|
102
|
+
architecture: z.literal("x86_64-little-endian"),
|
|
103
|
+
target_execution: z.literal("not-performed"),
|
|
104
|
+
live_process_identity: z.literal("unknown"),
|
|
105
|
+
segments: z.array(z.strictObject({
|
|
106
|
+
index,
|
|
107
|
+
type: z.union([z.string(), index]),
|
|
108
|
+
header_location: range,
|
|
109
|
+
offset: hex,
|
|
110
|
+
file_size: hex,
|
|
111
|
+
memory_size: hex,
|
|
112
|
+
virtual_address: hex,
|
|
113
|
+
physical_address: hex,
|
|
114
|
+
alignment: hex,
|
|
115
|
+
flags: hex,
|
|
116
|
+
file_backing: z.enum(["file", "none"]),
|
|
117
|
+
})),
|
|
118
|
+
notes: z.array(z.strictObject({
|
|
119
|
+
index,
|
|
120
|
+
segment_index: index,
|
|
121
|
+
type: z.union([z.string(), index]),
|
|
122
|
+
location: range,
|
|
123
|
+
owner_location: range,
|
|
124
|
+
owner_bytes_base64: base64,
|
|
125
|
+
owner_display: z.string().nullable(),
|
|
126
|
+
descriptor_location: range,
|
|
127
|
+
descriptor_bytes_base64: base64,
|
|
128
|
+
})),
|
|
129
|
+
note_padding: z.array(z.strictObject({
|
|
130
|
+
segment_index: index,
|
|
131
|
+
location: range,
|
|
132
|
+
bytes_base64: base64,
|
|
133
|
+
})),
|
|
134
|
+
threads: z.array(z.strictObject({
|
|
135
|
+
note_index: index,
|
|
136
|
+
historical_pid: signed32,
|
|
137
|
+
recorded_current_signal: z.number().int().min(-32768).max(32767),
|
|
138
|
+
registers: z.array(z.strictObject({
|
|
139
|
+
name: z.string().min(1),
|
|
140
|
+
value: hex,
|
|
141
|
+
location: range,
|
|
142
|
+
})),
|
|
143
|
+
})),
|
|
144
|
+
signals: z.array(z.strictObject({
|
|
145
|
+
note_index: index,
|
|
146
|
+
number: signed32,
|
|
147
|
+
code: signed32,
|
|
148
|
+
errno: signed32,
|
|
149
|
+
fault_address: hex.nullable(),
|
|
150
|
+
fault_address_meaning: z.enum(["recorded-sigsegv-address", "unknown"]),
|
|
151
|
+
thread_association: z.literal("unknown"),
|
|
152
|
+
})),
|
|
153
|
+
debugger: recordedCrashDebuggerSchema,
|
|
154
|
+
note_interpretation_completeness: z.literal("unknown"),
|
|
155
|
+
decoder_diagnostics: diagnostics,
|
|
156
|
+
diagnostics,
|
|
157
|
+
limitations: z.array(z.string()),
|
|
158
|
+
});
|
|
159
|
+
/** Provider payload excludes identity and diagnostics bound by the owned snapshot boundary. */
|
|
160
|
+
export const recordedCrashPayloadSchema = recordedCrashObjectSchema.omit({
|
|
161
|
+
artifact: true,
|
|
162
|
+
diagnostics: true,
|
|
163
|
+
debugger: true,
|
|
164
|
+
decoder_diagnostics: true,
|
|
165
|
+
});
|
|
166
|
+
/** Validate the next consumer's file ranges, note links and register representations. */
|
|
167
|
+
export const recordedCrashSchema = recordedCrashObjectSchema.superRefine((value, context) => {
|
|
168
|
+
const fail = (message) => context.addIssue({ code: "custom", message });
|
|
169
|
+
const within = (item, start = 0n, end = BigInt(value.artifact.bytes)) => {
|
|
170
|
+
const offset = BigInt(item.offset);
|
|
171
|
+
const bytes = BigInt(item.bytes);
|
|
172
|
+
return offset >= start && offset <= end && bytes <= end - offset;
|
|
173
|
+
};
|
|
174
|
+
for (const issue of recordedNoteCoverageIssues(value.segments, value.notes, value.note_padding))
|
|
175
|
+
fail(issue);
|
|
176
|
+
for (const [position, segment] of value.segments.entries()) {
|
|
177
|
+
if (position !== segment.index || !within(segment.header_location))
|
|
178
|
+
fail("Invalid original segment identity or header range.");
|
|
179
|
+
if (segment.file_backing === "file" &&
|
|
180
|
+
!within({ offset: segment.offset, bytes: segment.file_size }))
|
|
181
|
+
fail("Recorded segment exceeds artifact bytes.");
|
|
182
|
+
if (segment.type === "PT_LOAD" &&
|
|
183
|
+
BigInt(segment.file_size) > BigInt(segment.memory_size))
|
|
184
|
+
fail("Recorded PT_LOAD file size exceeds memory size.");
|
|
185
|
+
}
|
|
186
|
+
for (const [position, note] of value.notes.entries()) {
|
|
187
|
+
const segment = value.segments[note.segment_index];
|
|
188
|
+
if (position !== note.index || segment?.type !== "PT_NOTE") {
|
|
189
|
+
fail("Invalid original note identity or segment reference.");
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const decodedLength = (raw) => BigInt((raw.length / 4) * 3 -
|
|
193
|
+
(raw.endsWith("==") ? 2 : raw.endsWith("=") ? 1 : 0));
|
|
194
|
+
if (decodedLength(note.owner_bytes_base64) !==
|
|
195
|
+
BigInt(note.owner_location.bytes) ||
|
|
196
|
+
decodedLength(note.descriptor_bytes_base64) !==
|
|
197
|
+
BigInt(note.descriptor_location.bytes))
|
|
198
|
+
fail("Raw note lengths differ from their source ranges.");
|
|
199
|
+
const start = BigInt(segment.offset);
|
|
200
|
+
const end = start + BigInt(segment.file_size);
|
|
201
|
+
if (!within(note.location, start, end) ||
|
|
202
|
+
!within(note.owner_location, BigInt(note.location.offset), BigInt(note.location.offset) + BigInt(note.location.bytes)) ||
|
|
203
|
+
!within(note.descriptor_location, BigInt(note.location.offset), BigInt(note.location.offset) + BigInt(note.location.bytes)))
|
|
204
|
+
fail("Note source ranges exceed their recorded segment.");
|
|
205
|
+
}
|
|
206
|
+
for (const padding of value.note_padding) {
|
|
207
|
+
const segment = value.segments[padding.segment_index];
|
|
208
|
+
if (segment?.type !== "PT_NOTE" ||
|
|
209
|
+
!within(padding.location, BigInt(segment.offset), BigInt(segment.offset) + BigInt(segment.file_size)))
|
|
210
|
+
fail("Invalid recorded note padding range.");
|
|
211
|
+
}
|
|
212
|
+
for (const [kind, rows] of [
|
|
213
|
+
["NT_PRSTATUS", value.threads],
|
|
214
|
+
["NT_SIGINFO", value.signals],
|
|
215
|
+
]) {
|
|
216
|
+
const matchingNotes = value.notes.filter((note) => note.type === kind &&
|
|
217
|
+
note.owner_bytes_base64 === LINUX_CORE_OWNER_BASE64);
|
|
218
|
+
if (rows.length !== matchingNotes.length ||
|
|
219
|
+
new Set(rows.map(({ note_index }) => note_index)).size !== rows.length)
|
|
220
|
+
fail("Each supported raw note requires exactly one corresponding interpretation.");
|
|
221
|
+
}
|
|
222
|
+
for (const thread of value.threads) {
|
|
223
|
+
const note = value.notes[thread.note_index];
|
|
224
|
+
if (note?.type !== "NT_PRSTATUS" ||
|
|
225
|
+
note.owner_display !== "CORE" ||
|
|
226
|
+
note.owner_bytes_base64 !== LINUX_CORE_OWNER_BASE64) {
|
|
227
|
+
fail("Thread references a different note kind.");
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (thread.registers.length !== RECORDED_LINUX_AMD64_REGISTERS.length ||
|
|
231
|
+
!thread.registers.every(({ name }) => supportedRegisters.has(name)) ||
|
|
232
|
+
new Set(thread.registers.map(({ name }) => name)).size !==
|
|
233
|
+
thread.registers.length)
|
|
234
|
+
fail("Each recorded thread requires every supported register exactly once.");
|
|
235
|
+
for (const register of thread.registers)
|
|
236
|
+
if (BigInt(register.location.bytes) !== 8n ||
|
|
237
|
+
!within(register.location, BigInt(note.descriptor_location.offset), BigInt(note.descriptor_location.offset) +
|
|
238
|
+
BigInt(note.descriptor_location.bytes)))
|
|
239
|
+
fail("Register source exceeds its descriptor.");
|
|
240
|
+
}
|
|
241
|
+
for (const signal of value.signals) {
|
|
242
|
+
if (value.notes[signal.note_index]?.type !== "NT_SIGINFO" ||
|
|
243
|
+
value.notes[signal.note_index]?.owner_display !== "CORE" ||
|
|
244
|
+
value.notes[signal.note_index]?.owner_bytes_base64 !==
|
|
245
|
+
LINUX_CORE_OWNER_BASE64)
|
|
246
|
+
fail("Signal references a different note kind.");
|
|
247
|
+
const known = signal.number === 11 && (signal.code === 1 || signal.code === 2);
|
|
248
|
+
if (known !== (signal.fault_address !== null) ||
|
|
249
|
+
(signal.fault_address !== null) !==
|
|
250
|
+
(signal.fault_address_meaning === "recorded-sigsegv-address"))
|
|
251
|
+
fail("Signal union meaning does not match recorded SIGSEGV code.");
|
|
252
|
+
}
|
|
253
|
+
if (value.debugger.status === "available")
|
|
254
|
+
for (const map of value.debugger.maps) {
|
|
255
|
+
if (BigInt(map.end) < BigInt(map.start))
|
|
256
|
+
fail("Debugger mapping end precedes its start.");
|
|
257
|
+
if (map.permissions !== null &&
|
|
258
|
+
(map.permissions.read !== Boolean(map.reported_flags & 4) ||
|
|
259
|
+
map.permissions.write !== Boolean(map.reported_flags & 2) ||
|
|
260
|
+
map.permissions.execute !== Boolean(map.reported_flags & 1)))
|
|
261
|
+
fail("Mapping permissions differ from reported flags.");
|
|
262
|
+
if ((map.reported_flags === 0 || map.reported_flags > 7) !==
|
|
263
|
+
(map.permissions === null))
|
|
264
|
+
fail("Unreported or unfamiliar map flags require unknown permissions.");
|
|
265
|
+
}
|
|
266
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const align = (size) => ((size + 3n) / 4n) * 4n;
|
|
2
|
+
const layoutMatches = (note) => {
|
|
3
|
+
const start = BigInt(note.location.offset);
|
|
4
|
+
const owner = BigInt(note.owner_location.bytes);
|
|
5
|
+
const descriptor = BigInt(note.descriptor_location.bytes);
|
|
6
|
+
return (BigInt(note.owner_location.offset) === start + 12n &&
|
|
7
|
+
BigInt(note.descriptor_location.offset) === start + 12n + align(owner) &&
|
|
8
|
+
BigInt(note.location.bytes) === 12n + align(owner) + align(descriptor));
|
|
9
|
+
};
|
|
10
|
+
const paddingMatches = (item, segment) => {
|
|
11
|
+
const bytes = BigInt(item.location.bytes);
|
|
12
|
+
const decodedLength = (item.bytes_base64.length / 4) * 3 -
|
|
13
|
+
(item.bytes_base64.endsWith("==")
|
|
14
|
+
? 2
|
|
15
|
+
: item.bytes_base64.endsWith("=")
|
|
16
|
+
? 1
|
|
17
|
+
: 0);
|
|
18
|
+
return (bytes > 0n &&
|
|
19
|
+
bytes < 12n &&
|
|
20
|
+
BigInt(decodedLength) === bytes &&
|
|
21
|
+
/^A*={0,2}$/.test(item.bytes_base64) &&
|
|
22
|
+
segment !== undefined &&
|
|
23
|
+
BigInt(item.location.offset) + bytes ===
|
|
24
|
+
BigInt(segment.offset) + BigInt(segment.file_size));
|
|
25
|
+
};
|
|
26
|
+
/** Require exact aligned note layouts and one complete, nonoverlapping physical coverage. */
|
|
27
|
+
export function recordedNoteCoverageIssues(segments, notes, padding) {
|
|
28
|
+
const issues = [];
|
|
29
|
+
const bySegment = new Map();
|
|
30
|
+
const all = [];
|
|
31
|
+
const add = (segment, range) => {
|
|
32
|
+
const ranges = bySegment.get(segment) ?? [];
|
|
33
|
+
ranges.push(range);
|
|
34
|
+
bySegment.set(segment, ranges);
|
|
35
|
+
all.push(range);
|
|
36
|
+
};
|
|
37
|
+
for (const note of notes) {
|
|
38
|
+
if (!layoutMatches(note))
|
|
39
|
+
issues.push("Recorded note layout differs from its aligned header/owner/descriptor ranges.");
|
|
40
|
+
add(note.segment_index, note.location);
|
|
41
|
+
}
|
|
42
|
+
for (const item of padding) {
|
|
43
|
+
if (!paddingMatches(item, segments[item.segment_index]))
|
|
44
|
+
issues.push("Recorded note padding must be a short zero-filled segment tail.");
|
|
45
|
+
add(item.segment_index, item.location);
|
|
46
|
+
}
|
|
47
|
+
const compare = (left, right) => BigInt(left.offset) < BigInt(right.offset)
|
|
48
|
+
? -1
|
|
49
|
+
: BigInt(left.offset) > BigInt(right.offset)
|
|
50
|
+
? 1
|
|
51
|
+
: 0;
|
|
52
|
+
let end = 0n;
|
|
53
|
+
for (const range of all.sort(compare)) {
|
|
54
|
+
const start = BigInt(range.offset);
|
|
55
|
+
if (start < end)
|
|
56
|
+
issues.push("Physical recorded note/padding ranges overlap or repeat.");
|
|
57
|
+
if (start + BigInt(range.bytes) > end)
|
|
58
|
+
end = start + BigInt(range.bytes);
|
|
59
|
+
}
|
|
60
|
+
for (const segment of segments) {
|
|
61
|
+
if (segment.type !== "PT_NOTE")
|
|
62
|
+
continue;
|
|
63
|
+
let cursor = BigInt(segment.offset);
|
|
64
|
+
for (const range of (bySegment.get(segment.index) ?? []).sort(compare)) {
|
|
65
|
+
if (BigInt(range.offset) !== cursor)
|
|
66
|
+
issues.push("Recorded note/padding ranges leave a gap or overlap in their segment.");
|
|
67
|
+
cursor = BigInt(range.offset) + BigInt(range.bytes);
|
|
68
|
+
}
|
|
69
|
+
if (cursor !== BigInt(segment.offset) + BigInt(segment.file_size))
|
|
70
|
+
issues.push("Recorded note/padding ranges do not completely cover their segment.");
|
|
71
|
+
}
|
|
72
|
+
return issues;
|
|
73
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { jsonValueSchema } from "
|
|
2
|
+
import { jsonValueSchema } from "../jsonValue.js";
|
|
3
3
|
import { normalizationSchema } from "./processScenario.js";
|
|
4
4
|
import { collectProcessCaptureIssues } from "./processCaptureValidation.js";
|
|
5
5
|
export * from "./processScenario.js";
|
|
@@ -61,6 +61,18 @@ const fileEffectSchema = z.discriminatedUnion("status", [
|
|
|
61
61
|
}),
|
|
62
62
|
]);
|
|
63
63
|
/** Exact serialized shape of a process capture. */
|
|
64
|
+
const processSettlementSchema = z.discriminatedUnion("state", [
|
|
65
|
+
z.object({
|
|
66
|
+
state: z.literal("quiesced"),
|
|
67
|
+
elapsed_ms: z.number().int().nonnegative(),
|
|
68
|
+
cleanup_outcome: z.enum(["not_required", "failed"]),
|
|
69
|
+
}),
|
|
70
|
+
z.object({
|
|
71
|
+
state: z.enum(["alive_at_deadline", "unverifiable"]),
|
|
72
|
+
elapsed_ms: z.number().int().nonnegative(),
|
|
73
|
+
cleanup_outcome: z.enum(["cleaned", "failed"]),
|
|
74
|
+
}),
|
|
75
|
+
]);
|
|
64
76
|
const processCaptureShapeSchema = z.strictObject({
|
|
65
77
|
manifest: z.strictObject({
|
|
66
78
|
rea_version: z.string().min(1),
|
|
@@ -74,7 +86,22 @@ const processCaptureShapeSchema = z.strictObject({
|
|
|
74
86
|
comparison_contract: z.record(z.string(), jsonValueSchema),
|
|
75
87
|
full_scenario_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
76
88
|
comparison_contract_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
77
|
-
|
|
89
|
+
selected_executable_sha256: z
|
|
90
|
+
.string()
|
|
91
|
+
.regex(/^[a-f0-9]{64}$/u)
|
|
92
|
+
.nullable(),
|
|
93
|
+
executable_sha256: z
|
|
94
|
+
.string()
|
|
95
|
+
.regex(/^[a-f0-9]{64}$/u)
|
|
96
|
+
.nullable(),
|
|
97
|
+
legacy_executable_sha256: z
|
|
98
|
+
.string()
|
|
99
|
+
.regex(/^[a-f0-9]{64}$/u)
|
|
100
|
+
.optional(),
|
|
101
|
+
executable_identity: z.strictObject({
|
|
102
|
+
state: z.enum(["path_metadata_unchanged", "unknown"]),
|
|
103
|
+
reason: z.string().nullable(),
|
|
104
|
+
}),
|
|
78
105
|
normalization_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
79
106
|
}),
|
|
80
107
|
normalization: normalizationSchema,
|
|
@@ -164,9 +191,119 @@ const processCaptureShapeSchema = z.strictObject({
|
|
|
164
191
|
temporary_root: z.literal("removed"),
|
|
165
192
|
}),
|
|
166
193
|
});
|
|
194
|
+
const processCleanupResourceSchema = z.strictObject({
|
|
195
|
+
state: z.enum(["cleaned", "failed", "unverified", "not_required"]),
|
|
196
|
+
reason: z.string().nullable(),
|
|
197
|
+
});
|
|
198
|
+
const partialObservationFieldSchema = (value) => z.discriminatedUnion("state", [
|
|
199
|
+
z.strictObject({ state: z.literal("available"), value }),
|
|
200
|
+
z.strictObject({ state: z.literal("unavailable"), reason: z.string() }),
|
|
201
|
+
]);
|
|
202
|
+
const incompleteProcessCaptureObservationsSchema = z.strictObject({
|
|
203
|
+
target_pid: partialObservationFieldSchema(z.number().int().positive()),
|
|
204
|
+
frames: partialObservationFieldSchema(processCaptureShapeSchema.shape.frames),
|
|
205
|
+
rendered_frames: partialObservationFieldSchema(processCaptureShapeSchema.shape.rendered_frames),
|
|
206
|
+
interaction_events: partialObservationFieldSchema(processCaptureShapeSchema.shape.interaction_events),
|
|
207
|
+
exit: partialObservationFieldSchema(z.strictObject({
|
|
208
|
+
code: z.number().int().nullable(),
|
|
209
|
+
signal: z.number().int().nullable(),
|
|
210
|
+
reason: z.enum(["exited", "timeout", "idle_timeout", "cancelled"]),
|
|
211
|
+
})),
|
|
212
|
+
settlement: partialObservationFieldSchema(z.strictObject({
|
|
213
|
+
state: z.enum(["quiesced", "alive_at_deadline", "unverifiable"]),
|
|
214
|
+
elapsed_ms: z.number().int().nonnegative(),
|
|
215
|
+
})),
|
|
216
|
+
process_samples: partialObservationFieldSchema(processCaptureShapeSchema.shape.process_samples),
|
|
217
|
+
filesystem_snapshots: z.strictObject({
|
|
218
|
+
before: partialObservationFieldSchema(z.strictObject({
|
|
219
|
+
files: z.array(fileStateSchema),
|
|
220
|
+
truncated: z.boolean(),
|
|
221
|
+
})),
|
|
222
|
+
after: partialObservationFieldSchema(z.strictObject({
|
|
223
|
+
files: z.array(fileStateSchema),
|
|
224
|
+
truncated: z.boolean(),
|
|
225
|
+
})),
|
|
226
|
+
}),
|
|
227
|
+
event_journal: partialObservationFieldSchema(processCaptureShapeSchema.shape.event_journal),
|
|
228
|
+
manifest: partialObservationFieldSchema(processCaptureShapeSchema.shape.manifest),
|
|
229
|
+
});
|
|
230
|
+
const partialCleanupReportSchema = z.strictObject({
|
|
231
|
+
owned_process_group: processCleanupResourceSchema,
|
|
232
|
+
terminal_renderer: processCleanupResourceSchema,
|
|
233
|
+
temporary_root: processCleanupResourceSchema,
|
|
234
|
+
});
|
|
235
|
+
const partialObservationMetadataSchema = z.strictObject({
|
|
236
|
+
cleanup: partialCleanupReportSchema,
|
|
237
|
+
execution_failure: z.string().nullable(),
|
|
238
|
+
});
|
|
239
|
+
const completedCaptureObservationSchema = z.strictObject({
|
|
240
|
+
capture: processCaptureShapeSchema
|
|
241
|
+
.omit({ cleanup: true })
|
|
242
|
+
.extend({ settlement: processSettlementSchema }),
|
|
243
|
+
});
|
|
244
|
+
const incompleteCaptureObservationSchema = z.strictObject({
|
|
245
|
+
observations: incompleteProcessCaptureObservationsSchema,
|
|
246
|
+
});
|
|
247
|
+
/** Validated, non-comparable process observations attached to cleanup errors. */
|
|
248
|
+
export const partialProcessCaptureObservationSchema = z
|
|
249
|
+
.union([
|
|
250
|
+
completedCaptureObservationSchema.extend(partialObservationMetadataSchema.shape),
|
|
251
|
+
incompleteCaptureObservationSchema.extend(partialObservationMetadataSchema.shape),
|
|
252
|
+
])
|
|
253
|
+
.superRefine((partial, context) => {
|
|
254
|
+
if (!Object.values(partial.cleanup).some(({ state }) => state === "failed" || state === "unverified"))
|
|
255
|
+
context.addIssue({
|
|
256
|
+
code: "custom",
|
|
257
|
+
path: ["cleanup"],
|
|
258
|
+
message: "partial observations require incomplete resource cleanup",
|
|
259
|
+
});
|
|
260
|
+
if ("observations" in partial) {
|
|
261
|
+
const { observations } = partial;
|
|
262
|
+
if (![
|
|
263
|
+
observations.frames,
|
|
264
|
+
observations.interaction_events,
|
|
265
|
+
observations.process_samples,
|
|
266
|
+
observations.filesystem_snapshots.before,
|
|
267
|
+
observations.event_journal,
|
|
268
|
+
].some(({ state }) => state === "available"))
|
|
269
|
+
context.addIssue({
|
|
270
|
+
code: "custom",
|
|
271
|
+
path: ["observations"],
|
|
272
|
+
message: "incomplete observations contain no collected evidence",
|
|
273
|
+
});
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const { capture } = partial;
|
|
277
|
+
const settlement = capture.settlement.state === "quiesced"
|
|
278
|
+
? {
|
|
279
|
+
...capture.settlement,
|
|
280
|
+
cleanup_outcome: "not_required",
|
|
281
|
+
}
|
|
282
|
+
: { ...capture.settlement, cleanup_outcome: "cleaned" };
|
|
283
|
+
const validationCapture = {
|
|
284
|
+
...capture,
|
|
285
|
+
settlement,
|
|
286
|
+
cleanup: {
|
|
287
|
+
owned_process_group: "verified",
|
|
288
|
+
temporary_root: "removed",
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
for (const issue of collectProcessCaptureIssues(validationCapture))
|
|
292
|
+
context.addIssue({
|
|
293
|
+
code: "custom",
|
|
294
|
+
path: ["capture", ...issue.path.split(".")],
|
|
295
|
+
message: issue.message,
|
|
296
|
+
});
|
|
297
|
+
});
|
|
167
298
|
/** Exact serialized shape plus all process-capture semantic invariants. */
|
|
168
299
|
export const processCaptureSchema = processCaptureShapeSchema
|
|
169
300
|
.superRefine((capture, context) => {
|
|
301
|
+
if (capture.settlement.cleanup_outcome === "failed")
|
|
302
|
+
context.addIssue({
|
|
303
|
+
code: "custom",
|
|
304
|
+
path: ["settlement", "cleanup_outcome"],
|
|
305
|
+
message: "verified captures cannot contain failed cleanup",
|
|
306
|
+
});
|
|
170
307
|
for (const issue of collectProcessCaptureIssues(capture))
|
|
171
308
|
context.addIssue({
|
|
172
309
|
code: "custom",
|
|
@@ -22,7 +22,12 @@ export const EMPTY_PROCESS_CAPTURE_EXAMPLE = {
|
|
|
22
22
|
comparison_contract: comparisonContract,
|
|
23
23
|
full_scenario_sha256: digestProcessCommitment(scenario),
|
|
24
24
|
comparison_contract_sha256: digestProcessCommitment(comparisonContract),
|
|
25
|
+
selected_executable_sha256: "0".repeat(64),
|
|
25
26
|
executable_sha256: "0".repeat(64),
|
|
27
|
+
executable_identity: {
|
|
28
|
+
state: "path_metadata_unchanged",
|
|
29
|
+
reason: null,
|
|
30
|
+
},
|
|
26
31
|
normalization_sha256: digestProcessCommitment(normalization),
|
|
27
32
|
},
|
|
28
33
|
normalization,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { processCaptureSchema, } from "./processCapture.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const verifiedCaptures = new WeakSet();
|
|
4
|
+
const isVerifiedCapture = (capture) => verifiedCaptures.has(capture);
|
|
5
|
+
const attachProof = (capture) => {
|
|
6
|
+
verifiedCaptures.add(capture);
|
|
7
|
+
if (isVerifiedCapture(capture))
|
|
8
|
+
return capture;
|
|
9
|
+
throw new TypeError("Process Capture proof ownership failed");
|
|
10
|
+
};
|
|
11
|
+
const recordSchema = z.record(z.string(), z.unknown());
|
|
12
|
+
const LEGACY_EXECUTABLE_IDENTITY_REASON = "Legacy v3 capture did not distinguish selected and launch executable digests";
|
|
13
|
+
const migrateLegacyV3Capture = (input) => {
|
|
14
|
+
const capture = recordSchema.safeParse(input);
|
|
15
|
+
if (!capture.success)
|
|
16
|
+
return input;
|
|
17
|
+
const manifest = recordSchema.safeParse(capture.data.manifest);
|
|
18
|
+
if (!manifest.success)
|
|
19
|
+
return input;
|
|
20
|
+
if (manifest.data.provider_version !== "3" ||
|
|
21
|
+
"selected_executable_sha256" in manifest.data ||
|
|
22
|
+
"executable_identity" in manifest.data ||
|
|
23
|
+
"legacy_executable_sha256" in manifest.data)
|
|
24
|
+
return input;
|
|
25
|
+
const { executable_sha256 } = manifest.data;
|
|
26
|
+
if (typeof executable_sha256 !== "string" ||
|
|
27
|
+
!/^[a-f0-9]{64}$/u.test(executable_sha256))
|
|
28
|
+
return input;
|
|
29
|
+
return {
|
|
30
|
+
...capture.data,
|
|
31
|
+
manifest: {
|
|
32
|
+
...manifest.data,
|
|
33
|
+
selected_executable_sha256: null,
|
|
34
|
+
executable_sha256: null,
|
|
35
|
+
executable_identity: {
|
|
36
|
+
state: "unknown",
|
|
37
|
+
reason: LEGACY_EXECUTABLE_IDENTITY_REASON,
|
|
38
|
+
},
|
|
39
|
+
legacy_executable_sha256: executable_sha256,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
/** Parse unknown input and reject invalid commitments or semantics. */
|
|
44
|
+
export const parseProcessCapture = (input) => {
|
|
45
|
+
const parsed = processCaptureSchema.safeParse(migrateLegacyV3Capture(input));
|
|
46
|
+
if (parsed.success)
|
|
47
|
+
return attachProof(parsed.data);
|
|
48
|
+
const issues = parsed.error.issues.flatMap((issue) => issue.code === "custom"
|
|
49
|
+
? [{ path: issue.path.join("."), message: issue.message }]
|
|
50
|
+
: []);
|
|
51
|
+
if (issues.length > 0)
|
|
52
|
+
throw new TypeError(`Invalid process capture: ${issues.map(({ path, message }) => `${path}: ${message}`).join("; ")}`);
|
|
53
|
+
throw parsed.error;
|
|
54
|
+
};
|
|
@@ -5,8 +5,24 @@ const orderedTimestamps = (values) => values.every((value, index) => {
|
|
|
5
5
|
});
|
|
6
6
|
const validateCommitments = (capture, require) => {
|
|
7
7
|
const { manifest } = capture;
|
|
8
|
-
|
|
9
|
-
manifest.
|
|
8
|
+
if (manifest.legacy_executable_sha256 !== undefined) {
|
|
9
|
+
require(manifest.selected_executable_sha256 === null &&
|
|
10
|
+
manifest.executable_sha256 === null &&
|
|
11
|
+
manifest.executable_identity.state === "unknown" &&
|
|
12
|
+
manifest.executable_identity.reason !== null &&
|
|
13
|
+
manifest.scenario.executable_sha256 ===
|
|
14
|
+
manifest.legacy_executable_sha256, "manifest.legacy_executable_sha256", "legacy executable digest must remain distinct from unknown selected and launch digests");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
require(manifest.scenario.executable_sha256 ===
|
|
18
|
+
manifest.selected_executable_sha256, "manifest.selected_executable_sha256", "selected executable commitment does not match the scenario projection");
|
|
19
|
+
}
|
|
20
|
+
require(manifest.executable_identity.state === "path_metadata_unchanged"
|
|
21
|
+
? manifest.executable_sha256 === manifest.selected_executable_sha256 &&
|
|
22
|
+
manifest.executable_identity.reason === null
|
|
23
|
+
: manifest.executable_sha256 === null &&
|
|
24
|
+
manifest.executable_identity.reason !==
|
|
25
|
+
null, "manifest.executable_identity", "launch identity and executable digest do not agree");
|
|
10
26
|
for (const [field, value] of [
|
|
11
27
|
["full_scenario_sha256", manifest.scenario],
|
|
12
28
|
["comparison_contract_sha256", manifest.comparison_contract],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import canonicalize from "canonicalize";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { jsonValueSchema } from "
|
|
3
|
+
import { jsonValueSchema } from "../jsonValue.js";
|
|
4
4
|
import { compareProcessTraces, processTraceOutcomesDiffer, processTraceComparisonResultSchema, } from "./processTraceComparison.js";
|
|
5
5
|
import { dimensionsForTraceSources, traceCoversObservedDimension, } from "./processTraceDimensionProjection.js";
|
|
6
6
|
/** Comparison classification that never equates incomplete evidence. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { jsonValueSchema } from "
|
|
2
|
+
import { jsonValueSchema } from "../jsonValue.js";
|
|
3
3
|
/** Process-capture observation families shared by live drivers and trace assertions. */
|
|
4
4
|
export const processObservationSourceSchema = z.enum([
|
|
5
5
|
"terminal_raw",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import canonicalize from "canonicalize";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { jsonValueSchema } from "
|
|
3
|
+
import { jsonValueSchema } from "../jsonValue.js";
|
|
4
4
|
import { comparableProcessObservationPayload, processObservationSourceSchema, } from "./processObservation.js";
|
|
5
5
|
const identifierSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9._-]*$/u);
|
|
6
6
|
/** Process observation families admitted by a declared trace specification. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnalysisError } from "./analysisErrorBase.js";
|
|
1
|
+
import { AnalysisError, } from "./analysisErrorBase.js";
|
|
2
2
|
/** No configured provider can satisfy a requested operation or target binding. */
|
|
3
3
|
export class ProviderSelectionError extends AnalysisError {
|
|
4
4
|
_tag = "ProviderSelectionError";
|
|
@@ -10,7 +10,9 @@ export class ProviderSelectionError extends AnalysisError {
|
|
|
10
10
|
userMessage;
|
|
11
11
|
constructor(options) {
|
|
12
12
|
const operation = options.operation ?? "open_binary";
|
|
13
|
-
super(providerSelectionDiagnostic(options)
|
|
13
|
+
super(providerSelectionDiagnostic(options), options.capturedOutput === undefined
|
|
14
|
+
? undefined
|
|
15
|
+
: { capturedOutput: options.capturedOutput });
|
|
14
16
|
this.operation = operation;
|
|
15
17
|
this.reason = options.reason;
|
|
16
18
|
this.requestedProviderId = options.requestedProviderId;
|
|
@@ -7,9 +7,9 @@ import { uniqueSorted } from "./canonicalOrdering.js";
|
|
|
7
7
|
import { parseEvidence } from "./evidence.js";
|
|
8
8
|
import { parseEvidenceBundle } from "./evidenceBundle.js";
|
|
9
9
|
import { functionComparisonResultSchema } from "./functionComparison.js";
|
|
10
|
-
import { PROCESS_PROVIDER, isProcessEvidenceProvider, } from "./processEvidenceProvider.js";
|
|
10
|
+
import { PROCESS_PROVIDER, isProcessEvidenceProvider, } from "./process/processEvidenceProvider.js";
|
|
11
11
|
import { functionDossierSchema } from "./hopperValues.js";
|
|
12
|
-
import { deriveProcessComparisonStatus, PROCESS_COMPARISON_DIMENSIONS, processCaptureComparisonSchema,
|
|
12
|
+
import { deriveProcessComparisonStatus, PROCESS_COMPARISON_DIMENSIONS, processCaptureComparisonSchema, parseProcessCapture, } from "./process/processCapture.js";
|
|
13
13
|
import { reconstructionClaimResultSchema, reconstructionSpecificationSchema, reconstructionVerificationResultSchema, } from "./reconstructionVerificationSchemas.js";
|
|
14
14
|
import { reconstructionClaimUnknowns, reconstructionProbes, reconstructionUnknownHeads, } from "./reconstructionUnknowns.js";
|
|
15
15
|
const evidenceIdSchema = prefixedDigestSchema("ev");
|
|
@@ -125,7 +125,7 @@ const validateSourceKinds = (claim, sources) => {
|
|
|
125
125
|
throw new TypeError("Comparison source Evidence has an unexpected observation type");
|
|
126
126
|
for (const source of sources) {
|
|
127
127
|
if (claim.kind === "behavioral")
|
|
128
|
-
|
|
128
|
+
parseProcessCapture(source.normalized_result);
|
|
129
129
|
else if (claim.kind === "structural-function")
|
|
130
130
|
functionDossierSchema.parse(source.normalized_result);
|
|
131
131
|
else
|