From 26a8e0b572cb1ca0982c48d9885614995b8bfeb6 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 7 Apr 2025 09:51:38 +0200 Subject: [PATCH] JS: Handle package.json-specified main files --- .../javascript/internal/PathResolution.qll | 48 ++++++++++++++++++- .../PathResolution/Basic/import-packages.ts | 6 +-- .../PathResolution/test.expected | 8 +++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/PathResolution.qll b/javascript/ql/lib/semmle/javascript/internal/PathResolution.qll index 896cae25574..e33618a9566 100644 --- a/javascript/ql/lib/semmle/javascript/internal/PathResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/PathResolution.qll @@ -273,9 +273,55 @@ module PathResolution { ) } + private JsonValue getAPartOfExportsSection(PackageJson pkg, string exportedPath) { + result = pkg.getPropValue("exports") and + exportedPath = "." + or + exists(string prop, string prevPath | + result = getAPartOfExportsSection(pkg, prevPath).getPropValue(prop) and + if prop.matches(".%") then exportedPath = prop else exportedPath = prevPath + ) + } + + private module ResolvePackageMainConfig implements ResolvePathsSig { + additional predicate shouldResolve( + PackageJson pkg, string exportedPath, Folder base, string path + ) { + base = pkg.getJsonFile().getParentContainer() and + ( + path = pkg.getPropStringValue(["main", "module"]) and + exportedPath = "." + or + path = getAPartOfExportsSection(pkg, exportedPath).getStringValue() + ) + } + + predicate shouldResolve(Folder base, string path) { shouldResolve(_, _, base, path) } + } + + private module ResolvePackageMain = ResolvePaths; + + private Container resolvePackageMain(PackageJson pkg, string exportedPath) { + exists(Folder base, string path | + ResolvePackageMainConfig::shouldResolve(pkg, exportedPath, base, path) and + result = ResolvePackageMain::resolve(base, path) + ) + } + + private Container resolvePackageMain(PackageJson pkg) { result = resolvePackageMain(pkg, ".") } + + private File getFileFromFolderImport(Folder folder) { + result = folder.getJavaScriptFile("index") + or + exists(PackageJson pkg | + pkg.getJsonFile().getParentContainer() = folder and + result = resolvePackageMain(pkg) + ) + } + File resolvePathExpr(PathExpr expr) { result = resolvePathExpr1(expr) or - result = resolvePathExpr1(expr).(Folder).getJavaScriptFile("index") + result = getFileFromFolderImport(resolvePathExpr1(expr)) } } diff --git a/javascript/ql/test/library-tests/PathResolution/Basic/import-packages.ts b/javascript/ql/test/library-tests/PathResolution/Basic/import-packages.ts index 192fb784a7f..204996d340d 100644 --- a/javascript/ql/test/library-tests/PathResolution/Basic/import-packages.ts +++ b/javascript/ql/test/library-tests/PathResolution/Basic/import-packages.ts @@ -1,6 +1,6 @@ -import '../PackageWithMain'; // $ MISSING: importTarget=PackageWithMain/main.js -import '../PackageWithModuleMain'; // $ MISSING: importTarget=PackageWithModuleMain/main.js -import '../PackageWithExports'; // $ MISSING: importTarget=PackageWithExports/main.js +import '../PackageWithMain'; // $ importTarget=PackageWithMain/main.js +import '../PackageWithModuleMain'; // $ importTarget=PackageWithModuleMain/main.js +import '../PackageWithExports'; // $ importTarget=PackageWithExports/main.js import '@example/package-with-main'; // $ importTarget=PackageWithMain/main.js import '@example/package-with-module-main'; // $ importTarget=PackageWithModuleMain/main.js diff --git a/javascript/ql/test/library-tests/PathResolution/test.expected b/javascript/ql/test/library-tests/PathResolution/test.expected index 4797ada8182..92d1e76f7c9 100644 --- a/javascript/ql/test/library-tests/PathResolution/test.expected +++ b/javascript/ql/test/library-tests/PathResolution/test.expected @@ -22,8 +22,12 @@ | Basic/Subdir/sub.ts:2:1:2:13 | import '../'; | Basic/index.ts | | Basic/Subdir/sub.ts:3:1:3:14 | import './..'; | Basic/index.ts | | Basic/Subdir/sub.ts:4:1:4:15 | import './../'; | Basic/index.ts | -| Basic/import-packages.ts:4:1:4:36 | import ... -main'; | PackageWithMain/main.js | -| Basic/import-packages.ts:5:1:5:43 | import ... -main'; | PackageWithModuleMain/main.js | +| Basic/import-packages.ts:1:1:1:28 | import ... hMain'; | PackageWithMain/main.js | +| Basic/import-packages.ts:2:1:2:34 | import ... eMain'; | PackageWithModuleMain/main.js | +| Basic/import-packages.ts:3:1:3:31 | import ... ports'; | PackageWithExports/main.js | +| Basic/import-packages.ts:5:1:5:36 | import ... -main'; | PackageWithMain/main.js | +| Basic/import-packages.ts:6:1:6:43 | import ... -main'; | PackageWithModuleMain/main.js | +| Basic/import-packages.ts:7:1:7:39 | import ... ports'; | PackageWithExports/main.js | | Basic/index.ts:1:1:1:22 | import ... r/sub'; | Basic/Subdir/sub.ts | | Extended/src/main.ts:2:1:2:21 | import ... /file"; | Extended/lib/file.ts | | Extended/src/main.ts:3:1:3:24 | import ... le.ts"; | Extended/lib/file.ts |