From b1d4776b173d3b5e8946ccaba2843e5793e4f75e Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 10 Jun 2025 11:25:32 +0200 Subject: [PATCH] JS: Handle name resolution through dynamic imports --- .../javascript/internal/NameResolution.qll | 22 +++++++++++++++++++ .../UnderlyingTypes/dynamicImportUse.ts | 2 +- .../UnderlyingTypes/test.expected | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index 2397716bd58..b25c98fd693 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -46,6 +46,9 @@ module NameResolution { this instanceof Module or this instanceof NamespaceDefinition // `module {}` or `enum {}` statement + or + // A module wrapped in a promise. We model this as a module exporting the actual module in a property called `$$promise-content`. + this instanceof DynamicImportExpr } } @@ -232,6 +235,19 @@ module NameResolution { name = expr.getName() and node2 = expr ) + or + exists(AwaitExpr await | + node1 = await.getOperand() and + name = "$$promise-content" and + node2 = await + ) + or + exists(MethodCallExpr call | + call.getMethodName() = "then" and + node1 = call.getReceiver() and + name = "$$promise-content" and + node2 = call.getArgument(0).(Function).getParameter(0) + ) } private signature module TypeResolutionInputSig { @@ -334,6 +350,12 @@ module NameResolution { ) or storeToVariable(result, name, mod.(Closure::ClosureModule).getExportsVariable()) + or + exists(DynamicImportExpr imprt | + mod = imprt and + name = "$$promise-content" and + result = imprt.getImportedPathExpr() + ) } /** diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/dynamicImportUse.ts b/javascript/ql/test/library-tests/UnderlyingTypes/dynamicImportUse.ts index 9be24770148..cca2b265f28 100644 --- a/javascript/ql/test/library-tests/UnderlyingTypes/dynamicImportUse.ts +++ b/javascript/ql/test/library-tests/UnderlyingTypes/dynamicImportUse.ts @@ -1,4 +1,4 @@ async function t1() { const e = await import('./dynamicImportLib'); - e.getRequest(); // $ MISSING: hasUnderlyingType='express'.Request + e.getRequest(); // $ hasUnderlyingType='express'.Request } diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected index 9525a32706b..21df0c0b921 100644 --- a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected +++ b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected @@ -15,6 +15,7 @@ | contextualTypes.ts:27:16:27:18 | req | 'express'.Request | | contextualTypes.ts:34:20:34:22 | req | 'express'.Request | | contextualTypes.ts:41:16:41:18 | req | 'express'.Request | +| dynamicImportUse.ts:3:5:3:18 | e.getRequest() | 'express'.Request | | expressBulkExport.use.ts:3:13:3:15 | req | 'express'.Request | | expressBulkExport.use.ts:6:13:6:15 | res | 'express'.Response | | expressExportAssign.use.ts:3:13:3:15 | req | 'express'.Request |