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:
morlutoandShuaixinHuang authored and GitHub committed 2026-11-09 13:21:24 +08:00
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),
},