mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
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:
@@ -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 = (
|
||||
|
||||
Reference in New Issue
Block a user