Files
reverse-engineering-anything/src/domain/javascript/javascriptNetworkRequests.ts
T
ShuaixinHuang a2134b3a74 fix(javascript): resolve module-level Electron options in functions (#8960)
* fix(javascript): resolve module-level Electron options in functions

* fix(javascript): limit module option ancestry walk

* fix(javascript): preserve uncertain captured argument ordering

* fix(javascript): retain hoisted activation uncertainty

* fix(javascript): retain function ownership through loops

* fix(javascript): account for classic global activations

* fix(javascript): bind first activations to lexical identities

* fix(javascript): retain uncertain global activation timing

* fix(javascript): isolate receiver facts and share computed reads

* fix(javascript): retain projected and opaque global activations

* fix(javascript): refresh activation bounds as opaque effects grow

* fix(ci): format website watch directories without changing configuration

* fix(javascript): share source reads across opaque-effect updates

* fix(javascript): retain constructed-code global activation bounds

* fix(javascript): share runtime activation and receiver facts

* fix(javascript): reuse pattern reads and function positions

* fix(javascript): isolate copied paths and preserve runtime reads

* style(javascript): format migrated reference imports

* fix(javascript): normalize constructor activation and receiver effects

* fix(javascript): retain aliased receiver timing and lexical global this

* fix(javascript): preserve effect-free receiver identity across activations

* fix(javascript): retain outer activation and prototype effects

* fix(javascript): index implicit reads without deep path rescans

* fix(javascript): retain aliased prototype and parameter read uncertainty

* fix(javascript): index reflected reads through intrinsic invocation facts

* fix(javascript): retain unresolved global activation reads

* chore: refresh PR branch after review fix publication

* fix(javascript): retain projected receiver reads and cache method facts

* fix(javascript): retain inherited and implicit activation reads

* fix(javascript): retain caller-owned this uncertainty

* fix(javascript): retain reflected reads with unresolved spread arguments

* fix(javascript): preserve enclosing activation for decorators

* fix(javascript): track intrinsic literal prototype effects

* fix(javascript): retain catch activation from implicit reads

* perf(javascript): reuse effective keys for projected reads

* fix(javascript): retain possible caller-owned ambient effects
2026-11-05 16:52:19 +08:00

115 lines
4.0 KiB
TypeScript

import { completeSemanticSteps } from "./javascriptSemanticTraversal.js";
import {
semanticStaticPropertyName,
unwrapJavaScriptExpression,
} from "./javascriptAstValues.js";
import { semanticBindingAtRead } from "./javascriptSemanticMutationInitializers.js";
import { semanticOpaqueGlobalMayHaveChanged } from "./javascriptSemanticOpaqueEffects.js";
import {
resolveSemanticBindingState,
semanticStableInitializer,
type JavaScriptBindingIndex,
type JavaScriptSemanticAnalysisState,
} from "./javascriptSemanticState.js";
import * as t from "@babel/types";
import { isUnmodifiedGlobalReference } from "./javascriptSemanticAmbientWrites.js";
export const isFetchCall = (
node: t.Node,
bindings: JavaScriptSemanticAnalysisState,
): boolean =>
(t.isCallExpression(node) || t.isOptionalCallExpression(node)) &&
isUnmodifiedGlobalReference(node.callee, bindings, "fetch");
/** Browser request identity requires an intact intrinsic, not a method spelling. */
export const isBrowserNetworkRequest = (
node: t.CallExpression | t.OptionalCallExpression | t.NewExpression,
bindings: JavaScriptSemanticAnalysisState,
): boolean =>
isFetchCall(node, bindings) ||
(t.isNewExpression(node) &&
(isUnmodifiedGlobalReference(node.callee, bindings, "WebSocket") ||
isUnmodifiedGlobalReference(node.callee, bindings, "EventSource"))) ||
((t.isCallExpression(node) || t.isOptionalCallExpression(node)) &&
isUnmodifiedGlobalReference(node.callee, bindings, [
"navigator",
"sendBeacon",
]));
const isXhrOpenRequest = (
node: t.Node,
bindings: JavaScriptSemanticAnalysisState,
index?: Pick<JavaScriptBindingIndex, "referenceMayHaveChangedAt">,
): boolean | undefined => {
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
return false;
const callee = unwrapJavaScriptExpression(node.callee).node;
if (
(!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee)) ||
semanticStaticPropertyName(callee.property, callee.computed) !== "open"
)
return false;
if (
["XMLHttpRequest", "Object"].some((owner) =>
["open", "__proto__"].some(
(member) =>
bindings.ambientWrites.covers([
"global",
owner,
"prototype",
member,
]) ||
bindings.ambientEscapes.covers([
"global",
owner,
"prototype",
member,
]),
),
)
)
return false;
let receiver = unwrapJavaScriptExpression(callee.object).node;
const seen = new Set<string>();
while (t.isIdentifier(receiver)) {
if (
index !== undefined &&
(index.referenceMayHaveChangedAt(node, receiver, ["open"]) ||
index.referenceMayHaveChangedAt(node, receiver, ["__proto__"]))
)
return false;
const binding = resolveSemanticBindingState(
bindings,
receiver,
receiver.name,
);
if (binding === undefined || seen.has(binding.bindingId)) return false;
seen.add(binding.bindingId);
const initializer = semanticStableInitializer(binding, bindings);
if (
initializer === undefined ||
initializer.projection.length !== 0 ||
!semanticBindingAtRead(binding, receiver, bindings).hasDefiniteInitializer
)
return false;
receiver = unwrapJavaScriptExpression(initializer.node).node;
}
if (
!t.isNewExpression(receiver) ||
!isUnmodifiedGlobalReference(receiver.callee, bindings, "XMLHttpRequest") ||
semanticOpaqueGlobalMayHaveChanged(callee, bindings)
)
return false;
return index === undefined && seen.size > 0 ? undefined : true;
};
/** Resolve once through the shared mutation index when lexical proof needs it. */
export const createXhrOpenRequestQuery =
(index: JavaScriptBindingIndex): ((node: t.Node) => boolean) =>
(node) => {
const proof = isXhrOpenRequest(node, index.bindings);
if (proof !== undefined) return proof;
completeSemanticSteps(index.indexMemberMutationFactsSteps());
return isXhrOpenRequest(node, index.bindings, index) === true;
};