From f7552a77c3aa24c0986a127893ba30634274453e Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 2 Jul 2020 12:42:31 +0100 Subject: [PATCH 1/6] JS: Add metric for number of types with qualified names --- .../ql/src/meta/types/TypesWithQualifiedName.ql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 javascript/ql/src/meta/types/TypesWithQualifiedName.ql diff --git a/javascript/ql/src/meta/types/TypesWithQualifiedName.ql b/javascript/ql/src/meta/types/TypesWithQualifiedName.ql new file mode 100644 index 00000000000..db23d2a807e --- /dev/null +++ b/javascript/ql/src/meta/types/TypesWithQualifiedName.ql @@ -0,0 +1,14 @@ +/** + * @name Types with qualified name + * @description The number of type annotations with a qualified name + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/types-with-qualified-name + */ + +import javascript +import meta.MetaMetrics + +select projectRoot(), count(TypeAnnotation t | t.hasQualifiedName(_) or t.hasQualifiedName(_, _)) From bfcc434a6136212e369229c0a9e4e4ea9a3856f6 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 2 Jul 2020 12:26:51 +0100 Subject: [PATCH 2/6] JS: Use both local and global names in hasQualifiedName --- .../ql/src/semmle/javascript/TypeScript.qll | 46 +++++++++++++++++++ .../HasQualifiedNameFallback/Test.expected | 14 ++++++ .../HasQualifiedNameFallback/Test.ql | 13 ++++++ .../HasQualifiedNameFallback/relative.ts | 4 ++ .../HasQualifiedNameFallback/tst.ts | 16 +++++++ 5 files changed, 93 insertions(+) create mode 100644 javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected create mode 100644 javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql create mode 100644 javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/relative.ts create mode 100644 javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/tst.ts diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 6e620ded255..69039052ad3 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -509,6 +509,15 @@ class LocalNamespaceName extends @local_namespace_name, LexicalName { /** Gets a use of this namespace in an export. */ ExportVarAccess getAnExportAccess() { namespacebind(result, this) } + /** + * Gets an access to a type member of this namespace alias, + * such as `http.ServerRequest` where `http` is a reference to this namespace. + */ + QualifiedTypeAccess getAMemberAccess(string member) { + result.getIdentifier().getName() = member and + result.getQualifier() = this.getAnAccess() + } + /** Gets an identifier that refers to this namespace name. */ Identifier getAnAccess() { namespacebind(result, this) } @@ -663,13 +672,50 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { override predicate hasQualifiedName(string globalName) { getTypeName().hasQualifiedName(globalName) + or + exists(LocalTypeAccess local | local = this | + not exists(local.getLocalTypeName()) and // Without a local type name, the type is looked up in the global scope. + globalName = local.getName() + ) } override predicate hasQualifiedName(string moduleName, string exportedName) { getTypeName().hasQualifiedName(moduleName, exportedName) + or + exists(ImportDeclaration imprt, ImportSpecifier spec | + moduleName = getImportName(imprt) and + spec = imprt.getASpecifier() + | + spec.getImportedName() = exportedName and + this = spec.getLocal().(TypeDecl).getLocalTypeName().getAnAccess() + or + spec instanceof ImportNamespaceSpecifier and + this = + spec.getLocal().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) + ) + or + exists(ImportEqualsDeclaration imprt | + moduleName = getImportName(imprt.getImportedEntity()) and + this = + imprt.getId().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) + ) } } +/** + * Gets a suitable name for the library imported by `import`. + * + * For relative imports, this is the snapshot-relative path to the imported module. + * For non-relative imports, it is the import path itself. + */ +private string getImportName(Import imprt) { + exists(string path | path = imprt.getImportedPath().getValue() | + if path.regexpMatch("[./].*") + then result = imprt.getImportedModule().getFile().getRelativePath() + else result = path + ) +} + /** An identifier that is used as part of a type, such as `Date`. */ class LocalTypeAccess extends @localtypeaccess, TypeAccess, Identifier, LexicalAccess { override predicate isStringy() { getName() = "String" } diff --git a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected new file mode 100644 index 00000000000..5da73c5cfe4 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected @@ -0,0 +1,14 @@ +hasQualifiedNameModule +| default-import | default | tst.ts:11:9:11:21 | DefaultImport | +| import-assign | Foo | tst.ts:10:9:10:15 | asn.Foo | +| library-tests/TypeScript/HasQualifiedNameFallback/tst.ts | ExportedClass | relative.ts:4:8:4:20 | ExportedClass | +| named-import | Name1 | tst.ts:7:9:7:13 | Name1 | +| named-import | Name1 | tst.ts:13:9:13:13 | Name1 | +| named-import | Name1 | tst.ts:13:9:13:21 | Name1 | +| named-import | Name2 | tst.ts:8:9:8:13 | Name2 | +| namespace-import | Foo | tst.ts:9:9:9:21 | namespace.Foo | +hasQualifiedNameGlobal +| UnresolvedName | tst.ts:12:9:12:22 | UnresolvedName | +paramExample +| tst.ts:7:5:7:6 | x1 | +| tst.ts:13:5:13:6 | x8 | diff --git a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql new file mode 100644 index 00000000000..2b63e171f1e --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql @@ -0,0 +1,13 @@ +import javascript + +query TypeAnnotation hasQualifiedNameModule(string moduleName, string member) { + result.hasQualifiedName(moduleName, member) +} + +query TypeAnnotation hasQualifiedNameGlobal(string globalName) { + result.hasQualifiedName(globalName) +} + +query Parameter paramExample() { + result.getTypeAnnotation().hasQualifiedName("named-import", "Name1") +} diff --git a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/relative.ts b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/relative.ts new file mode 100644 index 00000000000..4ac53932304 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/relative.ts @@ -0,0 +1,4 @@ +import * as foo from "./tst"; +import { ExportedClass } from "./tst"; + +var x: ExportedClass; diff --git a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/tst.ts b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/tst.ts new file mode 100644 index 00000000000..ffe4537da06 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/tst.ts @@ -0,0 +1,16 @@ +import * as namespace from "namespace-import"; +import { Name1, Name2 } from "named-import"; +import DefaultImport from "default-import"; +import asn = require("import-assign"); + +function foo( + x1: Name1, + x2: Name2, + x3: namespace.Foo, + x5: asn.Foo, + x6: DefaultImport, + x7: UnresolvedName, + x8: Name1 +) {} + +export class ExportedClass {}; From 393db73d0aaa5c66085619e67d46fb79cd97087f Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 30 Jul 2020 14:50:51 +0100 Subject: [PATCH 3/6] JS: Update test --- .../TSUnresolvedQualifiedName/QualifiedNames.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.expected b/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.expected index beb1f820742..04ee275d5fe 100644 --- a/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.expected +++ b/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.expected @@ -1 +1,2 @@ +| tst.ts:4:8:4:21 | UnresolvedType | unresolved | UnresolvedType | | tst.ts:5:8:5:19 | ResolvedType | resolved | ResolvedType | From c05f5c1bc26f3e97a9dd829cf984c5e7bd4c098b Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 2 Jul 2020 12:28:51 +0100 Subject: [PATCH 4/6] JS: Change note --- change-notes/1.26/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md index 4aca3117ee3..ea3935a92d8 100644 --- a/change-notes/1.26/analysis-javascript.md +++ b/change-notes/1.26/analysis-javascript.md @@ -32,3 +32,4 @@ ## Changes to libraries +* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction. From 0704be4d4154ff717cfae54798ea2cfd322d8846 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 4 Sep 2020 08:55:31 +0100 Subject: [PATCH 5/6] Update javascript/ql/src/semmle/javascript/TypeScript.qll Co-authored-by: Erik Krogh Kristensen --- javascript/ql/src/semmle/javascript/TypeScript.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 69039052ad3..622a7195e4e 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -697,7 +697,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { exists(ImportEqualsDeclaration imprt | moduleName = getImportName(imprt.getImportedEntity()) and this = - imprt.getId().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) + imprt.getIdentifier().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) ) } } From 961554eb6f3527e1e0ff43fdf4666ed02f4c4149 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 4 Sep 2020 10:42:26 +0100 Subject: [PATCH 6/6] JS: Autoformat --- javascript/ql/src/semmle/javascript/TypeScript.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 622a7195e4e..45e66e79de8 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -697,7 +697,11 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { exists(ImportEqualsDeclaration imprt | moduleName = getImportName(imprt.getImportedEntity()) and this = - imprt.getIdentifier().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) + imprt + .getIdentifier() + .(LocalNamespaceDecl) + .getLocalNamespaceName() + .getAMemberAccess(exportedName) ) } }