зеркало из https://github.com/github/codeql.git
JS: Autoformat everything
This commit is contained in:
Родитель
9c06c48dc7
Коммит
fefcf1a7a6
|
@ -24,8 +24,9 @@ where
|
|||
exists(string n | p = f.getDependencyParameter(n) |
|
||||
p.getName() != n and
|
||||
exists(f.getDependencyParameter(p.getName())) and
|
||||
msg = "This parameter is named '" + p.getName() + "', " +
|
||||
"but actually refers to dependency '" + n + "'."
|
||||
msg =
|
||||
"This parameter is named '" + p.getName() + "', " + "but actually refers to dependency '" +
|
||||
n + "'."
|
||||
)
|
||||
)
|
||||
select p, msg
|
||||
|
|
|
@ -15,7 +15,8 @@ import javascript
|
|||
from AngularJS::ServiceReference compile, SimpleParameter elem, CallExpr c
|
||||
where
|
||||
compile.getName() = "$compile" and
|
||||
elem = any(AngularJS::CustomDirective d)
|
||||
elem =
|
||||
any(AngularJS::CustomDirective d)
|
||||
.getALinkFunction()
|
||||
.(AngularJS::LinkFunction)
|
||||
.getElementParameter() and
|
||||
|
|
|
@ -130,7 +130,8 @@ where
|
|||
kind = getServiceKind(request, name) and
|
||||
exists(request.getAServiceDefinition(name)) and // ignore unknown/undefined services
|
||||
not isCompatibleRequestedService(request, kind) and
|
||||
compatibleWithString = concat(string compatibleKind |
|
||||
compatibleWithString =
|
||||
concat(string compatibleKind |
|
||||
isCompatibleRequestedService(request, compatibleKind) and
|
||||
not isWildcardKind(compatibleKind)
|
||||
|
|
||||
|
|
|
@ -34,8 +34,9 @@ predicate isMissingParameter(AngularJS::InjectableFunction f, string msg, ASTNod
|
|||
then dependenciesAreString = "dependency is"
|
||||
else dependenciesAreString = "dependencies are"
|
||||
) and
|
||||
msg = "This function has " + paramCount + " " + parametersString + ", but " + injectionCount +
|
||||
" " + dependenciesAreString + " injected into it."
|
||||
msg =
|
||||
"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`.
|
||||
*/
|
||||
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
|
||||
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`.
|
||||
*/
|
||||
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
|
||||
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`.
|
||||
*/
|
||||
GlobalVarAccess getFirstAccessIn(GlobalVariable v, Function f) {
|
||||
result = min(getAccessIn(v, f) as gva
|
||||
result =
|
||||
min(getAccessIn(v, f) as gva
|
||||
order by
|
||||
gva.getLocation().getStartLine(), gva.getLocation().getStartColumn()
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* @name Suspicious method name declaration
|
||||
* @description A method declaration with a name that is a special keyword in another
|
||||
* context is suspicious.
|
||||
* @description A method declaration with a name that is a special keyword in another
|
||||
* context is suspicious.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id js/suspicious-method-name-declaration
|
||||
|
@ -19,7 +19,7 @@ import javascript
|
|||
predicate isSuspiciousMethodName(string name, ClassOrInterface container) {
|
||||
name = "function"
|
||||
or
|
||||
// "constructor" is only suspicious outside a class.
|
||||
// "constructor" is only suspicious outside a class.
|
||||
name = "constructor" and not container instanceof ClassDefinition
|
||||
or
|
||||
// "new" is only suspicious inside a class.
|
||||
|
@ -31,7 +31,6 @@ where
|
|||
container.getLocation().getFile().getFileType().isTypeScript() and
|
||||
container.getMember(name) = member and
|
||||
isSuspiciousMethodName(name, container) and
|
||||
|
||||
// Cases to ignore.
|
||||
not (
|
||||
// Assume that a "new" method is intentional if the class has an explicit constructor.
|
||||
|
@ -39,31 +38,34 @@ where
|
|||
container instanceof ClassDefinition and
|
||||
exists(ConstructorDeclaration constructor |
|
||||
container.getMember("constructor") = constructor and
|
||||
not constructor.isSynthetic()
|
||||
)
|
||||
not constructor.isSynthetic()
|
||||
)
|
||||
or
|
||||
// Explicitly declared static methods are fine.
|
||||
container instanceof ClassDefinition and
|
||||
member.isStatic()
|
||||
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())
|
||||
or
|
||||
// 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() |
|
||||
other.getName() != "function" and
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -44,43 +44,39 @@ string getKind(MemberDeclaration m) {
|
|||
* A call-signature that originates from a MethodSignature in the AST.
|
||||
*/
|
||||
private class MethodCallSig extends CallSignatureType {
|
||||
string name;
|
||||
|
||||
MethodCallSig() {
|
||||
exists(MethodSignature sig |
|
||||
this = sig.getBody().getCallSignature() and
|
||||
name = sig.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of any member that has this signature.
|
||||
*/
|
||||
string getName() {
|
||||
result = name
|
||||
}
|
||||
string name;
|
||||
|
||||
MethodCallSig() {
|
||||
exists(MethodSignature sig |
|
||||
this = sig.getBody().getCallSignature() and
|
||||
name = sig.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of any member that has this signature.
|
||||
*/
|
||||
string getName() { result = name }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the two call signatures could be overloads of each other and have the same parameter types.
|
||||
*/
|
||||
predicate matchingCallSignature(MethodCallSig method, MethodCallSig other) {
|
||||
method.getName() = other.getName() and
|
||||
|
||||
method.getName() = other.getName() and
|
||||
method.getNumOptionalParameter() = other.getNumOptionalParameter() and
|
||||
method.getNumParameter() = other.getNumParameter() and
|
||||
method.getNumRequiredParameter() = other.getNumRequiredParameter() and
|
||||
// purposely not looking at number of type arguments.
|
||||
|
||||
method.getKind() = other.getKind() and
|
||||
|
||||
|
||||
forall(int i | i in [0 .. -1 + method.getNumParameter()] |
|
||||
// purposely not looking at number of type arguments.
|
||||
method.getKind() = other.getKind() and
|
||||
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.
|
||||
) and
|
||||
|
||||
// shared type parameters are equal.
|
||||
forall(int i | i in [0 .. -1 + min(int num | num = method.getNumTypeParameter() or num = other.getNumTypeParameter())] |
|
||||
) and
|
||||
// shared type parameters are equal.
|
||||
forall(int i |
|
||||
i in [0 .. -1 +
|
||||
min(int num | num = method.getNumTypeParameter() or num = other.getNumTypeParameter())]
|
||||
|
|
||||
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.
|
||||
*/
|
||||
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
|
||||
// same static modifier.
|
||||
getKind(method) = getKind(other) and
|
||||
|
||||
// same name.
|
||||
method.getName() = other.getName() and
|
||||
|
||||
// same number of parameters.
|
||||
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.
|
||||
forall(int i | i in [0 .. -1 + method.getBody().getNumParameter()] |
|
||||
getParameterTypeAnnotation(method, i) = getParameterTypeAnnotation(other, i)
|
||||
getParameterTypeAnnotation(method, i) = getParameterTypeAnnotation(other, i)
|
||||
) and
|
||||
|
||||
matchingCallSignature(method.getBody().getCallSignature(), other.getBody().getCallSignature())
|
||||
}
|
||||
|
||||
|
@ -119,22 +111,19 @@ from ClassOrInterface decl, string name, MethodSignature previous, MethodSignatu
|
|||
where
|
||||
previous = decl.getMethod(name) 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.
|
||||
not exists(decl.getASuperTypeDeclaration().getMethod(name)) and
|
||||
not exists(ClassOrInterface sub |
|
||||
not exists(decl.getASuperTypeDeclaration().getMethod(name)) and
|
||||
not exists(ClassOrInterface sub |
|
||||
decl = sub.getASuperTypeDeclaration() and
|
||||
exists(sub.getMethod(name))
|
||||
) 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.
|
||||
// This comparison removes those cases.
|
||||
unreachable.getBody().getNumTypeParameter() <= previous.getBody().getNumTypeParameter() and
|
||||
|
||||
// We always select the first of the overloaded methods.
|
||||
not exists(MethodSignature later | later = getOtherMatchingSignatures(previous) |
|
||||
getOverloadIndex(later) < getOverloadIndex(previous)
|
||||
)
|
||||
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.
|
||||
*/
|
||||
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
|
||||
p.getFirstToken().getIndex()
|
||||
)
|
||||
|
@ -133,14 +134,15 @@ string pathToModule(Module source, Module destination, int steps) {
|
|||
steps > 1 and
|
||||
exists(Module next |
|
||||
// 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
|
||||
numberOfStepsToModule(mod, destination, steps - 1)
|
||||
|
|
||||
mod order by mod.getName()
|
||||
) and
|
||||
result = repr(getARuntimeImport(source, next)) + " => " +
|
||||
pathToModule(next, destination, steps - 1)
|
||||
result =
|
||||
repr(getARuntimeImport(source, next)) + " => " + pathToModule(next, destination, steps - 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import javascript
|
|||
import ExprHasNoEffect
|
||||
import semmle.javascript.RestrictedLocations
|
||||
|
||||
|
||||
from Expr e
|
||||
where hasNoEffect(e)
|
||||
select e.(FirstLineOf), "This expression has no effect."
|
||||
|
|
|
@ -200,10 +200,12 @@ where
|
|||
rightExprDescription = getDescription(right.asExpr(), "an expression") and
|
||||
leftTypeCount = strictcount(left.getAType()) and
|
||||
rightTypeCount = strictcount(right.getAType()) and
|
||||
leftTypeDescription = getTypeDescription("is of type " + leftTypes,
|
||||
"cannot be of type " + rightTypes, leftTypeCount, rightTypeCount) and
|
||||
rightTypeDescription = getTypeDescription("of type " + rightTypes,
|
||||
", which cannot be of type " + leftTypes, rightTypeCount, leftTypeCount)
|
||||
leftTypeDescription =
|
||||
getTypeDescription("is of type " + leftTypes, "cannot be of type " + rightTypes, leftTypeCount,
|
||||
rightTypeCount) and
|
||||
rightTypeDescription =
|
||||
getTypeDescription("of type " + rightTypes, ", which cannot be of type " + leftTypes,
|
||||
rightTypeCount, leftTypeCount)
|
||||
select left,
|
||||
leftExprDescription + " " + leftTypeDescription + ", but it is compared to $@ " +
|
||||
rightTypeDescription + ".", right, rightExprDescription
|
||||
|
|
|
@ -23,48 +23,40 @@ Expr leftChild(Expr e) {
|
|||
}
|
||||
|
||||
predicate isInConcat(Expr e) {
|
||||
exists(ParExpr par | isInConcat(par) and par.getExpression() = e)
|
||||
or
|
||||
exists(AddExpr a | a.getAnOperand() = e)
|
||||
exists(ParExpr par | isInConcat(par) and par.getExpression() = e)
|
||||
or
|
||||
exists(AddExpr a | a.getAnOperand() = e)
|
||||
}
|
||||
|
||||
class ConcatenationLiteral extends Expr {
|
||||
ConcatenationLiteral() {
|
||||
(
|
||||
this instanceof TemplateLiteral
|
||||
this instanceof TemplateLiteral
|
||||
or
|
||||
this instanceof Literal
|
||||
)
|
||||
and isInConcat(this)
|
||||
) and
|
||||
isInConcat(this)
|
||||
}
|
||||
}
|
||||
|
||||
Expr getConcatChild(Expr e) {
|
||||
result = rightChild(e) or
|
||||
result = leftChild(e)
|
||||
result = rightChild(e) or
|
||||
result = leftChild(e)
|
||||
}
|
||||
|
||||
Expr getConcatParent(Expr e) {
|
||||
e = getConcatChild(result)
|
||||
}
|
||||
Expr getConcatParent(Expr e) { e = getConcatChild(result) }
|
||||
|
||||
predicate isWordLike(ConcatenationLiteral lit) {
|
||||
lit.getStringValue().regexpMatch("(?i).*[a-z]{3,}.*")
|
||||
lit.getStringValue().regexpMatch("(?i).*[a-z]{3,}.*")
|
||||
}
|
||||
|
||||
class ConcatRoot extends AddExpr {
|
||||
ConcatRoot() {
|
||||
not isInConcat(this)
|
||||
}
|
||||
ConcatRoot() { not isInConcat(this) }
|
||||
}
|
||||
|
||||
ConcatRoot getAddRoot(AddExpr e) {
|
||||
result = getConcatParent*(e)
|
||||
}
|
||||
ConcatRoot getAddRoot(AddExpr e) { result = getConcatParent*(e) }
|
||||
|
||||
predicate hasWordLikeFragment(AddExpr e) {
|
||||
isWordLike(getConcatChild*(getAddRoot(e)))
|
||||
}
|
||||
predicate hasWordLikeFragment(AddExpr e) { isWordLike(getConcatChild*(getAddRoot(e))) }
|
||||
|
||||
from AddExpr e, ConcatenationLiteral l, ConcatenationLiteral r, string word
|
||||
where
|
||||
|
@ -79,7 +71,6 @@ where
|
|||
word = l.getStringValue().regexpCapture(".* (([-A-Za-z/'\\.:,]*[a-zA-Z]|[0-9]+)[\\.:,!?']*)", 1) and
|
||||
r.getStringValue().regexpMatch("[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.
|
||||
hasWordLikeFragment(e)
|
||||
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)
|
||||
|
|
||||
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
|
||||
result = rightName.toLowerCase()
|
||||
)
|
||||
|
|
|
@ -36,7 +36,8 @@ private predicate isBoundInMethod(MethodDeclaration method) {
|
|||
mod = "react-autobind"
|
||||
|
|
||||
thiz.flowsTo(DataFlow::moduleImport(mod).getACall().getArgument(0))
|
||||
) or
|
||||
)
|
||||
or
|
||||
// heuristic reflective binders
|
||||
exists(DataFlow::CallNode binder, string calleeName |
|
||||
(
|
||||
|
@ -92,8 +93,8 @@ private DOM::AttributeDefinition getAnEventHandlerAttribute() {
|
|||
from MethodDeclaration callback, DOM::AttributeDefinition attribute, ThisExpr unbound
|
||||
where
|
||||
attribute = getAnEventHandlerAttribute() and
|
||||
attribute.getValueNode().analyze().getAValue().(AbstractFunction).getFunction() = callback
|
||||
.getBody() and
|
||||
attribute.getValueNode().analyze().getAValue().(AbstractFunction).getFunction() =
|
||||
callback.getBody() and
|
||||
unbound.getBinder() = callback.getBody() and
|
||||
not isBoundInMethod(callback)
|
||||
select attribute,
|
||||
|
|
|
@ -59,7 +59,8 @@ where
|
|||
not isCallToFunction(cs) and
|
||||
// conservatively only flag call sites where _all_ callees are illegal
|
||||
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, _, _)
|
||||
) and
|
||||
// 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.
|
||||
*/
|
||||
Function getALikelyCallee(DataFlow::InvokeNode cs, boolean isNew) {
|
||||
result = min(Function callee, int imprecision |
|
||||
result =
|
||||
min(Function callee, int imprecision |
|
||||
callee = cs.getACallee(imprecision)
|
||||
|
|
||||
callee order by imprecision
|
||||
|
@ -84,7 +85,8 @@ predicate whitelistedCall(DataFlow::CallNode call) {
|
|||
* and start column.
|
||||
*/
|
||||
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, _, _)
|
||||
|
|
||||
invk order by path, line, col
|
||||
|
|
|
@ -14,7 +14,8 @@ import javascript
|
|||
|
||||
from File f, float n
|
||||
where
|
||||
n = avg(Function fun, int toAvg |
|
||||
n =
|
||||
avg(Function fun, int toAvg |
|
||||
fun.getTopLevel().getFile() = f and toAvg = fun.getCyclomaticComplexity()
|
||||
|
|
||||
toAvg
|
||||
|
|
|
@ -16,7 +16,6 @@ import javascript
|
|||
import semmle.javascript.security.performance.PolynomialReDoS::PolynomialReDoS
|
||||
import DataFlow::PathGraph
|
||||
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This expensive $@ use depends on $@.",
|
||||
|
|
|
@ -292,9 +292,7 @@ class EdgeLabel extends TInputSymbol {
|
|||
* Gets the state before matching `t`.
|
||||
*/
|
||||
pragma[inline]
|
||||
State before(RegExpTerm t) {
|
||||
result = Match(t, 0)
|
||||
}
|
||||
State before(RegExpTerm t) { result = Match(t, 0) }
|
||||
|
||||
/**
|
||||
* Gets a state the NFA may be in after matching `t`.
|
||||
|
@ -337,9 +335,7 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
|
|||
)
|
||||
)
|
||||
or
|
||||
exists(RegExpDot dot |
|
||||
q1 = before(dot) and q2 = after(dot)
|
||||
|
|
||||
exists(RegExpDot dot | q1 = before(dot) and q2 = after(dot) |
|
||||
if dot.getLiteral().isDotAll() then lbl = Any() else lbl = Dot()
|
||||
)
|
||||
or
|
||||
|
@ -545,7 +541,8 @@ string intersect(InputSymbol c, InputSymbol d) {
|
|||
* Gets a character matched by character class `cc`.
|
||||
*/
|
||||
string choose(RegExpCharacterClass cc) {
|
||||
result = min(string c |
|
||||
result =
|
||||
min(string c |
|
||||
exists(RegExpTerm child | child = cc.getAChild() |
|
||||
c = child.(RegExpConstant).getValue() or
|
||||
child.(RegExpCharacterRange).isRange(c, _)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
import javascript
|
||||
|
||||
from RegExpCharEscape rece
|
||||
where rece.toString() = "\\b"
|
||||
and rece.isPartOfRegExpLiteral()
|
||||
where
|
||||
rece.toString() = "\\b" and
|
||||
rece.isPartOfRegExpLiteral()
|
||||
select rece, "Backspace escape in regular expression."
|
||||
|
|
|
@ -18,7 +18,8 @@ import javascript
|
|||
* Indexing is 1-based.
|
||||
*/
|
||||
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 order by j
|
||||
|
|
|
@ -58,7 +58,8 @@ predicate regExpMatchesString(RegExpTerm t, string s) {
|
|||
or
|
||||
// sequences match the concatenation of their elements
|
||||
exists(RegExpSequence seq | seq = t |
|
||||
s = concat(int i, RegExpTerm child |
|
||||
s =
|
||||
concat(int i, RegExpTerm child |
|
||||
child = seq.getChild(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 {
|
||||
DataFlow::RegExpCreationNode regexp;
|
||||
|
||||
RegExpTestCall() {
|
||||
this = regexp.getAReference().getAMethodCall("test")
|
||||
}
|
||||
RegExpTestCall() { this = regexp.getAReference().getAMethodCall("test") }
|
||||
|
||||
override RegExpTerm getRegExp() {
|
||||
result = regexp.getRoot()
|
||||
}
|
||||
override RegExpTerm getRegExp() { result = regexp.getRoot() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,9 +89,7 @@ class RegExpSearchCall extends DataFlow::MethodCallNode, RegExpQuery {
|
|||
regexp.getAReference().flowsTo(getArgument(0))
|
||||
}
|
||||
|
||||
override RegExpTerm getRegExp() {
|
||||
result = regexp.getRoot()
|
||||
}
|
||||
override RegExpTerm getRegExp() { result = regexp.getRoot() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,10 +110,12 @@ where
|
|||
(
|
||||
call instanceof RegExpTestCall 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
|
||||
call instanceof RegExpSearchCall 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"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Provides predicates for reasoning about regular expressions
|
||||
* that match URLs and hostname patterns.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
|
@ -39,9 +40,7 @@ predicate isDotLike(RegExpTerm term) {
|
|||
predicate matchesBeginningOfString(RegExpTerm term) {
|
||||
term.isRootTerm()
|
||||
or
|
||||
exists(RegExpTerm parent |
|
||||
matchesBeginningOfString(parent)
|
||||
|
|
||||
exists(RegExpTerm parent | matchesBeginningOfString(parent) |
|
||||
term = parent.(RegExpSequence).getChild(0)
|
||||
or
|
||||
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.
|
||||
*/
|
||||
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
|
||||
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,
|
||||
* such as `.com`.
|
||||
*/
|
||||
predicate hasTopLevelDomainEnding(RegExpSequence seq) {
|
||||
hasTopLevelDomainEnding(seq, _)
|
||||
}
|
||||
predicate hasTopLevelDomainEnding(RegExpSequence seq) { hasTopLevelDomainEnding(seq, _) }
|
||||
|
||||
/**
|
||||
* 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(i - 1) and // Should not be the '.' immediately before the TLD
|
||||
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`
|
||||
//
|
||||
// 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
|
||||
not seq.getAChild() instanceof RegExpDollar and
|
||||
seq.getChild([i .. i + 1]).(RegExpConstant).getValue().regexpMatch(".*[/?#].*") and
|
||||
msg = "has an unrestricted wildcard '" +
|
||||
unescapedDot.getParent().(RegExpQuantifier).getRawValue() +
|
||||
"' which may cause '" + hostname + "' to be matched anywhere in the URL, outside the hostname."
|
||||
) else (
|
||||
msg = "has an unescaped '.' before '" + hostname + "', so it might match more hosts than expected."
|
||||
)
|
||||
msg =
|
||||
"has an unrestricted wildcard '" + unescapedDot.getParent().(RegExpQuantifier).getRawValue()
|
||||
+ "' which may cause '" + hostname +
|
||||
"' to be matched anywhere in the URL, outside the hostname."
|
||||
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
|
||||
regexp = re.getRegExpTerm() and
|
||||
isIncompleteHostNameRegExpPattern(regexp, hostSequence, msg) and
|
||||
|
@ -96,5 +103,4 @@ where
|
|||
)
|
||||
) and
|
||||
not CharacterEscapes::hasALikelyRegExpPatternMistake(re)
|
||||
select hostSequence,
|
||||
"This " + kind + " " + msg, aux, "here"
|
||||
select hostSequence, "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`. */
|
||||
DataFlow::Node schemeCheck(
|
||||
DataFlow::Node nd, DangerousScheme scheme
|
||||
) {
|
||||
DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
|
||||
// check of the form `nd.startsWith(scheme)`
|
||||
exists(StringOps::StartsWith sw | sw = result |
|
||||
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
|
||||
* in its tree, such as `(foo|bar$|baz)`.
|
||||
*/
|
||||
predicate containsInteriorAnchor(RegExpTerm term) {
|
||||
isInteriorAnchor(term.getAChild*())
|
||||
}
|
||||
predicate containsInteriorAnchor(RegExpTerm term) { isInteriorAnchor(term.getAChild*()) }
|
||||
|
||||
/**
|
||||
* 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
|
||||
* literals with a trailing alternative such as `foo|`.
|
||||
*/
|
||||
predicate isEmpty(RegExpSequence term) {
|
||||
term.getNumChild() = 0
|
||||
}
|
||||
predicate isEmpty(RegExpSequence term) { term.getNumChild() = 0 }
|
||||
|
||||
/**
|
||||
* Holds if `term` contains a letter constant.
|
||||
|
@ -131,14 +127,14 @@ predicate hasMisleadingAnchorPrecedence(RegExpPatternSource src, string msg) {
|
|||
(
|
||||
anchoredTerm = root.getChild(0) and
|
||||
anchoredTerm.getChild(0) instanceof RegExpCaret and
|
||||
not containsLeadingPseudoAnchor(root.getChild([ 1 .. root.getNumChild() - 1 ])) and
|
||||
containsLetters(root.getChild([ 1 .. root.getNumChild() - 1 ])) and
|
||||
not containsLeadingPseudoAnchor(root.getChild([1 .. root.getNumChild() - 1])) and
|
||||
containsLetters(root.getChild([1 .. root.getNumChild() - 1])) and
|
||||
direction = "beginning"
|
||||
or
|
||||
anchoredTerm = root.getLastChild() and
|
||||
anchoredTerm.getLastChild() instanceof RegExpDollar and
|
||||
not containsTrailingPseudoAnchor(root.getChild([ 0 .. root.getNumChild() - 2 ])) and
|
||||
containsLetters(root.getChild([ 0 .. root.getNumChild() - 2 ])) and
|
||||
not containsTrailingPseudoAnchor(root.getChild([0 .. root.getNumChild() - 2])) and
|
||||
containsLetters(root.getChild([0 .. root.getNumChild() - 2])) and
|
||||
direction = "end"
|
||||
) and
|
||||
// is not used for replace
|
||||
|
@ -146,8 +142,10 @@ predicate hasMisleadingAnchorPrecedence(RegExpPatternSource src, string msg) {
|
|||
replace.getMethodName() = "replace" and
|
||||
src.getARegExpObject().flowsTo(replace.getArgument(0))
|
||||
) and
|
||||
msg = "Misleading operator precedence. The subexpression '" + anchoredTerm.getRawValue() +
|
||||
"' is anchored at the " + direction + ", but the other parts of this regular expression are not"
|
||||
msg =
|
||||
"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
|
||||
isFinalRegExpTerm(tld.getChild(i)) and // nothing is matched after the TLD
|
||||
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())
|
||||
)
|
||||
) 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 {
|
||||
RegExpPatternSource src;
|
||||
|
||||
string char;
|
||||
|
||||
string mistake;
|
||||
|
||||
int index;
|
||||
|
||||
ASTNode rawStringNode;
|
||||
|
||||
IdentityEscapeInStringMistake() {
|
||||
|
@ -130,19 +126,19 @@ class IdentityEscapeInStringMistake extends RegExpPatternMistake, TIdentityEscap
|
|||
}
|
||||
|
||||
/**
|
||||
* A backspace as '\b' in a regular expression string, indicating
|
||||
* programmer intent to use the word-boundary assertion '\b'.
|
||||
*/
|
||||
* A backspace as '\b' in a regular expression string, indicating
|
||||
* programmer intent to use the word-boundary assertion '\b'.
|
||||
*/
|
||||
class BackspaceInStringMistake extends RegExpPatternMistake, TBackspaceInStringMistake {
|
||||
RegExpPatternSource src;
|
||||
|
||||
int index;
|
||||
|
||||
ASTNode rawStringNode;
|
||||
|
||||
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 }
|
||||
|
||||
|
|
|
@ -16,11 +16,16 @@ import javascript
|
|||
import semmle.javascript.security.dataflow.CommandInjection::CommandInjection
|
||||
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
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
if cfg.isSinkWithHighlight(sink.getNode(), _)
|
||||
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
|
||||
else highlight = sink.getNode() and
|
||||
(
|
||||
if cfg.isSinkWithHighlight(sink.getNode(), _)
|
||||
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
|
||||
else highlight = sink.getNode()
|
||||
) and
|
||||
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 DataFlow::PathGraph
|
||||
|
||||
from
|
||||
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where
|
||||
cfg.hasFlowPath(source, sink)
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
sink.getNode().(Xss::Shared::Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
sink.getNode().(Xss::Shared::Sink).getVulnerabilityKind() + " vulnerability due to $@.",
|
||||
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.
|
||||
*/
|
||||
class Replacement extends StringReplaceCall {
|
||||
Replacement() {
|
||||
isGlobal()
|
||||
}
|
||||
Replacement() { isGlobal() }
|
||||
|
||||
/**
|
||||
* Gets the input of this replacement.
|
||||
*/
|
||||
DataFlow::Node getInput() {
|
||||
result = this.getReceiver()
|
||||
}
|
||||
DataFlow::Node getInput() { result = this.getReceiver() }
|
||||
|
||||
/**
|
||||
* Gets the output of this replacement.
|
||||
*/
|
||||
DataFlow::SourceNode getOutput() {
|
||||
result = this
|
||||
}
|
||||
DataFlow::SourceNode getOutput() { result = this }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
Replacement getPreviousReplacement() {
|
||||
result.getOutput() = getASimplePredecessor*(getInput())
|
||||
}
|
||||
Replacement getPreviousReplacement() { result.getOutput() = getASimplePredecessor*(getInput()) }
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
predicate isSimpleAlt(RegExpAlt t) {
|
||||
forall(RegExpTerm ch | ch = t.getAChild() | isSimple(ch))
|
||||
}
|
||||
predicate isSimpleAlt(RegExpAlt t) { forall(RegExpTerm ch | ch = t.getAChild() | isSimple(ch)) }
|
||||
|
||||
/**
|
||||
* Holds if `mce` is of the form `x.replace(re, new)`, where `re` is a global
|
||||
|
|
|
@ -13,26 +13,20 @@
|
|||
import javascript
|
||||
|
||||
/** Gets a property name of `req` which refers to data usually derived from cookie data. */
|
||||
string cookieProperty() {
|
||||
result = "session" or result = "cookies" or result = "user"
|
||||
}
|
||||
string cookieProperty() { 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`. */
|
||||
private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
exists(DataFlow::PropRead value |
|
||||
value = result.getAPropertyRead(cookieProperty()).getAPropertyRead() and
|
||||
|
||||
// Ignore accesses to values that are part of a CSRF or captcha check
|
||||
not value.getPropertyName().regexpMatch("(?i).*(csrf|xsrf|captcha).*") and
|
||||
|
||||
// Ignore calls like `req.session.save()`
|
||||
not value = any(DataFlow::InvokeNode call).getCalleeNode()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 |
|
||||
result = nodeLeadingToCookieAccess(t2).backtrack(t2, t)
|
||||
)
|
||||
exists(DataFlow::TypeBackTracker t2 | result = nodeLeadingToCookieAccess(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** 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
|
||||
isRouteHandlerUsingCookies(result)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = getARouteUsingCookies(t2).track(t2, t)
|
||||
)
|
||||
exists(DataFlow::TypeTracker t2 | result = getARouteUsingCookies(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a route handler that uses cookies. */
|
||||
|
@ -113,7 +105,6 @@ from
|
|||
where
|
||||
router = setup.getRouter() and
|
||||
handler = setup.getARouteHandlerExpr() and
|
||||
|
||||
// Require that the handler uses cookies and has cookie middleware.
|
||||
//
|
||||
// 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.
|
||||
getARouteUsingCookies().flowsToExpr(handler) and
|
||||
hasCookieMiddleware(handler, cookie) and
|
||||
|
||||
// Only flag the cookie parser registered first.
|
||||
not hasCookieMiddleware(cookie, _) and
|
||||
|
||||
not hasCsrfMiddleware(handler) and
|
||||
// Only warn for the last handler in a chain.
|
||||
handler.isLastHandler() and
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* @name Prototype pollution in utility function
|
||||
* @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
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
|
@ -30,9 +30,7 @@ predicate arePropertiesEnumerated(DataFlow::SourceNode node) {
|
|||
predicate isEnumeratedPropName(Node node) {
|
||||
node instanceof EnumeratedPropName
|
||||
or
|
||||
exists(Node pred |
|
||||
isEnumeratedPropName(pred)
|
||||
|
|
||||
exists(Node pred | isEnumeratedPropName(pred) |
|
||||
node = pred.getASuccessor()
|
||||
or
|
||||
argumentPassingStep(_, pred, _, node)
|
||||
|
@ -54,7 +52,6 @@ predicate isPotentiallyObjectPrototype(SourceNode node) {
|
|||
exists(Node base, Node key |
|
||||
dynamicPropReadStep(base, key, node) and
|
||||
isEnumeratedPropName(key) and
|
||||
|
||||
// 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).
|
||||
// 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())
|
||||
)
|
||||
or
|
||||
exists(Node use |
|
||||
isPotentiallyObjectPrototype(use.getALocalSource())
|
||||
|
|
||||
exists(Node use | isPotentiallyObjectPrototype(use.getALocalSource()) |
|
||||
argumentPassingStep(_, use, _, node)
|
||||
)
|
||||
}
|
||||
|
@ -87,7 +82,6 @@ predicate dynamicPropWrite(DataFlow::Node base, DataFlow::Node prop, DataFlow::N
|
|||
rhs = write.getRhs().flow() and
|
||||
not exists(prop.getStringValue()) and
|
||||
not arePropertiesEnumerated(base.getALocalSource()) and
|
||||
|
||||
// Prune writes that are unlikely to modify Object.prototype.
|
||||
// This is mainly for performance, but may block certain results due to
|
||||
// not tracking out of function returns and into callbacks.
|
||||
|
@ -435,9 +429,7 @@ private DataFlow::SourceNode getANodeLeadingToBase(DataFlow::TypeBackTracker t,
|
|||
isPrototypePollutingAssignment(base, _, _, _) and
|
||||
result = base.getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 |
|
||||
result = getANodeLeadingToBase(t2, base).backtrack(t2, t)
|
||||
)
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getANodeLeadingToBase(t2, base).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,8 +14,9 @@ import PortalExitSource
|
|||
import PortalEntrySink
|
||||
|
||||
from
|
||||
TaintTracking::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink, Portal p1,
|
||||
Portal p2, DataFlow::FlowLabel lbl1, DataFlow::FlowLabel lbl2, DataFlow::MidPathNode last
|
||||
TaintTracking::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink,
|
||||
Portal p1, Portal p2, DataFlow::FlowLabel lbl1, DataFlow::FlowLabel lbl2,
|
||||
DataFlow::MidPathNode last
|
||||
where
|
||||
cfg = source.getConfiguration() and
|
||||
last = source.getASuccessor*() and
|
||||
|
|
|
@ -11,7 +11,8 @@ import Configurations
|
|||
import PortalExitSource
|
||||
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
|
||||
where
|
||||
cfg = source.getConfiguration() and
|
||||
|
|
|
@ -11,7 +11,8 @@ import Configurations
|
|||
import PortalEntrySink
|
||||
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
|
||||
where
|
||||
cfg = source.getConfiguration() and
|
||||
|
|
|
@ -127,7 +127,8 @@ class AdditionalStepSpec extends ExternalData {
|
|||
exists(string config |
|
||||
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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import javascript
|
|||
import Expressions.ExprHasNoEffect
|
||||
|
||||
DataFlow::SourceNode callsArray(DataFlow::TypeBackTracker t, DataFlow::MethodCallNode call) {
|
||||
isIgnoredPureArrayCall(call) and
|
||||
isIgnoredPureArrayCall(call) and
|
||||
t.start() and
|
||||
result = call.getReceiver().getALocalSource()
|
||||
or
|
||||
|
@ -39,7 +39,7 @@ predicate isIgnoredPureArrayCall(DataFlow::MethodCallNode call) {
|
|||
}
|
||||
|
||||
from DataFlow::MethodCallNode call
|
||||
where
|
||||
where
|
||||
callsArray(call) instanceof DataFlow::ArrayCreationNode and
|
||||
not call.getReceiver().asExpr().(ArrayExpr).getSize() = 0
|
||||
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`. */
|
||||
ValueReturn getFirstExplicitReturn(Function f) {
|
||||
result = min(ValueReturn ret |
|
||||
result =
|
||||
min(ValueReturn ret |
|
||||
ret.getContainer() = f
|
||||
|
|
||||
ret order by ret.getLocation().getStartLine(), ret.getLocation().getStartColumn()
|
||||
|
|
|
@ -21,40 +21,41 @@ predicate returnsVoid(Function f) {
|
|||
}
|
||||
|
||||
predicate isStub(Function f) {
|
||||
f.getBody().(BlockStmt).getNumChild() = 0
|
||||
or
|
||||
f instanceof ExternalDecl
|
||||
f.getBody().(BlockStmt).getNumChild() = 0
|
||||
or
|
||||
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.
|
||||
*/
|
||||
predicate benignContext(Expr e) {
|
||||
inVoidContext(e) or
|
||||
|
||||
inVoidContext(e)
|
||||
or
|
||||
// A return statement is often used to just end the function.
|
||||
e = any(Function f).getBody()
|
||||
or
|
||||
e = any(ReturnStmt r).getExpr()
|
||||
or
|
||||
exists(ConditionalExpr cond | cond.getABranch() = e and benignContext(cond))
|
||||
or
|
||||
exists(LogicalBinaryExpr bin | bin.getAnOperand() = e and benignContext(bin))
|
||||
or
|
||||
exists(ConditionalExpr cond | cond.getABranch() = e and benignContext(cond))
|
||||
or
|
||||
exists(LogicalBinaryExpr bin | bin.getAnOperand() = e and benignContext(bin))
|
||||
or
|
||||
exists(Expr parent | parent.getUnderlyingValue() = e and benignContext(parent))
|
||||
or
|
||||
or
|
||||
any(VoidExpr voidExpr).getOperand() = e
|
||||
or
|
||||
// weeds out calls inside HTML-attributes.
|
||||
e.getParent().(ExprStmt).getParent() instanceof CodeInAttribute or
|
||||
e.getParent().(ExprStmt).getParent() instanceof CodeInAttribute
|
||||
or
|
||||
// and JSX-attributes.
|
||||
e = any(JSXAttribute attr).getValue() or
|
||||
|
||||
exists(AwaitExpr await | await.getOperand() = e and benignContext(await))
|
||||
e = any(JSXAttribute attr).getValue()
|
||||
or
|
||||
exists(AwaitExpr await | await.getOperand() = e and benignContext(await))
|
||||
or
|
||||
// Avoid double reporting with js/trivial-conditional
|
||||
isExplicitConditional(_, e)
|
||||
or
|
||||
or
|
||||
// Avoid double reporting with js/comparison-between-incompatible-types
|
||||
any(Comparison binOp).getAnOperand() = e
|
||||
or
|
||||
|
@ -62,12 +63,14 @@ predicate benignContext(Expr e) {
|
|||
any(PropAccess ac).getBase() = e
|
||||
or
|
||||
// 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
|
||||
// Avoid double reporting with js/call-to-non-callable
|
||||
any(InvokeExpr invoke).getCallee() = e
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -86,15 +89,13 @@ predicate alwaysThrows(Function f) {
|
|||
/**
|
||||
* Holds if the last statement in the function is flagged by the js/useless-expression query.
|
||||
*/
|
||||
predicate lastStatementHasNoEffect(Function f) {
|
||||
hasNoEffect(f.getExit().getAPredecessor())
|
||||
}
|
||||
predicate lastStatementHasNoEffect(Function f) { hasNoEffect(f.getExit().getAPredecessor()) }
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
not call.isIncomplete() and
|
||||
not call.isIncomplete() and
|
||||
func = call.getACallee() and
|
||||
forall(Function f | f = call.getACallee() |
|
||||
returnsVoid(f) and not isStub(f) and not alwaysThrows(f)
|
||||
|
@ -122,22 +123,20 @@ predicate hasNonVoidCallbackMethod(string name) {
|
|||
DataFlow::SourceNode array(DataFlow::TypeTracker t) {
|
||||
t.start() and result instanceof DataFlow::ArrayCreationNode
|
||||
or
|
||||
exists (DataFlow::TypeTracker t2 |
|
||||
result = array(t2).track(t2, t)
|
||||
)
|
||||
exists(DataFlow::TypeTracker t2 | result = array(t2).track(t2, t))
|
||||
}
|
||||
|
||||
DataFlow::SourceNode array() { result = array(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* Holds if `call` is an Array or Lodash method accepting a callback `func`,
|
||||
* where the `call` expects a callback that returns an expression,
|
||||
* but `func` does not return a value.
|
||||
* where the `call` expects a callback that returns an expression,
|
||||
* but `func` does not return a value.
|
||||
*/
|
||||
predicate voidArrayCallback(DataFlow::CallNode call, Function func) {
|
||||
hasNonVoidCallbackMethod(call.getCalleeName()) and
|
||||
exists(int index |
|
||||
index = min(int i | exists(call.getCallback(i))) and
|
||||
exists(int index |
|
||||
index = min(int i | exists(call.getCallback(i))) and
|
||||
func = call.getCallback(index).getFunction()
|
||||
) and
|
||||
returnsVoid(func) and
|
||||
|
@ -151,26 +150,23 @@ predicate voidArrayCallback(DataFlow::CallNode call, Function func) {
|
|||
}
|
||||
|
||||
predicate hasNonVoidReturnType(Function f) {
|
||||
exists(TypeAnnotation type | type = f.getReturnTypeAnnotation() |
|
||||
not type.isVoid()
|
||||
)
|
||||
exists(TypeAnnotation type | type = f.getReturnTypeAnnotation() | not type.isVoid())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides classes for working with various Deferred implementations.
|
||||
* It is a heuristic. The heuristic assume that a class is a promise defintion
|
||||
* Provides classes for working with various Deferred implementations.
|
||||
* 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.
|
||||
*
|
||||
* 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 {
|
||||
/**
|
||||
* An instance of a `Deferred` class.
|
||||
* An instance of a `Deferred` class.
|
||||
* For example the result from `new Deferred()` or `new $.Deferred()`.
|
||||
*/
|
||||
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" }
|
||||
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
|
@ -179,7 +175,7 @@ module Deferred {
|
|||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
|
||||
}
|
||||
|
||||
|
||||
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
|
@ -188,7 +184,7 @@ module Deferred {
|
|||
*/
|
||||
private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance {
|
||||
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"))
|
||||
}
|
||||
|
||||
|
@ -210,12 +206,14 @@ module Deferred {
|
|||
from DataFlow::CallNode call, Function func, string name, string msg
|
||||
where
|
||||
(
|
||||
callToVoidFunction(call, func) and
|
||||
callToVoidFunction(call, func) and
|
||||
msg = "the $@ does not return anything, yet the return value is used." and
|
||||
name = func.describe()
|
||||
or
|
||||
voidArrayCallback(call, func) and
|
||||
msg = "the $@ does not return anything, yet the return value from the call to " + call.getCalleeName() + " is used." and
|
||||
voidArrayCallback(call, func) and
|
||||
msg =
|
||||
"the $@ does not return anything, yet the return value from the call to " +
|
||||
call.getCalleeName() + " is used." and
|
||||
name = "callback function"
|
||||
) and
|
||||
not benignContext(call.getEnclosingExpr()) and
|
||||
|
@ -224,5 +222,4 @@ where
|
|||
not oneshotClosure(call) and
|
||||
not hasNonVoidReturnType(func) and
|
||||
not call.getEnclosingExpr() instanceof SuperCall
|
||||
select
|
||||
call, msg, func, name
|
||||
select call, msg, func, name
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Provides predicates for working with useless conditionals.
|
||||
*/
|
||||
/**
|
||||
* Provides predicates for working with useless conditionals.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
|
@ -18,4 +18,4 @@ predicate isExplicitConditional(ASTNode cond, Expr e) {
|
|||
or
|
||||
isExplicitConditional(_, cond) and
|
||||
e = cond.(Expr).getUnderlyingValue().(LogicalBinaryExpr).getAnOperand()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,7 +266,8 @@ predicate similarLines(File f, int line) {
|
|||
}
|
||||
|
||||
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
|
||||
toSum = b.sourceLines()
|
||||
|
|
||||
|
@ -278,7 +279,8 @@ pragma[noopt]
|
|||
private predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
coveredApprox =
|
||||
strictsum(int num |
|
||||
exists(int equivClass |
|
||||
similarLinesPerEquivalenceClass(equivClass, num, f) 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) {
|
||||
lines = strictsum(DuplicateBlock b, int toSum |
|
||||
lines =
|
||||
strictsum(DuplicateBlock b, int toSum |
|
||||
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and
|
||||
toSum = b.sourceLines()
|
||||
|
|
||||
|
@ -313,7 +316,8 @@ pragma[noopt]
|
|||
private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
coveredApprox =
|
||||
strictsum(int num |
|
||||
exists(int equivClass |
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
|
|
|
@ -48,7 +48,8 @@ class DefectResult extends int {
|
|||
|
||||
/** Gets the URL corresponding to the location of this query result. */
|
||||
string getURL() {
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn()
|
||||
+ ":" + getEndLine() + ":" + getEndColumn()
|
||||
result =
|
||||
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
|
||||
getEndLine() + ":" + getEndColumn()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,8 @@ class MetricResult extends int {
|
|||
|
||||
/** Gets the URL corresponding to the location of this query result. */
|
||||
string getURL() {
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn()
|
||||
+ ":" + getEndLine() + ":" + getEndColumn()
|
||||
result =
|
||||
"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
|
||||
* and a relevant function.
|
||||
*/
|
||||
predicate relevantCall(RelevantInvoke invoke, RelevantFunction f) {
|
||||
FlowSteps::calls(invoke, f)
|
||||
}
|
||||
predicate relevantCall(RelevantInvoke invoke, RelevantFunction f) { FlowSteps::calls(invoke, f) }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
class UnresolvableCall extends RelevantInvoke {
|
||||
UnresolvableCall() {
|
||||
not this instanceof ResolvableCall
|
||||
}
|
||||
UnresolvableCall() { not this instanceof ResolvableCall }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
import javascript
|
||||
import CallGraphQuality
|
||||
|
||||
Import unresolvableImport() {
|
||||
not exists(result.getImportedModule())
|
||||
}
|
||||
Import unresolvableImport() { not exists(result.getImportedModule()) }
|
||||
|
||||
select projectRoot(), count(unresolvableImport())
|
||||
|
|
|
@ -12,7 +12,8 @@ import javascript
|
|||
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
|
||||
|
||||
from File f, string cause
|
||||
where not extraction_data(f, _, _, _) and cause = "No extraction_data for this file"
|
||||
or
|
||||
not extraction_time(f, _,_, _) and cause = "No extraction_time for this file"
|
||||
select f, cause
|
||||
where
|
||||
not extraction_data(f, _, _, _) and cause = "No extraction_data for this file"
|
||||
or
|
||||
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
|
||||
|
||||
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) {
|
||||
defAt(i, v, d) and
|
||||
result = min(int j |
|
||||
result =
|
||||
min(int j |
|
||||
(defAt(j, v, _) or useAt(j, v, _) or j = length()) and
|
||||
j > i
|
||||
)
|
||||
|
|
|
@ -57,7 +57,8 @@ module CharacterEscapes {
|
|||
hasRawStringAndQuote(n, delim, rawStringNode, raw) and
|
||||
if rawStringNode instanceof RegExpLiteral
|
||||
then
|
||||
additionalEscapeChars = Sets::regexpMetaChars() + Sets::regexpAssertionChars() + Sets::regexpCharClassChars() +
|
||||
additionalEscapeChars =
|
||||
Sets::regexpMetaChars() + Sets::regexpAssertionChars() + Sets::regexpCharClassChars() +
|
||||
Sets::regexpBackreferenceChars()
|
||||
else additionalEscapeChars = "b"
|
||||
|
|
||||
|
|
|
@ -123,13 +123,14 @@ class ClassOrInterface extends @classorinterface, TypeParameterized {
|
|||
* Anonymous classes and interfaces do not have a canonical name.
|
||||
*/
|
||||
TypeName getTypeName() { result.getADefinition() = this }
|
||||
|
||||
|
||||
/**
|
||||
* Gets the ClassOrInterface corresponding to either a super type or an implemented interface.
|
||||
*/
|
||||
ClassOrInterface getASuperTypeDeclaration() {
|
||||
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
|
||||
if exists(getClassInitializedMember())
|
||||
then
|
||||
result = min(ClassInitializedMember m |
|
||||
m = getClassInitializedMember()
|
||||
|
|
||||
m order by m.getIndex()
|
||||
)
|
||||
result =
|
||||
min(ClassInitializedMember m | m = getClassInitializedMember() | m order by m.getIndex())
|
||||
else result = this
|
||||
}
|
||||
|
||||
|
@ -688,7 +686,8 @@ class MethodDeclaration extends MemberDeclaration {
|
|||
*/
|
||||
int getOverloadIndex() {
|
||||
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)
|
||||
|
|
||||
method order by i
|
||||
|
@ -696,7 +695,8 @@ class MethodDeclaration extends MemberDeclaration {
|
|||
)
|
||||
or
|
||||
exists(ClassDefinition type |
|
||||
this = rank[result + 1](ConstructorDeclaration ctor, int i |
|
||||
this =
|
||||
rank[result + 1](ConstructorDeclaration ctor, int i |
|
||||
ctor = type.getMemberByIndex(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. */
|
||||
int getOverloadIndex() {
|
||||
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 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. */
|
||||
int getOverloadIndex() {
|
||||
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 order by i
|
||||
|
|
|
@ -284,7 +284,8 @@ private SsaDefinition getAPseudoDefinitionInput(SsaDefinition nd) {
|
|||
*/
|
||||
private int nextDefAfter(BasicBlock bb, Variable v, int i, VarDef d) {
|
||||
bb.defAt(i, v, d) and
|
||||
result = min(int jj |
|
||||
result =
|
||||
min(int jj |
|
||||
(bb.defAt(jj, v, _) or jj = bb.length()) and
|
||||
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. */
|
||||
predicate isTypeOnly() {
|
||||
hasTypeKeyword(this)
|
||||
}
|
||||
predicate isTypeOnly() { hasTypeKeyword(this) }
|
||||
|
||||
override predicate isAmbient() {
|
||||
Stmt.super.isAmbient() or
|
||||
|
@ -268,9 +266,7 @@ abstract class ExportDeclaration extends Stmt, @exportdeclaration {
|
|||
abstract DataFlow::Node getSourceNode(string name);
|
||||
|
||||
/** Holds if is declared with the `type` keyword, so only types are exported. */
|
||||
predicate isTypeOnly() {
|
||||
hasTypeKeyword(this)
|
||||
}
|
||||
predicate isTypeOnly() { hasTypeKeyword(this) }
|
||||
|
||||
override predicate isAmbient() {
|
||||
Stmt.super.isAmbient() or
|
||||
|
@ -614,15 +610,10 @@ abstract class ReExportDeclaration extends ExportDeclaration {
|
|||
*
|
||||
* Gets the module from which this declaration re-exports.
|
||||
*/
|
||||
deprecated
|
||||
ES2015Module getImportedModule() {
|
||||
result = getReExportedModule()
|
||||
}
|
||||
deprecated ES2015Module getImportedModule() { result = getReExportedModule() }
|
||||
|
||||
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
|
||||
ES2015Module getReExportedES2015Module() {
|
||||
result = getReExportedModule()
|
||||
}
|
||||
ES2015Module getReExportedES2015Module() { result = getReExportedModule() }
|
||||
|
||||
/** Gets the module from which this declaration re-exports. */
|
||||
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.
|
||||
*/
|
||||
private Module resolveFromTypeRoot() {
|
||||
result.getFile() = min(TypeRootFolder typeRoot |
|
||||
result.getFile() =
|
||||
min(TypeRootFolder typeRoot |
|
||||
|
|
||||
typeRoot.getModuleFile(getImportedPath().getStringValue())
|
||||
order by
|
||||
|
|
|
@ -43,9 +43,8 @@ abstract class EmailSender extends DataFlow::SourceNode {
|
|||
*/
|
||||
private class NodemailerEmailSender extends EmailSender, DataFlow::MethodCallNode {
|
||||
NodemailerEmailSender() {
|
||||
this = DataFlow::moduleMember("nodemailer", "createTransport")
|
||||
.getACall()
|
||||
.getAMethodCall("sendMail")
|
||||
this =
|
||||
DataFlow::moduleMember("nodemailer", "createTransport").getACall().getAMethodCall("sendMail")
|
||||
}
|
||||
|
||||
override DataFlow::Node getPlainTextBody() { result = getOptionArgument(0, "text") }
|
||||
|
|
|
@ -253,12 +253,15 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
|
|||
DataFlow::Node getExceptionTarget() {
|
||||
if exists(this.getEnclosingStmt().getEnclosingTryCatchStmt())
|
||||
then
|
||||
result = DataFlow::parameterNode(this
|
||||
result =
|
||||
DataFlow::parameterNode(this
|
||||
.getEnclosingStmt()
|
||||
.getEnclosingTryCatchStmt()
|
||||
.getACatchClause()
|
||||
.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.
|
||||
*/
|
||||
File getJavaScriptFile(string stem) {
|
||||
result = min(int p, string ext |
|
||||
p = getFileExtensionPriority(ext)
|
||||
|
|
||||
getFile(stem, ext) order by p
|
||||
)
|
||||
result =
|
||||
min(int p, string ext | p = getFileExtensionPriority(ext) | getFile(stem, ext) order by p)
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
int getCyclomaticComplexity() {
|
||||
result = 2 +
|
||||
result =
|
||||
2 +
|
||||
sum(Expr nd |
|
||||
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.
|
||||
*/
|
||||
CallSignatureType getCallSignature() {
|
||||
declared_function_signature(this, result)
|
||||
}
|
||||
CallSignatureType getCallSignature() { declared_function_signature(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,8 +35,10 @@ class CodeGeneratorMarkerComment extends GeneratedCodeMarkerComment {
|
|||
*/
|
||||
private predicate codeGeneratorMarkerComment(Comment c, string tool) {
|
||||
exists(string toolPattern |
|
||||
toolPattern = "js_of_ocaml|CoffeeScript|LiveScript|dart2js|ANTLR|PEG\\.js|Opal|JSX|jison(?:-lex)?|(?:Microsoft \\(R\\) AutoRest Code Generator)|purs" and
|
||||
tool = c
|
||||
toolPattern =
|
||||
"js_of_ocaml|CoffeeScript|LiveScript|dart2js|ANTLR|PEG\\.js|Opal|JSX|jison(?:-lex)?|(?:Microsoft \\(R\\) AutoRest Code Generator)|purs" and
|
||||
tool =
|
||||
c
|
||||
.getText()
|
||||
.regexpCapture("(?s)[\\s*]*(?:parser |Code )?[gG]eneratedy? (?:from .*)?by (" +
|
||||
toolPattern + ")\\b.*", 1)
|
||||
|
|
|
@ -5,23 +5,18 @@
|
|||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
deprecated
|
||||
module GlobalAccessPath {
|
||||
deprecated module GlobalAccessPath {
|
||||
/**
|
||||
* DEPRECATED. Instead use `AccessPath::getAReferenceTo` with the result and parameter reversed.
|
||||
*/
|
||||
pragma[inline]
|
||||
string fromReference(DataFlow::Node node) {
|
||||
node = AccessPath::getAReferenceTo(result)
|
||||
}
|
||||
string fromReference(DataFlow::Node node) { node = AccessPath::getAReferenceTo(result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED. Instead use `AccessPath::getAnAssignmentTo` with the result and parameter reversed.
|
||||
*/
|
||||
pragma[inline]
|
||||
string fromRhs(DataFlow::Node node) {
|
||||
node = AccessPath::getAnAssignmentTo(result)
|
||||
}
|
||||
string fromRhs(DataFlow::Node node) { node = AccessPath::getAnAssignmentTo(result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `AccessPath::getAReferenceOrAssignmentTo`.
|
||||
|
@ -67,9 +62,7 @@ module AccessPath {
|
|||
}
|
||||
|
||||
/** Holds if this represents the root of the global access path. */
|
||||
predicate isGlobal() {
|
||||
this = DataFlow::globalAccessPathRootPseudoNode()
|
||||
}
|
||||
predicate isGlobal() { this = DataFlow::globalAccessPathRootPseudoNode() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,7 +205,8 @@ module AccessPath {
|
|||
* ```
|
||||
*/
|
||||
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]
|
||||
DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) {
|
||||
exists(DataFlow::SourceNode root, string accessPath |
|
||||
node = AccessPath::getAReferenceTo(root, accessPath) and
|
||||
exists(DataFlow::SourceNode root, string accessPath |
|
||||
node = AccessPath::getAReferenceTo(root, accessPath) and
|
||||
result = AccessPath::getAReferenceTo(root, accessPath)
|
||||
)
|
||||
or
|
||||
|
|
|
@ -49,9 +49,8 @@ private class DefaultHtmlSanitizerCall extends HtmlSanitizerCall {
|
|||
callee = LodashUnderscore::member("escape")
|
||||
or
|
||||
exists(string name | name = "encode" or name = "encodeNonUTF" |
|
||||
callee = DataFlow::moduleMember("html-entities", _)
|
||||
.getAnInstantiation()
|
||||
.getAPropertyRead(name) or
|
||||
callee =
|
||||
DataFlow::moduleMember("html-entities", _).getAnInstantiation().getAPropertyRead(name) or
|
||||
callee = DataFlow::moduleMember("html-entities", _).getAPropertyRead(name)
|
||||
)
|
||||
or
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Contains classes for recognizing array and string inclusion tests.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
|
||||
/**
|
||||
|
|
|
@ -142,5 +142,6 @@ class Locatable extends @locatable {
|
|||
*/
|
||||
private class FileLocatable extends File, Locatable {
|
||||
override Location getLocation() { result = File.super.getLocation() }
|
||||
|
||||
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.
|
||||
*/
|
||||
private Module resolveFromTypeRoot() {
|
||||
result.getFile() = min(TypeRootFolder typeRoot |
|
||||
result.getFile() =
|
||||
min(TypeRootFolder typeRoot |
|
||||
|
|
||||
typeRoot.getModuleFile(getImportedPath().getValue())
|
||||
order by
|
||||
|
|
|
@ -243,7 +243,8 @@ class Require extends CallExpr, Import {
|
|||
private File load(int priority) {
|
||||
exists(int r | getEnclosingModule().searchRoot(getImportedPath(), _, r) |
|
||||
result = loadAsFile(this, r, priority - prioritiesPerCandidate() * r) or
|
||||
result = loadAsDirectory(this, r,
|
||||
result =
|
||||
loadAsDirectory(this, r,
|
||||
priority - (prioritiesPerCandidate() * r + numberOfExtensions() + 1))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ File resolveMainModule(PackageJSON pkg, int priority) {
|
|||
not exists(main.resolve()) and
|
||||
not exists(main.getExtension()) and
|
||||
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)
|
||||
|
|
|
@ -128,17 +128,13 @@ private module PromiseFlow {
|
|||
/**
|
||||
* Gets the pseudo-field used to describe resolved values in a promise.
|
||||
*/
|
||||
string resolveField() {
|
||||
result = "$PromiseResolveField$"
|
||||
}
|
||||
string resolveField() { result = "$PromiseResolveField$" }
|
||||
|
||||
/**
|
||||
* Gets the pseudo-field used to describe rejected values in a promise.
|
||||
*/
|
||||
string rejectField() {
|
||||
result = "$PromiseRejectField$"
|
||||
}
|
||||
|
||||
string rejectField() { result = "$PromiseRejectField$" }
|
||||
|
||||
/**
|
||||
* A flow step describing a promise definition.
|
||||
*
|
||||
|
@ -146,9 +142,8 @@ private module PromiseFlow {
|
|||
*/
|
||||
class PromiseDefitionStep extends DataFlow::AdditionalFlowStep {
|
||||
PromiseDefinition promise;
|
||||
PromiseDefitionStep() {
|
||||
this = promise
|
||||
}
|
||||
|
||||
PromiseDefitionStep() { this = promise }
|
||||
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
prop = resolveField() and
|
||||
|
@ -170,15 +165,14 @@ private module PromiseFlow {
|
|||
succ = this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A flow step describing the a Promise.resolve (and similar) call.
|
||||
*/
|
||||
class CreationStep extends DataFlow::AdditionalFlowStep {
|
||||
PromiseCreationCall promise;
|
||||
CreationStep() {
|
||||
this = promise
|
||||
}
|
||||
|
||||
CreationStep() { this = promise }
|
||||
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
prop = resolveField() and
|
||||
|
@ -194,7 +188,6 @@ private module PromiseFlow {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A load step loading the pseudo-field describing that the promise is rejected.
|
||||
* The rejected value is thrown as a exception.
|
||||
|
@ -202,6 +195,7 @@ private module PromiseFlow {
|
|||
class AwaitStep extends DataFlow::AdditionalFlowStep {
|
||||
DataFlow::Node operand;
|
||||
AwaitExpr await;
|
||||
|
||||
AwaitStep() {
|
||||
this.getEnclosingExpr() = await and
|
||||
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.
|
||||
*/
|
||||
class ThenStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
ThenStep() {
|
||||
this.getMethodName() = "then"
|
||||
}
|
||||
ThenStep() { this.getMethodName() = "then" }
|
||||
|
||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
prop = resolveField() and
|
||||
|
@ -235,7 +227,7 @@ private module PromiseFlow {
|
|||
pred = getReceiver() and
|
||||
succ = getCallback(1).getParameter(0)
|
||||
}
|
||||
|
||||
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
not exists(this.getArgument(1)) and
|
||||
prop = rejectField() and
|
||||
|
@ -244,17 +236,17 @@ private module PromiseFlow {
|
|||
or
|
||||
// read the value of a resolved/rejected promise that is returned
|
||||
(prop = rejectField() or prop = resolveField()) and
|
||||
pred = getCallback([0..1]).getAReturn() and
|
||||
pred = getCallback([0 .. 1]).getAReturn() and
|
||||
succ = this
|
||||
}
|
||||
|
||||
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
prop = resolveField() and
|
||||
pred = getCallback([0..1]).getAReturn() and
|
||||
pred = getCallback([0 .. 1]).getAReturn() and
|
||||
succ = this
|
||||
or
|
||||
prop = rejectField() and
|
||||
pred = getCallback([0..1]).getExceptionalReturn() and
|
||||
pred = getCallback([0 .. 1]).getExceptionalReturn() and
|
||||
succ = this
|
||||
}
|
||||
}
|
||||
|
@ -263,9 +255,7 @@ private module PromiseFlow {
|
|||
* A flow step describing the data-flow related to the `.catch` method of a promise.
|
||||
*/
|
||||
class CatchStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
CatchStep() {
|
||||
this.getMethodName() = "catch"
|
||||
}
|
||||
CatchStep() { this.getMethodName() = "catch" }
|
||||
|
||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
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.
|
||||
*/
|
||||
class FinallyStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
FinallyStep() {
|
||||
this.getMethodName() = "finally"
|
||||
}
|
||||
FinallyStep() { this.getMethodName() = "finally" }
|
||||
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
(prop = resolveField() or prop = rejectField()) and
|
||||
|
@ -332,15 +320,13 @@ predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|||
// from `x` to `Promise.resolve(x)`
|
||||
pred = succ.(PromiseCreationCall).getValue()
|
||||
or
|
||||
exists(DataFlow::MethodCallNode thn |
|
||||
thn.getMethodName() = "then"
|
||||
|
|
||||
exists(DataFlow::MethodCallNode thn | thn.getMethodName() = "then" |
|
||||
// from `p` to `x` in `p.then(x => ...)`
|
||||
pred = thn.getReceiver() and
|
||||
succ = thn.getCallback(0).getParameter(0)
|
||||
or
|
||||
// from `v` to `p.then(x => return v)`
|
||||
pred = thn.getCallback([0..1]).getAReturn() and
|
||||
pred = thn.getCallback([0 .. 1]).getAReturn() and
|
||||
succ = thn
|
||||
)
|
||||
or
|
||||
|
@ -406,22 +392,19 @@ module Bluebird {
|
|||
|
||||
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 {
|
||||
AggregateBluebirdPromiseDefinition() {
|
||||
exists(string m | m = "all" or m = "race" or m = "map" |
|
||||
this = bluebird().getAMemberCall(m)
|
||||
)
|
||||
exists(string m | m = "all" or m = "race" or m = "map" | this = bluebird().getAMemberCall(m))
|
||||
}
|
||||
|
||||
override DataFlow::Node getValue() {
|
||||
result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -296,9 +296,7 @@ class RegExpSequence extends RegExpTerm, @regexp_seq {
|
|||
forall(RegExpTerm child | child = getAChild() | child.isNullable())
|
||||
}
|
||||
|
||||
override string getConstantValue() {
|
||||
result = getConstantValue(0)
|
||||
}
|
||||
override string getConstantValue() { result = getConstantValue(0) }
|
||||
|
||||
/**
|
||||
* 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
|
||||
result = ""
|
||||
or
|
||||
result = getChild(i).getConstantValue() + getConstantValue(i+1)
|
||||
result = getChild(i).getConstantValue() + getConstantValue(i + 1)
|
||||
}
|
||||
|
||||
/** Gets the element preceding `element` in this sequence. */
|
||||
RegExpTerm previousElement(RegExpTerm element) {
|
||||
element = nextElement(result)
|
||||
}
|
||||
/** Gets the element preceding `element` in this sequence. */
|
||||
RegExpTerm previousElement(RegExpTerm element) { element = nextElement(result) }
|
||||
|
||||
/** Gets the element following `element` in this sequence. */
|
||||
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) {
|
||||
exists(ExternalInstanceMemberDecl decl |
|
||||
|
@ -856,9 +852,7 @@ predicate isInterpretedAsRegExp(DataFlow::Node source) {
|
|||
exists(DataFlow::MethodCallNode mce, string methodName |
|
||||
mce.getReceiver().analyze().getAType() = TTString() and
|
||||
mce.getMethodName() = methodName and
|
||||
not exists(Function func |
|
||||
func = mce.getACallee()
|
||||
|
|
||||
not exists(Function func | func = mce.getACallee() |
|
||||
not isNativeStringMethod(func, methodName)
|
||||
)
|
||||
|
|
||||
|
|
|
@ -25,9 +25,8 @@ class FirstLineOf extends Locatable {
|
|||
if xl = startline
|
||||
then endcolumn = xc
|
||||
else
|
||||
endcolumn = max(int c |
|
||||
any(Location l).hasLocationInfo(filepath, startline, _, startline, c)
|
||||
)
|
||||
endcolumn =
|
||||
max(int c | any(Location l).hasLocationInfo(filepath, startline, _, startline, c))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -647,9 +647,7 @@ class SsaPhiNode extends SsaPseudoDefinition, TPhi {
|
|||
result = getDefReachingEndOf(bb, getSourceVariable())
|
||||
}
|
||||
|
||||
override SsaVariable getAnInput() {
|
||||
result = getInputFromBlock(_)
|
||||
}
|
||||
override SsaVariable getAnInput() { result = getInputFromBlock(_) }
|
||||
|
||||
override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
|
||||
bb = getBasicBlock() and v = getSourceVariable() and i = -1
|
||||
|
@ -676,9 +674,7 @@ class SsaPhiNode extends SsaPseudoDefinition, TPhi {
|
|||
* gets that variable.
|
||||
*/
|
||||
SsaVariable getRephinedVariable() {
|
||||
forex(SsaVariable input | input = getAnInput() |
|
||||
result = getRefinedVariable(input)
|
||||
)
|
||||
forex(SsaVariable input | input = getAnInput() | result = getRefinedVariable(input))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,8 @@ class DirectEval extends CallExpr {
|
|||
* Models `Array.prototype.map` and friends as partial invocations that pass their second
|
||||
* 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() {
|
||||
getNumArgument() = 2 and
|
||||
// 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() }
|
||||
|
||||
|
||||
/**
|
||||
* Gets the `try` statement with a catch block containing this statement without
|
||||
* crossing function boundaries or other `try ` statements with catch blocks.
|
||||
*/
|
||||
* Gets the `try` statement with a catch block containing this statement without
|
||||
* crossing function boundaries or other `try ` statements with catch blocks.
|
||||
*/
|
||||
TryStmt getEnclosingTryCatchStmt() {
|
||||
getParentStmt+() = result.getBody() 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()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -165,10 +165,16 @@ module StringOps {
|
|||
|
||||
StartsWith_Substring() {
|
||||
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
|
||||
(
|
||||
AccessPath::getAnAliasedSourceNode(substring).getAPropertyRead("length").flowsTo(call.getArgument(1))
|
||||
AccessPath::getAnAliasedSourceNode(substring)
|
||||
.getAPropertyRead("length")
|
||||
.flowsTo(call.getArgument(1))
|
||||
or
|
||||
substring.getStringValue().length() = call.getArgument(1).asExpr().getIntValue()
|
||||
)
|
||||
|
@ -502,7 +508,8 @@ module StringOps {
|
|||
result = getStringValue()
|
||||
or
|
||||
not exists(getStringValue()) and
|
||||
result = strictconcat(StringLiteralLike leaf |
|
||||
result =
|
||||
strictconcat(StringLiteralLike leaf |
|
||||
leaf = getALeaf().asExpr()
|
||||
|
|
||||
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`.
|
||||
*/
|
||||
predicate hasAssertsKeyword() {
|
||||
hasAssertsKeyword(this)
|
||||
}
|
||||
predicate hasAssertsKeyword() { hasAssertsKeyword(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -954,9 +952,7 @@ class PredicateTypeExpr extends @predicatetypeexpr, TypeExpr {
|
|||
* ```
|
||||
*/
|
||||
class IsTypeExpr extends PredicateTypeExpr {
|
||||
IsTypeExpr() {
|
||||
exists(getPredicateType())
|
||||
}
|
||||
IsTypeExpr() { exists(getPredicateType()) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2312,18 +2308,14 @@ class EnumLiteralType extends TypeReference {
|
|||
* A type that refers to a type alias.
|
||||
*/
|
||||
class TypeAliasReference extends TypeReference {
|
||||
TypeAliasReference() {
|
||||
type_alias(this, _)
|
||||
}
|
||||
TypeAliasReference() { type_alias(this, _) }
|
||||
|
||||
/**
|
||||
* Gets the type behind the type alias.
|
||||
*
|
||||
* For example, for `type B<T> = T[][]`, this maps the type `B<number>` to `number[][]`.
|
||||
*/
|
||||
Type getAliasedType() {
|
||||
type_alias(this, result)
|
||||
}
|
||||
Type getAliasedType() { 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[]`.
|
||||
*/
|
||||
PlainArrayType getRestParameterArrayType() {
|
||||
signature_rest_parameter(this, result)
|
||||
}
|
||||
PlainArrayType getRestParameterArrayType() { signature_rest_parameter(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -189,10 +189,8 @@ class Variable extends @variable, LexicalName {
|
|||
*/
|
||||
predicate isCaptured() {
|
||||
this instanceof GlobalVariable or
|
||||
getAnAccess().getContainer().getFunctionBoundary() != this
|
||||
.(LocalVariable)
|
||||
.getDeclaringContainer()
|
||||
.getFunctionBoundary()
|
||||
getAnAccess().getContainer().getFunctionBoundary() !=
|
||||
this.(LocalVariable).getDeclaringContainer().getFunctionBoundary()
|
||||
}
|
||||
|
||||
/** Holds if there is a declaration of this variable in `tl`. */
|
||||
|
@ -764,9 +762,7 @@ class Parameter extends BindingPattern {
|
|||
* function f(x?: number) {}
|
||||
* ```
|
||||
*/
|
||||
predicate isDeclaredOptional() {
|
||||
isOptionalParameterDeclaration(this)
|
||||
}
|
||||
predicate isDeclaredOptional() { isOptionalParameterDeclaration(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,9 +18,7 @@ import javascript
|
|||
private class BackwardExploringConfiguration extends DataFlow::Configuration {
|
||||
DataFlow::Configuration cfg;
|
||||
|
||||
BackwardExploringConfiguration() {
|
||||
this = cfg
|
||||
}
|
||||
BackwardExploringConfiguration() { this = cfg }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { any() }
|
||||
|
||||
|
|
|
@ -1035,7 +1035,9 @@ private predicate flowIntoHigherOrderCall(
|
|||
summary = oldSummary.append(PathSummary::call())
|
||||
)
|
||||
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
|
||||
cb = CallGraph::getABoundFunctionReference(f, boundArgs, false) 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)`.
|
||||
* Can be added to a `isBarrier` in a data-flow configuration to block flow through such checks.
|
||||
*/
|
||||
* 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.
|
||||
*/
|
||||
class VarAccessBarrier extends DataFlow::Node {
|
||||
VarAccessBarrier() {
|
||||
exists(ConditionGuardNode guard, SsaRefinementNode refinement |
|
||||
|
|
|
@ -87,10 +87,10 @@ module DataFlow {
|
|||
Expr asExpr() { this = TValueNode(result) }
|
||||
|
||||
/**
|
||||
* Gets the expression enclosing this data flow node.
|
||||
* In most cases the result is the same as `asExpr()`, however this method
|
||||
* additionally the `InvokeExpr` corresponding to reflective calls, and the `Parameter`
|
||||
* for a `DataFlow::ParameterNode`.
|
||||
* Gets the expression enclosing this data flow node.
|
||||
* In most cases the result is the same as `asExpr()`, however this method
|
||||
* additionally the `InvokeExpr` corresponding to reflective calls, and the `Parameter`
|
||||
* for a `DataFlow::ParameterNode`.
|
||||
*/
|
||||
Expr getEnclosingExpr() {
|
||||
result = asExpr() or
|
||||
|
@ -536,10 +536,10 @@ module DataFlow {
|
|||
*/
|
||||
pragma[noinline]
|
||||
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.
|
||||
*/
|
||||
*/
|
||||
predicate isPrivateField() {
|
||||
getPropertyName().charAt(0) = "#" and getPropertyNameExpr() instanceof Label
|
||||
}
|
||||
|
@ -860,7 +860,7 @@ module DataFlow {
|
|||
override Expr getPropertyNameExpr() { none() }
|
||||
|
||||
override string getPropertyName() {
|
||||
exists (int i |
|
||||
exists(int i |
|
||||
elt = pattern.getElement(i) and
|
||||
result = i.toString()
|
||||
)
|
||||
|
@ -872,7 +872,6 @@ module DataFlow {
|
|||
*/
|
||||
private class ImportSpecifierAsPropRead extends PropRead, ValueNode {
|
||||
override ImportSpecifier astNode;
|
||||
|
||||
ImportDeclaration imprt;
|
||||
|
||||
ImportSpecifierAsPropRead() {
|
||||
|
@ -894,9 +893,7 @@ module DataFlow {
|
|||
private class ForOfLvalueAsPropRead extends PropRead {
|
||||
ForOfStmt stmt;
|
||||
|
||||
ForOfLvalueAsPropRead() {
|
||||
this = lvalueNode(stmt.getLValue())
|
||||
}
|
||||
ForOfLvalueAsPropRead() { this = lvalueNode(stmt.getLValue()) }
|
||||
|
||||
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.
|
||||
*/
|
||||
DataFlow::Node globalAccessPathRootPseudoNode() { result instanceof TGlobalAccessPathRoot }
|
||||
|
||||
|
||||
/**
|
||||
* Gets a data flow node representing the underlying call performed by the given
|
||||
* call to `Function.prototype.call` or `Function.prototype.apply`.
|
||||
|
|
|
@ -16,9 +16,7 @@ import javascript
|
|||
private class ForwardExploringConfiguration extends DataFlow::Configuration {
|
||||
DataFlow::Configuration cfg;
|
||||
|
||||
ForwardExploringConfiguration() {
|
||||
this = cfg
|
||||
}
|
||||
ForwardExploringConfiguration() { this = cfg }
|
||||
|
||||
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.
|
||||
*/
|
||||
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"
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ private import semmle.javascript.dependencies.Dependencies
|
|||
private import internal.CallGraphs
|
||||
|
||||
/**
|
||||
* A data flow node corresponding to an expression.
|
||||
* A data flow node corresponding to an expression.
|
||||
*
|
||||
* Examples:
|
||||
* ```js
|
||||
|
@ -147,7 +147,8 @@ class InvokeNode extends DataFlow::SourceNode {
|
|||
*/
|
||||
ParameterNode getABoundCallbackParameter(int callback, int param) {
|
||||
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() }
|
||||
|
||||
/** Gets the flags of this regular expression literal. */
|
||||
string getFlags() {
|
||||
result = astNode.getFlags()
|
||||
}
|
||||
string 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. */
|
||||
int getSize() {
|
||||
if getNumArgument() = 1 then
|
||||
result = getArgument(0).getIntValue()
|
||||
else
|
||||
result = count(getAnElement())
|
||||
if getNumArgument() = 1
|
||||
then result = getArgument(0).getIntValue()
|
||||
else result = count(getAnElement())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -596,7 +594,7 @@ class ArrayConstructorInvokeNode extends DataFlow::InvokeNode {
|
|||
* Examples:
|
||||
* ```js
|
||||
* ['apple', 'orange'];
|
||||
* Array('apple', 'orange')
|
||||
* Array('apple', 'orange')
|
||||
* new Array('apple', 'orange')
|
||||
* 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.
|
||||
*/
|
||||
DataFlow::Node getBoundReceiver(DataFlow::Node callback) { result = range.getBoundReceiver(callback) }
|
||||
DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
|
||||
result = range.getBoundReceiver(callback)
|
||||
}
|
||||
}
|
||||
|
||||
module PartialInvokeNode {
|
||||
|
@ -1247,7 +1247,9 @@ module PartialInvokeNode {
|
|||
/**
|
||||
* 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.
|
||||
|
@ -1259,8 +1261,7 @@ module PartialInvokeNode {
|
|||
*
|
||||
* Gets the node holding the receiver to be passed to the bound function, if specified.
|
||||
*/
|
||||
deprecated
|
||||
DataFlow::Node getBoundReceiver() { none() }
|
||||
deprecated DataFlow::Node getBoundReceiver() { none() }
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
BindPartialCall() {
|
||||
getMethodName() = "bind" and
|
||||
|
||||
// Avoid overlap with angular.bind and goog.bind
|
||||
not this = AngularJS::angular().getAMethodCall() and
|
||||
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 {
|
||||
LodashPartialCall() { this = LodashUnderscore::member("partial").getACall() }
|
||||
|
||||
|
@ -1323,9 +1323,7 @@ module PartialInvokeNode {
|
|||
private class RamdaPartialCall extends PartialInvokeNode::Range, DataFlow::CallNode {
|
||||
RamdaPartialCall() { this = DataFlow::moduleMember("ramda", "partial").getACall() }
|
||||
|
||||
private DataFlow::ArrayCreationNode getArgumentsArray() {
|
||||
result.flowsTo(getArgument(1))
|
||||
}
|
||||
private DataFlow::ArrayCreationNode getArgumentsArray() { result.flowsTo(getArgument(1)) }
|
||||
|
||||
override predicate isPartialArgument(DataFlow::Node callback, DataFlow::Node argument, int index) {
|
||||
callback = getArgument(0) and
|
||||
|
@ -1376,17 +1374,13 @@ deprecated class AdditionalPartialInvokeNode = PartialInvokeNode::Range;
|
|||
* ```
|
||||
*/
|
||||
class RegExpConstructorInvokeNode extends DataFlow::InvokeNode {
|
||||
RegExpConstructorInvokeNode() {
|
||||
this = DataFlow::globalVarRef("RegExp").getAnInvocation()
|
||||
}
|
||||
RegExpConstructorInvokeNode() { this = DataFlow::globalVarRef("RegExp").getAnInvocation() }
|
||||
|
||||
/**
|
||||
* Gets the AST of the regular expression created here, provided that the
|
||||
* first argument is a string literal.
|
||||
*/
|
||||
RegExpTerm getRoot() {
|
||||
result = getArgument(0).asExpr().(StringLiteral).asRegExp()
|
||||
}
|
||||
RegExpTerm getRoot() { result = getArgument(0).asExpr().(StringLiteral).asRegExp() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
result = this
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = getAReference(t2).track(t2, t)
|
||||
)
|
||||
exists(DataFlow::TypeTracker t2 | result = getAReference(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to this regular expression. */
|
||||
DataFlow::SourceNode getAReference() {
|
||||
result = getAReference(DataFlow::TypeTracker::end())
|
||||
}
|
||||
DataFlow::SourceNode getAReference() { result = getAReference(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
|
|
@ -341,7 +341,7 @@ module TaintTracking {
|
|||
pred = call.getAnArgument() and
|
||||
succ = call
|
||||
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
|
||||
succ = call
|
||||
or
|
||||
|
@ -574,7 +574,6 @@ module TaintTracking {
|
|||
succ = this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from calling `String.prototype.match()`.
|
||||
|
@ -583,7 +582,7 @@ module TaintTracking {
|
|||
StringMatchTaintStep() {
|
||||
this.getMethodName() = "match" 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) {
|
||||
|
@ -696,9 +695,7 @@ module TaintTracking {
|
|||
* A taint step through the Node.JS function `util.inspect(..)`.
|
||||
*/
|
||||
class UtilInspectTaintStep extends AdditionalTaintStep, DataFlow::InvokeNode {
|
||||
UtilInspectTaintStep() {
|
||||
this = DataFlow::moduleImport("util").getAMemberCall("inspect")
|
||||
}
|
||||
UtilInspectTaintStep() { this = DataFlow::moduleImport("util").getAMemberCall("inspect") }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
|
@ -719,14 +716,16 @@ module TaintTracking {
|
|||
mce = astNode and mce.calls(base, m) and firstArg = mce.getArgument(0)
|
||||
|
|
||||
// /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
|
||||
firstArg = expr
|
||||
or
|
||||
// u.match(/re/) or u.match("re")
|
||||
base = expr and
|
||||
m = "match" and
|
||||
RegExp::isGenericRegExpSanitizer(RegExp::getRegExpFromNode(firstArg.flow()), sanitizedOutcome)
|
||||
RegExp::isGenericRegExpSanitizer(RegExp::getRegExpFromNode(firstArg.flow()),
|
||||
sanitizedOutcome)
|
||||
)
|
||||
or
|
||||
// m = /re/.exec(u) and similar
|
||||
|
|
|
@ -137,10 +137,11 @@ module StepSummary {
|
|||
summary = LoadStep(prop)
|
||||
)
|
||||
) and
|
||||
if param = fun.getAParameter() then (
|
||||
if param = fun.getAParameter()
|
||||
then
|
||||
// Step from argument to call site.
|
||||
argumentPassing(succ, pred, fun.getFunction(), param)
|
||||
) else (
|
||||
else (
|
||||
// Step from captured parameter to local call sites
|
||||
pred = param and
|
||||
succ = fun.getAnInvocation()
|
||||
|
|
|
@ -128,8 +128,9 @@ class AccessPath extends TAccessPath {
|
|||
exists(AccessPath base, PropertyName name, string rest |
|
||||
rest = "." + any(string s | name = StaticPropertyName(s))
|
||||
or
|
||||
rest = "[" +
|
||||
any(SsaVariable var | name = DynamicPropertyName(var)).getSourceVariable().getName() + "]"
|
||||
rest =
|
||||
"[" + any(SsaVariable var | name = DynamicPropertyName(var)).getSourceVariable().getName() +
|
||||
"]"
|
||||
|
|
||||
result = base.toString() + rest and
|
||||
this = MkAccessStep(base, name)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Internal predicates for computing the call graph.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
|
||||
cached
|
||||
|
@ -24,21 +25,22 @@ module CallGraph {
|
|||
* from underlying class tracking if the function came from a class or instance.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private
|
||||
DataFlow::SourceNode getAFunctionReference(DataFlow::FunctionNode function, int imprecision, DataFlow::TypeTracker t) {
|
||||
private DataFlow::SourceNode getAFunctionReference(
|
||||
DataFlow::FunctionNode function, int imprecision, DataFlow::TypeTracker t
|
||||
) {
|
||||
t.start() and
|
||||
exists(Function fun |
|
||||
fun = function.getFunction() and
|
||||
fun = getAFunctionValue(result)
|
||||
|
|
||||
if isIndefiniteGlobal(result) then
|
||||
if isIndefiniteGlobal(result)
|
||||
then
|
||||
fun.getFile() = result.getFile() and imprecision = 0
|
||||
or
|
||||
fun.inExternsFile() and imprecision = 1
|
||||
or
|
||||
imprecision = 2
|
||||
else
|
||||
imprecision = 0
|
||||
else imprecision = 0
|
||||
)
|
||||
or
|
||||
imprecision = 0 and
|
||||
|
@ -73,8 +75,9 @@ module CallGraph {
|
|||
* with `function` as the underlying function.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private
|
||||
DataFlow::SourceNode getABoundFunctionReferenceAux(DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t) {
|
||||
private DataFlow::SourceNode getABoundFunctionReferenceAux(
|
||||
DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t
|
||||
) {
|
||||
exists(DataFlow::PartialInvokeNode partial, DataFlow::Node callback |
|
||||
result = partial.getBoundFunction(callback, boundArgs) and
|
||||
getAFunctionReference(function, 0, t.continue()).flowsTo(callback)
|
||||
|
@ -87,8 +90,10 @@ module CallGraph {
|
|||
}
|
||||
|
||||
pragma[noinline]
|
||||
private
|
||||
DataFlow::SourceNode getABoundFunctionReferenceAux(DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t, DataFlow::StepSummary summary) {
|
||||
private DataFlow::SourceNode getABoundFunctionReferenceAux(
|
||||
DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t,
|
||||
DataFlow::StepSummary summary
|
||||
) {
|
||||
exists(DataFlow::SourceNode prev |
|
||||
prev = getABoundFunctionReferenceAux(function, boundArgs, t) and
|
||||
DataFlow::StepSummary::step(prev, result, summary)
|
||||
|
@ -100,7 +105,9 @@ module CallGraph {
|
|||
* with `function` as the underlying function.
|
||||
*/
|
||||
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 |
|
||||
result = getABoundFunctionReferenceAux(function, boundArgs, t) and
|
||||
t.end() and
|
||||
|
@ -116,8 +123,9 @@ module CallGraph {
|
|||
* This predicate may be overridden to customize the class hierarchy analysis.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private
|
||||
DataFlow::PropRead getAnInstanceMemberAccess(DataFlow::ClassNode cls, string name, DataFlow::TypeTracker t) {
|
||||
private DataFlow::PropRead getAnInstanceMemberAccess(
|
||||
DataFlow::ClassNode cls, string name, DataFlow::TypeTracker t
|
||||
) {
|
||||
result = cls.getAnInstanceReference(t.continue()).getAPropertyRead(name)
|
||||
or
|
||||
exists(DataFlow::ClassNode subclass |
|
||||
|
|
|
@ -440,8 +440,8 @@ class PathSummary extends TPathSummary {
|
|||
exists(Boolean hasReturn2, Boolean hasCall2, FlowLabel end2 |
|
||||
that = MkPathSummary(hasReturn2, hasCall2, end, end2)
|
||||
|
|
||||
result = MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start,
|
||||
end2) and
|
||||
result =
|
||||
MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start, end2) and
|
||||
// avoid constructing invalid paths
|
||||
not (hasCall = true and hasReturn2 = true)
|
||||
)
|
||||
|
@ -456,8 +456,8 @@ class PathSummary extends TPathSummary {
|
|||
exists(Boolean hasReturn2, Boolean hasCall2 |
|
||||
that = MkPathSummary(hasReturn2, hasCall2, FlowLabel::data(), FlowLabel::data())
|
||||
|
|
||||
result = MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start,
|
||||
end) and
|
||||
result =
|
||||
MkPathSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2), start, end) and
|
||||
// avoid constructing invalid paths
|
||||
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 hasCall = true then withCall = "with" else withCall = "without")
|
||||
|
|
||||
result = "path " + withReturn + " return steps and " + withCall + " call steps " +
|
||||
"transforming " + start + " into " + end
|
||||
result =
|
||||
"path " + withReturn + " return steps and " + withCall + " call steps " + "transforming " +
|
||||
start + " into " + end
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,8 +145,9 @@ abstract class FrameworkLibraryWithGenericURL extends FrameworkLibraryWithURLReg
|
|||
override string getAURLRegex() {
|
||||
exists(string id | id = getId() or id = getAnAlias() |
|
||||
result = ".*(?:^|/)" + id + "-(" + semverRegex() + ")" + variantRegex() + "\\.js" or
|
||||
result = ".*/(?:\\w+@)?(" + semverRegex() + ")/(?:(?:dist|js|" + id + ")/)?" + id +
|
||||
variantRegex() + "\\.js"
|
||||
result =
|
||||
".*/(?:\\w+@)?(" + semverRegex() + ")/(?:(?:dist|js|" + id + ")/)?" + id + variantRegex() +
|
||||
"\\.js"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +159,8 @@ abstract class FrameworkLibraryWithGenericURL extends FrameworkLibraryWithURLReg
|
|||
* We ignore these when identifying frameworks.
|
||||
*/
|
||||
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))*"
|
||||
}
|
||||
|
||||
|
@ -235,8 +237,8 @@ private predicate jQueryMarkerComment(Comment c, TopLevel tl, string version) {
|
|||
tl = c.getTopLevel() and
|
||||
exists(string txt | txt = c.getText() |
|
||||
// more recent versions use this format
|
||||
version = txt
|
||||
.regexpCapture("(?s).*jQuery (?:JavaScript Library )?v(" + versionRegex() + ").*", 1)
|
||||
version =
|
||||
txt.regexpCapture("(?s).*jQuery (?:JavaScript Library )?v(" + versionRegex() + ").*", 1)
|
||||
or
|
||||
// earlier versions used this format
|
||||
version = txt.regexpCapture("(?s).*jQuery (" + versionRegex() + ") - New Wave Javascript.*", 1)
|
||||
|
@ -502,7 +504,8 @@ private class Lodash extends FrameworkLibraryWithGenericURL, FrameworkLibraryWit
|
|||
Lodash() { this = "lodash" }
|
||||
|
||||
override string getAMarkerCommentRegex() {
|
||||
result = "(?s).* (?:lod|Lo-D)ash (<VERSION>)" + "(?: \\(Custom Build\\))? " +
|
||||
result =
|
||||
"(?s).* (?:lod|Lo-D)ash (<VERSION>)" + "(?: \\(Custom Build\\))? " +
|
||||
"<https?://lodash.com/>.*"
|
||||
}
|
||||
|
||||
|
@ -842,7 +845,8 @@ private class ApplicationInsightsInstance extends FrameworkLibraryInstance {
|
|||
string version;
|
||||
|
||||
ApplicationInsightsInstance() {
|
||||
version = this
|
||||
version =
|
||||
this
|
||||
.(TopLevel)
|
||||
.getFile()
|
||||
.getAbsolutePath()
|
||||
|
|
|
@ -832,7 +832,8 @@ private newtype TAngularScope =
|
|||
} or
|
||||
MkIsolateScope(CustomDirective dir) { dir.hasIsolateScope() } or
|
||||
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() {
|
||||
this = directive.getMember("template").asExpr() and
|
||||
source = this
|
||||
source =
|
||||
this
|
||||
.(ConstantString)
|
||||
.getStringValue()
|
||||
.regexpFind(getInterpolatedExpressionPattern(), _, offset)
|
||||
|
@ -211,11 +212,8 @@ abstract class NgToken extends TNgToken {
|
|||
*/
|
||||
private int getIndex() {
|
||||
exists(NgSource src, int start | this.at(src, start) |
|
||||
start = rank[result + 1](NgToken someToken, int someStart |
|
||||
someToken.at(src, someStart)
|
||||
|
|
||||
someStart
|
||||
)
|
||||
start =
|
||||
rank[result + 1](NgToken someToken, int someStart | someToken.at(src, someStart) | someStart)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -278,7 +276,8 @@ private module Lexer {
|
|||
NgOpTokenType() { this = "NgOpTokenType" }
|
||||
|
||||
override string getPattern() {
|
||||
result = concat(string op |
|
||||
result =
|
||||
concat(string op |
|
||||
op = "===" or
|
||||
op = "!==" or
|
||||
op = "==" or
|
||||
|
@ -383,7 +382,8 @@ abstract class NgAstNode extends TNode {
|
|||
*/
|
||||
language[monotonicAggregates]
|
||||
string ppChildren() {
|
||||
result = concat(NgAstNode child, int idx |
|
||||
result =
|
||||
concat(NgAstNode child, int idx |
|
||||
child = getChild(idx) and
|
||||
not child instanceof Empty
|
||||
|
|
||||
|
|
|
@ -553,10 +553,10 @@ module ClientRequest {
|
|||
/**
|
||||
* Gets a reference to an instance of `chrome-remote-interface`.
|
||||
*
|
||||
* 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
|
||||
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
|
||||
* 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
|
||||
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
|
||||
*/
|
||||
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t, boolean isPromise) {
|
||||
t.start() and
|
||||
|
|
|
@ -609,7 +609,8 @@ private module Forge {
|
|||
NonKeyCipher() {
|
||||
exists(string algorithmName | algorithm.matchesName(algorithmName) |
|
||||
// require("forge").md.md5.create().update('The quick brown fox jumps over the lazy dog');
|
||||
this = getAnImportNode()
|
||||
this =
|
||||
getAnImportNode()
|
||||
.getAPropertyRead("md")
|
||||
.getAPropertyRead(algorithmName)
|
||||
.getAMemberCall("create")
|
||||
|
|
|
@ -138,9 +138,7 @@ module Electron {
|
|||
result = this.getABoundCallbackParameter(1, 0).getAPropertyWrite("returnValue").getRhs()
|
||||
}
|
||||
|
||||
override IPCDispatch getAReturnDispatch() {
|
||||
result.getCalleeName() = "sendSync"
|
||||
}
|
||||
override IPCDispatch getAReturnDispatch() { 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() {
|
||||
this.getEmitter() instanceof RendererProcess and
|
||||
|
|
|
@ -129,9 +129,7 @@ module EventRegistration {
|
|||
* argument is the event handler callback.
|
||||
*/
|
||||
abstract class DefaultEventRegistration extends Range, DataFlow::InvokeNode {
|
||||
override string getChannel() {
|
||||
this.getArgument(0).mayHaveStringValue(result)
|
||||
}
|
||||
override string getChannel() { this.getArgument(0).mayHaveStringValue(result) }
|
||||
|
||||
override DataFlow::Node getReceivedItem(int i) {
|
||||
result = this.getABoundCallbackParameter(1, i)
|
||||
|
@ -187,13 +185,9 @@ module EventDispatch {
|
|||
* is the `i`th item sent to the event handler.
|
||||
*/
|
||||
abstract class DefaultEventDispatch extends Range, DataFlow::InvokeNode {
|
||||
override string getChannel() {
|
||||
this.getArgument(0).mayHaveStringValue(result)
|
||||
}
|
||||
override string getChannel() { this.getArgument(0).mayHaveStringValue(result) }
|
||||
|
||||
override DataFlow::Node getSentItem(int i) {
|
||||
result = this.getArgument(i + 1)
|
||||
}
|
||||
override DataFlow::Node getSentItem(int i) { result = this.getArgument(i + 1) }
|
||||
|
||||
override EventRegistration::Range getAReceiver() { this.getEmitter() = result.getEmitter() }
|
||||
}
|
||||
|
@ -223,4 +217,3 @@ private class EventEmitterTaintStep extends DataFlow::AdditionalFlowStep {
|
|||
succ = dispatch
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -147,36 +147,34 @@ module Express {
|
|||
this.getRequestMethod() = that.getRequestMethod()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A call that sets up a Passport router that includes the request object.
|
||||
*/
|
||||
private class PassportRouteSetup extends HTTP::Servers::StandardRouteSetup, CallExpr {
|
||||
DataFlow::ModuleImportNode importNode;
|
||||
DataFlow::FunctionNode callback;
|
||||
|
||||
|
||||
// looks for this pattern: passport.use(new Strategy({passReqToCallback: true}, callback))
|
||||
PassportRouteSetup() {
|
||||
importNode = DataFlow::moduleImport("passport") and
|
||||
this = importNode.getAMemberCall("use").asExpr() and
|
||||
exists(DataFlow::NewNode strategy |
|
||||
strategy.flowsToExpr(this.getArgument(0)) and
|
||||
strategy.getNumArgument() = 2 and
|
||||
// new Strategy({passReqToCallback: true}, ...)
|
||||
strategy.getOptionArgument(0, "passReqToCallback").mayHaveBooleanValue(true) and
|
||||
callback.flowsTo(strategy.getArgument(1))
|
||||
exists(DataFlow::NewNode strategy |
|
||||
strategy.flowsToExpr(this.getArgument(0)) and
|
||||
strategy.getNumArgument() = 2 and
|
||||
// new Strategy({passReqToCallback: true}, ...)
|
||||
strategy.getOptionArgument(0, "passReqToCallback").mayHaveBooleanValue(true) and
|
||||
callback.flowsTo(strategy.getArgument(1))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override Expr getServer() { result = importNode.asExpr() }
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() {
|
||||
result = callback
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() { result = callback }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The callback given to passport in PassportRouteSetup.
|
||||
* The callback given to passport in PassportRouteSetup.
|
||||
*/
|
||||
private class PassportRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler,
|
||||
DataFlow::ValueNode {
|
||||
|
@ -185,7 +183,7 @@ module Express {
|
|||
PassportRouteHandler() { this = any(PassportRouteSetup setup).getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and
|
||||
kind = "request" and
|
||||
result = astNode.getParameter(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ private class WalkFileNameSource extends FileNameSource {
|
|||
WalkFileNameSource() {
|
||||
// `stats.name` in `require('walk').walk(_).on(_, (_, stats) => stats.name)`
|
||||
exists(DataFlow::FunctionNode callback |
|
||||
callback = DataFlow::moduleMember("walk", "walk")
|
||||
callback =
|
||||
DataFlow::moduleMember("walk", "walk")
|
||||
.getACall()
|
||||
.getAMethodCall(EventEmitter::on())
|
||||
.getCallback(1)
|
||||
|
@ -66,7 +67,8 @@ private class GlobbyFileNameSource extends FileNameSource {
|
|||
this = DataFlow::moduleMember(moduleName, "sync").getACall()
|
||||
or
|
||||
// `files` in `require('globby')(_).then(files => ...)`
|
||||
this = DataFlow::moduleImport(moduleName)
|
||||
this =
|
||||
DataFlow::moduleImport(moduleName)
|
||||
.getACall()
|
||||
.getAMethodCall("then")
|
||||
.getCallback(0)
|
||||
|
@ -95,7 +97,8 @@ private class FastGlobFileNameSource extends FileNameSource {
|
|||
)
|
||||
or
|
||||
// `file` in `require('fast-glob').stream(_).on(_, file => ...)`
|
||||
this = DataFlow::moduleMember(moduleName, "stream")
|
||||
this =
|
||||
DataFlow::moduleMember(moduleName, "stream")
|
||||
.getACall()
|
||||
.getAMethodCall(EventEmitter::on())
|
||||
.getCallback(1)
|
||||
|
|
|
@ -123,8 +123,7 @@ module HTTP {
|
|||
*
|
||||
* Gets the string `http` or `https`.
|
||||
*/
|
||||
deprecated
|
||||
string httpOrHttps() { result = "http" or result = "https" }
|
||||
deprecated string httpOrHttps() { result = "http" or result = "https" }
|
||||
|
||||
/**
|
||||
* An expression whose value is sent as (part of) the body of an HTTP response.
|
||||
|
|
|
@ -6,7 +6,7 @@ import javascript
|
|||
|
||||
module Handlebars {
|
||||
/**
|
||||
* A reference to the Handlebars library.
|
||||
* A reference to the Handlebars library.
|
||||
*/
|
||||
class Handlebars extends DataFlow::SourceNode {
|
||||
Handlebars() {
|
||||
|
|
|
@ -11,7 +11,7 @@ module Koa {
|
|||
*/
|
||||
class AppDefinition extends HTTP::Servers::StandardServerDefinition, InvokeExpr {
|
||||
AppDefinition() {
|
||||
// `app = new Koa()` / `app = Koa()`
|
||||
// `app = new Koa()` / `app = Koa()`
|
||||
this = DataFlow::moduleImport("koa").getAnInvocation().asExpr()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,9 +58,11 @@ private module Console {
|
|||
}
|
||||
|
||||
override DataFlow::Node getAMessageComponent() {
|
||||
if name = "assert"
|
||||
then result = getArgument([1 .. getNumArgument()])
|
||||
else result = getAnArgument()
|
||||
(
|
||||
if name = "assert"
|
||||
then result = getArgument([1 .. getNumArgument()])
|
||||
else result = getAnArgument()
|
||||
)
|
||||
or
|
||||
result = getASpreadArgument()
|
||||
}
|
||||
|
@ -68,9 +70,7 @@ private module Console {
|
|||
/**
|
||||
* Gets the name of the console logging method, e.g. "log", "error", "assert", etc.
|
||||
*/
|
||||
string getName() {
|
||||
result = name
|
||||
}
|
||||
string getName() { result = name }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,8 @@ private module Winston {
|
|||
*/
|
||||
class WinstonLoggerCall extends LoggerCall, DataFlow::MethodCallNode {
|
||||
WinstonLoggerCall() {
|
||||
this = DataFlow::moduleMember("winston", "createLogger")
|
||||
this =
|
||||
DataFlow::moduleMember("winston", "createLogger")
|
||||
.getACall()
|
||||
.getAMethodCall(getAStandardLoggerMethodName())
|
||||
}
|
||||
|
@ -121,7 +122,8 @@ private module log4js {
|
|||
*/
|
||||
class Log4jsLoggerCall extends LoggerCall {
|
||||
Log4jsLoggerCall() {
|
||||
this = DataFlow::moduleMember("log4js", "getLogger")
|
||||
this =
|
||||
DataFlow::moduleMember("log4js", "getLogger")
|
||||
.getACall()
|
||||
.getAMethodCall(getAStandardLoggerMethodName())
|
||||
}
|
||||
|
|
|
@ -84,9 +84,7 @@ private module MongoDB {
|
|||
* of `mongodb.Collection`.
|
||||
*/
|
||||
private class CollectionFromType extends Collection {
|
||||
CollectionFromType() {
|
||||
hasUnderlyingType("mongodb", "Collection")
|
||||
}
|
||||
CollectionFromType() { hasUnderlyingType("mongodb", "Collection") }
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MongoDB collection. */
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче