mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
JS: Functionality from untrusted sources query (CWE-830)
This commit is contained in:
@@ -173,6 +173,24 @@ module HTML {
|
||||
DocumentElement() { getName() = "html" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An HTML `<iframe>` element.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <iframe src="https://test.local/somepage.html"></iframe>
|
||||
* ```
|
||||
*/
|
||||
class IframeElement extends Element {
|
||||
IframeElement() { getName() = "iframe" }
|
||||
|
||||
/**
|
||||
* Gets the value of the `src` attribute.
|
||||
*/
|
||||
string getSourcePath() { result = getAttributeByName("src").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An HTML `<script>` element.
|
||||
*
|
||||
@@ -207,6 +225,11 @@ module HTML {
|
||||
*/
|
||||
string getSourcePath() { result = getAttributeByName("src").getValue() }
|
||||
|
||||
/**
|
||||
* Gets the value of the `integrity` attribute.
|
||||
*/
|
||||
string getIntegrityDigest() { result = getAttributeByName("integrity").getValue() }
|
||||
|
||||
/**
|
||||
* Gets the folder relative to which the `src` attribute is resolved.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Including functionality from an external source via an http link may
|
||||
allow an attacker to inject malicious code via a MITM (man-in-the-middle) attack.
|
||||
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
|
||||
When including external pages or behaviour, use <em>https</em> links (instead of http)
|
||||
to be certain that you are getting a response from the intended server, not
|
||||
someone else.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
Using http links is unsafe because the request sent may be intercepted by an attacker,
|
||||
and malicious data may be sent back in reply.
|
||||
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<references>
|
||||
|
||||
<li>
|
||||
|
||||
MDN — Subresource Integrity: <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity">
|
||||
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @name Inclusion of untrusted functionality by a HTML element.
|
||||
* @description Including untrusted functionality by a HTML element
|
||||
* opens up for potential man-in-the-middle attacks.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.1
|
||||
* @precision high
|
||||
* @id js/functionality-from-untrusted-source
|
||||
* @tags security
|
||||
* external/cwe/cwe-830
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.HTML
|
||||
|
||||
bindingset[host]
|
||||
predicate isAllowedHost(string host) { host.toLowerCase().regexpMatch("localhost(:[0-9]+)?/.*") }
|
||||
|
||||
bindingset[path]
|
||||
predicate isUntrustedSourcePath(string path) {
|
||||
path.substring(0, 2) = "//"
|
||||
or
|
||||
exists(string hostPath | hostPath = path.regexpCapture("http://(.*)", 1) |
|
||||
not isAllowedHost(hostPath)
|
||||
)
|
||||
}
|
||||
|
||||
abstract class IncludesUntrustedContent extends HTML::Element {
|
||||
IncludesUntrustedContent() { this = this }
|
||||
|
||||
/** Gets an explanation why this source is untrusted. */
|
||||
abstract string getProblem();
|
||||
}
|
||||
|
||||
/** A script element that refers to untrusted content. */
|
||||
class ScriptElementWithUntrustedContent extends IncludesUntrustedContent, HTML::ScriptElement {
|
||||
ScriptElementWithUntrustedContent() {
|
||||
isUntrustedSourcePath(this.getSourcePath()) and
|
||||
not exists(string digest | not digest = "" | this.getIntegrityDigest() = digest)
|
||||
}
|
||||
|
||||
override string getProblem() {
|
||||
result = "script elements should use an https link and/or use the integrity attribute"
|
||||
}
|
||||
}
|
||||
|
||||
/** An iframe element that includes untrusted content. */
|
||||
class IframeElementWithUntrustedContent extends HTML::IframeElement, IncludesUntrustedContent {
|
||||
IframeElementWithUntrustedContent() { isUntrustedSourcePath(this.getSourcePath()) }
|
||||
|
||||
override string getProblem() { result = "iframe elements should use an https link" }
|
||||
}
|
||||
|
||||
from IncludesUntrustedContent s, string problem
|
||||
where problem = s.getProblem()
|
||||
select s, "HTML-element imports untrusted content (" + problem + ")"
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.html:6:9:6:56 | <script>...</> | HTML-element imports untrusted content (script elements should use an https link and/or use the integrity attribute) |
|
||||
| test.html:9:9:9:58 | <iframe>...</> | HTML-element imports untrusted content (iframe elements should use an https link) |
|
||||
| test.html:11:9:11:53 | <iframe>...</> | HTML-element imports untrusted content (iframe elements should use an https link) |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-830/FunctionalityFromUntrustedSource.ql
|
||||
13
javascript/ql/test/query-tests/Security/CWE-830/test.html
Normal file
13
javascript/ql/test/query-tests/Security/CWE-830/test.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<script src="http://test.local/foo.js"></script>> <!-- NOT OK -->
|
||||
<script src="http://test.local/foo.js" integrity="some-integrity-hash"></script>> <!-- OK (integrity digest present) -->
|
||||
<script src="https://test.local/bar.js"></script>> <!-- OK (https) -->
|
||||
<iframe src="http://test.local/foo.html"></iframe> <!-- NOT OK -->
|
||||
<iframe src="https://test.local/foo.html"></iframe> <!-- OK (https) -->
|
||||
<iframe src="//test.local/foo.html"></iframe> <!-- NOT OK (protocol-relative url) -->
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user