Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/codeql/support/reusables/frameworks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ JavaScript and TypeScript built-in support
mssql, Database
mysql, Database
node, Runtime environment
nest.js, Server
postgres, Database
ramda, Utility library
react, HTML framework
Expand Down
3 changes: 3 additions & 0 deletions javascript/change-notes/2021-04-15-fs-promises.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lgtm,codescanning
* Support for `fs.promises` has been added, leading to more results for security queries
related to file system access.
3 changes: 3 additions & 0 deletions javascript/change-notes/2021-04-15-nestjs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lgtm,codescanning
* Support for Nest.js has been added. The security queries now recognize sources and sinks
specific to the Nest.js framework.
2 changes: 2 additions & 0 deletions javascript/ql/src/javascript.qll
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import semmle.javascript.frameworks.Babel
import semmle.javascript.frameworks.Cheerio
import semmle.javascript.frameworks.ComposedFunctions
import semmle.javascript.frameworks.Classnames
import semmle.javascript.frameworks.ClassValidator
import semmle.javascript.frameworks.ClientRequests
import semmle.javascript.frameworks.ClosureLibrary
import semmle.javascript.frameworks.CookieLibraries
Expand All @@ -98,6 +99,7 @@ import semmle.javascript.frameworks.Logging
import semmle.javascript.frameworks.HttpFrameworks
import semmle.javascript.frameworks.HttpProxy
import semmle.javascript.frameworks.Markdown
import semmle.javascript.frameworks.Nest
import semmle.javascript.frameworks.Next
import semmle.javascript.frameworks.NoSQL
import semmle.javascript.frameworks.PkgCloud
Expand Down
14 changes: 14 additions & 0 deletions javascript/ql/src/semmle/javascript/dataflow/Nodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class ParameterNode extends DataFlow::SourceNode {

/** Holds if this parameter is a rest parameter. */
predicate isRestParameter() { p.isRestParameter() }

/** Gets the data flow node for an expression that is applied to this decorator. */
DataFlow::Node getADecorator() { result = getParameter().getADecorator().getExpression().flow() }
}

/**
Expand Down Expand Up @@ -1078,56 +1081,67 @@ module ClassNode {
* Subclass this to introduce new kinds of class nodes. If you want to refine
* the definition of existing class nodes, subclass `DataFlow::ClassNode` instead.
*/
cached
abstract class Range extends DataFlow::SourceNode {
/**
* Gets the name of the class, if it has one.
*/
cached
abstract string getName();

/**
* Gets a description of the class.
*/
cached
abstract string describe();

/**
* Gets the constructor function of this class.
*/
cached
abstract FunctionNode getConstructor();

/**
* Gets the instance member with the given name and kind.
*/
cached
abstract FunctionNode getInstanceMember(string name, MemberKind kind);

/**
* Gets an instance member with the given kind.
*/
cached
abstract FunctionNode getAnInstanceMember(MemberKind kind);

/**
* Gets the static method of this class with the given name.
*/
cached
abstract FunctionNode getStaticMethod(string name);

/**
* Gets a static method of this class.
*
* The constructor is not considered a static method.
*/
cached
abstract FunctionNode getAStaticMethod();

/**
* Gets a dataflow node representing a class to be used as the super-class
* of this node.
*/
cached
abstract DataFlow::Node getASuperClassNode();

/**
* Gets the type annotation for the field `fieldName`, if any.
*/
cached
TypeAnnotation getFieldTypeAnnotation(string fieldName) { none() }

/** Gets a decorator applied to this class. */
cached
DataFlow::Node getADecorator() { none() }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,8 @@ module TaintTracking {
// reading from a tainted object yields a tainted result
succ.(DataFlow::PropRead).getBase() = pred and
not AccessPath::DominatingPaths::hasDominatingWrite(succ) and
not isSafeClientSideUrlProperty(succ)
not isSafeClientSideUrlProperty(succ) and
not ClassValidator::isAccessToSanitizedField(succ)
or
// iterating over a tainted iterator taints the loop variable
exists(ForOfStmt fos |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Provides predicates for reasoning about sanitization via the `class-validator` library.
*/

import javascript

/**
* Provides predicates for reasoning about sanitization via the `class-validator` library.
*/
module ClassValidator {
/**
* Holds if the decorator with the given name sanitizes the input, for the purpose of taint tracking.
*/
bindingset[name]
private predicate isSanitizingDecoratorName(string name) {
// Most decorators do sanitize the input, so only list those that don't.
not name =
[
"IsDefined", "IsOptional", "NotEquals", "IsNotEmpty", "IsNotIn", "IsString", "IsArray",
"Contains", "NotContains", "IsAscii", "IsByteLength", "IsDataURI", "IsFQDN", "IsJSON",
"IsJWT", "IsObject", "IsNotEmptyObject", "IsLowercase", "IsSurrogatePair", "IsUrl",
"IsUppercase", "Length", "MinLength", "MaxLength", "ArrayContains", "ArrayNotContains",
"ArrayNotEmpty", "ArrayMinSize", "ArrayMaxSize", "ArrayUnique", "Allow", "ValidateNested",
"Validate",
// Consider "Matches" to be non-sanitizing as it is special-cased below
"Matches"
]
}

/** Holds if the given call is a decorator that sanitizes values for the purpose of taint tracking, such as `IsBoolean()`. */
API::CallNode sanitizingDecorator() {
exists(string name | result = API::moduleImport("class-validator").getMember(name).getACall() |
isSanitizingDecoratorName(name)
or
name = "Matches" and
RegExp::isGenericRegExpSanitizer(RegExp::getRegExpFromNode(result.getArgument(0)), true)
)
}

/** Holds if the given field has a decorator that sanitizes its value for the purpose of taint tracking. */
predicate isFieldSanitizedByDecorator(FieldDefinition field) {
field.getADecorator().getExpression().flow() = sanitizingDecorator().getReturn().getAUse()
}

pragma[noinline]
private predicate isFieldSanitizedByDecorator(ClassDefinition cls, string name) {
isFieldSanitizedByDecorator(cls.getField(name))
}

pragma[noinline]
private ClassDefinition getClassReferencedByPropRead(DataFlow::PropRead read) {
read.getBase().asExpr().getType().unfold().(ClassType).getClass() = result
}

/**
* Holds if the given property read refers to a field that has a sanitizing decorator.
*
* Only holds when TypeScript types are available.
*/
pragma[noinline]
predicate isAccessToSanitizedField(DataFlow::PropRead read) {
exists(ClassDefinition class_ |
class_ = getClassReferencedByPropRead(read) and
isFieldSanitizedByDecorator(class_, read.getPropertyName())
)
}
}
Loading