Merge branch 'main' into ts4

This commit is contained in:
Erik Krogh Kristensen
2020-08-21 15:08:30 +02:00
277 changed files with 20078 additions and 3191 deletions

View File

@@ -15,18 +15,18 @@ import DataFlow::PathGraph
/**
* An instance of `mysql.createConnection()`, tracked globally.
*/
class MysqlConnection extends TrackedNode {
MysqlConnection() { this = moduleImport("mysql").getAMemberCall("createConnection") }
/**
* Gets a call to the `query` method on this connection object.
*/
MethodCallNode getAQueryCall() {
this.flowsTo(result.getReceiver()) and
result.getMethodName() = "query"
}
DataFlow::SourceNode mysqlConnection(DataFlow::TypeTracker t) {
t.start() and
result = moduleImport("mysql").getAMemberCall("createConnection")
or
exists(DataFlow::TypeTracker t2 | result = mysqlConnection(t2).track(t2, t))
}
/**
* An instance of `mysql.createConnection()`, tracked globally.
*/
DataFlow::SourceNode mysqlConnection() { result = mysqlConnection(DataFlow::TypeTracker::end()) }
/**
* Data returned from a MySQL query.
*
@@ -42,7 +42,7 @@ class MysqlConnection extends TrackedNode {
* ```
*/
class MysqlSource extends StoredXss::Source {
MysqlSource() { this = any(MysqlConnection con).getAQueryCall().getCallback(1).getParameter(1) }
MysqlSource() { this = mysqlConnection().getAMethodCall("query").getCallback(1).getParameter(1) }
}
from StoredXss::Configuration cfg, PathNode source, PathNode sink

View File

@@ -24,5 +24,6 @@ where
// ignore ambient, abstract, and overloaded declarations in TypeScript
f.hasBody() and
g.hasBody()
select f.getId(), "Declaration of " + f.describe() + " conflicts with $@ in the same scope.",
g.getId(), "another declaration"
select f.getIdentifier(),
"Declaration of " + f.describe() + " conflicts with $@ in the same scope.", g.getIdentifier(),
"another declaration"

View File

@@ -42,11 +42,11 @@ where
access.isAmbient()
) and
// don't flag function expressions
not exists(FunctionExpr fe | dead = fe.getId()) and
not exists(FunctionExpr fe | dead = fe.getIdentifier()) and
// don't flag function declarations nested inside blocks or other compound statements;
// their meaning is only partially specified by the standard
not exists(FunctionDeclStmt fd, StmtContainer outer |
dead = fd.getId() and outer = fd.getEnclosingContainer()
dead = fd.getIdentifier() and outer = fd.getEnclosingContainer()
|
not fd = outer.getBody().(BlockStmt).getAStmt()
) and

View File

@@ -22,6 +22,6 @@ where
not decl.isAmbient() and
not redecl.isAmbient() and
// Redeclaring a namespace extends the previous definition.
not decl = any(NamespaceDeclaration ns).getId() and
not redecl = any(NamespaceDeclaration ns).getId()
not decl = any(NamespaceDeclaration ns).getIdentifier() and
not redecl = any(NamespaceDeclaration ns).getIdentifier()
select redecl, "This variable has already been declared $@.", decl, "here"

View File

