Skip to content

Commit beb66fc

Browse files
authored
Merge pull request github#5719 from asgerf/js/nestjs
Approved by esbena
2 parents 744c495 + f4e636d commit beb66fc

File tree

23 files changed

+997
-95
lines changed

23 files changed

+997
-95
lines changed

docs/codeql/support/reusables/frameworks.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ JavaScript and TypeScript built-in support
129129
mssql, Database
130130
mysql, Database
131131
node, Runtime environment
132+
nest.js, Server
132133
postgres, Database
133134
ramda, Utility library
134135
react, HTML framework
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* Support for `fs.promises` has been added, leading to more results for security queries
3+
related to file system access.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* Support for Nest.js has been added. The security queries now recognize sources and sinks
3+
specific to the Nest.js framework.

javascript/ql/src/javascript.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import semmle.javascript.frameworks.Babel
7676
import semmle.javascript.frameworks.Cheerio
7777
import semmle.javascript.frameworks.ComposedFunctions
7878
import semmle.javascript.frameworks.Classnames
79+
import semmle.javascript.frameworks.ClassValidator
7980
import semmle.javascript.frameworks.ClientRequests
8081
import semmle.javascript.frameworks.ClosureLibrary
8182
import semmle.javascript.frameworks.CookieLibraries
@@ -99,6 +100,7 @@ import semmle.javascript.frameworks.Logging
99100
import semmle.javascript.frameworks.HttpFrameworks
100101
import semmle.javascript.frameworks.HttpProxy
101102
import semmle.javascript.frameworks.Markdown
103+
import semmle.javascript.frameworks.Nest
102104
import semmle.javascript.frameworks.Next
103105
import semmle.javascript.frameworks.NoSQL
104106
import semmle.javascript.frameworks.PkgCloud

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class ParameterNode extends DataFlow::SourceNode {
4747

4848
/** Holds if this parameter is a rest parameter. */
4949
predicate isRestParameter() { p.isRestParameter() }
50+
51+
/** Gets the data flow node for an expression that is applied to this decorator. */
52+
DataFlow::Node getADecorator() { result = getParameter().getADecorator().getExpression().flow() }
5053
}
5154

5255
/**
@@ -1078,56 +1081,67 @@ module ClassNode {
10781081
* Subclass this to introduce new kinds of class nodes. If you want to refine
10791082
* the definition of existing class nodes, subclass `DataFlow::ClassNode` instead.
10801083
*/
1084+
cached
10811085
abstract class Range extends DataFlow::SourceNode {
10821086
/**
10831087
* Gets the name of the class, if it has one.
10841088
*/
1089+
cached
10851090
abstract string getName();
10861091

10871092
/**
10881093
* Gets a description of the class.
10891094
*/
1095+
cached
10901096
abstract string describe();
10911097

10921098
/**
10931099
* Gets the constructor function of this class.
10941100
*/
1101+
cached
10951102
abstract FunctionNode getConstructor();
10961103

10971104
/**
10981105
* Gets the instance member with the given name and kind.
10991106
*/
1107+
cached
11001108
abstract FunctionNode getInstanceMember(string name, MemberKind kind);
11011109

11021110
/**
11031111
* Gets an instance member with the given kind.
11041112
*/
1113+
cached
11051114
abstract FunctionNode getAnInstanceMember(MemberKind kind);
11061115

11071116
/**
11081117
* Gets the static method of this class with the given name.
11091118
*/
1119+
cached
11101120
abstract FunctionNode getStaticMethod(string name);
11111121

11121122
/**
11131123
* Gets a static method of this class.
11141124
*
11151125
* The constructor is not considered a static method.
11161126
*/
1127+
cached
11171128
abstract FunctionNode getAStaticMethod();
11181129

11191130
/**
11201131
* Gets a dataflow node representing a class to be used as the super-class
11211132
* of this node.
11221133
*/
1134+
cached
11231135
abstract DataFlow::Node getASuperClassNode();
11241136

11251137
/**
11261138
* Gets the type annotation for the field `fieldName`, if any.
11271139
*/
1140+
cached
11281141
TypeAnnotation getFieldTypeAnnotation(string fieldName) { none() }
11291142

11301143
/** Gets a decorator applied to this class. */
1144+
cached
11311145
DataFlow::Node getADecorator() { none() }
11321146
}
11331147

javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ module TaintTracking {
550550
// reading from a tainted object yields a tainted result
551551
succ.(DataFlow::PropRead).getBase() = pred and
552552
not AccessPath::DominatingPaths::hasDominatingWrite(succ) and
553-
not isSafeClientSideUrlProperty(succ)
553+
not isSafeClientSideUrlProperty(succ) and
554+
not ClassValidator::isAccessToSanitizedField(succ)
554555
or
555556
// iterating over a tainted iterator taints the loop variable
556557
exists(ForOfStmt fos |
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Provides predicates for reasoning about sanitization via the `class-validator` library.
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* Provides predicates for reasoning about sanitization via the `class-validator` library.
9+
*/
10+
module ClassValidator {
11+
/**
12+
* Holds if the decorator with the given name sanitizes the input, for the purpose of taint tracking.
13+
*/
14+
bindingset[name]
15+
private predicate isSanitizingDecoratorName(string name) {
16+
// Most decorators do sanitize the input, so only list those that don't.
17+
not name =
18+
[
19+
"IsDefined", "IsOptional", "NotEquals", "IsNotEmpty", "IsNotIn", "IsString", "IsArray",
20+
"Contains", "NotContains", "IsAscii", "IsByteLength", "IsDataURI", "IsFQDN", "IsJSON",
21+
"IsJWT", "IsObject", "IsNotEmptyObject", "IsLowercase", "IsSurrogatePair", "IsUrl",
22+
"IsUppercase", "Length", "MinLength", "MaxLength", "ArrayContains", "ArrayNotContains",
23+
"ArrayNotEmpty", "ArrayMinSize", "ArrayMaxSize", "ArrayUnique", "Allow", "ValidateNested",
24+
"Validate",
25+
// Consider "Matches" to be non-sanitizing as it is special-cased below
26+
"Matches"
27+
]
28+
}
29+
30+
/** Holds if the given call is a decorator that sanitizes values for the purpose of taint tracking, such as `IsBoolean()`. */
31+
API::CallNode sanitizingDecorator() {
32+
exists(string name | result = API::moduleImport("class-validator").getMember(name).getACall() |
33+
isSanitizingDecoratorName(name)
34+
or
35+
name = "Matches" and
36+
RegExp::isGenericRegExpSanitizer(RegExp::getRegExpFromNode(result.getArgument(0)), true)
37+
)
38+
}
39+
40+
/** Holds if the given field has a decorator that sanitizes its value for the purpose of taint tracking. */
41+
predicate isFieldSanitizedByDecorator(FieldDefinition field) {
42+
field.getADecorator().getExpression().flow() = sanitizingDecorator().getReturn().getAUse()
43+
}
44+
45+
pragma[noinline]
46+
private predicate isFieldSanitizedByDecorator(ClassDefinition cls, string name) {
47+
isFieldSanitizedByDecorator(cls.getField(name))
48+
}
49+
50+
pragma[noinline]
51+
private ClassDefinition getClassReferencedByPropRead(DataFlow::PropRead read) {
52+
read.getBase().asExpr().getType().unfold().(ClassType).getClass() = result
53+
}
54+
55+
/**
56+
* Holds if the given property read refers to a field that has a sanitizing decorator.
57+
*
58+
* Only holds when TypeScript types are available.
59+
*/
60+
pragma[noinline]
61+
predicate isAccessToSanitizedField(DataFlow::PropRead read) {
62+
exists(ClassDefinition class_ |
63+
class_ = getClassReferencedByPropRead(read) and
64+
isFieldSanitizedByDecorator(class_, read.getPropertyName())
65+
)
66+
}
67+
}

0 commit comments

Comments
 (0)