From ed2a832a555f1001b15965de4e8b55b674a2eb6c Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 29 Apr 2025 13:22:54 +0200 Subject: [PATCH] JS: Deprecate PathExpr and related classes --- javascript/ql/examples/snippets/importfrom.ql | 2 +- javascript/ql/lib/definitions.qll | 2 +- javascript/ql/lib/semmle/javascript/AMD.qll | 28 ++++++++++------ .../lib/semmle/javascript/ES2015Modules.qll | 8 ++--- javascript/ql/lib/semmle/javascript/Expr.qll | 4 +-- javascript/ql/lib/semmle/javascript/HTML.qll | 10 ++++-- .../ql/lib/semmle/javascript/Modules.qll | 16 +++++++--- .../ql/lib/semmle/javascript/NodeJS.qll | 32 ++++++------------- .../javascript/NodeModuleResolutionImpl.qll | 20 ++++++------ javascript/ql/lib/semmle/javascript/Paths.qll | 30 ++++++++--------- .../ql/lib/semmle/javascript/TypeScript.qll | 8 ++--- .../lib/semmle/javascript/dataflow/Nodes.qll | 2 +- .../internal/InterModuleTypeInference.qll | 4 +-- .../javascript/dependencies/Dependencies.qll | 7 +++- .../frameworks/AngularJS/AngularJSCore.qll | 2 +- .../semmle/javascript/frameworks/Babel.qll | 4 +-- .../javascript/frameworks/LazyCache.qll | 4 +-- .../semmle/javascript/frameworks/React.qll | 2 +- .../lib/semmle/javascript/frameworks/Vue.qll | 4 +-- .../data/internal/ApiGraphModelsSpecific.qll | 2 +- .../internal/paths/PathExprResolver.qll | 2 +- ...APIUsedWithUntrustedDataCustomizations.qll | 2 +- .../src/Declarations/UnstableCyclicImport.ql | 2 +- .../ql/src/NodeJS/UnresolvableImport.ql | 2 +- javascript/ql/src/NodeJS/UnusedDependency.ql | 17 +++++++--- javascript/ql/test/library-tests/AMD/tests.ql | 4 +-- .../ql/test/library-tests/Modules/tests.ql | 4 +-- .../ql/test/library-tests/NodeJS/tests.ql | 2 +- .../TypeScript/ImportEquals/tests.ql | 2 +- .../RegressionTests/ImportDtsFile/test.ql | 2 +- 30 files changed, 126 insertions(+), 104 deletions(-) diff --git a/javascript/ql/examples/snippets/importfrom.ql b/javascript/ql/examples/snippets/importfrom.ql index d466136ca2f..a2efb17e169 100644 --- a/javascript/ql/examples/snippets/importfrom.ql +++ b/javascript/ql/examples/snippets/importfrom.ql @@ -11,5 +11,5 @@ import javascript from ImportDeclaration id -where id.getImportedPath().getValue() = "react" +where id.getImportedPathString() = "react" select id diff --git a/javascript/ql/lib/definitions.qll b/javascript/ql/lib/definitions.qll index 7b4806b1478..2cc9313d313 100644 --- a/javascript/ql/lib/definitions.qll +++ b/javascript/ql/lib/definitions.qll @@ -70,7 +70,7 @@ private predicate importLookup(AstNode path, Module target, string kind) { kind = "I" and ( exists(Import i | - path = i.getImportedPath() and + path = i.getImportedPathExpr() and target = i.getImportedModule() ) or diff --git a/javascript/ql/lib/semmle/javascript/AMD.qll b/javascript/ql/lib/semmle/javascript/AMD.qll index 3239dba9026..4828aff27cc 100644 --- a/javascript/ql/lib/semmle/javascript/AMD.qll +++ b/javascript/ql/lib/semmle/javascript/AMD.qll @@ -61,8 +61,14 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range result = this.getArgument(1) } + /** DEPRECATED. Use `getDependencyExpr` instead. */ + deprecated PathExpr getDependency(int i) { result = this.getDependencyExpr(i) } + + /** DEPRECATED. Use `getADependencyExpr` instead. */ + deprecated PathExpr getADependency() { result = this.getADependencyExpr() } + /** Gets the `i`th dependency of this module definition. */ - PathExpr getDependency(int i) { + Expr getDependencyExpr(int i) { exists(Expr expr | expr = this.getDependencies().getElement(i) and not isPseudoDependency(expr.getStringValue()) and @@ -71,8 +77,8 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range } /** Gets a dependency of this module definition. */ - PathExpr getADependency() { - result = this.getDependency(_) or + Expr getADependencyExpr() { + result = this.getDependencyExpr(_) or result = this.getARequireCall().getAnArgument() } @@ -233,7 +239,7 @@ private class AmdDependencyPath extends PathExprCandidate { } /** A constant path element appearing in an AMD dependency expression. */ -private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString { +deprecated private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString { ConstantAmdDependencyPathElement() { this = any(AmdDependencyPath amd).getAPart() } override string getValue() { result = this.getStringValue() } @@ -261,11 +267,13 @@ private predicate amdModuleTopLevel(AmdModuleDefinition def, TopLevel tl) { * An AMD dependency, viewed as an import. */ private class AmdDependencyImport extends Import { - AmdDependencyImport() { this = any(AmdModuleDefinition def).getADependency() } + AmdDependencyImport() { this = any(AmdModuleDefinition def).getADependencyExpr() } - override Module getEnclosingModule() { this = result.(AmdModule).getDefine().getADependency() } + override Module getEnclosingModule() { + this = result.(AmdModule).getDefine().getADependencyExpr() + } - override PathExpr getImportedPath() { result = this } + override Expr getImportedPathExpr() { result = this } /** * Gets a file that looks like it might be the target of this import. @@ -274,7 +282,7 @@ private class AmdDependencyImport extends Import { * adding well-known JavaScript file extensions like `.js`. */ private File guessTarget() { - exists(PathString imported, string abspath, string dirname, string basename | + exists(FilePath imported, string abspath, string dirname, string basename | this.targetCandidate(result, abspath, imported, dirname, basename) | abspath.regexpMatch(".*/\\Q" + imported + "\\E") @@ -296,9 +304,9 @@ private class AmdDependencyImport extends Import { * `dirname` and `basename` to the dirname and basename (respectively) of `imported`. */ private predicate targetCandidate( - File f, string abspath, PathString imported, string dirname, string basename + File f, string abspath, FilePath imported, string dirname, string basename ) { - imported = this.getImportedPath().getValue() and + imported = this.getImportedPathString() and f.getStem() = imported.getStem() and f.getAbsolutePath() = abspath and dirname = imported.getDirName() and diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll index 488c6909984..379403eb0ee 100644 --- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll @@ -92,13 +92,13 @@ private predicate hasDefaultExport(ES2015Module mod) { class ImportDeclaration extends Stmt, Import, @import_declaration { override ES2015Module getEnclosingModule() { result = this.getTopLevel() } - override PathExpr getImportedPath() { result = this.getChildExpr(-1) } - /** * INTERNAL USE ONLY. DO NOT USE. */ string getRawImportPath() { result = this.getChildExpr(-1).getStringValue() } + override Expr getImportedPathExpr() { result = this.getChildExpr(-1) } + /** * Gets the object literal passed as part of the `with` (or `assert`) clause in this import declaration. * @@ -155,7 +155,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration { } /** A literal path expression appearing in an `import` declaration. */ -private class LiteralImportPath extends PathExpr, ConstantString { +deprecated private class LiteralImportPath extends PathExpr, ConstantString { LiteralImportPath() { exists(ImportDeclaration req | this = req.getChildExpr(-1)) } override string getValue() { result = this.getStringValue() } @@ -736,7 +736,7 @@ abstract class ReExportDeclaration extends ExportDeclaration { } /** A literal path expression appearing in a re-export declaration. */ -private class LiteralReExportPath extends PathExpr, ConstantString { +deprecated private class LiteralReExportPath extends PathExpr, ConstantString { LiteralReExportPath() { exists(ReExportDeclaration bred | this = bred.getImportedPath()) } override string getValue() { result = this.getStringValue() } diff --git a/javascript/ql/lib/semmle/javascript/Expr.qll b/javascript/ql/lib/semmle/javascript/Expr.qll index e8ec55f0174..8695c893f81 100644 --- a/javascript/ql/lib/semmle/javascript/Expr.qll +++ b/javascript/ql/lib/semmle/javascript/Expr.qll @@ -2821,7 +2821,7 @@ class DynamicImportExpr extends @dynamic_import, Expr, Import { result = this.getSource().getFirstControlFlowNode() } - override PathExpr getImportedPath() { result = this.getSource() } + override Expr getImportedPathExpr() { result = this.getSource() } /** * Gets the second "argument" to the import expression, that is, the `Y` in `import(X, Y)`. @@ -2852,7 +2852,7 @@ class DynamicImportExpr extends @dynamic_import, Expr, Import { } /** A literal path expression appearing in a dynamic import. */ -private class LiteralDynamicImportPath extends PathExpr, ConstantString { +deprecated private class LiteralDynamicImportPath extends PathExpr, ConstantString { LiteralDynamicImportPath() { exists(DynamicImportExpr di | this.getParentExpr*() = di.getSource()) } diff --git a/javascript/ql/lib/semmle/javascript/HTML.qll b/javascript/ql/lib/semmle/javascript/HTML.qll index 01ce54cef52..43b66db459f 100644 --- a/javascript/ql/lib/semmle/javascript/HTML.qll +++ b/javascript/ql/lib/semmle/javascript/HTML.qll @@ -214,7 +214,7 @@ module HTML { result = path.regexpCapture("file://(/.*)", 1) or not path.regexpMatch("(\\w+:)?//.*") and - result = this.getSourcePath().(ScriptSrcPath).resolve(this.getSearchRoot()).toString() + result = ResolveScriptSrc::resolve(this.getSearchRoot(), this.getSourcePath()).toString() ) } @@ -274,10 +274,16 @@ module HTML { ) } + private module ResolverConfig implements Folder::ResolveSig { + predicate shouldResolve(Container base, string path) { scriptSrc(path, base) } + } + + private module ResolveScriptSrc = Folder::Resolve; + /** * A path string arising from the `src` attribute of a `script` tag. */ - private class ScriptSrcPath extends PathString { + deprecated private class ScriptSrcPath extends PathString { ScriptSrcPath() { scriptSrc(this, _) } override Folder getARootFolder() { scriptSrc(this, result) } diff --git a/javascript/ql/lib/semmle/javascript/Modules.qll b/javascript/ql/lib/semmle/javascript/Modules.qll index 6dc27f980b8..1949abe4cdc 100644 --- a/javascript/ql/lib/semmle/javascript/Modules.qll +++ b/javascript/ql/lib/semmle/javascript/Modules.qll @@ -69,7 +69,7 @@ abstract class Module extends TopLevel { * This predicate is not part of the public API, it is only exposed to allow * overriding by subclasses. */ - predicate searchRoot(PathExpr path, Folder searchRoot, int priority) { + deprecated predicate searchRoot(PathExpr path, Folder searchRoot, int priority) { path.getEnclosingModule() = this and priority = 0 and exists(string v | v = path.getValue() | @@ -90,7 +90,7 @@ abstract class Module extends TopLevel { * resolves to a folder containing a main module (such as `index.js`), then * that file is the result. */ - File resolve(PathExpr path) { + deprecated File resolve(PathExpr path) { path.getEnclosingModule() = this and ( // handle the case where the import path is complete @@ -123,8 +123,14 @@ abstract class Import extends AstNode { /** Gets the module in which this import appears. */ abstract Module getEnclosingModule(); + /** DEPRECATED. Use `getImportedPathExpr` instead. */ + deprecated PathExpr getImportedPath() { result = this.getImportedPathExpr() } + /** Gets the (unresolved) path that this import refers to. */ - abstract PathExpr getImportedPath(); + abstract Expr getImportedPathExpr(); + + /** Gets the imported path as a string. */ + final string getImportedPathString() { result = this.getImportedPathExpr().getStringValue() } /** * Gets an externs module the path of this import resolves to. @@ -133,7 +139,7 @@ abstract class Import extends AstNode { * path is assumed to be a possible target of the import. */ Module resolveExternsImport() { - result.isExterns() and result.getName() = this.getImportedPath().getValue() + result.isExterns() and result.getName() = this.getImportedPathString() } /** @@ -144,7 +150,7 @@ abstract class Import extends AstNode { /** * Gets the module the path of this import resolves to. */ - File getTargetFile() { result = ImportPathResolver::resolveExpr(this.getImportedPath()) } + File getTargetFile() { result = ImportPathResolver::resolveExpr(this.getImportedPathExpr()) } /** * DEPRECATED. Use `getImportedModule()` instead. diff --git a/javascript/ql/lib/semmle/javascript/NodeJS.qll b/javascript/ql/lib/semmle/javascript/NodeJS.qll index 1be6ad9902f..175991ffa5e 100644 --- a/javascript/ql/lib/semmle/javascript/NodeJS.qll +++ b/javascript/ql/lib/semmle/javascript/NodeJS.qll @@ -146,7 +146,7 @@ class NodeModule extends Module { ) } - override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) { + deprecated override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) { path.getEnclosingModule() = this and exists(string pathval | pathval = path.getValue() | // paths starting with `./` or `../` are resolved relative to the importing @@ -236,11 +236,6 @@ private class RequireVariable extends Variable { } } -/** - * Holds if module `m` is in file `f`. - */ -private predicate moduleInFile(Module m, File f) { m.getFile() = f } - private predicate isModuleModule(EarlyStageNode nd) { exists(ImportDeclaration imp | imp.getRawImportPath() = "module" | nd = TDestructuredModuleImportNode(imp) @@ -328,24 +323,17 @@ private predicate isRequire(EarlyStageNode nd) { class Require extends CallExpr, Import { Require() { isRequire(TValueNode(this.getCallee())) } - override PathExpr getImportedPath() { result = this.getArgument(0) } + override Expr getImportedPathExpr() { result = this.getArgument(0) } override Module getEnclosingModule() { this = result.getAnImport() } - override Module resolveImportedPath() { - moduleInFile(result, this.load(min(int prio | moduleInFile(_, this.load(prio))))) - or - not moduleInFile(_, this.load(_)) and - result = Import.super.resolveImportedPath() - } - /** * Gets the file that is imported by this `require`. * * The result can be a JavaScript file, a JSON file or a `.node` file. * Externs files are not treated differently from other files by this predicate. */ - File getImportedFile() { result = this.load(min(int prio | exists(this.load(prio)))) } + deprecated File getImportedFile() { result = this.load(min(int prio | exists(this.load(prio)))) } /** * Gets the file that this `require` refers to (which may not be a JavaScript file), @@ -402,8 +390,8 @@ class Require extends CallExpr, Import { * predicate `tryExtensions` that handles the repeated distinction between * `.js`, `.json` and `.node`. */ - private File load(int priority) { - exists(int r | this.getEnclosingModule().searchRoot(this.getImportedPath(), _, r) | + deprecated private File load(int priority) { + exists(int r | this.getEnclosingModule().searchRoot(this.getImportedPathExpr(), _, r) | result = loadAsFile(this, r, priority - prioritiesPerCandidate() * r) or result = loadAsDirectory(this, r, @@ -415,7 +403,7 @@ class Require extends CallExpr, Import { } /** An argument to `require` or `require.resolve`, considered as a path expression. */ -private class RequirePath extends PathExprCandidate { +deprecated private class RequirePath extends PathExprCandidate { RequirePath() { this = any(Require req).getArgument(0) or @@ -428,14 +416,14 @@ private class RequirePath extends PathExprCandidate { } /** A constant path element appearing in a call to `require` or `require.resolve`. */ -private class ConstantRequirePathElement extends PathExpr, ConstantString { +deprecated private class ConstantRequirePathElement extends PathExpr, ConstantString { ConstantRequirePathElement() { this = any(RequirePath rp).getAPart() } override string getValue() { result = this.getStringValue() } } /** A `__dirname` path expression. */ -private class DirNamePath extends PathExpr, VarAccess { +deprecated private class DirNamePath extends PathExpr, VarAccess { DirNamePath() { this.getName() = "__dirname" and this.getVariable().getScope() instanceof ModuleScope @@ -445,7 +433,7 @@ private class DirNamePath extends PathExpr, VarAccess { } /** A `__filename` path expression. */ -private class FileNamePath extends PathExpr, VarAccess { +deprecated private class FileNamePath extends PathExpr, VarAccess { FileNamePath() { this.getName() = "__filename" and this.getVariable().getScope() instanceof ModuleScope @@ -458,7 +446,7 @@ private class FileNamePath extends PathExpr, VarAccess { * A path expression of the form `path.join(p, "...")` where * `p` is also a path expression. */ -private class JoinedPath extends PathExpr, @call_expr { +deprecated private class JoinedPath extends PathExpr, @call_expr { JoinedPath() { exists(MethodCallExpr call | call = this | call.getReceiver().(VarAccess).getName() = "path" and diff --git a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll index 03b5bf93fb0..467e14aa74f 100644 --- a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll +++ b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll @@ -45,7 +45,7 @@ int numberOfExtensions() { result = count(getFileExtensionPriority(_)) } * Gets the resolution target with the given `priority` of `req` * when resolved from the root with priority `rootPriority`. */ -File loadAsFile(Require req, int rootPriority, int priority) { +deprecated File loadAsFile(Require req, int rootPriority, int priority) { exists(PathExpr path | path = req.getImportedPath() | result = path.resolve(rootPriority) and priority = 0 or @@ -60,7 +60,7 @@ File loadAsFile(Require req, int rootPriority, int priority) { * with the given `priority` of `req` when resolved from the root with * priority `rootPriority`. */ -File loadAsDirectory(Require req, int rootPriority, int priority) { +deprecated File loadAsDirectory(Require req, int rootPriority, int priority) { exists(Folder dir | dir = req.getImportedPath().resolve(rootPriority) | result = resolveMainModule(dir.(NpmPackage).getPackageJson(), priority, ".") or result = tryExtensions(dir, "index", priority - (numberOfExtensions() + 1)) @@ -99,7 +99,7 @@ private string getStem(string name) { * Gets a file that a main module from `pkg` exported as `mainPath` with the given `priority`. * `mainPath` is "." if it's the main module of the package. */ -private File resolveMainPath(PackageJson pkg, string mainPath, int priority) { +deprecated private File resolveMainPath(PackageJson pkg, string mainPath, int priority) { exists(PathExpr main | main = MainModulePath::of(pkg, mainPath) | result = main.resolve() and priority = 0 or @@ -132,7 +132,7 @@ private File resolveMainPath(PackageJson pkg, string mainPath, int priority) { /** * Gets the main module described by `pkg` with the given `priority`. */ -File resolveMainModule(PackageJson pkg, int priority, string exportPath) { +deprecated File resolveMainModule(PackageJson pkg, int priority, string exportPath) { result = resolveMainPath(pkg, exportPath, priority) or exportPath = "." and @@ -178,7 +178,7 @@ private string getASrcFolderName() { result = ["ts", "js", "src", "lib"] } * A JSON string in a `package.json` file specifying the path of one of the exported * modules of the package. */ -class MainModulePath extends PathExpr, @json_string { +deprecated class MainModulePath extends PathExpr, @json_string { PackageJson pkg; MainModulePath() { @@ -228,7 +228,7 @@ private string getExportRelativePath(JsonValue part) { result.matches(".%") } -module MainModulePath { +deprecated module MainModulePath { /** Gets the path to the main entry point of `pkg`. */ MainModulePath of(PackageJson pkg) { result = of(pkg, ".") } @@ -244,7 +244,7 @@ module MainModulePath { * These files are often imported directly from a client when a "main" module is not specified. * For performance reasons this only exists if there is no "main" field in the `package.json` file. */ -private class FilesPath extends PathExpr, @json_string { +deprecated private class FilesPath extends PathExpr, @json_string { PackageJson pkg; FilesPath() { @@ -263,7 +263,7 @@ private class FilesPath extends PathExpr, @json_string { } } -private module FilesPath { +deprecated private module FilesPath { FilesPath of(PackageJson pkg) { result.getPackageJson() = pkg } } @@ -271,7 +271,7 @@ private module FilesPath { * A JSON string in a `package.json` file specifying the path of the * TypeScript typings entry point. */ -class TypingsModulePathString extends PathString { +deprecated class TypingsModulePathString extends PathString { PackageJson pkg; TypingsModulePathString() { @@ -288,7 +288,7 @@ class TypingsModulePathString extends PathString { } /** Companion module to the `TypingsModulePathString` class. */ -module TypingsModulePathString { +deprecated module TypingsModulePathString { /** Get the typings path for the given `package.json` file. */ TypingsModulePathString of(PackageJson pkg) { result.getPackageJson() = pkg } } diff --git a/javascript/ql/lib/semmle/javascript/Paths.qll b/javascript/ql/lib/semmle/javascript/Paths.qll index 981e43f8966..38c24a1b8e9 100644 --- a/javascript/ql/lib/semmle/javascript/Paths.qll +++ b/javascript/ql/lib/semmle/javascript/Paths.qll @@ -9,13 +9,13 @@ private import semmle.javascript.dataflow.internal.DataFlowNode /** * Internal representation of paths as lists of components. */ -private newtype TPath = +deprecated private newtype TPath = /** A root path. */ TRootPath(string root) { root = any(Folder f | not exists(f.getParentContainer())).getAbsolutePath() } or /** A path of the form `/`. */ - TConsPath(Path parent, string component) { + deprecated TConsPath(Path parent, string component) { // make sure we can represent paths of files in snapshot exists(Folder f | f = parent.getContainer() | exists(f.getChildContainer(component))) or @@ -32,7 +32,7 @@ private newtype TPath = * Gets a textual representation of path `p` using slashes as delimiters; * the empty path is represented as the empty string `""`. */ -private string pp(TPath p) { +deprecated private string pp(TPath p) { p = TRootPath(result + "/") or exists(TPath parent, string component | p = TConsPath(parent, component) | @@ -45,7 +45,7 @@ private string pp(TPath p) { * which may (but does not have to) correspond to a file or folder included * in the snapshot. */ -class Path extends TPath { +deprecated class Path extends TPath { /** * Gets the file or folder referred to by this path, if it exists. */ @@ -60,14 +60,14 @@ class Path extends TPath { /** * The empty path, which refers to the file system root. */ -private class RootPath extends Path, TRootPath { +deprecated private class RootPath extends Path, TRootPath { override string toString() { this = TRootPath(result) } } /** * A non-empty path of the form `/`. */ -private class ConsPath extends Path, TConsPath { +deprecated private class ConsPath extends Path, TConsPath { /** Gets the parent path of this path. */ Path getParent() { this = TConsPath(result, _) } @@ -170,7 +170,7 @@ class FilePath extends string { * usually resolved relative to the module's folder, with a default * lookup path as the fallback. */ -abstract class PathString extends FilePath { +abstract deprecated class PathString extends FilePath { bindingset[this] PathString() { any() } @@ -196,7 +196,7 @@ abstract class PathString extends FilePath { * components of this path refers to when resolved relative to the * given `root` folder. */ -private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) { +deprecated private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) { n = 0 and result.getContainer() = root and root = p.getARootFolder() and inTS = false or exists(Path base, string next | next = getComponent(p, n - 1, base, root, inTS) | @@ -225,7 +225,7 @@ private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) { * Supports that the root directory might be compiled output from TypeScript. * `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`. */ -private string getComponent(PathString str, int n, Path base, Folder root, boolean inTS) { +deprecated private string getComponent(PathString str, int n, Path base, Folder root, boolean inTS) { exists(boolean prevTS | base = resolveUpTo(str, n, root, prevTS) and ( @@ -248,7 +248,7 @@ private string getComponent(PathString str, int n, Path base, Folder root, boole /** * Predicates for resolving imports to compiled TypeScript. */ -private module TypeScriptOutDir { +deprecated private module TypeScriptOutDir { /** * Gets a folder of TypeScript files that is compiled to JavaScript files in `outdir` relative to a `parent`. */ @@ -340,7 +340,7 @@ private module TypeScriptOutDir { * as their highest-priority root, with default library paths as additional roots * of lower priority. */ -abstract class PathExpr extends Locatable { +abstract deprecated class PathExpr extends Locatable { /** Gets the (unresolved) path represented by this expression. */ abstract string getValue(); @@ -413,7 +413,7 @@ abstract class PathExpr extends Locatable { } /** A path string derived from a path expression. */ -private class PathExprString extends PathString { +deprecated private class PathExprString extends PathString { PathExprString() { this = any(PathExpr pe).getValue() } override Folder getARootFolder() { @@ -422,13 +422,13 @@ private class PathExprString extends PathString { } pragma[nomagic] -private EarlyStageNode getAPathExprAlias(PathExpr expr) { +deprecated private EarlyStageNode getAPathExprAlias(PathExpr expr) { DataFlow::Impl::earlyStageImmediateFlowStep(TValueNode(expr), result) or DataFlow::Impl::earlyStageImmediateFlowStep(getAPathExprAlias(expr), result) } -private class PathExprFromAlias extends PathExpr { +deprecated private class PathExprFromAlias extends PathExpr { private PathExpr other; PathExprFromAlias() { TValueNode(this) = getAPathExprAlias(other) } @@ -444,7 +444,7 @@ private class PathExprFromAlias extends PathExpr { * A path expression of the form `p + q`, where both `p` and `q` * are path expressions. */ -private class ConcatPath extends PathExpr { +deprecated private class ConcatPath extends PathExpr { ConcatPath() { exists(AddExpr add | this = add | add.getLeftOperand() instanceof PathExpr and diff --git a/javascript/ql/lib/semmle/javascript/TypeScript.qll b/javascript/ql/lib/semmle/javascript/TypeScript.qll index d8b6b63a366..4be331ed6a5 100644 --- a/javascript/ql/lib/semmle/javascript/TypeScript.qll +++ b/javascript/ql/lib/semmle/javascript/TypeScript.qll @@ -207,7 +207,7 @@ class ExternalModuleReference extends Expr, Import, @external_module_reference { /** Gets the expression specifying the module. */ Expr getExpression() { result = this.getChildExpr(0) } - override PathExpr getImportedPath() { result = this.getExpression() } + override Expr getImportedPathExpr() { result = this.getExpression() } override Module getEnclosingModule() { result = this.getTopLevel() } @@ -221,7 +221,7 @@ class ExternalModuleReference extends Expr, Import, @external_module_reference { } /** A literal path expression appearing in an external module reference. */ -private class LiteralExternalModulePath extends PathExpr, ConstantString { +deprecated private class LiteralExternalModulePath extends PathExpr, ConstantString { LiteralExternalModulePath() { exists(ExternalModuleReference emr | this.getParentExpr*() = emr.getExpression()) } @@ -743,7 +743,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { * For non-relative imports, it is the import path itself. */ private string getImportName(Import imprt) { - exists(string path | path = imprt.getImportedPath().getValue() | + exists(string path | path = imprt.getImportedPathString() | if path.regexpMatch("[./].*") then result = imprt.getImportedModule().getFile().getRelativePath() else result = path @@ -1731,7 +1731,7 @@ class TSGlobalDeclImport extends DataFlow::ModuleImportNode::Range { pkg = tt.getExpressionName() and // then, check pkg is imported as "import * as pkg from path" i.getLocal().getVariable() = pkg.getVariable() and - path = i.getImportedPath().getValue() and + path = i.getImportedPathString() and // finally, "this" needs to be a reference to gv this = DataFlow::exprNode(gv.getAnAccess()) ) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll index 2e231383522..7f9b375f534 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll @@ -738,7 +738,7 @@ module ModuleImportNode { DefaultRange() { exists(Import i | this = i.getImportedModuleNode() and - i.getImportedPath().getValue() = path + i.getImportedPathString() = path ) or // AMD require diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll index bb18ff07846..3134d76d8f1 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll @@ -25,7 +25,7 @@ private class AnalyzedImportSpecifier extends AnalyzedVarDef, @import_specifier override predicate isIncomplete(DataFlow::Incompleteness cause) { // mark as incomplete if the import could rely on the lookup path - mayDependOnLookupPath(id.getImportedPath().getValue()) and + mayDependOnLookupPath(id.getImportedPathString()) and cause = "import" or // mark as incomplete if we cannot fully analyze this import @@ -260,7 +260,7 @@ private class AnalyzedAmdImport extends AnalyzedPropertyRead, DataFlow::Node { Module required; AnalyzedAmdImport() { - exists(AmdModule amd, PathExpr dep | + exists(AmdModule amd, Expr dep | exists(Parameter p | amd.getDefine().dependencyParameter(dep, p) and this = DataFlow::parameterNode(p) diff --git a/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll b/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll index 948b8c9aff2..8ca2add4a27 100644 --- a/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll +++ b/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll @@ -151,6 +151,11 @@ class ExternalNpmDependency extends NpmDependency { } } +pragma[nomagic] +private string getPackagePrefix(Import i) { + result = i.getImportedPathString().(FilePath).getPackagePrefix() +} + /** * Holds if import `i` may refer to the declared dependency `dep` of package `pkg`, * where the result value is the nesting depth of the file containing `i` within `pkg`. @@ -159,7 +164,7 @@ private int importsDependency(Import i, NpmPackage pkg, NpmDependency dep) { exists(string name | dep = pkg.getPackageJson().getADependenciesObject(_).getPropValue(name) and not exists(i.getImportedModule()) and - i.getImportedPath().getComponent(0) = name and + name = getPackagePrefix(i) and i.getEnclosingModule() = pkg.getAModule() and result = distance(pkg, i.getFile()) ) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll index 1a6d11cd753..a85a0a7813c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll @@ -32,7 +32,7 @@ pragma[nomagic] private predicate isAngularTopLevel(TopLevel tl) { exists(Import imprt | imprt.getTopLevel() = tl and - imprt.getImportedPath().getValue() = "angular" + imprt.getImportedPathString() = "angular" ) or exists(GlobalVarAccess global | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll index 52315013d17..4b3f70b7772 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll @@ -139,7 +139,7 @@ module Babel { /** * An import path expression that may be transformed by `babel-plugin-root-import`. */ - private class BabelRootTransformedPathExpr extends PathExpr, Expr { + deprecated private class BabelRootTransformedPathExpr extends PathExpr, Expr { RootImportConfig plugin; string prefix; string mappedPrefix; @@ -167,7 +167,7 @@ module Babel { /** * An import path transformed by `babel-plugin-root-import`. */ - private class BabelRootTransformedPath extends PathString { + deprecated private class BabelRootTransformedPath extends PathString { BabelRootTransformedPathExpr pathExpr; BabelRootTransformedPath() { this = pathExpr.getValue() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll b/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll index 15240ce5cc0..2c460fcc345 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll @@ -36,7 +36,7 @@ module LazyCache { override Module getEnclosingModule() { result = this.getTopLevel() } - override PathExpr getImportedPath() { result = this.getArgument(0) } + override Expr getImportedPathExpr() { result = this.getArgument(0) } private LazyCacheVariable getVariable() { result = cache } @@ -58,7 +58,7 @@ module LazyCache { } /** A constant path element appearing in a call to a lazy-cache object. */ - private class LazyCachePathExpr extends PathExpr, ConstantString { + deprecated private class LazyCachePathExpr extends PathExpr, ConstantString { LazyCachePathExpr() { this = any(LazyCacheImport rp).getArgument(0) } override string getValue() { result = this.getStringValue() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/React.qll b/javascript/ql/lib/semmle/javascript/frameworks/React.qll index 5ac0e419e26..4d126b88829 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/React.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/React.qll @@ -733,7 +733,7 @@ private class ReactRouterSource extends ClientSideRemoteFlowSource { * Holds if `mod` transitively depends on `react-router-dom`. */ private predicate dependsOnReactRouter(Module mod) { - mod.getAnImport().getImportedPath().getValue() = "react-router-dom" + mod.getAnImport().getImportedPathString() = "react-router-dom" or dependsOnReactRouter(mod.getAnImportedModule()) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index faba601df52..2465ec66820 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -442,7 +442,7 @@ module Vue { override DataFlow::SourceNode getASource() { exists(Import imprt | - imprt.getImportedPath().resolve() instanceof VueFile and + imprt.getTargetFile() instanceof VueFile and result = imprt.getImportedModuleNode() ) } @@ -494,7 +494,7 @@ module Vue { // There is no explicit `new Vue()` call in .vue files, so instead get all the imports // of the .vue file. exists(Import imprt | - imprt.getImportedPath().resolve() = file and + imprt.getTargetFile() = file and result.asSource() = imprt.getImportedModuleNode() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index 1f51af3efda..ee579d2c9db 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -58,7 +58,7 @@ predicate parseTypeString(string rawType, string package, string qualifiedName) predicate isPackageUsed(string package) { package = "global" or - package = any(JS::Import imp).getImportedPath().getValue() + package = any(JS::Import imp).getImportedPathString() or any(JS::TypeName t).hasQualifiedName(package, _) or diff --git a/javascript/ql/lib/semmle/javascript/internal/paths/PathExprResolver.qll b/javascript/ql/lib/semmle/javascript/internal/paths/PathExprResolver.qll index 853939dfe5f..05b8a620717 100644 --- a/javascript/ql/lib/semmle/javascript/internal/paths/PathExprResolver.qll +++ b/javascript/ql/lib/semmle/javascript/internal/paths/PathExprResolver.qll @@ -240,7 +240,7 @@ module ResolveExpr { } private predicate isImportPathExpr(Expr e) { - e = any(Import imprt).getImportedPath() + e = any(Import imprt).getImportedPathExpr() or e = any(ReExportDeclaration decl).getImportedPath() } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll index 7a6575f8647..f59c7b78e36 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll @@ -88,7 +88,7 @@ module ExternalApiUsedWithUntrustedData { not path instanceof SafeExternalApiPackage and // Exclude paths that can be resolved to a file in the project not exists(Import imprt | - imprt.getImportedPath().getValue() = path and exists(imprt.getImportedModule()) + imprt.getImportedPathString() = path and exists(imprt.getImportedModule()) ) ) or diff --git a/javascript/ql/src/Declarations/UnstableCyclicImport.ql b/javascript/ql/src/Declarations/UnstableCyclicImport.ql index 56f418c40a2..4ef4d8ca946 100644 --- a/javascript/ql/src/Declarations/UnstableCyclicImport.ql +++ b/javascript/ql/src/Declarations/UnstableCyclicImport.ql @@ -152,4 +152,4 @@ where cycleAlert(mod, import_, importedModule, access) select access, access.getName() + " is uninitialized if $@ is loaded first in the cyclic import:" + " " + repr(import_) + " => " + min(pathToModule(importedModule, mod, _)) + " => " + repr(import_) + - ".", import_.getImportedPath(), importedModule.getName() + ".", import_.getImportedPathExpr(), importedModule.getName() diff --git a/javascript/ql/src/NodeJS/UnresolvableImport.ql b/javascript/ql/src/NodeJS/UnresolvableImport.ql index 16feba14348..28c46229cf6 100644 --- a/javascript/ql/src/NodeJS/UnresolvableImport.ql +++ b/javascript/ql/src/NodeJS/UnresolvableImport.ql @@ -24,7 +24,7 @@ PackageJson getClosestPackageJson(Folder f) { from Require r, string path, string mod where - path = r.getImportedPath().getValue() and + path = r.getImportedPathString() and // the imported module is the initial segment of the path, up to // `/` or the end of the string, whichever comes first; we exclude // local paths starting with `.` or `/`, since they might refer to files diff --git a/javascript/ql/src/NodeJS/UnusedDependency.ql b/javascript/ql/src/NodeJS/UnusedDependency.ql index 45bbde37294..438f589cac9 100644 --- a/javascript/ql/src/NodeJS/UnusedDependency.ql +++ b/javascript/ql/src/NodeJS/UnusedDependency.ql @@ -23,7 +23,17 @@ predicate declaresDependency(NpmPackage pkg, string name, JsonValue dep) { /** * Gets a path expression in a module belonging to `pkg`. */ -PathExpr getAPathExpr(NpmPackage pkg) { result.getEnclosingModule() = pkg.getAModule() } +Expr getAPathExpr(NpmPackage pkg) { + exists(Import imprt | + result = imprt.getImportedPathExpr() and + pkg.getAModule() = imprt.getEnclosingModule() + ) + or + exists(ReExportDeclaration decl | + result = decl.getImportedPath() and + pkg.getAModule() = decl.getEnclosingModule() + ) +} /** * Gets a URL-valued attribute in a module or HTML file belonging to `pkg`. @@ -56,9 +66,8 @@ predicate usesDependency(NpmPackage pkg, string name) { ( // there is a path expression (e.g., in a `require` or `import`) that // references `pkg` - exists(PathExpr path | path = getAPathExpr(pkg) | - // check whether the path is `name` or starts with `name/`, ignoring a prefix that ends with '!' (example: "scriptloader!moment") - path.getValue().regexpMatch("(.*!)?\\Q" + name + "\\E(/.*)?") + exists(Expr path | path = getAPathExpr(pkg) | + path.getStringValue().(FilePath).getPackagePrefix() = name ) or // there is an HTML URL attribute that may reference `pkg` diff --git a/javascript/ql/test/library-tests/AMD/tests.ql b/javascript/ql/test/library-tests/AMD/tests.ql index 956ad6d040e..e71ae089f2d 100644 --- a/javascript/ql/test/library-tests/AMD/tests.ql +++ b/javascript/ql/test/library-tests/AMD/tests.ql @@ -18,8 +18,8 @@ query predicate amdModuleDefinition(AmdModuleDefinition mod, DataFlow::SourceNod mod.getFactoryNode() = factory } -query predicate amdModuleDependencies(AmdModuleDefinition mod, PathExpr dependency) { - dependency = mod.getADependency() +query predicate amdModuleDependencies(AmdModuleDefinition mod, Expr dependency) { + dependency = mod.getADependencyExpr() } query predicate amdModuleExportedSymbol(AmdModule m, string sym) { sym = m.getAnExportedSymbol() } diff --git a/javascript/ql/test/library-tests/Modules/tests.ql b/javascript/ql/test/library-tests/Modules/tests.ql index ca0e196f488..efe72731a7b 100644 --- a/javascript/ql/test/library-tests/Modules/tests.ql +++ b/javascript/ql/test/library-tests/Modules/tests.ql @@ -22,8 +22,8 @@ query predicate test_ImportNamespaceSpecifier(ImportNamespaceSpecifier ins) { an query predicate test_ImportSpecifiers(ImportSpecifier is, VarDecl res) { res = is.getLocal() } -query predicate test_Imports(ImportDeclaration id, PathExpr res0, int res1) { - res0 = id.getImportedPath() and res1 = count(id.getASpecifier()) +query predicate test_Imports(ImportDeclaration id, Expr res0, int res1) { + res0 = id.getImportedPathExpr() and res1 = count(id.getASpecifier()) } query predicate test_Module_exports(Module m, string name, DataFlow::Node exportValue) { diff --git a/javascript/ql/test/library-tests/NodeJS/tests.ql b/javascript/ql/test/library-tests/NodeJS/tests.ql index eca3a132d26..f5e0f58dc46 100644 --- a/javascript/ql/test/library-tests/NodeJS/tests.ql +++ b/javascript/ql/test/library-tests/NodeJS/tests.ql @@ -26,7 +26,7 @@ query predicate require(Require r) { any() } query predicate requireImport(Require r, string path, Module mod) { exists(string fullpath, string prefix | - fullpath = r.getImportedPath().getValue() and + fullpath = r.getImportedPathString() and sourceLocationPrefix(prefix) and path = fullpath.replaceAll(prefix, "") and mod = r.getImportedModule() diff --git a/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql b/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql index 839ba2c560b..5de6c99c748 100644 --- a/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql +++ b/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql @@ -5,7 +5,7 @@ query predicate dataFlowModuleImports(string name, DataFlow::SourceNode imp) { } query predicate imports(Import imprt, string path, Module mod) { - path = imprt.getImportedPath().getValue() and + path = imprt.getImportedPathString() and mod = imprt.getImportedModule() } diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql index 3c5e8a7fd16..0c8a95e3b3b 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql @@ -1,4 +1,4 @@ import javascript from Import imprt -select imprt, imprt.getImportedPath().getValue(), imprt.getImportedModule() +select imprt, imprt.getImportedPathString(), imprt.getImportedModule()