mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
fbfdb6abe9
Resolve unchanged Map operations through shared binding, capture, mutation, and ambient escape facts. Reuse that proof in application and captured bundle analysis; unresolved get/delete receivers remain inferred candidates. Preserve repeated native operations without manufacturing receiver escapes. Retain uncertainty for real escapes, opaque eval, and changed constructor adder hooks. Converge stored aliases at the producing boundary. Adapt the JavaScript changes from #4863, excluding its Linux resource fix. Add captured-bundle parity, repeated-operation and constructor-hook coverage. Fixes #4853 Co-authored-by: abdooo2235 <abdalrhmantheiv15@gmail.com>
71 lines
3.9 KiB
TypeScript
71 lines
3.9 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
|
|
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
|
|
import { onlyCallable } from "./javascriptSemanticAnalysis.fixture.js";
|
|
|
|
it.each([
|
|
'eval("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'eval("Map.prototype.delete = fetch"); new Map().delete("/accounts");',
|
|
'"use strict"; eval("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'function run() { eval("Map.prototype.get = fetch"); return new Map().get("/accounts"); }',
|
|
'function patch() { eval("Map.prototype.get = fetch"); } patch(); new Map().get("/accounts");',
|
|
'for (const item of [1, 2]) { new Map().get("/accounts"); eval("Map.prototype.get = fetch"); }',
|
|
'const run = eval; run("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'let run; (run = eval)("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'let run; (run = globalThis.eval)("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'(0, eval)("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'globalThis.eval("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'eval?.("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'let run = eval; run("Map.prototype.get = fetch"); run = () => {}; new Map().get("/accounts");',
|
|
'let run = () => {}; run = eval; run("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'const { eval: run } = globalThis; run("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'eval.call(null, "Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'const run = eval.bind(null); run("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'globalThis["ev" + "al"]("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'const { ["eval"]: run } = globalThis; run("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'const root = globalThis; const run = root["eval"]; run("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'function invoke() { run("Map.prototype.get = fetch"); } const run = eval; invoke(); new Map().get("/accounts");',
|
|
'function getRun() { return eval; } getRun()("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'Reflect.apply(eval, null, ["Map.prototype.get = fetch"]); new Map().get("/accounts");',
|
|
])(
|
|
"keeps a Map lookup after possible opaque mutation as a candidate: %s",
|
|
(source) => {
|
|
expect(analyzeJavaScriptStaticSource(source).endpoints).toMatchObject([
|
|
{ kind: "network", value: "/accounts", interpretation: "candidate" },
|
|
]);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
'new Map().get("/accounts"); eval("Map.prototype.get = fetch");',
|
|
'(new Map().get("/accounts"), eval("Map.prototype.get = fetch"));',
|
|
'function run(eval) { eval("Map.prototype.get = fetch"); return new Map().get("/accounts"); }',
|
|
'const eval = () => {}; eval("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'for (const item of []) { eval("Map.prototype.get = fetch"); } new Map().get("/accounts");',
|
|
'const run = eval; new Map().get("/accounts"); run("Map.prototype.get = fetch");',
|
|
'function inspect(eval) { const run = eval; run("Map.prototype.get = fetch"); return new Map().get("/accounts"); }',
|
|
'let run = eval; run = () => {}; run("Map.prototype.get = fetch"); new Map().get("/accounts");',
|
|
'eval.bind(null); new Map().get("/accounts");',
|
|
])(
|
|
"preserves a Map proof when eval cannot precede the lookup: %s",
|
|
(source) => {
|
|
expect(analyzeJavaScriptStaticSource(source).endpoints).toEqual([]);
|
|
},
|
|
);
|
|
|
|
it("does not grant indirect eval access to the caller's lexical bindings", () => {
|
|
const analysis = analyzeJavaScriptSemantics(`
|
|
export function inspect() {
|
|
let value = 1;
|
|
const run = eval;
|
|
run("value = 2");
|
|
return value;
|
|
}
|
|
`);
|
|
expect(onlyCallable(analysis, "inspect").returnSites[0]?.value).toEqual({
|
|
status: "literal",
|
|
value: 1,
|
|
});
|
|
});
|