@@ -93,7 +93,7 @@ predicate isEnumMember(VarDecl decl) { decl = any(EnumMember member).getIdentifi
* "function f", "variable v" or "class c".
*/
string describeVarDecl(VarDecl vd) {
if vd = any(Function f).getId()
if vd = any(Function f).getIdentifier()
then result = "function " + vd.getName()
else
if vd = any(ClassDefinition c).getIdentifier()
@@ -115,7 +115,7 @@ class ImportVarDeclProvider extends Stmt {
*/
VarDecl getAVarDecl() {
result = this.(ImportDeclaration).getASpecifier().getLocal() or
result = this.(ImportEqualsDeclaration).getId()
result = this.(ImportEqualsDeclaration).getIdentifier()
}
/**

View File

@@ -23,7 +23,7 @@ private import semmle.javascript.dataflow.InferredTypes
* if it is part of a const enum access, so we conservatively silence the alert in that case.
*/
predicate namespaceOrConstEnumAccess(VarAccess e) {
exists(NamespaceDeclaration decl | e.getVariable().getADeclaration() = decl.getId())
exists(NamespaceDeclaration decl | e.getVariable().getADeclaration() = decl.getIdentifier())
or
exists(EnumDeclaration decl | e.getVariable().getADeclaration() = decl.getIdentifier() |
decl.isConst()

View File

@@ -43,6 +43,10 @@ where
or
// target is a HTTP URL to a domain on any TLD
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?")
or
// target is a HTTP URL to a domain on any TLD with path elements, and the check is an includes check
check instanceof StringOps::Includes and
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/[a-z0-9/_-]+")
) and
(
if check instanceof StringOps::StartsWith

View File

@@ -30,15 +30,17 @@ private predicate defn(ControlFlowNode def, Expr lhs, AST::ValueNode rhs) {
or
exists(VariableDeclarator vd | def = vd | lhs = vd.getBindingPattern() and rhs = vd.getInit())
or
exists(Function f | def = f.getId() | lhs = def and rhs = f)
exists(Function f | def = f.getIdentifier() | lhs = def and rhs = f)
or
exists(ClassDefinition c | lhs = c.getIdentifier() | def = c and rhs = c and not c.isAmbient())
or
exists(NamespaceDeclaration n | def = n | lhs = n.getId() and rhs = n)
exists(NamespaceDeclaration n | def = n | lhs = n.getIdentifier() and rhs = n)
or
exists(EnumDeclaration ed | def = ed.getIdentifier() | lhs = def and rhs = ed)
or
exists(ImportEqualsDeclaration i | def = i | lhs = i.getId() and rhs = i.getImportedEntity())
exists(ImportEqualsDeclaration i | def = i |
lhs = i.getIdentifier() and rhs = i.getImportedEntity()
)
or
exists(ImportSpecifier i | def = i | lhs = i.getLocal() and rhs = i)
or
@@ -149,7 +151,7 @@ class RValue extends RefExpr {
or
this = any(UpdateExpr u).getOperand().getUnderlyingReference()
or
this = any(NamespaceDeclaration decl).getId()
this = any(NamespaceDeclaration decl).getIdentifier()
}
}

View File

@@ -330,7 +330,7 @@ class ExportDefaultDeclaration extends ExportDeclaration, @exportdefaultdeclarat
/** Gets the declaration, if any, exported by this default export. */
VarDecl getADecl() {
exists(ExprOrStmt op | op = getOperand() |
result = op.(FunctionDeclStmt).getId() or
result = op.(FunctionDeclStmt).getIdentifier() or
result = op.(ClassDeclStmt).getIdentifier()
)
}
@@ -364,13 +364,13 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
Identifier getAnExportedDecl() {
exists(ExprOrStmt op | op = getOperand() |
result = op.(DeclStmt).getADecl().getBindingPattern().getABindingVarRef() or
result = op.(FunctionDeclStmt).getId() or
result = op.(FunctionDeclStmt).getIdentifier() or
result = op.(ClassDeclStmt).getIdentifier() or
result = op.(NamespaceDeclaration).getId() or
result = op.(NamespaceDeclaration).getIdentifier() or
result = op.(EnumDeclaration).getIdentifier() or
result = op.(InterfaceDeclaration).getIdentifier() or
result = op.(TypeAliasDeclaration).getIdentifier() or
result = op.(ImportEqualsDeclaration).getId()
result = op.(ImportEqualsDeclaration).getIdentifier()
)
}

View File

@@ -77,8 +77,15 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
result = getDocumentation().getATagByTitle("this").getType()
}
/**
* DEPRECATED: Use `getIdentifier()` instead.
*
* Gets the identifier specifying the name of this function, if any.
*/
deprecated VarDecl getId() { result = getIdentifier() }
/** Gets the identifier specifying the name of this function, if any. */
VarDecl getId() { result = getChildExpr(-1) }
VarDecl getIdentifier() { result = getChildExpr(-1) }
/**
* Gets the name of this function if it has one, or a name inferred from its context.
@@ -89,9 +96,9 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
* can be inferred, there is no result.
*/
string getName() {
result = getId().getName()
result = getIdentifier().getName()
or
not exists(getId()) and
not exists(getIdentifier()) and
(
exists(VarDef vd | this = vd.getSource() | result = vd.getTarget().(VarRef).getName())
or
@@ -111,7 +118,7 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
}
/** Gets the variable holding this function. */
Variable getVariable() { result = getId().getVariable() }
Variable getVariable() { result = getIdentifier().getVariable() }
/** Gets the `arguments` variable of this function, if any. */
ArgumentsVariable getArgumentsVariable() { result.getFunction() = this }
@@ -183,11 +190,11 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
not exists(getAParameter()) and
(
// if the function has a name, the opening parenthesis comes right after it
result = getId().getLastToken().getNextToken()
result = getIdentifier().getLastToken().getNextToken()
or
// otherwise this must be an arrow function with no parameters, so the opening
// parenthesis is the very first token of the function
not exists(getId()) and result = getFirstToken()
not exists(getIdentifier()) and result = getFirstToken()
)
}
@@ -309,8 +316,8 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
*/
private string inferNameFromVarDef() {
// in ambiguous cases like `var f = function g() {}`, prefer `g` to `f`
if exists(getId())
then result = "function " + getId().getName()
if exists(getIdentifier())
then result = "function " + getIdentifier().getName()
else
exists(VarDef vd | this = vd.getSource() |
result = "function " + vd.getTarget().(VarRef).getName()

View File

@@ -267,7 +267,7 @@ module AccessPath {
or
exists(FunctionDeclStmt fun |
node = DataFlow::valueNode(fun) and
result = fun.getId().(GlobalVarDecl).getName() and
result = fun.getIdentifier().(GlobalVarDecl).getName() and
root.isGlobal()
)
or
@@ -285,7 +285,7 @@ module AccessPath {
or
exists(NamespaceDeclaration decl |
node = DataFlow::valueNode(decl) and
result = decl.getId().(GlobalVarDecl).getName() and
result = decl.getIdentifier().(GlobalVarDecl).getName() and
root.isGlobal()
)
}

View File

@@ -221,7 +221,7 @@ class Require extends CallExpr, Import {
*
* <ul>
* <li> the file `c/p`;
* <li> the file `c/p.{tsx,ts,jsx,es6,es,mjs}`;
* <li> the file `c/p.{tsx,ts,jsx,es6,es,mjs,cjs}`;
* <li> the file `c/p.js`;
* <li> the file `c/p.json`;
* <li> the file `c/p.node`;
@@ -230,12 +230,12 @@ class Require extends CallExpr, Import {
* <li> if `c/p/package.json` exists and specifies a `main` module `m`:
* <ul>
* <li> the file `c/p/m`;
* <li> the file `c/p/m.{tsx,ts,jsx,es6,es,mjs}`;
* <li> the file `c/p/m.{tsx,ts,jsx,es6,es,mjs,cjs}`;
* <li> the file `c/p/m.js`;
* <li> the file `c/p/m.json`;
* <li> the file `c/p/m.node`;
* </ul>
* <li> the file `c/p/index.{tsx,ts,jsx,es6,es,mjs}`;
* <li> the file `c/p/index.{tsx,ts,jsx,es6,es,mjs,cjs}`;
* <li> the file `c/p/index.js`;
* <li> the file `c/p/index.json`;
* <li> the file `c/p/index.node`.

View File

@@ -26,11 +26,13 @@ int getFileExtensionPriority(string ext) {
or
ext = "mjs" and result = 5
or
ext = "js" and result = 6
ext = "cjs" and result = 6
or
ext = "json" and result = 7
ext = "js" and result = 7
or
ext = "node" and result = 8
ext = "json" and result = 8
or
ext = "node" and result = 9
}
int prioritiesPerCandidate() { result = 3 * (numberOfExtensions() + 1) }

View File

@@ -8,13 +8,16 @@ import javascript
*/
class NamespaceDefinition extends Stmt, @namespacedefinition, AST::ValueNode {
/**
* DEPRECATED: Use `getIdentifier()` instead.
*
* Gets the identifier naming the namespace.
*/
Identifier getId() {
result = this.(NamespaceDeclaration).getId()
or
result = this.(EnumDeclaration).getIdentifier()
}
deprecated Identifier getId() { result = getIdentifier() }
/**
* Gets the identifier naming the namespace.
*/
Identifier getIdentifier() { none() } // Overridden in subtypes.
/**
* Gets unqualified name of the namespace being defined.
@@ -29,7 +32,7 @@ class NamespaceDefinition extends Stmt, @namespacedefinition, AST::ValueNode {
* Gets the local namespace name induced by this namespace.
*/
LocalNamespaceName getLocalNamespaceName() {
result = getId().(LocalNamespaceDecl).getLocalNamespaceName()
result = getIdentifier().(LocalNamespaceDecl).getLocalNamespaceName()
}
/**
@@ -55,10 +58,10 @@ class NamespaceDefinition extends Stmt, @namespacedefinition, AST::ValueNode {
*/
class NamespaceDeclaration extends NamespaceDefinition, StmtContainer, @namespacedeclaration {
/** Gets the name of this namespace. */
override Identifier getId() { result = getChildExpr(-1) }
override Identifier getIdentifier() { result = getChildExpr(-1) }
/** Gets the name of this namespace as a string. */
override string getName() { result = getId().getName() }
override string getName() { result = getIdentifier().getName() }
/** Gets the `i`th statement in this namespace. */
Stmt getStmt(int i) {
@@ -83,7 +86,7 @@ class NamespaceDeclaration extends NamespaceDefinition, StmtContainer, @namespac
predicate isInstantiated() { isInstantiated(this) }
override ControlFlowNode getFirstControlFlowNode() {
if hasDeclareKeyword(this) then result = this else result = getId()
if hasDeclareKeyword(this) then result = this else result = getIdentifier()
}
}
@@ -178,13 +181,20 @@ class GlobalAugmentationDeclaration extends Stmt, StmtContainer, @globalaugmenta
/** A TypeScript "import-equals" declaration. */
class ImportEqualsDeclaration extends Stmt, @importequalsdeclaration {
/**
* DEPRECATED: Use `getIdentifier()` instead.
*
* Gets the name under which the imported entity is imported.
*/
deprecated Identifier getId() { result = getIdentifier() }
/** Gets the name under which the imported entity is imported. */
Identifier getId() { result = getChildExpr(0) }
Identifier getIdentifier() { result = getChildExpr(0) }
/** Gets the expression specifying the imported module or entity. */
Expr getImportedEntity() { result = getChildExpr(1) }
override ControlFlowNode getFirstControlFlowNode() { result = getId() }
override ControlFlowNode getFirstControlFlowNode() { result = getIdentifier() }
}
/**
@@ -348,7 +358,7 @@ class TypeDecl extends Identifier, TypeRef, LexicalDecl {
this = any(ClassOrInterface ci).getIdentifier() or
this = any(TypeParameter tp).getIdentifier() or
this = any(ImportSpecifier im).getLocal() or
this = any(ImportEqualsDeclaration im).getId() or
this = any(ImportEqualsDeclaration im).getIdentifier() or
this = any(TypeAliasDeclaration td).getIdentifier() or
this = any(EnumDeclaration ed).getIdentifier() or
this = any(EnumMember member).getIdentifier()
@@ -1235,8 +1245,8 @@ abstract class NamespaceRef extends ASTNode { }
*/
class LocalNamespaceDecl extends VarDecl, NamespaceRef {
LocalNamespaceDecl() {
any(NamespaceDeclaration nd).getId() = this or
any(ImportEqualsDeclaration im).getId() = this or
any(NamespaceDeclaration nd).getIdentifier() = this or
any(ImportEqualsDeclaration im).getIdentifier() = this or
any(ImportSpecifier im).getLocal() = this or
any(EnumDeclaration ed).getIdentifier() = this
}
@@ -1334,7 +1344,7 @@ class ImportVarTypeAccess extends VarTypeAccess, ImportTypeExpr, @importvartypea
*/
class EnumDeclaration extends NamespaceDefinition, @enumdeclaration, AST::ValueNode {
/** Gets the name of this enum, such as `E` in `enum E { A, B }`. */
Identifier getIdentifier() { result = getChildExpr(0) }
override Identifier getIdentifier() { result = getChildExpr(0) }
/** Gets the name of this enum as a string. */
override string getName() { result = getIdentifier().getName() }

View File

@@ -312,8 +312,11 @@ class LocalVariable extends Variable {
this = result.getScope().getAVariable()
or
exists(VarDecl d | d = getADeclaration() |
if d = any(FunctionDeclStmt fds).getId()
then exists(FunctionDeclStmt fds | d = fds.getId() | result = fds.getEnclosingContainer())
if d = any(FunctionDeclStmt fds).getIdentifier()
then
exists(FunctionDeclStmt fds | d = fds.getIdentifier() |
result = fds.getEnclosingContainer()
)
else result = d.getContainer()
)
}

View File

@@ -2,7 +2,7 @@
* Provides support for intra-procedural tracking of a customizable
* set of data flow nodes.
*
* Note that unlike `TrackedNodes`, this library only performs
* Note that unlike `TypeTracking.qll`, this library only performs
* local tracking within a function.
*/

View File

@@ -1,4 +1,52 @@
/**
* DEPRECATED: Use `TypeTracking.qll` instead.
*
* The following `TrackedNode` usage is usually equivalent to the type tracking usage below.
*
* ```
* class MyTrackedNode extends TrackedNode {
* MyTrackedNode() { isInteresting(this) }
* }
*
* DataFlow::Node getMyTrackedNodeLocation(MyTrackedNode n) {
* n.flowsTo(result)
* }
* ```
*
* ```
* DataFlow::SourceNode getMyTrackedNodeLocation(DataFlow::SourceNode start, DataFlow::TypeTracker t) {
* t.start() and
* isInteresting(result) and
* result = start
* or
* exists (DataFlow::TypeTracker t2 |
* result = getMyTrackedNodeLocation(start, t2).track(t2, t)
* )
* }
*
* DataFlow::SourceNode getMyTrackedNodeLocation(DataFlow::SourceNode n) {
* result = getMyTrackedNodeLocation(n, DataFlow::TypeTracker::end())
* }
* ```
*
* In rare cases, additional tracking is required, for instance when tracking string constants, and the following type tracking formulation is required instead.
*
* ```
* DataFlow::Node getMyTrackedNodeLocation(DataFlow::Node start, DataFlow::TypeTracker t) {
* t.start() and
* isInteresting(result) and
* result = start
* or
* exists(DataFlow::TypeTracker t2 |
* t = t2.smallstep(getMyTrackedNodeLocation(start, t2), result)
* )
* }
*
* DataFlow::Node getMyTrackedNodeLocation(DataFlow::Node n) {
* result = getMyTrackedNodeLocation(n, DataFlow::TypeTracker::end())
* }
* ```
*
* Provides support for inter-procedural tracking of a customizable
* set of data flow nodes.
*/
@@ -12,7 +60,7 @@ private import internal.FlowSteps as FlowSteps
* To track additional values, extends this class with additional
* subclasses.
*/
abstract class TrackedNode extends DataFlow::Node {
abstract deprecated class TrackedNode extends DataFlow::Node {
/**
* Holds if this node flows into `sink` in zero or more (possibly
* inter-procedural) steps.
@@ -26,7 +74,7 @@ abstract class TrackedNode extends DataFlow::Node {
* To track additional expressions, extends this class with additional
* subclasses.
*/
abstract class TrackedExpr extends Expr {
abstract deprecated class TrackedExpr extends Expr {
predicate flowsTo(Expr sink) {
exists(TrackedExprNode ten | ten.asExpr() = this | ten.flowsTo(DataFlow::valueNode(sink)))
}
@@ -35,7 +83,7 @@ abstract class TrackedExpr extends Expr {
/**
* Turn all `TrackedExpr`s into `TrackedNode`s.
*/
private class TrackedExprNode extends TrackedNode {
deprecated private class TrackedExprNode extends TrackedNode {
TrackedExprNode() { asExpr() instanceof TrackedExpr }
}
@@ -64,7 +112,9 @@ private module NodeTracking {
*
* Summary steps through function calls are not taken into account.
*/
private predicate basicFlowStep(DataFlow::Node pred, DataFlow::Node succ, PathSummary summary) {
deprecated private predicate basicFlowStep(
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
) {
isRelevant(pred) and
(
// Local flow
@@ -94,7 +144,7 @@ private module NodeTracking {
*
* No call/return matching is done, so this is a relatively coarse over-approximation.
*/
private predicate isRelevant(DataFlow::Node nd) {
deprecated private predicate isRelevant(DataFlow::Node nd) {
nd instanceof TrackedNode
or
exists(DataFlow::Node mid | isRelevant(mid) |
@@ -115,7 +165,7 @@ private module NodeTracking {
* either `pred` is an argument of `f` and `succ` the corresponding parameter, or
* `pred` is a variable definition whose value is captured by `f` at `succ`.
*/
private predicate callInputStep(
deprecated private predicate callInputStep(
Function f, DataFlow::Node invk, DataFlow::Node pred, DataFlow::Node succ
) {
isRelevant(pred) and
@@ -136,7 +186,7 @@ private module NodeTracking {
* that is captured by `f`, may flow to `nd` (possibly through callees, but not containing
* any unmatched calls or returns) along a path summarized by `summary`.
*/
private predicate reachableFromInput(
deprecated private predicate reachableFromInput(
Function f, DataFlow::Node invk, DataFlow::Node input, DataFlow::Node nd, PathSummary summary
) {
callInputStep(f, invk, input, nd) and
@@ -154,7 +204,7 @@ private module NodeTracking {
* Holds if `nd` may flow into a return statement of `f`
* (possibly through callees) along a path summarized by `summary`.
*/
private predicate reachesReturn(Function f, DataFlow::Node nd, PathSummary summary) {
deprecated private predicate reachesReturn(Function f, DataFlow::Node nd, PathSummary summary) {
returnExpr(f, nd, _) and
summary = PathSummary::level()
or
@@ -170,7 +220,7 @@ private module NodeTracking {
* which is either an argument or a definition captured by the function, flows,
* possibly through callees.
*/
private predicate flowThroughCall(DataFlow::Node input, DataFlow::Node output) {
deprecated private predicate flowThroughCall(DataFlow::Node input, DataFlow::Node output) {
exists(Function f, DataFlow::ValueNode ret |
ret.asExpr() = f.getAReturnedExpr() and
reachableFromInput(f, output, input, ret, _)
@@ -187,7 +237,7 @@ private module NodeTracking {
/**
* Holds if `pred` may flow into property `prop` of `succ` along a path summarized by `summary`.
*/
private predicate storeStep(
deprecated private predicate storeStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string prop, PathSummary summary
) {
basicStoreStep(pred, succ, prop) and
@@ -210,7 +260,7 @@ private module NodeTracking {
* Holds if property `prop` of `pred` may flow into `succ` along a path summarized by
* `summary`.
*/
private predicate loadStep(
deprecated private predicate loadStep(
DataFlow::Node pred, DataFlow::Node succ, string prop, PathSummary summary
) {
basicLoadStep(pred, succ, prop) and
@@ -226,7 +276,7 @@ private module NodeTracking {
* Holds if `rhs` is the right-hand side of a write to property `prop`, and `nd` is reachable
* from the base of that write (possibly through callees) along a path summarized by `summary`.
*/
private predicate reachableFromStoreBase(
deprecated private predicate reachableFromStoreBase(
string prop, DataFlow::Node rhs, DataFlow::Node nd, PathSummary summary
) {
storeStep(rhs, nd, prop, summary)
@@ -244,7 +294,7 @@ private module NodeTracking {
*
* In other words, `pred` may flow to `succ` through a property.
*/
private predicate flowThroughProperty(
deprecated private predicate flowThroughProperty(
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
) {
exists(string prop, DataFlow::Node base, PathSummary oldSummary, PathSummary newSummary |
@@ -259,7 +309,7 @@ private module NodeTracking {
* invokes `cb`, passing `arg` as its `i`th argument. `arg` flows along a path summarized
* by `summary`, while `cb` is only tracked locally.
*/
private predicate summarizedHigherOrderCall(
deprecated private predicate summarizedHigherOrderCall(
DataFlow::Node arg, DataFlow::Node cb, int i, PathSummary summary
) {
exists(
@@ -293,7 +343,7 @@ private module NodeTracking {
* Alternatively, the callback can flow into a call `f(callback)` which itself provides the `arg`.
* That is, `arg` refers to a value defined in `f` or one of its callees.
*/
predicate higherOrderCall(
deprecated predicate higherOrderCall(
DataFlow::Node arg, DataFlow::SourceNode callback, int i, PathSummary summary
) {
// Summarized call
@@ -328,7 +378,7 @@ private module NodeTracking {
* of `cb`. `arg` flows along a path summarized by `summary`, while `cb` is only tracked
* locally.
*/
private predicate flowIntoHigherOrderCall(
deprecated private predicate flowIntoHigherOrderCall(
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
) {
exists(DataFlow::FunctionNode cb, int i, PathSummary oldSummary |
@@ -341,7 +391,9 @@ private module NodeTracking {
/**
* Holds if there is a flow step from `pred` to `succ` described by `summary`.
*/
private predicate flowStep(DataFlow::Node pred, DataFlow::Node succ, PathSummary summary) {
deprecated private predicate flowStep(
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
) {
basicFlowStep(pred, succ, summary)
or
// Flow through a function that returns a value that depends on one of its arguments
@@ -360,7 +412,7 @@ private module NodeTracking {
* Holds if there is a path from `source` to `nd` along a path summarized by
* `summary`.
*/
predicate flowsTo(TrackedNode source, DataFlow::Node nd, PathSummary summary) {
deprecated predicate flowsTo(TrackedNode source, DataFlow::Node nd, PathSummary summary) {
source = nd and
summary = PathSummary::level()
or

View File

@@ -1,7 +1,7 @@
/**
* Provides the `TypeTracker` class for tracking types interprocedurally.
*
* This provides an alternative to `DataFlow::TrackedNode` and `AbstractValue`
* This provides an alternative to `AbstractValue`
* for tracking certain types interprocedurally without computing which source
* a given value came from.
*/

View File

@@ -111,7 +111,7 @@ private class AnalyzedNamespaceDeclaration extends DataFlow::AnalyzedValueNode {
AbstractValue getPreviousValue() {
exists(AnalyzedSsaDefinition def |
def.getVariable().getAUse() = astNode.getId() and
def.getVariable().getAUse() = astNode.getIdentifier() and
result = def.getAnRhsValue()
)
}

View File

@@ -1,5 +1,5 @@
/**
* Provides classes for modelling cryptographic libraries.
* Provides classes for modeling cryptographic libraries.
*/
import javascript

View File

@@ -0,0 +1,8 @@
(function () {
if (true) {
function foo() {
return 3;
}
}
return foo(); // this resolves to `foo` above, because we have function-scope in non-strict mode.
})();

View File

@@ -0,0 +1,10 @@
(function () {
if (true) {
function foo() {
return 3;
}
}
return foo(); // `foo` is not defined, because we are in strict-mode.
})();
export default 3; // strict-mode implied because ES2015 module.

View File

@@ -0,0 +1,12 @@
"use strict";
(function () {
"use strict";
if (true) {
function foo() {
return 3;
}
}
return foo(); // `foo` is not defined, because we are in strict-mode.
})();
export default 3; // strict-mode implied because ES2015 module.

View File

@@ -90,6 +90,11 @@ test_getAFunctionValue
| m.js:3:1:3:16 | module.exports.f | m.js:1:13:1:25 | function() {} |
| n.js:2:1:2:3 | m.f | m.js:1:13:1:25 | function() {} |
| n.js:5:1:5:4 | m2.f | m2.js:2:6:2:18 | function() {} |
| non-strict.js:1:1:8:2 | (functi ... ode.\\n}) | non-strict.js:1:2:8:1 | functio ... mode.\\n} |
| non-strict.js:1:2:8:1 | functio ... mode.\\n} | non-strict.js:1:2:8:1 | functio ... mode.\\n} |
| non-strict.js:3:5:5:5 | functio ... ;\\n } | non-strict.js:3:5:5:5 | functio ... ;\\n } |
| non-strict.js:3:14:3:16 | foo | non-strict.js:3:5:5:5 | functio ... ;\\n } |
| non-strict.js:7:10:7:12 | foo | non-strict.js:3:5:5:5 | functio ... ;\\n } |
| protoclass.js:3:1:5:1 | functio ... it();\\n} | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:3:10:3:10 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:4:3:4:11 | this.init | protoclass.js:7:20:11:1 | functio ... m();\\n} |
@@ -110,6 +115,12 @@ test_getAFunctionValue
| reflection.js:7:1:7:3 | add | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:8:1:8:3 | add | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:8:1:8:9 | add.apply | reflection.js:5:15:5:39 | functio ... n 56; } |
| strict2.js:2:1:10:2 | (functi ... ode.\\n}) | strict2.js:2:2:10:1 | functio ... mode.\\n} |
| strict2.js:2:2:10:1 | functio ... mode.\\n} | strict2.js:2:2:10:1 | functio ... mode.\\n} |
| strict2.js:5:5:7:5 | functio ... ;\\n } | strict2.js:5:5:7:5 | functio ... ;\\n } |
| strict.js:1:1:8:2 | (functi ... ode.\\n}) | strict.js:1:2:8:1 | functio ... mode.\\n} |
| strict.js:1:2:8:1 | functio ... mode.\\n} | strict.js:1:2:8:1 | functio ... mode.\\n} |
| strict.js:3:5:5:5 | functio ... ;\\n } | strict.js:3:5:5:5 | functio ... ;\\n } |
| tst3.js:1:1:1:22 | functio ... fn() {} | tst3.js:1:1:1:22 | functio ... fn() {} |
| tst3.js:2:1:2:23 | functio ... n2() {} | tst3.js:2:1:2:23 | functio ... n2() {} |
| tst.js:1:1:1:15 | function f() {} | tst.js:1:1:1:15 | function f() {} |
@@ -225,6 +236,8 @@ test_getNumArgument
| n.js:2:1:2:5 | m.f() | 0 |
| n.js:4:10:4:24 | require('./m2') | 1 |
| n.js:5:1:5:6 | m2.f() | 0 |
| non-strict.js:1:1:8:4 | (functi ... e.\\n})() | 0 |
| non-strict.js:7:10:7:14 | foo() | 0 |
| protoclass.js:4:3:4:13 | this.init() | 0 |
| protoclass.js:8:3:8:15 | this.method() | 0 |
| protoclass.js:9:11:9:32 | this.me ... d(this) | 1 |
@@ -233,6 +246,10 @@ test_getNumArgument
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 3 |
| reflection.js:7:1:7:22 | reflective call | 2 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | 2 |
| strict2.js:2:1:10:4 | (functi ... e.\\n})() | 0 |
| strict2.js:9:10:9:14 | foo() | 0 |
| strict.js:1:1:8:4 | (functi ... e.\\n})() | 0 |
| strict.js:7:10:7:14 | foo() | 0 |
| tst.js:6:1:6:3 | f() | 0 |
| tst.js:7:1:7:3 | g() | 0 |
| tst.js:8:1:8:3 | h() | 0 |
@@ -321,6 +338,8 @@ test_getCalleeNode
| n.js:2:1:2:5 | m.f() | n.js:2:1:2:3 | m.f |
| n.js:4:10:4:24 | require('./m2') | n.js:4:10:4:16 | require |
| n.js:5:1:5:6 | m2.f() | n.js:5:1:5:4 | m2.f |
| non-strict.js:1:1:8:4 | (functi ... e.\\n})() | non-strict.js:1:1:8:2 | (functi ... ode.\\n}) |
| non-strict.js:7:10:7:14 | foo() | non-strict.js:7:10:7:12 | foo |
| protoclass.js:4:3:4:13 | this.init() | protoclass.js:4:3:4:11 | this.init |
| protoclass.js:8:3:8:15 | this.method() | protoclass.js:8:3:8:13 | this.method |
| protoclass.js:9:11:9:32 | this.me ... d(this) | protoclass.js:9:11:9:26 | this.method.bind |
@@ -330,6 +349,10 @@ test_getCalleeNode
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:1:7:3 | add |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:1:8:9 | add.apply |
| reflection.js:8:1:8:25 | reflective call | reflection.js:8:1:8:3 | add |
| strict2.js:2:1:10:4 | (functi ... e.\\n})() | strict2.js:2:1:10:2 | (functi ... ode.\\n}) |
| strict2.js:9:10:9:14 | foo() | strict2.js:9:10:9:12 | foo |
| strict.js:1:1:8:4 | (functi ... e.\\n})() | strict.js:1:1:8:2 | (functi ... ode.\\n}) |
| strict.js:7:10:7:14 | foo() | strict.js:7:10:7:12 | foo |
| tst.js:6:1:6:3 | f() | tst.js:6:1:6:1 | f |
| tst.js:7:1:7:3 | g() | tst.js:7:1:7:1 | g |
| tst.js:8:1:8:3 | h() | tst.js:8:1:8:1 | h |
@@ -408,11 +431,15 @@ test_getACallee
| m.js:3:1:3:18 | module.exports.f() | m.js:1:13:1:25 | function() {} |
| n.js:2:1:2:5 | m.f() | m.js:1:13:1:25 | function() {} |
| n.js:5:1:5:6 | m2.f() | m2.js:2:6:2:18 | function() {} |
| non-strict.js:1:1:8:4 | (functi ... e.\\n})() | non-strict.js:1:2:8:1 | functio ... mode.\\n} |
| non-strict.js:7:10:7:14 | foo() | non-strict.js:3:5:5:5 | functio ... ;\\n } |
| protoclass.js:4:3:4:13 | this.init() | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:8:3:8:15 | this.method() | protoclass.js:13:22:13:34 | function() {} |
| reflection.js:7:1:7:22 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:5:15:5:39 | functio ... n 56; } |
| reflection.js:8:1:8:25 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| strict2.js:2:1:10:4 | (functi ... e.\\n})() | strict2.js:2:2:10:1 | functio ... mode.\\n} |
| strict.js:1:1:8:4 | (functi ... e.\\n})() | strict.js:1:2:8:1 | functio ... mode.\\n} |
| tst.js:6:1:6:3 | f() | tst.js:1:1:1:15 | function f() {} |
| tst.js:7:1:7:3 | g() | tst.js:2:9:2:21 | function() {} |
| tst.js:8:1:8:3 | h() | tst.js:3:5:3:17 | function() {} |
@@ -463,6 +490,7 @@ test_getCalleeName
| n.js:2:1:2:5 | m.f() | f |
| n.js:4:10:4:24 | require('./m2') | require |
| n.js:5:1:5:6 | m2.f() | f |
| non-strict.js:7:10:7:14 | foo() | foo |
| protoclass.js:4:3:4:13 | this.init() | init |
| protoclass.js:8:3:8:15 | this.method() | method |
| protoclass.js:9:11:9:32 | this.me ... d(this) | bind |
@@ -470,6 +498,8 @@ test_getCalleeName
| reflection.js:4:5:4:12 | sneaky() | sneaky |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | call |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | apply |
| strict2.js:9:10:9:14 | foo() | foo |
| strict.js:7:10:7:14 | foo() | foo |
| tst.js:6:1:6:3 | f() | f |
| tst.js:7:1:7:3 | g() | g |
| tst.js:8:1:8:3 | h() | h |

Binary file not shown.

View File

@@ -1,5 +1,5 @@
import javascript
query predicate test_getId(Function f, VarDecl res0, string res1) {
res0 = f.getId() and res1 = f.getName()
res0 = f.getIdentifier() and res1 = f.getName()
}

View File

@@ -1,55 +0,0 @@
| missing | callback.js:17:15:17:23 | "source2" | callback.js:8:16:8:20 | xs[i] |
| missing | callback.js:17:15:17:23 | "source2" | callback.js:12:16:12:16 | x |
| missing | callback.js:17:15:17:23 | "source2" | callback.js:12:16:12:16 | x |
| missing | callback.js:17:15:17:23 | "source2" | callback.js:13:14:13:14 | x |
| missing | promises.js:1:2:1:2 | source | promises.js:6:26:6:28 | val |
| missing | promises.js:1:2:1:2 | source | promises.js:6:26:6:28 | val |
| missing | promises.js:1:2:1:2 | source | promises.js:7:16:7:18 | val |
| missing | promises.js:1:2:1:2 | source | promises.js:37:11:37:11 | v |
| missing | promises.js:1:2:1:2 | source | promises.js:37:11:37:11 | v |
| missing | promises.js:1:2:1:2 | source | promises.js:38:32:38:32 | v |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:6:26:6:28 | val |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:6:26:6:28 | val |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:37:11:37:11 | v |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:37:11:37:11 | v |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:38:32:38:32 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:20:7:20:7 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:20:7:20:7 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:21:20:21:20 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:23:19:23:19 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:23:19:23:19 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:24:20:24:20 | v |
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:18:18:18:18 | v |
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:18:18:18:18 | v |
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:20:7:20:7 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:20:7:20:7 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:21:20:21:20 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:23:19:23:19 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:23:19:23:19 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:24:20:24:20 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:20:7:20:7 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:20:7:20:7 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:21:20:21:20 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:23:19:23:19 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:23:19:23:19 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:24:20:24:20 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:20:7:20:7 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:20:7:20:7 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:21:20:21:20 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:23:19:23:19 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:23:19:23:19 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:24:20:24:20 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:20:7:20:7 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:20:7:20:7 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:21:20:21:20 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:23:19:23:19 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:23:19:23:19 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:24:20:24:20 | v |
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:37:11:37:11 | v |
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:37:11:37:11 | v |
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:38:32:38:32 | v |
| missing | tst.js:2:17:2:22 | "src1" | tst.js:27:22:27:24 | elt |
| missing | tst.js:2:17:2:22 | "src1" | tst.js:27:22:27:24 | elt |
| missing | tst.js:2:17:2:22 | "src1" | tst.js:28:20:28:22 | elt |

View File

@@ -1,31 +0,0 @@
import javascript
/**
* Track all nodes that do not have flow predecessors.
*/
class TrackAllSources extends DataFlow::TrackedNode {
TrackAllSources() { not exists(getAPredecessor()) }
}
/**
* A data flow configuration that emulates the flow tracking done by
* `DataFlow::TrackedNode`.
*/
class AllSourcesTrackingConfig extends DataFlow::Configuration {
AllSourcesTrackingConfig() { this = "TrackAllTrackedNodes" }
override predicate isSource(DataFlow::Node src) { src instanceof DataFlow::TrackedNode }
override predicate isSink(DataFlow::Node snk) { any() }
}
from DataFlow::Node source, DataFlow::Node sink, AllSourcesTrackingConfig cfg, string problem
where
cfg.hasFlow(source, sink) and
not source.(DataFlow::TrackedNode).flowsTo(sink) and
problem = "missing"
or
not cfg.hasFlow(source, sink) and
source.(DataFlow::TrackedNode).flowsTo(sink) and
problem = "spurious"
select problem, source, sink

View File

@@ -0,0 +1,3 @@
var fs = require("fs");
console.log("I'm a .cjs file!");
console.log(fs);

View File

@@ -0,0 +1,5 @@
import * as fs from "fs";
export default function (x) {
return fs.readFileSync(x);
};

View File

@@ -0,0 +1 @@
console.log("I'm a .mjs file!");

View File

@@ -0,0 +1,7 @@
var fs = require("fs");
module.exports = {
foo: function (x) {
return fs.readFileSync(x);
}
}

View File

@@ -0,0 +1 @@
console.log("I'm a plain script!");

View File

@@ -0,0 +1,5 @@
| commonjs.cjs:1:1:3:16 | <toplevel> | node |
| import.js:1:1:5:2 | <toplevel> | es2015 |
| mjs.mjs:1:1:1:32 | <toplevel> | es2015 |
| require.js:1:1:7:1 | <toplevel> | node |
| script.js:1:1:1:35 | <toplevel> | non-module |

View File

@@ -0,0 +1,18 @@
import javascript
query string getModuleType(TopLevel top) {
not top.isExterns() and
(
not top instanceof Module and
result = "non-module"
or
top instanceof NodeModule and
result = "node"
or
top instanceof ES2015Module and
result = "es2015"
or
top instanceof AmdModule and
result = "amd"
)
}

View File

@@ -10,7 +10,7 @@ class ResolveCall extends CallExpr {
string getDeclaredValue() {
result = getVariable().getAnAssignedExpr().getStringValue()
or
exists(NamespaceDeclaration decl | decl.getId() = getVariable().getADeclaration() |
exists(NamespaceDeclaration decl | decl.getIdentifier() = getVariable().getADeclaration() |
result = getNamespaceName(decl)
)
}
@@ -21,7 +21,8 @@ string getNamespaceName(NamespaceDeclaration decl) {
or
not decl.getStmt(0).(ExprStmt).getExpr() instanceof ConstantString and
result =
"Namespace " + decl.getId() + " on line " + decl.getFirstToken().getLocation().getStartLine()
"Namespace " + decl.getIdentifier() + " on line " +
decl.getFirstToken().getLocation().getStartLine()
}
from ResolveCall resolve

View File

@@ -20,3 +20,6 @@
| tst-IncompleteUrlSubstringSanitization.js:63:4:63:33 | x.index ... !== -1 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:63:14:63:25 | "secure.com" | secure.com |
| tst-IncompleteUrlSubstringSanitization.js:64:3:64:26 | x.inclu ... e.com") | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:64:14:64:25 | "secure.com" | secure.com |
| tst-IncompleteUrlSubstringSanitization.js:66:6:66:29 | x.inclu ... e.com") | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:66:17:66:28 | "secure.com" | secure.com |
| tst-IncompleteUrlSubstringSanitization.js:73:5:73:48 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:73:15:73:42 | "https: ... oo/bar" | https://secure.com/foo/bar |
| tst-IncompleteUrlSubstringSanitization.js:74:5:74:40 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:74:15:74:34 | "https://secure.com" | https://secure.com |
| tst-IncompleteUrlSubstringSanitization.js:75:5:75:52 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:75:15:75:46 | "https: ... ar-baz" | https://secure.com/foo/bar-baz |

View File

@@ -67,5 +67,10 @@
} else {
doSomeThingWithTrustedURL(x);
}
}
x.startsWith("https://secure.com/foo/bar"); // OK - a forward slash after the domain makes prefix checks safe.
x.indexOf("https://secure.com/foo/bar") >= 0 // NOT OK - the url can be anywhere in the string.
x.indexOf("https://secure.com") >= 0 // NOT OK
x.indexOf("https://secure.com/foo/bar-baz") >= 0 // NOT OK - the url can be anywhere in the string.
});

View File

@@ -1,12 +1,15 @@
import javascript
class TrackedStringLiteral extends DataFlow::TrackedNode {
TrackedStringLiteral() { this.asExpr() instanceof ConstantString }
DataFlow::Node constantString(DataFlow::TypeTracker t) {
t.start() and
result.asExpr() instanceof ConstantString
or
exists(DataFlow::TypeTracker t2 | t = t2.smallstep(constantString(t2), result))
}
query predicate test_query15(DataFlow::Node sink) {
exists(TrackedStringLiteral source, SsaExplicitDefinition def |
source.flowsTo(sink) and
exists(SsaExplicitDefinition def |
sink = constantString(DataFlow::TypeTracker::end()) and
sink = DataFlow::ssaDefinitionNode(def) and
def.getSourceVariable().getName().toLowerCase() = "password"
|