mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
* 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
232 lines
6.9 KiB
TypeScript
232 lines
6.9 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
|
|
|
|
it.each([
|
|
[
|
|
"direct factory",
|
|
"const getApp=()=>app;getApp().setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"returned alias",
|
|
"const getApp=()=>app;const runtime=getApp();runtime.setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"method factory",
|
|
"const api={get(){return app}};api.get().setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"projected return",
|
|
"const getApp=()=>({runtime:app});getApp().runtime.setAppPath('./lib');",
|
|
true,
|
|
],
|
|
["method return", "const change=()=>app.setAppPath;change()('./lib');", true],
|
|
[
|
|
"awaited factory",
|
|
"const getApp=async()=>app;(await getApp()).setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"unawaited promise",
|
|
"const getApp=async()=>app;getApp().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"generator",
|
|
"function*getApp(){return app}getApp().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"awaited generator",
|
|
"function*getApp(){return app}(await getApp()).setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"local factory",
|
|
"const getApp=()=>({setAppPath(){}});getApp().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"escaped local",
|
|
"const local={setAppPath(){}};escape(local);local.setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"external local",
|
|
"const local=require('local-library');local.setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"unknown return",
|
|
"const getApp=()=>unknown;getApp().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"unrelated unknown invocation",
|
|
"const local={make(){}};escape(local);local.make();",
|
|
false,
|
|
],
|
|
[
|
|
"local method",
|
|
"const api={get(){return {setAppPath(){}}}};api.get().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"shadowed app",
|
|
"function local(app){const getApp=()=>app;getApp().setAppPath('./lib');}",
|
|
false,
|
|
],
|
|
[
|
|
"mixed return",
|
|
"const getApp=()=>flag?app:{setAppPath(){}};getApp().setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"replacement factory",
|
|
"const api={get:()=>({setAppPath(){}})};api.get=()=>app;api.get().setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"local replacement",
|
|
"const api={get:()=>app};api.get=()=>({setAppPath(){}});api.get().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"later member replacement",
|
|
"const getApp=()=>app;getApp().setAppPath('./lib');app.setAppPath=()=>{};",
|
|
true,
|
|
],
|
|
[
|
|
"returned setter call",
|
|
"const getApp=()=>app;getApp().setAppPath.call(app,'./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"returned setter reflect",
|
|
"const getApp=()=>app;Reflect.apply(getApp().setAppPath,app,['./lib']);",
|
|
true,
|
|
],
|
|
[
|
|
"factory call delegate",
|
|
"const getApp=()=>app;getApp.call(null).setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"factory apply delegate",
|
|
"const getApp=()=>app;getApp.apply(null,[]).setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"captured app before replacement",
|
|
"let runtime=app;const held=runtime;runtime={setAppPath(){}};const getApp=()=>held;getApp().setAppPath('./lib');",
|
|
true,
|
|
],
|
|
[
|
|
"captured local before replacement",
|
|
"let runtime={setAppPath(){}};const held=runtime;runtime=app;const getApp=()=>held;getApp().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"captured local after replacement",
|
|
"let runtime=app;runtime={setAppPath(){}};const held=runtime;const getApp=()=>held;getApp().setAppPath('./lib');",
|
|
false,
|
|
],
|
|
[
|
|
"captured app after replacement",
|
|
"let runtime={setAppPath(){}};runtime=app;const held=runtime;const getApp=()=>held;getApp().setAppPath('./lib');",
|
|
true,
|
|
],
|
|
] as const)(
|
|
"retains only possible Electron app path changes from %s",
|
|
(_name, body, expected) => {
|
|
const analysis = analyzeJavaScriptStaticSource(
|
|
`const {app}=require('electron');${body}`,
|
|
);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect((analysis.electron.app_path_changes ?? []).length > 0).toBe(
|
|
expected,
|
|
);
|
|
// A possible return must not promote other APIs to proven Electron identity.
|
|
expect(analysis.electron.browser_windows).toEqual([]);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
"const getApp=()=>require('electron').app;getApp().setAppPath('./lib');",
|
|
"const getElectron=()=>require('electron');getElectron().app.setAppPath('./lib');",
|
|
"const getApp=()=>require('other').app;getApp().setAppPath('./lib');",
|
|
])(
|
|
"follows returned require origins without an import shortcut: %s",
|
|
(source) => {
|
|
const analysis = analyzeJavaScriptStaticSource(source);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect((analysis.electron.app_path_changes ?? []).length > 0).toBe(
|
|
!source.includes("'other'"),
|
|
);
|
|
},
|
|
);
|
|
|
|
it("does not promote a returned Electron constructor to proven API identity", () => {
|
|
const analysis = analyzeJavaScriptStaticSource(
|
|
"const {BrowserWindow}=require('electron');const get=()=>BrowserWindow;new (get())();",
|
|
);
|
|
expect(analysis.electron.browser_windows).toEqual([]);
|
|
expect(analysis.electron.app_path_changes ?? []).toEqual([]);
|
|
});
|
|
|
|
it.each([
|
|
["call", true],
|
|
["call", false],
|
|
["apply", true],
|
|
["apply", false],
|
|
] as const)(
|
|
"uses the actual receiver of a captured %s wrapper (host=%s)",
|
|
(method, host) => {
|
|
const local = "({setAppPath(){}})";
|
|
const analysis = analyzeJavaScriptStaticSource(
|
|
`const {app}=require('electron');
|
|
const original=()=>${host ? local : "app"};
|
|
const selected=()=>${host ? "app" : local};
|
|
const invoke=original.${method};
|
|
invoke.call(selected,null${method === "apply" ? ",[]" : ""}).setAppPath('./lib');`,
|
|
);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect((analysis.electron.app_path_changes ?? []).length > 0).toBe(host);
|
|
},
|
|
);
|
|
|
|
it.each([true, false])(
|
|
"keeps a finite source closure after an Electron app selector budget is reached (host=%s)",
|
|
(host) => {
|
|
const declarations = Array.from(
|
|
{ length: 550 },
|
|
(_, index) =>
|
|
`const selected${index}=selected${index === 0 ? "Root" : index - 1};`,
|
|
).join("");
|
|
const analysis = analyzeJavaScriptStaticSource(
|
|
`const {app}=require('electron');const selectedRoot={runtime:${host ? "app" : "{setAppPath(){}}"}};${declarations}const getApp=()=>selected549.runtime;getApp().setAppPath('./lib');`,
|
|
);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect((analysis.electron.app_path_changes ?? []).length > 0).toBe(host);
|
|
expect(analysis.limitations).toEqual(
|
|
expect.arrayContaining([
|
|
expect.stringContaining("Alias selection expansion"),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
it.each([true, false])(
|
|
"closes cyclic returned app copies without inventing an Electron origin (host=%s)",
|
|
(host) => {
|
|
const analysis = analyzeJavaScriptStaticSource(
|
|
`const {app}=require('electron');let box={runtime:${host ? "app" : "{setAppPath(){}}"}};while(flag)box={...box};const getApp=()=>box.runtime;getApp().setAppPath('./lib');`,
|
|
);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect((analysis.electron.app_path_changes ?? []).length > 0).toBe(host);
|
|
},
|
|
);
|