Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 119b1ff

Browse files
committed
changes based on review from max
1 parent dc891dc commit 119b1ff

5 files changed

Lines changed: 77 additions & 94 deletions

File tree

javascript/ql/src/Security/CWE-834/TaintedLength.qhelp

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,43 @@
44
<qhelp>
55

66
<overview>
7-
<p>
8-
Iterating the elements of an untrusted object using the
9-
<code>.length</code> property can lead to a server looping
10-
indefinitely. This looping causes a denial-of-service or DoS by
11-
causing the server to hang or run out of memory.
12-
This happens when the server expects an array but an attacker sends
13-
a regular JSON object with a huge number in the
14-
<code>.length</code> property, such as <code>{length: 1e100}</code>,
15-
that the server then loops through.
16-
</p>
7+
<p>
8+
Using the .length property of an untrusted object as a loop bound may
9+
cause indefinite looping since a malicious attacker can set the
10+
<code>.length</code> property to a very large number. For example,
11+
when a program that expects an array is passed a JSON object such as
12+
<code>{length: 1e100}</code>, the loop will be run for 1e100
13+
iterations. This may cause the program to hang or run out of memory,
14+
which can be used to mount a denial-of-service (DoS) attack.
15+
</p>
1716
</overview>
1817

1918
<recommendation>
20-
<p>
21-
Either force the user controlled object to be an array or limit the
22-
size of the <code>.length</code> property.
23-
</p>
19+
<p>
20+
Either check that the object is indeed an array or limit the
21+
size of the <code>.length</code> property.
22+
</p>
2423
</recommendation>
2524

2625
<example>
27-
<p>
28-
In the example below, a server iterates over a user controlled object
29-
<code>obj</code> using the <code>obj.length</code> property in order
30-
to copy the elements from <code>obj</code> to an array.
31-
</p>
26+
<p>
27+
In the example below, an HTTP request handler iterates over a
28+
user-controlled object <code>obj</code> using the
29+
<code>obj.length</code> property in order to copy the elements from
30+
<code>obj</code> to an array.
31+
</p>
3232

33-
<sample src="examples/TaintedLength.js"/>
33+
<sample src="examples/TaintedLength.js" />
3434

35-
<p>
36-
This is not secure since an attacker can control the value of
37-
<code>obj.length</code>, and thereby cause the loop to loop
38-
indefinitely.
39-
Here the potential DoS is fixed by enforcing that the user controlled
40-
object is an array.
41-
</p>
35+
<p>
36+
This is not secure since an attacker can control the value of
37+
<code>obj.length</code>, and thereby cause the loop to iterate
38+
indefinitely. Here the potential DoS is fixed by enforcing that
39+
the user controlled object is an array.
40+
</p>
4241

43-
<sample src="examples/TaintedLength_fixed.js"/>
42+
<sample src="examples/TaintedLength_fixed.js" />
4443
</example>
4544

46-
<references>
47-
<li>CWE entry:
48-
<a href="https://cwe.mitre.org/data/definitions/834.html">CWE-834</a>
49-
</li>
50-
</references>
45+
<references></references>
5146
</qhelp>
Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
/**
22
* @name Tainted .length in loop condition
3-
* @description If server-side code iterates over a user-controlled object with
4-
* an arbitrary .length value an attacker can trick the server
5-
* to loop infinitely.
3+
* @description Iterating over an object with a user-controlled .length
4+
* property can cause indefinite looping
65
* @kind path-problem
76
* @problem.severity warning
87
* @id js/loop-bound-injection
98
* @tags security
9+
* external/cwe/cwe-834
1010
* @precision medium
1111
*/
1212

1313
import javascript
14-
1514
import semmle.javascript.security.dataflow.TaintedLength::TaintedLength
1615

17-
from
18-
Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
16+
from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
1917
where dataflow.hasFlowPath(source, sink)
2018
select sink, source, sink,
21-
"Iterating over user controlled object with an unbounded .length property $@.",
19+
"Iterating over user-controlled object with a potentially unbounded .length property from $@.",
2220
source, "here"

javascript/ql/src/semmle/javascript/security/dataflow/TaintedLength.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
2-
* Provides a taint tracking configuration for reasoning about DOS attacks
3-
* using a user controlled object with an unbounded .length property.
2+
* Provides a taint tracking configuration for reasoning about DoS attacks
3+
* using a user controlled object with an unbounded .length property.
44
*
55
* Note, for performance reasons: only import this file if
66
* `TaintedLength::Configuration` is needed, otherwise

