rea-agents 2.0.0 → 2.2.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 +103 -38
- package/bridge/ghidra/ReaGhidraBridge.java +96 -11
- package/dist/application/AndroidApplicationService.js +17 -0
- package/dist/application/AppleApplicationService.js +17 -0
- package/dist/application/ArtifactExtraction.js +1 -1
- package/dist/application/ArtifactInventory/classify.js +42 -0
- package/dist/application/ArtifactInventory/hash.js +43 -0
- package/dist/application/ArtifactInventory/reader.js +41 -0
- package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
- package/dist/application/ArtifactInventory/scanReader.js +124 -0
- package/dist/application/ArtifactInventory/types.js +10 -0
- package/dist/application/ArtifactInventory.js +9 -351
- package/dist/application/AuthorizedArtifactInventory.js +2 -2
- package/dist/application/BinarySession.js +47 -270
- package/dist/application/BinarySessionExecution.js +28 -0
- package/dist/application/BinarySessionOpen.js +39 -0
- package/dist/application/BinarySessionRecords.js +149 -0
- package/dist/application/BinarySessionStatus.js +75 -0
- package/dist/application/CapabilityInventory.js +24 -2
- package/dist/application/ClientRegistrationStatus.js +11 -4
- package/dist/application/CrossVersionInventory.js +8 -2
- package/dist/application/CrossVersionInvestigation.js +1 -23
- package/dist/application/CrossVersionInvestigationCollections.js +23 -0
- package/dist/application/DirectAnalysis.js +47 -44
- package/dist/application/Doctor.js +156 -238
- package/dist/application/DoctorDiagnostics.js +180 -0
- package/dist/application/ElectronBoundaryAnalysis.js +23 -7
- package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
- package/dist/application/EnhancedToolPagination.js +22 -0
- package/dist/application/EnhancedToolTypes.js +4 -0
- package/dist/application/EnhancedTools.js +20 -37
- package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
- package/dist/application/InventoryProjectionEvidence.js +41 -0
- package/dist/application/InvestigationProviders.js +12 -0
- package/dist/application/InvestigationWorkspaceStore.js +24 -10
- package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
- package/dist/application/JavaScriptArtifactFiles.js +6 -2
- package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
- package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
- package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
- package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
- package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
- package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
- package/dist/application/JavaScriptJsonModules.js +39 -0
- package/dist/application/JavaScriptModuleRelationships.js +262 -0
- package/dist/application/JavaScriptReplayPermission.js +16 -0
- package/dist/application/JavaScriptReplayService.js +182 -156
- package/dist/application/LinuxHopper.js +10 -0
- package/dist/application/MacHopper.js +22 -11
- package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
- package/dist/application/PermissionAuthority.js +106 -63
- package/dist/application/ProcessCaptureCapability.js +1 -1
- package/dist/application/ProcessCapturePermission.js +13 -0
- package/dist/application/ProcessCli.js +2 -12
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessPairedExperiment.js +72 -0
- package/dist/application/ProjectPermissionStore.js +48 -9
- package/dist/application/RealHopperAssertions.js +76 -0
- package/dist/application/ReconstructionCoverageService.js +69 -0
- package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
- package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
- package/dist/application/Setup.js +148 -260
- package/dist/application/SetupClientConfiguration.js +224 -0
- package/dist/application/SetupClients.js +24 -2
- package/dist/application/SetupPlan.js +124 -12
- package/dist/application/SetupSkill.js +54 -36
- package/dist/application/SupportedClients.js +1 -0
- package/dist/application/Uninstall.js +12 -3
- package/dist/application/Upgrade.js +16 -1
- package/dist/application/WindowsCapabilities.js +86 -0
- package/dist/artifacts/AsarArtifactReader.js +11 -0
- package/dist/browser/BrowserProcessStartup.js +88 -0
- package/dist/browser/CdpCaptureDocuments.js +12 -8
- package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
- package/dist/browser/CdpCaptureEventHandlers.js +336 -0
- package/dist/browser/CdpCaptureEventHelpers.js +71 -0
- package/dist/browser/CdpCaptureEventState.js +1 -0
- package/dist/browser/CdpCaptureEventTypes.js +1 -0
- package/dist/browser/CdpCaptureEvents.js +29 -496
- package/dist/browser/CdpObservationSession.js +51 -8
- package/dist/browser/CdpPageCapture.js +12 -133
- package/dist/browser/CdpPageCaptureScripts.js +97 -0
- package/dist/browser/CdpPageCaptureWorkers.js +46 -0
- package/dist/browser/CdpWebMcpDiscovery.js +38 -14
- package/dist/browser/PngVisualDiff.js +9 -2
- package/dist/catalogIdentity.js +4 -0
- package/dist/cli/artifactCommands.js +82 -0
- package/dist/cli/coreAnalysisCommands.js +232 -0
- package/dist/cli/managedCommands.js +397 -0
- package/dist/cli/managedProjectGraphCommand.js +24 -0
- package/dist/cli/options.js +9 -0
- package/dist/cli/setupCommands.js +113 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utilityCommands.js +122 -0
- package/dist/cli.js +10 -768
- package/dist/cliApplicationCommands.js +118 -4
- package/dist/cliBrowserAdvancedCommands.js +73 -16
- package/dist/cliBrowserCommands.js +39 -115
- package/dist/cliCommandNames.js +4 -0
- package/dist/cliElectronCommands.js +33 -62
- package/dist/cliEvidenceCommands.js +13 -2
- package/dist/cliInvestigationCommands.js +53 -10
- package/dist/cliObservationOptions.js +137 -0
- package/dist/cliPolicyCommands.js +80 -46
- package/dist/cliSetup.js +306 -0
- package/dist/config/environment.js +107 -0
- package/dist/config/parseConfig.js +228 -0
- package/dist/config/parsers.js +68 -0
- package/dist/config/permissions.js +30 -0
- package/dist/config/types.js +1 -0
- package/dist/config.js +1 -375
- package/dist/contracts/applicationToolContracts.js +134 -0
- package/dist/contracts/electronToolContracts.js +51 -3
- package/dist/contracts/managedToolContracts.js +8 -3
- package/dist/contracts/managedWorkflowToolContracts.js +3 -3
- package/dist/contracts/providerSelection.js +2 -0
- package/dist/contracts/sessionStatusContract.js +26 -0
- package/dist/contracts/toolContractTypes.js +12 -1
- package/dist/contracts/toolContracts.js +5 -12
- package/dist/contracts/toolEffects.js +14 -0
- package/dist/contracts/toolOutputSchemaGroups.js +274 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
- package/dist/contracts/toolOutputSchemas.js +2 -550
- package/dist/domain/analysisErrorPresentation.js +234 -0
- package/dist/domain/analysisErrorProjection.js +284 -0
- package/dist/domain/androidApplication.js +206 -0
- package/dist/domain/appleApplication.js +221 -0
- package/dist/domain/artifactComparison.js +34 -15
- package/dist/domain/binaryTarget.js +87 -37
- package/dist/domain/boundedCartesianProjection.js +15 -0
- package/dist/domain/browserObservation.js +1 -285
- package/dist/domain/browserObservationSchemas.js +286 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
- package/dist/domain/errors.js +4 -486
- package/dist/domain/functionComparison.js +4 -165
- package/dist/domain/functionComparisonDimensions.js +164 -0
- package/dist/domain/javascriptApplicationVersionItems.js +18 -6
- package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
- package/dist/domain/javascriptExportInstrumentation.js +97 -0
- package/dist/domain/javascriptFeatureTraversal.js +19 -7
- package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
- package/dist/domain/javascriptSemanticAnalysis.js +386 -0
- package/dist/domain/javascriptSemanticIr.js +30 -0
- package/dist/domain/javascriptSemanticProjection.js +366 -0
- package/dist/domain/javascriptSemanticState.js +40 -0
- package/dist/domain/javascriptSemanticTraversal.js +22 -0
- package/dist/domain/javascriptSemanticValues.js +352 -0
- package/dist/domain/javascriptStaticAnalysis.js +5 -221
- package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
- package/dist/domain/knownPageCoverage.js +21 -0
- package/dist/domain/managedApplicationGraph.js +14 -364
- package/dist/domain/managedApplicationGraphCoverage.js +170 -0
- package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
- package/dist/domain/managedApplicationGraphNodes.js +336 -0
- package/dist/domain/managedArtifact.js +16 -8
- package/dist/domain/managedMemberComparison.js +37 -391
- package/dist/domain/managedMemberComparisonCoverage.js +72 -0
- package/dist/domain/managedMemberComparisonMatch.js +356 -0
- package/dist/domain/managedNativeVerification.js +27 -389
- package/dist/domain/managedNativeVerificationMatch.js +272 -0
- package/dist/domain/managedNativeVerificationSchemas.js +141 -0
- package/dist/domain/managedReconstruction.js +82 -69
- package/dist/domain/managedRuntimeCorrelation.js +12 -3
- package/dist/domain/nodeRuntimeCharacterization.js +66 -0
- package/dist/domain/permissionPolicy.js +2 -0
- package/dist/domain/processComparison.js +19 -0
- package/dist/domain/processPairedExperiment.js +128 -0
- package/dist/domain/reconstructionCoverage.js +387 -0
- package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
- package/dist/domain/reconstructionVerificationSchemas.js +8 -0
- package/dist/domain/replayMachine.js +316 -0
- package/dist/domain/replayMachineRuntime.js +215 -0
- package/dist/domain/runtimeCharacterization.js +85 -0
- package/dist/domain/runtimeIdentification.js +210 -0
- package/dist/domain/webBundleAnalyzer.js +3 -409
- package/dist/domain/webBundleAnalyzerAst.js +77 -0
- package/dist/domain/webBundleAnalyzerInspection.js +307 -0
- package/dist/domain/webBundleAnalyzerResult.js +120 -0
- package/dist/dotnet/ManagedArtifactInspector.js +50 -29
- package/dist/dotnet/ManagedMemberInspector.js +51 -785
- package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
- package/dist/dotnet/ManagedMemberRows.js +120 -0
- package/dist/dotnet/ManagedMetadataInventory.js +42 -306
- package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
- package/dist/dotnet/ManagedMetadataLayout.js +48 -111
- package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
- package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
- package/dist/dotnet/ManagedPeReader.js +59 -28
- package/dist/evaluation/CodexAgentEval.js +132 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraClient.js +76 -91
- package/dist/ghidra/GhidraClientStartup.js +29 -0
- package/dist/ghidra/GhidraClientWire.js +34 -0
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraDiagnostics.js +6 -1
- package/dist/ghidra/GhidraInstallation.js +20 -15
- package/dist/ghidra/GhidraLauncher.js +93 -29
- package/dist/ghidra/GhidraProvider.js +61 -161
- package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
- package/dist/ghidra/GhidraSessionValues.js +2 -0
- package/dist/ghidra/GhidraSocketConnection.js +44 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
- package/dist/ghidra/GhidraTransport.js +71 -0
- package/dist/identity.js +4 -2
- package/dist/main/messages.js +5 -0
- package/dist/main/reload.js +49 -0
- package/dist/main/shutdown.js +25 -0
- package/dist/main/startup.js +14 -0
- package/dist/main/state.js +26 -0
- package/dist/main/transport.js +61 -0
- package/dist/main/types.js +1 -0
- package/dist/main.js +35 -146
- package/dist/process/PrivateRuntimeRoot.js +3 -2
- package/dist/process/ProcessOwnership.js +35 -0
- package/dist/process/ProviderProcess.js +2 -0
- package/dist/replay/JavaScriptReplayWorker.js +64 -50
- package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
- package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
- package/dist/replay/ReplayOutcome.js +95 -0
- package/dist/replay/ReplayProcessLifecycle.js +71 -0
- package/dist/server/ProcessCaptureElicitation.js +157 -0
- package/dist/server/createServer.js +124 -56
- package/dist/server/javascriptApplicationResult.js +85 -0
- package/dist/server/registerApplicationTools/characterization.js +57 -0
- package/dist/server/registerApplicationTools/compareVersions.js +34 -0
- package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
- package/dist/server/registerApplicationTools/coverage.js +84 -0
- package/dist/server/registerApplicationTools/helpers.js +42 -0
- package/dist/server/registerApplicationTools/traceFeature.js +26 -0
- package/dist/server/registerApplicationTools/types.js +1 -0
- package/dist/server/registerApplicationTools.js +10 -112
- package/dist/server/registerBrowserTools.js +59 -83
- package/dist/server/registerElectronTools.js +48 -49
- package/dist/server/registerEnhancedTools.js +34 -42
- package/dist/server/registerEvidenceResources.js +56 -0
- package/dist/server/registerInvestigationTools/helpers.js +74 -0
- package/dist/server/registerInvestigationTools/types.js +1 -0
- package/dist/server/registerInvestigationTools.js +83 -116
- package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
- package/dist/server/registerManagedTools.js +18 -1
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
- package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
- package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
- package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
- package/dist/server/registerManagedWorkflowTools/types.js +1 -0
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
- package/dist/server/registerManagedWorkflowTools.js +10 -274
- package/dist/server/registerOfficialTools.js +73 -59
- package/dist/server/registerSessionRecordTools.js +137 -0
- package/dist/server/registerSessionStatusTool.js +97 -26
- package/dist/server/registerSessionTools.js +60 -164
- package/dist/server/sessionAvailabilityPolicy.js +10 -0
- package/dist/server/toolRegistrationOptions.js +101 -3
- package/dist/server/toolResult.js +15 -4
- package/package.json +11 -11
- package/skills/reverse-engineer-anything/SKILL.md +76 -0
- package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
- package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
- package/scripts/prepare-node-pty.mjs +0 -35
- package/scripts/rebuild-native.mjs +0 -22
- package/skills/rea-analysis/SKILL.md +0 -222
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import { addAstContainsEdge, artifactLocalIdentity, } from "./JavaScriptArtifactGraphContext.js";
|
|
3
|
+
import { astObservationEvidence, completeReconstructionCoverage, partialReconstructionCoverage, staticInferenceEvidence, } from "./JavaScriptArtifactGraphEvidence.js";
|
|
4
|
+
import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
|
|
5
|
+
/** Create one path-scoped source-module identity for every analyzed source file. */
|
|
6
|
+
export const addJavaScriptSourceModules = (context) => {
|
|
7
|
+
for (const analyzed of context.analysis.files) {
|
|
8
|
+
const { file, semantic } = analyzed;
|
|
9
|
+
const asset = context.assetNodes.get(file.path);
|
|
10
|
+
const program = semantic?.ir.scopes.find(({ kind }) => kind === "program");
|
|
11
|
+
if (asset === undefined || semantic === null || program === undefined)
|
|
12
|
+
continue;
|
|
13
|
+
const exports = semantic.ir.moduleLinks
|
|
14
|
+
.flatMap(({ exportedName }) => exportedName === null ? [] : [exportedName])
|
|
15
|
+
.sort(compareCodePoints);
|
|
16
|
+
const retainedExports = [...new Set(exports)].slice(0, 128);
|
|
17
|
+
const source = context.accumulator.addNode({
|
|
18
|
+
kind: "javascript-module",
|
|
19
|
+
identity: artifactLocalIdentity(file.sha256, "source-module", `${file.container_sha256}:${file.path}`),
|
|
20
|
+
observations: [
|
|
21
|
+
{
|
|
22
|
+
label: file.path,
|
|
23
|
+
properties: {
|
|
24
|
+
semantic_role: "source-module",
|
|
25
|
+
path: file.path,
|
|
26
|
+
container_sha256: file.container_sha256,
|
|
27
|
+
logical_module_key: file.path,
|
|
28
|
+
module_format: moduleFormat(file.path, semantic.ir),
|
|
29
|
+
import_relationships: semantic.ir.moduleLinks.filter(({ kind }) => kind === "import" || kind === "require").length,
|
|
30
|
+
export_relationships: exports.length,
|
|
31
|
+
export_names: retainedExports,
|
|
32
|
+
omitted_export_names: Math.max(0, exports.length - retainedExports.length),
|
|
33
|
+
},
|
|
34
|
+
evidence: astObservationEvidence({
|
|
35
|
+
sha256: file.sha256,
|
|
36
|
+
path: file.path,
|
|
37
|
+
range: program.location,
|
|
38
|
+
operation: "recover-source-module",
|
|
39
|
+
coverage: semanticCoverage(semantic),
|
|
40
|
+
limitations: semantic.ir.limitations,
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
context.sourceModuleNodes.set(file.path, source);
|
|
46
|
+
addAstContainsEdge(context, {
|
|
47
|
+
source: asset,
|
|
48
|
+
target: source,
|
|
49
|
+
file,
|
|
50
|
+
range: program.location,
|
|
51
|
+
coverage: semanticCoverage(semantic),
|
|
52
|
+
properties: { logical_module_key: file.path },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/** Compose bounded CommonJS and ESM binding relationships across artifact files. */
|
|
57
|
+
export const addJavaScriptModuleRelationships = (context) => {
|
|
58
|
+
for (const analyzed of context.analysis.files) {
|
|
59
|
+
const { file, semantic } = analyzed;
|
|
60
|
+
const source = context.sourceModuleNodes.get(file.path);
|
|
61
|
+
if (semantic === null || source === undefined)
|
|
62
|
+
continue;
|
|
63
|
+
for (const link of semantic.ir.moduleLinks) {
|
|
64
|
+
const input = { context, file, semantic, source, link };
|
|
65
|
+
if (isExportLink(link))
|
|
66
|
+
addExportRelationship(input);
|
|
67
|
+
else if (link.specifier !== null)
|
|
68
|
+
addImportRelationship(input, source, {
|
|
69
|
+
specifier: link.specifier,
|
|
70
|
+
importedPath: link.importedName === null ? [] : [link.importedName],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const addExportRelationship = (input) => {
|
|
76
|
+
const { context, file, semantic, source, link } = input;
|
|
77
|
+
if (link.exportedName === null)
|
|
78
|
+
return;
|
|
79
|
+
const exported = context.accumulator.addNode({
|
|
80
|
+
kind: "javascript-module",
|
|
81
|
+
identity: artifactLocalIdentity(file.sha256, "module-export", `${file.container_sha256}:${file.path}:${link.exportedName}`),
|
|
82
|
+
observations: [
|
|
83
|
+
{
|
|
84
|
+
label: `${file.path}:${link.exportedName}`,
|
|
85
|
+
properties: {
|
|
86
|
+
semantic_role: "export-binding",
|
|
87
|
+
module_path: file.path,
|
|
88
|
+
relationship_kind: link.kind,
|
|
89
|
+
exported_name: link.exportedName,
|
|
90
|
+
local_name: link.localName,
|
|
91
|
+
imported_name: link.importedName,
|
|
92
|
+
declared_specifier: link.specifier,
|
|
93
|
+
},
|
|
94
|
+
evidence: astObservationEvidence({
|
|
95
|
+
sha256: file.sha256,
|
|
96
|
+
path: file.path,
|
|
97
|
+
range: link.location,
|
|
98
|
+
operation: "recover-module-export",
|
|
99
|
+
coverage: semanticCoverage(semantic),
|
|
100
|
+
limitations: semantic.ir.limitations,
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
context.accumulator.addEdge({
|
|
106
|
+
source_node_id: source.node_id,
|
|
107
|
+
target_node_id: exported.node_id,
|
|
108
|
+
relation: "exposes",
|
|
109
|
+
properties: relationshipProperties(link, null),
|
|
110
|
+
evidence: relationshipEvidence(input, "expose-module-export", []),
|
|
111
|
+
});
|
|
112
|
+
const origin = moduleOriginForExport(semantic.ir, link);
|
|
113
|
+
if (origin !== null)
|
|
114
|
+
addImportRelationship(input, exported, origin);
|
|
115
|
+
};
|
|
116
|
+
const addImportRelationship = (input, source, origin) => {
|
|
117
|
+
const target = resolveModuleTarget(input, origin.specifier);
|
|
118
|
+
input.context.accumulator.addEdge({
|
|
119
|
+
source_node_id: source.node_id,
|
|
120
|
+
target_node_id: target.node.node_id,
|
|
121
|
+
relation: "imports",
|
|
122
|
+
properties: relationshipProperties(input.link, target, origin),
|
|
123
|
+
evidence: relationshipEvidence(input, "resolve-semantic-module-relationship", target.limitations, target.status === "resolved"
|
|
124
|
+
? "high"
|
|
125
|
+
: target.status === "external"
|
|
126
|
+
? "medium"
|
|
127
|
+
: "low"),
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
const resolveModuleTarget = (input, specifier) => {
|
|
131
|
+
const resolution = resolveArtifactPathByContext({
|
|
132
|
+
declaredPath: specifier,
|
|
133
|
+
sourcePath: input.file.path,
|
|
134
|
+
context: "module-specifier",
|
|
135
|
+
files: input.context.filesByPath,
|
|
136
|
+
moduleKind: input.link.kind === "require" || input.link.kind === "commonjs-export"
|
|
137
|
+
? "require"
|
|
138
|
+
: "import",
|
|
139
|
+
});
|
|
140
|
+
const path = resolution.resolved_path;
|
|
141
|
+
const file = path === null ? undefined : input.context.filesByPath.get(path);
|
|
142
|
+
const node = path === null
|
|
143
|
+
? undefined
|
|
144
|
+
: (input.context.sourceModuleNodes.get(path) ??
|
|
145
|
+
input.context.fileNodes.get(path));
|
|
146
|
+
if (path !== null && file !== undefined && node !== undefined)
|
|
147
|
+
return {
|
|
148
|
+
node,
|
|
149
|
+
path,
|
|
150
|
+
file,
|
|
151
|
+
status: "resolved",
|
|
152
|
+
limitations: resolution.limitations,
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
node: unresolvedModuleNode(input, specifier, resolution.resolution_status),
|
|
156
|
+
path: null,
|
|
157
|
+
file: null,
|
|
158
|
+
status: resolution.resolution_status,
|
|
159
|
+
limitations: resolution.limitations,
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
const unresolvedModuleNode = (input, specifier, status) => input.context.accumulator.addNode({
|
|
163
|
+
kind: "javascript-module",
|
|
164
|
+
identity: artifactLocalIdentity(input.file.sha256, "unresolved-semantic-module", `${status}:${specifier}`),
|
|
165
|
+
observations: [
|
|
166
|
+
{
|
|
167
|
+
label: specifier,
|
|
168
|
+
properties: {
|
|
169
|
+
semantic_role: "module-reference",
|
|
170
|
+
declared_specifier: specifier,
|
|
171
|
+
resolution_status: status,
|
|
172
|
+
},
|
|
173
|
+
evidence: staticInferenceEvidence({
|
|
174
|
+
sha256: input.file.sha256,
|
|
175
|
+
path: input.file.path,
|
|
176
|
+
range: input.link.location,
|
|
177
|
+
operation: "retain-unresolved-semantic-module",
|
|
178
|
+
coverage: semanticCoverage(input.semantic),
|
|
179
|
+
confidence: "low",
|
|
180
|
+
limitations: input.semantic.ir.limitations,
|
|
181
|
+
}),
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
const moduleOriginForExport = (ir, link) => {
|
|
186
|
+
if (link.specifier !== null)
|
|
187
|
+
return {
|
|
188
|
+
specifier: link.specifier,
|
|
189
|
+
importedPath: link.importedName === null ? [] : [link.importedName],
|
|
190
|
+
};
|
|
191
|
+
if (link.localName === null)
|
|
192
|
+
return null;
|
|
193
|
+
const program = ir.scopes.find(({ kind }) => kind === "program");
|
|
194
|
+
const binding = ir.bindings.find(({ name, scopeId }) => name === link.localName && scopeId === program?.scopeId);
|
|
195
|
+
return binding?.provenance.status === "module" &&
|
|
196
|
+
binding.provenance.origins.length === 1
|
|
197
|
+
? (binding.provenance.origins[0] ?? null)
|
|
198
|
+
: null;
|
|
199
|
+
};
|
|
200
|
+
const relationshipProperties = (link, target, origin) => ({
|
|
201
|
+
module_link_kind: link.kind,
|
|
202
|
+
specifier: origin?.specifier ?? link.specifier,
|
|
203
|
+
imported_name: origin?.importedPath.at(-1) ?? link.importedName,
|
|
204
|
+
imported_path: origin?.importedPath ?? [],
|
|
205
|
+
local_name: link.localName,
|
|
206
|
+
exported_name: link.exportedName,
|
|
207
|
+
resolved_path: target?.path ?? null,
|
|
208
|
+
resolution_status: target?.status ?? null,
|
|
209
|
+
target_file_kind: target?.file?.kind ?? null,
|
|
210
|
+
target_json_status: target?.file?.kind === "json" ? jsonStatus(target.file) : null,
|
|
211
|
+
});
|
|
212
|
+
const relationshipEvidence = (input, operation, limitations, confidence = "high") => staticInferenceEvidence({
|
|
213
|
+
sha256: input.file.sha256,
|
|
214
|
+
path: input.file.path,
|
|
215
|
+
range: input.link.location,
|
|
216
|
+
operation,
|
|
217
|
+
coverage: semanticCoverage(input.semantic),
|
|
218
|
+
confidence,
|
|
219
|
+
limitations: [...input.semantic.ir.limitations, ...limitations],
|
|
220
|
+
});
|
|
221
|
+
const semanticCoverage = (semantic) => {
|
|
222
|
+
const limits = Object.entries(semantic.limits).map(([name, value]) => ({
|
|
223
|
+
name: `semantic-${camelToKebab(name)}`,
|
|
224
|
+
value,
|
|
225
|
+
unit: "items",
|
|
226
|
+
}));
|
|
227
|
+
if (semantic.ir.coverage.status === "complete")
|
|
228
|
+
return completeReconstructionCoverage(limits);
|
|
229
|
+
return partialReconstructionCoverage(limits, semantic.ir.coverage.omittedCount, semantic.ir.coverage.status === "truncated");
|
|
230
|
+
};
|
|
231
|
+
const moduleFormat = (path, ir) => {
|
|
232
|
+
const extension = posix.extname(path).toLowerCase();
|
|
233
|
+
if (extension === ".mjs")
|
|
234
|
+
return "esm";
|
|
235
|
+
if (extension === ".cjs")
|
|
236
|
+
return "commonjs";
|
|
237
|
+
const esm = ir.moduleLinks.some(({ kind }) => ["import", "export", "re-export"].includes(kind));
|
|
238
|
+
const commonJs = ir.moduleLinks.some(({ kind }) => ["require", "commonjs-export"].includes(kind));
|
|
239
|
+
return esm && commonJs
|
|
240
|
+
? "mixed"
|
|
241
|
+
: esm
|
|
242
|
+
? "esm"
|
|
243
|
+
: commonJs
|
|
244
|
+
? "commonjs"
|
|
245
|
+
: "unknown";
|
|
246
|
+
};
|
|
247
|
+
const jsonStatus = (file) => {
|
|
248
|
+
if (!file.text.included)
|
|
249
|
+
return "unavailable";
|
|
250
|
+
try {
|
|
251
|
+
JSON.parse(file.text.value);
|
|
252
|
+
return "included";
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return "invalid";
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const isExportLink = (link) => link.kind === "export" ||
|
|
259
|
+
link.kind === "re-export" ||
|
|
260
|
+
link.kind === "commonjs-export";
|
|
261
|
+
const camelToKebab = (value) => value.replaceAll(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
|
|
262
|
+
const compareCodePoints = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Build the exact permission request for one controlled replay operation. */
|
|
2
|
+
export const replayPermissionRequest = (input, policy) => ({
|
|
3
|
+
capability: "javascript_replay",
|
|
4
|
+
roots: [...input.left.modules, ...(input.right?.modules ?? [])].map(({ path }) => path),
|
|
5
|
+
executables: [
|
|
6
|
+
policy.nodePath,
|
|
7
|
+
policy.bubblewrapPath,
|
|
8
|
+
policy.systemdRunPath,
|
|
9
|
+
policy.systemctlPath,
|
|
10
|
+
policy.shellPath,
|
|
11
|
+
],
|
|
12
|
+
environment_names: [],
|
|
13
|
+
network: "none",
|
|
14
|
+
mount: true,
|
|
15
|
+
operation_identity: `run_controlled_replay:${input.plan_digest ?? "plan"}`,
|
|
16
|
+
});
|
|
@@ -6,6 +6,7 @@ import { createEvidence } from "../domain/evidence.js";
|
|
|
6
6
|
import { projectInputIssues } from "../domain/inputIssueProjection.js";
|
|
7
7
|
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
8
8
|
import { err, ok } from "../domain/result.js";
|
|
9
|
+
import { replayPermissionRequest } from "./JavaScriptReplayPermission.js";
|
|
9
10
|
import { digestBytes, prepareReplayPlan, } from "./JavaScriptReplayPlanning.js";
|
|
10
11
|
const OPERATION = "run_controlled_replay";
|
|
11
12
|
const isAborted = (signal) => signal?.aborted === true;
|
|
@@ -18,13 +19,46 @@ export const runControlledReplay = async (dependencies, rawInput, options = {})
|
|
|
18
19
|
};
|
|
19
20
|
/** Plan or execute replay input already parsed by a trusted adapter. */
|
|
20
21
|
export const runControlledReplayValidated = async (dependencies, input, options = {}) => {
|
|
22
|
+
const authorized = await authorizeReplay(dependencies, input, options.signal);
|
|
23
|
+
if (!authorized.ok)
|
|
24
|
+
return authorized;
|
|
25
|
+
const prepared = await prepareValidatedReplay(dependencies, input, options.signal);
|
|
26
|
+
if (!prepared.ok)
|
|
27
|
+
return prepared;
|
|
28
|
+
if (isAborted(options.signal))
|
|
29
|
+
return err(new AnalysisCancelledError(OPERATION));
|
|
30
|
+
if (input.mode === "plan")
|
|
31
|
+
return ok(asJson({
|
|
32
|
+
phase: "plan",
|
|
33
|
+
plan: prepared.value.publicPlan,
|
|
34
|
+
source_evidence: [],
|
|
35
|
+
evidence: null,
|
|
36
|
+
}));
|
|
37
|
+
if (input.plan_digest !== prepared.value.publicPlan.plan_digest)
|
|
38
|
+
return err(new ReplayPlanStaleError(input.plan_digest ?? "", prepared.value.publicPlan.plan_digest));
|
|
39
|
+
const exportContext = await authorizeReproducerExport(dependencies.authority, input, prepared.value);
|
|
40
|
+
if (!exportContext.ok)
|
|
41
|
+
return exportContext;
|
|
42
|
+
const executed = await executeValidatedReplay(dependencies, prepared.value, exportContext.value, options);
|
|
43
|
+
if (!executed.ok)
|
|
44
|
+
return executed;
|
|
45
|
+
try {
|
|
46
|
+
return ok(buildReplayOutput(prepared.value, executed.value.executed, executed.value.sourceEvidence));
|
|
47
|
+
}
|
|
48
|
+
catch (cause) {
|
|
49
|
+
return err(new AnalysisProtocolError(cause instanceof z.ZodError
|
|
50
|
+
? "Controlled replay produced an invalid bounded result"
|
|
51
|
+
: "Controlled replay could not establish or clean up its sandbox", { cause }));
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const authorizeReplay = async (dependencies, input, signal) => {
|
|
21
55
|
if (!dependencies.policy.enabled)
|
|
22
56
|
return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, "controlled replay is disabled; configure exact roots and executables before enabling it"));
|
|
23
57
|
if (dependencies.authority === undefined)
|
|
24
58
|
return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, "JavaScript replay permission policy is not configured"));
|
|
25
|
-
if (isAborted(
|
|
59
|
+
if (isAborted(signal))
|
|
26
60
|
return err(new AnalysisCancelledError(OPERATION));
|
|
27
|
-
const request =
|
|
61
|
+
const request = replayPermissionRequest(input, dependencies.policy);
|
|
28
62
|
const authorized = input.mode === "plan"
|
|
29
63
|
? await dependencies.authority.explain(request, "read")
|
|
30
64
|
: await dependencies.authority.authorize(request, "read");
|
|
@@ -34,51 +68,51 @@ export const runControlledReplayValidated = async (dependencies, input, options
|
|
|
34
68
|
: new AnalysisProtocolError(authorized.error.message, {
|
|
35
69
|
cause: authorized.error,
|
|
36
70
|
}));
|
|
37
|
-
|
|
71
|
+
return ok(true);
|
|
72
|
+
};
|
|
73
|
+
const prepareValidatedReplay = async (dependencies, input, signal) => {
|
|
74
|
+
if (isAborted(signal))
|
|
75
|
+
return err(new AnalysisCancelledError(OPERATION));
|
|
38
76
|
try {
|
|
39
|
-
|
|
77
|
+
return ok(await prepareReplayPlan(input, dependencies.policy, dependencies.host));
|
|
40
78
|
}
|
|
41
79
|
catch (cause) {
|
|
42
80
|
return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, cause instanceof Error ? cause.message : "replay planning failed"));
|
|
43
81
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (input.
|
|
47
|
-
return ok(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
operation_identity: `${OPERATION}:reproducer:${prepared.publicPlan.plan_digest}`,
|
|
67
|
-
}, "write");
|
|
68
|
-
if (!exportAuthorized.ok)
|
|
69
|
-
return err(exportAuthorized.error instanceof PermissionRequiredError
|
|
70
|
-
? exportAuthorized.error
|
|
71
|
-
: new AnalysisProtocolError(exportAuthorized.error.message, {
|
|
72
|
-
cause: exportAuthorized.error,
|
|
73
|
-
}));
|
|
74
|
-
canonicalExportPath = exportAuthorized.value.request.roots[0];
|
|
75
|
-
if (canonicalExportPath !== input.reproducer_export.path)
|
|
76
|
-
return err(new AnalysisInputError(`${OPERATION}:reproducer_export:path`, {
|
|
77
|
-
cause: new TypeError(`Use the canonical reproducer path: ${canonicalExportPath ?? "unavailable"}`),
|
|
82
|
+
};
|
|
83
|
+
const authorizeReproducerExport = async (authority, input, prepared) => {
|
|
84
|
+
if (input.reproducer_export === undefined)
|
|
85
|
+
return ok(undefined);
|
|
86
|
+
if (input.reproducer_export.approved !== true)
|
|
87
|
+
return err(new AnalysisInputError(`${OPERATION}:reproducer_export`));
|
|
88
|
+
if (authority === undefined)
|
|
89
|
+
return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, "JavaScript replay permission policy is not configured"));
|
|
90
|
+
const exportAuthorized = await authority.authorize({
|
|
91
|
+
capability: "evidence_write",
|
|
92
|
+
roots: [input.reproducer_export.path],
|
|
93
|
+
executables: [],
|
|
94
|
+
environment_names: [],
|
|
95
|
+
network: "none",
|
|
96
|
+
mount: false,
|
|
97
|
+
operation_identity: `${OPERATION}:reproducer:${prepared.publicPlan.plan_digest}`,
|
|
98
|
+
}, "write");
|
|
99
|
+
if (!exportAuthorized.ok)
|
|
100
|
+
return err(exportAuthorized.error instanceof PermissionRequiredError
|
|
101
|
+
? exportAuthorized.error
|
|
102
|
+
: new AnalysisProtocolError(exportAuthorized.error.message, {
|
|
103
|
+
cause: exportAuthorized.error,
|
|
78
104
|
}));
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
return err(new
|
|
105
|
+
const canonicalExportPath = exportAuthorized.value.request.roots[0];
|
|
106
|
+
if (canonicalExportPath !== input.reproducer_export.path)
|
|
107
|
+
return err(new AnalysisInputError(`${OPERATION}:reproducer_export:path`, {
|
|
108
|
+
cause: new TypeError(`Use the canonical reproducer path: ${canonicalExportPath ?? "unavailable"}`),
|
|
109
|
+
}));
|
|
110
|
+
return ok({
|
|
111
|
+
path: canonicalExportPath,
|
|
112
|
+
includeSources: input.reproducer_export.include_sources === true,
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
const executeValidatedReplay = async (dependencies, prepared, exportContext, options) => {
|
|
82
116
|
await options.progress?.report({
|
|
83
117
|
phase: OPERATION,
|
|
84
118
|
completed: 0,
|
|
@@ -98,106 +132,9 @@ export const runControlledReplayValidated = async (dependencies, input, options
|
|
|
98
132
|
});
|
|
99
133
|
if (executed.termination === "cancelled")
|
|
100
134
|
return err(new AnalysisCancelledError(OPERATION));
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const manifest = {
|
|
105
|
-
schema_version: 1,
|
|
106
|
-
plan: prepared.publicPlan,
|
|
107
|
-
result: executed,
|
|
108
|
-
sources: input.reproducer_export.include_sources
|
|
109
|
-
? {
|
|
110
|
-
left: prepared.leftSources,
|
|
111
|
-
...(prepared.rightSources === undefined
|
|
112
|
-
? {}
|
|
113
|
-
: { right: prepared.rightSources }),
|
|
114
|
-
}
|
|
115
|
-
: null,
|
|
116
|
-
};
|
|
117
|
-
const encoded = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
118
|
-
try {
|
|
119
|
-
await writeFileAtomic(canonicalExportPath, encoded, {
|
|
120
|
-
mode: 0o600,
|
|
121
|
-
});
|
|
122
|
-
executed = replayExecutionResultSchema.parse({
|
|
123
|
-
...executed,
|
|
124
|
-
reproducer: {
|
|
125
|
-
state: "written",
|
|
126
|
-
path: canonicalExportPath,
|
|
127
|
-
sha256: digestBytes(encoded),
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
catch (cause) {
|
|
132
|
-
executed = replayExecutionResultSchema.parse({
|
|
133
|
-
...executed,
|
|
134
|
-
limitations: [
|
|
135
|
-
...executed.limitations,
|
|
136
|
-
"Replay completed, but the separately approved reproducer export failed.",
|
|
137
|
-
],
|
|
138
|
-
reproducer: {
|
|
139
|
-
state: "failed",
|
|
140
|
-
path: canonicalExportPath,
|
|
141
|
-
error: cause instanceof Error
|
|
142
|
-
? cause.message
|
|
143
|
-
: "unknown reproducer export failure",
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
const leftCount = prepared.publicPlan.cases.length;
|
|
149
|
-
const sourceEvidence = [
|
|
150
|
-
createReplayEvidence(prepared, executed, "left", executed.outcomes.slice(0, leftCount)),
|
|
151
|
-
...(prepared.publicPlan.right === undefined
|
|
152
|
-
? []
|
|
153
|
-
: [
|
|
154
|
-
createReplayEvidence(prepared, executed, "right", executed.outcomes.slice(leftCount)),
|
|
155
|
-
]),
|
|
156
|
-
];
|
|
157
|
-
const subject = prepared.publicPlan.left.modules[0];
|
|
158
|
-
const evidence = createEvidence(subject === undefined
|
|
159
|
-
? undefined
|
|
160
|
-
: {
|
|
161
|
-
path: subject.canonical_path,
|
|
162
|
-
sha256: subject.sha256,
|
|
163
|
-
format: "javascript",
|
|
164
|
-
}, {
|
|
165
|
-
id: "rea-javascript-replay",
|
|
166
|
-
name: "REA isolated JavaScript replay",
|
|
167
|
-
version: "1",
|
|
168
|
-
}, {
|
|
169
|
-
predicateType: "javascript-controlled-replay",
|
|
170
|
-
operation: OPERATION,
|
|
171
|
-
parameters: {
|
|
172
|
-
replay_plan: jsonValueSchema.parse(JSON.parse(JSON.stringify(prepared.publicPlan))),
|
|
173
|
-
},
|
|
174
|
-
result: jsonValueSchema.parse(JSON.parse(JSON.stringify(executed))),
|
|
175
|
-
rawResult: jsonValueSchema.parse(JSON.parse(JSON.stringify({
|
|
176
|
-
cases: prepared.publicPlan.cases,
|
|
177
|
-
outcomes: executed.outcomes,
|
|
178
|
-
stderr: executed.stderr,
|
|
179
|
-
}))),
|
|
180
|
-
confidence: executed.comparison === undefined ? "observed" : "derived",
|
|
181
|
-
authority: "controlled-replay",
|
|
182
|
-
environment: {
|
|
183
|
-
id: prepared.publicPlan.plan_digest,
|
|
184
|
-
platform: process.platform,
|
|
185
|
-
architecture: process.arch,
|
|
186
|
-
isolation: "container",
|
|
187
|
-
},
|
|
188
|
-
limitations: executed.limitations,
|
|
189
|
-
locations: prepared.publicPlan.left.modules.map((module) => ({
|
|
190
|
-
kind: "artifact-path",
|
|
191
|
-
path: module.canonical_path,
|
|
192
|
-
})),
|
|
193
|
-
evidenceLinks: sourceEvidence.map(({ evidence_id: id }) => id),
|
|
194
|
-
});
|
|
195
|
-
return ok(asJson({
|
|
196
|
-
phase: "execute",
|
|
197
|
-
plan: null,
|
|
198
|
-
source_evidence: sourceEvidence.map((item) => replayEvidenceSchema.parse(item)),
|
|
199
|
-
evidence: replayEvidenceSchema.parse(evidence),
|
|
200
|
-
}));
|
|
135
|
+
executed = await applyReproducerExport(executed, exportContext, prepared);
|
|
136
|
+
const sourceEvidence = buildSourceEvidence(prepared, executed);
|
|
137
|
+
return ok({ executed, sourceEvidence });
|
|
201
138
|
}
|
|
202
139
|
catch (cause) {
|
|
203
140
|
return err(new AnalysisProtocolError(cause instanceof z.ZodError
|
|
@@ -205,6 +142,110 @@ export const runControlledReplayValidated = async (dependencies, input, options
|
|
|
205
142
|
: "Controlled replay could not establish or clean up its sandbox", { cause }));
|
|
206
143
|
}
|
|
207
144
|
};
|
|
145
|
+
const applyReproducerExport = async (executed, exportContext, prepared) => {
|
|
146
|
+
if (exportContext === undefined || executed.cleanup.state !== "complete")
|
|
147
|
+
return executed;
|
|
148
|
+
const manifest = {
|
|
149
|
+
schema_version: 1,
|
|
150
|
+
plan: prepared.publicPlan,
|
|
151
|
+
result: executed,
|
|
152
|
+
sources: exportContext.includeSources
|
|
153
|
+
? {
|
|
154
|
+
left: prepared.leftSources,
|
|
155
|
+
...(prepared.rightSources === undefined
|
|
156
|
+
? {}
|
|
157
|
+
: { right: prepared.rightSources }),
|
|
158
|
+
}
|
|
159
|
+
: null,
|
|
160
|
+
};
|
|
161
|
+
const encoded = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
162
|
+
try {
|
|
163
|
+
await writeFileAtomic(exportContext.path, encoded, {
|
|
164
|
+
mode: 0o600,
|
|
165
|
+
});
|
|
166
|
+
return replayExecutionResultSchema.parse({
|
|
167
|
+
...executed,
|
|
168
|
+
reproducer: {
|
|
169
|
+
state: "written",
|
|
170
|
+
path: exportContext.path,
|
|
171
|
+
sha256: digestBytes(encoded),
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (cause) {
|
|
176
|
+
return replayExecutionResultSchema.parse({
|
|
177
|
+
...executed,
|
|
178
|
+
limitations: [
|
|
179
|
+
...executed.limitations,
|
|
180
|
+
"Replay completed, but the separately approved reproducer export failed.",
|
|
181
|
+
],
|
|
182
|
+
reproducer: {
|
|
183
|
+
state: "failed",
|
|
184
|
+
path: exportContext.path,
|
|
185
|
+
error: cause instanceof Error
|
|
186
|
+
? cause.message
|
|
187
|
+
: "unknown reproducer export failure",
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const buildSourceEvidence = (prepared, executed) => {
|
|
193
|
+
const leftCount = prepared.publicPlan.cases.length;
|
|
194
|
+
return [
|
|
195
|
+
createReplayEvidence(prepared, executed, "left", executed.outcomes.slice(0, leftCount)),
|
|
196
|
+
...(prepared.publicPlan.right === undefined
|
|
197
|
+
? []
|
|
198
|
+
: [
|
|
199
|
+
createReplayEvidence(prepared, executed, "right", executed.outcomes.slice(leftCount)),
|
|
200
|
+
]),
|
|
201
|
+
];
|
|
202
|
+
};
|
|
203
|
+
const buildReplayOutput = (prepared, executed, sourceEvidence) => {
|
|
204
|
+
const subject = prepared.publicPlan.left.modules[0];
|
|
205
|
+
const evidence = createEvidence(subject === undefined
|
|
206
|
+
? undefined
|
|
207
|
+
: {
|
|
208
|
+
path: subject.canonical_path,
|
|
209
|
+
sha256: subject.sha256,
|
|
210
|
+
format: "javascript",
|
|
211
|
+
}, {
|
|
212
|
+
id: "rea-javascript-replay",
|
|
213
|
+
name: "REA isolated JavaScript replay",
|
|
214
|
+
version: "1",
|
|
215
|
+
}, {
|
|
216
|
+
predicateType: "javascript-controlled-replay",
|
|
217
|
+
operation: OPERATION,
|
|
218
|
+
parameters: {
|
|
219
|
+
replay_plan: jsonValueSchema.parse(JSON.parse(JSON.stringify(prepared.publicPlan))),
|
|
220
|
+
},
|
|
221
|
+
result: jsonValueSchema.parse(JSON.parse(JSON.stringify(executed))),
|
|
222
|
+
rawResult: jsonValueSchema.parse(JSON.parse(JSON.stringify({
|
|
223
|
+
cases: prepared.publicPlan.cases,
|
|
224
|
+
outcomes: executed.outcomes,
|
|
225
|
+
stderr: executed.stderr,
|
|
226
|
+
}))),
|
|
227
|
+
confidence: executed.comparison === undefined ? "observed" : "derived",
|
|
228
|
+
authority: "controlled-replay",
|
|
229
|
+
environment: {
|
|
230
|
+
id: prepared.publicPlan.plan_digest,
|
|
231
|
+
platform: process.platform,
|
|
232
|
+
architecture: process.arch,
|
|
233
|
+
isolation: "container",
|
|
234
|
+
},
|
|
235
|
+
limitations: executed.limitations,
|
|
236
|
+
locations: prepared.publicPlan.left.modules.map((module) => ({
|
|
237
|
+
kind: "artifact-path",
|
|
238
|
+
path: module.canonical_path,
|
|
239
|
+
})),
|
|
240
|
+
evidenceLinks: sourceEvidence.map(({ evidence_id: id }) => id),
|
|
241
|
+
});
|
|
242
|
+
return asJson({
|
|
243
|
+
phase: "execute",
|
|
244
|
+
plan: null,
|
|
245
|
+
source_evidence: sourceEvidence.map((item) => replayEvidenceSchema.parse(item)),
|
|
246
|
+
evidence: replayEvidenceSchema.parse(evidence),
|
|
247
|
+
});
|
|
248
|
+
};
|
|
208
249
|
const createReplayEvidence = (prepared, executed, side, outcomes) => {
|
|
209
250
|
const manifest = side === "left" ? prepared.publicPlan.left : prepared.publicPlan.right;
|
|
210
251
|
const subject = manifest?.modules[0];
|
|
@@ -249,18 +290,3 @@ const createReplayEvidence = (prepared, executed, side, outcomes) => {
|
|
|
249
290
|
});
|
|
250
291
|
};
|
|
251
292
|
const asJson = (value) => jsonValueSchema.parse(JSON.parse(JSON.stringify(controlledReplayOutputSchema.parse(value))));
|
|
252
|
-
const permissionRequest = (input, policy) => ({
|
|
253
|
-
capability: "javascript_replay",
|
|
254
|
-
roots: [...input.left.modules, ...(input.right?.modules ?? [])].map(({ path }) => path),
|
|
255
|
-
executables: [
|
|
256
|
-
policy.nodePath,
|
|
257
|
-
policy.bubblewrapPath,
|
|
258
|
-
policy.systemdRunPath,
|
|
259
|
-
policy.systemctlPath,
|
|
260
|
-
policy.shellPath,
|
|
261
|
-
],
|
|
262
|
-
environment_names: [],
|
|
263
|
-
network: "none",
|
|
264
|
-
mount: true,
|
|
265
|
-
operation_identity: `${OPERATION}:${input.plan_digest ?? "plan"}`,
|
|
266
|
-
});
|
|
@@ -26,6 +26,16 @@ const SUPPORTED_RELEASES = {
|
|
|
26
26
|
file_hash: "92d780b61cab38eda95dc4112dee4fbeb0920f46",
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
|
+
/** Describe the release and package-manager commands without downloading or writing. */
|
|
30
|
+
export const linuxHopperInstallDisclosure = (family, isRoot) => {
|
|
31
|
+
const release = SUPPORTED_RELEASES[family];
|
|
32
|
+
return {
|
|
33
|
+
downloadUrl: release.filename,
|
|
34
|
+
expectedBytes: Number(release.file_length),
|
|
35
|
+
expectedSha1: release.file_hash,
|
|
36
|
+
commands: linuxPackageManagerCommands(family, `<verified-hopper.${family}>`, isRoot).map(({ executable, args }) => `${executable} ${args.join(" ")}`),
|
|
37
|
+
};
|
|
38
|
+
};
|
|
29
39
|
/** Parse an os-release document without executing its shell syntax. */
|
|
30
40
|
export const parseLinuxDistribution = (text) => {
|
|
31
41
|
const fields = new Map();
|