Skip to content

Commit 552b7ad

Browse files
authored
Merge pull request github#3765 from asger-semmle/js-team-sprint-merge2
JS: Merge js-team-sprint
2 parents a5a3573 + b4f75ef commit 552b7ad

File tree

94 files changed

+2814
-104
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+2814
-104
lines changed

change-notes/1.25/analysis-javascript.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [jGrowl](https://github.com/stanlemon/jGrowl)
1212
- [jQuery](https://jquery.com/)
1313
- [marsdb](https://www.npmjs.com/package/marsdb)
14+
- [micro](https://www.npmjs.com/package/micro/)
1415
- [minimongo](https://www.npmjs.com/package/minimongo/)
1516
- [mssql](https://www.npmjs.com/package/mssql)
1617
- [mysql](https://www.npmjs.com/package/mysql)
@@ -20,6 +21,7 @@
2021
- [sqlite](https://www.npmjs.com/package/sqlite)
2122
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
2223
- [ssh2](https://www.npmjs.com/package/ssh2)
24+
- [yargs](https://www.npmjs.com/package/yargs)
2325
- [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
2426

2527
* TypeScript 3.9 is now supported.
@@ -35,6 +37,13 @@
3537
| Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. |
3638
| 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. |
3739
| 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. |
40+
| 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. |
41+
| 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. |
42+
| 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. |
43+
| 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. |
44+
| 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. |
45+
| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highlights locations where SSL certificate validation is disabled. Results are shown on LGTM by default. |
46+
| Incomplete multi-character sanitization (`js/incomplete-multi-character-sanitization`) | correctness, security, external/cwe/cwe-20, external/cwe/cwe-116 | Highlights sanitizers that fail to remove dangerous substrings completely. Results are shown on LGTM by default. |
3847

3948
## Changes to existing queries
4049

javascript/config/suites/javascript/security

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,33 @@
1313
+ semmlecode-javascript-queries/Security/CWE-078/ShellCommandInjectionFromEnvironment.ql: /Security/CWE/CWE-078
1414
+ semmlecode-javascript-queries/Security/CWE-079/ReflectedXss.ql: /Security/CWE/CWE-079
1515
+ semmlecode-javascript-queries/Security/CWE-079/StoredXss.ql: /Security/CWE/CWE-079
16-
+ semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079
1716
+ semmlecode-javascript-queries/Security/CWE-079/UnsafeJQueryPlugin.ql: /Security/CWE/CWE-079
17+
+ semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079
1818
+ semmlecode-javascript-queries/Security/CWE-089/SqlInjection.ql: /Security/CWE/CWE-089
1919
+ semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094
20+
+ semmlecode-javascript-queries/Security/CWE-094/ImproperCodeSanitization.ql: /Security/CWE/CWE-094
2021
+ semmlecode-javascript-queries/Security/CWE-094/UnsafeDynamicMethodAccess.ql: /Security/CWE/CWE-094
21-
+ semmlecode-javascript-queries/Security/CWE-116/IncompleteSanitization.ql: /Security/CWE/CWE-116
22-
+ semmlecode-javascript-queries/Security/CWE-116/IncompleteHtmlAttributeSanitization.ql: /Security/CWE/CWE-116
2322
+ semmlecode-javascript-queries/Security/CWE-116/DoubleEscaping.ql: /Security/CWE/CWE-116
23+
+ semmlecode-javascript-queries/Security/CWE-116/IncompleteHtmlAttributeSanitization.ql: /Security/CWE/CWE-116
24+
+ semmlecode-javascript-queries/Security/CWE-116/IncompleteMultiCharacterSanitization.ql: /Security/CWE/CWE-116
25+
+ semmlecode-javascript-queries/Security/CWE-116/IncompleteSanitization.ql: /Security/CWE/CWE-116
2426
+ semmlecode-javascript-queries/Security/CWE-134/TaintedFormatString.ql: /Security/CWE/CWE-134
27+
+ semmlecode-javascript-queries/Security/CWE-200/PrivateFileExposure.ql: /Security/CWE/CWE-200
2528
+ semmlecode-javascript-queries/Security/CWE-201/PostMessageStar.ql: /Security/CWE/CWE-201
2629
+ semmlecode-javascript-queries/Security/CWE-209/StackTraceExposure.ql: /Security/CWE/CWE-209
27-
+ semmlecode-javascript-queries/Security/CWE-312/CleartextStorage.ql: /Security/CWE/CWE-312
30+
+ semmlecode-javascript-queries/Security/CWE-295/DisablingCertificateValidation.ql: /Security/CWE/CWE-295
31+
+ semmlecode-javascript-queries/Security/CWE-312/BuildArtifactLeak.ql: /Security/CWE/CWE-312
2832
+ semmlecode-javascript-queries/Security/CWE-312/CleartextLogging.ql: /Security/CWE/CWE-312
33+
+ semmlecode-javascript-queries/Security/CWE-312/CleartextStorage.ql: /Security/CWE/CWE-312
2934
+ semmlecode-javascript-queries/Security/CWE-313/PasswordInConfigurationFile.ql: /Security/CWE/CWE-313
35+
+ semmlecode-javascript-queries/Security/CWE-327/BadRandomness.ql: /Security/CWE/CWE-327
3036
+ semmlecode-javascript-queries/Security/CWE-327/BrokenCryptoAlgorithm.ql: /Security/CWE/CWE-327
3137
+ semmlecode-javascript-queries/Security/CWE-338/InsecureRandomness.ql: /Security/CWE/CWE-338
3238
+ semmlecode-javascript-queries/Security/CWE-346/CorsMisconfigurationForCredentials.ql: /Security/CWE/CWE-346
3339
+ semmlecode-javascript-queries/Security/CWE-352/MissingCsrfMiddleware.ql: /Security/CWE/CWE-352
34-
+ semmlecode-javascript-queries/Security/CWE-400/RemotePropertyInjection.ql: /Security/CWE/CWE-400
3540
+ semmlecode-javascript-queries/Security/CWE-400/PrototypePollution.ql: /Security/CWE/CWE-400
3641
+ semmlecode-javascript-queries/Security/CWE-400/PrototypePollutionUtility.ql: /Security/CWE/CWE-400
42+
+ semmlecode-javascript-queries/Security/CWE-400/RemotePropertyInjection.ql: /Security/CWE/CWE-400
3743
+ semmlecode-javascript-queries/Security/CWE-502/UnsafeDeserialization.ql: /Security/CWE/CWE-502
3844
+ semmlecode-javascript-queries/Security/CWE-506/HardcodedDataInterpretedAsCode.ql: /Security/CWE/CWE-506
3945
+ semmlecode-javascript-queries/Security/CWE-601/ClientSideUrlRedirect.ql: /Security/CWE/CWE-601
@@ -48,6 +54,7 @@
4854
+ semmlecode-javascript-queries/Security/CWE-798/HardcodedCredentials.ql: /Security/CWE/CWE-798
4955
+ semmlecode-javascript-queries/Security/CWE-807/ConditionalBypass.ql: /Security/CWE/CWE-807
5056
+ semmlecode-javascript-queries/Security/CWE-807/DifferentKindsComparisonBypass.ql: /Security/CWE/CWE-807
57+
+ semmlecode-javascript-queries/Security/CWE-829/InsecureDownload.ql: /Security/CWE/CWE-829
5158
+ semmlecode-javascript-queries/Security/CWE-834/LoopBoundInjection.ql: /Security/CWE/CWE-834
5259
+ semmlecode-javascript-queries/Security/CWE-843/TypeConfusionThroughParameterTampering.ql: /Security/CWE/CWE-834
5360
+ semmlecode-javascript-queries/Security/CWE-916/InsufficientPasswordHash.ql: /Security/CWE/CWE-916
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Using string concatenation to construct JavaScript code can be error-prone, or in the worst
9+
case, enable code injection if an input is constructed by an attacker.
10+
</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>
15+
If using <code>JSON.stringify</code> or a HTML sanitizer to sanitize a string inserted into
16+
JavaScript code, then make sure to perform additional sanitization or remove potentially
17+
dangerous characters.
18+
</p>
19+
</recommendation>
20+
21+
<example>
22+
<p>
23+
The example below constructs a function that assigns the number 42 to the property <code>key</code>
24+
on an object <code>obj</code>. However, if <code>key</code> contains <code>&lt;/script&gt;</code>, then
25+
the generated code will break out of a <code>&lt;/script&gt;</code> if inserted into a
26+
<code>&lt;/script&gt;</code> tag.
27+
</p>
28+
<sample src="examples/ImproperCodeSanitization.js" />
29+
<p>
30+
The issue has been fixed by escaping potentially dangerous characters, as shown below.
31+
</p>
32+
<sample src="examples/ImproperCodeSanitizationFixed.js" />
33+
</example>
34+
35+
<references>
36+
<li>OWASP: <a href="https://www.owasp.org/index.php/Code_Injection">Code Injection</a>.</li>
37+
</references>
38+
</qhelp>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* @name Improper code sanitization
3+
* @description Escaping code as HTML does not provide protection against code injection.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @precision high
7+
* @id js/bad-code-sanitization
8+
* @tags security
9+
* external/cwe/cwe-094
10+
* external/cwe/cwe-079
11+
* external/cwe/cwe-116
12+
*/
13+
14+
import javascript
15+
import semmle.javascript.security.dataflow.ImproperCodeSanitization::ImproperCodeSanitization
16+
import DataFlow::PathGraph
17+
private import semmle.javascript.heuristics.HeuristicSinks
18+
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
19+
20+
/**
21+
* Gets a type-tracked instance of `RemoteFlowSource` using type-tracker `t`.
22+
*/
23+
private DataFlow::Node remoteFlow(DataFlow::TypeTracker t) {
24+
t.start() and
25+
result instanceof RemoteFlowSource
26+
or
27+
exists(DataFlow::TypeTracker t2, DataFlow::Node prev | prev = remoteFlow(t2) |
28+
t2 = t.smallstep(prev, result)
29+
or
30+
any(TaintTracking::AdditionalTaintStep dts).step(prev, result) and
31+
t = t2
32+
)
33+
}
34+
35+
/**
36+
* Gets a type-tracked reference to a `RemoteFlowSource`.
37+
*/
38+
private DataFlow::Node remoteFlow() { result = remoteFlow(DataFlow::TypeTracker::end()) }
39+
40+
/**
41+
* Gets a type-back-tracked instance of a code injection sink using type-tracker `t`.
42+
*/
43+
private DataFlow::Node endsInCodeInjectionSink(DataFlow::TypeBackTracker t) {
44+
t.start() and
45+
(
46+
result instanceof CodeInjection::Sink
47+
or
48+
result instanceof HeuristicCodeInjectionSink and
49+
not result instanceof StringOps::ConcatenationRoot // the heuristic CodeInjection sink looks for string-concats, we are not interrested in those here.
50+
)
51+
or
52+
exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, endsInCodeInjectionSink(t2)))
53+
}
54+
55+
/**
56+
* Gets a reference to to a data-flow node that ends in a code injection sink.
57+
*/
58+
private DataFlow::Node endsInCodeInjectionSink() {
59+
result = endsInCodeInjectionSink(DataFlow::TypeBackTracker::end())
60+
}
61+
62+
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
63+
where
64+
cfg.hasFlowPath(source, sink) and
65+
// Basic detection of duplicate results with `js/code-injection`.
66+
not (
67+
sink.getNode().(StringOps::ConcatenationLeaf).getRoot() = endsInCodeInjectionSink() and
68+
remoteFlow() = source.getNode().(DataFlow::InvokeNode).getAnArgument()
69+
)
70+
select sink.getNode(), source, sink, "$@ flows to here and is used to construct code.",
71+
source.getNode(), "Improperly sanitized value"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
function createObjectWrite() {
2+
const assignment = `obj[${JSON.stringify(key)}]=42`;
3+
return `(function(){${assignment}})` // NOT OK
4+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const charMap = {
2+
'<': '\\u003C',
3+
'>' : '\\u003E',
4+
'/': '\\u002F',
5+
'\\': '\\\\',
6+
'\b': '\\b',
7+
'\f': '\\f',
8+
'\n': '\\n',
9+
'\r': '\\r',
10+
'\t': '\\t',
11+
'\0': '\\0',
12+
'\u2028': '\\u2028',
13+
'\u2029': '\\u2029'
14+
};
15+
16+
function escapeUnsafeChars(str) {
17+
return str.replace(/[<>\b\f\n\r\t\0\u2028\u2029]/g, x => charMap[x])
18+
}
19+
20+
function createObjectWrite() {
21+
const assignment = `obj[${escapeUnsafeChars(JSON.stringify(key))}]=42`;
22+
return `(function(){${assignment}})` // OK
23+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<include src="IncompleteSanitization.qhelp" />
7+
8+
</qhelp>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* @name Incomplete multi-character sanitization
3+
* @description A sanitizer that removes a sequence of characters may reintroduce the dangerous sequence.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @precision high
7+
* @id js/incomplete-multi-character-sanitization
8+
* @tags correctness
9+
* security
10+
* external/cwe/cwe-116
11+
* external/cwe/cwe-20
12+
*/
13+
14+
import javascript
15+
import semmle.javascript.security.IncompleteBlacklistSanitizer
16+
17+
predicate isDangerous(RegExpTerm t) {
18+
// path traversals
19+
t.getAMatchedString() = ["..", "/..", "../"]
20+
or
21+
exists(RegExpTerm start |
22+
start = t.(RegExpSequence).getAChild() and
23+
start.getConstantValue() = "." and
24+
start.getSuccessor().getConstantValue() = "." and
25+
not [start.getPredecessor(), start.getSuccessor().getSuccessor()].getConstantValue() = "."
26+
)
27+
or
28+
// HTML comments
29+
t.getAMatchedString() = "<!--"
30+
or
31+
// HTML scripts
32+
t.getAMatchedString().regexpMatch("(?i)<script.*")
33+
or
34+
exists(RegExpSequence seq | seq = t |
35+
t.getChild(0).getConstantValue() = "<" and
36+
// the `cript|scrip` case has been observed in the wild, not sure what the goal of that pattern is...
37+
t
38+
.getChild(0)
39+
.getSuccessor+()
40+
.getAMatchedString()
41+
.regexpMatch("(?i)iframe|script|cript|scrip|style")
42+
)
43+
or
44+
// HTML attributes
45+
exists(string dangerousPrefix | dangerousPrefix = ["ng-", "on"] |
46+
t.getAMatchedString().regexpMatch("(i?)" + dangerousPrefix + "[a-z]+")
47+
or
48+
exists(RegExpTerm start, RegExpTerm event | start = t.getAChild() |
49+
start.getConstantValue().regexpMatch("(?i)[^a-z]*" + dangerousPrefix) and
50+
event = start.getSuccessor() and
51+
exists(RegExpTerm quantified | quantified = event.(RegExpQuantifier).getChild(0) |
52+
quantified
53+
.(RegExpCharacterClass)
54+
.getAChild()
55+
.(RegExpCharacterRange)
56+
.isRange(["a", "A"], ["z", "Z"]) or
57+
[quantified, quantified.(RegExpRange).getAChild()].(RegExpCharacterClassEscape).getValue() =
58+
"w"
59+
)
60+
)
61+
)
62+
}
63+
64+
from StringReplaceCall replace, RegExpTerm regexp, RegExpTerm dangerous
65+
where
66+
[replace.getRawReplacement(), replace.getCallback(1).getAReturn()].mayHaveStringValue("") and
67+
replace.isGlobal() and
68+
regexp = replace.getRegExp().getRoot() and
69+
dangerous.getRootTerm() = regexp and
70+
isDangerous(dangerous) and
71+
// avoid anchored terms
72+
not exists(RegExpAnchor a | a.getRootTerm() = regexp) and
73+
// avoid flagging wrappers
74+
not (
75+
dangerous instanceof RegExpAlt or
76+
dangerous instanceof RegExpGroup
77+
) and
78+
// don't flag replace operations in a loop
79+
not replace.getReceiver().getALocalSource() = replace
80+
select replace, "The replaced string may still contain a substring that starts matching at $@.",
81+
dangerous, dangerous.toString()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
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.
14+
</p>
15+
</overview>
16+
17+
<recommendation>
18+
<p>
19+
Limit which folders of static files are served from a web server.
20+
</p>
21+
</recommendation>
22+
23+
<example>
24+
<p>
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.
28+
</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"/>
35+
</example>
36+
37+
<references>
38+
<li>OWASP: <a href="https://www.owasp.org/index.php/Top_10-2017_A3-Sensitive_Data_Exposure">Sensitive Data Exposure</a>.</li>
39+
</references>
40+
</qhelp>

0 commit comments

Comments
 (0)