rea-agents 1.7.0 → 2.1.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 +112 -59
- package/bridge/ghidra/ReaGhidraBridge.java +96 -11
- 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 +42 -2
- package/dist/application/ClientRegistrationStatus.js +1 -1
- package/dist/application/CrossVersionInventory.js +8 -2
- package/dist/application/CrossVersionInvestigation.js +11 -29
- package/dist/application/CrossVersionInvestigationCollections.js +23 -0
- package/dist/application/DirectAnalysis.js +47 -44
- package/dist/application/Doctor.js +165 -197
- package/dist/application/DoctorDiagnostics.js +180 -0
- package/dist/application/ElectronBoundaryAnalysis.js +112 -0
- package/dist/application/ElectronBoundaryGraph.js +9 -0
- package/dist/application/ElectronBoundaryGraphContext.js +62 -0
- package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
- package/dist/application/ElectronBoundaryGraphNative.js +86 -0
- package/dist/application/ElectronBoundaryGraphWindows.js +223 -0
- package/dist/application/EnhancedToolPagination.js +22 -0
- package/dist/application/EnhancedToolTypes.js +4 -0
- package/dist/application/EnhancedTools.js +58 -50
- package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
- package/dist/application/InvestigationProviders.js +30 -0
- package/dist/application/InvestigationWorkspaceStore.js +24 -10
- package/dist/application/JavaScriptApplicationEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
- package/dist/application/JavaScriptApplicationService.js +74 -0
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
- package/dist/application/JavaScriptArtifactAnalysis.js +52 -7
- package/dist/application/JavaScriptArtifactFiles.js +6 -2
- package/dist/application/JavaScriptArtifactGraphBuilder.js +82 -17
- package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
- package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
- package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
- 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 +4 -1
- package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
- package/dist/application/JavaScriptJsonModules.js +39 -0
- package/dist/application/JavaScriptModuleRelationships.js +262 -0
- package/dist/application/JavaScriptReplayPermission.js +16 -0
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +292 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- package/dist/application/LinuxHopper.js +10 -0
- package/dist/application/MacHopper.js +22 -11
- package/dist/application/ManagedApplicationGraphService.js +55 -0
- package/dist/application/ManagedMemberComparisonService.js +122 -0
- package/dist/application/ManagedNativeVerificationService.js +39 -0
- package/dist/application/ManagedReconstructionService.js +58 -0
- package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
- 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/ProcessSampling.js +15 -11
- package/dist/application/ProjectPermissionStore.js +49 -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 +147 -259
- package/dist/application/SetupClientConfiguration.js +220 -0
- package/dist/application/SetupClients.js +24 -2
- package/dist/application/SetupPlan.js +123 -12
- package/dist/application/SetupSkill.js +26 -12
- package/dist/application/SupportedClients.js +1 -0
- package/dist/application/Uninstall.js +6 -3
- package/dist/application/WindowsCapabilities.js +86 -0
- package/dist/application/runtime.js +2 -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 +46 -464
- package/dist/browser/CdpElectronInspection.js +106 -162
- package/dist/browser/CdpElectronProvider.js +5 -1
- package/dist/browser/CdpElectronScriptEvents.js +69 -0
- package/dist/browser/CdpElectronScripts.js +94 -0
- package/dist/browser/CdpElectronWorkers.js +56 -0
- package/dist/browser/CdpObservationSession.js +51 -8
- package/dist/browser/CdpPageCapture.js +12 -129
- 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 +8 -1
- 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 +108 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utilityCommands.js +122 -0
- package/dist/cli.js +13 -383
- package/dist/cliApplicationCommands.js +176 -0
- package/dist/cliBrowserAdvancedCommands.js +73 -16
- package/dist/cliBrowserCommands.js +39 -115
- package/dist/cliCommandNames.js +17 -0
- package/dist/cliElectronCommands.js +100 -27
- package/dist/cliEvidenceCommands.js +13 -2
- package/dist/cliInvestigationCommands.js +53 -10
- package/dist/cliJsonInput.js +54 -0
- package/dist/cliObservationOptions.js +137 -0
- package/dist/cliPolicyCommands.js +80 -46
- package/dist/cliSetup.js +289 -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 -292
- package/dist/contracts/applicationToolContracts.js +225 -0
- package/dist/contracts/artifactToolContracts.js +5 -9
- package/dist/contracts/browserToolContracts.js +14 -62
- package/dist/contracts/electronToolContracts.js +45 -19
- package/dist/contracts/errorSchemas.js +1 -0
- package/dist/contracts/investigationExamples.js +0 -6
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
- package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
- package/dist/contracts/managedToolContracts.js +153 -0
- package/dist/contracts/managedWorkflowExamples.js +346 -0
- package/dist/contracts/managedWorkflowToolContracts.js +203 -0
- package/dist/contracts/nativeToolContracts.js +2 -6
- package/dist/contracts/promptContracts.js +18 -2
- package/dist/contracts/providerSelection.js +2 -0
- package/dist/contracts/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +49 -47
- package/dist/contracts/toolEffects.js +174 -0
- package/dist/contracts/toolOutputSchemaGroups.js +232 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
- package/dist/contracts/toolOutputSchemas.js +2 -535
- package/dist/doctorRuntime.js +42 -1
- package/dist/domain/analysisErrorPresentation.js +234 -0
- package/dist/domain/analysisErrorProjection.js +284 -0
- package/dist/domain/artifactComparison.js +37 -19
- package/dist/domain/binaryTarget.js +87 -37
- package/dist/domain/browserObservation.js +1 -282
- package/dist/domain/browserObservationSchemas.js +286 -0
- package/dist/domain/bundleComparison.js +4 -4
- package/dist/domain/electronObservation.js +13 -0
- package/dist/domain/electronStaticAnalysis.js +9 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +206 -0
- package/dist/domain/electronStaticAnalysisIpc.js +171 -0
- package/dist/domain/electronStaticAnalysisNative.js +164 -0
- package/dist/domain/electronStaticAnalysisTypes.js +1 -0
- package/dist/domain/electronStaticAnalysisValues.js +103 -0
- package/dist/domain/errors.js +32 -448
- package/dist/domain/functionComparison.js +4 -165
- package/dist/domain/functionComparisonDimensions.js +164 -0
- package/dist/domain/functionComparisonSchemas.js +3 -8
- package/dist/domain/inputIssueProjection.js +52 -0
- package/dist/domain/javascriptApplicationAnalysis.js +155 -0
- package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
- package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
- package/dist/domain/javascriptApplicationGraph.js +2 -1
- package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
- package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
- package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionItems.js +233 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
- package/dist/domain/javascriptExportInstrumentation.js +97 -0
- package/dist/domain/javascriptFeatureSeed.js +140 -0
- package/dist/domain/javascriptFeatureTrace.js +232 -0
- package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
- package/dist/domain/javascriptFeatureTraversal.js +100 -0
- package/dist/domain/javascriptNativeHandoff.js +85 -0
- package/dist/domain/javascriptReplay.js +361 -0
- package/dist/domain/javascriptRuntimeLoadState.js +106 -0
- package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
- package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
- package/dist/domain/javascriptRuntimeStaticCandidates.js +193 -0
- 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 +26 -254
- package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +81 -2
- package/dist/domain/javascriptStaticAnalysisState.js +6 -0
- package/dist/domain/knownPageCoverage.js +21 -0
- package/dist/domain/managedApplicationGraph.js +206 -0
- 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 +452 -0
- package/dist/domain/managedMemberComparison.js +211 -0
- package/dist/domain/managedMemberComparisonCoverage.js +72 -0
- package/dist/domain/managedMemberComparisonMatch.js +356 -0
- package/dist/domain/managedNativeVerification.js +46 -0
- package/dist/domain/managedNativeVerificationMatch.js +272 -0
- package/dist/domain/managedNativeVerificationSchemas.js +141 -0
- package/dist/domain/managedReconstruction.js +196 -0
- package/dist/domain/managedRuntimeCorrelation.js +184 -0
- package/dist/domain/nodeRuntimeCharacterization.js +66 -0
- package/dist/domain/permissionPolicy.js +2 -0
- package/dist/domain/reconstructionCoverage.js +387 -0
- package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
- package/dist/domain/reconstructionVerificationSchemas.js +9 -3
- package/dist/domain/runtimeCharacterization.js +85 -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 +339 -0
- package/dist/dotnet/ManagedMemberInspector.js +153 -0
- package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
- package/dist/dotnet/ManagedMemberRows.js +120 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +104 -0
- package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
- package/dist/dotnet/ManagedMetadataLayout.js +259 -0
- 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 +143 -0
- package/dist/dotnet/ManagedPeReader.js +188 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -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 +67 -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 +3 -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 -132
- 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 +300 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +217 -0
- package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayOutcome.js +95 -0
- package/dist/replay/ReplayProcessLifecycle.js +71 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/ProcessCaptureElicitation.js +157 -0
- package/dist/server/createServer.js +158 -41
- 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 +13 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +61 -60
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +39 -12
- package/dist/server/registerEnhancedTools.js +52 -15
- package/dist/server/registerEvidenceResources.js +54 -0
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools/helpers.js +74 -0
- package/dist/server/registerInvestigationTools/types.js +1 -0
- package/dist/server/registerInvestigationTools.js +103 -132
- package/dist/server/registerManagedTools.js +6 -0
- 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 +13 -0
- package/dist/server/registerOfficialTools.js +81 -67
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionRecordTools.js +137 -0
- package/dist/server/registerSessionStatusTool.js +9 -8
- package/dist/server/registerSessionTools.js +77 -216
- package/dist/server/sessionAvailabilityPolicy.js +10 -0
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +177 -2
- package/dist/server/toolResult.js +30 -17
- package/package.json +21 -14
- package/scripts/rea.mjs +1 -1
- package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +75 -6
- package/scripts/prepare-node-pty.mjs +0 -35
- package/scripts/rebuild-native.mjs +0 -22
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import canonicalize from "canonicalize";
|
|
3
|
+
import { compareCodePoints, } from "./javascriptApplicationGraph.js";
|
|
4
|
+
/** Classify matched, unmatched, and ambiguous nodes without inventing absence. */
|
|
5
|
+
export const classifyJavaScriptApplicationVersions = (matching, context) => {
|
|
6
|
+
const pairByLeft = new Map(matching.pairs.map(({ left, right }) => [
|
|
7
|
+
left.node_id,
|
|
8
|
+
`pair:${left.node_id}:${right.node_id}`,
|
|
9
|
+
]));
|
|
10
|
+
const pairByRight = new Map(matching.pairs.map(({ left, right }) => [
|
|
11
|
+
right.node_id,
|
|
12
|
+
`pair:${left.node_id}:${right.node_id}`,
|
|
13
|
+
]));
|
|
14
|
+
const fullContext = { ...context, pairByLeft, pairByRight };
|
|
15
|
+
let omittedCandidateReferences = 0;
|
|
16
|
+
const leftItems = matching.unmatchedLeft.map((node) => {
|
|
17
|
+
const candidates = matching.candidatesForLeft.get(node.node_id) ?? [];
|
|
18
|
+
omittedCandidateReferences += Math.max(0, candidates.length - context.maxCandidates);
|
|
19
|
+
return unmatchedItem(node, "left", candidates.slice(0, context.maxCandidates), fullContext);
|
|
20
|
+
});
|
|
21
|
+
const rightItems = matching.unmatchedRight.map((node) => {
|
|
22
|
+
const candidates = matching.candidatesForRight.get(node.node_id) ?? [];
|
|
23
|
+
omittedCandidateReferences += Math.max(0, candidates.length - context.maxCandidates);
|
|
24
|
+
return unmatchedItem(node, "right", candidates.slice(0, context.maxCandidates), fullContext);
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
items: [
|
|
28
|
+
...matching.pairs.map((pair) => matchedItem(pair, fullContext)),
|
|
29
|
+
...leftItems,
|
|
30
|
+
...rightItems,
|
|
31
|
+
].sort((left, right) => compareCodePoints(left.item_id, right.item_id)),
|
|
32
|
+
omittedCandidateReferences,
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const matchedItem = (pair, context) => {
|
|
36
|
+
const dimensions = changedDimensions(pair, context);
|
|
37
|
+
const complete = graphsComplete(context);
|
|
38
|
+
const observedChange = dimensions.some((dimension) => ["content", "location", "properties"].includes(dimension));
|
|
39
|
+
const status = observedChange || (complete && dimensions.length > 0)
|
|
40
|
+
? "changed"
|
|
41
|
+
: complete
|
|
42
|
+
? "unchanged"
|
|
43
|
+
: "unknown";
|
|
44
|
+
const finalDimensions = status === "unknown" && !dimensions.includes("coverage")
|
|
45
|
+
? [...dimensions, "coverage"]
|
|
46
|
+
: dimensions;
|
|
47
|
+
const semantic = {
|
|
48
|
+
status,
|
|
49
|
+
node_kind: pair.left.kind,
|
|
50
|
+
left_node_id: pair.left.node_id,
|
|
51
|
+
right_node_id: pair.right.node_id,
|
|
52
|
+
match: {
|
|
53
|
+
status: "matched",
|
|
54
|
+
basis: pair.basis,
|
|
55
|
+
confidence: pair.confidence,
|
|
56
|
+
candidate_left_node_ids: [],
|
|
57
|
+
candidate_right_node_ids: [],
|
|
58
|
+
},
|
|
59
|
+
dimensions: finalDimensions,
|
|
60
|
+
evidence_links: itemEvidenceLinks(pair.left, pair.right, context),
|
|
61
|
+
limitations: pairLimitations(pair, status),
|
|
62
|
+
};
|
|
63
|
+
return itemWithId(semantic);
|
|
64
|
+
};
|
|
65
|
+
const unmatchedItem = (node, side, candidates, context) => {
|
|
66
|
+
const ambiguous = candidates.length > 0;
|
|
67
|
+
const oppositeComplete = (side === "left" ? context.rightGraph : context.leftGraph).coverage
|
|
68
|
+
.status === "complete";
|
|
69
|
+
const status = ambiguous
|
|
70
|
+
? "unknown"
|
|
71
|
+
: oppositeComplete
|
|
72
|
+
? side === "left"
|
|
73
|
+
? "removed"
|
|
74
|
+
: "added"
|
|
75
|
+
: "unknown";
|
|
76
|
+
const semantic = {
|
|
77
|
+
status,
|
|
78
|
+
node_kind: node.kind,
|
|
79
|
+
left_node_id: side === "left" ? node.node_id : null,
|
|
80
|
+
right_node_id: side === "right" ? node.node_id : null,
|
|
81
|
+
match: {
|
|
82
|
+
status: ambiguous ? "ambiguous" : "unmatched",
|
|
83
|
+
basis: "none",
|
|
84
|
+
confidence: "unknown",
|
|
85
|
+
candidate_left_node_ids: side === "right" ? [...candidates] : [],
|
|
86
|
+
candidate_right_node_ids: side === "left" ? [...candidates] : [],
|
|
87
|
+
},
|
|
88
|
+
dimensions: [
|
|
89
|
+
"availability",
|
|
90
|
+
...(status === "unknown" && !ambiguous ? ["coverage"] : []),
|
|
91
|
+
],
|
|
92
|
+
evidence_links: itemEvidenceLinks(side === "left" ? node : undefined, side === "right" ? node : undefined, context),
|
|
93
|
+
limitations: [
|
|
94
|
+
...(ambiguous
|
|
95
|
+
? [
|
|
96
|
+
"Multiple nodes share the same permitted match key; no cross-version pair was selected.",
|
|
97
|
+
]
|
|
98
|
+
: []),
|
|
99
|
+
...(status === "unknown" && !ambiguous
|
|
100
|
+
? [
|
|
101
|
+
"The opposite graph is incomplete, so an unmatched node cannot be classified as added or removed.",
|
|
102
|
+
]
|
|
103
|
+
: []),
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
return itemWithId(semantic);
|
|
107
|
+
};
|
|
108
|
+
const changedDimensions = (pair, context) => {
|
|
109
|
+
const dimensions = [];
|
|
110
|
+
const leftContent = contentIdentity(pair.left);
|
|
111
|
+
const rightContent = contentIdentity(pair.right);
|
|
112
|
+
if (leftContent !== null &&
|
|
113
|
+
rightContent !== null &&
|
|
114
|
+
leftContent !== rightContent)
|
|
115
|
+
dimensions.push("content");
|
|
116
|
+
if (canonical(locations(pair.left)) !== canonical(locations(pair.right)))
|
|
117
|
+
dimensions.push("location");
|
|
118
|
+
if (canonical(properties(pair.left)) !== canonical(properties(pair.right)))
|
|
119
|
+
dimensions.push("properties");
|
|
120
|
+
if (relationshipSignature(pair.left, "left", context) !==
|
|
121
|
+
relationshipSignature(pair.right, "right", context))
|
|
122
|
+
dimensions.push("relationships");
|
|
123
|
+
return dimensions;
|
|
124
|
+
};
|
|
125
|
+
const contentIdentity = (node) => {
|
|
126
|
+
if (node.identity.strategy === "content-digest")
|
|
127
|
+
return `content:${node.identity.sha256}`;
|
|
128
|
+
if (node.kind === "javascript-module")
|
|
129
|
+
return stringProperty(node, "source_sha256");
|
|
130
|
+
if (node.kind === "source-module" &&
|
|
131
|
+
node.identity.strategy === "source-map-original")
|
|
132
|
+
return node.identity.source_sha256;
|
|
133
|
+
return null;
|
|
134
|
+
};
|
|
135
|
+
const locations = (node) => node.observations
|
|
136
|
+
.map(({ evidence }) => evidence.location.available ? evidence.location.value : evidence.location)
|
|
137
|
+
.sort((left, right) => compareCodePoints(canonical(left), canonical(right)));
|
|
138
|
+
const properties = (node) => node.observations
|
|
139
|
+
.map(({ label, properties: values }) => ({ label, properties: values }))
|
|
140
|
+
.sort((left, right) => compareCodePoints(canonical(left), canonical(right)));
|
|
141
|
+
const relationshipSignature = (node, side, context) => {
|
|
142
|
+
const graph = side === "left" ? context.leftGraph : context.rightGraph;
|
|
143
|
+
const paired = side === "left" ? context.pairByLeft : context.pairByRight;
|
|
144
|
+
const nodeById = new Map(graph.nodes.map((value) => [value.node_id, value]));
|
|
145
|
+
const signatures = graph.edges.flatMap((edge) => {
|
|
146
|
+
if (edge.source_node_id === node.node_id)
|
|
147
|
+
return [
|
|
148
|
+
edgeSignature({
|
|
149
|
+
direction: "out",
|
|
150
|
+
relation: edge.relation,
|
|
151
|
+
neighborId: edge.target_node_id,
|
|
152
|
+
paired,
|
|
153
|
+
nodeById,
|
|
154
|
+
}),
|
|
155
|
+
];
|
|
156
|
+
if (edge.target_node_id === node.node_id)
|
|
157
|
+
return [
|
|
158
|
+
edgeSignature({
|
|
159
|
+
direction: "in",
|
|
160
|
+
relation: edge.relation,
|
|
161
|
+
neighborId: edge.source_node_id,
|
|
162
|
+
paired,
|
|
163
|
+
nodeById,
|
|
164
|
+
}),
|
|
165
|
+
];
|
|
166
|
+
return [];
|
|
167
|
+
});
|
|
168
|
+
return canonical(signatures.sort(compareCodePoints));
|
|
169
|
+
};
|
|
170
|
+
const edgeSignature = (context) => {
|
|
171
|
+
const mapped = context.paired.get(context.neighborId);
|
|
172
|
+
const neighbor = context.nodeById.get(context.neighborId);
|
|
173
|
+
return `${context.direction}\0${context.relation}\0${mapped ?? `unmatched:${neighbor?.kind ?? "unknown"}`}`;
|
|
174
|
+
};
|
|
175
|
+
const itemEvidenceLinks = (left, right, context) => [
|
|
176
|
+
...new Set([
|
|
177
|
+
context.leftEvidenceId,
|
|
178
|
+
context.rightEvidenceId,
|
|
179
|
+
...nativeLinks(left, context.leftNativeEvidence),
|
|
180
|
+
...nativeLinks(right, context.rightNativeEvidence),
|
|
181
|
+
]),
|
|
182
|
+
].sort(compareCodePoints);
|
|
183
|
+
const nativeLinks = (node, evidence) => {
|
|
184
|
+
const digest = node === undefined
|
|
185
|
+
? null
|
|
186
|
+
: contentIdentity(node)?.replace(/^content:/u, "");
|
|
187
|
+
return digest === null
|
|
188
|
+
? []
|
|
189
|
+
: evidence
|
|
190
|
+
.filter(({ subject }) => subject?.digest.sha256 === digest)
|
|
191
|
+
.map(({ evidence_id: id }) => id);
|
|
192
|
+
};
|
|
193
|
+
const pairLimitations = (pair, status) => [
|
|
194
|
+
...(pair.basis === "structural-fingerprint"
|
|
195
|
+
? [
|
|
196
|
+
"Structural fingerprint equality is rename-resistant inference; it is not byte identity or proof of semantic equivalence.",
|
|
197
|
+
]
|
|
198
|
+
: []),
|
|
199
|
+
...(pair.basis === "source-map-identity"
|
|
200
|
+
? [
|
|
201
|
+
"Source-map original names identify a candidate source lineage; changed or unavailable source content remains explicit.",
|
|
202
|
+
]
|
|
203
|
+
: []),
|
|
204
|
+
...(pair.basis === "semantic-key"
|
|
205
|
+
? [
|
|
206
|
+
"Semantic-key pairing is a deterministic inference and is never promoted to exact identity.",
|
|
207
|
+
]
|
|
208
|
+
: []),
|
|
209
|
+
...(status === "unknown"
|
|
210
|
+
? ["Incomplete graph coverage prevents a complete node-level conclusion."]
|
|
211
|
+
: []),
|
|
212
|
+
];
|
|
213
|
+
const itemWithId = (semantic) => ({
|
|
214
|
+
...semantic,
|
|
215
|
+
item_id: `javc_item_${digestCanonical(semantic)}`,
|
|
216
|
+
});
|
|
217
|
+
const stringProperty = (node, key) => {
|
|
218
|
+
const values = [
|
|
219
|
+
...new Set(node.observations
|
|
220
|
+
.map(({ properties: values }) => values[key])
|
|
221
|
+
.filter((value) => typeof value === "string")),
|
|
222
|
+
];
|
|
223
|
+
return values.length === 1 ? (values[0] ?? null) : null;
|
|
224
|
+
};
|
|
225
|
+
const graphsComplete = (context) => context.leftGraph.coverage.status === "complete" &&
|
|
226
|
+
context.rightGraph.coverage.status === "complete";
|
|
227
|
+
const canonical = (value) => {
|
|
228
|
+
const encoded = canonicalize(value);
|
|
229
|
+
if (encoded === undefined)
|
|
230
|
+
throw new TypeError("Application version item could not canonicalize data");
|
|
231
|
+
return encoded;
|
|
232
|
+
};
|
|
233
|
+
const digestCanonical = (value) => createHash("sha256").update(canonical(value)).digest("hex");
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { compareCodePoints } from "./javascriptApplicationGraph.js";
|
|
2
|
+
const matchTiers = () => [
|
|
3
|
+
{ basis: "exact-content-digest", confidence: "exact", key: contentDigestKey },
|
|
4
|
+
{
|
|
5
|
+
basis: "exact-module-source-digest",
|
|
6
|
+
confidence: "exact",
|
|
7
|
+
key: moduleSourceDigestKey,
|
|
8
|
+
},
|
|
9
|
+
{ basis: "exact-node-identity", confidence: "exact", key: nodeIdentityKey },
|
|
10
|
+
{ basis: "source-map-identity", confidence: "high", key: sourceMapKey },
|
|
11
|
+
{
|
|
12
|
+
basis: "structural-fingerprint",
|
|
13
|
+
confidence: "medium",
|
|
14
|
+
key: structuralFingerprintKey,
|
|
15
|
+
},
|
|
16
|
+
{ basis: "semantic-key", confidence: "medium", key: semanticKey },
|
|
17
|
+
];
|
|
18
|
+
/** Apply ordered, unique-only matching tiers without fuzzy pairing. */
|
|
19
|
+
export const matchJavaScriptApplicationVersions = (leftNodes, rightNodes) => {
|
|
20
|
+
const left = new Map(leftNodes.map((node) => [node.node_id, node]));
|
|
21
|
+
const right = new Map(rightNodes.map((node) => [node.node_id, node]));
|
|
22
|
+
const leftCandidates = new Map();
|
|
23
|
+
const rightCandidates = new Map();
|
|
24
|
+
const pairs = [];
|
|
25
|
+
for (const tier of matchTiers())
|
|
26
|
+
applyTier({ left, right, pairs, leftCandidates, rightCandidates, tier });
|
|
27
|
+
const remainingLeft = sortedNodes(left.values());
|
|
28
|
+
const remainingRight = sortedNodes(right.values());
|
|
29
|
+
const leftIds = new Set(remainingLeft.map(({ node_id: id }) => id));
|
|
30
|
+
const rightIds = new Set(remainingRight.map(({ node_id: id }) => id));
|
|
31
|
+
return {
|
|
32
|
+
pairs: pairs.sort((a, b) => compareCodePoints(`${a.left.node_id}\0${a.right.node_id}`, `${b.left.node_id}\0${b.right.node_id}`)),
|
|
33
|
+
unmatchedLeft: remainingLeft,
|
|
34
|
+
unmatchedRight: remainingRight,
|
|
35
|
+
candidatesForLeft: filteredCandidates(leftCandidates, rightIds),
|
|
36
|
+
candidatesForRight: filteredCandidates(rightCandidates, leftIds),
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const applyTier = (context) => {
|
|
40
|
+
const leftByKey = nodesByKey(context.left.values(), context.tier.key);
|
|
41
|
+
const rightByKey = nodesByKey(context.right.values(), context.tier.key);
|
|
42
|
+
for (const key of [...leftByKey.keys()].sort(compareCodePoints)) {
|
|
43
|
+
const leftGroup = leftByKey.get(key) ?? [];
|
|
44
|
+
const rightGroup = rightByKey.get(key) ?? [];
|
|
45
|
+
if (rightGroup.length === 0)
|
|
46
|
+
continue;
|
|
47
|
+
recordCandidates(leftGroup, rightGroup, context.leftCandidates, context.rightCandidates);
|
|
48
|
+
if (leftGroup.length !== 1 || rightGroup.length !== 1)
|
|
49
|
+
continue;
|
|
50
|
+
const leftNode = leftGroup[0];
|
|
51
|
+
const rightNode = rightGroup[0];
|
|
52
|
+
if (leftNode === undefined || rightNode === undefined)
|
|
53
|
+
continue;
|
|
54
|
+
context.pairs.push({ left: leftNode, right: rightNode, ...context.tier });
|
|
55
|
+
context.left.delete(leftNode.node_id);
|
|
56
|
+
context.right.delete(rightNode.node_id);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const nodesByKey = (nodes, keyFor) => {
|
|
60
|
+
const output = new Map();
|
|
61
|
+
for (const node of nodes) {
|
|
62
|
+
const key = keyFor(node);
|
|
63
|
+
if (key === null)
|
|
64
|
+
continue;
|
|
65
|
+
output.set(key, [...(output.get(key) ?? []), node]);
|
|
66
|
+
}
|
|
67
|
+
return output;
|
|
68
|
+
};
|
|
69
|
+
const recordCandidates = (left, right, leftCandidates, rightCandidates) => {
|
|
70
|
+
for (const leftNode of left) {
|
|
71
|
+
const candidates = leftCandidates.get(leftNode.node_id) ?? new Set();
|
|
72
|
+
for (const rightNode of right)
|
|
73
|
+
candidates.add(rightNode.node_id);
|
|
74
|
+
leftCandidates.set(leftNode.node_id, candidates);
|
|
75
|
+
}
|
|
76
|
+
for (const rightNode of right) {
|
|
77
|
+
const candidates = rightCandidates.get(rightNode.node_id) ?? new Set();
|
|
78
|
+
for (const leftNode of left)
|
|
79
|
+
candidates.add(leftNode.node_id);
|
|
80
|
+
rightCandidates.set(rightNode.node_id, candidates);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const filteredCandidates = (candidates, retained) => new Map([...candidates.entries()].flatMap(([nodeId, values]) => {
|
|
84
|
+
const filtered = [...values]
|
|
85
|
+
.filter((value) => retained.has(value))
|
|
86
|
+
.sort(compareCodePoints);
|
|
87
|
+
return filtered.length === 0 ? [] : [[nodeId, filtered]];
|
|
88
|
+
}));
|
|
89
|
+
const nodeIdentityKey = (node) => `node\0${node.kind}\0${node.node_id}`;
|
|
90
|
+
const contentDigestKey = (node) => node.identity.strategy === "content-digest"
|
|
91
|
+
? `content\0${node.kind}\0${node.identity.sha256}`
|
|
92
|
+
: null;
|
|
93
|
+
const moduleSourceDigestKey = (node) => {
|
|
94
|
+
if (node.kind !== "javascript-module")
|
|
95
|
+
return null;
|
|
96
|
+
const value = uniqueStringProperty(node, "source_sha256");
|
|
97
|
+
return value === null ? null : `module-source\0${node.kind}\0${value}`;
|
|
98
|
+
};
|
|
99
|
+
const sourceMapKey = (node) => node.kind === "source-module" &&
|
|
100
|
+
node.identity.strategy === "source-map-original"
|
|
101
|
+
? `source-map\0${node.identity.original_source}`
|
|
102
|
+
: null;
|
|
103
|
+
const structuralFingerprintKey = (node) => {
|
|
104
|
+
if (node.kind !== "javascript-module")
|
|
105
|
+
return null;
|
|
106
|
+
const digest = uniqueStringProperty(node, "structural_fingerprint_sha256");
|
|
107
|
+
const algorithm = uniqueStringProperty(node, "structural_fingerprint_algorithm");
|
|
108
|
+
const status = uniqueStringProperty(node, "structural_fingerprint_status");
|
|
109
|
+
return digest === null || algorithm === null || status !== "complete"
|
|
110
|
+
? null
|
|
111
|
+
: `structural\0${node.kind}\0${algorithm}\0${digest}`;
|
|
112
|
+
};
|
|
113
|
+
const semanticKey = (node) => {
|
|
114
|
+
if ([
|
|
115
|
+
"javascript-module",
|
|
116
|
+
"javascript-chunk",
|
|
117
|
+
"runtime-script-instance",
|
|
118
|
+
"unknown",
|
|
119
|
+
].includes(node.kind))
|
|
120
|
+
return null;
|
|
121
|
+
const properties = firstProperties(node);
|
|
122
|
+
const value = semanticValue(node, properties);
|
|
123
|
+
return value === null ? null : `semantic\0${node.kind}\0${value}`;
|
|
124
|
+
};
|
|
125
|
+
const semanticValue = (node, properties) => {
|
|
126
|
+
if (node.kind === "endpoint")
|
|
127
|
+
return joined(properties.endpoint_kind, properties.value);
|
|
128
|
+
if (node.kind === "storage")
|
|
129
|
+
return joined(properties.storage_kind, properties.name ?? properties.mechanism);
|
|
130
|
+
if (node.kind === "ipc-channel")
|
|
131
|
+
return properties.resolution === "dynamic"
|
|
132
|
+
? null
|
|
133
|
+
: (stringValue(properties.channel) ?? firstLabel(node));
|
|
134
|
+
if (node.kind === "context-bridge-api")
|
|
135
|
+
return firstLabel(node) ?? stringValue(properties.api_name);
|
|
136
|
+
if (node.kind === "native-export")
|
|
137
|
+
return (joined(properties.specifier, stringList(properties.requested_members).join(",")) ?? firstLabel(node));
|
|
138
|
+
if ([
|
|
139
|
+
"electron-main",
|
|
140
|
+
"electron-preload",
|
|
141
|
+
"electron-renderer",
|
|
142
|
+
"electron-utility",
|
|
143
|
+
].includes(node.kind))
|
|
144
|
+
return joined(properties.declared_path, properties.resolved_path);
|
|
145
|
+
if (node.kind === "package")
|
|
146
|
+
return stringValue(properties.name);
|
|
147
|
+
return canonicalPath(node) ?? stringValue(properties.path);
|
|
148
|
+
};
|
|
149
|
+
const canonicalPath = (node) => node.identity.strategy === "canonical-path" ? node.identity.path : null;
|
|
150
|
+
const firstProperties = (node) => node.observations[0]?.properties ?? {};
|
|
151
|
+
const firstLabel = (node) => node.observations.find(({ label }) => label !== null)?.label ?? null;
|
|
152
|
+
const uniqueStringProperty = (node, name) => {
|
|
153
|
+
const values = [
|
|
154
|
+
...new Set(node.observations
|
|
155
|
+
.map(({ properties }) => properties[name])
|
|
156
|
+
.filter((value) => typeof value === "string")),
|
|
157
|
+
];
|
|
158
|
+
return values.length === 1 ? (values[0] ?? null) : null;
|
|
159
|
+
};
|
|
160
|
+
const joined = (...values) => {
|
|
161
|
+
const strings = values.map(stringValue);
|
|
162
|
+
return strings.some((value) => value === null) ? null : strings.join("\0");
|
|
163
|
+
};
|
|
164
|
+
const stringValue = (value) => typeof value === "string" && value !== "" ? value : null;
|
|
165
|
+
const stringList = (value) => Array.isArray(value)
|
|
166
|
+
? value
|
|
167
|
+
.filter((item) => typeof item === "string")
|
|
168
|
+
.sort(compareCodePoints)
|
|
169
|
+
: [];
|
|
170
|
+
const sortedNodes = (nodes) => [...nodes].sort((left, right) => compareCodePoints(left.node_id, right.node_id));
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
4
|
+
export const javascriptExportInstrumentationInputSchema = z.strictObject({
|
|
5
|
+
artifact_path: z.string().min(1).max(4_096),
|
|
6
|
+
artifact_sha256: digestSchema,
|
|
7
|
+
selection: z.strictObject({
|
|
8
|
+
byte_start: z.number().int().min(0),
|
|
9
|
+
byte_end: z.number().int().min(1),
|
|
10
|
+
selected_sha256: digestSchema,
|
|
11
|
+
export_name: z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]{0,199}$/u),
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
14
|
+
export const javascriptExportTransformationManifestSchema = z.strictObject({
|
|
15
|
+
schema_version: z.literal(1),
|
|
16
|
+
kind: z.literal("commonjs-factory-export-v1"),
|
|
17
|
+
artifact_path: z.string().min(1).max(4_096),
|
|
18
|
+
original_sha256: digestSchema,
|
|
19
|
+
original_byte_length: z.number().int().min(0),
|
|
20
|
+
instrumented_sha256: digestSchema,
|
|
21
|
+
instrumented_byte_length: z.number().int().min(0),
|
|
22
|
+
selected_range: z.strictObject({
|
|
23
|
+
byte_start: z.number().int().min(0),
|
|
24
|
+
byte_end: z.number().int().min(1),
|
|
25
|
+
selected_sha256: digestSchema,
|
|
26
|
+
}),
|
|
27
|
+
inserted_ranges: z
|
|
28
|
+
.array(z.strictObject({
|
|
29
|
+
byte_start: z.number().int().min(0),
|
|
30
|
+
byte_end: z.number().int().min(1),
|
|
31
|
+
inserted_sha256: digestSchema,
|
|
32
|
+
purpose: z.enum(["factory-prefix", "export-binding", "factory-suffix"]),
|
|
33
|
+
}))
|
|
34
|
+
.length(3),
|
|
35
|
+
output_format: z.literal("commonjs-factory"),
|
|
36
|
+
source_map: z.literal(null),
|
|
37
|
+
});
|
|
38
|
+
/** Instrument one exact UTF-8 expression range without mutating source bytes. */
|
|
39
|
+
export const instrumentJavaScriptExport = (source, rawInput) => {
|
|
40
|
+
const input = javascriptExportInstrumentationInputSchema.parse(rawInput);
|
|
41
|
+
if (sha256(source) !== input.artifact_sha256)
|
|
42
|
+
throw new TypeError("JavaScript instrumentation artifact digest mismatch");
|
|
43
|
+
const { byte_start: start, byte_end: end } = input.selection;
|
|
44
|
+
if (end <= start || end > source.byteLength)
|
|
45
|
+
throw new TypeError("JavaScript instrumentation selection is out of bounds");
|
|
46
|
+
const selected = source.slice(start, end);
|
|
47
|
+
if (sha256(selected) !== input.selection.selected_sha256)
|
|
48
|
+
throw new TypeError("JavaScript instrumentation selection digest mismatch");
|
|
49
|
+
decodeUtf8(source);
|
|
50
|
+
decodeUtf8(selected);
|
|
51
|
+
const prefix = encode("function(module, exports, require) {\n");
|
|
52
|
+
const binding = encode(`\nmodule.exports[${JSON.stringify(input.selection.export_name)}] = (${decodeUtf8(selected)});\n`);
|
|
53
|
+
const suffix = encode("}\n");
|
|
54
|
+
const bytes = concatenate([prefix, source, binding, suffix]);
|
|
55
|
+
const bindingStart = prefix.byteLength + source.byteLength;
|
|
56
|
+
const suffixStart = bindingStart + binding.byteLength;
|
|
57
|
+
const manifest = javascriptExportTransformationManifestSchema.parse({
|
|
58
|
+
schema_version: 1,
|
|
59
|
+
kind: "commonjs-factory-export-v1",
|
|
60
|
+
artifact_path: input.artifact_path,
|
|
61
|
+
original_sha256: input.artifact_sha256,
|
|
62
|
+
original_byte_length: source.byteLength,
|
|
63
|
+
instrumented_sha256: sha256(bytes),
|
|
64
|
+
instrumented_byte_length: bytes.byteLength,
|
|
65
|
+
selected_range: {
|
|
66
|
+
byte_start: start,
|
|
67
|
+
byte_end: end,
|
|
68
|
+
selected_sha256: input.selection.selected_sha256,
|
|
69
|
+
},
|
|
70
|
+
inserted_ranges: [
|
|
71
|
+
range(0, prefix, "factory-prefix"),
|
|
72
|
+
range(bindingStart, binding, "export-binding"),
|
|
73
|
+
range(suffixStart, suffix, "factory-suffix"),
|
|
74
|
+
],
|
|
75
|
+
output_format: "commonjs-factory",
|
|
76
|
+
source_map: null,
|
|
77
|
+
});
|
|
78
|
+
return { bytes, manifest };
|
|
79
|
+
};
|
|
80
|
+
const range = (start, bytes, purpose) => ({
|
|
81
|
+
byte_start: start,
|
|
82
|
+
byte_end: start + bytes.byteLength,
|
|
83
|
+
inserted_sha256: sha256(bytes),
|
|
84
|
+
purpose,
|
|
85
|
+
});
|
|
86
|
+
const encode = (value) => new TextEncoder().encode(value);
|
|
87
|
+
const decodeUtf8 = (value) => new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
88
|
+
const concatenate = (values) => {
|
|
89
|
+
const output = new Uint8Array(values.reduce((total, value) => total + value.byteLength, 0));
|
|
90
|
+
let offset = 0;
|
|
91
|
+
for (const value of values) {
|
|
92
|
+
output.set(value, offset);
|
|
93
|
+
offset += value.byteLength;
|
|
94
|
+
}
|
|
95
|
+
return output;
|
|
96
|
+
};
|
|
97
|
+
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { compareCodePoints } from "./javascriptApplicationGraph.js";
|
|
2
|
+
/** Locate every graph node matching a typed literal seed without regex execution. */
|
|
3
|
+
export const findApplicationFeatureSeeds = (nodes, seed) => nodes
|
|
4
|
+
.flatMap((node) => seedMatch(node, seed))
|
|
5
|
+
.sort((left, right) => compareCodePoints(`${left.node_id}\0${left.basis}\0${left.field}`, `${right.node_id}\0${right.basis}\0${right.field}`));
|
|
6
|
+
const seedMatch = (node, seed) => {
|
|
7
|
+
if (seed.kind === "node-id")
|
|
8
|
+
return node.node_id === seed.value
|
|
9
|
+
? [
|
|
10
|
+
{
|
|
11
|
+
node_id: node.node_id,
|
|
12
|
+
kind: node.kind,
|
|
13
|
+
basis: "node-id",
|
|
14
|
+
field: "node_id",
|
|
15
|
+
},
|
|
16
|
+
]
|
|
17
|
+
: [];
|
|
18
|
+
if (!kindMatches(node, seed.kind))
|
|
19
|
+
return [];
|
|
20
|
+
const mode = seed.match ?? (seed.kind === "string" ? "contains" : "exact");
|
|
21
|
+
const query = normalized(seed.value, seed.case_sensitive);
|
|
22
|
+
const match = candidateFields(node, seed.kind).find((candidate) => {
|
|
23
|
+
const value = normalized(candidate.value, seed.case_sensitive);
|
|
24
|
+
return mode === "exact" ? value === query : value.includes(query);
|
|
25
|
+
});
|
|
26
|
+
return match === undefined
|
|
27
|
+
? []
|
|
28
|
+
: [
|
|
29
|
+
{
|
|
30
|
+
node_id: node.node_id,
|
|
31
|
+
kind: node.kind,
|
|
32
|
+
basis: match.basis,
|
|
33
|
+
field: match.field,
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
};
|
|
37
|
+
const kindMatches = (node, kind) => {
|
|
38
|
+
if (kind === "string")
|
|
39
|
+
return true;
|
|
40
|
+
if (kind === "route")
|
|
41
|
+
return (node.kind === "endpoint" &&
|
|
42
|
+
node.observations.some(({ properties }) => properties.endpoint_kind === "route"));
|
|
43
|
+
if (kind === "api")
|
|
44
|
+
return node.kind === "context-bridge-api";
|
|
45
|
+
if (kind === "channel")
|
|
46
|
+
return node.kind === "ipc-channel";
|
|
47
|
+
if (kind === "module")
|
|
48
|
+
return ["javascript-asset", "javascript-module", "source-module"].includes(node.kind);
|
|
49
|
+
return node.kind === "native-export";
|
|
50
|
+
};
|
|
51
|
+
const candidateFields = (node, seedKind) => {
|
|
52
|
+
const fields = [
|
|
53
|
+
...identityFields(node),
|
|
54
|
+
...node.observations.flatMap((observation, index) => [
|
|
55
|
+
...(observation.label === null
|
|
56
|
+
? []
|
|
57
|
+
: [
|
|
58
|
+
{
|
|
59
|
+
basis: "label",
|
|
60
|
+
field: `observations[${String(index)}].label`,
|
|
61
|
+
value: observation.label,
|
|
62
|
+
},
|
|
63
|
+
]),
|
|
64
|
+
...propertyFields(observation.properties, `observations[${String(index)}].properties`),
|
|
65
|
+
]),
|
|
66
|
+
];
|
|
67
|
+
if (seedKind === "string")
|
|
68
|
+
return fields;
|
|
69
|
+
const names = relevantFields[seedKind];
|
|
70
|
+
return fields.filter(({ field }) => names.some((name) => field === "identity" || field.endsWith(name)));
|
|
71
|
+
};
|
|
72
|
+
const relevantFields = {
|
|
73
|
+
route: [".label", ".value", ".path"],
|
|
74
|
+
api: [".label", ".api_name", ".key", ".methods", ".members"],
|
|
75
|
+
channel: [".label", ".channel", ".key"],
|
|
76
|
+
module: [
|
|
77
|
+
".label",
|
|
78
|
+
".module_key",
|
|
79
|
+
".path",
|
|
80
|
+
".source",
|
|
81
|
+
".original_source",
|
|
82
|
+
".exports",
|
|
83
|
+
],
|
|
84
|
+
"native-export": [".label", ".key", ".requested_members", ".members"],
|
|
85
|
+
};
|
|
86
|
+
const identityFields = (node) => {
|
|
87
|
+
const identity = node.identity;
|
|
88
|
+
if (identity.strategy === "content-digest")
|
|
89
|
+
return [{ basis: "identity", field: "identity", value: identity.sha256 }];
|
|
90
|
+
if (identity.strategy === "source-map-original")
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
basis: "identity",
|
|
94
|
+
field: "identity.original_source",
|
|
95
|
+
value: identity.original_source,
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
if (identity.strategy === "canonical-path")
|
|
99
|
+
return [
|
|
100
|
+
{ basis: "identity", field: "identity.path", value: identity.path },
|
|
101
|
+
];
|
|
102
|
+
if (identity.strategy === "artifact-local-key")
|
|
103
|
+
return [{ basis: "identity", field: "identity.key", value: identity.key }];
|
|
104
|
+
if (identity.strategy === "runtime-instance")
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
basis: "identity",
|
|
108
|
+
field: "identity.runtime_key",
|
|
109
|
+
value: identity.runtime_key,
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
return [];
|
|
113
|
+
};
|
|
114
|
+
const propertyFields = (value, prefix) => {
|
|
115
|
+
const output = [];
|
|
116
|
+
const visit = (current, path, depth) => {
|
|
117
|
+
if (output.length >= 512 || depth > 6)
|
|
118
|
+
return;
|
|
119
|
+
if (typeof current === "string") {
|
|
120
|
+
output.push({ basis: "property", field: path, value: current });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(current)) {
|
|
124
|
+
for (const [index, item] of current.entries())
|
|
125
|
+
visit(item, `${path}[${String(index)}]`, depth + 1);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (current !== null && typeof current === "object")
|
|
129
|
+
for (const key of Object.keys(current).sort(compareCodePoints))
|
|
130
|
+
visit(current[key] ?? null, `${path}.${key}`, depth + 1);
|
|
131
|
+
};
|
|
132
|
+
visit(value, prefix, 0);
|
|
133
|
+
return output;
|
|
134
|
+
};
|
|
135
|
+
const normalized = (value, caseSensitive) => {
|
|
136
|
+
const normalizedValue = value.normalize("NFKC");
|
|
137
|
+
return caseSensitive
|
|
138
|
+
? normalizedValue
|
|
139
|
+
: normalizedValue.toLocaleLowerCase("en-US");
|
|
140
|
+
};
|