JavaScript: Add query IncompleteUrlSchemeCheck.ql.

This commit is contained in:
Max Schaefer
2019-11-08 11:22:45 +00:00
parent 3a7f9a588d
commit ab583b7994
10 changed files with 127 additions and 0 deletions

View File

@@ -28,6 +28,7 @@
| Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. | | Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. |
| Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. | | Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. |
| Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. | | Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. |
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | security, correctness, external/cwe/cwe-020 | Highlights checks for `javascript:` URLs that do not take `data:` or `vbscript:` URLs into account. |
## Changes to existing queries ## Changes to existing queries

View File

@@ -1,6 +1,7 @@
+ semmlecode-javascript-queries/DOM/TargetBlank.ql: /Security/CWE/CWE-200 + semmlecode-javascript-queries/DOM/TargetBlank.ql: /Security/CWE/CWE-200
+ semmlecode-javascript-queries/Electron/EnablingNodeIntegration.ql: /Security/CWE/CWE-094 + semmlecode-javascript-queries/Electron/EnablingNodeIntegration.ql: /Security/CWE/CWE-094
+ semmlecode-javascript-queries/Security/CWE-020/IncompleteHostnameRegExp.ql: /Security/CWE/CWE-020 + semmlecode-javascript-queries/Security/CWE-020/IncompleteHostnameRegExp.ql: /Security/CWE/CWE-020
+ semmlecode-javascript-queries/Security/CWE-020/IncompleteUrlSchemeCheck.ql: /Security/CWE/CWE-020
+ semmlecode-javascript-queries/Security/CWE-020/IncompleteUrlSubstringSanitization.ql: /Security/CWE/CWE-020 + semmlecode-javascript-queries/Security/CWE-020/IncompleteUrlSubstringSanitization.ql: /Security/CWE/CWE-020
+ semmlecode-javascript-queries/Security/CWE-020/IncorrectSuffixCheck.ql: /Security/CWE/CWE-020 + semmlecode-javascript-queries/Security/CWE-020/IncorrectSuffixCheck.ql: /Security/CWE/CWE-020
+ semmlecode-javascript-queries/Security/CWE-020/MissingRegExpAnchor.ql: /Security/CWE/CWE-020 + semmlecode-javascript-queries/Security/CWE-020/MissingRegExpAnchor.ql: /Security/CWE/CWE-020

View File

@@ -0,0 +1,42 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
URLs starting with <code>javascript:</code> can be used to encode JavaScript code to be executed
when the URL is visited. While this is a powerful mechanism for creating feature-rich and responsive
web applications, it is also a potential security risk: if the URL comes from an untrusted source,
it might contain harmful JavaScript code. For this reason, many frameworks and libraries first check
the URL scheme of any untrusted URL, and reject URLs with the <code>javascript:</code> scheme.
</p>
<p>
However, the <code>data:</code> and <code>vbscript:</code> schemes can be used to represent
executable code in a very similar way, so any validation logic that checks against
<code>javascript:</code> but not against <code>data:</code> and <code>vbscript:</code> is likely to
be insufficient.
</p>
</overview>
<recommendation>
<p>
Add checks covering both <code>data:</code> and <code>vbscript:</code>.
</p>
</recommendation>
<example>
<p>
The following function validates a (presumably untrusted) URL <code>url</code>. If it starts with
<code>javascript:</code> (case-insensitive and potentially preceded by whitespace), the harmless
placeholder URL <code>about:blank</code> is returned to prevent code injection; otherwise
<code>url</code> itself is returned.
</p>
<sample src="examples/IncompleteUrlSchemeCheck.js"/>
<p>
While this check provides partial projection, it should be extended to cover <code>data:</code>
and <code>vbscript:</code> as well:
</p>
<sample src="examples/IncompleteUrlSchemeCheckGood.js"/>
</example>
<references>
<li>WHATWG: <a href="https://wiki.whatwg.org/wiki/URL_schemes">URL schemes</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,57 @@
/**
* @name Incomplete URL scheme check
* @description Checking for the "javascript:" URL scheme without also checking for "vbscript:"
* and "data:" suggests a logic error or even a security vulnerability.
* @kind problem
* @problem.severity warning
* @precision high
* @id js/incomplete-url-scheme-check
* @tags security
* correctness
* external/cwe/cwe-020
*/
import javascript
import semmle.javascript.dataflow.internal.AccessPaths
/** A URL scheme that can be used to represent executable code. */
class DangerousScheme extends string {
DangerousScheme() { this = "data:" or this = "javascript:" or this = "vbscript:" }
}
/** Gets a data-flow node that checks `nd` against the given `scheme`. */
DataFlow::Node schemeCheck(
DataFlow::Node nd, DangerousScheme scheme
) {
// check of the form `nd.startsWith(scheme)`
exists(StringOps::StartsWith sw | sw = result |
sw.getBaseString() = nd and
sw.getSubstring().mayHaveStringValue(scheme)
)
or
// propagate through trimming, case conversion, and regexp replace
exists(DataFlow::MethodCallNode stringop |
stringop.getMethodName().matches("trim%") or
stringop.getMethodName().matches("to%Case") or
stringop.getMethodName() = "replace"
|
result = schemeCheck(stringop, scheme) and
nd = stringop.getReceiver()
)
or
// propagate througb local data flow
result = schemeCheck(nd.getASuccessor(), scheme)
}
/** Gets a data-flow node that checks an instance of `ap` against the given `scheme`. */
DataFlow::Node schemeCheckOn(AccessPath ap, DangerousScheme scheme) {
result = schemeCheck(ap.getAnInstance().flow(), scheme)
}
from AccessPath ap, int n
where
n = strictcount(DangerousScheme s) and
strictcount(DangerousScheme s | exists(schemeCheckOn(ap, s))) < n
select schemeCheckOn(ap, "javascript:"),
"This check does not consider " +
strictconcat(DangerousScheme s | not exists(schemeCheckOn(ap, s)) | s, " and ") + "."

View File

@@ -0,0 +1,6 @@
function sanitizeUrl(url) {
let u = decodeURI(url).trim().toLowerCase();
if (u.startsWith("javascript:"))
return "about:blank";
return url;
}

View File

@@ -0,0 +1,6 @@
function sanitizeUrl(url) {
let u = decodeURI(url).trim().toLowerCase();
if (u.startsWith("javascript:") || u.startsWith("data:") || u.startsWith("vbscript:"))
return "about:blank";
return url;
}

View File

@@ -0,0 +1 @@
| IncompleteUrlSchemeCheck.js:3:9:3:35 | u.start ... ript:") | This check does not consider data: and vbscript:. |

View File

@@ -0,0 +1,6 @@
function sanitizeUrl(url) {
let u = decodeURI(url).trim().toLowerCase();
if (u.startsWith("javascript:"))
return "about:blank";
return url;
}

View File

@@ -0,0 +1 @@
Security/CWE-020/IncompleteUrlSchemeCheck.ql

View File

@@ -0,0 +1,6 @@
function sanitizeUrl(url) {
let u = decodeURI(url).trim().toLowerCase();
if (u.startsWith("javascript:") || u.startsWith("data:") || u.startsWith("vbscript:"))
return "about:blank";
return url;
}