Skip to content

Commit 2172c48

Browse files
authored
Merge pull request #15380 from asgerf/js/endpoint-naming
JS: Add library for naming endpoints
2 parents 393251d + 6598a66 commit 2172c48

35 files changed

+730
-14
lines changed

javascript/ql/lib/semmle/javascript/ApiGraphs.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,9 @@ module API {
594594
exportedName = "" and
595595
result = getAModuleImportRaw(moduleName)
596596
}
597+
598+
/** Gets a sink node that represents instances of `cls`. */
599+
Node getClassInstance(DataFlow::ClassNode cls) { result = Impl::MkClassInstance(cls) }
597600
}
598601

599602
/**
@@ -1621,6 +1624,7 @@ private predicate exports(string m, DataFlow::Node rhs) {
16211624
exists(Module mod | mod = importableModule(m) |
16221625
rhs = mod.(AmdModule).getDefine().getModuleExpr().flow()
16231626
or
1627+
not mod.(ES2015Module).hasBothNamedAndDefaultExports() and
16241628
exports(m, "default", rhs)
16251629
or
16261630
exists(ExportAssignDeclaration assgn | assgn.getTopLevel() = mod |
@@ -1634,6 +1638,7 @@ private predicate exports(string m, DataFlow::Node rhs) {
16341638
/** Holds if module `m` exports `rhs` under the name `prop`. */
16351639
private predicate exports(string m, string prop, DataFlow::Node rhs) {
16361640
exists(ExportDeclaration exp | exp.getEnclosingModule() = importableModule(m) |
1641+
not exp.isTypeOnly() and
16371642
rhs = exp.getSourceNode(prop)
16381643
or
16391644
exists(Variable v |

javascript/ql/lib/semmle/javascript/Classes.qll

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,16 +516,37 @@ class MemberDeclaration extends @property, Documentable {
516516
*/
517517
predicate hasPublicKeyword() { has_public_keyword(this) }
518518

519+
/**
520+
* Holds if this member is considered private.
521+
*
522+
* This may occur in two cases:
523+
* - it is a TypeScript member annotated with the `private` keyword, or
524+
* - the member has a private name, such as `#foo`, referring to a private field in the class
525+
*/
526+
predicate isPrivate() { this.hasPrivateKeyword() or this.hasPrivateFieldName() }
527+
519528
/**
520529
* Holds if this is a TypeScript member annotated with the `private` keyword.
521530
*/
522-
predicate isPrivate() { has_private_keyword(this) }
531+
predicate hasPrivateKeyword() { has_private_keyword(this) }
523532

524533
/**
525534
* Holds if this is a TypeScript member annotated with the `protected` keyword.
526535
*/
527536
predicate isProtected() { has_protected_keyword(this) }
528537

538+
/**
539+
* Holds if the member has a private name, such as `#foo`, referring to a private field in the class.
540+
*
541+
* For example:
542+
* ```js
543+
* class Foo {
544+
* #method() {}
545+
* }
546+
* ```
547+
*/
548+
predicate hasPrivateFieldName() { this.getNameExpr().(Label).getName().charAt(0) = "#" }
549+
529550
/**
530551
* Gets the expression specifying the name of this member,
531552
* or nothing if this is a call signature.

javascript/ql/lib/semmle/javascript/ES2015Modules.qll

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ class ES2015Module extends Module {
3939
// modules are implicitly strict
4040
any()
4141
}
42+
43+
/**
44+
* Holds if this module contains both named and `default` exports.
45+
*
46+
* This is used to determine whether a default-import of the module should be reinterpreted
47+
* as a namespace-import, to accommodate the non-standard behavior implemented by some compilers.
48+
*
49+
* When a module has both named and `default` exports, the non-standard interpretation can lead to
50+
* ambiguities, so we only allow the standard interpretation in that case.
51+
*/
52+
predicate hasBothNamedAndDefaultExports() {
53+
hasNamedExports(this) and
54+
hasDefaultExport(this)
55+
}
4256
}
4357

4458
/**
@@ -64,17 +78,6 @@ private predicate hasDefaultExport(ES2015Module mod) {
6478
mod.getAnExport().(ExportNamedDeclaration).getASpecifier().getExportedName() = "default"
6579
}
6680

67-
/**
68-
* Holds if `mod` contains both named and `default` exports.
69-
*
70-
* This is used to determine whether a default-import of the module should be reinterpreted
71-
* as a namespace-import, to accommodate the non-standard behavior implemented by some compilers.
72-
*/
73-
private predicate hasBothNamedAndDefaultExports(ES2015Module mod) {
74-
hasNamedExports(mod) and
75-
hasDefaultExport(mod)
76-
}
77-
7881
/**
7982
* An import declaration.
8083
*
@@ -131,7 +134,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
131134
// For compatibility with the non-standard implementation of default imports,
132135
// treat default imports as namespace imports in cases where it can't cause ambiguity
133136
// between named exports and the properties of a default-exported object.
134-
not hasBothNamedAndDefaultExports(this.getImportedModule()) and
137+
not this.getImportedModule().(ES2015Module).hasBothNamedAndDefaultExports() and
135138
is.getImportedName() = "default"
136139
)
137140
or

javascript/ql/lib/semmle/javascript/NPM.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class PackageJson extends JsonObject {
2929
parentDir.getAChildContainer+() = currentDir and
3030
pkgNameDiff = currentDir.getAbsolutePath().suffix(parentDir.getAbsolutePath().length()) and
3131
not exists(pkgNameDiff.indexOf("/node_modules/")) and
32-
result = parentPkgName + pkgNameDiff
32+
result = parentPkgName + pkgNameDiff and
33+
not parentPkg.isPrivate()
3334
)
3435
}
3536

0 commit comments

Comments
 (0)