mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
JS: Autoformat everything
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -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."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 + "'."
|
||||||
|
|||||||
@@ -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()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 $@.",
|
||||||
|
|||||||
@@ -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, _)
|
||||||
|
|||||||
@@ -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."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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."
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
12
javascript/ql/src/external/CodeDuplication.qll
vendored
12
javascript/ql/src/external/CodeDuplication.qll
vendored
@@ -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
|
||||||
|
|||||||
5
javascript/ql/src/external/DefectFilter.qll
vendored
5
javascript/ql/src/external/DefectFilter.qll
vendored
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
javascript/ql/src/external/MetricFilter.qll
vendored
5
javascript/ql/src/external/MetricFilter.qll
vendored
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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") }
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -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))
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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() }
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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`.
|
||||||
|
|||||||
@@ -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() }
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user