mirror of
https://github.com/morluto/rea.git
synced 2026-10-30 03:15:54 +00:00
refactor(javascript): group application workflows and verification (#896)
This commit is contained in:
1 parent
faaee693d5
commit
0804722cdf
129 files changed
+402
-367
No files matched your search
@@ -0,0 +1,276 @@
|
||||
import type { ApplicationNode } from "../../domain/javascriptApplicationGraph.js";
|
||||
import type {
|
||||
ElectronBrowserWindowFinding,
|
||||
ElectronContextBridgeFinding,
|
||||
ElectronUtilityProcessFinding,
|
||||
} from "../../domain/electronStaticAnalysisTypes.js";
|
||||
import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js";
|
||||
import {
|
||||
artifactLocalIdentity,
|
||||
createElectronRoleNode,
|
||||
javascriptAnalysisCoverage,
|
||||
linkElectronRoleToAsset,
|
||||
type JavaScriptArtifactGraphContext,
|
||||
type JavaScriptArtifactGraphCoverage,
|
||||
} from "./JavaScriptArtifactGraphContext.js";
|
||||
import { astObservationEvidence } from "./JavaScriptArtifactGraphEvidence.js";
|
||||
import {
|
||||
addElectronAstEdge,
|
||||
addElectronInferenceEdge,
|
||||
electronFindingSourceNode,
|
||||
electronRangeKey,
|
||||
} from "./ElectronBoundaryGraphContext.js";
|
||||
import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
|
||||
|
||||
interface FindingInput<Value> {
|
||||
readonly context: JavaScriptArtifactGraphContext;
|
||||
readonly file: JavaScriptArtifactFile;
|
||||
readonly source: ApplicationNode;
|
||||
readonly value: Value;
|
||||
readonly coverage: JavaScriptArtifactGraphCoverage;
|
||||
}
|
||||
|
||||
/** Project BrowserWindow, contextBridge, preload, and utility-process facts. */
|
||||
export const addElectronWindowBoundaries = (
|
||||
context: JavaScriptArtifactGraphContext,
|
||||
): void => {
|
||||
for (const analyzed of context.analysis.files) {
|
||||
const { file, javascript } = analyzed;
|
||||
if (javascript === null) continue;
|
||||
const coverage = javascriptAnalysisCoverage(javascript);
|
||||
for (const value of javascript.electron.browser_windows) {
|
||||
const source = electronFindingSourceNode(context, file, value.module_key);
|
||||
if (source !== undefined)
|
||||
addBrowserWindow({ context, file, source, value, coverage });
|
||||
}
|
||||
for (const value of javascript.electron.context_bridge_apis) {
|
||||
const source = electronFindingSourceNode(context, file, value.module_key);
|
||||
if (source !== undefined)
|
||||
addContextBridge({ context, file, source, value, coverage });
|
||||
}
|
||||
for (const value of javascript.electron.utility_processes) {
|
||||
const source = electronFindingSourceNode(context, file, value.module_key);
|
||||
if (source !== undefined)
|
||||
addUtilityProcess({ context, file, source, value, coverage });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addBrowserWindow = (
|
||||
input: FindingInput<ElectronBrowserWindowFinding>,
|
||||
): void => {
|
||||
const { context, file, value, coverage } = input;
|
||||
const window = context.accumulator.addNode({
|
||||
kind: "browser-window",
|
||||
identity: artifactLocalIdentity(
|
||||
file.sha256,
|
||||
"electron-browser-window",
|
||||
electronRangeKey(value.location),
|
||||
),
|
||||
observations: [
|
||||
{
|
||||
label: `BrowserWindow@${electronRangeKey(value.location)}`,
|
||||
properties: {
|
||||
source_path: file.path,
|
||||
options_status: value.options_status,
|
||||
web_preferences_status: value.web_preferences_status,
|
||||
web_preferences: value.web_preferences,
|
||||
preload_path: value.preload_path,
|
||||
preload_resolution_context: value.preload_resolution_context,
|
||||
absence_means_default: false,
|
||||
},
|
||||
evidence: astObservationEvidence({
|
||||
sha256: file.sha256,
|
||||
path: file.path,
|
||||
range: value.location,
|
||||
operation: "observe-browser-window-construction",
|
||||
coverage,
|
||||
limitations: [
|
||||
"Only explicitly present webPreferences are reported; omitted properties are not interpreted as Electron defaults.",
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
addElectronAstEdge(context, {
|
||||
source: input.source,
|
||||
target: window,
|
||||
file,
|
||||
range: value.location,
|
||||
coverage,
|
||||
relation: "contains",
|
||||
operation: "locate-browser-window-construction",
|
||||
properties: { source_path: file.path },
|
||||
});
|
||||
addWindowPreload(input, window);
|
||||
};
|
||||
|
||||
const addWindowPreload = (
|
||||
input: FindingInput<ElectronBrowserWindowFinding>,
|
||||
window: ApplicationNode,
|
||||
): void => {
|
||||
if (input.value.preload_path === null) return;
|
||||
const {
|
||||
preload_path: declared,
|
||||
preload_resolution_context: resolutionContext,
|
||||
} = input.value;
|
||||
const resolution = resolveArtifactPathByContext({
|
||||
declaredPath: declared,
|
||||
sourcePath: input.file.path,
|
||||
context: resolutionContext,
|
||||
files: input.context.filesByPath,
|
||||
});
|
||||
const role = createElectronRoleNode(input.context, {
|
||||
kind: "electron-preload",
|
||||
anchor: input.file,
|
||||
resolution,
|
||||
mechanism: "BrowserWindow:webPreferences.preload",
|
||||
range: input.value.location,
|
||||
coverage: input.coverage,
|
||||
});
|
||||
addElectronInferenceEdge(input.context, {
|
||||
source: window,
|
||||
target: role,
|
||||
file: input.file,
|
||||
range: input.value.location,
|
||||
coverage: input.coverage,
|
||||
relation: "loads",
|
||||
operation: "associate-browser-window-preload",
|
||||
properties: {
|
||||
declared_path: declared,
|
||||
resolution_context: resolution.resolution_context,
|
||||
resolved_path: resolution.resolved_path,
|
||||
resolution_status: resolution.resolution_status,
|
||||
limitations: resolution.limitations,
|
||||
},
|
||||
});
|
||||
linkElectronRoleToAsset(input.context, {
|
||||
role,
|
||||
anchor: input.file,
|
||||
resolution,
|
||||
range: input.value.location,
|
||||
coverage: input.coverage,
|
||||
});
|
||||
};
|
||||
|
||||
const addContextBridge = (
|
||||
input: FindingInput<ElectronContextBridgeFinding>,
|
||||
): void => {
|
||||
const { context, file, value, coverage } = input;
|
||||
const bridge = context.accumulator.addNode({
|
||||
kind: "context-bridge-api",
|
||||
identity: artifactLocalIdentity(
|
||||
file.sha256,
|
||||
"electron-context-bridge",
|
||||
`${value.world}:${value.api_key ?? value.api_key_expression ?? "[missing]"}:${electronRangeKey(value.location)}`,
|
||||
),
|
||||
observations: [
|
||||
{
|
||||
label: value.api_key,
|
||||
properties: {
|
||||
world: value.world,
|
||||
world_id: value.world_id,
|
||||
api_key: value.api_key,
|
||||
api_key_expression: value.api_key_expression,
|
||||
api_status: value.api_status,
|
||||
members: value.members,
|
||||
unknown_members: value.unknown_members,
|
||||
},
|
||||
evidence: astObservationEvidence({
|
||||
sha256: file.sha256,
|
||||
path: file.path,
|
||||
range: value.location,
|
||||
operation: "observe-context-bridge-api",
|
||||
coverage,
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
addElectronInferenceEdge(context, {
|
||||
source: input.source,
|
||||
target: bridge,
|
||||
file,
|
||||
range: value.location,
|
||||
coverage,
|
||||
relation: "exposes",
|
||||
operation: "map-context-bridge-exposure",
|
||||
properties: {
|
||||
world: value.world,
|
||||
api_key: value.api_key,
|
||||
resolution: value.api_key === null ? "dynamic" : "literal",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const addUtilityProcess = (
|
||||
input: FindingInput<ElectronUtilityProcessFinding>,
|
||||
): void => {
|
||||
const { context, file, value, coverage } = input;
|
||||
const utility = context.accumulator.addNode({
|
||||
kind: "electron-utility",
|
||||
identity: artifactLocalIdentity(
|
||||
file.sha256,
|
||||
"electron-utility-process",
|
||||
`${value.module_path ?? value.module_expression}:${electronRangeKey(value.location)}`,
|
||||
),
|
||||
observations: [
|
||||
{
|
||||
label: value.service_name ?? value.module_path,
|
||||
properties: {
|
||||
module_path: value.module_path,
|
||||
module_resolution_context: value.module_resolution_context,
|
||||
module_expression: value.module_expression,
|
||||
service_name: value.service_name,
|
||||
},
|
||||
evidence: astObservationEvidence({
|
||||
sha256: file.sha256,
|
||||
path: file.path,
|
||||
range: value.location,
|
||||
operation: "observe-utility-process-fork",
|
||||
coverage,
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
addElectronInferenceEdge(context, {
|
||||
source: input.source,
|
||||
target: utility,
|
||||
file,
|
||||
range: value.location,
|
||||
coverage,
|
||||
relation: "loads",
|
||||
operation: "map-utility-process-launch",
|
||||
properties: {
|
||||
declared_path: value.module_path,
|
||||
expression: value.module_expression,
|
||||
},
|
||||
});
|
||||
if (value.module_path === null) return;
|
||||
const resolution = resolveArtifactPathByContext({
|
||||
declaredPath: value.module_path,
|
||||
sourcePath: file.path,
|
||||
context: value.module_resolution_context,
|
||||
files: context.filesByPath,
|
||||
});
|
||||
const resolved = resolution.resolved_path;
|
||||
const target =
|
||||
resolved === null
|
||||
? undefined
|
||||
: (context.assetNodes.get(resolved) ?? context.fileNodes.get(resolved));
|
||||
if (target === undefined) return;
|
||||
addElectronInferenceEdge(context, {
|
||||
source: utility,
|
||||
target,
|
||||
file,
|
||||
range: value.location,
|
||||
coverage,
|
||||
relation: "maps_to",
|
||||
operation: "resolve-utility-process-entrypoint",
|
||||
properties: {
|
||||
declared_path: value.module_path,
|
||||
resolution_context: resolution.resolution_context,
|
||||
resolved_path: resolved,
|
||||
resolution_status: resolution.resolution_status,
|
||||
},
|
||||
});
|
||||
};
|
||||
Reference in new issue
Block a user