JS: Autoformat everything

This commit is contained in:
Asger Feldthaus
2020-02-27 09:41:01 +00:00
parent 9c06c48dc7
commit fefcf1a7a6
130 changed files with 657 additions and 774 deletions

View File

@@ -24,8 +24,9 @@ where
exists(string n | p = f.getDependencyParameter(n) | exists(string n | p = f.getDependencyParameter(n) |
p.getName() != n and p.getName() != n and
exists(f.getDependencyParameter(p.getName())) and exists(f.getDependencyParameter(p.getName())) and
msg = "This parameter is named '" + p.getName() + "', " + msg =
"but actually refers to dependency '" + n + "'." "This parameter is named '" + p.getName() + "', " + "but actually refers to dependency '" +
n + "'."
) )
) )
select p, msg select p, msg

View File

@@ -15,7 +15,8 @@ import javascript
from AngularJS::ServiceReference compile, SimpleParameter elem, CallExpr c from AngularJS::ServiceReference compile, SimpleParameter elem, CallExpr c
where where
compile.getName() = "$compile" and compile.getName() = "$compile" and
elem = any(AngularJS::CustomDirective d) elem =
any(AngularJS::CustomDirective d)
.getALinkFunction() .getALinkFunction()
.(AngularJS::LinkFunction) .(AngularJS::LinkFunction)
.getElementParameter() and .getElementParameter() and

View File

@@ -130,7 +130,8 @@ where
kind = getServiceKind(request, name) and kind = getServiceKind(request, name) and
exists(request.getAServiceDefinition(name)) and // ignore unknown/undefined services exists(request.getAServiceDefinition(name)) and // ignore unknown/undefined services
not isCompatibleRequestedService(request, kind) and not isCompatibleRequestedService(request, kind) and
compatibleWithString = concat(string compatibleKind | compatibleWithString =
concat(string compatibleKind |
isCompatibleRequestedService(request, compatibleKind) and isCompatibleRequestedService(request, compatibleKind) and
not isWildcardKind(compatibleKind) not isWildcardKind(compatibleKind)
| |

View File

@@ -34,8 +34,9 @@ predicate isMissingParameter(AngularJS::InjectableFunction f, string msg, ASTNod
then dependenciesAreString = "dependency is" then dependenciesAreString = "dependency is"
else dependenciesAreString = "dependencies are" else dependenciesAreString = "dependencies are"
) and ) and
msg = "This function has " + paramCount + " " + parametersString + ", but " + injectionCount + msg =
" " + dependenciesAreString + " injected into it." "This function has " + paramCount + " " + parametersString + ", but " + injectionCount + " "
+ dependenciesAreString + " injected into it."
) )
) )
} }

View File

