diff --git a/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts b/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts index 675234dec67..2e0327a8c91 100644 --- a/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts +++ b/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts @@ -357,6 +357,28 @@ describe("CommonJS dependency links", () => { }); }); +it.each([ + ['{"__proto__": {require: true}}', false], + ["{__proto__: {require: true}}", false], + ["{require: true}", false], + ["{require: false}", true], + ['{["__proto__"]: {require: true}}', true], +] as const)( + "tracks an outer require write with unscopables %s", + (entry, resolved) => { + const source = ` + with ({require: null, [Symbol.unscopables]: ${entry}}) { + require = () => "LOCAL"; + } + require("./dep.cjs"); + `; + const links = analyzeJavaScriptSemantics(source).moduleLinks; + expect(links.map(({ kind, specifier }) => ({ kind, specifier }))).toEqual( + resolved ? [{ kind: "require", specifier: "./dep.cjs" }] : [], + ); + }, +); + it("preserves unshadowed require origins through aliases and assignments", () => { const ir = analyzeJavaScriptSemantics(` const { ipcMain: bus } = require("electron"); diff --git a/src/domain/javascript/javascriptSemanticState.ts b/src/domain/javascript/javascriptSemanticState.ts index 946ac319779..ec84e59e684 100644 --- a/src/domain/javascript/javascriptSemanticState.ts +++ b/src/domain/javascript/javascriptSemanticState.ts @@ -1,5 +1,6 @@ import * as t from "@babel/types"; import { + isObjectPrototypeSetter, semanticStaticPropertyKey, staticJavaScriptTruthiness, unwrapJavaScriptExpression, @@ -411,10 +412,8 @@ const semanticWithRequireLookup = ( ); if (key === "require") ownRequire = true; else if ( - key === "__proto__" && t.isObjectProperty(property) && - !property.computed && - !property.shorthand + isObjectPrototypeSetter(property) ) { const prototype = unwrapJavaScriptExpression(property.value).node; if (t.isNullLiteral(prototype)) prototypeIsNull = true; @@ -497,12 +496,7 @@ const semanticWithPrototypeKind = ( object: t.ObjectExpression, ): "null" | "ordinary" | "other" => { for (const property of object.properties) { - if ( - t.isObjectProperty(property) && - !property.computed && - !property.shorthand && - t.isIdentifier(property.key, { name: "__proto__" }) - ) + if (t.isObjectProperty(property) && isObjectPrototypeSetter(property)) return t.isNullLiteral(unwrapJavaScriptExpression(property.value).node) ? "null" : "other";