fix(artifacts): retain permission omissions through JavaScript lookups

This commit is contained in:
morluto
2026-10-24 03:00:21 +08:00
parent dd55e2c7d5
commit 693ecec082
14 changed files with 343 additions and 30 deletions
@@ -159,6 +159,7 @@ function* buildJavaScriptArtifactGraphInputSteps(
root,
filesByPath: new Map(fileSet.files.map((file) => [file.path, file])),
unanalyzedFiles: {
coverage: snapshot.coverage,
get: (path) =>
unavailableFilesByPath.get(path) ?? fileSet.unanalyzed_files?.get(path),
hasDirectory: (path, container) =>
@@ -118,6 +118,7 @@ export const applicationRootPath = (
context: JavaScriptArtifactGraphContext,
source: JavaScriptArtifactFile,
): string | null => {
if (context.snapshot.coverage.status !== "complete") return 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;
@@ -329,6 +329,7 @@ it.each([
gap === "opaque"
? {
get: (path) => (path === target.path ? target : undefined),
coverage: base.snapshot.coverage,
hasDirectory: () => false,
}
: undefined,
@@ -10,6 +10,72 @@ import { artifactLocalIdentity } from "./JavaScriptArtifactGraphContext.js";
import { applicationNodeIdentitySchema } from "../../domain/javascript/javascriptApplicationEvidenceSchemas.js";
import { analyzeJavaScriptStaticSource } from "../../domain/javascript/javascriptStaticAnalysis.js";
const completeInventoryCoverage = {
scope: "container-entries",
status: "complete",
} as const;
describe("incomplete inventory lookups", () => {
it.each(["complete", "unknown"] as const)(
"qualifies missing candidates before extension fallbacks under %s traversal",
(status) => {
const files = fileMap([
file("app/main.cjs", "root"),
file("app/target.js", "root"),
]);
const unanalyzedFiles = {
coverage: { scope: "container-entries" as const, status },
get: () => undefined,
hasDirectory: () => false,
};
expect(
resolve({
declaredPath: "./target",
sourcePath: "app/main.cjs",
context: "module-specifier",
moduleKind: "require",
files,
unanalyzedFiles,
}),
).toMatchObject(
status === "complete"
? { resolution_status: "resolved", resolved_path: "app/target.js" }
: {
resolution_status: "unavailable",
resolved_path: null,
candidate_path: "app/target",
},
);
expect(
resolve({
declaredPath: "./target.js",
sourcePath: "app/main.cjs",
context: "module-specifier",
moduleKind: "require",
files,
unanalyzedFiles,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "app/target.js",
});
expect(
resolve({
declaredPath: "./missing.js",
sourcePath: "app/main.cjs",
context: "module-specifier",
moduleKind: "require",
files,
unanalyzedFiles,
}),
).toMatchObject({
resolution_status: status === "complete" ? "not-found" : "unavailable",
resolved_path: null,
});
},
);
});
describe("Windows CommonJS request classification", () => {
it.each([
"..\\shared\\window.cjs",
@@ -578,6 +644,7 @@ describe("loader-specific module suffix lookup", () => {
const files = fileMap([file("app/entry.cjs", "root")]);
const unanalyzedFiles = {
get: (path: string) => opaque.get(path),
coverage: completeInventoryCoverage,
hasDirectory: () => false,
};
expect(
@@ -650,6 +717,7 @@ describe("unavailable inventoried native add-ons", () => {
files: fileMap([file("app/entry.cjs", "root")]),
unanalyzedFiles: {
get: (path) => (path === unavailable.path ? unavailable : undefined),
coverage: completeInventoryCoverage,
hasDirectory: () => false,
},
});
@@ -689,6 +757,7 @@ describe("unavailable package metadata", () => {
requestedPaths.push(path);
return path === manifest.path ? manifest : undefined;
},
coverage: completeInventoryCoverage,
hasDirectory: () => false,
};
const relativeRequire = resolve({
@@ -757,6 +826,7 @@ describe("unavailable package metadata", () => {
]);
const lookup = {
get: (path: string) => (path === manifest.path ? manifest : undefined),
coverage: completeInventoryCoverage,
hasDirectory: () => false,
};
for (const [sourcePath, moduleKind] of [
@@ -4128,6 +4198,7 @@ describe("Node launch package main ownership", () => {
limitations: ["main readback unavailable"],
}
: undefined,
coverage: completeInventoryCoverage,
hasDirectory: () => false,
},
}),
@@ -4376,6 +4447,7 @@ describe("package metadata at archive module boundaries", () => {
unanalyzedFiles: {
get: (requested) =>
opaque && requested === path ? target : undefined,
coverage: completeInventoryCoverage,
hasDirectory: () => false,
},
}),
@@ -1426,6 +1426,11 @@ const readPackageMetadata = (
unavailable.limitations?.[0] ??
`Directory package metadata ${path} was inventoried but its bytes are unavailable.`,
};
if (input.unanalyzedFiles?.coverage.status === "unknown")
return {
kind: "unavailable",
limitation: `Directory package metadata ${path} was not observed, and artifact traversal is incomplete; its absence is unknown.`,
};
return { kind: "absent" };
}
if (!admitsRuntimeContainer(input, path, file.container_sha256))
@@ -1750,6 +1755,15 @@ const findFileCandidate = (
: `${path} is an inventoried ${unanalyzed.kind} file, but its bytes are unavailable (hash status ${unanalyzed.hash_status ?? "unknown"}); resolution cannot be confirmed.`,
],
};
if (input.unanalyzedFiles?.coverage.status === "unknown")
return {
resolvedPath: null,
status: "unavailable",
candidatePath: path,
limitations: [
`Artifact path ${path} was not observed, and artifact traversal is incomplete; its absence is unknown.`,
],
};
return null;
};