Skip to content

Commit eca5e2d

Browse files
authored
Merge pull request github#3702 from esbena/js/memory-exhaustion
JS: add query js/memory-exhaustion
2 parents 1548eca + 0463c42 commit eca5e2d

21 files changed

+871
-0
lines changed

change-notes/1.25/analysis-javascript.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
| Creating biased random numbers from a cryptographically secure source (`js/biased-cryptographic-random`) | security, external/cwe/cwe-327 | Highlights mathematical operations on cryptographically secure numbers that can create biased results. Results are shown on LGTM by default. |
4242
| Storage of sensitive information in build artifact (`js/build-artifact-leak`) | security, external/cwe/cwe-312 | Highlights storage of sensitive information in build artifacts. Results are shown on LGTM by default. |
4343
| Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. |
44+
| Resource exhaustion (`js/resource-exhaustion`) | security, external/cwe/cwe-770 | Highlights operations that may cause the resources of the application to be exhausted. Results are shown on LGTM by default. |
4445

4546
## Changes to existing queries
4647

javascript/config/suites/javascript/security

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
+ semmlecode-javascript-queries/Security/CWE-730/RegExpInjection.ql: /Security/CWE/CWE-730
4545
+ semmlecode-javascript-queries/Security/CWE-754/UnvalidatedDynamicMethodCall.ql: /Security/CWE/CWE-754
4646
+ semmlecode-javascript-queries/Security/CWE-770/MissingRateLimiting.ql: /Security/CWE/CWE-770
47+
+ semmlecode-javascript-queries/Security/CWE-770/ResourceExhaustion.ql: /Security/CWE/CWE-770
4748
+ semmlecode-javascript-queries/Security/CWE-776/XmlBomb.ql: /Security/CWE/CWE-776
4849
+ semmlecode-javascript-queries/Security/CWE-798/HardcodedCredentials.ql: /Security/CWE/CWE-798
4950
+ semmlecode-javascript-queries/Security/CWE-807/ConditionalBypass.ql: /Security/CWE/CWE-807
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
8+
<p>
9+
10+
Applications are constrained by how many resources they can make use
11+
of. Failing to respect these constraints may cause the application to
12+
be unresponsive or crash. It is therefore problematic if attackers
13+
can control the sizes or lifetimes of allocated objects.
14+
15+
</p>
16+
17+
</overview>
18+
19+
<recommendation>
20+
21+
<p>
22+
23+
Ensure that attackers can not control object sizes and their
24+
lifetimes. If object sizes and lifetimes must be controlled by
25+
external parties, ensure you restrict the object sizes and lifetimes so that
26+
they are within acceptable ranges.
27+
28+
</p>
29+
30+
</recommendation>
31+
32+
<example>
33+
34+
<p>
35+
36+
The following example allocates a buffer with a user-controlled
37+
size.
38+
39+
</p>
40+
41+
<sample src="examples/ResourceExhaustion_buffer.js" />
42+
43+
<p>
44+
45+
This is problematic since an attacker can choose a size
46+
that makes the application run out of memory. Even worse, in older
47+
versions of Node.js, this could leak confidential memory.
48+
49+
To prevent such attacks, limit the buffer size:
50+
51+
</p>
52+
53+
<sample src="examples/ResourceExhaustion_buffer_fixed.js" />
54+
55+
</example>
56+
57+
<example>
58+
59+
<p>
60+
61+
As another example, consider an application that allocates an
62+
array with a user-controlled size, and then fills it with values:
63+
64+
</p>
65+
66+
<sample src="examples/ResourceExhaustion_array.js" />
67+
68+
<p>
69+
The allocation of the array itself is not problematic since arrays are
70+
allocated sparsely, but the subsequent filling of the array will take
71+
a long time, causing the application to be unresponsive, or even run
72+
out of memory.
73+
74+
Again, a limit on the size will prevent the attack:
75+
76+
</p>
77+
78+
<sample src="examples/ResourceExhaustion_array_fixed.js" />
79+
80+
</example>
81+
82+
<example>
83+
84+
<p>
85+
86+
Finally, the following example lets a user choose a delay after
87+
which a function is executed:
88+
89+
</p>
90+
91+
<sample src="examples/ResourceExhaustion_timeout.js" />
92+
93+
<p>
94+
95+
This is problematic because a large delay essentially makes the
96+
application wait indefinitely before executing the function. Repeated
97+
registrations of such delays will therefore use up all of the memory
98+
in the application.
99+
100+
Again, a limit on the delay will prevent the attack:
101+
102+
</p>
103+
104+
<sample src="examples/ResourceExhaustion_timeout_fixed.js" />
105+
106+
107+
</example>
108+
109+
<references>
110+
111+
</references>
112+
113+
</qhelp>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Resource exhaustion
3+
* @description Allocating objects or timers with user-controlled
4+
* sizes or durations can cause resource exhaustion.
5+
* @kind path-problem
6+
* @problem.severity warning
7+
* @id js/resource-exhaustion
8+
* @precision high
9+
* @tags security
10+
* external/cwe/cwe-770
11+
*/
12+
13+
import javascript
14+
import DataFlow::PathGraph
15+
import semmle.javascript.security.dataflow.ResourceExhaustion::ResourceExhaustion
16+
17+
from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
18+
where dataflow.hasFlowPath(source, sink)
19+
select sink, source, sink, sink.getNode().(Sink).getProblemDescription() + " from $@.", source,
20+
"here"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var http = require("http"),
2+
url = require("url");
3+
4+
var server = http.createServer(function(req, res) {
5+
var size = parseInt(url.parse(req.url, true).query.size);
6+
7+
let dogs = new Array(size).fill(x => "dog"); // BAD
8+
9+
// ... use the dogs
10+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
var http = require("http"),
2+
url = require("url");
3+
4+
var server = http.createServer(function(req, res) {
5+
var size = parseInt(url.parse(req.url, true).query.size);
6+
7+
if (size > 1024) {
8+
res.statusCode = 400;
9+
res.end("Bad request.");
10+
return;
11+
}
12+
13+
let dogs = new Array(size).fill(x => "dog"); // GOOD
14+
15+
// ... use the dogs
16+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var http = require("http"),
2+
url = require("url");
3+
4+
var server = http.createServer(function(req, res) {
5+
var size = parseInt(url.parse(req.url, true).query.size);
6+
7+
let buffer = Buffer.alloc(size); // BAD
8+
9+
// ... use the buffer
10+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
var http = require("http"),
2+
url = require("url");
3+
4+
var server = http.createServer(function(req, res) {
5+
var size = parseInt(url.parse(req.url, true).query.size);
6+
7+
if (size > 1024) {
8+
res.statusCode = 400;
9+
res.end("Bad request.");
10+
return;
11+
}
12+
13+
let buffer = Buffer.alloc(size); // GOOD
14+
15+
// ... use the buffer
16+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
var http = require("http"),
2+
url = require("url");
3+
4+
var server = http.createServer(function(req, res) {
5+
var delay = parseInt(url.parse(req.url, true).query.delay);
6+
7+
setTimeout(f, delay); // BAD
8+
9+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var http = require("http"),
2+
url = require("url");
3+
4+
var server = http.createServer(function(req, res) {
5+
var delay = parseInt(url.parse(req.url, true).query.delay);
6+
7+
if (delay > 1000) {
8+
res.statusCode = 400;
9+
res.end("Bad request.");
10+
return;
11+
}
12+
13+
setTimeout(f, delay); // GOOD
14+
15+
});

0 commit comments

Comments
 (0)