javascript/ql/src/semmle/javascript/security/dataflow/TaintedLengthCustomizations.qll

Lines changed: 33 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Provides default sources, sinks and sanitisers for reasoning about
3-
* DOS attacks using objects with unbounded length property.
4-
* As well as extension points for adding your own.
3+
* DoS attacks using objects with unbounded length property,
4+
* as well as extension points for adding your own.
55
*/
66

77
import javascript
@@ -30,48 +30,45 @@ module TaintedLength {
3030
}
3131

3232
/**
33-
* A loop that iterates through some array using the `length` property.
34-
* The loop is either of the style `for(..; i < arr.length;...)` or `while(i < arr.length) {..;i++;..}`.
33+
* A loop that iterates through some array using the `length` property.
34+
* The loop is either of the style `for(..; i < arr.length;...)` or `while(i < arr.length) {..;i++;..}`.
3535
*/
3636
class ArrayIterationLoop extends Stmt {
3737
LocalVariable indexVariable;
38+
3839
LoopStmt loop;
40+
3941
DataFlow::PropRead lengthRead;
4042

4143
ArrayIterationLoop() {
42-
this = loop and
44+
this = loop and
4345
exists(RelationalComparison compare |
4446
compare = loop.getTest() and
4547
compare.getLesserOperand() = indexVariable.getAnAccess() and
4648
lengthRead.getPropertyName() = "length" and
4749
lengthRead.flowsToExpr(compare.getGreaterOperand())
4850
) and
49-
(
50-
loop.(ForStmt).getUpdate().(IncExpr).getOperand() = indexVariable.getAnAccess() or
51-
loop.getBody().getAChild*().(IncExpr).getOperand() = indexVariable.getAnAccess()
51+
exists(IncExpr inc | inc.getOperand() = indexVariable.getAnAccess() |
52+
inc = loop.(ForStmt).getUpdate()
53+
or
54+
inc.getEnclosingStmt().getParentStmt*() = loop.getBody()
5255
)
5356
}
54-
57+
5558
/**
5659
* Gets the length read in the loop test
5760
*/
58-
DataFlow::PropRead getLengthRead() {
59-
result = lengthRead
60-
}
61-
61+
DataFlow::PropRead getLengthRead() { result = lengthRead }
62+
6263
/**
6364
* Gets the loop test of this loop.
6465
*/
65-
Expr getTest() {
66-
result = loop.getTest()
67-
}
68-
66+
Expr getTest() { result = loop.getTest() }
67+
6968
/**
7069
* Gets the body of this loop.
7170
*/
72-
Stmt getBody() {
73-
result = loop.getBody()
74-
}
71+
Stmt getBody() { result = loop.getBody() }
7572

7673
/**
7774
* Gets the variable holding the loop variable and current array index.
@@ -91,33 +88,30 @@ module TaintedLength {
9188
LoopSink() {
9289
exists(ArrayIterationLoop loop |
9390
this = loop.getLengthRead().getBase() and
94-
95-
// In the DOS we are looking for arrayRead will evaluate to undefined,
96-
// this may cause an exception to be thrown, thus bailing out of the loop.
97-
// A DoS cannot happen if such an exception is thrown.
91+
// In the DoS we are looking for arrayRead will evaluate to undefined,
92+
// this may cause an exception to be thrown, thus bailing out of the loop.
93+
// A DoS cannot happen if such an exception is thrown.
9894
not exists(DataFlow::PropRead arrayRead, Expr throws |
99-
loop.getBody().getAChild*() = arrayRead.asExpr() and
100-
loop.getBody().getAChild*() = throws and
10195
arrayRead.getPropertyNameExpr() = loop.getIndexVariable().getAnAccess() and
10296
arrayRead.flowsToExpr(throws) and
10397
isCrashingWithNullValues(throws)
10498
) and
10599
// The existence of some kind of early-exit usually indicates that the loop will stop early and no DOS happens.
106100
not exists(BreakStmt br | br.getTarget() = loop) and
107101
not exists(ReturnStmt ret |
108-
loop.getBody().getAChild*() = ret and
102+
ret.getParentStmt*() = loop.getBody() and
109103
ret.getContainer() = loop.getContainer()
110104
) and
111105
not exists(ThrowStmt throw |
112-
loop.getBody().getAChild*() = throw and
113-
not loop.getBody().getAChild*() = throw.getTarget()
106+
loop.getBody() = throw.getParentStmt*() and
107+
not loop.getBody() = throw.getTarget().getParent*()
114108
)
115109
)
116110
}
117111
}
118112

119113
/**
120-
* Holds if `name` is a method from lodash vulnerable to a DOS attack if called with a tained object.
114+
* Holds if `name` is a method from lodash vulnerable to a DOS attack if called with a tained object.
121115
*/
122116
predicate loopableLodashMethod(string name) {
123117
name = "chunk" or
@@ -183,8 +177,8 @@ module TaintedLength {
183177
}
184178

185179
/**
186-
* A method call to a lodash method that iterates over an array-like structure,
187-
* such as `_.filter(sink, ...)`.
180+
* A method call to a lodash method that iterates over an array-like structure,
181+
* such as `_.filter(sink, ...)`.
188182
*/
189183
private class LodashIterationSink extends Sink {
190184
DataFlow::CallNode call;
@@ -194,7 +188,6 @@ module TaintedLength {
194188
loopableLodashMethod(name) and
195189
call = LodashUnderscore::member(name).getACall() and
196190
call.getArgument(0) = this and
197-
198191
// Here it is just assumed that the array element is the first parameter in the callback function.
199192
not exists(DataFlow::FunctionNode func, DataFlow::ParameterNode e |
200193
func.flowsTo(call.getAnArgument()) and
@@ -203,39 +196,36 @@ module TaintedLength {
203196
// Looking for obvious null-pointers happening on the array elements in the iteration.
204197
// Similar to what is done in the loop iteration sink.
205198
exists(Expr throws |
206-
throws = func.asExpr().(Function).getBody().getAChild*() and
207199
e.flowsToExpr(throws) and
208200
isCrashingWithNullValues(throws)
209201
)
210202
or
211203
// similar to the loop sink - the existence of an early-exit usually means that no DOS can happen.
212204
exists(ThrowStmt throw |
213-
throw = func.asExpr().(Function).getBody().getAChild*() and
214205
throw.getTarget() = func.asExpr()
215206
)
216207
)
217208
)
218209
)
219210
}
220211
}
221-
212+
222213
/**
223-
* A source of objects that can cause DOS if looped over.
214+
* A source of objects that can cause DOS if looped over.
224215
*/
225216
abstract class Source extends DataFlow::Node { }
226-
217+
227218
/**
228219
* A source of remote user input objects.
229220
*/
230221
class TaintedObjectSource extends Source {
231222
TaintedObjectSource() { this instanceof TaintedObject::Source }
232223
}
233-
224+
234225
/**
235226
* A sanitizer that blocks taint flow if the array is checked to be an array using an `isArray` method.
236227
*/
237-
class IsArraySanitizerGuard extends TaintTracking::LabeledSanitizerGuardNode,
238-
DataFlow::ValueNode {
228+
class IsArraySanitizerGuard extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode {
239229
override CallExpr astNode;
240230

241231
IsArraySanitizerGuard() { astNode.getCalleeName() = "isArray" }
@@ -268,8 +258,8 @@ module TaintedLength {
268258

269259
/**
270260
* A sanitizer that blocks taint flow if the length of an array is limited.
271-
*
272-
* Also implicitly makes sure that only the first DOS-prone loop is selected by the query. (as the .length test has outcome=false when exiting the loop).
261+
*
262+
* Also implicitly makes sure that only the first DoS-prone loop is selected by the query. (as the .length test has outcome=false when exiting the loop).
273263
*/
274264
class LengthCheckSanitizerGuard extends TaintTracking::LabeledSanitizerGuardNode,
275265
DataFlow::ValueNode {

javascript/ql/test/query-tests/Security/CWE-834/TaintedLength.expected

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ edges
4646
| TaintedLengthLodash.js:9:13:9:20 | req.body | TaintedLengthLodash.js:12:18:12:20 | val |
4747
| TaintedLengthLodash.js:12:18:12:20 | val | TaintedLengthLodash.js:13:13:13:15 | val |
4848
#select
49-
| TaintedLengthBad.js:20:25:20:27 | val | TaintedLengthBad.js:8:13:8:20 | req.body | TaintedLengthBad.js:20:25:20:27 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthBad.js:8:13:8:20 | req.body | here |
50-
| TaintedLengthBad.js:29:16:29:18 | val | TaintedLengthBad.js:10:15:10:22 | req.body | TaintedLengthBad.js:29:16:29:18 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthBad.js:10:15:10:22 | req.body | here |
51-
| TaintedLengthBad.js:38:15:38:17 | val | TaintedLengthBad.js:12:25:12:32 | req.body | TaintedLengthBad.js:38:15:38:17 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthBad.js:12:25:12:32 | req.body | here |
52-
| TaintedLengthBad.js:51:25:51:27 | val | TaintedLengthBad.js:14:19:14:26 | req.body | TaintedLengthBad.js:51:25:51:27 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthBad.js:14:19:14:26 | req.body | here |
53-
| TaintedLengthExitBad.js:20:22:20:24 | val | TaintedLengthExitBad.js:8:9:8:16 | req.body | TaintedLengthExitBad.js:20:22:20:24 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthExitBad.js:8:9:8:16 | req.body | here |
54-
| TaintedLengthExitBad.js:34:22:34:24 | val | TaintedLengthExitBad.js:10:9:10:16 | req.body | TaintedLengthExitBad.js:34:22:34:24 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthExitBad.js:10:9:10:16 | req.body | here |
55-
| TaintedLengthExitBad.js:49:22:49:24 | val | TaintedLengthExitBad.js:12:10:12:17 | req.body | TaintedLengthExitBad.js:49:22:49:24 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthExitBad.js:12:10:12:17 | req.body | here |
56-
| TaintedLengthExitBad.js:60:8:60:10 | val | TaintedLengthExitBad.js:14:14:14:21 | req.body | TaintedLengthExitBad.js:60:8:60:10 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthExitBad.js:14:14:14:21 | req.body | here |
57-
| TaintedLengthLodash.js:13:13:13:15 | val | TaintedLengthLodash.js:9:13:9:20 | req.body | TaintedLengthLodash.js:13:13:13:15 | val | Iterating over user controlled object with an unbounded .length property $@. | TaintedLengthLodash.js:9:13:9:20 | req.body | here |
49+
| TaintedLengthBad.js:20:25:20:27 | val | TaintedLengthBad.js:8:13:8:20 | req.body | TaintedLengthBad.js:20:25:20:27 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthBad.js:8:13:8:20 | req.body | here |
50+
| TaintedLengthBad.js:29:16:29:18 | val | TaintedLengthBad.js:10:15:10:22 | req.body | TaintedLengthBad.js:29:16:29:18 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthBad.js:10:15:10:22 | req.body | here |
51+
| TaintedLengthBad.js:38:15:38:17 | val | TaintedLengthBad.js:12:25:12:32 | req.body | TaintedLengthBad.js:38:15:38:17 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthBad.js:12:25:12:32 | req.body | here |
52+
| TaintedLengthBad.js:51:25:51:27 | val | TaintedLengthBad.js:14:19:14:26 | req.body | TaintedLengthBad.js:51:25:51:27 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthBad.js:14:19:14:26 | req.body | here |
53+
| TaintedLengthExitBad.js:20:22:20:24 | val | TaintedLengthExitBad.js:8:9:8:16 | req.body | TaintedLengthExitBad.js:20:22:20:24 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthExitBad.js:8:9:8:16 | req.body | here |
54+
| TaintedLengthExitBad.js:34:22:34:24 | val | TaintedLengthExitBad.js:10:9:10:16 | req.body | TaintedLengthExitBad.js:34:22:34:24 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthExitBad.js:10:9:10:16 | req.body | here |
55+
| TaintedLengthExitBad.js:49:22:49:24 | val | TaintedLengthExitBad.js:12:10:12:17 | req.body | TaintedLengthExitBad.js:49:22:49:24 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthExitBad.js:12:10:12:17 | req.body | here |
56+
| TaintedLengthExitBad.js:60:8:60:10 | val | TaintedLengthExitBad.js:14:14:14:21 | req.body | TaintedLengthExitBad.js:60:8:60:10 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthExitBad.js:14:14:14:21 | req.body | here |
57+
| TaintedLengthLodash.js:13:13:13:15 | val | TaintedLengthLodash.js:9:13:9:20 | req.body | TaintedLengthLodash.js:13:13:13:15 | val | Iterating over user-controlled object with a potentially unbounded .length property from $@. | TaintedLengthLodash.js:9:13:9:20 | req.body | here |

0 commit comments

Comments
 (0)