mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(javascript): qualify renderer endpoint observations
This commit is contained in:
1 parent
9cad06db10
commit
0e1500dc8d
4 files changed
+99
-8
No files matched your search
@@ -518,7 +518,7 @@ const inspectEndpointCall = (
|
||||
context: FindingContext,
|
||||
bindings: JavaScriptSemanticAnalysisState,
|
||||
): void => {
|
||||
if (isKnownNetworkRequest(node, bindings)) {
|
||||
if (isKnownNetworkRequest(node, bindings, context)) {
|
||||
const target = argumentNode(node.arguments[0]);
|
||||
if (target === undefined) return;
|
||||
const literal =
|
||||
@@ -609,8 +609,17 @@ const isNetworkRequestSpelling = (name: string): boolean =>
|
||||
const isKnownNetworkRequest = (
|
||||
node: JavaScriptCallLike,
|
||||
bindings: JavaScriptSemanticAnalysisState,
|
||||
context: FindingContext,
|
||||
): boolean => {
|
||||
if (isFetchCall(node, bindings)) return true;
|
||||
const callee = unwrapJavaScriptExpression(node.callee).node;
|
||||
if (
|
||||
(t.isCallExpression(node) || t.isOptionalCallExpression(node)) &&
|
||||
(t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) &&
|
||||
semanticStaticPropertyKey(callee.property, callee.computed) === "loadURL" &&
|
||||
context.rendererLoad?.(node.callee, node) === true
|
||||
)
|
||||
return true;
|
||||
if (
|
||||
t.isNewExpression(node) &&
|
||||
(isUnmodifiedGlobalReference(node.callee, bindings, "WebSocket") ||
|
||||
@@ -633,7 +642,7 @@ export const finishPartialNetworkEndpoints = (
|
||||
index: JavaScriptBindingIndex,
|
||||
): void => {
|
||||
for (const { node, name } of context.accumulator.pendingNetworkCalls) {
|
||||
if (!isKnownNetworkRequest(node, bindings)) {
|
||||
if (!isKnownNetworkRequest(node, bindings, context)) {
|
||||
if (!isIntrinsicMapLookup(node, bindings, index))
|
||||
inspectLiteralEndpointCall(node, name, context);
|
||||
continue;
|
||||
|
||||
@@ -657,7 +657,7 @@ const isKeyedCollectionReceiver = (callee: t.Node): boolean => {
|
||||
);
|
||||
};
|
||||
|
||||
/** A literal endpoint shape or an ambiguous argument of an HTTP-method-named call. */
|
||||
/** A literal endpoint shape or an argument whose callee spelling is unproven. */
|
||||
export interface JavaScriptEndpointArgument {
|
||||
readonly kind: "endpoint" | "candidate";
|
||||
readonly value: string;
|
||||
@@ -728,8 +728,10 @@ export const endpointArgument = (
|
||||
// Beacon URLs are endpoint observations, retaining the upstream API support.
|
||||
if (name === "sendBeacon" || name.endsWith(".sendBeacon"))
|
||||
return literal(stringValue(args[0]));
|
||||
if (name === "loadURL" || name.endsWith(".loadURL"))
|
||||
return literal(stringValue(args[0]));
|
||||
if (name === "loadURL" || name.endsWith(".loadURL")) {
|
||||
const value = stringValue(args[0]);
|
||||
return value === undefined ? undefined : { kind: "candidate", value };
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -1087,12 +1087,17 @@ describe("web bundle configuration and navigation parity", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("recognizes loadURL endpoints like static analysis", () => {
|
||||
it("keeps loadURL spelling without Electron receiver proof as a candidate", () => {
|
||||
const result = analyzeWebBundleFixture(
|
||||
inspection('window.win.loadURL("https://embedded.test/app");'),
|
||||
);
|
||||
expect(result.observations.endpoints).toContainEqual(
|
||||
expect.objectContaining({ value: "https://embedded.test/app" }),
|
||||
expect(result.observations.endpoints).toEqual([]);
|
||||
expect(result.inferences).toContainEqual(
|
||||
expect.objectContaining({
|
||||
kind: "endpoint_candidate",
|
||||
value: "https://embedded.test/app",
|
||||
confidence: "low",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { expect } from "vitest";
|
||||
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { writeFixtureFiles } from "../../support/javascriptApplicationFixture.js";
|
||||
import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js";
|
||||
|
||||
cliTest(
|
||||
"preserves renderer endpoint certainty through CLI graph delivery",
|
||||
async ({ cli }) => {
|
||||
const root = await createTestTempDirectory("rea-renderer-endpoints-");
|
||||
await writeFixtureFiles(root, {
|
||||
"main.cjs": `
|
||||
const { BrowserWindow } = require("electron");
|
||||
const replaced = new BrowserWindow();
|
||||
replaced.loadURL = () => {};
|
||||
replaced.loadURL("https://replaced.test");
|
||||
const local = { loadURL(value) { return value; } };
|
||||
local.loadURL("https://local.test");
|
||||
const intact = new BrowserWindow();
|
||||
intact.loadURL("https://intact.test");
|
||||
new BrowserWindow().loadURL(runtimeUrl);
|
||||
new BrowserWindow().loadFile("page.html");
|
||||
`,
|
||||
"page.html": "<p>Local renderer</p>",
|
||||
});
|
||||
const result = await analyzeJavaScriptDirectory("CLI", cli, root);
|
||||
const endpoints = result.graph.nodes.filter(
|
||||
({ kind }) => kind === "endpoint",
|
||||
);
|
||||
expect(endpoints).toHaveLength(4);
|
||||
for (const value of ["https://replaced.test", "https://local.test"]) {
|
||||
const endpoint = endpoints.find(({ observations }) =>
|
||||
observations.some(({ properties }) => properties.value === value),
|
||||
);
|
||||
expect(endpoint?.observations[0]).toMatchObject({
|
||||
properties: { value, interpretation: "candidate" },
|
||||
evidence: { state: "inferred", confidence: "low" },
|
||||
});
|
||||
expect(endpoint?.observations[0]?.evidence.limitations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining("receiver does not establish a network call"),
|
||||
]),
|
||||
);
|
||||
expect(
|
||||
result.graph.edges.find(
|
||||
({ target_node_id }) => target_node_id === endpoint?.node_id,
|
||||
),
|
||||
).toMatchObject({
|
||||
relation: "calls",
|
||||
properties: { interpretation: "candidate" },
|
||||
evidence: { state: "inferred", confidence: "low" },
|
||||
});
|
||||
}
|
||||
const intact = endpoints.find(({ observations }) =>
|
||||
observations.some(
|
||||
({ properties }) => properties.value === "https://intact.test",
|
||||
),
|
||||
);
|
||||
expect(intact?.observations[0]).toMatchObject({
|
||||
properties: { value: "https://intact.test" },
|
||||
evidence: { state: "observed", confidence: "exact" },
|
||||
});
|
||||
expect(intact?.observations[0]?.properties).not.toHaveProperty(
|
||||
"interpretation",
|
||||
);
|
||||
const dynamic = endpoints.find(({ observations }) =>
|
||||
observations.some(({ properties }) => properties.value === "runtimeUrl"),
|
||||
);
|
||||
expect(dynamic?.observations[0]).toMatchObject({
|
||||
properties: { value: "runtimeUrl", resolution: "partial" },
|
||||
evidence: { state: "observed" },
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user