rea-agents 1.6.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -66
- package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
- package/bridge/hopper_bridge.py +13 -3
- package/dist/application/AbortablePromise.js +25 -0
- package/dist/application/AnalysisProvider.js +3 -0
- package/dist/application/AnalysisProviderEvaluation.js +60 -0
- package/dist/application/AnalysisProviderRegistry.js +228 -0
- package/dist/application/AnalysisSnapshotCache.js +41 -21
- package/dist/application/AnalysisSnapshotFiles.js +5 -6
- package/dist/application/BinarySession.js +218 -101
- package/dist/application/CapabilityInventory.js +18 -0
- package/dist/application/CompositeProvider.js +20 -8
- package/dist/application/CrossVersionInvestigation.js +10 -6
- package/dist/application/DirectAnalysis.js +49 -35
- package/dist/application/Doctor.js +110 -8
- package/dist/application/ElectronBoundaryAnalysis.js +96 -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 +199 -0
- package/dist/application/EnhancedTools.js +39 -14
- package/dist/application/InvestigationProviders.js +41 -0
- 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 +305 -0
- package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
- package/dist/application/JavaScriptArtifactFiles.js +231 -0
- package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
- package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
- package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
- package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
- package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
- package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
- package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +266 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- package/dist/application/MacHopper.js +20 -4
- 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/PermissionAuthority.js +1 -1
- package/dist/application/ProcessCaptureLifecycle.js +1 -1
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessSampling.js +16 -12
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/SessionProviderRouter.js +219 -0
- package/dist/application/Setup.js +84 -60
- package/dist/application/SetupClients.js +1 -1
- package/dist/application/SetupPlan.js +9 -2
- package/dist/application/SupportedClients.js +40 -16
- package/dist/application/Upgrade.js +1 -1
- package/dist/application/runtime.js +11 -6
- package/dist/artifacts/AsarArtifactReader.js +23 -3
- package/dist/browser/CdpBrowserProvider.js +4 -2
- package/dist/browser/CdpCaptureEvents.js +49 -0
- package/dist/browser/CdpElectronInspection.js +106 -162
- package/dist/browser/CdpElectronProvider.js +9 -3
- package/dist/browser/CdpElectronScriptEvents.js +69 -0
- package/dist/browser/CdpElectronScripts.js +94 -0
- package/dist/browser/CdpElectronWorkers.js +56 -0
- package/dist/browser/CdpEndpoint.js +14 -1
- package/dist/browser/CdpPageCapture.js +8 -4
- package/dist/catalogIdentity.js +6 -42
- package/dist/cli.js +446 -34
- package/dist/cliApplicationCommands.js +62 -0
- package/dist/cliBrowserAdvancedCommands.js +5 -4
- package/dist/cliBrowserCommands.js +5 -4
- package/dist/cliCommandNames.js +56 -0
- package/dist/cliElectronCommands.js +106 -3
- package/dist/cliEvidenceCommands.js +4 -3
- package/dist/cliInvestigationCommands.js +2 -1
- package/dist/cliJsonInput.js +54 -0
- package/dist/cliPolicyCommands.js +2 -1
- package/dist/cliProcessCommands.js +3 -2
- package/dist/config.js +101 -2
- package/dist/contracts/applicationToolContracts.js +91 -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/enhancedInputs.js +2 -2
- 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 +28 -0
- package/dist/contracts/sessionLifecycleInputs.js +2 -0
- package/dist/contracts/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +69 -65
- package/dist/contracts/toolEffects.js +160 -0
- package/dist/contracts/toolOutputSchemas.js +152 -42
- package/dist/doctorRuntime.js +47 -0
- package/dist/domain/analysisProfile.js +78 -0
- package/dist/domain/analysisSnapshot.js +92 -46
- package/dist/domain/artifactComparison.js +3 -4
- package/dist/domain/artifactGraph.js +1 -0
- package/dist/domain/binaryTarget.js +4 -29
- package/dist/domain/browserObservation.js +3 -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 +194 -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 +148 -7
- package/dist/domain/evidence.js +44 -5
- package/dist/domain/evidenceBundle.js +9 -7
- package/dist/domain/functionComparison.js +9 -3
- package/dist/domain/functionComparisonNormalization.js +37 -0
- package/dist/domain/functionComparisonSchemas.js +3 -8
- package/dist/domain/functionDossierEvidence.js +4 -1
- package/dist/domain/hopperValues.js +66 -9
- package/dist/domain/inputIssueProjection.js +52 -0
- package/dist/domain/investigationWorkspace.js +2 -1
- package/dist/domain/javascriptApplicationAnalysis.js +155 -0
- package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
- package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
- package/dist/domain/javascriptApplicationGraph.js +282 -0
- package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
- package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionItems.js +221 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
- package/dist/domain/javascriptAstFingerprint.js +143 -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 +88 -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 +191 -0
- package/dist/domain/javascriptStaticAnalysis.js +346 -0
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
- package/dist/domain/javascriptStaticAnalysisState.js +23 -0
- package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
- package/dist/domain/managedApplicationGraph.js +556 -0
- package/dist/domain/managedArtifact.js +444 -0
- package/dist/domain/managedMemberComparison.js +565 -0
- package/dist/domain/managedNativeVerification.js +408 -0
- package/dist/domain/managedReconstruction.js +183 -0
- package/dist/domain/managedRuntimeCorrelation.js +175 -0
- package/dist/domain/reconstructionVerificationSchemas.js +1 -3
- package/dist/dotnet/ManagedArtifactInspector.js +318 -0
- package/dist/dotnet/ManagedMemberInspector.js +887 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +368 -0
- package/dist/dotnet/ManagedMetadataLayout.js +322 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
- package/dist/dotnet/ManagedPeReader.js +157 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
- package/dist/ghidra/GhidraClient.js +400 -0
- package/dist/ghidra/GhidraClientTypes.js +1 -0
- package/dist/ghidra/GhidraDefaults.js +20 -0
- package/dist/ghidra/GhidraDiagnostics.js +43 -0
- package/dist/ghidra/GhidraDoctor.js +59 -0
- package/dist/ghidra/GhidraFunctionValues.js +264 -0
- package/dist/ghidra/GhidraInstallation.js +239 -0
- package/dist/ghidra/GhidraInventoryValues.js +217 -0
- package/dist/ghidra/GhidraLauncher.js +139 -0
- package/dist/ghidra/GhidraProvider.js +366 -0
- package/dist/ghidra/GhidraRequestQueue.js +98 -0
- package/dist/ghidra/GhidraResponseBuffer.js +31 -0
- package/dist/ghidra/GhidraResponseRouter.js +24 -0
- package/dist/ghidra/GhidraSessionError.js +39 -0
- package/dist/ghidra/GhidraSessionValues.js +62 -0
- package/dist/ghidra/GhidraSocketConnection.js +46 -0
- package/dist/ghidra/protocol.js +69 -0
- package/dist/hopper/BridgeLauncher.js +19 -54
- package/dist/hopper/HopperAnalysisProfile.js +106 -0
- package/dist/hopper/HopperClient.js +154 -162
- package/dist/hopper/HopperProvider.js +99 -7
- package/dist/hopper/HopperSessionValues.js +27 -0
- package/dist/hopper/HopperSocketConnection.js +31 -0
- package/dist/main.js +14 -0
- package/dist/native/NativeMacOSProvider.js +3 -1
- package/dist/process/PendingOperations.js +59 -0
- package/dist/process/PrivateRuntimeRoot.js +33 -0
- package/dist/process/ProviderDeadline.js +95 -0
- package/dist/process/ProviderProcess.js +275 -0
- package/dist/replay/JavaScriptReplayWorker.js +286 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/createServer.js +53 -0
- package/dist/server/promptCompletion.js +9 -3
- package/dist/server/registerApplicationTools.js +115 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +33 -8
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +45 -3
- package/dist/server/registerEnhancedTools.js +67 -19
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools.js +21 -17
- package/dist/server/registerManagedTools.js +6 -0
- package/dist/server/registerManagedWorkflowTools.js +277 -0
- package/dist/server/registerOfficialTools.js +11 -8
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionStatusTool.js +7 -7
- package/dist/server/registerSessionTools.js +57 -87
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +79 -2
- package/dist/server/toolResult.js +25 -17
- package/package.json +17 -7
- package/scripts/rea.mjs +36 -2
- package/skills/rea-analysis/SKILL.md +59 -3
- /package/dist/{application → process}/ProcessOwnership.js +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import canonicalize from "canonicalize";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { analysisProfileSchema, analysisProfilesEqual, committedProviderSchema, } from "./analysisProfile.js";
|
|
4
5
|
import { evidenceBundleForTarget, evidenceBundleSchema, parseEvidenceBundle, } from "./evidenceBundle.js";
|
|
5
|
-
import { evidenceLocationSchema,
|
|
6
|
+
import { evidenceLocationSchema, } from "./evidence.js";
|
|
6
7
|
import { jsonObjectSchema, jsonValueSchema, } from "./jsonValue.js";
|
|
7
8
|
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
8
9
|
const architectureSchema = z.enum(["x86", "x86_64", "arm", "arm64"]);
|
|
9
10
|
const formatSchema = z.enum([
|
|
10
|
-
"
|
|
11
|
+
"analysis-database",
|
|
11
12
|
"mach-o",
|
|
12
13
|
"elf",
|
|
13
14
|
"pe",
|
|
@@ -21,11 +22,13 @@ const formatSchema = z.enum([
|
|
|
21
22
|
"javascript",
|
|
22
23
|
"source-map",
|
|
23
24
|
]);
|
|
25
|
+
const kindSchema = z.enum(["executable", "database", "archive", "artifact"]);
|
|
24
26
|
const subjectSchema = z.object({
|
|
25
27
|
path: z.string().min(1),
|
|
26
28
|
sha256: digestSchema,
|
|
27
29
|
format: z.enum([
|
|
28
30
|
...formatSchema.options,
|
|
31
|
+
"hopper",
|
|
29
32
|
"directory",
|
|
30
33
|
"file",
|
|
31
34
|
"unknown",
|
|
@@ -37,9 +40,24 @@ const subjectSchema = z.object({
|
|
|
37
40
|
});
|
|
38
41
|
const targetSchema = z.object({
|
|
39
42
|
sha256: digestSchema,
|
|
43
|
+
kind: kindSchema,
|
|
40
44
|
format: formatSchema,
|
|
41
45
|
architecture: architectureSchema.nullable(),
|
|
42
|
-
|
|
46
|
+
});
|
|
47
|
+
const bindingSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
provider: committedProviderSchema,
|
|
50
|
+
analysis_profile: analysisProfileSchema,
|
|
51
|
+
})
|
|
52
|
+
.superRefine((binding, context) => {
|
|
53
|
+
if (binding.provider.id !== binding.analysis_profile.provider.id ||
|
|
54
|
+
binding.provider.name !== binding.analysis_profile.provider.name ||
|
|
55
|
+
binding.provider.version !== binding.analysis_profile.provider.version)
|
|
56
|
+
context.addIssue({
|
|
57
|
+
code: "custom",
|
|
58
|
+
path: ["provider"],
|
|
59
|
+
message: "Snapshot binding provider does not match analysis profile",
|
|
60
|
+
});
|
|
43
61
|
});
|
|
44
62
|
const entrySchema = z.object({
|
|
45
63
|
query_id: z.string().regex(/^query_[a-f0-9]{64}$/u),
|
|
@@ -48,16 +66,17 @@ const entrySchema = z.object({
|
|
|
48
66
|
execution: z.object({
|
|
49
67
|
result: jsonValueSchema,
|
|
50
68
|
raw_result: jsonValueSchema.nullable(),
|
|
51
|
-
provider:
|
|
69
|
+
provider: committedProviderSchema,
|
|
52
70
|
limitations: z.array(z.string()),
|
|
53
71
|
locations: z.array(evidenceLocationSchema),
|
|
54
72
|
subject: subjectSchema.nullable(),
|
|
55
73
|
}),
|
|
56
74
|
});
|
|
57
|
-
/** Provider-
|
|
58
|
-
const analysisSnapshotSchema = z.object({
|
|
59
|
-
snapshot_version: z.literal(
|
|
75
|
+
/** Provider- and profile-exact cache of successful immutable analysis calls. */
|
|
76
|
+
export const analysisSnapshotSchema = z.object({
|
|
77
|
+
snapshot_version: z.literal(2),
|
|
60
78
|
target: targetSchema,
|
|
79
|
+
binding: bindingSchema,
|
|
61
80
|
entries: z.array(entrySchema).max(10_000),
|
|
62
81
|
evidence_bundle: evidenceBundleSchema,
|
|
63
82
|
});
|
|
@@ -71,19 +90,32 @@ const canonicalJson = (value) => {
|
|
|
71
90
|
/** Project a binary into the path-free identity used for cache invalidation. */
|
|
72
91
|
export const snapshotTarget = (target) => ({
|
|
73
92
|
sha256: target.sha256,
|
|
93
|
+
kind: target.kind,
|
|
74
94
|
format: target.format,
|
|
75
95
|
architecture: target.architecture ?? null,
|
|
76
|
-
loader_args: [...target.loaderArgs],
|
|
77
96
|
});
|
|
78
|
-
/**
|
|
97
|
+
/** Build the immutable provider/profile binding used by snapshot v2. */
|
|
98
|
+
export const snapshotBinding = (profile) => bindingSchema.parse({
|
|
99
|
+
provider: profile.provider,
|
|
100
|
+
analysis_profile: profile,
|
|
101
|
+
});
|
|
102
|
+
/** Compare an open binary with the immutable target identity in a snapshot. */
|
|
79
103
|
export const snapshotMatchesTarget = (snapshot, target) => snapshot.sha256 === target.sha256 &&
|
|
104
|
+
snapshot.kind === target.kind &&
|
|
80
105
|
snapshot.format === target.format &&
|
|
81
|
-
snapshot.architecture === (target.architecture ?? null)
|
|
82
|
-
|
|
83
|
-
|
|
106
|
+
snapshot.architecture === (target.architecture ?? null);
|
|
107
|
+
/** Compare a snapshot binding with the selected concrete analysis profile. */
|
|
108
|
+
export const snapshotMatchesProfile = (binding, profile) => binding.provider.id === profile.provider.id &&
|
|
109
|
+
binding.provider.name === profile.provider.name &&
|
|
110
|
+
binding.provider.version === profile.provider.version &&
|
|
111
|
+
analysisProfilesEqual(binding.analysis_profile, profile);
|
|
112
|
+
/** Require an exact target and selected provider/profile cache partition. */
|
|
113
|
+
export const snapshotMatchesBinding = (snapshot, target, profile) => snapshotMatchesTarget(snapshot.target, target) &&
|
|
114
|
+
snapshotMatchesProfile(snapshot.binding, profile);
|
|
115
|
+
/** Find exact persisted CLI Evidence without starting an analysis provider. */
|
|
84
116
|
export const snapshotEvidenceForQuery = (snapshot, query) => {
|
|
85
|
-
const { target, operation, parameters, provider } = query;
|
|
86
|
-
if (!
|
|
117
|
+
const { target, bindingProfile, operation, parameters, provider, evidenceProfile, } = query;
|
|
118
|
+
if (!snapshotMatchesBinding(snapshot, target, bindingProfile))
|
|
87
119
|
return undefined;
|
|
88
120
|
const encodedParameters = canonicalJson(parameters);
|
|
89
121
|
return snapshot.evidence_bundle.records.find((record) => record.subject?.digest.sha256 === target.sha256 &&
|
|
@@ -91,49 +123,56 @@ export const snapshotEvidenceForQuery = (snapshot, query) => {
|
|
|
91
123
|
record.provider.id === provider.id &&
|
|
92
124
|
record.provider.name === provider.name &&
|
|
93
125
|
record.provider.version === provider.version &&
|
|
126
|
+
"analysis_profile" in record &&
|
|
127
|
+
analysisProfilesEqual(record.analysis_profile, evidenceProfile) &&
|
|
94
128
|
canonicalJson(record.parameters) === encodedParameters);
|
|
95
129
|
};
|
|
96
|
-
/** Compute the stable lookup key for one provider-specific
|
|
97
|
-
export const analysisQueryId = (target, operation, parameters
|
|
98
|
-
.update(canonicalJson({
|
|
99
|
-
target,
|
|
100
|
-
operation,
|
|
101
|
-
parameters,
|
|
102
|
-
provider: {
|
|
103
|
-
id: provider.id,
|
|
104
|
-
name: provider.name,
|
|
105
|
-
version: provider.version,
|
|
106
|
-
},
|
|
107
|
-
}))
|
|
130
|
+
/** Compute the stable lookup key for one provider/profile-specific query. */
|
|
131
|
+
export const analysisQueryId = (target, binding, operation, parameters) => `query_${createHash("sha256")
|
|
132
|
+
.update(canonicalJson({ target, binding, operation, parameters }))
|
|
108
133
|
.digest("hex")}`;
|
|
109
134
|
/** Create one serializable snapshot entry from a successful provider call. */
|
|
110
|
-
export const createAnalysisSnapshotEntry = (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
export const createAnalysisSnapshotEntry = (input) => {
|
|
136
|
+
const { target, binding, operation, parameters, execution } = input;
|
|
137
|
+
if (execution.analysisProfile === undefined ||
|
|
138
|
+
!snapshotMatchesProfile(binding, execution.analysisProfile) ||
|
|
139
|
+
execution.provider.id !== binding.provider.id ||
|
|
140
|
+
execution.provider.name !== binding.provider.name ||
|
|
141
|
+
execution.provider.version !== binding.provider.version)
|
|
142
|
+
throw new TypeError("Snapshot execution does not match its provider/profile binding");
|
|
143
|
+
return {
|
|
144
|
+
query_id: analysisQueryId(target, binding, operation, parameters),
|
|
145
|
+
operation,
|
|
146
|
+
parameters: jsonObjectSchema.parse(parameters),
|
|
147
|
+
execution: {
|
|
148
|
+
result: execution.result,
|
|
149
|
+
raw_result: execution.rawResult,
|
|
150
|
+
provider: binding.provider,
|
|
151
|
+
limitations: [...execution.limitations],
|
|
152
|
+
locations: [...execution.locations],
|
|
153
|
+
subject: execution.subject === null
|
|
154
|
+
? null
|
|
155
|
+
: {
|
|
156
|
+
...execution.subject,
|
|
157
|
+
architecture: execution.subject.architecture ?? null,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
/** Parse v2 and reject legacy snapshots, altered query IDs, or non-canonical order. */
|
|
129
163
|
export const parseAnalysisSnapshot = (input) => {
|
|
164
|
+
rejectLegacySnapshot(input);
|
|
130
165
|
const parsed = analysisSnapshotSchema.parse(input);
|
|
131
166
|
parseEvidenceBundle(parsed.evidence_bundle);
|
|
132
167
|
if (JSON.stringify(evidenceBundleForTarget(parsed.evidence_bundle, parsed.target.sha256)) !== JSON.stringify(parsed.evidence_bundle))
|
|
133
168
|
throw new TypeError("Analysis snapshot evidence contains records for another target");
|
|
134
169
|
const ids = new Set();
|
|
135
170
|
for (const entry of parsed.entries) {
|
|
136
|
-
|
|
171
|
+
if (entry.execution.provider.id !== parsed.binding.provider.id ||
|
|
172
|
+
entry.execution.provider.name !== parsed.binding.provider.name ||
|
|
173
|
+
entry.execution.provider.version !== parsed.binding.provider.version)
|
|
174
|
+
throw new TypeError("Analysis snapshot entry provider does not match");
|
|
175
|
+
const expected = analysisQueryId(parsed.target, parsed.binding, entry.operation, entry.parameters);
|
|
137
176
|
if (entry.query_id !== expected)
|
|
138
177
|
throw new TypeError("Analysis snapshot query identifier does not match");
|
|
139
178
|
if (ids.has(entry.query_id))
|
|
@@ -147,3 +186,10 @@ export const parseAnalysisSnapshot = (input) => {
|
|
|
147
186
|
};
|
|
148
187
|
/** Serialize a validated snapshot with byte-stable canonical entry ordering. */
|
|
149
188
|
export const serializeAnalysisSnapshot = (snapshot) => `${canonicalJson(parseAnalysisSnapshot(snapshot))}\n`;
|
|
189
|
+
const rejectLegacySnapshot = (input) => {
|
|
190
|
+
if (typeof input === "object" &&
|
|
191
|
+
input !== null &&
|
|
192
|
+
"snapshot_version" in input &&
|
|
193
|
+
input.snapshot_version === 1)
|
|
194
|
+
throw new TypeError("Analysis snapshot v1 is incompatible with profile-exact replay; recapture it as snapshot v2. Its Evidence bundle may be imported separately.");
|
|
195
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import canonicalize from "canonicalize";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import {} from "./artifactGraph.js";
|
|
4
|
-
import { evidenceSchema } from "./evidence.js";
|
|
5
4
|
import { parseArtifactInventoryEvidence, } from "./artifactInventoryEvidence.js";
|
|
6
5
|
const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
|
|
7
6
|
const comparisonStatusSchema = z.enum([
|
|
@@ -32,9 +31,9 @@ const comparisonDimensionSchema = z.enum([
|
|
|
32
31
|
"integrity",
|
|
33
32
|
]);
|
|
34
33
|
/** Strict Evidence-backed input for deterministic artifact comparison. */
|
|
35
|
-
export const artifactComparisonInputSchema = z.
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
export const artifactComparisonInputSchema = z.strictObject({
|
|
35
|
+
left_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
|
|
36
|
+
right_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
|
|
38
37
|
offset: z.number().int().min(0).default(0),
|
|
39
38
|
limit: z.number().int().min(1).max(500).default(100),
|
|
40
39
|
unknown_registry_approved: z
|
|
@@ -9,7 +9,7 @@ import { BinaryTargetError } from "./errors.js";
|
|
|
9
9
|
import { err, ok } from "./result.js";
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
11
11
|
/**
|
|
12
|
-
* Resolve and classify a readable local target before
|
|
12
|
+
* Resolve and classify a readable local target before a provider is selected.
|
|
13
13
|
* FAT Mach-O inputs select only a host-compatible architecture so setup remains
|
|
14
14
|
* non-interactive; unsupported or ambiguous inputs are returned as typed errors.
|
|
15
15
|
*/
|
|
@@ -31,8 +31,7 @@ export const parseBinaryTarget = async (input, cwd = process.cwd(), hostArchitec
|
|
|
31
31
|
sourcePath: canonical,
|
|
32
32
|
sha256: await sha256File(path),
|
|
33
33
|
kind: "database",
|
|
34
|
-
format: "
|
|
35
|
-
loaderArgs: [],
|
|
34
|
+
format: "analysis-database",
|
|
36
35
|
});
|
|
37
36
|
const handle = await open(path, "r");
|
|
38
37
|
let detected;
|
|
@@ -45,7 +44,6 @@ export const parseBinaryTarget = async (input, cwd = process.cwd(), hostArchitec
|
|
|
45
44
|
sha256: await sha256File(path),
|
|
46
45
|
kind: isArchiveFormat(artifactFormat) ? "archive" : "artifact",
|
|
47
46
|
format: artifactFormat,
|
|
48
|
-
loaderArgs: [],
|
|
49
47
|
});
|
|
50
48
|
detected = await readExecutableMetadata(handle, hostArchitecture);
|
|
51
49
|
}
|
|
@@ -210,9 +208,8 @@ const readExecutableMetadata = async (handle, hostArchitecture) => {
|
|
|
210
208
|
return parseExecutableHeader(bytes, hostArchitecture);
|
|
211
209
|
};
|
|
212
210
|
/**
|
|
213
|
-
* Parse supported executable headers without I/O
|
|
214
|
-
*
|
|
215
|
-
* Mach-O slice selection.
|
|
211
|
+
* Parse supported executable headers without I/O. The caller supplies the host
|
|
212
|
+
* architecture used for deterministic FAT Mach-O slice selection.
|
|
216
213
|
*/
|
|
217
214
|
export const parseExecutableHeader = (bytes, hostArchitecture) => {
|
|
218
215
|
if (bytes.length >= 4 &&
|
|
@@ -240,7 +237,6 @@ const parseThinMachO = (bytes, magic) => {
|
|
|
240
237
|
format: "mach-o",
|
|
241
238
|
architecture,
|
|
242
239
|
availableArchitectures: [architecture],
|
|
243
|
-
loaderArgs: ["-l", "Mach-O", hopperArchitectureFlag(architecture)],
|
|
244
240
|
});
|
|
245
241
|
};
|
|
246
242
|
const parseFatMachO = (bytes, magic, host) => {
|
|
@@ -274,13 +270,6 @@ const parseFatMachO = (bytes, magic, host) => {
|
|
|
274
270
|
format: "mach-o",
|
|
275
271
|
architecture: preferred,
|
|
276
272
|
availableArchitectures: architectures,
|
|
277
|
-
loaderArgs: [
|
|
278
|
-
"-l",
|
|
279
|
-
"FAT",
|
|
280
|
-
hopperArchitectureFlag(preferred),
|
|
281
|
-
"-l",
|
|
282
|
-
"Mach-O",
|
|
283
|
-
],
|
|
284
273
|
});
|
|
285
274
|
};
|
|
286
275
|
const parseElf = (bytes) => {
|
|
@@ -299,7 +288,6 @@ const parseElf = (bytes) => {
|
|
|
299
288
|
format: "elf",
|
|
300
289
|
architecture,
|
|
301
290
|
availableArchitectures: [architecture],
|
|
302
|
-
loaderArgs: ["-l", "ELF", hopperArchitectureFlag(architecture)],
|
|
303
291
|
});
|
|
304
292
|
};
|
|
305
293
|
const parsePe = (bytes) => {
|
|
@@ -320,7 +308,6 @@ const parsePeRecord = (record) => {
|
|
|
320
308
|
format: "pe",
|
|
321
309
|
architecture,
|
|
322
310
|
availableArchitectures: [architecture],
|
|
323
|
-
loaderArgs: ["-l", "WinPE", hopperArchitectureFlag(architecture)],
|
|
324
311
|
});
|
|
325
312
|
};
|
|
326
313
|
const machArchitecture = (cpu) => {
|
|
@@ -362,15 +349,3 @@ const peArchitecture = (machine) => {
|
|
|
362
349
|
}
|
|
363
350
|
return undefined;
|
|
364
351
|
};
|
|
365
|
-
const hopperArchitectureFlag = (architecture) => {
|
|
366
|
-
switch (architecture) {
|
|
367
|
-
case "x86":
|
|
368
|
-
return "--intel-32";
|
|
369
|
-
case "x86_64":
|
|
370
|
-
return "--intel-64";
|
|
371
|
-
case "arm":
|
|
372
|
-
return "--armv7";
|
|
373
|
-
case "arm64":
|
|
374
|
-
return "--aarch64";
|
|
375
|
-
}
|
|
376
|
-
};
|
|
@@ -382,6 +382,7 @@ const browserResourceReconciliationSchema = z.discriminatedUnion("status", [
|
|
|
382
382
|
]);
|
|
383
383
|
const browserScriptSchema = z.object({
|
|
384
384
|
script_key: z.string().regex(/^scr_[a-f0-9]{64}(?:_[0-9]+)?$/u),
|
|
385
|
+
frame_id: z.string().nullable().default(null),
|
|
385
386
|
url: z.string(),
|
|
386
387
|
origin: z.string().nullable(),
|
|
387
388
|
cdp_hash: z.string(),
|
|
@@ -464,6 +465,8 @@ const browserWorkerSchema = z.object({
|
|
|
464
465
|
url: z.string(),
|
|
465
466
|
origin: z.string().nullable(),
|
|
466
467
|
attached: z.boolean(),
|
|
468
|
+
opener_target_id: z.string().nullable().default(null),
|
|
469
|
+
parent_frame_id: z.string().nullable().default(null),
|
|
467
470
|
});
|
|
468
471
|
const browserCspSourceSchema = z.object({
|
|
469
472
|
kind: z.enum([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import { parseEvidenceBundle, serializeEvidenceBundle, } from "./evidenceBundle.js";
|
|
4
4
|
const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
|
|
5
5
|
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
6
6
|
const classificationSchema = z.enum([
|
|
@@ -12,9 +12,9 @@ const classificationSchema = z.enum([
|
|
|
12
12
|
"history_diverged",
|
|
13
13
|
]);
|
|
14
14
|
/** Strict bounded input for canonical Evidence bundle comparison. */
|
|
15
|
-
export const bundleComparisonInputSchema = z.
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
export const bundleComparisonInputSchema = z.strictObject({
|
|
16
|
+
left_bundle_path: z.string().min(1).max(4_096),
|
|
17
|
+
right_bundle_path: z.string().min(1).max(4_096),
|
|
18
18
|
record_pairs: z
|
|
19
19
|
.array(z.object({
|
|
20
20
|
left_evidence_id: evidenceIdSchema,
|
|
@@ -38,6 +38,7 @@ export const inspectElectronPageInputSchema = z
|
|
|
38
38
|
max_dom_nodes: z.number().int().min(1).max(10_000).default(2_000),
|
|
39
39
|
max_scripts: z.number().int().min(1).max(2_000).default(500),
|
|
40
40
|
max_resources: z.number().int().min(1).max(10_000).default(2_000),
|
|
41
|
+
max_workers: z.number().int().min(1).max(5_000).default(500),
|
|
41
42
|
max_script_source_bytes: z
|
|
42
43
|
.number()
|
|
43
44
|
.int()
|
|
@@ -56,6 +57,7 @@ export const inspectElectronPageInputSchema = z
|
|
|
56
57
|
max_dom_nodes: 2_000,
|
|
57
58
|
max_scripts: 500,
|
|
58
59
|
max_resources: 2_000,
|
|
60
|
+
max_workers: 500,
|
|
59
61
|
max_script_source_bytes: 1_024 * 1_024,
|
|
60
62
|
max_total_script_source_bytes: 4 * 1_024 * 1_024,
|
|
61
63
|
}),
|
|
@@ -143,6 +145,7 @@ export const electronPageInspectionSchema = z.object({
|
|
|
143
145
|
total: z.number().int().min(0),
|
|
144
146
|
items: z.array(z.object({
|
|
145
147
|
script_key: z.string().regex(/^electron_script_[a-f0-9]{64}$/u),
|
|
148
|
+
frame_id: z.string().nullable().default(null),
|
|
146
149
|
file_path: z.string(),
|
|
147
150
|
cdp_hash: z.string(),
|
|
148
151
|
length: z.number().int().min(0),
|
|
@@ -158,5 +161,15 @@ export const electronPageInspectionSchema = z.object({
|
|
|
158
161
|
mime_type: z.string(),
|
|
159
162
|
content_size: z.number().min(0).nullable(),
|
|
160
163
|
})),
|
|
164
|
+
workers: z
|
|
165
|
+
.array(z.object({
|
|
166
|
+
target_id: z.string().min(1).max(256),
|
|
167
|
+
type: z.string().min(1).max(100),
|
|
168
|
+
file_path: z.string(),
|
|
169
|
+
attached: z.boolean(),
|
|
170
|
+
opener_target_id: z.string().min(1).max(256).nullable(),
|
|
171
|
+
parent_frame_id: z.string().min(1).max(256).nullable(),
|
|
172
|
+
}))
|
|
173
|
+
.default([]),
|
|
161
174
|
limitations: z.array(z.string()),
|
|
162
175
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { inspectElectronBrowserNode } from "./electronStaticAnalysisBrowser.js";
|
|
2
|
+
import { inspectElectronIpcNode } from "./electronStaticAnalysisIpc.js";
|
|
3
|
+
import { inspectElectronNativeNode } from "./electronStaticAnalysisNative.js";
|
|
4
|
+
/** Collect Electron-specific syntax during the shared inert AST traversal. */
|
|
5
|
+
export const inspectElectronStaticNode = (node, context) => {
|
|
6
|
+
inspectElectronBrowserNode(node, context);
|
|
7
|
+
inspectElectronIpcNode(node, context);
|
|
8
|
+
inspectElectronNativeNode(node, context);
|
|
9
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { boundedExpression, collectContextBridgeMembers, electronStaticValue, objectProperty, } from "./electronStaticAnalysisValues.js";
|
|
3
|
+
import { addLocatedFinding } from "./javascriptStaticAnalysisFindings.js";
|
|
4
|
+
import { argumentNode, calleeName, compareCodePoints, propertyName, range, staticPath, } from "./javascriptStaticAnalysisHelpers.js";
|
|
5
|
+
const MAX_WEB_PREFERENCES = 64;
|
|
6
|
+
/** Inspect BrowserWindow, contextBridge, and utility-process syntax. */
|
|
7
|
+
export const inspectElectronBrowserNode = (node, context) => {
|
|
8
|
+
if (t.isNewExpression(node))
|
|
9
|
+
inspectBrowserWindow(node, context);
|
|
10
|
+
if (!t.isCallExpression(node))
|
|
11
|
+
return;
|
|
12
|
+
inspectContextBridge(node, context);
|
|
13
|
+
inspectUtilityProcess(node, context);
|
|
14
|
+
};
|
|
15
|
+
const inspectBrowserWindow = (node, context) => {
|
|
16
|
+
const name = calleeName(node.callee);
|
|
17
|
+
if (name !== "BrowserWindow" && !name.endsWith(".BrowserWindow"))
|
|
18
|
+
return;
|
|
19
|
+
const options = argumentNode(node.arguments[0]);
|
|
20
|
+
const collected = collectWindowOptions(context.source, options);
|
|
21
|
+
accountForStructure(context, collected.unknown, collected.omitted);
|
|
22
|
+
const finding = {
|
|
23
|
+
options_status: options === undefined
|
|
24
|
+
? "missing"
|
|
25
|
+
: t.isObjectExpression(options)
|
|
26
|
+
? "object-literal"
|
|
27
|
+
: "dynamic",
|
|
28
|
+
web_preferences_status: collected.status,
|
|
29
|
+
web_preferences: collected.preferences,
|
|
30
|
+
omitted_web_preferences: collected.omitted,
|
|
31
|
+
preload_path: collected.preloadPath,
|
|
32
|
+
module_key: null,
|
|
33
|
+
location: range(node),
|
|
34
|
+
};
|
|
35
|
+
addLocatedFinding(context, {
|
|
36
|
+
collection: context.accumulator.browserWindows,
|
|
37
|
+
key: `electron-window\0${name}`,
|
|
38
|
+
node,
|
|
39
|
+
value: finding,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const collectWindowOptions = (source, options) => {
|
|
43
|
+
if (!t.isObjectExpression(options))
|
|
44
|
+
return {
|
|
45
|
+
status: options === undefined ? "missing" : "dynamic",
|
|
46
|
+
preferences: [],
|
|
47
|
+
preloadPath: null,
|
|
48
|
+
unknown: options === undefined ? 0 : 1,
|
|
49
|
+
omitted: 0,
|
|
50
|
+
};
|
|
51
|
+
const property = objectProperty(options, "webPreferences");
|
|
52
|
+
if (property === undefined)
|
|
53
|
+
return {
|
|
54
|
+
status: "missing",
|
|
55
|
+
preferences: [],
|
|
56
|
+
preloadPath: null,
|
|
57
|
+
unknown: 0,
|
|
58
|
+
omitted: 0,
|
|
59
|
+
};
|
|
60
|
+
if (!t.isObjectExpression(property.value))
|
|
61
|
+
return {
|
|
62
|
+
status: "dynamic",
|
|
63
|
+
preferences: [],
|
|
64
|
+
preloadPath: null,
|
|
65
|
+
unknown: 1,
|
|
66
|
+
omitted: 0,
|
|
67
|
+
};
|
|
68
|
+
return collectWebPreferences(source, property.value);
|
|
69
|
+
};
|
|
70
|
+
const collectWebPreferences = (source, object) => {
|
|
71
|
+
const preferences = [];
|
|
72
|
+
let unknown = 0;
|
|
73
|
+
let preloadPath = null;
|
|
74
|
+
for (const property of object.properties) {
|
|
75
|
+
if (t.isSpreadElement(property)) {
|
|
76
|
+
unknown += 1;
|
|
77
|
+
preferences.push({
|
|
78
|
+
name: `[spread@${String(property.start ?? -1)}]`,
|
|
79
|
+
value: electronStaticValue(source, property.argument),
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const name = propertyName(property.key);
|
|
84
|
+
if (name === "" || property.computed) {
|
|
85
|
+
unknown += 1;
|
|
86
|
+
preferences.push({
|
|
87
|
+
name: `[dynamic@${String(property.start ?? -1)}]`,
|
|
88
|
+
value: electronStaticValue(source, property),
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (t.isObjectMethod(property)) {
|
|
93
|
+
unknown += 1;
|
|
94
|
+
preferences.push({
|
|
95
|
+
name,
|
|
96
|
+
value: electronStaticValue(source, property),
|
|
97
|
+
});
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const path = name === "preload" ? staticPath(property.value) : undefined;
|
|
101
|
+
if (path !== undefined)
|
|
102
|
+
preloadPath = path;
|
|
103
|
+
const value = path === undefined
|
|
104
|
+
? electronStaticValue(source, property.value)
|
|
105
|
+
: { status: "literal", value: path, expression: null };
|
|
106
|
+
if (value.status === "dynamic")
|
|
107
|
+
unknown += 1;
|
|
108
|
+
preferences.push({ name, value });
|
|
109
|
+
}
|
|
110
|
+
preferences.sort((left, right) => compareCodePoints(left.name, right.name));
|
|
111
|
+
return {
|
|
112
|
+
status: "object-literal",
|
|
113
|
+
preferences: preferences.slice(0, MAX_WEB_PREFERENCES),
|
|
114
|
+
preloadPath,
|
|
115
|
+
unknown,
|
|
116
|
+
omitted: Math.max(0, preferences.length - MAX_WEB_PREFERENCES),
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
const inspectContextBridge = (node, context) => {
|
|
120
|
+
const name = calleeName(node.callee);
|
|
121
|
+
const main = name === "contextBridge.exposeInMainWorld" ||
|
|
122
|
+
name.endsWith(".contextBridge.exposeInMainWorld");
|
|
123
|
+
const isolated = name === "contextBridge.exposeInIsolatedWorld" ||
|
|
124
|
+
name.endsWith(".contextBridge.exposeInIsolatedWorld");
|
|
125
|
+
if (!main && !isolated)
|
|
126
|
+
return;
|
|
127
|
+
const keyNode = argumentNode(node.arguments[isolated ? 1 : 0]);
|
|
128
|
+
const apiNode = argumentNode(node.arguments[isolated ? 2 : 1]);
|
|
129
|
+
const key = electronStaticValue(context.source, keyNode);
|
|
130
|
+
const api = collectContextBridgeMembers(apiNode);
|
|
131
|
+
const worldId = isolated
|
|
132
|
+
? electronStaticValue(context.source, argumentNode(node.arguments[0]))
|
|
133
|
+
: null;
|
|
134
|
+
const unknown = (key.status === "dynamic" ? 1 : 0) +
|
|
135
|
+
(worldId?.status === "dynamic" ? 1 : 0) +
|
|
136
|
+
api.unknown;
|
|
137
|
+
accountForStructure(context, unknown, api.omitted);
|
|
138
|
+
const finding = {
|
|
139
|
+
world: isolated ? "isolated" : "main",
|
|
140
|
+
world_id: worldId,
|
|
141
|
+
api_key: key.status === "literal" && typeof key.value === "string"
|
|
142
|
+
? key.value
|
|
143
|
+
: null,
|
|
144
|
+
api_key_expression: key.status === "dynamic" ? key.expression : null,
|
|
145
|
+
api_status: api.status,
|
|
146
|
+
members: api.members,
|
|
147
|
+
unknown_members: api.unknown,
|
|
148
|
+
omitted_members: api.omitted,
|
|
149
|
+
module_key: null,
|
|
150
|
+
location: range(node),
|
|
151
|
+
};
|
|
152
|
+
addLocatedFinding(context, {
|
|
153
|
+
collection: context.accumulator.contextBridgeApis,
|
|
154
|
+
key: `context-bridge\0${isolated ? "isolated" : "main"}\0${finding.api_key ?? finding.api_key_expression ?? "[missing]"}`,
|
|
155
|
+
node,
|
|
156
|
+
value: finding,
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
const inspectUtilityProcess = (node, context) => {
|
|
160
|
+
const name = calleeName(node.callee);
|
|
161
|
+
if (name !== "utilityProcess.fork" && !name.endsWith(".utilityProcess.fork"))
|
|
162
|
+
return;
|
|
163
|
+
const moduleNode = argumentNode(node.arguments[0]);
|
|
164
|
+
const modulePath = moduleNode === undefined ? undefined : staticPath(moduleNode);
|
|
165
|
+
const options = argumentNode(node.arguments[2]);
|
|
166
|
+
const serviceName = t.isObjectExpression(options) &&
|
|
167
|
+
objectProperty(options, "serviceName") !== undefined
|
|
168
|
+
? staticPath(objectProperty(options, "serviceName")?.value ?? options)
|
|
169
|
+
: undefined;
|
|
170
|
+
if (modulePath === undefined)
|
|
171
|
+
context.accumulator.unknownFindings += 1;
|
|
172
|
+
const finding = {
|
|
173
|
+
module_path: modulePath ?? null,
|
|
174
|
+
module_expression: modulePath === undefined
|
|
175
|
+
? boundedExpression(context.source, moduleNode)
|
|
176
|
+
: null,
|
|
177
|
+
service_name: serviceName ?? null,
|
|
178
|
+
module_key: null,
|
|
179
|
+
location: range(node),
|
|
180
|
+
};
|
|
181
|
+
addLocatedFinding(context, {
|
|
182
|
+
collection: context.accumulator.utilityProcesses,
|
|
183
|
+
key: `electron-utility\0${modulePath ?? finding.module_expression ?? "[missing]"}`,
|
|
184
|
+
node,
|
|
185
|
+
value: finding,
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
const accountForStructure = (context, unknown, omitted) => {
|
|
189
|
+
context.accumulator.unknownFindings += unknown;
|
|
190
|
+
if (omitted === 0)
|
|
191
|
+
return;
|
|
192
|
+
context.accumulator.structuralTruncation = true;
|
|
193
|
+
context.accumulator.droppedFindings += omitted;
|
|
194
|
+
};
|