mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
e9fc8fb30c
* 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
269 lines
8.8 KiB
TypeScript
269 lines
8.8 KiB
TypeScript
import { createPackageWithOptions } from "@electron/asar";
|
|
import { createHash } from "node:crypto";
|
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect } from "vitest";
|
|
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js";
|
|
import { writeFixtureFiles } from "../../support/javascriptApplicationFixture.js";
|
|
|
|
const escapingTarget = "./%2\te%2\te/outside.js";
|
|
const targets = {
|
|
feature: { value: "./feature.js", imports: "relative", exports: "relative" },
|
|
encodedSeparator: {
|
|
value: "./feature%2\tf.js",
|
|
imports: "rejected",
|
|
exports: "rejected",
|
|
},
|
|
malformedEncoding: {
|
|
value: "./feature%ZZ.js",
|
|
imports: "rejected",
|
|
exports: "rejected",
|
|
},
|
|
blocked: { value: null, imports: "external", exports: "external" },
|
|
empty: { value: [], imports: "external", exports: "external" },
|
|
inactive: {
|
|
value: { browser: "./feature.js" },
|
|
imports: "external",
|
|
exports: "external",
|
|
},
|
|
external: {
|
|
value: "another-package",
|
|
imports: "unavailable",
|
|
exports: "rejected",
|
|
},
|
|
recursive: { value: "#another", imports: "unavailable", exports: "rejected" },
|
|
invalid: { value: "../feature.js", imports: "rejected", exports: "rejected" },
|
|
scalar: { value: false, imports: "rejected", exports: "rejected" },
|
|
numeric: { value: { 0: null }, imports: "rejected", exports: "rejected" },
|
|
arrayNull: { value: [false, null], imports: "external", exports: "external" },
|
|
arrayInactive: {
|
|
value: [{ browser: "./feature.js" }],
|
|
imports: "external",
|
|
exports: "external",
|
|
},
|
|
arrayExternal: {
|
|
value: [false, "another-package"],
|
|
imports: "unavailable",
|
|
exports: "rejected",
|
|
},
|
|
arrayRelative: {
|
|
value: [false, "./feature.js"],
|
|
imports: "relative",
|
|
exports: "relative",
|
|
},
|
|
relativeFirst: {
|
|
value: ["./feature.js", null],
|
|
imports: "relative",
|
|
exports: "relative",
|
|
},
|
|
escapeExternal: {
|
|
value: [escapingTarget, "another-package"],
|
|
imports: "unavailable",
|
|
exports: "rejected",
|
|
},
|
|
escapeNull: {
|
|
value: [escapingTarget, null],
|
|
imports: "external",
|
|
exports: "external",
|
|
},
|
|
escapeRelative: {
|
|
value: [escapingTarget, "./feature.js"],
|
|
imports: "relative",
|
|
exports: "relative",
|
|
},
|
|
escapeCondition: {
|
|
value: { node: escapingTarget, default: "./feature.js" },
|
|
imports: "rejected",
|
|
exports: "rejected",
|
|
},
|
|
escapeNested: {
|
|
value: [{ node: escapingTarget }, "another-package"],
|
|
imports: "unavailable",
|
|
exports: "rejected",
|
|
},
|
|
escapeConfig: {
|
|
value: [{ 0: escapingTarget }, "another-package"],
|
|
imports: "rejected",
|
|
exports: "rejected",
|
|
},
|
|
wtf8Reentry: {
|
|
value: ["./%2\te%2\te/%ED%A0%80/feature.js", "another-package"],
|
|
imports: "unavailable",
|
|
exports: "rejected",
|
|
},
|
|
opaqueReentry: {
|
|
value: ["./%2\te%2\te/rea-op\taque-0/feature.js", "another-package"],
|
|
imports: "unavailable",
|
|
exports: "rejected",
|
|
},
|
|
structuralReentry: {
|
|
value: "./%2\te%2\te/mapped/feature.js",
|
|
imports: "rejected",
|
|
exports: "relative",
|
|
},
|
|
} as const;
|
|
const requests = [
|
|
{
|
|
specifier: "legacy/feature.js",
|
|
path: "node_modules/legacy/feature.js",
|
|
status: "legacy" as const,
|
|
},
|
|
{
|
|
specifier: "legacy/feature.js/.",
|
|
path: "",
|
|
status: "rejected" as const,
|
|
},
|
|
{
|
|
specifier: "legacy/../mapped/feature.js",
|
|
path: "node_modules/mapped/feature.js",
|
|
status: "legacy" as const,
|
|
},
|
|
{
|
|
specifier: "legacy/../mapped/feature.js/.",
|
|
path: "",
|
|
status: "rejected" as const,
|
|
},
|
|
{
|
|
specifier: "legacy/../../../outside.js",
|
|
path: "outside.js",
|
|
status: "root-legacy" as const,
|
|
},
|
|
{
|
|
specifier: "legacy/../../../absent.js",
|
|
path: "",
|
|
status: "not-found" as const,
|
|
},
|
|
...Object.entries(targets).flatMap(([name, target]) => [
|
|
{ specifier: `#${name}`, path: "feature.js", status: target.imports },
|
|
{
|
|
specifier: `mapped/${name}`,
|
|
path: "node_modules/mapped/feature.js",
|
|
status: target.exports,
|
|
},
|
|
]),
|
|
{ specifier: "#missing", path: "", status: "external" as const },
|
|
{ specifier: "mapped/missing", path: "", status: "external" as const },
|
|
];
|
|
const sources = ["import", "require"].map((mode) => ({
|
|
mode,
|
|
path: mode === "import" ? "main.mjs" : "main.cjs",
|
|
source: requests
|
|
.filter(({ specifier }) => mode === "import" || !specifier.endsWith("/."))
|
|
.map(({ specifier }) =>
|
|
mode === "import"
|
|
? `import ${JSON.stringify(specifier)};`
|
|
: `require(${JSON.stringify(specifier)});`,
|
|
)
|
|
.join("\n"),
|
|
}));
|
|
|
|
cliTest.for(["CLI", "stdio MCP"] as const)(
|
|
"preserves package target facts under unrepresentable URL paths through %s",
|
|
async (surface, { cli }) => {
|
|
const root = await createTestTempDirectory("rea-package-url-unicode-");
|
|
const application = join(root, "application");
|
|
await mkdir(application);
|
|
const fixture = join(root, "source");
|
|
await writeFixtureFiles(fixture, { "outside.js": "export default 4;" });
|
|
const prefixes = ["highxx", "lowxxx", "valid\ud83d\ude00"];
|
|
for (const prefix of prefixes)
|
|
await writeFixtureFiles(join(fixture, prefix), {
|
|
...Object.fromEntries(
|
|
sources.map(({ path, source }) => [path, source]),
|
|
),
|
|
"package.json": JSON.stringify({
|
|
imports: Object.fromEntries(
|
|
Object.entries(targets).map(([name, { value }]) => [
|
|
`#${name}`,
|
|
value,
|
|
]),
|
|
),
|
|
}),
|
|
"feature.js": "export default 1;",
|
|
"node_modules/legacy/package.json": "{}",
|
|
"node_modules/legacy/feature.js": "export default 2;",
|
|
"node_modules/mapped/package.json": JSON.stringify({
|
|
exports: Object.fromEntries(
|
|
Object.entries(targets).map(([name, { value }]) => [
|
|
`./${name}`,
|
|
value,
|
|
]),
|
|
),
|
|
}),
|
|
"node_modules/mapped/feature.js": "export default 3;",
|
|
});
|
|
const archive = join(application, "app.asar");
|
|
await createPackageWithOptions(fixture, archive, {});
|
|
const bytes = await readFile(archive);
|
|
const header = bytes.subarray(16, 16 + bytes.readUInt32LE(12));
|
|
const escaped = Buffer.from(
|
|
header
|
|
.toString("utf8")
|
|
.replace('"highxx":', '"\\ud800":')
|
|
.replace('"lowxxx":', '"\\udc00":'),
|
|
);
|
|
expect(escaped.length).toBe(header.length);
|
|
expect(escaped.equals(header)).toBe(false);
|
|
escaped.copy(bytes, 16);
|
|
await writeFile(archive, bytes);
|
|
|
|
const { graph } = await analyzeJavaScriptDirectory(
|
|
surface,
|
|
cli,
|
|
application,
|
|
);
|
|
for (const prefix of ["\ud800", "\udc00", "valid\ud83d\ude00"])
|
|
for (const { mode, path, source } of sources)
|
|
for (const request of requests) {
|
|
if (mode === "require" && request.specifier.endsWith("/.")) continue;
|
|
const base = `app.asar/${prefix}`;
|
|
const available =
|
|
prefix.startsWith("valid") ||
|
|
request.status === "root-legacy" ||
|
|
(mode === "require" && request.status === "legacy");
|
|
const fileStatus = available ? "resolved" : "unavailable";
|
|
const status =
|
|
request.status === "relative" ||
|
|
request.status === "legacy" ||
|
|
request.status === "root-legacy"
|
|
? fileStatus
|
|
: request.status;
|
|
expect(graph.edges).toContainEqual(
|
|
expect.objectContaining({
|
|
relation: "imports",
|
|
properties: expect.objectContaining({
|
|
module_link_kind: mode,
|
|
specifier: request.specifier,
|
|
resolution_status: status,
|
|
resolved_path:
|
|
status === "resolved"
|
|
? `${request.status === "root-legacy" ? "app.asar" : base}/${request.path}`
|
|
: null,
|
|
}),
|
|
evidence: expect.objectContaining({
|
|
limitations:
|
|
status === "unavailable" && request.status !== "unavailable"
|
|
? expect.arrayContaining([
|
|
expect.stringContaining("unpaired UTF-16 surrogate"),
|
|
])
|
|
: expect.not.arrayContaining([
|
|
expect.stringContaining("unpaired UTF-16 surrogate"),
|
|
]),
|
|
artifact: expect.objectContaining({
|
|
sha256: createHash("sha256").update(source).digest("hex"),
|
|
}),
|
|
location: expect.objectContaining({
|
|
value: expect.objectContaining({ source: `${base}/${path}` }),
|
|
}),
|
|
}),
|
|
}),
|
|
);
|
|
}
|
|
expect(await readFile(archive)).toEqual(bytes);
|
|
},
|
|
);
|