Skip to content

Commit 0f5ef2c

Browse files
authored
Merge branch 'js-team-sprint' into https-fix
2 parents 7d6dac4 + e13353f commit 0f5ef2c

File tree

14 files changed

+181
-34
lines changed

14 files changed

+181
-34
lines changed

change-notes/1.25/analysis-javascript.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
| Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. |
3838
| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. |
3939
| Download of sensitive file through insecure connection (`js/insecure-download`) | security, external/cwe/cwe-829 | Highlights downloads of sensitive files through an unencrypted protocol. Results are shown on LGTM by default. |
40+
| Exposure of private files (`js/exposure-of-private-files`) | security, external/cwe/cwe-200 | Highlights servers that serve private files. Results are shown on LGTM by default. |
41+
| 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. |
4042
| 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. |
4143
| 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. |
4244

javascript/ql/src/Security/CWE-200/PrivateFileExposure.qhelp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,33 @@
55

66
<overview>
77
<p>
8-
Placeholder
8+
Libraries like <code>express</code> provide easy methods for serving entire
9+
directories of static files from a web server.
10+
However, using these can sometimes lead to accidental information exposure.
11+
If for example the <code>node_modules</code> folder is served, then an attacker
12+
can access the <code>_where</code> field from a <code>package.json</code> file,
13+
which gives access to the absolute path of the file.
914
</p>
1015
</overview>
1116

1217
<recommendation>
1318
<p>
14-
Placeholder
19+
Limit which folders of static files are served from a web server.
1520
</p>
1621
</recommendation>
1722

1823
<example>
1924
<p>
20-
Placeholder
25+
In the example below, all the files from the <code>node_modules</code> are served.
26+
This allows clients to easily access all the files inside that folder,
27+
which includes potentially private information inside <code>package.json</code> files.
2128
</p>
29+
<sample src="examples/PrivateFileExposure.js"/>
30+
<p>
31+
The issue has been fixed below by only serving specific folders within the
32+
<code>node_modules</code> folder.
33+
</p>
34+
<sample src="examples/PrivateFileExposureFixed.js"/>
2235
</example>
2336

2437
<references>

javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,33 @@ pragma[noinline]
6969
Folder getAPackageJSONFolder() { result = any(PackageJSON json).getFile().getParentContainer() }
7070

7171
/**
72-
* Gets a reference to `dirname` that might cause information to be leaked.
73-
* That can happen if there is a `package.json` file in the same folder.
74-
* (It is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist.
72+
* Gets a reference to `dirname`, the home folder, the current working folder, or the root folder.
73+
* All of these might cause information to be leaked.
74+
*
75+
* For `dirname` that can happen if there is a `package.json` file in the same folder.
76+
* It is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist.
77+
*
78+
* For the root/home/working folder, they contain so much information that they must leak information somehow (e.g. ssh keys in the `~/.ssh` folder).
7579
*/
76-
DataFlow::Node dirname() {
80+
DataFlow::Node getALeakingFolder(string description) {
7781
exists(ModuleScope ms | result.asExpr() = ms.getVariable("__dirname").getAnAccess()) and
78-
result.getFile().getParentContainer() = getAPackageJSONFolder()
82+
result.getFile().getParentContainer() = getAPackageJSONFolder() and
83+
description = "the folder " + result.getFile().getParentContainer().getRelativePath()
84+
or
85+
result = DataFlow::moduleImport("os").getAMemberCall("homedir") and
86+
description = "the home folder "
87+
or
88+
result.mayHaveStringValue("/") and
89+
description = "the root folder"
90+
or
91+
result.getStringValue() = [".", "./"] and
92+
description = "the current working folder"
7993
or
80-
result.getAPredecessor() = dirname()
94+
result.getAPredecessor() = getALeakingFolder(description)
8195
or
8296
exists(StringOps::ConcatenationRoot root | root = result |
8397
root.getNumOperand() = 2 and
84-
root.getOperand(0) = dirname() and
98+
root.getOperand(0) = getALeakingFolder(description) and
8599
root.getOperand(1).getStringValue() = "/"
86100
)
87101
}
@@ -94,18 +108,17 @@ DataFlow::Node getAPrivateFolderPath(string description) {
94108
result = getANodeModulePath(path) and description = "the folder \"" + path + "\""
95109
)
96110
or
97-
result = dirname() and
98-
description = "the folder " + result.getFile().getParentContainer().getRelativePath()
99-
or
100-
result.getStringValue() = [".", "./"] and
101-
description = "the current working folder"
111+
result = getALeakingFolder(description)
102112
}
103113

104114
/**
105115
* Gest a call that serves the folder `path` to the public.
106116
*/
107117
DataFlow::CallNode servesAPrivateFolder(string description) {
108-
result = DataFlow::moduleMember("express", "static").getACall() and
118+
result = DataFlow::moduleMember(["express", "connect"], "static").getACall() and
119+
result.getArgument(0) = getAPrivateFolderPath(description)
120+
or
121+
result = DataFlow::moduleImport("serve-static").getACall() and
109122
result.getArgument(0) = getAPrivateFolderPath(description)
110123
}
111124

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
var express = require('express');
3+
4+
var app = express();
5+
6+
app.use('/node_modules', express.static(path.resolve(__dirname, '../node_modules')));
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
var express = require('express');
3+
4+
var app = express();
5+
6+
app.use("jquery", express.static('./node_modules/jquery/dist'));
7+
app.use("bootstrap", express.static('./node_modules/bootstrap/dist'));

