mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 01:41:21 +00:00
fix(javascript): preserve loader authority for exact suffixes (#9477)
* fix(javascript): require exact loader suffix case * fix(javascript): share exact loader mode across import resolution * test(process): align symlink fixture with filesystem checkpoints * test(javascript): normalize the module resolution oracle path separators * test(frida): assert the CLI spawn Evidence contract * test(mcp): use an unsupported analysis-view parent * test(browser): separate source-location line bounds from parity * fix(javascript): preserve uncertain loader provenance * test(javascript): cover uncertain require resolution * fix(javascript): retain conditional Electron evidence * fix(javascript): preserve timed semantic provenance * fix(javascript): retain conditional builtin effects * test(javascript): distinguish bundled host authority * fix(javascript): preserve nested loader invocation identity * test(javascript): qualify Electron fixture authority * fix(javascript): preserve loader invocation authority * fix(javascript): respect source order for resolver escapes * fix(javascript): check nested loader callable mutations * fix(javascript): retain conditional constructor requests * fix(reference): retain constructor loader relationships * fix(reference): follow nested loader callable paths * refactor(javascript): share callable mutation interpretation * fix(javascript): require exact package loader suffixes * refactor(javascript): unify conditional relation evidence * refactor(javascript): unify conditional node evidence * fix(javascript): retain unknown authority for mixed case sources * fix(javascript): preserve loader uncertainty for dotfiles * fix(reference): classify javascript suffix dotfiles * test(reference): cover suffix dotfiles through cli * fix(javascript): retain equivalent conditional built-in origins * fix(javascript): preserve wrapped resolver operation * fix(javascript): retain nested conditional built-in receivers * test(javascript): declare CommonJS in native addon fixtures * fix(javascript): retain wrapped resolver candidates * style: format merged native command schema * fix(javascript): preserve uncertain loader authority * test(javascript): require candidate preload evidence * test(javascript): cover reference loader member writes --------- Co-authored-by: ShuaixinHuang <1836656514@qq.com>
This commit is contained in:
1 parent
78a84d75c0
commit
b46f4fcebd
112 files changed
+5156
-797
No files matched your search
@@ -86,10 +86,26 @@ export const inspectCall = (
|
||||
bindings,
|
||||
accumulator.factoryRequireBindingIds,
|
||||
) !== "local";
|
||||
const unwrappedCallee = unwrapJavaScriptExpression(node.callee).node;
|
||||
const invocationMember =
|
||||
(t.isMemberExpression(unwrappedCallee) ||
|
||||
t.isOptionalMemberExpression(unwrappedCallee)) &&
|
||||
["call", "apply"].includes(
|
||||
semanticStaticPropertyKey(
|
||||
unwrappedCallee.property,
|
||||
unwrappedCallee.computed,
|
||||
) ?? "",
|
||||
);
|
||||
const deferredModuleRequire =
|
||||
semanticModuleRequireReceiver(node) !== undefined ||
|
||||
semanticModuleRequireCallAt(node, bindings, false) ||
|
||||
deferredRequireResolve;
|
||||
deferredRequireResolve ||
|
||||
(invocationMember &&
|
||||
semanticRequireCallAt(
|
||||
node,
|
||||
bindings,
|
||||
accumulator.factoryRequireBindingIds,
|
||||
) !== undefined);
|
||||
if (deferredModuleRequire) accumulator.pendingModuleRequireCalls.push(node);
|
||||
const requireCall = deferredModuleRequire
|
||||
? null
|
||||
@@ -106,6 +122,9 @@ export const inspectCall = (
|
||||
...(requireCall.loader === undefined
|
||||
? {}
|
||||
: { loader: requireCall.loader }),
|
||||
...(requireCall.loaderAuthority === undefined
|
||||
? {}
|
||||
: { loaderAuthority: requireCall.loaderAuthority }),
|
||||
});
|
||||
const chunkLoad = chunkLoadSpecifier(node, bindings, context);
|
||||
if (chunkLoad !== null)
|
||||
@@ -160,9 +179,16 @@ export const inspectCall = (
|
||||
});
|
||||
inspectEndpointCall(node, name, first, context, bindings);
|
||||
inspectStorageCall(node, name, first, context, bindings);
|
||||
const renderer =
|
||||
name === "loadFile" ||
|
||||
name.endsWith(".loadFile") ||
|
||||
name === "loadURL" ||
|
||||
name.endsWith(".loadURL")
|
||||
? context.rendererLoad?.(node.callee, node)
|
||||
: undefined;
|
||||
if (
|
||||
(name === "loadFile" || name.endsWith(".loadFile")) &&
|
||||
context.rendererLoad?.(node.callee, node) === true
|
||||
renderer !== undefined
|
||||
) {
|
||||
const argument = argumentNode(node.arguments[0]);
|
||||
const filePath =
|
||||
@@ -185,6 +211,7 @@ export const inspectCall = (
|
||||
: {}),
|
||||
resolution_context: "filesystem-expression",
|
||||
mechanism: `call:${name}`,
|
||||
...renderer,
|
||||
module_key: null,
|
||||
location: range(node),
|
||||
},
|
||||
@@ -192,7 +219,7 @@ export const inspectCall = (
|
||||
}
|
||||
if (
|
||||
(name === "loadURL" || name.endsWith(".loadURL")) &&
|
||||
context.rendererLoad?.(node.callee, node) === true
|
||||
renderer !== undefined
|
||||
) {
|
||||
if (first === undefined) accumulator.unknownFindings += 1;
|
||||
else
|
||||
@@ -205,6 +232,7 @@ export const inspectCall = (
|
||||
path: first,
|
||||
resolution_context: "module-specifier",
|
||||
mechanism: `call:${name}`,
|
||||
...renderer,
|
||||
module_key: null,
|
||||
location: range(node),
|
||||
},
|
||||
@@ -279,28 +307,33 @@ const requireCallSpecifier = (
|
||||
node: JavaScriptCallLike,
|
||||
bindings: JavaScriptSemanticAnalysisState,
|
||||
context: FindingContext,
|
||||
resolveMemberChanged = false,
|
||||
index?: JavaScriptBindingIndex,
|
||||
): {
|
||||
readonly specifier: string | undefined;
|
||||
readonly operation: "load" | "resolve";
|
||||
readonly resolutionBasePath?: string;
|
||||
readonly loader?: "commonjs-loader" | "factory-loader" | "create-require";
|
||||
readonly loaderAuthority?: "candidate";
|
||||
} | null => {
|
||||
const call = semanticRequireCallAt(
|
||||
node,
|
||||
bindings,
|
||||
context.accumulator.factoryRequireBindingIds,
|
||||
index,
|
||||
);
|
||||
if (call === undefined || call.loader === "local") return null;
|
||||
if (
|
||||
resolveMemberChanged ||
|
||||
call.loader === "unknown" ||
|
||||
!call.sourceRelative
|
||||
) {
|
||||
if (call.loader === "unknown" || !call.sourceRelative) {
|
||||
context.accumulator.unknownFindings += 1;
|
||||
const candidate =
|
||||
!call.memberUnproven && call.loaderAuthority === "candidate"
|
||||
? (argumentValue(call.arguments?.[0]) ?? undefined)
|
||||
: undefined;
|
||||
return {
|
||||
specifier: undefined,
|
||||
operation: resolveMemberChanged ? "load" : call.operation,
|
||||
specifier: candidate,
|
||||
operation: call.operation,
|
||||
...(candidate === undefined
|
||||
? {}
|
||||
: { loaderAuthority: "candidate" as const }),
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -325,23 +358,7 @@ export const finishModuleRequireCalls = (
|
||||
index: JavaScriptBindingIndex,
|
||||
): void => {
|
||||
for (const node of context.accumulator.pendingModuleRequireCalls) {
|
||||
const resolveMemberChanged =
|
||||
isRequireResolveCall(node) &&
|
||||
(t.isMemberExpression(node.callee) ||
|
||||
t.isOptionalMemberExpression(node.callee)) &&
|
||||
index
|
||||
.effectsForReferenceAt(node, node.callee.object, ["resolve"])
|
||||
.effects.some(
|
||||
({ kind, timing }) =>
|
||||
timing !== "after" &&
|
||||
(kind === "member-write" || kind === "escape"),
|
||||
);
|
||||
const call = requireCallSpecifier(
|
||||
node,
|
||||
index.bindings,
|
||||
context,
|
||||
resolveMemberChanged,
|
||||
);
|
||||
const call = requireCallSpecifier(node, index.bindings, context, index);
|
||||
if (call !== null)
|
||||
addReference(context, {
|
||||
node,
|
||||
@@ -355,6 +372,9 @@ export const finishModuleRequireCalls = (
|
||||
// still names a bundle module key, not a package. Dropping the loader here
|
||||
// would read that key as an external specifier.
|
||||
...(call.loader === undefined ? {} : { loader: call.loader }),
|
||||
...(call.loaderAuthority === undefined
|
||||
? {}
|
||||
: { loaderAuthority: call.loaderAuthority }),
|
||||
});
|
||||
}
|
||||
context.accumulator.pendingModuleRequireCalls.length = 0;
|
||||
@@ -363,14 +383,14 @@ export const finishModuleRequireCalls = (
|
||||
);
|
||||
};
|
||||
|
||||
const isRequireResolveCall = (
|
||||
node: JavaScriptCallLike,
|
||||
): node is t.CallExpression | t.OptionalCallExpression =>
|
||||
(t.isCallExpression(node) || t.isOptionalCallExpression(node)) &&
|
||||
(t.isMemberExpression(node.callee) ||
|
||||
t.isOptionalMemberExpression(node.callee)) &&
|
||||
semanticStaticPropertyKey(node.callee.property, node.callee.computed) ===
|
||||
"resolve";
|
||||
const isRequireResolveCall = (node: JavaScriptCallLike): boolean => {
|
||||
if (t.isNewExpression(node)) return false;
|
||||
const callee = unwrapJavaScriptExpression(node.callee).node;
|
||||
return (
|
||||
(t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) &&
|
||||
semanticStaticPropertyKey(callee.property, callee.computed) === "resolve"
|
||||
);
|
||||
};
|
||||
|
||||
const chunkLoadSpecifier = (
|
||||
node: JavaScriptCallLike,
|
||||
@@ -551,7 +571,8 @@ const inspectEndpointCall = (
|
||||
context: FindingContext,
|
||||
bindings: JavaScriptSemanticAnalysisState,
|
||||
): void => {
|
||||
if (isKnownNetworkRequest(node, bindings, context)) {
|
||||
const networkAuthority = networkRequestAuthority(node, bindings, context);
|
||||
if (networkAuthority !== undefined) {
|
||||
const target = argumentNode(node.arguments[0]);
|
||||
if (target === undefined) return;
|
||||
const literal =
|
||||
@@ -565,6 +586,9 @@ const inspectEndpointCall = (
|
||||
kind: "network",
|
||||
value: literal,
|
||||
mechanism: `call:${name}`,
|
||||
...(networkAuthority === "candidate"
|
||||
? { interpretation: "candidate" }
|
||||
: {}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -578,7 +602,7 @@ const inspectEndpointCall = (
|
||||
context.accumulator.pendingNetworkCalls.push({ node, name });
|
||||
return;
|
||||
}
|
||||
addPartialNetworkEndpoint(context, node, name, target);
|
||||
addPartialNetworkEndpoint(context, node, name, target, networkAuthority);
|
||||
return;
|
||||
}
|
||||
// A local or shadowed function with a familiar network name is not a known
|
||||
@@ -642,21 +666,26 @@ const isNetworkRequestSpelling = (name: string): boolean =>
|
||||
name === "navigator.sendBeacon";
|
||||
|
||||
/** Confirm that the call resolves to a built-in network API at this source site. */
|
||||
const isKnownNetworkRequest = (
|
||||
const networkRequestAuthority = (
|
||||
node: JavaScriptCallLike,
|
||||
bindings: JavaScriptSemanticAnalysisState,
|
||||
context: FindingContext,
|
||||
): boolean => {
|
||||
if (isBrowserNetworkRequest(node, bindings)) return true;
|
||||
): "proven" | "candidate" | undefined => {
|
||||
if (isBrowserNetworkRequest(node, bindings)) return "proven";
|
||||
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;
|
||||
return false;
|
||||
semanticStaticPropertyKey(callee.property, callee.computed) === "loadURL"
|
||||
) {
|
||||
const renderer = context.rendererLoad?.(node.callee, node);
|
||||
return renderer === undefined
|
||||
? undefined
|
||||
: renderer.loader_authority === "candidate"
|
||||
? "candidate"
|
||||
: "proven";
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/** Finish queued targets after shared member-mutation facts are indexed. */
|
||||
@@ -666,7 +695,8 @@ export const finishPartialNetworkEndpoints = (
|
||||
index: JavaScriptBindingIndex,
|
||||
): void => {
|
||||
for (const { node, name } of context.accumulator.pendingNetworkCalls) {
|
||||
if (!isKnownNetworkRequest(node, bindings, context)) {
|
||||
const authority = networkRequestAuthority(node, bindings, context);
|
||||
if (authority === undefined) {
|
||||
if (!isIntrinsicMapLookup(node, bindings, index))
|
||||
inspectLiteralEndpointCall(node, name, context);
|
||||
continue;
|
||||
@@ -680,8 +710,9 @@ export const finishPartialNetworkEndpoints = (
|
||||
kind: "network",
|
||||
value: resolved,
|
||||
mechanism: `call:${name}`,
|
||||
...(authority === "candidate" ? { interpretation: "candidate" } : {}),
|
||||
});
|
||||
else addPartialNetworkEndpoint(context, node, name, target);
|
||||
else addPartialNetworkEndpoint(context, node, name, target, authority);
|
||||
}
|
||||
context.accumulator.pendingNetworkCalls.length = 0;
|
||||
context.accumulator.endpoints.sort(
|
||||
@@ -694,6 +725,7 @@ const addPartialNetworkEndpoint = (
|
||||
node: JavaScriptCallLike,
|
||||
name: string,
|
||||
target: t.Node,
|
||||
authority: "proven" | "candidate",
|
||||
): void =>
|
||||
addEndpoint(context, {
|
||||
node,
|
||||
@@ -701,6 +733,7 @@ const addPartialNetworkEndpoint = (
|
||||
// Partial values preserve the original expression text, byte for byte.
|
||||
value: sourceSlice(context.source, target),
|
||||
mechanism: `call:${name}`,
|
||||
...(authority === "candidate" ? { interpretation: "candidate" } : {}),
|
||||
resolution: "partial",
|
||||
});
|
||||
|
||||
@@ -997,6 +1030,9 @@ export const addReference = (
|
||||
? {}
|
||||
: { resolution_base_path: input.resolutionBasePath }),
|
||||
...(input.loader === undefined ? {} : { loader: input.loader }),
|
||||
...(input.loaderAuthority === undefined
|
||||
? {}
|
||||
: { loader_authority: input.loaderAuthority }),
|
||||
module_key: null,
|
||||
location: range(input.node),
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user