fix(javascript): preserve runtime path bytes and Electron ownership (#4941)

* fix(javascript): preserve literal percent runtime paths

* fix(javascript): ignore resolve prefixes before the final anchor

* fix(electron): retain application roots for loadFile declarations

* fix(electron): resolve loadFile from enclosing application package

* fix(electron): retain outer application root across package scopes

* fix(electron): derive application ownership from inventoried packages

* fix(electron): retain resolved package entrypoint ownership

* fix(electron): preserve competing application ownership

* fix(electron): retain invalid and omitted runtime ownership

* fix(electron): preserve omitted module ownership and runtime paths

* fix(electron): preserve proven roots before confining page paths

* fix(electron): retain possible owners behind missing imports

* fix(electron): distinguish absent imports and owned page candidates

* fix(electron): keep unavailable JSON out of owner gaps

* fix(electron): retain exact loader formats and default entry status

* fix(electron): retain unavailable entrypoint ownership candidates

* test(ghidra): preserve Windows Node probe prerequisites

* test(ghidra): assert cancellation during profile shutdown

* fix(electron): separate examined imports from frontend ownership

* fix(electron): retain unavailable directory entrypoint owners

* test(jeb): retain Linux ownership prerequisite in isolation

* fix(javascript): retain unchecked loader ownership and literal main paths

* fix(javascript): preserve runtime paths and call-site module aliases

* fix(javascript): bound module alias traversal and preserve projections

* fix(javascript): retain host module and nested artifact path boundaries

* fix(javascript): preserve ESM and archive module loader provenance

* test(javascript): distinguish unmapped containers from missing exports

* fix: retain default module aliases and Electron builtin imports

* fix: preserve host file URL and inward ASAR ownership

* fix: preserve CommonJS file URL classification in CI fixtures

* fix: retain bound module loaders and Windows entrypoint uncertainty

* fix: retain ASAR uncertainty and invalidate changed Electron app roots

* fix(javascript): retain ownership uncertainty across loader boundaries

* fix(javascript): preserve loader sources and bound alias queries

* fix(javascript): retain destructured loader sources

* fix(javascript): preserve loader uncertainty at resolution boundaries

* fix(javascript): preserve callable and package launch ownership

* fix(javascript): preserve captured values across member reads

* test(process): assert tagged filesystem cancellation failures

* fix(javascript): preserve inventory bases and invocation read timing

* fix(javascript): preserve ownership across indirect and unread loads

* test(javascript): order fixture directory creation on Windows

* test(javascript): share ordered filesystem fixture writer

* fix(javascript): unify loader provenance and package validation

* fix(javascript): preserve captured Node require loader values

* fix(javascript): preserve loader invocation authority

* fix(javascript): verify generic loader invocation authority

* fix(javascript): preserve conditional loader method alternatives

* fix(javascript): require proven factories for source association

* fix(javascript): honor replaced Electron app path setters

* fix(javascript): prove path and assignment source ownership

* fix(javascript): retain native delegate eligibility through shared writes

* fix(process): settle stale baseline errors after confirmed absence

* fix(javascript): preserve assignment and path boundary evidence

* fix(javascript): prove source-directory resolve anchors

* fix: preserve application path provenance and portable schemas

* fix(javascript): retain CommonJS exported setter effects

* fix: isolate Linux identity observations and retain native module exports

* fix(javascript): preserve published app and helper provenance

* fix(javascript): normalize typed stores and native path fixtures
This commit is contained in:
ShuaixinHuang
2026-10-20 17:25:19 +08:00
committed by GitHub
parent d532b18123
commit e9fc8fb30c
149 changed files with 16881 additions and 988 deletions
@@ -1,6 +1,9 @@
import type { ArtifactInventorySnapshot } from "../../domain/artifactInventorySnapshot.js";
import type { JsonValue } from "../../domain/jsonValue.js";
import { compositeKey } from "../../domain/unicodeCodePointOrder.js";
import {
compositeKey,
compareUnicodeCodePoints,
} from "../../domain/unicodeCodePointOrder.js";
import {
completeApplicationCoverage,
partialApplicationCoverage,
@@ -51,8 +54,112 @@ export interface JavaScriptArtifactGraphContext {
readonly chunkNodes: Map<string, ApplicationNode>;
readonly moduleNodes: Map<string, ApplicationNode>;
readonly containerNodes: Map<string, ApplicationNode>;
/** Resolved entrypoints/imports retain unique or competing application owners. */
readonly applicationRootsBySource: Map<string, Set<string>>;
/** Incomplete package facts can hide competing launch declarations. */
readonly applicationOwnershipUnknownContainers: ReadonlySet<string>;
/** Gap origins retain possible owners separately from proven source owners. */
readonly applicationModuleGapSources: Map<string, Set<string>>;
readonly applicationPossibleRootsByContainer: Map<string, Set<string>>;
/** Sources with retained references; unchecked loads carry their own gaps. */
readonly applicationModuleReferencesRetained: ReadonlySet<string>;
}
/** Application manifests exclude dependency packages but retain nested app roots. */
export const dependencyManifest = (path: string): boolean =>
path.split("/").slice(0, -1).includes("node_modules");
/** Two distinct roots prove ambiguity; further roots cannot restore uniqueness. */
export const addApplicationRootCandidate = (
roots: Set<string>,
root: string,
): void => {
if (roots.size < 2) roots.add(root);
};
const applicationContainerRoot = (
fileSet: JavaScriptArtifactFileSet,
source: Pick<JavaScriptArtifactFile, "path" | "container_sha256">,
): string =>
fileSet.containers.find(
({ sha256, path }) =>
sha256 === source.container_sha256 && source.path.startsWith(`${path}/`),
)?.path ?? "";
/** Equal archive bytes at different logical paths are distinct ownership scopes. */
export const applicationContainerIdentity = (
fileSet: JavaScriptArtifactFileSet,
source: Pick<JavaScriptArtifactFile, "path" | "container_sha256">,
): string =>
compositeKey([
source.container_sha256,
applicationContainerRoot(fileSet, source),
]);
/** Select the inventory's display root; this does not establish runtime ownership. */
export const applicationManifestPath = (
context: JavaScriptArtifactGraphContext,
): string | undefined =>
context.analysis.packages
.map(({ path }) => path)
.filter((path) => {
const file = context.filesByPath.get(path);
return file !== undefined && !dependencyManifest(path);
})
.sort(
(left, right) =>
left.split("/").length - right.split("/").length ||
compareUnicodeCodePoints(left, right),
)[0];
/** Inspect inventoried ancestor packages; absent parsing is not absent ownership.
* Entry-bearing packages are candidates; multiple candidates stay unknown.
*/
export const applicationRootPath = (
context: JavaScriptArtifactGraphContext,
source: JavaScriptArtifactFile,
): string | null => {
// Unread launch metadata can name a main in another inventoried container.
// Archive occurrence identity does not prove isolation from the Node loader.
if (context.applicationOwnershipUnknownContainers.size > 0) return null;
const entryRoots = context.applicationRootsBySource.get(source.path);
const container = applicationContainerIdentity(context.fileSet, source);
for (const root of context.applicationPossibleRootsByContainer.get(
container,
) ?? [])
if (!entryRoots?.has(root)) return null;
if (
entryRoots === undefined &&
context.applicationModuleGapSources.has(container)
)
return null;
const roots = new Set<string>(entryRoots);
const directories = source.path.split("/").slice(0, -1);
for (let depth = directories.length; depth >= 0; depth -= 1) {
const root = directories.slice(0, depth).join("/");
const prefix = root === "" ? "" : `${root}/`;
const path = `${prefix}package.json`;
if (dependencyManifest(path)) continue;
const file =
context.filesByPath.get(path) ?? context.unanalyzedFiles?.get(path);
if (file?.container_sha256 !== source.container_sha256) continue;
const metadata = context.analysis.packages.find(
(value) => value.path === path,
);
if (metadata?.status !== "included") return null;
const defaultEntry = context.filesByPath.get(`${prefix}index.js`);
if (
entryRoots === undefined &&
(metadata.main_status === "declared" ||
defaultEntry?.container_sha256 === source.container_sha256)
)
addApplicationRootCandidate(roots, root);
}
if (roots.size > 1) return null;
for (const root of roots) return root;
return applicationContainerRoot(context.fileSet, source);
};
/** Evidence coverage shape shared by reconstruction graph helpers. */
export type JavaScriptArtifactGraphCoverage =
ApplicationGraphEvidence["coverage"];
@@ -302,13 +409,25 @@ export const artifactFileNodeKind = (
export const createElectronRoleNode = (
context: JavaScriptArtifactGraphContext,
input: RoleNodeInput,
): ApplicationNode =>
context.accumulator.addNode({
): ApplicationNode => {
const target =
input.resolution.resolved_path ??
(input.resolution.runtime_path_base_proven === true
? input.resolution.candidate_path
: undefined) ??
input.resolution.declared_path;
// Raw application-relative declarations keep their source occurrence even
// when identical source bytes appear under separate application prefixes.
const identityPath =
input.resolution.runtime_path_base === "application-root"
? compositeKey([input.anchor.path, target])
: target;
return context.accumulator.addNode({
kind: input.kind,
identity: artifactLocalIdentity(
input.anchor.sha256,
"electron-role",
`${input.kind}:${input.resolution.resolution_context}:${input.resolution.resolved_path ?? input.resolution.declared_path}`,
`${input.kind}:${input.resolution.resolution_context}:${identityPath}`,
),
observations: [
{
@@ -333,6 +452,7 @@ export const createElectronRoleNode = (
},
],
});
};
/** Resolve an Electron role to an inventoried asset when available. */
export const linkElectronRoleToAsset = (