@@ -31,7 +31,8 @@ VarRef refInContainer(Variable var, RefKind kind, StmtContainer sc) {
* declaration of `var` (if `kind` is `Decl()`) in `sc`. * declaration of `var` (if `kind` is `Decl()`) in `sc`.
*/ */
VarRef firstRefInContainer(Variable var, RefKind kind, StmtContainer sc) { VarRef firstRefInContainer(Variable var, RefKind kind, StmtContainer sc) {
result = min(refInContainer(var, kind, sc) as ref result =
min(refInContainer(var, kind, sc) as ref
order by order by
ref.getLocation().getStartLine(), ref.getLocation().getStartColumn() ref.getLocation().getStartLine(), ref.getLocation().getStartColumn()
) )
@@ -52,7 +53,8 @@ VarRef refInTopLevel(Variable var, RefKind kind, TopLevel tl) {
* declaration of `var` (if `kind` is `Decl()`) in `tl`. * declaration of `var` (if `kind` is `Decl()`) in `tl`.
*/ */
VarRef firstRefInTopLevel(Variable var, RefKind kind, TopLevel tl) { VarRef firstRefInTopLevel(Variable var, RefKind kind, TopLevel tl) {
result = min(refInTopLevel(var, kind, tl) as ref result =
min(refInTopLevel(var, kind, tl) as ref
order by order by
ref.getLocation().getStartLine(), ref.getLocation().getStartColumn() ref.getLocation().getStartLine(), ref.getLocation().getStartColumn()
) )

View File

@@ -57,7 +57,8 @@ GlobalVarAccess getAccessIn(GlobalVariable v, Function f) {
* Gets the (lexically) first access to variable `v` in function `f`. * Gets the (lexically) first access to variable `v` in function `f`.
*/ */
GlobalVarAccess getFirstAccessIn(GlobalVariable v, Function f) { GlobalVarAccess getFirstAccessIn(GlobalVariable v, Function f) {
result = min(getAccessIn(v, f) as gva result =
min(getAccessIn(v, f) as gva
order by order by
gva.getLocation().getStartLine(), gva.getLocation().getStartColumn() gva.getLocation().getStartLine(), gva.getLocation().getStartColumn()
) )

View File

@@ -1,7 +1,7 @@
/** /**
* @name Suspicious method name declaration * @name Suspicious method name declaration
* @description A method declaration with a name that is a special keyword in another * @description A method declaration with a name that is a special keyword in another
* context is suspicious. * context is suspicious.
* @kind problem * @kind problem
* @problem.severity warning * @problem.severity warning
* @id js/suspicious-method-name-declaration * @id js/suspicious-method-name-declaration
@@ -19,7 +19,7 @@ import javascript
predicate isSuspiciousMethodName(string name, ClassOrInterface container) { predicate isSuspiciousMethodName(string name, ClassOrInterface container) {
name = "function" name = "function"
or or
// "constructor" is only suspicious outside a class. // "constructor" is only suspicious outside a class.
name = "constructor" and not container instanceof ClassDefinition name = "constructor" and not container instanceof ClassDefinition
or or
// "new" is only suspicious inside a class. // "new" is only suspicious inside a class.
@@ -31,7 +31,6 @@ where
container.getLocation().getFile().getFileType().isTypeScript() and container.getLocation().getFile().getFileType().isTypeScript() and
container.getMember(name) = member and container.getMember(name) = member and
isSuspiciousMethodName(name, container) and isSuspiciousMethodName(name, container) and
// Cases to ignore. // Cases to ignore.
not ( not (
// Assume that a "new" method is intentional if the class has an explicit constructor. // Assume that a "new" method is intentional if the class has an explicit constructor.
@@ -39,31 +38,34 @@ where
container instanceof ClassDefinition and container instanceof ClassDefinition and
exists(ConstructorDeclaration constructor | exists(ConstructorDeclaration constructor |
container.getMember("constructor") = constructor and container.getMember("constructor") = constructor and
not constructor.isSynthetic() not constructor.isSynthetic()
) )
or or
// Explicitly declared static methods are fine. // Explicitly declared static methods are fine.
container instanceof ClassDefinition and container instanceof ClassDefinition and
member.isStatic() member.isStatic()
or or
// Only looking for declared methods. Methods with a body are OK. // Only looking for declared methods. Methods with a body are OK.
exists(member.getBody().getBody()) exists(member.getBody().getBody())
or or
// The developer was not confused about "function" when there are other methods in the interface. // The developer was not confused about "function" when there are other methods in the interface.
name = "function" and name = "function" and
exists(MethodDeclaration other | other = container.getAMethod() | exists(MethodDeclaration other | other = container.getAMethod() |
other.getName() != "function" and other.getName() != "function" and
not other.(ConstructorDeclaration).isSynthetic() not other.(ConstructorDeclaration).isSynthetic()
) )
) ) and
and
( (
name = "constructor" and msg = "The member name 'constructor' does not declare a constructor in interfaces, but it does in classes." name = "constructor" and
msg =
"The member name 'constructor' does not declare a constructor in interfaces, but it does in classes."
or or
name = "new" and msg = "The member name 'new' does not declare a constructor, but 'constructor' does in class declarations." name = "new" and
msg =
"The member name 'new' does not declare a constructor, but 'constructor' does in class declarations."
or or
name = "function" and msg = "The member name 'function' does not declare a function, it declares a method named 'function'." name = "function" and
msg =
"The member name 'function' does not declare a function, it declares a method named 'function'."
) )
select member, msg select member, msg

View File

@@ -44,43 +44,39 @@ string getKind(MemberDeclaration m) {
* A call-signature that originates from a MethodSignature in the AST. * A call-signature that originates from a MethodSignature in the AST.
*/ */
private class MethodCallSig extends CallSignatureType { private class MethodCallSig extends CallSignatureType {
string name; string name;
MethodCallSig() { MethodCallSig() {
exists(MethodSignature sig | exists(MethodSignature sig |
this = sig.getBody().getCallSignature() and this = sig.getBody().getCallSignature() and
name = sig.getName() name = sig.getName()
) )
} }
/** /**
* Gets the name of any member that has this signature. * Gets the name of any member that has this signature.
*/ */
string getName() { string getName() { result = name }
result = name
}
} }
/** /**
* Holds if the two call signatures could be overloads of each other and have the same parameter types. * Holds if the two call signatures could be overloads of each other and have the same parameter types.
*/ */
predicate matchingCallSignature(MethodCallSig method, MethodCallSig other) { predicate matchingCallSignature(MethodCallSig method, MethodCallSig other) {
method.getName() = other.getName() and method.getName() = other.getName() and
method.getNumOptionalParameter() = other.getNumOptionalParameter() and method.getNumOptionalParameter() = other.getNumOptionalParameter() and
method.getNumParameter() = other.getNumParameter() and method.getNumParameter() = other.getNumParameter() and
method.getNumRequiredParameter() = other.getNumRequiredParameter() and method.getNumRequiredParameter() = other.getNumRequiredParameter() and
// purposely not looking at number of type arguments. // purposely not looking at number of type arguments.
method.getKind() = other.getKind() and
method.getKind() = other.getKind() and forall(int i | i in [0 .. -1 + method.getNumParameter()] |
forall(int i | i in [0 .. -1 + method.getNumParameter()] |
method.getParameter(i) = other.getParameter(i) // This is sometimes imprecise, so it is still a good idea to compare type annotations. method.getParameter(i) = other.getParameter(i) // This is sometimes imprecise, so it is still a good idea to compare type annotations.
) and ) and
// shared type parameters are equal.
// shared type parameters are equal. forall(int i |
forall(int i | i in [0 .. -1 + min(int num | num = method.getNumTypeParameter() or num = other.getNumTypeParameter())] | i in [0 .. -1 +
min(int num | num = method.getNumTypeParameter() or num = other.getNumTypeParameter())]
|
method.getTypeParameterBound(i) = other.getTypeParameterBound(i) method.getTypeParameterBound(i) = other.getTypeParameterBound(i)
) )
} }
@@ -89,7 +85,7 @@ predicate matchingCallSignature(MethodCallSig method, MethodCallSig other) {
* Gets which overload index the MethodSignature has among the overloads of the same name. * Gets which overload index the MethodSignature has among the overloads of the same name.
*/ */
int getOverloadIndex(MethodSignature sig) { int getOverloadIndex(MethodSignature sig) {
sig.getDeclaringType().getMethodOverload(sig.getName(), result) = sig sig.getDeclaringType().getMethodOverload(sig.getName(), result) = sig
} }
/** /**
@@ -100,18 +96,14 @@ predicate signaturesMatch(MethodSignature method, MethodSignature other) {
method.getDeclaringType() = other.getDeclaringType() and method.getDeclaringType() = other.getDeclaringType() and
// same static modifier. // same static modifier.
getKind(method) = getKind(other) and getKind(method) = getKind(other) and
// same name. // same name.
method.getName() = other.getName() and method.getName() = other.getName() and
// same number of parameters. // same number of parameters.
method.getBody().getNumParameter() = other.getBody().getNumParameter() and method.getBody().getNumParameter() = other.getBody().getNumParameter() and
// The types are compared in matchingCallSignature. This is sanity-check that the textual representation of the type-annotations are somewhat similar. // The types are compared in matchingCallSignature. This is sanity-check that the textual representation of the type-annotations are somewhat similar.
forall(int i | i in [0 .. -1 + method.getBody().getNumParameter()] | forall(int i | i in [0 .. -1 + method.getBody().getNumParameter()] |
getParameterTypeAnnotation(method, i) = getParameterTypeAnnotation(other, i) getParameterTypeAnnotation(method, i) = getParameterTypeAnnotation(other, i)
) and ) and
matchingCallSignature(method.getBody().getCallSignature(), other.getBody().getCallSignature()) matchingCallSignature(method.getBody().getCallSignature(), other.getBody().getCallSignature())
} }
@@ -119,22 +111,19 @@ from ClassOrInterface decl, string name, MethodSignature previous, MethodSignatu
where where
previous = decl.getMethod(name) and previous = decl.getMethod(name) and
unreachable = getOtherMatchingSignatures(previous) and unreachable = getOtherMatchingSignatures(previous) and
// If the method is part of inheritance between classes/interfaces, then there can sometimes be reasons for having this pattern. // If the method is part of inheritance between classes/interfaces, then there can sometimes be reasons for having this pattern.
not exists(decl.getASuperTypeDeclaration().getMethod(name)) and not exists(decl.getASuperTypeDeclaration().getMethod(name)) and
not exists(ClassOrInterface sub | not exists(ClassOrInterface sub |
decl = sub.getASuperTypeDeclaration() and decl = sub.getASuperTypeDeclaration() and
exists(sub.getMethod(name)) exists(sub.getMethod(name))
) and ) and
// If a later method overload has more type parameters, then that overload can be selected by explicitly declaring the type arguments at the callsite. // If a later method overload has more type parameters, then that overload can be selected by explicitly declaring the type arguments at the callsite.
// This comparison removes those cases. // This comparison removes those cases.
unreachable.getBody().getNumTypeParameter() <= previous.getBody().getNumTypeParameter() and unreachable.getBody().getNumTypeParameter() <= previous.getBody().getNumTypeParameter() and
// We always select the first of the overloaded methods. // We always select the first of the overloaded methods.
not exists(MethodSignature later | later = getOtherMatchingSignatures(previous) | not exists(MethodSignature later | later = getOtherMatchingSignatures(previous) |
getOverloadIndex(later) < getOverloadIndex(previous) getOverloadIndex(later) < getOverloadIndex(previous)
) )
select unreachable, select unreachable,
"This overload of " + name + "() is unreachable, the $@ overload will always be selected.", previous, "previous" "This overload of " + name + "() is unreachable, the $@ overload will always be selected.",
previous, "previous"

View File

@@ -74,7 +74,8 @@ class CandidateVarAccess extends VarAccess {
* We use this to avoid duplicate alerts about the same underlying cyclic import. * We use this to avoid duplicate alerts about the same underlying cyclic import.
*/ */
VarAccess getFirstCandidateAccess(ImportDeclaration decl) { VarAccess getFirstCandidateAccess(ImportDeclaration decl) {
result = min(decl.getASpecifier().getLocal().getVariable().getAnAccess().(CandidateVarAccess) as p result =
min(decl.getASpecifier().getLocal().getVariable().getAnAccess().(CandidateVarAccess) as p
order by order by
p.getFirstToken().getIndex() p.getFirstToken().getIndex()
) )
@@ -133,14 +134,15 @@ string pathToModule(Module source, Module destination, int steps) {
steps > 1 and steps > 1 and
exists(Module next | exists(Module next |
// Only extend the path to one of the potential successors, as we only need one example. // Only extend the path to one of the potential successors, as we only need one example.
next = min(Module mod | next =
min(Module mod |
isImportedAtRuntime(source, mod) and isImportedAtRuntime(source, mod) and
numberOfStepsToModule(mod, destination, steps - 1) numberOfStepsToModule(mod, destination, steps - 1)
| |
mod order by mod.getName() mod order by mod.getName()
) and ) and
result = repr(getARuntimeImport(source, next)) + " => " + result =
pathToModule(next, destination, steps - 1) repr(getARuntimeImport(source, next)) + " => " + pathToModule(next, destination, steps - 1)
) )
) )
} }

View File

@@ -16,7 +16,6 @@ import javascript
import ExprHasNoEffect import ExprHasNoEffect
import semmle.javascript.RestrictedLocations import semmle.javascript.RestrictedLocations
from Expr e from Expr e
where hasNoEffect(e) where hasNoEffect(e)
select e.(FirstLineOf), "This expression has no effect." select e.(FirstLineOf), "This expression has no effect."

View File

@@ -200,10 +200,12 @@ where
rightExprDescription = getDescription(right.asExpr(), "an expression") and rightExprDescription = getDescription(right.asExpr(), "an expression") and
leftTypeCount = strictcount(left.getAType()) and leftTypeCount = strictcount(left.getAType()) and
rightTypeCount = strictcount(right.getAType()) and rightTypeCount = strictcount(right.getAType()) and
leftTypeDescription = getTypeDescription("is of type " + leftTypes, leftTypeDescription =
"cannot be of type " + rightTypes, leftTypeCount, rightTypeCount) and getTypeDescription("is of type " + leftTypes, "cannot be of type " + rightTypes, leftTypeCount,
rightTypeDescription = getTypeDescription("of type " + rightTypes, rightTypeCount) and
", which cannot be of type " + leftTypes, rightTypeCount, leftTypeCount) rightTypeDescription =
getTypeDescription("of type " + rightTypes, ", which cannot be of type " + leftTypes,
rightTypeCount, leftTypeCount)
select left, select left,
leftExprDescription + " " + leftTypeDescription + ", but it is compared to $@ " + leftExprDescription + " " + leftTypeDescription + ", but it is compared to $@ " +
rightTypeDescription + ".", right, rightExprDescription rightTypeDescription + ".", right, rightExprDescription

View File

@@ -23,48 +23,40 @@ Expr leftChild(Expr e) {
} }
predicate isInConcat(Expr e) { predicate isInConcat(Expr e) {
exists(ParExpr par | isInConcat(par) and par.getExpression() = e) exists(ParExpr par | isInConcat(par) and par.getExpression() = e)
or or
exists(AddExpr a | a.getAnOperand() = e) exists(AddExpr a | a.getAnOperand() = e)
} }
class ConcatenationLiteral extends Expr { class ConcatenationLiteral extends Expr {
ConcatenationLiteral() { ConcatenationLiteral() {
( (
this instanceof TemplateLiteral this instanceof TemplateLiteral
or or
this instanceof Literal this instanceof Literal
) ) and
and isInConcat(this) isInConcat(this)
} }
} }
Expr getConcatChild(Expr e) { Expr getConcatChild(Expr e) {
result = rightChild(e) or result = rightChild(e) or
result = leftChild(e) result = leftChild(e)
} }
Expr getConcatParent(Expr e) { Expr getConcatParent(Expr e) { e = getConcatChild(result) }
e = getConcatChild(result)
}
predicate isWordLike(ConcatenationLiteral lit) { predicate isWordLike(ConcatenationLiteral lit) {
lit.getStringValue().regexpMatch("(?i).*[a-z]{3,}.*") lit.getStringValue().regexpMatch("(?i).*[a-z]{3,}.*")
} }
class ConcatRoot extends AddExpr { class ConcatRoot extends AddExpr {
ConcatRoot() { ConcatRoot() { not isInConcat(this) }
not isInConcat(this)
}
} }
ConcatRoot getAddRoot(AddExpr e) { ConcatRoot getAddRoot(AddExpr e) { result = getConcatParent*(e) }
result = getConcatParent*(e)
}
predicate hasWordLikeFragment(AddExpr e) { predicate hasWordLikeFragment(AddExpr e) { isWordLike(getConcatChild*(getAddRoot(e))) }
isWordLike(getConcatChild*(getAddRoot(e)))
}
from AddExpr e, ConcatenationLiteral l, ConcatenationLiteral r, string word from AddExpr e, ConcatenationLiteral l, ConcatenationLiteral r, string word
where where
@@ -79,7 +71,6 @@ where
word = l.getStringValue().regexpCapture(".* (([-A-Za-z/'\\.:,]*[a-zA-Z]|[0-9]+)[\\.:,!?']*)", 1) and word = l.getStringValue().regexpCapture(".* (([-A-Za-z/'\\.:,]*[a-zA-Z]|[0-9]+)[\\.:,!?']*)", 1) and
r.getStringValue().regexpMatch("[a-zA-Z].*") and r.getStringValue().regexpMatch("[a-zA-Z].*") and
not word.regexpMatch(".*[,\\.:].*[a-zA-Z].*[^a-zA-Z]") and not word.regexpMatch(".*[,\\.:].*[a-zA-Z].*[^a-zA-Z]") and
// There must be a constant-string in the concatenation that looks like a word. // There must be a constant-string in the concatenation that looks like a word.
hasWordLikeFragment(e) hasWordLikeFragment(e)
select l, "This string appears to be missing a space after '" + word + "'." select l, "This string appears to be missing a space after '" + word + "'."

View File

@@ -91,7 +91,8 @@ private string replaceATypoAndLowerCase(Identifier wrong) {
idPart(wrong, wrongPart, offset) idPart(wrong, wrongPart, offset)
| |
normalized_typos(wrongPart, rightPart, _, _, _, _) and normalized_typos(wrongPart, rightPart, _, _, _, _) and
rightName = wrong.getName().substring(0, offset) + rightPart + rightName =
wrong.getName().substring(0, offset) + rightPart +
wrong.getName().suffix(offset + wrongPart.length()) and wrong.getName().suffix(offset + wrongPart.length()) and
result = rightName.toLowerCase() result = rightName.toLowerCase()
) )

View File

@@ -36,7 +36,8 @@ private predicate isBoundInMethod(MethodDeclaration method) {
mod = "react-autobind" mod = "react-autobind"
| |
thiz.flowsTo(DataFlow::moduleImport(mod).getACall().getArgument(0)) thiz.flowsTo(DataFlow::moduleImport(mod).getACall().getArgument(0))
) or )
or
// heuristic reflective binders // heuristic reflective binders
exists(DataFlow::CallNode binder, string calleeName | exists(DataFlow::CallNode binder, string calleeName |
( (
@@ -92,8 +93,8 @@ private DOM::AttributeDefinition getAnEventHandlerAttribute() {
from MethodDeclaration callback, DOM::AttributeDefinition attribute, ThisExpr unbound from MethodDeclaration callback, DOM::AttributeDefinition attribute, ThisExpr unbound
where where
attribute = getAnEventHandlerAttribute() and attribute = getAnEventHandlerAttribute() and
attribute.getValueNode().analyze().getAValue().(AbstractFunction).getFunction() = callback attribute.getValueNode().analyze().getAValue().(AbstractFunction).getFunction() =
.getBody() and callback.getBody() and
unbound.getBinder() = callback.getBody() and unbound.getBinder() = callback.getBody() and
not isBoundInMethod(callback) not isBoundInMethod(callback)
select attribute, select attribute,

View File

@@ -59,7 +59,8 @@ where
not isCallToFunction(cs) and not isCallToFunction(cs) and
// conservatively only flag call sites where _all_ callees are illegal // conservatively only flag call sites where _all_ callees are illegal
forex(DataFlow::InvokeNode cs2, Function otherCallee | forex(DataFlow::InvokeNode cs2, Function otherCallee |
cs2.getInvokeExpr() = cs.getInvokeExpr() and otherCallee = cs2.getACallee() | cs2.getInvokeExpr() = cs.getInvokeExpr() and otherCallee = cs2.getACallee()
|
illegalInvocation(cs, otherCallee, _, _) illegalInvocation(cs, otherCallee, _, _)
) and ) and
// require that all callees are known // require that all callees are known

View File

@@ -35,7 +35,8 @@ predicate guardsAgainstMissingNew(Function f) {
* `true` if `cs` is a `new` expression, and to `false` otherwise. * `true` if `cs` is a `new` expression, and to `false` otherwise.
*/ */
Function getALikelyCallee(DataFlow::InvokeNode cs, boolean isNew) { Function getALikelyCallee(DataFlow::InvokeNode cs, boolean isNew) {
result = min(Function callee, int imprecision | result =
min(Function callee, int imprecision |
callee = cs.getACallee(imprecision) callee = cs.getACallee(imprecision)
| |
callee order by imprecision callee order by imprecision
@@ -84,7 +85,8 @@ predicate whitelistedCall(DataFlow::CallNode call) {
* and start column. * and start column.
*/ */
DataFlow::InvokeNode getFirstInvocation(Function f, boolean isNew) { DataFlow::InvokeNode getFirstInvocation(Function f, boolean isNew) {
result = min(DataFlow::InvokeNode invk, string path, int line, int col | result =
min(DataFlow::InvokeNode invk, string path, int line, int col |
f = getALikelyCallee(invk, isNew) and invk.hasLocationInfo(path, line, col, _, _) f = getALikelyCallee(invk, isNew) and invk.hasLocationInfo(path, line, col, _, _)
| |
invk order by path, line, col invk order by path, line, col

View File

@@ -14,7 +14,8 @@ import javascript
from File f, float n from File f, float n
where where
n = avg(Function fun, int toAvg | n =
avg(Function fun, int toAvg |
fun.getTopLevel().getFile() = f and toAvg = fun.getCyclomaticComplexity() fun.getTopLevel().getFile() = f and toAvg = fun.getCyclomaticComplexity()
| |
toAvg toAvg

View File

@@ -16,7 +16,6 @@ import javascript
import semmle.javascript.security.performance.PolynomialReDoS::PolynomialReDoS import semmle.javascript.security.performance.PolynomialReDoS::PolynomialReDoS
import DataFlow::PathGraph import DataFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "This expensive $@ use depends on $@.", select sink.getNode(), source, sink, "This expensive $@ use depends on $@.",

View File

@@ -292,9 +292,7 @@ class EdgeLabel extends TInputSymbol {
* Gets the state before matching `t`. * Gets the state before matching `t`.
*/ */
pragma[inline] pragma[inline]
State before(RegExpTerm t) { State before(RegExpTerm t) { result = Match(t, 0) }
result = Match(t, 0)
}
/** /**
* Gets a state the NFA may be in after matching `t`. * Gets a state the NFA may be in after matching `t`.
@@ -337,9 +335,7 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
) )
) )
or or
exists(RegExpDot dot | exists(RegExpDot dot | q1 = before(dot) and q2 = after(dot) |
q1 = before(dot) and q2 = after(dot)
|
if dot.getLiteral().isDotAll() then lbl = Any() else lbl = Dot() if dot.getLiteral().isDotAll() then lbl = Any() else lbl = Dot()
) )
or or
@@ -545,7 +541,8 @@ string intersect(InputSymbol c, InputSymbol d) {
* Gets a character matched by character class `cc`. * Gets a character matched by character class `cc`.
*/ */
string choose(RegExpCharacterClass cc) { string choose(RegExpCharacterClass cc) {
result = min(string c | result =
min(string c |
exists(RegExpTerm child | child = cc.getAChild() | exists(RegExpTerm child | child = cc.getAChild() |
c = child.(RegExpConstant).getValue() or c = child.(RegExpConstant).getValue() or
child.(RegExpCharacterRange).isRange(c, _) child.(RegExpCharacterRange).isRange(c, _)

View File

@@ -14,6 +14,7 @@
import javascript import javascript
from RegExpCharEscape rece from RegExpCharEscape rece
where rece.toString() = "\\b" where
and rece.isPartOfRegExpLiteral() rece.toString() = "\\b" and
rece.isPartOfRegExpLiteral()
select rece, "Backspace escape in regular expression." select rece, "Backspace escape in regular expression."

View File

@@ -18,7 +18,8 @@ import javascript
* Indexing is 1-based. * Indexing is 1-based.
*/ */
predicate constantInCharacterClass(RegExpCharacterClass recc, int i, RegExpConstant cc, string val) { predicate constantInCharacterClass(RegExpCharacterClass recc, int i, RegExpConstant cc, string val) {
cc = rank[i](RegExpConstant cc2, int j | cc =
rank[i](RegExpConstant cc2, int j |
cc2 = recc.getChild(j) and cc2.isCharacter() and cc2.getValue() = val cc2 = recc.getChild(j) and cc2.isCharacter() and cc2.getValue() = val
| |
cc2 order by j cc2 order by j

View File

@@ -58,7 +58,8 @@ predicate regExpMatchesString(RegExpTerm t, string s) {
or or
// sequences match the concatenation of their elements // sequences match the concatenation of their elements
exists(RegExpSequence seq | seq = t | exists(RegExpSequence seq | seq = t |
s = concat(int i, RegExpTerm child | s =
concat(int i, RegExpTerm child |
child = seq.getChild(i) child = seq.getChild(i)
| |
any(string subs | regExpMatchesString(child, subs)) order by i any(string subs | regExpMatchesString(child, subs)) order by i

View File

@@ -73,13 +73,9 @@ abstract class RegExpQuery extends DataFlow::CallNode {
class RegExpTestCall extends DataFlow::MethodCallNode, RegExpQuery { class RegExpTestCall extends DataFlow::MethodCallNode, RegExpQuery {
DataFlow::RegExpCreationNode regexp; DataFlow::RegExpCreationNode regexp;
RegExpTestCall() { RegExpTestCall() { this = regexp.getAReference().getAMethodCall("test") }
this = regexp.getAReference().getAMethodCall("test")
}
override RegExpTerm getRegExp() { override RegExpTerm getRegExp() { result = regexp.getRoot() }
result = regexp.getRoot()
}
} }
/** /**
@@ -93,9 +89,7 @@ class RegExpSearchCall extends DataFlow::MethodCallNode, RegExpQuery {
regexp.getAReference().flowsTo(getArgument(0)) regexp.getAReference().flowsTo(getArgument(0))
} }
override RegExpTerm getRegExp() { override RegExpTerm getRegExp() { result = regexp.getRoot() }
result = regexp.getRoot()
}
} }
/** /**
@@ -116,10 +110,12 @@ where
( (
call instanceof RegExpTestCall and call instanceof RegExpTestCall and
not isPossiblyAnchoredOnBothEnds(term) and not isPossiblyAnchoredOnBothEnds(term) and
message = "This regular expression always matches when used in a test $@, as it can match an empty substring." message =
"This regular expression always matches when used in a test $@, as it can match an empty substring."
or or
call instanceof RegExpSearchCall and call instanceof RegExpSearchCall and
not term.getAChild*() instanceof RegExpDollar and not term.getAChild*() instanceof RegExpDollar and
message = "This regular expression always the matches at index 0 when used $@, as it matches the empty substring." message =
"This regular expression always the matches at index 0 when used $@, as it matches the empty substring."
) )
select term, message, call, "here" select term, message, call, "here"

View File

@@ -2,6 +2,7 @@
* Provides predicates for reasoning about regular expressions * Provides predicates for reasoning about regular expressions
* that match URLs and hostname patterns. * that match URLs and hostname patterns.
*/ */
import javascript import javascript
/** /**
@@ -39,9 +40,7 @@ predicate isDotLike(RegExpTerm term) {
predicate matchesBeginningOfString(RegExpTerm term) { predicate matchesBeginningOfString(RegExpTerm term) {
term.isRootTerm() term.isRootTerm()
or or
exists(RegExpTerm parent | exists(RegExpTerm parent | matchesBeginningOfString(parent) |
matchesBeginningOfString(parent)
|
term = parent.(RegExpSequence).getChild(0) term = parent.(RegExpSequence).getChild(0)
or or
parent.(RegExpSequence).getChild(0) instanceof RegExpCaret and parent.(RegExpSequence).getChild(0) instanceof RegExpCaret and
@@ -60,7 +59,11 @@ predicate matchesBeginningOfString(RegExpTerm term) {
* `i` is bound to the index of the last child in the top-level domain part. * `i` is bound to the index of the last child in the top-level domain part.
*/ */
predicate hasTopLevelDomainEnding(RegExpSequence seq, int i) { predicate hasTopLevelDomainEnding(RegExpSequence seq, int i) {
seq.getChild(i).(RegExpConstant).getValue().regexpMatch("(?i)" + RegExpPatterns::commonTLD() + "(:\\d+)?([/?#].*)?") and seq
.getChild(i)
.(RegExpConstant)
.getValue()
.regexpMatch("(?i)" + RegExpPatterns::commonTLD() + "(:\\d+)?([/?#].*)?") and
isDotLike(seq.getChild(i - 1)) and isDotLike(seq.getChild(i - 1)) and
not (i = 1 and matchesBeginningOfString(seq)) not (i = 1 and matchesBeginningOfString(seq))
} }
@@ -69,9 +72,7 @@ predicate hasTopLevelDomainEnding(RegExpSequence seq, int i) {
* Holds if the given regular expression term contains top-level domain preceded by a dot, * Holds if the given regular expression term contains top-level domain preceded by a dot,
* such as `.com`. * such as `.com`.
*/ */
predicate hasTopLevelDomainEnding(RegExpSequence seq) { predicate hasTopLevelDomainEnding(RegExpSequence seq) { hasTopLevelDomainEnding(seq, _) }
hasTopLevelDomainEnding(seq, _)
}
/** /**
* Holds if `term` will always match a hostname, that is, all disjunctions contain * Holds if `term` will always match a hostname, that is, all disjunctions contain

View File

@@ -61,9 +61,12 @@ predicate isIncompleteHostNameRegExpPattern(RegExpTerm regexp, RegExpSequence se
unescapedDot = seq.getChild([0 .. i - 1]).getAChild*() and unescapedDot = seq.getChild([0 .. i - 1]).getAChild*() and
unescapedDot != seq.getChild(i - 1) and // Should not be the '.' immediately before the TLD unescapedDot != seq.getChild(i - 1) and // Should not be the '.' immediately before the TLD
not hasConsecutiveDots(unescapedDot.getParent()) and not hasConsecutiveDots(unescapedDot.getParent()) and
hostname = seq.getChild(i - 2).getRawValue() + seq.getChild(i - 1).getRawValue() + seq.getChild(i).getRawValue() hostname =
seq.getChild(i - 2).getRawValue() + seq.getChild(i - 1).getRawValue() +
seq.getChild(i).getRawValue()
| |
if unescapedDot.getParent() instanceof RegExpQuantifier then ( if unescapedDot.getParent() instanceof RegExpQuantifier
then
// `.*\.example.com` can match `evil.com/?x=.example.com` // `.*\.example.com` can match `evil.com/?x=.example.com`
// //
// This problem only occurs when the pattern is applied against a full URL, not just a hostname/origin. // This problem only occurs when the pattern is applied against a full URL, not just a hostname/origin.
@@ -73,16 +76,20 @@ predicate isIncompleteHostNameRegExpPattern(RegExpTerm regexp, RegExpSequence se
seq.getChild(0) instanceof RegExpCaret and seq.getChild(0) instanceof RegExpCaret and
not seq.getAChild() instanceof RegExpDollar and not seq.getAChild() instanceof RegExpDollar and
seq.getChild([i .. i + 1]).(RegExpConstant).getValue().regexpMatch(".*[/?#].*") and seq.getChild([i .. i + 1]).(RegExpConstant).getValue().regexpMatch(".*[/?#].*") and
msg = "has an unrestricted wildcard '" + msg =
unescapedDot.getParent().(RegExpQuantifier).getRawValue() + "has an unrestricted wildcard '" + unescapedDot.getParent().(RegExpQuantifier).getRawValue()
"' which may cause '" + hostname + "' to be matched anywhere in the URL, outside the hostname." + "' which may cause '" + hostname +
) else ( "' to be matched anywhere in the URL, outside the hostname."
msg = "has an unescaped '.' before '" + hostname + "', so it might match more hosts than expected." else
) msg =
"has an unescaped '.' before '" + hostname +
"', so it might match more hosts than expected."
) )
} }
from RegExpPatternSource re, RegExpTerm regexp, RegExpSequence hostSequence, string msg, string kind, DataFlow::Node aux from
RegExpPatternSource re, RegExpTerm regexp, RegExpSequence hostSequence, string msg, string kind,
DataFlow::Node aux
where where
regexp = re.getRegExpTerm() and regexp = re.getRegExpTerm() and
isIncompleteHostNameRegExpPattern(regexp, hostSequence, msg) and isIncompleteHostNameRegExpPattern(regexp, hostSequence, msg) and
@@ -96,5 +103,4 @@ where
) )
) and ) and
not CharacterEscapes::hasALikelyRegExpPatternMistake(re) not CharacterEscapes::hasALikelyRegExpPatternMistake(re)
select hostSequence, select hostSequence, "This " + kind + " " + msg, aux, "here"
"This " + kind + " " + msg, aux, "here"

View File

@@ -20,9 +20,7 @@ class DangerousScheme extends string {
} }
/** Gets a data-flow node that checks `nd` against the given `scheme`. */ /** Gets a data-flow node that checks `nd` against the given `scheme`. */
DataFlow::Node schemeCheck( DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
DataFlow::Node nd, DangerousScheme scheme
) {
// check of the form `nd.startsWith(scheme)` // check of the form `nd.startsWith(scheme)`
exists(StringOps::StartsWith sw | sw = result | exists(StringOps::StartsWith sw | sw = result |
sw.getBaseString() = nd and sw.getBaseString() = nd and

View File

@@ -46,9 +46,7 @@ predicate isInteriorAnchor(RegExpAnchor term) {
* Holds if `term` contains an anchor that is not the first or last node * Holds if `term` contains an anchor that is not the first or last node
* in its tree, such as `(foo|bar$|baz)`. * in its tree, such as `(foo|bar$|baz)`.
*/ */
predicate containsInteriorAnchor(RegExpTerm term) { predicate containsInteriorAnchor(RegExpTerm term) { isInteriorAnchor(term.getAChild*()) }
isInteriorAnchor(term.getAChild*())
}
/** /**
* Holds if `term` starts with a word boundary or lookbehind assertion, * Holds if `term` starts with a word boundary or lookbehind assertion,
@@ -78,9 +76,7 @@ predicate containsTrailingPseudoAnchor(RegExpSequence term) {
* Holds if `term` is an empty sequence, usually arising from * Holds if `term` is an empty sequence, usually arising from
* literals with a trailing alternative such as `foo|`. * literals with a trailing alternative such as `foo|`.
*/ */
predicate isEmpty(RegExpSequence term) { predicate isEmpty(RegExpSequence term) { term.getNumChild() = 0 }
term.getNumChild() = 0
}
/** /**
* Holds if `term` contains a letter constant. * Holds if `term` contains a letter constant.
@@ -131,14 +127,14 @@ predicate hasMisleadingAnchorPrecedence(RegExpPatternSource src, string msg) {
( (
anchoredTerm = root.getChild(0) and anchoredTerm = root.getChild(0) and
anchoredTerm.getChild(0) instanceof RegExpCaret and anchoredTerm.getChild(0) instanceof RegExpCaret and
not containsLeadingPseudoAnchor(root.getChild([ 1 .. root.getNumChild() - 1 ])) and not containsLeadingPseudoAnchor(root.getChild([1 .. root.getNumChild() - 1])) and
containsLetters(root.getChild([ 1 .. root.getNumChild() - 1 ])) and containsLetters(root.getChild([1 .. root.getNumChild() - 1])) and
direction = "beginning" direction = "beginning"
or or
anchoredTerm = root.getLastChild() and anchoredTerm = root.getLastChild() and
anchoredTerm.getLastChild() instanceof RegExpDollar and anchoredTerm.getLastChild() instanceof RegExpDollar and
not containsTrailingPseudoAnchor(root.getChild([ 0 .. root.getNumChild() - 2 ])) and not containsTrailingPseudoAnchor(root.getChild([0 .. root.getNumChild() - 2])) and
containsLetters(root.getChild([ 0 .. root.getNumChild() - 2 ])) and containsLetters(root.getChild([0 .. root.getNumChild() - 2])) and
direction = "end" direction = "end"
) and ) and
// is not used for replace // is not used for replace
@@ -146,8 +142,10 @@ predicate hasMisleadingAnchorPrecedence(RegExpPatternSource src, string msg) {
replace.getMethodName() = "replace" and replace.getMethodName() = "replace" and
src.getARegExpObject().flowsTo(replace.getArgument(0)) src.getARegExpObject().flowsTo(replace.getArgument(0))
) and ) and
msg = "Misleading operator precedence. The subexpression '" + anchoredTerm.getRawValue() + msg =
"' is anchored at the " + direction + ", but the other parts of this regular expression are not" "Misleading operator precedence. The subexpression '" + anchoredTerm.getRawValue() +
"' is anchored at the " + direction +
", but the other parts of this regular expression are not"
) )
} }
@@ -181,7 +179,8 @@ predicate isSemiAnchoredHostnameRegExp(RegExpPatternSource src, string msg) {
hasTopLevelDomainEnding(tld, i) and hasTopLevelDomainEnding(tld, i) and
isFinalRegExpTerm(tld.getChild(i)) and // nothing is matched after the TLD isFinalRegExpTerm(tld.getChild(i)) and // nothing is matched after the TLD
tld.getChild(0) instanceof RegExpCaret and tld.getChild(0) instanceof RegExpCaret and
msg = "This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end." msg =
"This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end."
) )
} }
@@ -214,7 +213,8 @@ predicate isUnanchoredHostnameRegExp(RegExpPatternSource src, string msg) {
name = "match" and exists(mcn.getAPropertyRead()) name = "match" and exists(mcn.getAPropertyRead())
) )
) and ) and
msg = "When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it." msg =
"When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it."
) )
} }

View File

@@ -105,13 +105,9 @@ class RegExpPatternMistake extends TRegExpPatternMistake {
*/ */
class IdentityEscapeInStringMistake extends RegExpPatternMistake, TIdentityEscapeInStringMistake { class IdentityEscapeInStringMistake extends RegExpPatternMistake, TIdentityEscapeInStringMistake {
RegExpPatternSource src; RegExpPatternSource src;
string char; string char;
string mistake; string mistake;
int index; int index;
ASTNode rawStringNode; ASTNode rawStringNode;
IdentityEscapeInStringMistake() { IdentityEscapeInStringMistake() {
@@ -130,19 +126,19 @@ class IdentityEscapeInStringMistake extends RegExpPatternMistake, TIdentityEscap
} }
/** /**
* A backspace as '\b' in a regular expression string, indicating * A backspace as '\b' in a regular expression string, indicating
* programmer intent to use the word-boundary assertion '\b'. * programmer intent to use the word-boundary assertion '\b'.
*/ */
class BackspaceInStringMistake extends RegExpPatternMistake, TBackspaceInStringMistake { class BackspaceInStringMistake extends RegExpPatternMistake, TBackspaceInStringMistake {
RegExpPatternSource src; RegExpPatternSource src;
int index; int index;
ASTNode rawStringNode; ASTNode rawStringNode;
BackspaceInStringMistake() { this = TBackspaceInStringMistake(src, rawStringNode, index) } BackspaceInStringMistake() { this = TBackspaceInStringMistake(src, rawStringNode, index) }
override string getMessage() { result = "'\\b' is a backspace, and not a word-boundary assertion" } override string getMessage() {
result = "'\\b' is a backspace, and not a word-boundary assertion"
}
override int getIndex() { result = index } override int getIndex() { result = index }

View File

@@ -16,11 +16,16 @@ import javascript
import semmle.javascript.security.dataflow.CommandInjection::CommandInjection import semmle.javascript.security.dataflow.CommandInjection::CommandInjection
import DataFlow::PathGraph import DataFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight, Source sourceNode from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight,
Source sourceNode
where where
cfg.hasFlowPath(source, sink) and cfg.hasFlowPath(source, sink) and
if cfg.isSinkWithHighlight(sink.getNode(), _) (
then cfg.isSinkWithHighlight(sink.getNode(), highlight) if cfg.isSinkWithHighlight(sink.getNode(), _)
else highlight = sink.getNode() and then cfg.isSinkWithHighlight(sink.getNode(), highlight)
else highlight = sink.getNode()
) and
sourceNode = source.getNode() sourceNode = source.getNode()
select highlight, source, sink, "This command depends on $@.", sourceNode, sourceNode.getSourceType() select highlight, source, sink, "This command depends on $@.", sourceNode,
sourceNode.getSourceType()

View File

@@ -15,10 +15,8 @@ import javascript
import semmle.javascript.security.dataflow.ExceptionXss::ExceptionXss import semmle.javascript.security.dataflow.ExceptionXss::ExceptionXss
import DataFlow::PathGraph import DataFlow::PathGraph
from from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink where cfg.hasFlowPath(source, sink)
where
cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, select sink.getNode(), source, sink,
sink.getNode().(Xss::Shared::Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(), sink.getNode().(Xss::Shared::Sink).getVulnerabilityKind() + " vulnerability due to $@.",
"user-provided value" source.getNode(), "user-provided value"

View File

@@ -44,23 +44,17 @@ predicate escapingScheme(string metachar, string regex) {
* A call to `String.prototype.replace` that replaces all instances of a pattern. * A call to `String.prototype.replace` that replaces all instances of a pattern.
*/ */
class Replacement extends StringReplaceCall { class Replacement extends StringReplaceCall {
Replacement() { Replacement() { isGlobal() }
isGlobal()
}
/** /**
* Gets the input of this replacement. * Gets the input of this replacement.
*/ */
DataFlow::Node getInput() { DataFlow::Node getInput() { result = this.getReceiver() }
result = this.getReceiver()
}
/** /**
* Gets the output of this replacement. * Gets the output of this replacement.
*/ */
DataFlow::SourceNode getOutput() { DataFlow::SourceNode getOutput() { result = this }
result = this
}
/** /**
* Holds if this replacement escapes `char` using `metachar`. * Holds if this replacement escapes `char` using `metachar`.
@@ -93,9 +87,7 @@ class Replacement extends StringReplaceCall {
/** /**
* Gets the previous replacement in this chain of replacements. * Gets the previous replacement in this chain of replacements.
*/ */
Replacement getPreviousReplacement() { Replacement getPreviousReplacement() { result.getOutput() = getASimplePredecessor*(getInput()) }
result.getOutput() = getASimplePredecessor*(getInput())
}
/** /**
* Gets an earlier replacement in this chain of replacements that * Gets an earlier replacement in this chain of replacements that

View File

@@ -44,9 +44,7 @@ predicate isSimpleCharacterClass(RegExpCharacterClass t) {
} }
/** Holds if `t` is an alternation of simple terms. */ /** Holds if `t` is an alternation of simple terms. */
predicate isSimpleAlt(RegExpAlt t) { predicate isSimpleAlt(RegExpAlt t) { forall(RegExpTerm ch | ch = t.getAChild() | isSimple(ch)) }
forall(RegExpTerm ch | ch = t.getAChild() | isSimple(ch))
}
/** /**
* Holds if `mce` is of the form `x.replace(re, new)`, where `re` is a global * Holds if `mce` is of the form `x.replace(re, new)`, where `re` is a global

View File

@@ -13,26 +13,20 @@
import javascript import javascript
/** Gets a property name of `req` which refers to data usually derived from cookie data. */ /** Gets a property name of `req` which refers to data usually derived from cookie data. */
string cookieProperty() { string cookieProperty() { result = "session" or result = "cookies" or result = "user" }
result = "session" or result = "cookies" or result = "user"
}
/** Gets a data flow node that flows to the base of an access to `cookies`, `session`, or `user`. */ /** Gets a data flow node that flows to the base of an access to `cookies`, `session`, or `user`. */
private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) { private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) {
t.start() and t.start() and
exists(DataFlow::PropRead value | exists(DataFlow::PropRead value |
value = result.getAPropertyRead(cookieProperty()).getAPropertyRead() and value = result.getAPropertyRead(cookieProperty()).getAPropertyRead() and
// Ignore accesses to values that are part of a CSRF or captcha check // Ignore accesses to values that are part of a CSRF or captcha check
not value.getPropertyName().regexpMatch("(?i).*(csrf|xsrf|captcha).*") and not value.getPropertyName().regexpMatch("(?i).*(csrf|xsrf|captcha).*") and
// Ignore calls like `req.session.save()` // Ignore calls like `req.session.save()`
not value = any(DataFlow::InvokeNode call).getCalleeNode() not value = any(DataFlow::InvokeNode call).getCalleeNode()
) )
or or
exists(DataFlow::TypeBackTracker t2 | exists(DataFlow::TypeBackTracker t2 | result = nodeLeadingToCookieAccess(t2).backtrack(t2, t))
result = nodeLeadingToCookieAccess(t2).backtrack(t2, t)
)
} }
/** Gets a data flow node that flows to the base of an access to `cookies`, `session`, or `user`. */ /** Gets a data flow node that flows to the base of an access to `cookies`, `session`, or `user`. */
@@ -52,9 +46,7 @@ private DataFlow::SourceNode getARouteUsingCookies(DataFlow::TypeTracker t) {
t.start() and t.start() and
isRouteHandlerUsingCookies(result) isRouteHandlerUsingCookies(result)
or or
exists(DataFlow::TypeTracker t2 | exists(DataFlow::TypeTracker t2 | result = getARouteUsingCookies(t2).track(t2, t))
result = getARouteUsingCookies(t2).track(t2, t)
)
} }
/** Gets a data flow node referring to a route handler that uses cookies. */ /** Gets a data flow node referring to a route handler that uses cookies. */
@@ -113,7 +105,6 @@ from
where where
router = setup.getRouter() and router = setup.getRouter() and
handler = setup.getARouteHandlerExpr() and handler = setup.getARouteHandlerExpr() and
// Require that the handler uses cookies and has cookie middleware. // Require that the handler uses cookies and has cookie middleware.
// //
// In practice, handlers that use cookies always have the cookie middleware or // In practice, handlers that use cookies always have the cookie middleware or
@@ -122,10 +113,8 @@ where
// don't trust it to detect the presence of CSRF middleware either. // don't trust it to detect the presence of CSRF middleware either.
getARouteUsingCookies().flowsToExpr(handler) and getARouteUsingCookies().flowsToExpr(handler) and
hasCookieMiddleware(handler, cookie) and hasCookieMiddleware(handler, cookie) and
// Only flag the cookie parser registered first. // Only flag the cookie parser registered first.
not hasCookieMiddleware(cookie, _) and not hasCookieMiddleware(cookie, _) and
not hasCsrfMiddleware(handler) and not hasCsrfMiddleware(handler) and
// Only warn for the last handler in a chain. // Only warn for the last handler in a chain.
handler.isLastHandler() and handler.isLastHandler() and

View File

@@ -1,7 +1,7 @@
/** /**
* @name Prototype pollution in utility function * @name Prototype pollution in utility function
* @description Recursively copying properties between objects may cause * @description Recursively copying properties between objects may cause
accidental modification of a built-in prototype object. * accidental modification of a built-in prototype object.
* @kind path-problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision high * @precision high
@@ -30,9 +30,7 @@ predicate arePropertiesEnumerated(DataFlow::SourceNode node) {
predicate isEnumeratedPropName(Node node) { predicate isEnumeratedPropName(Node node) {
node instanceof EnumeratedPropName node instanceof EnumeratedPropName
or or
exists(Node pred | exists(Node pred | isEnumeratedPropName(pred) |
isEnumeratedPropName(pred)
|
node = pred.getASuccessor() node = pred.getASuccessor()
or or
argumentPassingStep(_, pred, _, node) argumentPassingStep(_, pred, _, node)
@@ -54,7 +52,6 @@ predicate isPotentiallyObjectPrototype(SourceNode node) {
exists(Node base, Node key | exists(Node base, Node key |
dynamicPropReadStep(base, key, node) and dynamicPropReadStep(base, key, node) and
isEnumeratedPropName(key) and isEnumeratedPropName(key) and
// Ignore cases where the properties of `base` are enumerated, to avoid FPs // Ignore cases where the properties of `base` are enumerated, to avoid FPs
// where the key came from that enumeration (and thus will not return Object.prototype). // where the key came from that enumeration (and thus will not return Object.prototype).
// For example, `src[key]` in `for (let key in src) { ... src[key] ... }` will generally // For example, `src[key]` in `for (let key in src) { ... src[key] ... }` will generally
@@ -62,9 +59,7 @@ predicate isPotentiallyObjectPrototype(SourceNode node) {
not arePropertiesEnumerated(base.getALocalSource()) not arePropertiesEnumerated(base.getALocalSource())
) )
or or
exists(Node use | exists(Node use | isPotentiallyObjectPrototype(use.getALocalSource()) |
isPotentiallyObjectPrototype(use.getALocalSource())
|
argumentPassingStep(_, use, _, node) argumentPassingStep(_, use, _, node)
) )
} }
@@ -87,7 +82,6 @@ predicate dynamicPropWrite(DataFlow::Node base, DataFlow::Node prop, DataFlow::N
rhs = write.getRhs().flow() and rhs = write.getRhs().flow() and
not exists(prop.getStringValue()) and not exists(prop.getStringValue()) and
not arePropertiesEnumerated(base.getALocalSource()) and not arePropertiesEnumerated(base.getALocalSource()) and
// Prune writes that are unlikely to modify Object.prototype. // Prune writes that are unlikely to modify Object.prototype.
// This is mainly for performance, but may block certain results due to // This is mainly for performance, but may block certain results due to
// not tracking out of function returns and into callbacks. // not tracking out of function returns and into callbacks.
@@ -435,9 +429,7 @@ private DataFlow::SourceNode getANodeLeadingToBase(DataFlow::TypeBackTracker t,
isPrototypePollutingAssignment(base, _, _, _) and isPrototypePollutingAssignment(base, _, _, _) and
result = base.getALocalSource() result = base.getALocalSource()
or or
exists(DataFlow::TypeBackTracker t2 | exists(DataFlow::TypeBackTracker t2 | result = getANodeLeadingToBase(t2, base).backtrack(t2, t))
result = getANodeLeadingToBase(t2, base).backtrack(t2, t)
)
} }
/** /**

View File

@@ -14,8 +14,9 @@ import PortalExitSource
import PortalEntrySink import PortalEntrySink
from from
TaintTracking::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink, Portal p1, TaintTracking::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink,
Portal p2, DataFlow::FlowLabel lbl1, DataFlow::FlowLabel lbl2, DataFlow::MidPathNode last Portal p1, Portal p2, DataFlow::FlowLabel lbl1, DataFlow::FlowLabel lbl2,
DataFlow::MidPathNode last
where where
cfg = source.getConfiguration() and cfg = source.getConfiguration() and
last = source.getASuccessor*() and last = source.getASuccessor*() and

View File

@@ -11,7 +11,8 @@ import Configurations
import PortalExitSource import PortalExitSource
import SinkFromAnnotation import SinkFromAnnotation
from DataFlow::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink, from
DataFlow::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink,
Portal p, DataFlow::MidPathNode last Portal p, DataFlow::MidPathNode last
where where
cfg = source.getConfiguration() and cfg = source.getConfiguration() and

View File

@@ -11,7 +11,8 @@ import Configurations
import PortalEntrySink import PortalEntrySink
import SourceFromAnnotation import SourceFromAnnotation
from DataFlow::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink, from
DataFlow::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink,
Portal p, DataFlow::MidPathNode last Portal p, DataFlow::MidPathNode last
where where
cfg = source.getConfiguration() and cfg = source.getConfiguration() and

View File

@@ -127,7 +127,8 @@ class AdditionalStepSpec extends ExternalData {
exists(string config | exists(string config |
if getField(4) = "" then config = "any configuration" else config = getConfiguration() if getField(4) = "" then config = "any configuration" else config = getConfiguration()
| |
result = "edge from " + getStartPortal() + " to " + getEndPortal() + ", transforming " + result =
"edge from " + getStartPortal() + " to " + getEndPortal() + ", transforming " +
getStartFlowLabel() + " into " + getEndFlowLabel() + " for " + config getStartFlowLabel() + " into " + getEndFlowLabel() + " for " + config
) )
} }

View File

@@ -13,7 +13,7 @@ import javascript
import Expressions.ExprHasNoEffect import Expressions.ExprHasNoEffect
DataFlow::SourceNode callsArray(DataFlow::TypeBackTracker t, DataFlow::MethodCallNode call) { DataFlow::SourceNode callsArray(DataFlow::TypeBackTracker t, DataFlow::MethodCallNode call) {
isIgnoredPureArrayCall(call) and isIgnoredPureArrayCall(call) and
t.start() and t.start() and
result = call.getReceiver().getALocalSource() result = call.getReceiver().getALocalSource()
or or
@@ -39,7 +39,7 @@ predicate isIgnoredPureArrayCall(DataFlow::MethodCallNode call) {
} }
from DataFlow::MethodCallNode call from DataFlow::MethodCallNode call
where where
callsArray(call) instanceof DataFlow::ArrayCreationNode and callsArray(call) instanceof DataFlow::ArrayCreationNode and
not call.getReceiver().asExpr().(ArrayExpr).getSize() = 0 not call.getReceiver().asExpr().(ArrayExpr).getSize() = 0
select call, "Result from call to " + call.getMethodName() + " ignored." select call, "Result from call to " + call.getMethodName() + " ignored."

View File

@@ -32,7 +32,8 @@ class ValueReturn extends ReturnStmt {
/** Gets the lexically first explicit return statement in function `f`. */ /** Gets the lexically first explicit return statement in function `f`. */
ValueReturn getFirstExplicitReturn(Function f) { ValueReturn getFirstExplicitReturn(Function f) {
result = min(ValueReturn ret | result =
min(ValueReturn ret |
ret.getContainer() = f ret.getContainer() = f
| |
ret order by ret.getLocation().getStartLine(), ret.getLocation().getStartColumn() ret order by ret.getLocation().getStartLine(), ret.getLocation().getStartColumn()

View File

@@ -21,40 +21,41 @@ predicate returnsVoid(Function f) {
} }
predicate isStub(Function f) { predicate isStub(Function f) {
f.getBody().(BlockStmt).getNumChild() = 0 f.getBody().(BlockStmt).getNumChild() = 0
or or
f instanceof ExternalDecl f instanceof ExternalDecl
} }
/** /**
* Holds if `e` is in a syntactic context where it likely is fine that the value of `e` comes from a call to a returnless function. * Holds if `e` is in a syntactic context where it likely is fine that the value of `e` comes from a call to a returnless function.
*/ */
predicate benignContext(Expr e) { predicate benignContext(Expr e) {
inVoidContext(e) or inVoidContext(e)
or
// A return statement is often used to just end the function. // A return statement is often used to just end the function.
e = any(Function f).getBody() e = any(Function f).getBody()
or or
e = any(ReturnStmt r).getExpr() e = any(ReturnStmt r).getExpr()
or or
exists(ConditionalExpr cond | cond.getABranch() = e and benignContext(cond)) exists(ConditionalExpr cond | cond.getABranch() = e and benignContext(cond))
or or
exists(LogicalBinaryExpr bin | bin.getAnOperand() = e and benignContext(bin)) exists(LogicalBinaryExpr bin | bin.getAnOperand() = e and benignContext(bin))
or or
exists(Expr parent | parent.getUnderlyingValue() = e and benignContext(parent)) exists(Expr parent | parent.getUnderlyingValue() = e and benignContext(parent))
or or
any(VoidExpr voidExpr).getOperand() = e any(VoidExpr voidExpr).getOperand() = e
or or
// weeds out calls inside HTML-attributes. // weeds out calls inside HTML-attributes.
e.getParent().(ExprStmt).getParent() instanceof CodeInAttribute or e.getParent().(ExprStmt).getParent() instanceof CodeInAttribute
or
// and JSX-attributes. // and JSX-attributes.
e = any(JSXAttribute attr).getValue() or e = any(JSXAttribute attr).getValue()
or
exists(AwaitExpr await | await.getOperand() = e and benignContext(await)) exists(AwaitExpr await | await.getOperand() = e and benignContext(await))
or or
// Avoid double reporting with js/trivial-conditional // Avoid double reporting with js/trivial-conditional
isExplicitConditional(_, e) isExplicitConditional(_, e)
or or
// Avoid double reporting with js/comparison-between-incompatible-types // Avoid double reporting with js/comparison-between-incompatible-types
any(Comparison binOp).getAnOperand() = e any(Comparison binOp).getAnOperand() = e
or or
@@ -62,12 +63,14 @@ predicate benignContext(Expr e) {
any(PropAccess ac).getBase() = e any(PropAccess ac).getBase() = e
or or
// Avoid double-reporting with js/unused-local-variable // Avoid double-reporting with js/unused-local-variable
exists(VariableDeclarator v | v.getInit() = e and v.getBindingPattern().getVariable() instanceof UnusedLocal) exists(VariableDeclarator v |
v.getInit() = e and v.getBindingPattern().getVariable() instanceof UnusedLocal
)
or or
// Avoid double reporting with js/call-to-non-callable // Avoid double reporting with js/call-to-non-callable
any(InvokeExpr invoke).getCallee() = e any(InvokeExpr invoke).getCallee() = e
or or
// arguments to Promise.resolve (and promise library variants) are benign. // arguments to Promise.resolve (and promise library variants) are benign.
e = any(PromiseCreationCall promise).getValue().asExpr() e = any(PromiseCreationCall promise).getValue().asExpr()
} }
@@ -86,15 +89,13 @@ predicate alwaysThrows(Function f) {
/** /**
* Holds if the last statement in the function is flagged by the js/useless-expression query. * Holds if the last statement in the function is flagged by the js/useless-expression query.
*/ */
predicate lastStatementHasNoEffect(Function f) { predicate lastStatementHasNoEffect(Function f) { hasNoEffect(f.getExit().getAPredecessor()) }
hasNoEffect(f.getExit().getAPredecessor())
}
/** /**
* Holds if `func` is a callee of `call`, and all possible callees of `call` never return a value. * Holds if `func` is a callee of `call`, and all possible callees of `call` never return a value.
*/ */
predicate callToVoidFunction(DataFlow::CallNode call, Function func) { predicate callToVoidFunction(DataFlow::CallNode call, Function func) {
not call.isIncomplete() and not call.isIncomplete() and
func = call.getACallee() and func = call.getACallee() and
forall(Function f | f = call.getACallee() | forall(Function f | f = call.getACallee() |
returnsVoid(f) and not isStub(f) and not alwaysThrows(f) returnsVoid(f) and not isStub(f) and not alwaysThrows(f)
@@ -122,22 +123,20 @@ predicate hasNonVoidCallbackMethod(string name) {
DataFlow::SourceNode array(DataFlow::TypeTracker t) { DataFlow::SourceNode array(DataFlow::TypeTracker t) {
t.start() and result instanceof DataFlow::ArrayCreationNode t.start() and result instanceof DataFlow::ArrayCreationNode
or or
exists (DataFlow::TypeTracker t2 | exists(DataFlow::TypeTracker t2 | result = array(t2).track(t2, t))
result = array(t2).track(t2, t)
)
} }
DataFlow::SourceNode array() { result = array(DataFlow::TypeTracker::end()) } DataFlow::SourceNode array() { result = array(DataFlow::TypeTracker::end()) }
/** /**
* Holds if `call` is an Array or Lodash method accepting a callback `func`, * Holds if `call` is an Array or Lodash method accepting a callback `func`,
* where the `call` expects a callback that returns an expression, * where the `call` expects a callback that returns an expression,
* but `func` does not return a value. * but `func` does not return a value.
*/ */
predicate voidArrayCallback(DataFlow::CallNode call, Function func) { predicate voidArrayCallback(DataFlow::CallNode call, Function func) {
hasNonVoidCallbackMethod(call.getCalleeName()) and hasNonVoidCallbackMethod(call.getCalleeName()) and
exists(int index | exists(int index |
index = min(int i | exists(call.getCallback(i))) and index = min(int i | exists(call.getCallback(i))) and
func = call.getCallback(index).getFunction() func = call.getCallback(index).getFunction()
) and ) and
returnsVoid(func) and returnsVoid(func) and
@@ -151,26 +150,23 @@ predicate voidArrayCallback(DataFlow::CallNode call, Function func) {
} }
predicate hasNonVoidReturnType(Function f) { predicate hasNonVoidReturnType(Function f) {
exists(TypeAnnotation type | type = f.getReturnTypeAnnotation() | exists(TypeAnnotation type | type = f.getReturnTypeAnnotation() | not type.isVoid())
not type.isVoid()
)
} }
/** /**
* Provides classes for working with various Deferred implementations. * Provides classes for working with various Deferred implementations.
* It is a heuristic. The heuristic assume that a class is a promise defintion * It is a heuristic. The heuristic assume that a class is a promise defintion
* if the class is called "Deferred" and the method `resolve` is called on an instance. * if the class is called "Deferred" and the method `resolve` is called on an instance.
* *
* Removes some false positives in the js/use-of-returnless-function query. * Removes some false positives in the js/use-of-returnless-function query.
*/ */
module Deferred { module Deferred {
/** /**
* An instance of a `Deferred` class. * An instance of a `Deferred` class.
* For example the result from `new Deferred()` or `new $.Deferred()`. * For example the result from `new Deferred()` or `new $.Deferred()`.
*/ */
class DeferredInstance extends DataFlow::NewNode { class DeferredInstance extends DataFlow::NewNode {
// Describes both `new Deferred()`, `new $.Deferred` and other variants. // Describes both `new Deferred()`, `new $.Deferred` and other variants.
DeferredInstance() { this.getCalleeName() = "Deferred" } DeferredInstance() { this.getCalleeName() = "Deferred" }
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) { private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
@@ -179,7 +175,7 @@ module Deferred {
or or
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t)) exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
} }
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) } DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
} }
@@ -188,7 +184,7 @@ module Deferred {
*/ */
private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance { private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance {
DeferredPromiseDefinition() { DeferredPromiseDefinition() {
// hardening of the "Deferred" heuristic: a method call to `resolve`. // hardening of the "Deferred" heuristic: a method call to `resolve`.
exists(ref().getAMethodCall("resolve")) exists(ref().getAMethodCall("resolve"))
} }
@@ -210,12 +206,14 @@ module Deferred {
from DataFlow::CallNode call, Function func, string name, string msg from DataFlow::CallNode call, Function func, string name, string msg
where where
( (
callToVoidFunction(call, func) and callToVoidFunction(call, func) and
msg = "the $@ does not return anything, yet the return value is used." and msg = "the $@ does not return anything, yet the return value is used." and
name = func.describe() name = func.describe()
or or
voidArrayCallback(call, func) and voidArrayCallback(call, func) and
msg = "the $@ does not return anything, yet the return value from the call to " + call.getCalleeName() + " is used." and msg =
"the $@ does not return anything, yet the return value from the call to " +
call.getCalleeName() + " is used." and
name = "callback function" name = "callback function"
) and ) and
not benignContext(call.getEnclosingExpr()) and not benignContext(call.getEnclosingExpr()) and
@@ -224,5 +222,4 @@ where
not oneshotClosure(call) and not oneshotClosure(call) and
not hasNonVoidReturnType(func) and not hasNonVoidReturnType(func) and
not call.getEnclosingExpr() instanceof SuperCall not call.getEnclosingExpr() instanceof SuperCall
select select call, msg, func, name
call, msg, func, name

View File

@@ -1,6 +1,6 @@
/** /**
* Provides predicates for working with useless conditionals. * Provides predicates for working with useless conditionals.
*/ */
import javascript import javascript
@@ -18,4 +18,4 @@ predicate isExplicitConditional(ASTNode cond, Expr e) {
or or
isExplicitConditional(_, cond) and isExplicitConditional(_, cond) and
e = cond.(Expr).getUnderlyingValue().(LogicalBinaryExpr).getAnOperand() e = cond.(Expr).getUnderlyingValue().(LogicalBinaryExpr).getAnOperand()
} }

View File

@@ -266,7 +266,8 @@ predicate similarLines(File f, int line) {
} }
private predicate similarLinesPerEquivalenceClass(int equivClass, int lines, File f) { private predicate similarLinesPerEquivalenceClass(int equivClass, int lines, File f) {
lines = strictsum(SimilarBlock b, int toSum | lines =
strictsum(SimilarBlock b, int toSum |
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and
toSum = b.sourceLines() toSum = b.sourceLines()
| |
@@ -278,7 +279,8 @@ pragma[noopt]
private predicate similarLinesCovered(File f, int coveredLines, File otherFile) { private predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
exists(int numLines | numLines = f.getNumberOfLines() | exists(int numLines | numLines = f.getNumberOfLines() |
exists(int coveredApprox | exists(int coveredApprox |
coveredApprox = strictsum(int num | coveredApprox =
strictsum(int num |
exists(int equivClass | exists(int equivClass |
similarLinesPerEquivalenceClass(equivClass, num, f) and similarLinesPerEquivalenceClass(equivClass, num, f) and
similarLinesPerEquivalenceClass(equivClass, num, otherFile) and similarLinesPerEquivalenceClass(equivClass, num, otherFile) and
@@ -301,7 +303,8 @@ predicate duplicateLines(File f, int line) {
} }
private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, File f) { private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, File f) {
lines = strictsum(DuplicateBlock b, int toSum | lines =
strictsum(DuplicateBlock b, int toSum |
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and
toSum = b.sourceLines() toSum = b.sourceLines()
| |
@@ -313,7 +316,8 @@ pragma[noopt]
private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) { private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
exists(int numLines | numLines = f.getNumberOfLines() | exists(int numLines | numLines = f.getNumberOfLines() |
exists(int coveredApprox | exists(int coveredApprox |
coveredApprox = strictsum(int num | coveredApprox =
strictsum(int num |
exists(int equivClass | exists(int equivClass |
duplicateLinesPerEquivalenceClass(equivClass, num, f) and duplicateLinesPerEquivalenceClass(equivClass, num, f) and
duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and

View File

@@ -48,7 +48,8 @@ class DefectResult extends int {
/** Gets the URL corresponding to the location of this query result. */ /** Gets the URL corresponding to the location of this query result. */
string getURL() { string getURL() {
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() result =
+ ":" + getEndLine() + ":" + getEndColumn() "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
getEndLine() + ":" + getEndColumn()
} }
} }

View File

@@ -66,7 +66,8 @@ class MetricResult extends int {
/** Gets the URL corresponding to the location of this query result. */ /** Gets the URL corresponding to the location of this query result. */
string getURL() { string getURL() {
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() result =
+ ":" + getEndLine() + ":" + getEndColumn() "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
getEndLine() + ":" + getEndColumn()
} }
} }

View File

@@ -86,9 +86,7 @@ SourceNode nodeLeadingToInvocation() {
* Holds if there is a call edge `invoke -> f` between a relevant invocation * Holds if there is a call edge `invoke -> f` between a relevant invocation
* and a relevant function. * and a relevant function.
*/ */
predicate relevantCall(RelevantInvoke invoke, RelevantFunction f) { predicate relevantCall(RelevantInvoke invoke, RelevantFunction f) { FlowSteps::calls(invoke, f) }
FlowSteps::calls(invoke, f)
}
/** /**
* A call site that can be resolved to a function in the same project. * A call site that can be resolved to a function in the same project.
@@ -105,9 +103,7 @@ class ResolvableCall extends RelevantInvoke {
* A call site that could not be resolved. * A call site that could not be resolved.
*/ */
class UnresolvableCall extends RelevantInvoke { class UnresolvableCall extends RelevantInvoke {
UnresolvableCall() { UnresolvableCall() { not this instanceof ResolvableCall }
not this instanceof ResolvableCall
}
} }
/** /**

View File

@@ -11,8 +11,6 @@
import javascript import javascript
import CallGraphQuality import CallGraphQuality
Import unresolvableImport() { Import unresolvableImport() { not exists(result.getImportedModule()) }
not exists(result.getImportedModule())
}
select projectRoot(), count(unresolvableImport()) select projectRoot(), count(unresolvableImport())

View File

@@ -12,7 +12,8 @@ import javascript
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
from File f, string cause from File f, string cause
where not extraction_data(f, _, _, _) and cause = "No extraction_data for this file" where
or not extraction_data(f, _, _, _) and cause = "No extraction_data for this file"
not extraction_time(f, _,_, _) and cause = "No extraction_time for this file" or
select f, cause not extraction_time(f, _, _, _) and cause = "No extraction_time for this file"
select f, cause

View File

@@ -9,4 +9,5 @@
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
from PhaseName phaseName from PhaseName phaseName
select phaseName, Aggregated::getCpuTime(phaseName) as CPU_NANO, Aggregated::getWallclockTime(phaseName) as WALLCLOCK_NANO select phaseName, Aggregated::getCpuTime(phaseName) as CPU_NANO,
Aggregated::getWallclockTime(phaseName) as WALLCLOCK_NANO

View File

@@ -235,7 +235,8 @@ class BasicBlock extends @cfg_node, Locatable {
*/ */
private int nextDefOrUseAfter(PurelyLocalVariable v, int i, VarDef d) { private int nextDefOrUseAfter(PurelyLocalVariable v, int i, VarDef d) {
defAt(i, v, d) and defAt(i, v, d) and
result = min(int j | result =
min(int j |
(defAt(j, v, _) or useAt(j, v, _) or j = length()) and (defAt(j, v, _) or useAt(j, v, _) or j = length()) and
j > i j > i
) )

View File

@@ -57,7 +57,8 @@ module CharacterEscapes {
hasRawStringAndQuote(n, delim, rawStringNode, raw) and hasRawStringAndQuote(n, delim, rawStringNode, raw) and
if rawStringNode instanceof RegExpLiteral if rawStringNode instanceof RegExpLiteral
then then
additionalEscapeChars = Sets::regexpMetaChars() + Sets::regexpAssertionChars() + Sets::regexpCharClassChars() + additionalEscapeChars =
Sets::regexpMetaChars() + Sets::regexpAssertionChars() + Sets::regexpCharClassChars() +
Sets::regexpBackreferenceChars() Sets::regexpBackreferenceChars()
else additionalEscapeChars = "b" else additionalEscapeChars = "b"
| |

View File

@@ -123,13 +123,14 @@ class ClassOrInterface extends @classorinterface, TypeParameterized {
* Anonymous classes and interfaces do not have a canonical name. * Anonymous classes and interfaces do not have a canonical name.
*/ */
TypeName getTypeName() { result.getADefinition() = this } TypeName getTypeName() { result.getADefinition() = this }
/** /**
* Gets the ClassOrInterface corresponding to either a super type or an implemented interface. * Gets the ClassOrInterface corresponding to either a super type or an implemented interface.
*/ */
ClassOrInterface getASuperTypeDeclaration() { ClassOrInterface getASuperTypeDeclaration() {
this.getSuperClass().(VarAccess).getVariable().getADeclaration() = result.getIdentifier() or this.getSuperClass().(VarAccess).getVariable().getADeclaration() = result.getIdentifier() or
this.getASuperInterface().(LocalTypeAccess).getLocalTypeName().getADeclaration() = result.getIdentifier() this.getASuperInterface().(LocalTypeAccess).getLocalTypeName().getADeclaration() =
result.getIdentifier()
} }
} }
@@ -334,11 +335,8 @@ class ClassExpr extends @classexpr, ClassDefinition, Expr {
else else
if exists(getClassInitializedMember()) if exists(getClassInitializedMember())
then then
result = min(ClassInitializedMember m | result =
m = getClassInitializedMember() min(ClassInitializedMember m | m = getClassInitializedMember() | m order by m.getIndex())
|
m order by m.getIndex()
)
else result = this else result = this
} }
@@ -688,7 +686,8 @@ class MethodDeclaration extends MemberDeclaration {
*/ */
int getOverloadIndex() { int getOverloadIndex() {
exists(ClassOrInterface type, string name | exists(ClassOrInterface type, string name |
this = rank[result + 1](MethodDeclaration method, int i | this =
rank[result + 1](MethodDeclaration method, int i |
methodDeclaredInType(type, name, i, method) methodDeclaredInType(type, name, i, method)
| |
method order by i method order by i
@@ -696,7 +695,8 @@ class MethodDeclaration extends MemberDeclaration {
) )
or or
exists(ClassDefinition type | exists(ClassDefinition type |
this = rank[result + 1](ConstructorDeclaration ctor, int i | this =
rank[result + 1](ConstructorDeclaration ctor, int i |
ctor = type.getMemberByIndex(i) ctor = type.getMemberByIndex(i)
| |
ctor order by i ctor order by i
@@ -1156,7 +1156,8 @@ class FunctionCallSignature extends @function_call_signature, CallSignature {
/** Gets the index of this function call signature among the function call signatures in the enclosing type. */ /** Gets the index of this function call signature among the function call signatures in the enclosing type. */
int getOverloadIndex() { int getOverloadIndex() {
exists(ClassOrInterface type | type = getDeclaringType() | exists(ClassOrInterface type | type = getDeclaringType() |
this = rank[result + 1](FunctionCallSignature sig, int i | this =
rank[result + 1](FunctionCallSignature sig, int i |
sig = type.getMemberByIndex(i) sig = type.getMemberByIndex(i)
| |
sig order by i sig order by i
@@ -1186,7 +1187,8 @@ class ConstructorCallSignature extends @constructor_call_signature, CallSignatur
/** Gets the index of this constructor call signature among the constructor call signatures in the enclosing type. */ /** Gets the index of this constructor call signature among the constructor call signatures in the enclosing type. */
int getOverloadIndex() { int getOverloadIndex() {
exists(ClassOrInterface type | type = getDeclaringType() | exists(ClassOrInterface type | type = getDeclaringType() |
this = rank[result + 1](ConstructorCallSignature sig, int i | this =
rank[result + 1](ConstructorCallSignature sig, int i |
sig = type.getMemberByIndex(i) sig = type.getMemberByIndex(i)
| |
sig order by i sig order by i

View File

@@ -284,7 +284,8 @@ private SsaDefinition getAPseudoDefinitionInput(SsaDefinition nd) {
*/ */
private int nextDefAfter(BasicBlock bb, Variable v, int i, VarDef d) { private int nextDefAfter(BasicBlock bb, Variable v, int i, VarDef d) {
bb.defAt(i, v, d) and bb.defAt(i, v, d) and
result = min(int jj | result =
min(int jj |
(bb.defAt(jj, v, _) or jj = bb.length()) and (bb.defAt(jj, v, _) or jj = bb.length()) and
jj > i jj > i
) )

View File

@@ -78,9 +78,7 @@ class ImportDeclaration extends Stmt, Import, @importdeclaration {
} }
/** Holds if this is declared with the `type` keyword, so it only imports types. */ /** Holds if this is declared with the `type` keyword, so it only imports types. */
predicate isTypeOnly() { predicate isTypeOnly() { hasTypeKeyword(this) }
hasTypeKeyword(this)
}
override predicate isAmbient() { override predicate isAmbient() {
Stmt.super.isAmbient() or Stmt.super.isAmbient() or
@@ -268,9 +266,7 @@ abstract class ExportDeclaration extends Stmt, @exportdeclaration {
abstract DataFlow::Node getSourceNode(string name); abstract DataFlow::Node getSourceNode(string name);
/** Holds if is declared with the `type` keyword, so only types are exported. */ /** Holds if is declared with the `type` keyword, so only types are exported. */
predicate isTypeOnly() { predicate isTypeOnly() { hasTypeKeyword(this) }
hasTypeKeyword(this)
}
override predicate isAmbient() { override predicate isAmbient() {
Stmt.super.isAmbient() or Stmt.super.isAmbient() or
@@ -614,15 +610,10 @@ abstract class ReExportDeclaration extends ExportDeclaration {
* *
* Gets the module from which this declaration re-exports. * Gets the module from which this declaration re-exports.
*/ */
deprecated deprecated ES2015Module getImportedModule() { result = getReExportedModule() }
ES2015Module getImportedModule() {
result = getReExportedModule()
}
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */ /** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
ES2015Module getReExportedES2015Module() { ES2015Module getReExportedES2015Module() { result = getReExportedModule() }
result = getReExportedModule()
}
/** Gets the module from which this declaration re-exports. */ /** Gets the module from which this declaration re-exports. */
Module getReExportedModule() { Module getReExportedModule() {
@@ -635,7 +626,8 @@ abstract class ReExportDeclaration extends ExportDeclaration {
* Gets a module in a `node_modules/@types/` folder that matches the imported module name. * Gets a module in a `node_modules/@types/` folder that matches the imported module name.
*/ */
private Module resolveFromTypeRoot() { private Module resolveFromTypeRoot() {
result.getFile() = min(TypeRootFolder typeRoot | result.getFile() =
min(TypeRootFolder typeRoot |
| |
typeRoot.getModuleFile(getImportedPath().getStringValue()) typeRoot.getModuleFile(getImportedPath().getStringValue())
order by order by

View File

@@ -43,9 +43,8 @@ abstract class EmailSender extends DataFlow::SourceNode {
*/ */
private class NodemailerEmailSender extends EmailSender, DataFlow::MethodCallNode { private class NodemailerEmailSender extends EmailSender, DataFlow::MethodCallNode {
NodemailerEmailSender() { NodemailerEmailSender() {
this = DataFlow::moduleMember("nodemailer", "createTransport") this =
.getACall() DataFlow::moduleMember("nodemailer", "createTransport").getACall().getAMethodCall("sendMail")
.getAMethodCall("sendMail")
} }
override DataFlow::Node getPlainTextBody() { result = getOptionArgument(0, "text") } override DataFlow::Node getPlainTextBody() { result = getOptionArgument(0, "text") }

View File

@@ -253,12 +253,15 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
DataFlow::Node getExceptionTarget() { DataFlow::Node getExceptionTarget() {
if exists(this.getEnclosingStmt().getEnclosingTryCatchStmt()) if exists(this.getEnclosingStmt().getEnclosingTryCatchStmt())
then then
result = DataFlow::parameterNode(this result =
DataFlow::parameterNode(this
.getEnclosingStmt() .getEnclosingStmt()
.getEnclosingTryCatchStmt() .getEnclosingTryCatchStmt()
.getACatchClause() .getACatchClause()
.getAParameter()) .getAParameter())
else result = any(DataFlow::FunctionNode f | f.getFunction() = this.getContainer()).getExceptionalReturn() else
result =
any(DataFlow::FunctionNode f | f.getFunction() = this.getContainer()).getExceptionalReturn()
} }
} }

View File

@@ -183,11 +183,8 @@ class Folder extends Container, @folder {
* HTML files will not be found by this method. * HTML files will not be found by this method.
*/ */
File getJavaScriptFile(string stem) { File getJavaScriptFile(string stem) {
result = min(int p, string ext | result =
p = getFileExtensionPriority(ext) min(int p, string ext | p = getFileExtensionPriority(ext) | getFile(stem, ext) order by p)
|
getFile(stem, ext) order by p
)
} }
/** Gets a subfolder contained in this folder. */ /** Gets a subfolder contained in this folder. */

View File

@@ -206,7 +206,8 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
/** Gets the cyclomatic complexity of this function. */ /** Gets the cyclomatic complexity of this function. */
int getCyclomaticComplexity() { int getCyclomaticComplexity() {
result = 2 + result =
2 +
sum(Expr nd | sum(Expr nd |
nd.getContainer() = this and nd.isBranch() nd.getContainer() = this and nd.isBranch()
| |
@@ -420,9 +421,7 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
/** /**
* Gets the call signature of this function, as determined by the TypeScript compiler, if any. * Gets the call signature of this function, as determined by the TypeScript compiler, if any.
*/ */
CallSignatureType getCallSignature() { CallSignatureType getCallSignature() { declared_function_signature(this, result) }
declared_function_signature(this, result)
}
} }
/** /**

View File

@@ -35,8 +35,10 @@ class CodeGeneratorMarkerComment extends GeneratedCodeMarkerComment {
*/ */
private predicate codeGeneratorMarkerComment(Comment c, string tool) { private predicate codeGeneratorMarkerComment(Comment c, string tool) {
exists(string toolPattern | exists(string toolPattern |
toolPattern = "js_of_ocaml|CoffeeScript|LiveScript|dart2js|ANTLR|PEG\\.js|Opal|JSX|jison(?:-lex)?|(?:Microsoft \\(R\\) AutoRest Code Generator)|purs" and toolPattern =
tool = c "js_of_ocaml|CoffeeScript|LiveScript|dart2js|ANTLR|PEG\\.js|Opal|JSX|jison(?:-lex)?|(?:Microsoft \\(R\\) AutoRest Code Generator)|purs" and
tool =
c
.getText() .getText()
.regexpCapture("(?s)[\\s*]*(?:parser |Code )?[gG]eneratedy? (?:from .*)?by (" + .regexpCapture("(?s)[\\s*]*(?:parser |Code )?[gG]eneratedy? (?:from .*)?by (" +
toolPattern + ")\\b.*", 1) toolPattern + ")\\b.*", 1)

View File

@@ -5,23 +5,18 @@
import javascript import javascript
private import semmle.javascript.dataflow.InferredTypes private import semmle.javascript.dataflow.InferredTypes
deprecated deprecated module GlobalAccessPath {
module GlobalAccessPath {
/** /**
* DEPRECATED. Instead use `AccessPath::getAReferenceTo` with the result and parameter reversed. * DEPRECATED. Instead use `AccessPath::getAReferenceTo` with the result and parameter reversed.
*/ */
pragma[inline] pragma[inline]
string fromReference(DataFlow::Node node) { string fromReference(DataFlow::Node node) { node = AccessPath::getAReferenceTo(result) }
node = AccessPath::getAReferenceTo(result)
}
/** /**
* DEPRECATED. Instead use `AccessPath::getAnAssignmentTo` with the result and parameter reversed. * DEPRECATED. Instead use `AccessPath::getAnAssignmentTo` with the result and parameter reversed.
*/ */
pragma[inline] pragma[inline]
string fromRhs(DataFlow::Node node) { string fromRhs(DataFlow::Node node) { node = AccessPath::getAnAssignmentTo(result) }
node = AccessPath::getAnAssignmentTo(result)
}
/** /**
* DEPRECATED. Use `AccessPath::getAReferenceOrAssignmentTo`. * DEPRECATED. Use `AccessPath::getAReferenceOrAssignmentTo`.
@@ -67,9 +62,7 @@ module AccessPath {
} }
/** Holds if this represents the root of the global access path. */ /** Holds if this represents the root of the global access path. */
predicate isGlobal() { predicate isGlobal() { this = DataFlow::globalAccessPathRootPseudoNode() }
this = DataFlow::globalAccessPathRootPseudoNode()
}
} }
/** /**
@@ -212,7 +205,8 @@ module AccessPath {
* ``` * ```
*/ */
private predicate isSelfAssignment(DataFlow::Node rhs) { private predicate isSelfAssignment(DataFlow::Node rhs) {
fromRhs(rhs, DataFlow::globalAccessPathRootPseudoNode()) = fromReference(rhs, DataFlow::globalAccessPathRootPseudoNode()) fromRhs(rhs, DataFlow::globalAccessPathRootPseudoNode()) =
fromReference(rhs, DataFlow::globalAccessPathRootPseudoNode())
} }
/** /**
@@ -418,8 +412,8 @@ module AccessPath {
*/ */
pragma[inline] pragma[inline]
DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) { DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) {
exists(DataFlow::SourceNode root, string accessPath | exists(DataFlow::SourceNode root, string accessPath |
node = AccessPath::getAReferenceTo(root, accessPath) and node = AccessPath::getAReferenceTo(root, accessPath) and
result = AccessPath::getAReferenceTo(root, accessPath) result = AccessPath::getAReferenceTo(root, accessPath)
) )
or or

View File

@@ -49,9 +49,8 @@ private class DefaultHtmlSanitizerCall extends HtmlSanitizerCall {
callee = LodashUnderscore::member("escape") callee = LodashUnderscore::member("escape")
or or
exists(string name | name = "encode" or name = "encodeNonUTF" | exists(string name | name = "encode" or name = "encodeNonUTF" |
callee = DataFlow::moduleMember("html-entities", _) callee =
.getAnInstantiation() DataFlow::moduleMember("html-entities", _).getAnInstantiation().getAPropertyRead(name) or
.getAPropertyRead(name) or
callee = DataFlow::moduleMember("html-entities", _).getAPropertyRead(name) callee = DataFlow::moduleMember("html-entities", _).getAPropertyRead(name)
) )
or or

View File

@@ -1,6 +1,7 @@
/** /**
* Contains classes for recognizing array and string inclusion tests. * Contains classes for recognizing array and string inclusion tests.
*/ */
private import javascript private import javascript
/** /**

View File

@@ -142,5 +142,6 @@ class Locatable extends @locatable {
*/ */
private class FileLocatable extends File, Locatable { private class FileLocatable extends File, Locatable {
override Location getLocation() { result = File.super.getLocation() } override Location getLocation() { result = File.super.getLocation() }
override string toString() { result = File.super.toString() } override string toString() { result = File.super.toString() }
} }

View File

@@ -140,7 +140,8 @@ abstract class Import extends ASTNode {
* Gets a module in a `node_modules/@types/` folder that matches the imported module name. * Gets a module in a `node_modules/@types/` folder that matches the imported module name.
*/ */
private Module resolveFromTypeRoot() { private Module resolveFromTypeRoot() {
result.getFile() = min(TypeRootFolder typeRoot | result.getFile() =
min(TypeRootFolder typeRoot |
| |
typeRoot.getModuleFile(getImportedPath().getValue()) typeRoot.getModuleFile(getImportedPath().getValue())
order by order by

View File

@@ -243,7 +243,8 @@ class Require extends CallExpr, Import {
private File load(int priority) { private File load(int priority) {
exists(int r | getEnclosingModule().searchRoot(getImportedPath(), _, r) | exists(int r | getEnclosingModule().searchRoot(getImportedPath(), _, r) |
result = loadAsFile(this, r, priority - prioritiesPerCandidate() * r) or result = loadAsFile(this, r, priority - prioritiesPerCandidate() * r) or
result = loadAsDirectory(this, r, result =
loadAsDirectory(this, r,
priority - (prioritiesPerCandidate() * r + numberOfExtensions() + 1)) priority - (prioritiesPerCandidate() * r + numberOfExtensions() + 1))
) )
} }

View File

@@ -92,7 +92,7 @@ File resolveMainModule(PackageJSON pkg, int priority) {
not exists(main.resolve()) and not exists(main.resolve()) and
not exists(main.getExtension()) and not exists(main.getExtension()) and
exists(int n | n = main.getNumComponent() | exists(int n | n = main.getNumComponent() |
result = tryExtensions(main.resolveUpTo(n-1), main.getComponent(n-1), priority) result = tryExtensions(main.resolveUpTo(n - 1), main.getComponent(n - 1), priority)
) )
) )
else result = tryExtensions(pkg.getFile().getParentContainer(), "index", priority) else result = tryExtensions(pkg.getFile().getParentContainer(), "index", priority)

View File

@@ -128,17 +128,13 @@ private module PromiseFlow {
/** /**
* Gets the pseudo-field used to describe resolved values in a promise. * Gets the pseudo-field used to describe resolved values in a promise.
*/ */
string resolveField() { string resolveField() { result = "$PromiseResolveField$" }
result = "$PromiseResolveField$"
}
/** /**
* Gets the pseudo-field used to describe rejected values in a promise. * Gets the pseudo-field used to describe rejected values in a promise.
*/ */
string rejectField() { string rejectField() { result = "$PromiseRejectField$" }
result = "$PromiseRejectField$"
}
/** /**
* A flow step describing a promise definition. * A flow step describing a promise definition.
* *
@@ -146,9 +142,8 @@ private module PromiseFlow {
*/ */
class PromiseDefitionStep extends DataFlow::AdditionalFlowStep { class PromiseDefitionStep extends DataFlow::AdditionalFlowStep {
PromiseDefinition promise; PromiseDefinition promise;
PromiseDefitionStep() {
this = promise PromiseDefitionStep() { this = promise }
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and prop = resolveField() and
@@ -170,15 +165,14 @@ private module PromiseFlow {
succ = this succ = this
} }
} }
/** /**
* A flow step describing the a Promise.resolve (and similar) call. * A flow step describing the a Promise.resolve (and similar) call.
*/ */
class CreationStep extends DataFlow::AdditionalFlowStep { class CreationStep extends DataFlow::AdditionalFlowStep {
PromiseCreationCall promise; PromiseCreationCall promise;
CreationStep() {
this = promise CreationStep() { this = promise }
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and prop = resolveField() and
@@ -194,7 +188,6 @@ private module PromiseFlow {
} }
} }
/** /**
* A load step loading the pseudo-field describing that the promise is rejected. * A load step loading the pseudo-field describing that the promise is rejected.
* The rejected value is thrown as a exception. * The rejected value is thrown as a exception.
@@ -202,6 +195,7 @@ private module PromiseFlow {
class AwaitStep extends DataFlow::AdditionalFlowStep { class AwaitStep extends DataFlow::AdditionalFlowStep {
DataFlow::Node operand; DataFlow::Node operand;
AwaitExpr await; AwaitExpr await;
AwaitStep() { AwaitStep() {
this.getEnclosingExpr() = await and this.getEnclosingExpr() = await and
operand.getEnclosingExpr() = await.getOperand() operand.getEnclosingExpr() = await.getOperand()
@@ -222,9 +216,7 @@ private module PromiseFlow {
* A flow step describing the data-flow related to the `.then` method of a promise. * A flow step describing the data-flow related to the `.then` method of a promise.
*/ */
class ThenStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode { class ThenStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ThenStep() { ThenStep() { this.getMethodName() = "then" }
this.getMethodName() = "then"
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and prop = resolveField() and
@@ -235,7 +227,7 @@ private module PromiseFlow {
pred = getReceiver() and pred = getReceiver() and
succ = getCallback(1).getParameter(0) succ = getCallback(1).getParameter(0)
} }
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
not exists(this.getArgument(1)) and not exists(this.getArgument(1)) and
prop = rejectField() and prop = rejectField() and
@@ -244,17 +236,17 @@ private module PromiseFlow {
or or
// read the value of a resolved/rejected promise that is returned // read the value of a resolved/rejected promise that is returned
(prop = rejectField() or prop = resolveField()) and (prop = rejectField() or prop = resolveField()) and
pred = getCallback([0..1]).getAReturn() and pred = getCallback([0 .. 1]).getAReturn() and
succ = this succ = this
} }
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and prop = resolveField() and
pred = getCallback([0..1]).getAReturn() and pred = getCallback([0 .. 1]).getAReturn() and
succ = this succ = this
or or
prop = rejectField() and prop = rejectField() and
pred = getCallback([0..1]).getExceptionalReturn() and pred = getCallback([0 .. 1]).getExceptionalReturn() and
succ = this succ = this
} }
} }
@@ -263,9 +255,7 @@ private module PromiseFlow {
* A flow step describing the data-flow related to the `.catch` method of a promise. * A flow step describing the data-flow related to the `.catch` method of a promise.
*/ */
class CatchStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode { class CatchStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
CatchStep() { CatchStep() { this.getMethodName() = "catch" }
this.getMethodName() = "catch"
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and prop = rejectField() and
@@ -299,9 +289,7 @@ private module PromiseFlow {
* A flow step describing the data-flow related to the `.finally` method of a promise. * A flow step describing the data-flow related to the `.finally` method of a promise.
*/ */
class FinallyStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode { class FinallyStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
FinallyStep() { FinallyStep() { this.getMethodName() = "finally" }
this.getMethodName() = "finally"
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
(prop = resolveField() or prop = rejectField()) and (prop = resolveField() or prop = rejectField()) and
@@ -332,15 +320,13 @@ predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
// from `x` to `Promise.resolve(x)` // from `x` to `Promise.resolve(x)`
pred = succ.(PromiseCreationCall).getValue() pred = succ.(PromiseCreationCall).getValue()
or or
exists(DataFlow::MethodCallNode thn | exists(DataFlow::MethodCallNode thn | thn.getMethodName() = "then" |
thn.getMethodName() = "then"
|
// from `p` to `x` in `p.then(x => ...)` // from `p` to `x` in `p.then(x => ...)`
pred = thn.getReceiver() and pred = thn.getReceiver() and
succ = thn.getCallback(0).getParameter(0) succ = thn.getCallback(0).getParameter(0)
or or
// from `v` to `p.then(x => return v)` // from `v` to `p.then(x => return v)`
pred = thn.getCallback([0..1]).getAReturn() and pred = thn.getCallback([0 .. 1]).getAReturn() and
succ = thn succ = thn
) )
or or
@@ -406,22 +392,19 @@ module Bluebird {
override DataFlow::Node getValue() { result = getArgument(0) } override DataFlow::Node getValue() { result = getArgument(0) }
} }
/** /**
* An aggregated promise produced either by `Promise.all`, `Promise.race` or `Promise.map`. * An aggregated promise produced either by `Promise.all`, `Promise.race` or `Promise.map`.
*/ */
class AggregateBluebirdPromiseDefinition extends PromiseCreationCall { class AggregateBluebirdPromiseDefinition extends PromiseCreationCall {
AggregateBluebirdPromiseDefinition() { AggregateBluebirdPromiseDefinition() {
exists(string m | m = "all" or m = "race" or m = "map" | exists(string m | m = "all" or m = "race" or m = "map" | this = bluebird().getAMemberCall(m))
this = bluebird().getAMemberCall(m)
)
} }
override DataFlow::Node getValue() { override DataFlow::Node getValue() {
result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement() result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()
} }
} }
} }
/** /**

View File

@@ -296,9 +296,7 @@ class RegExpSequence extends RegExpTerm, @regexp_seq {
forall(RegExpTerm child | child = getAChild() | child.isNullable()) forall(RegExpTerm child | child = getAChild() | child.isNullable())
} }
override string getConstantValue() { override string getConstantValue() { result = getConstantValue(0) }
result = getConstantValue(0)
}
/** /**
* Gets the single string matched by the `i`th child and all following children of * Gets the single string matched by the `i`th child and all following children of
@@ -308,13 +306,11 @@ class RegExpSequence extends RegExpTerm, @regexp_seq {
i = getNumChild() and i = getNumChild() and
result = "" result = ""
or or
result = getChild(i).getConstantValue() + getConstantValue(i+1) result = getChild(i).getConstantValue() + getConstantValue(i + 1)
} }
/** Gets the element preceding `element` in this sequence. */ /** Gets the element preceding `element` in this sequence. */
RegExpTerm previousElement(RegExpTerm element) { RegExpTerm previousElement(RegExpTerm element) { element = nextElement(result) }
element = nextElement(result)
}
/** Gets the element following `element` in this sequence. */ /** Gets the element following `element` in this sequence. */
RegExpTerm nextElement(RegExpTerm element) { RegExpTerm nextElement(RegExpTerm element) {
@@ -834,7 +830,7 @@ class RegExpParseError extends Error, @regexp_parse_error {
} }
/** /**
* Holds if `func` is a method defined on `String.prototype` with name `name`. * Holds if `func` is a method defined on `String.prototype` with name `name`.
*/ */
private predicate isNativeStringMethod(Function func, string name) { private predicate isNativeStringMethod(Function func, string name) {
exists(ExternalInstanceMemberDecl decl | exists(ExternalInstanceMemberDecl decl |
@@ -856,9 +852,7 @@ predicate isInterpretedAsRegExp(DataFlow::Node source) {
exists(DataFlow::MethodCallNode mce, string methodName | exists(DataFlow::MethodCallNode mce, string methodName |
mce.getReceiver().analyze().getAType() = TTString() and mce.getReceiver().analyze().getAType() = TTString() and
mce.getMethodName() = methodName and mce.getMethodName() = methodName and
not exists(Function func | not exists(Function func | func = mce.getACallee() |
func = mce.getACallee()
|
not isNativeStringMethod(func, methodName) not isNativeStringMethod(func, methodName)
) )
| |

View File

@@ -25,9 +25,8 @@ class FirstLineOf extends Locatable {
if xl = startline if xl = startline
then endcolumn = xc then endcolumn = xc
else else
endcolumn = max(int c | endcolumn =
any(Location l).hasLocationInfo(filepath, startline, _, startline, c) max(int c | any(Location l).hasLocationInfo(filepath, startline, _, startline, c))
)
) )
} }
} }

View File

@@ -647,9 +647,7 @@ class SsaPhiNode extends SsaPseudoDefinition, TPhi {
result = getDefReachingEndOf(bb, getSourceVariable()) result = getDefReachingEndOf(bb, getSourceVariable())
} }
override SsaVariable getAnInput() { override SsaVariable getAnInput() { result = getInputFromBlock(_) }
result = getInputFromBlock(_)
}
override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
bb = getBasicBlock() and v = getSourceVariable() and i = -1 bb = getBasicBlock() and v = getSourceVariable() and i = -1
@@ -676,9 +674,7 @@ class SsaPhiNode extends SsaPseudoDefinition, TPhi {
* gets that variable. * gets that variable.
*/ */
SsaVariable getRephinedVariable() { SsaVariable getRephinedVariable() {
forex(SsaVariable input | input = getAnInput() | forex(SsaVariable input | input = getAnInput() | result = getRefinedVariable(input))
result = getRefinedVariable(input)
)
} }
} }

View File

@@ -49,7 +49,8 @@ class DirectEval extends CallExpr {
* Models `Array.prototype.map` and friends as partial invocations that pass their second * Models `Array.prototype.map` and friends as partial invocations that pass their second
* argument as the receiver to the callback. * argument as the receiver to the callback.
*/ */
private class ArrayIterationCallbackAsPartialInvoke extends DataFlow::PartialInvokeNode::Range, DataFlow::MethodCallNode { private class ArrayIterationCallbackAsPartialInvoke extends DataFlow::PartialInvokeNode::Range,
DataFlow::MethodCallNode {
ArrayIterationCallbackAsPartialInvoke() { ArrayIterationCallbackAsPartialInvoke() {
getNumArgument() = 2 and getNumArgument() = 2 and
// Filter out library methods named 'forEach' etc // Filter out library methods named 'forEach' etc

View File

@@ -55,15 +55,15 @@ class Stmt extends @stmt, ExprOrStmt, Documentable {
} }
override predicate isAmbient() { hasDeclareKeyword(this) or getParent().isAmbient() } override predicate isAmbient() { hasDeclareKeyword(this) or getParent().isAmbient() }
/** /**
* Gets the `try` statement with a catch block containing this statement without * Gets the `try` statement with a catch block containing this statement without
* crossing function boundaries or other `try ` statements with catch blocks. * crossing function boundaries or other `try ` statements with catch blocks.
*/ */
TryStmt getEnclosingTryCatchStmt() { TryStmt getEnclosingTryCatchStmt() {
getParentStmt+() = result.getBody() and getParentStmt+() = result.getBody() and
exists(result.getACatchClause()) and exists(result.getACatchClause()) and
not exists(TryStmt mid | exists(mid.getACatchClause()) | not exists(TryStmt mid | exists(mid.getACatchClause()) |
getParentStmt+() = mid.getBody() and mid.getParentStmt+() = result.getBody() getParentStmt+() = mid.getBody() and mid.getParentStmt+() = result.getBody()
) )
} }

View File

@@ -165,10 +165,16 @@ module StringOps {
StartsWith_Substring() { StartsWith_Substring() {
astNode.hasOperands(call.asExpr(), substring.asExpr()) and astNode.hasOperands(call.asExpr(), substring.asExpr()) and
(call.getMethodName() = "substring" or call.getMethodName() = "substr" or call.getMethodName() = "slice") and (
call.getMethodName() = "substring" or
call.getMethodName() = "substr" or
call.getMethodName() = "slice"
) and
call.getNumArgument() = 2 and call.getNumArgument() = 2 and
( (
AccessPath::getAnAliasedSourceNode(substring).getAPropertyRead("length").flowsTo(call.getArgument(1)) AccessPath::getAnAliasedSourceNode(substring)
.getAPropertyRead("length")
.flowsTo(call.getArgument(1))
or or
substring.getStringValue().length() = call.getArgument(1).asExpr().getIntValue() substring.getStringValue().length() = call.getArgument(1).asExpr().getIntValue()
) )
@@ -502,7 +508,8 @@ module StringOps {
result = getStringValue() result = getStringValue()
or or
not exists(getStringValue()) and not exists(getStringValue()) and
result = strictconcat(StringLiteralLike leaf | result =
strictconcat(StringLiteralLike leaf |
leaf = getALeaf().asExpr() leaf = getALeaf().asExpr()
| |
leaf.getStringValue() order by leaf.getFirstToken().getIndex() leaf.getStringValue() order by leaf.getFirstToken().getIndex()

View File

@@ -939,9 +939,7 @@ class PredicateTypeExpr extends @predicatetypeexpr, TypeExpr {
/** /**
* Holds if this is a type of form `asserts E is T` or `asserts E`. * Holds if this is a type of form `asserts E is T` or `asserts E`.
*/ */
predicate hasAssertsKeyword() { predicate hasAssertsKeyword() { hasAssertsKeyword(this) }
hasAssertsKeyword(this)
}
} }
/** /**
@@ -954,9 +952,7 @@ class PredicateTypeExpr extends @predicatetypeexpr, TypeExpr {
* ``` * ```
*/ */
class IsTypeExpr extends PredicateTypeExpr { class IsTypeExpr extends PredicateTypeExpr {
IsTypeExpr() { IsTypeExpr() { exists(getPredicateType()) }
exists(getPredicateType())
}
} }
/** /**
@@ -2312,18 +2308,14 @@ class EnumLiteralType extends TypeReference {
* A type that refers to a type alias. * A type that refers to a type alias.
*/ */
class TypeAliasReference extends TypeReference { class TypeAliasReference extends TypeReference {
TypeAliasReference() { TypeAliasReference() { type_alias(this, _) }
type_alias(this, _)
}
/** /**
* Gets the type behind the type alias. * Gets the type behind the type alias.
* *
* For example, for `type B<T> = T[][]`, this maps the type `B<number>` to `number[][]`. * For example, for `type B<T> = T[][]`, this maps the type `B<number>` to `number[][]`.
*/ */
Type getAliasedType() { Type getAliasedType() { type_alias(this, result) }
type_alias(this, result)
}
} }
/** /**
@@ -2635,9 +2627,7 @@ class CallSignatureType extends @signature_type {
* *
* For example, for the signature `(...y: string[])`, this gets the type `string[]`. * For example, for the signature `(...y: string[])`, this gets the type `string[]`.
*/ */
PlainArrayType getRestParameterArrayType() { PlainArrayType getRestParameterArrayType() { signature_rest_parameter(this, result) }
signature_rest_parameter(this, result)
}
} }
/** /**

View File

@@ -189,10 +189,8 @@ class Variable extends @variable, LexicalName {
*/ */
predicate isCaptured() { predicate isCaptured() {
this instanceof GlobalVariable or this instanceof GlobalVariable or
getAnAccess().getContainer().getFunctionBoundary() != this getAnAccess().getContainer().getFunctionBoundary() !=
.(LocalVariable) this.(LocalVariable).getDeclaringContainer().getFunctionBoundary()
.getDeclaringContainer()
.getFunctionBoundary()
} }
/** Holds if there is a declaration of this variable in `tl`. */ /** Holds if there is a declaration of this variable in `tl`. */
@@ -764,9 +762,7 @@ class Parameter extends BindingPattern {
* function f(x?: number) {} * function f(x?: number) {}
* ``` * ```
*/ */
predicate isDeclaredOptional() { predicate isDeclaredOptional() { isOptionalParameterDeclaration(this) }
isOptionalParameterDeclaration(this)
}
} }
/** /**

View File

@@ -18,9 +18,7 @@ import javascript
private class BackwardExploringConfiguration extends DataFlow::Configuration { private class BackwardExploringConfiguration extends DataFlow::Configuration {
DataFlow::Configuration cfg; DataFlow::Configuration cfg;
BackwardExploringConfiguration() { BackwardExploringConfiguration() { this = cfg }
this = cfg
}
override predicate isSource(DataFlow::Node node) { any() } override predicate isSource(DataFlow::Node node) { any() }

View File

@@ -1035,7 +1035,9 @@ private predicate flowIntoHigherOrderCall(
summary = oldSummary.append(PathSummary::call()) summary = oldSummary.append(PathSummary::call())
) )
or or
exists(DataFlow::SourceNode cb, DataFlow::FunctionNode f, int i, int boundArgs, PathSummary oldSummary | exists(
DataFlow::SourceNode cb, DataFlow::FunctionNode f, int i, int boundArgs, PathSummary oldSummary
|
higherOrderCall(pred, cb, i, cfg, oldSummary) and higherOrderCall(pred, cb, i, cfg, oldSummary) and
cb = CallGraph::getABoundFunctionReference(f, boundArgs, false) and cb = CallGraph::getABoundFunctionReference(f, boundArgs, false) and
succ = f.getParameter(boundArgs + i) and succ = f.getParameter(boundArgs + i) and
@@ -1494,9 +1496,9 @@ private class AdditionalBarrierGuardCall extends AdditionalBarrierGuardNode, Dat
} }
/** /**
* A guard node for a variable in a negative condition, such as `x` in `if(!x)`. * A guard node for a variable in a negative condition, such as `x` in `if(!x)`.
* Can be added to a `isBarrier` in a data-flow configuration to block flow through such checks. * Can be added to a `isBarrier` in a data-flow configuration to block flow through such checks.
*/ */
class VarAccessBarrier extends DataFlow::Node { class VarAccessBarrier extends DataFlow::Node {
VarAccessBarrier() { VarAccessBarrier() {
exists(ConditionGuardNode guard, SsaRefinementNode refinement | exists(ConditionGuardNode guard, SsaRefinementNode refinement |

View File

@@ -87,10 +87,10 @@ module DataFlow {
Expr asExpr() { this = TValueNode(result) } Expr asExpr() { this = TValueNode(result) }
/** /**
* Gets the expression enclosing this data flow node. * Gets the expression enclosing this data flow node.
* In most cases the result is the same as `asExpr()`, however this method * In most cases the result is the same as `asExpr()`, however this method
* additionally the `InvokeExpr` corresponding to reflective calls, and the `Parameter` * additionally the `InvokeExpr` corresponding to reflective calls, and the `Parameter`
* for a `DataFlow::ParameterNode`. * for a `DataFlow::ParameterNode`.
*/ */
Expr getEnclosingExpr() { Expr getEnclosingExpr() {
result = asExpr() or result = asExpr() or
@@ -536,10 +536,10 @@ module DataFlow {
*/ */
pragma[noinline] pragma[noinline]
predicate accesses(Node base, string p) { getBase() = base and getPropertyName() = p } predicate accesses(Node base, string p) { getBase() = base and getPropertyName() = p }
/** /**
* Holds if this data flow node reads or writes a private field in a class. * Holds if this data flow node reads or writes a private field in a class.
*/ */
predicate isPrivateField() { predicate isPrivateField() {
getPropertyName().charAt(0) = "#" and getPropertyNameExpr() instanceof Label getPropertyName().charAt(0) = "#" and getPropertyNameExpr() instanceof Label
} }
@@ -860,7 +860,7 @@ module DataFlow {
override Expr getPropertyNameExpr() { none() } override Expr getPropertyNameExpr() { none() }
override string getPropertyName() { override string getPropertyName() {
exists (int i | exists(int i |
elt = pattern.getElement(i) and elt = pattern.getElement(i) and
result = i.toString() result = i.toString()
) )
@@ -872,7 +872,6 @@ module DataFlow {
*/ */
private class ImportSpecifierAsPropRead extends PropRead, ValueNode { private class ImportSpecifierAsPropRead extends PropRead, ValueNode {
override ImportSpecifier astNode; override ImportSpecifier astNode;
ImportDeclaration imprt; ImportDeclaration imprt;
ImportSpecifierAsPropRead() { ImportSpecifierAsPropRead() {
@@ -894,9 +893,7 @@ module DataFlow {
private class ForOfLvalueAsPropRead extends PropRead { private class ForOfLvalueAsPropRead extends PropRead {
ForOfStmt stmt; ForOfStmt stmt;
ForOfLvalueAsPropRead() { ForOfLvalueAsPropRead() { this = lvalueNode(stmt.getLValue()) }
this = lvalueNode(stmt.getLValue())
}
override Node getBase() { result = stmt.getIterationDomain().flow() } override Node getBase() { result = stmt.getIterationDomain().flow() }
@@ -1010,7 +1007,7 @@ module DataFlow {
* Gets a pseudo-node representing the root of a global access path. * Gets a pseudo-node representing the root of a global access path.
*/ */
DataFlow::Node globalAccessPathRootPseudoNode() { result instanceof TGlobalAccessPathRoot } DataFlow::Node globalAccessPathRootPseudoNode() { result instanceof TGlobalAccessPathRoot }
/** /**
* Gets a data flow node representing the underlying call performed by the given * Gets a data flow node representing the underlying call performed by the given
* call to `Function.prototype.call` or `Function.prototype.apply`. * call to `Function.prototype.call` or `Function.prototype.apply`.

View File

@@ -16,9 +16,7 @@ import javascript
private class ForwardExploringConfiguration extends DataFlow::Configuration { private class ForwardExploringConfiguration extends DataFlow::Configuration {
DataFlow::Configuration cfg; DataFlow::Configuration cfg;
ForwardExploringConfiguration() { ForwardExploringConfiguration() { this = cfg }
this = cfg
}
override predicate isSink(DataFlow::Node node) { any() } override predicate isSink(DataFlow::Node node) { any() }

View File

@@ -134,6 +134,7 @@ class NonPrimitiveType extends InferredType {
* Gets a pretty-printed list of all type tags in alphabetical order. * Gets a pretty-printed list of all type tags in alphabetical order.
*/ */
string ppAllTypeTags() { string ppAllTypeTags() {
result = "boolean, class, date, function, null, number, object, regular expression," + result =
"boolean, class, date, function, null, number, object, regular expression," +
"string or undefined" "string or undefined"
} }

View File

@@ -9,7 +9,7 @@ private import semmle.javascript.dependencies.Dependencies
private import internal.CallGraphs private import internal.CallGraphs
/** /**
* A data flow node corresponding to an expression. * A data flow node corresponding to an expression.
* *
* Examples: * Examples:
* ```js * ```js
@@ -147,7 +147,8 @@ class InvokeNode extends DataFlow::SourceNode {
*/ */
ParameterNode getABoundCallbackParameter(int callback, int param) { ParameterNode getABoundCallbackParameter(int callback, int param) {
exists(int boundArgs | exists(int boundArgs |
result = getArgument(callback).getABoundFunctionValue(boundArgs).getParameter(param + boundArgs) result =
getArgument(callback).getABoundFunctionValue(boundArgs).getParameter(param + boundArgs)
) )
} }
@@ -548,9 +549,7 @@ class RegExpLiteralNode extends DataFlow::ValueNode, DataFlow::SourceNode {
RegExpTerm getRoot() { result = astNode.getRoot() } RegExpTerm getRoot() { result = astNode.getRoot() }
/** Gets the flags of this regular expression literal. */ /** Gets the flags of this regular expression literal. */
string getFlags() { string getFlags() { result = astNode.getFlags() }
result = astNode.getFlags()
}
} }
/** /**
@@ -581,10 +580,9 @@ class ArrayConstructorInvokeNode extends DataFlow::InvokeNode {
/** Gets the initial size of the created array, if it can be determined. */ /** Gets the initial size of the created array, if it can be determined. */
int getSize() { int getSize() {
if getNumArgument() = 1 then if getNumArgument() = 1
result = getArgument(0).getIntValue() then result = getArgument(0).getIntValue()
else else result = count(getAnElement())
result = count(getAnElement())
} }
} }
@@ -596,7 +594,7 @@ class ArrayConstructorInvokeNode extends DataFlow::InvokeNode {
* Examples: * Examples:
* ```js * ```js
* ['apple', 'orange']; * ['apple', 'orange'];
* Array('apple', 'orange') * Array('apple', 'orange')
* new Array('apple', 'orange') * new Array('apple', 'orange')
* Array(16) * Array(16)
* new Array(16) * new Array(16)
@@ -1236,7 +1234,9 @@ class PartialInvokeNode extends DataFlow::Node {
/** /**
* Gets the node holding the receiver to be passed to the bound function, if specified. * Gets the node holding the receiver to be passed to the bound function, if specified.
*/ */
DataFlow::Node getBoundReceiver(DataFlow::Node callback) { result = range.getBoundReceiver(callback) } DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
result = range.getBoundReceiver(callback)
}
} }
module PartialInvokeNode { module PartialInvokeNode {
@@ -1247,7 +1247,9 @@ module PartialInvokeNode {
/** /**
* Holds if `argument` is passed as argument `index` to the function in `callback`. * Holds if `argument` is passed as argument `index` to the function in `callback`.
*/ */
predicate isPartialArgument(DataFlow::Node callback, DataFlow::Node argument, int index) { none() } predicate isPartialArgument(DataFlow::Node callback, DataFlow::Node argument, int index) {
none()
}
/** /**
* Gets a node referring to a bound version of `callback` with `boundArgs` arguments bound. * Gets a node referring to a bound version of `callback` with `boundArgs` arguments bound.
@@ -1259,8 +1261,7 @@ module PartialInvokeNode {
* *
* Gets the node holding the receiver to be passed to the bound function, if specified. * Gets the node holding the receiver to be passed to the bound function, if specified.
*/ */
deprecated deprecated DataFlow::Node getBoundReceiver() { none() }
DataFlow::Node getBoundReceiver() { none() }
/** /**
* Gets the node holding the receiver to be passed to `callback`. * Gets the node holding the receiver to be passed to `callback`.
@@ -1269,12 +1270,11 @@ module PartialInvokeNode {
} }
/** /**
* A partial call through the built-in `Function.prototype.bind`. * A partial call through the built-in `Function.prototype.bind`.
*/ */
private class BindPartialCall extends PartialInvokeNode::Range, DataFlow::MethodCallNode { private class BindPartialCall extends PartialInvokeNode::Range, DataFlow::MethodCallNode {
BindPartialCall() { BindPartialCall() {
getMethodName() = "bind" and getMethodName() = "bind" and
// Avoid overlap with angular.bind and goog.bind // Avoid overlap with angular.bind and goog.bind
not this = AngularJS::angular().getAMethodCall() and not this = AngularJS::angular().getAMethodCall() and
not getReceiver().accessesGlobal("goog") not getReceiver().accessesGlobal("goog")
@@ -1299,8 +1299,8 @@ module PartialInvokeNode {
} }
/** /**
* A partial call through `_.partial`. * A partial call through `_.partial`.
*/ */
private class LodashPartialCall extends PartialInvokeNode::Range, DataFlow::CallNode { private class LodashPartialCall extends PartialInvokeNode::Range, DataFlow::CallNode {
LodashPartialCall() { this = LodashUnderscore::member("partial").getACall() } LodashPartialCall() { this = LodashUnderscore::member("partial").getACall() }
@@ -1323,9 +1323,7 @@ module PartialInvokeNode {
private class RamdaPartialCall extends PartialInvokeNode::Range, DataFlow::CallNode { private class RamdaPartialCall extends PartialInvokeNode::Range, DataFlow::CallNode {
RamdaPartialCall() { this = DataFlow::moduleMember("ramda", "partial").getACall() } RamdaPartialCall() { this = DataFlow::moduleMember("ramda", "partial").getACall() }
private DataFlow::ArrayCreationNode getArgumentsArray() { private DataFlow::ArrayCreationNode getArgumentsArray() { result.flowsTo(getArgument(1)) }
result.flowsTo(getArgument(1))
}
override predicate isPartialArgument(DataFlow::Node callback, DataFlow::Node argument, int index) { override predicate isPartialArgument(DataFlow::Node callback, DataFlow::Node argument, int index) {
callback = getArgument(0) and callback = getArgument(0) and
@@ -1376,17 +1374,13 @@ deprecated class AdditionalPartialInvokeNode = PartialInvokeNode::Range;
* ``` * ```
*/ */
class RegExpConstructorInvokeNode extends DataFlow::InvokeNode { class RegExpConstructorInvokeNode extends DataFlow::InvokeNode {
RegExpConstructorInvokeNode() { RegExpConstructorInvokeNode() { this = DataFlow::globalVarRef("RegExp").getAnInvocation() }
this = DataFlow::globalVarRef("RegExp").getAnInvocation()
}
/** /**
* Gets the AST of the regular expression created here, provided that the * Gets the AST of the regular expression created here, provided that the
* first argument is a string literal. * first argument is a string literal.
*/ */
RegExpTerm getRoot() { RegExpTerm getRoot() { result = getArgument(0).asExpr().(StringLiteral).asRegExp() }
result = getArgument(0).asExpr().(StringLiteral).asRegExp()
}
/** /**
* Gets the flags provided in the second argument, or an empty string if no * Gets the flags provided in the second argument, or an empty string if no
@@ -1462,13 +1456,9 @@ class RegExpCreationNode extends DataFlow::SourceNode {
t.start() and t.start() and
result = this result = this
or or
exists(DataFlow::TypeTracker t2 | exists(DataFlow::TypeTracker t2 | result = getAReference(t2).track(t2, t))
result = getAReference(t2).track(t2, t)
)
} }
/** Gets a data flow node referring to this regular expression. */ /** Gets a data flow node referring to this regular expression. */
DataFlow::SourceNode getAReference() { DataFlow::SourceNode getAReference() { result = getAReference(DataFlow::TypeTracker::end()) }
result = getAReference(DataFlow::TypeTracker::end())
}
} }

View File

@@ -341,7 +341,7 @@ module TaintTracking {
pred = call.getAnArgument() and pred = call.getAnArgument() and
succ = call succ = call
or or
// `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`. // `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
call.(DataFlow::MethodCallNode).calls(pred, "concat") and call.(DataFlow::MethodCallNode).calls(pred, "concat") and
succ = call succ = call
or or
@@ -574,7 +574,6 @@ module TaintTracking {
succ = this succ = this
} }
} }
/** /**
* A taint propagating data flow edge arising from calling `String.prototype.match()`. * A taint propagating data flow edge arising from calling `String.prototype.match()`.
@@ -583,7 +582,7 @@ module TaintTracking {
StringMatchTaintStep() { StringMatchTaintStep() {
this.getMethodName() = "match" and this.getMethodName() = "match" and
this.getNumArgument() = 1 and this.getNumArgument() = 1 and
this.getArgument(0) .analyze().getAType() = TTRegExp() this.getArgument(0).analyze().getAType() = TTRegExp()
} }
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
@@ -696,9 +695,7 @@ module TaintTracking {
* A taint step through the Node.JS function `util.inspect(..)`. * A taint step through the Node.JS function `util.inspect(..)`.
*/ */
class UtilInspectTaintStep extends AdditionalTaintStep, DataFlow::InvokeNode { class UtilInspectTaintStep extends AdditionalTaintStep, DataFlow::InvokeNode {
UtilInspectTaintStep() { UtilInspectTaintStep() { this = DataFlow::moduleImport("util").getAMemberCall("inspect") }
this = DataFlow::moduleImport("util").getAMemberCall("inspect")
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
succ = this and succ = this and
@@ -719,14 +716,16 @@ module TaintTracking {
mce = astNode and mce.calls(base, m) and firstArg = mce.getArgument(0) mce = astNode and mce.calls(base, m) and firstArg = mce.getArgument(0)
| |
// /re/.test(u) or /re/.exec(u) // /re/.test(u) or /re/.exec(u)
RegExp::isGenericRegExpSanitizer(RegExp::getRegExpObjectFromNode(base.flow()), sanitizedOutcome) and RegExp::isGenericRegExpSanitizer(RegExp::getRegExpObjectFromNode(base.flow()),
sanitizedOutcome) and
(m = "test" or m = "exec") and (m = "test" or m = "exec") and
firstArg = expr firstArg = expr
or or
// u.match(/re/) or u.match("re") // u.match(/re/) or u.match("re")
base = expr and base = expr and
m = "match" and m = "match" and
RegExp::isGenericRegExpSanitizer(RegExp::getRegExpFromNode(firstArg.flow()), sanitizedOutcome) RegExp::isGenericRegExpSanitizer(RegExp::getRegExpFromNode(firstArg.flow()),
sanitizedOutcome)
) )
or or
// m = /re/.exec(u) and similar // m = /re/.exec(u) and similar

View File

@@ -137,10 +137,11 @@ module StepSummary {
summary = LoadStep(prop) summary = LoadStep(prop)
) )
) and ) and
if param = fun.getAParameter() then ( if param = fun.getAParameter()
then
// Step from argument to call site. // Step from argument to call site.
argumentPassing(succ, pred, fun.getFunction(), param) argumentPassing(succ, pred, fun.getFunction(), param)
) else ( else (
// Step from captured parameter to local call sites // Step from captured parameter to local call sites
pred = param and pred = param and
succ = fun.getAnInvocation() succ = fun.getAnInvocation()

View File

@@ -128,8 +128,9 @@ class AccessPath extends TAccessPath {
exists(AccessPath base, PropertyName name, string rest | exists(AccessPath base, PropertyName name, string rest |
rest = "." + any(string s | name = StaticPropertyName(s)) rest = "." + any(string s | name = StaticPropertyName(s))
or or
rest = "[" + rest =
any(SsaVariable var | name = DynamicPropertyName(var)).getSourceVariable().getName() + "]" "[" + any(SsaVariable var | name = DynamicPropertyName(var)).getSourceVariable().getName() +
"]"
| |
result = base.toString() + rest and result = base.toString() + rest and
this = MkAccessStep(base, name) this = MkAccessStep(base, name)

View File

@@ -1,6 +1,7 @@
/** /**
* Internal predicates for computing the call graph. * Internal predicates for computing the call graph.
*/ */
private import javascript private import javascript
cached cached
@@ -24,21 +25,22 @@ module CallGraph {
* from underlying class tracking if the function came from a class or instance. * from underlying class tracking if the function came from a class or instance.
*/ */
pragma[nomagic] pragma[nomagic]
private private DataFlow::SourceNode getAFunctionReference(
DataFlow::SourceNode getAFunctionReference(DataFlow::FunctionNode function, int imprecision, DataFlow::TypeTracker t) { DataFlow::FunctionNode function, int imprecision, DataFlow::TypeTracker t
) {
t.start() and t.start() and
exists(Function fun | exists(Function fun |
fun = function.getFunction() and fun = function.getFunction() and
fun = getAFunctionValue(result) fun = getAFunctionValue(result)
| |
if isIndefiniteGlobal(result) then if isIndefiniteGlobal(result)
then
fun.getFile() = result.getFile() and imprecision = 0 fun.getFile() = result.getFile() and imprecision = 0
or or
fun.inExternsFile() and imprecision = 1 fun.inExternsFile() and imprecision = 1
or or
imprecision = 2 imprecision = 2
else else imprecision = 0
imprecision = 0
) )
or or
imprecision = 0 and imprecision = 0 and
@@ -73,8 +75,9 @@ module CallGraph {
* with `function` as the underlying function. * with `function` as the underlying function.
*/ */
pragma[nomagic] pragma[nomagic]
private private DataFlow::SourceNode getABoundFunctionReferenceAux(
DataFlow::SourceNode getABoundFunctionReferenceAux(DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t) { DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t
) {
exists(DataFlow::PartialInvokeNode partial, DataFlow::Node callback | exists(DataFlow::PartialInvokeNode partial, DataFlow::Node callback |
result = partial.getBoundFunction(callback, boundArgs) and result = partial.getBoundFunction(callback, boundArgs) and
getAFunctionReference(function, 0, t.continue()).flowsTo(callback) getAFunctionReference(function, 0, t.continue()).flowsTo(callback)
@@ -87,8 +90,10 @@ module CallGraph {
} }
pragma[noinline] pragma[noinline]
private private DataFlow::SourceNode getABoundFunctionReferenceAux(
DataFlow::SourceNode getABoundFunctionReferenceAux(DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t, DataFlow::StepSummary summary) { DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t,
DataFlow::StepSummary summary
) {
exists(DataFlow::SourceNode prev | exists(DataFlow::SourceNode prev |
prev = getABoundFunctionReferenceAux(function, boundArgs, t) and prev = getABoundFunctionReferenceAux(function, boundArgs, t) and
DataFlow::StepSummary::step(prev, result, summary) DataFlow::StepSummary::step(prev, result, summary)
@@ -100,7 +105,9 @@ module CallGraph {
* with `function` as the underlying function. * with `function` as the underlying function.
*/ */
cached cached
DataFlow::SourceNode getABoundFunctionReference(DataFlow::FunctionNode function, int boundArgs, boolean contextDependent) { DataFlow::SourceNode getABoundFunctionReference(
DataFlow::FunctionNode function, int boundArgs, boolean contextDependent
) {
exists(DataFlow::TypeTracker t | exists(DataFlow::TypeTracker t |
result = getABoundFunctionReferenceAux(function, boundArgs, t) and result = getABoundFunctionReferenceAux(function, boundArgs, t) and
t.end() and t.end() and
@@ -116,8 +123,9 @@ module CallGraph {
* This predicate may be overridden to customize the class hierarchy analysis. * This predicate may be overridden to customize the class hierarchy analysis.
*/ */
pragma[nomagic] pragma[nomagic]
private private DataFlow::PropRead getAnInstanceMemberAccess(
DataFlow::PropRead getAnInstanceMemberAccess(DataFlow::ClassNode cls, string name, DataFlow::TypeTracker t) { DataFlow::ClassNode cls, string name, DataFlow::TypeTracker t
) {
result = cls.getAnInstanceReference(t.continue()).getAPropertyRead(name) result = cls.getAnInstanceReference(t.continue()).getAPropertyRead(name)
or or
exists(DataFlow::ClassNode subclass | exists(DataFlow::ClassNode subclass |

View File

@@ -440,8 +440,8 @@ class PathSummary extends TPathSummary {
exists(Boolean hasReturn2, Boolean hasCall2, FlowLabel end2 | exists(Boolean hasReturn2, Boolean hasCall2, FlowLabel end2 |
that = MkPathSummary(hasReturn2, hasCall2, end, end2) that = MkPathSummary(hasReturn2, hasCall2, end, end2)
| |
result = MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start, result =
end2) and MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start, end2) and
// avoid constructing invalid paths // avoid constructing invalid paths
not (hasCall = true and hasReturn2 = true) not (hasCall = true and hasReturn2 = true)
) )
@@ -456,8 +456,8 @@ class PathSummary extends TPathSummary {
exists(Boolean hasReturn2, Boolean hasCall2 | exists(Boolean hasReturn2, Boolean hasCall2 |
that = MkPathSummary(hasReturn2, hasCall2, FlowLabel::data(), FlowLabel::data()) that = MkPathSummary(hasReturn2, hasCall2, FlowLabel::data(), FlowLabel::data())
| |
result = MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start, result =
end) and MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start, end) and
// avoid constructing invalid paths // avoid constructing invalid paths
not (hasCall = true and hasReturn2 = true) not (hasCall = true and hasReturn2 = true)
) )
@@ -474,8 +474,9 @@ class PathSummary extends TPathSummary {
(if hasReturn = true then withReturn = "with" else withReturn = "without") and (if hasReturn = true then withReturn = "with" else withReturn = "without") and
(if hasCall = true then withCall = "with" else withCall = "without") (if hasCall = true then withCall = "with" else withCall = "without")
| |
result = "path " + withReturn + " return steps and " + withCall + " call steps " + result =
"transforming " + start + " into " + end "path " + withReturn + " return steps and " + withCall + " call steps " + "transforming " +
start + " into " + end
) )
} }
} }

View File

@@ -145,8 +145,9 @@ abstract class FrameworkLibraryWithGenericURL extends FrameworkLibraryWithURLReg
override string getAURLRegex() { override string getAURLRegex() {
exists(string id | id = getId() or id = getAnAlias() | exists(string id | id = getId() or id = getAnAlias() |
result = ".*(?:^|/)" + id + "-(" + semverRegex() + ")" + variantRegex() + "\\.js" or result = ".*(?:^|/)" + id + "-(" + semverRegex() + ")" + variantRegex() + "\\.js" or
result = ".*/(?:\\w+@)?(" + semverRegex() + ")/(?:(?:dist|js|" + id + ")/)?" + id + result =
variantRegex() + "\\.js" ".*/(?:\\w+@)?(" + semverRegex() + ")/(?:(?:dist|js|" + id + ")/)?" + id + variantRegex() +
"\\.js"
) )
} }
} }
@@ -158,7 +159,8 @@ abstract class FrameworkLibraryWithGenericURL extends FrameworkLibraryWithURLReg
* We ignore these when identifying frameworks. * We ignore these when identifying frameworks.
*/ */
private string variantRegex() { private string variantRegex() {
result = "([.-](slim|min|debug|dbg|umd|dev|all|testing|polyfills|" + result =
"([.-](slim|min|debug|dbg|umd|dev|all|testing|polyfills|" +
"core|compat|more|modern|sandbox|rtl|with-addons|legacy))*" "core|compat|more|modern|sandbox|rtl|with-addons|legacy))*"
} }
@@ -235,8 +237,8 @@ private predicate jQueryMarkerComment(Comment c, TopLevel tl, string version) {
tl = c.getTopLevel() and tl = c.getTopLevel() and
exists(string txt | txt = c.getText() | exists(string txt | txt = c.getText() |
// more recent versions use this format // more recent versions use this format
version = txt version =
.regexpCapture("(?s).*jQuery (?:JavaScript Library )?v(" + versionRegex() + ").*", 1) txt.regexpCapture("(?s).*jQuery (?:JavaScript Library )?v(" + versionRegex() + ").*", 1)
or or
// earlier versions used this format // earlier versions used this format
version = txt.regexpCapture("(?s).*jQuery (" + versionRegex() + ") - New Wave Javascript.*", 1) version = txt.regexpCapture("(?s).*jQuery (" + versionRegex() + ") - New Wave Javascript.*", 1)
@@ -502,7 +504,8 @@ private class Lodash extends FrameworkLibraryWithGenericURL, FrameworkLibraryWit
Lodash() { this = "lodash" } Lodash() { this = "lodash" }
override string getAMarkerCommentRegex() { override string getAMarkerCommentRegex() {
result = "(?s).* (?:lod|Lo-D)ash (<VERSION>)" + "(?: \\(Custom Build\\))? " + result =
"(?s).* (?:lod|Lo-D)ash (<VERSION>)" + "(?: \\(Custom Build\\))? " +
"<https?://lodash.com/>.*" "<https?://lodash.com/>.*"
} }
@@ -842,7 +845,8 @@ private class ApplicationInsightsInstance extends FrameworkLibraryInstance {
string version; string version;
ApplicationInsightsInstance() { ApplicationInsightsInstance() {
version = this version =
this
.(TopLevel) .(TopLevel)
.getFile() .getFile()
.getAbsolutePath() .getAbsolutePath()

View File

@@ -832,7 +832,8 @@ private newtype TAngularScope =
} or } or
MkIsolateScope(CustomDirective dir) { dir.hasIsolateScope() } or MkIsolateScope(CustomDirective dir) { dir.hasIsolateScope() } or
MkElementScope(DOM::ElementDefinition elem) { MkElementScope(DOM::ElementDefinition elem) {
any(DirectiveInstance d | not d.(CustomDirective).hasIsolateScope()).getAMatchingElement() = elem any(DirectiveInstance d | not d.(CustomDirective).hasIsolateScope()).getAMatchingElement() =
elem
} }
/** /**

View File

@@ -152,7 +152,8 @@ private class TemplateFieldNgSourceProvider extends NgSourceProvider {
TemplateFieldNgSourceProvider() { TemplateFieldNgSourceProvider() {
this = directive.getMember("template").asExpr() and this = directive.getMember("template").asExpr() and
source = this source =
this
.(ConstantString) .(ConstantString)
.getStringValue() .getStringValue()
.regexpFind(getInterpolatedExpressionPattern(), _, offset) .regexpFind(getInterpolatedExpressionPattern(), _, offset)
@@ -211,11 +212,8 @@ abstract class NgToken extends TNgToken {
*/ */
private int getIndex() { private int getIndex() {
exists(NgSource src, int start | this.at(src, start) | exists(NgSource src, int start | this.at(src, start) |
start = rank[result + 1](NgToken someToken, int someStart | start =
someToken.at(src, someStart) rank[result + 1](NgToken someToken, int someStart | someToken.at(src, someStart) | someStart)
|
someStart
)
) )
} }
@@ -278,7 +276,8 @@ private module Lexer {
NgOpTokenType() { this = "NgOpTokenType" } NgOpTokenType() { this = "NgOpTokenType" }
override string getPattern() { override string getPattern() {
result = concat(string op | result =
concat(string op |
op = "===" or op = "===" or
op = "!==" or op = "!==" or
op = "==" or op = "==" or
@@ -383,7 +382,8 @@ abstract class NgAstNode extends TNode {
*/ */
language[monotonicAggregates] language[monotonicAggregates]
string ppChildren() { string ppChildren() {
result = concat(NgAstNode child, int idx | result =
concat(NgAstNode child, int idx |
child = getChild(idx) and child = getChild(idx) and
not child instanceof Empty not child instanceof Empty
| |

View File

@@ -553,10 +553,10 @@ module ClientRequest {
/** /**
* Gets a reference to an instance of `chrome-remote-interface`. * Gets a reference to an instance of `chrome-remote-interface`.
* *
* An instantiation of `chrome-remote-interface` either accepts a callback or returns a promise. * An instantiation of `chrome-remote-interface` either accepts a callback or returns a promise.
* *
* The `isPromise` parameter reflects whether the reference is a promise containing * The `isPromise` parameter reflects whether the reference is a promise containing
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`. * an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
*/ */
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t, boolean isPromise) { private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t, boolean isPromise) {
t.start() and t.start() and

View File

@@ -609,7 +609,8 @@ private module Forge {
NonKeyCipher() { NonKeyCipher() {
exists(string algorithmName | algorithm.matchesName(algorithmName) | exists(string algorithmName | algorithm.matchesName(algorithmName) |
// require("forge").md.md5.create().update('The quick brown fox jumps over the lazy dog'); // require("forge").md.md5.create().update('The quick brown fox jumps over the lazy dog');
this = getAnImportNode() this =
getAnImportNode()
.getAPropertyRead("md") .getAPropertyRead("md")
.getAPropertyRead(algorithmName) .getAPropertyRead(algorithmName)
.getAMemberCall("create") .getAMemberCall("create")

View File

@@ -138,9 +138,7 @@ module Electron {
result = this.getABoundCallbackParameter(1, 0).getAPropertyWrite("returnValue").getRhs() result = this.getABoundCallbackParameter(1, 0).getAPropertyWrite("returnValue").getRhs()
} }
override IPCDispatch getAReturnDispatch() { override IPCDispatch getAReturnDispatch() { result.getCalleeName() = "sendSync" }
result.getCalleeName() = "sendSync"
}
} }
/** /**
@@ -168,7 +166,7 @@ module Electron {
} }
/** /**
* Gets a registration that this dispatch can send an event to. * Gets a registration that this dispatch can send an event to.
*/ */
override IPCSendRegistration getAReceiver() { override IPCSendRegistration getAReceiver() {
this.getEmitter() instanceof RendererProcess and this.getEmitter() instanceof RendererProcess and

View File

@@ -129,9 +129,7 @@ module EventRegistration {
* argument is the event handler callback. * argument is the event handler callback.
*/ */
abstract class DefaultEventRegistration extends Range, DataFlow::InvokeNode { abstract class DefaultEventRegistration extends Range, DataFlow::InvokeNode {
override string getChannel() { override string getChannel() { this.getArgument(0).mayHaveStringValue(result) }
this.getArgument(0).mayHaveStringValue(result)
}
override DataFlow::Node getReceivedItem(int i) { override DataFlow::Node getReceivedItem(int i) {
result = this.getABoundCallbackParameter(1, i) result = this.getABoundCallbackParameter(1, i)
@@ -187,13 +185,9 @@ module EventDispatch {
* is the `i`th item sent to the event handler. * is the `i`th item sent to the event handler.
*/ */
abstract class DefaultEventDispatch extends Range, DataFlow::InvokeNode { abstract class DefaultEventDispatch extends Range, DataFlow::InvokeNode {
override string getChannel() { override string getChannel() { this.getArgument(0).mayHaveStringValue(result) }
this.getArgument(0).mayHaveStringValue(result)
}
override DataFlow::Node getSentItem(int i) { override DataFlow::Node getSentItem(int i) { result = this.getArgument(i + 1) }
result = this.getArgument(i + 1)
}
override EventRegistration::Range getAReceiver() { this.getEmitter() = result.getEmitter() } override EventRegistration::Range getAReceiver() { this.getEmitter() = result.getEmitter() }
} }
@@ -223,4 +217,3 @@ private class EventEmitterTaintStep extends DataFlow::AdditionalFlowStep {
succ = dispatch succ = dispatch
} }
} }

View File

@@ -147,36 +147,34 @@ module Express {
this.getRequestMethod() = that.getRequestMethod() this.getRequestMethod() = that.getRequestMethod()
} }
} }
/** /**
* A call that sets up a Passport router that includes the request object. * A call that sets up a Passport router that includes the request object.
*/ */
private class PassportRouteSetup extends HTTP::Servers::StandardRouteSetup, CallExpr { private class PassportRouteSetup extends HTTP::Servers::StandardRouteSetup, CallExpr {
DataFlow::ModuleImportNode importNode; DataFlow::ModuleImportNode importNode;
DataFlow::FunctionNode callback; DataFlow::FunctionNode callback;
// looks for this pattern: passport.use(new Strategy({passReqToCallback: true}, callback)) // looks for this pattern: passport.use(new Strategy({passReqToCallback: true}, callback))
PassportRouteSetup() { PassportRouteSetup() {
importNode = DataFlow::moduleImport("passport") and importNode = DataFlow::moduleImport("passport") and
this = importNode.getAMemberCall("use").asExpr() and this = importNode.getAMemberCall("use").asExpr() and
exists(DataFlow::NewNode strategy | exists(DataFlow::NewNode strategy |
strategy.flowsToExpr(this.getArgument(0)) and strategy.flowsToExpr(this.getArgument(0)) and
strategy.getNumArgument() = 2 and strategy.getNumArgument() = 2 and
// new Strategy({passReqToCallback: true}, ...) // new Strategy({passReqToCallback: true}, ...)
strategy.getOptionArgument(0, "passReqToCallback").mayHaveBooleanValue(true) and strategy.getOptionArgument(0, "passReqToCallback").mayHaveBooleanValue(true) and
callback.flowsTo(strategy.getArgument(1)) callback.flowsTo(strategy.getArgument(1))
) )
} }
override Expr getServer() { result = importNode.asExpr() } override Expr getServer() { result = importNode.asExpr() }
override DataFlow::SourceNode getARouteHandler() { override DataFlow::SourceNode getARouteHandler() { result = callback }
result = callback
}
} }
/** /**
* The callback given to passport in PassportRouteSetup. * The callback given to passport in PassportRouteSetup.
*/ */
private class PassportRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler, private class PassportRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler,
DataFlow::ValueNode { DataFlow::ValueNode {
@@ -185,7 +183,7 @@ module Express {
PassportRouteHandler() { this = any(PassportRouteSetup setup).getARouteHandler() } PassportRouteHandler() { this = any(PassportRouteSetup setup).getARouteHandler() }
override SimpleParameter getRouteHandlerParameter(string kind) { override SimpleParameter getRouteHandlerParameter(string kind) {
kind = "request" and kind = "request" and
result = astNode.getParameter(0) result = astNode.getParameter(0)
} }
} }

View File

@@ -21,7 +21,8 @@ private class WalkFileNameSource extends FileNameSource {
WalkFileNameSource() { WalkFileNameSource() {
// `stats.name` in `require('walk').walk(_).on(_, (_, stats) => stats.name)` // `stats.name` in `require('walk').walk(_).on(_, (_, stats) => stats.name)`
exists(DataFlow::FunctionNode callback | exists(DataFlow::FunctionNode callback |
callback = DataFlow::moduleMember("walk", "walk") callback =
DataFlow::moduleMember("walk", "walk")
.getACall() .getACall()
.getAMethodCall(EventEmitter::on()) .getAMethodCall(EventEmitter::on())
.getCallback(1) .getCallback(1)
@@ -66,7 +67,8 @@ private class GlobbyFileNameSource extends FileNameSource {
this = DataFlow::moduleMember(moduleName, "sync").getACall() this = DataFlow::moduleMember(moduleName, "sync").getACall()
or or
// `files` in `require('globby')(_).then(files => ...)` // `files` in `require('globby')(_).then(files => ...)`
this = DataFlow::moduleImport(moduleName) this =
DataFlow::moduleImport(moduleName)
.getACall() .getACall()
.getAMethodCall("then") .getAMethodCall("then")
.getCallback(0) .getCallback(0)
@@ -95,7 +97,8 @@ private class FastGlobFileNameSource extends FileNameSource {
) )
or or
// `file` in `require('fast-glob').stream(_).on(_, file => ...)` // `file` in `require('fast-glob').stream(_).on(_, file => ...)`
this = DataFlow::moduleMember(moduleName, "stream") this =
DataFlow::moduleMember(moduleName, "stream")
.getACall() .getACall()
.getAMethodCall(EventEmitter::on()) .getAMethodCall(EventEmitter::on())
.getCallback(1) .getCallback(1)

View File

@@ -123,8 +123,7 @@ module HTTP {
* *
* Gets the string `http` or `https`. * Gets the string `http` or `https`.
*/ */
deprecated deprecated string httpOrHttps() { result = "http" or result = "https" }
string httpOrHttps() { result = "http" or result = "https" }
/** /**
* An expression whose value is sent as (part of) the body of an HTTP response. * An expression whose value is sent as (part of) the body of an HTTP response.

View File

@@ -6,7 +6,7 @@ import javascript
module Handlebars { module Handlebars {
/** /**
* A reference to the Handlebars library. * A reference to the Handlebars library.
*/ */
class Handlebars extends DataFlow::SourceNode { class Handlebars extends DataFlow::SourceNode {
Handlebars() { Handlebars() {

View File

@@ -11,7 +11,7 @@ module Koa {
*/ */
class AppDefinition extends HTTP::Servers::StandardServerDefinition, InvokeExpr { class AppDefinition extends HTTP::Servers::StandardServerDefinition, InvokeExpr {
AppDefinition() { AppDefinition() {
// `app = new Koa()` / `app = Koa()` // `app = new Koa()` / `app = Koa()`
this = DataFlow::moduleImport("koa").getAnInvocation().asExpr() this = DataFlow::moduleImport("koa").getAnInvocation().asExpr()
} }
} }

View File

@@ -58,9 +58,11 @@ private module Console {
} }
override DataFlow::Node getAMessageComponent() { override DataFlow::Node getAMessageComponent() {
if name = "assert" (
then result = getArgument([1 .. getNumArgument()]) if name = "assert"
else result = getAnArgument() then result = getArgument([1 .. getNumArgument()])
else result = getAnArgument()
)
or or
result = getASpreadArgument() result = getASpreadArgument()
} }
@@ -68,9 +70,7 @@ private module Console {
/** /**
* Gets the name of the console logging method, e.g. "log", "error", "assert", etc. * Gets the name of the console logging method, e.g. "log", "error", "assert", etc.
*/ */
string getName() { string getName() { result = name }
result = name
}
} }
} }
@@ -99,7 +99,8 @@ private module Winston {
*/ */
class WinstonLoggerCall extends LoggerCall, DataFlow::MethodCallNode { class WinstonLoggerCall extends LoggerCall, DataFlow::MethodCallNode {
WinstonLoggerCall() { WinstonLoggerCall() {
this = DataFlow::moduleMember("winston", "createLogger") this =
DataFlow::moduleMember("winston", "createLogger")
.getACall() .getACall()
.getAMethodCall(getAStandardLoggerMethodName()) .getAMethodCall(getAStandardLoggerMethodName())
} }
@@ -121,7 +122,8 @@ private module log4js {
*/ */
class Log4jsLoggerCall extends LoggerCall { class Log4jsLoggerCall extends LoggerCall {
Log4jsLoggerCall() { Log4jsLoggerCall() {
this = DataFlow::moduleMember("log4js", "getLogger") this =
DataFlow::moduleMember("log4js", "getLogger")
.getACall() .getACall()
.getAMethodCall(getAStandardLoggerMethodName()) .getAMethodCall(getAStandardLoggerMethodName())
} }

View File

@@ -84,9 +84,7 @@ private module MongoDB {
* of `mongodb.Collection`. * of `mongodb.Collection`.
*/ */
private class CollectionFromType extends Collection { private class CollectionFromType extends Collection {
CollectionFromType() { CollectionFromType() { hasUnderlyingType("mongodb", "Collection") }
hasUnderlyingType("mongodb", "Collection")
}
} }
/** Gets a data flow node referring to a MongoDB collection. */ /** Gets a data flow node referring to a MongoDB collection. */

Some files were not shown because too many files have changed in this diff Show More