javascript/ql/src/Security/CWE-327/BadRandomness.qhelp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,62 @@
44
<qhelp>
55
<overview>
66
<p>
7-
Placeholder
7+
Generating secure random numbers can be an important part of creating a
8+
secure software system. This can be done using APIs that create
9+
cryptographically secure random numbers.
10+
</p>
11+
<p>
12+
However, using some mathematical operations on these cryptographically
13+
secure random numbers can create biased results, where some outcomes
14+
are more likely than others.
15+
Such biased results can make it easier for an attacker to guess the random
16+
numbers, and thereby break the security of the software system.
817
</p>
9-
1018
</overview>
1119
<recommendation>
12-
1320
<p>
14-
Placeholder.
21+
Be very careful not to introduce bias when performing mathematical operations
22+
on cryptographically secure random numbers.
23+
</p>
24+
<p>
25+
If possible, avoid performing mathematical operations on cryptographically secure
26+
random numbers at all, and use a preexisting library instead.
1527
</p>
16-
1728
</recommendation>
1829
<example>
19-
2030
<p>
21-
Placeholder
31+
The example below uses the modulo operator to create an array of 10 random digits
32+
using random bytes as the source for randomness.
2233
</p>
34+
<sample src="examples/bad-random.js" />
35+
<p>
36+
The random byte is a uniformly random value between 0 and 255, and thus the result
37+
from using the modulo operator is slightly more likely to be between 0 and 5 than
38+
between 6 and 9.
39+
</p>
40+
<p>
41+
The issue has been fixed in the code below by using a library that correctly generates
42+
cryptographically secure random values.
43+
</p>
44+
<sample src="examples/bad-random-fixed.js" />
45+
<p>
46+
Alternatively, the issue can be fixed by fixing the math in the original code.
47+
In the code below the random byte is discarded if the value is greater than or equal to 250.
48+
Thus the modulo operator is used on a uniformly random number between 0 and 249, which
49+
results in a uniformly random digit between 0 and 9.
50+
</p>
51+
<sample src="examples/bad-random-fixed2.js" />
2352

2453
</example>
2554

55+
2656
<references>
27-
<li>NIST, FIPS 140 Annex a: <a href="http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf"> Approved Security Functions</a>.</li>
28-
<li>NIST, SP 800-131A: <a href="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf"> Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths</a>.</li>
57+
<li>Stack Overflow: <a href="https://stackoverflow.com/questions/3956478/understanding-randomness">Understanding “randomness”</a>.</li>
58+
<li>OWASP: <a href="https://owasp.org/www-community/vulnerabilities/Insecure_Randomness">Insecure Randomness</a>.</li>
2959
<li>OWASP: <a
3060
href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#rule---use-strong-approved-authenticated-encryption">Rule
3161
- Use strong approved cryptographic algorithms</a>.
3262
</li>
33-
<li>Stack Overflow: <a href="https://stackoverflow.com/questions/3956478/understanding-randomness">Understanding “randomness”</a>.</li>
3463
</references>
3564

3665
</qhelp>

javascript/ql/src/Security/CWE-327/BadRandomness.ql

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @name Creating biased random numbers from cryptographically secure source.
2+
* @name Creating biased random numbers from a cryptographically secure source.
33
* @description Some mathematical operations on random numbers can cause bias in
44
* the results and compromise security.
55
* @kind problem
@@ -132,6 +132,18 @@ DataFlow::Node goodRandom(DataFlow::SourceNode source) {
132132
result = goodRandom(DataFlow::TypeTracker::end(), source)
133133
}
134134

135+
/**
136+
* Gets a node that is passed to a rounding function from `Math`, using type-backtracker `t`.
137+
*/
138+
DataFlow::Node isRounded(DataFlow::TypeBackTracker t) {
139+
t.start() and
140+
result = DataFlow::globalVarRef("Math").getAMemberCall(["round", "floor", "ceil"]).getArgument(0)
141+
or
142+
exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, isRounded(t2)))
143+
or
144+
InsecureRandomness::isAdditionalTaintStep(result, isRounded(t.continue()))
145+
}
146+
135147
/**
136148
* Gets a node that that produces a biased result from otherwise cryptographically secure random numbers produced by `source`.
137149
*/
@@ -153,10 +165,7 @@ DataFlow::Node badCrypto(string description, DataFlow::SourceNode source) {
153165
goodRandom(source).asExpr() = div.getLeftOperand() and
154166
description = "division and rounding the result" and
155167
not div.getRightOperand() = isPowerOfTwoMinusOne().asExpr() and // division by (2^n)-1 most of the time produces a uniformly random number between 0 and 1.
156-
DataFlow::globalVarRef("Math")
157-
.getAMemberCall(["round", "floor", "ceil"])
158-
.getArgument(0)
159-
.asExpr() = div
168+
div = isRounded(DataFlow::TypeBackTracker::end()).asExpr()
160169
)
161170
or
162171
// modulo - only bad if not by a power of 2 - and the result is not checked for bias
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const cryptoRandomString = require('crypto-random-string');
2+
3+
const digits = cryptoRandomString({length: 10, type: 'numeric'});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const crypto = require('crypto');
2+
3+
const digits = [];
4+
while (digits.length < 10) {
5+
const byte = crypto.randomBytes(1)[0];
6+
if (byte >= 250) {
7+
continue;
8+
}
9+
digits.push(byte % 10); // OK
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const crypto = require('crypto');
2+
3+
const digits = [];
4+
for (let i = 0; i < 10; i++) {
5+
digits.push(crypto.randomBytes(1)[0] % 10); // NOT OK
6+
}

0 commit comments

Comments
 (0)