JS: add query: js/stored-xss

This commit is contained in:
Esben Sparre Andreasen
2018-08-30 12:52:59 +02:00
parent e2fac8a03c
commit 33f98dd1a7
9 changed files with 218 additions and 0 deletions

View File

@@ -2,6 +2,7 @@
+ semmlecode-javascript-queries/Security/CWE-022/TaintedPath.ql: /Security/CWE/CWE-022
+ semmlecode-javascript-queries/Security/CWE-078/CommandInjection.ql: /Security/CWE/CWE-078
+ semmlecode-javascript-queries/Security/CWE-079/ReflectedXss.ql: /Security/CWE/CWE-079
+ semmlecode-javascript-queries/Security/CWE-079/StoredXss.ql: /Security/CWE/CWE-079
+ semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079
+ semmlecode-javascript-queries/Security/CWE-089/SqlInjection.ql: /Security/CWE/CWE-089
+ semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094

View File

@@ -0,0 +1,63 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Directly using uncontrolled stored value (for example, file names) to
create HTML content without properly sanitizing the input first,
allows for a cross-site scripting vulnerability.
</p>
<p>
This kind of vulnerability is also called <i>stored</i> cross-site
scripting, to distinguish it from other types of cross-site scripting.
</p>
</overview>
<recommendation>
<p>
To guard against cross-site scripting, consider using contextual
output encoding/escaping before using uncontrolled stored values to
create HTML content, or one of the other solutions that are mentioned
in the references.
</p>
</recommendation>
<example>
<p>
The following example code writes file names directly to a HTTP
response. This leaves the website vulnerable to cross-site scripting,
if an attacker can choose the file names on the disk.
</p>
<sample src="examples/StoredXss.js" />
<p>
Sanitizing the file names prevents the vulnerability:
</p>
<sample src="examples/StoredXssGood.js" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet">XSS
(Cross Site Scripting) Prevention Cheat Sheet</a>.
</li>
<li>
OWASP
<a href="https://www.owasp.org/index.php/Types_of_Cross-Site_Scripting">Types of Cross-Site
Scripting</a>.
</li>
<li>
Wikipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Stored cross-site scripting
* @description Using uncontrolled stored values in HTML allows for
* a stored cross-site scripting vulnerability.
* @kind problem
* @problem.severity error
* @precision high
* @id js/stored-xss
* @tags security
* external/cwe/cwe-079
* external/cwe/cwe-116
*/
import javascript
import semmle.javascript.security.dataflow.StoredXss::StoredXss
from Configuration xss, DataFlow::Node source, DataFlow::Node sink
where xss.hasFlow(source, sink)
select sink, "Stored cross-site scripting vulnerability due to $@.",
source, "stored value"

View File

@@ -0,0 +1,13 @@
var express = require('express'),
fs = require('fs');
express().get('/list-directory', function(req, res) {
fs.readdir('/public', function (error, fileNames) {
var list = '<ul>';
fileNames.forEach(fileName => {
list += '<li>' + fileName '</li>'; // BAD: `fileName` can contain HTML elements
});
list += '</ul>'
res.send(list);
});
});

View File

@@ -0,0 +1,14 @@
var express = require('express'),
fs = require('fs'),
escape = require('escape-html');
express().get('/list-directory', function(req, res) {
fs.readdir('/public', function (error, fileNames) {
var list = '<ul>';
fileNames.forEach(fileName => {
list += '<li>' + escape(fileName) '</li>'; // GOOD: escaped `fileName` can not contain HTML elements
});
list += '</ul>'
res.send(list);
});
});

View File

@@ -0,0 +1,62 @@
/**
* Provides a taint-tracking configuration for reasoning about stored
* cross-site scripting vulnerabilities.
*/
import javascript
import semmle.javascript.security.dataflow.RemoteFlowSources
import semmle.javascript.security.dataflow.ReflectedXss as ReflectedXss
import semmle.javascript.security.dataflow.DomBasedXss as DomBasedXss
module StoredXss {
/**
* A data flow source for XSS vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for XSS vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for XSS vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A taint-tracking configuration for reasoning about XSS.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "StoredXss" }
override predicate isSource(DataFlow::Node source) {
source instanceof Source
}
override predicate isSink(DataFlow::Node sink) {
sink instanceof Sink
}
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
}
/** A file name, considered as a flow source for stored XSS. */
class FileNameSourceAsSource extends Source {
FileNameSourceAsSource() {
this instanceof FileNameSource
}
}
/** An ordinary XSS sink, considered as a flow sink for stored XSS. */
class XssSinkAsSink extends Sink {
XssSinkAsSink() {
this instanceof ReflectedXss::ReflectedXss::Sink or
this instanceof DomBasedXss::DomBasedXss::Sink
}
}
}

View File

@@ -0,0 +1,4 @@
| xss-through-filenames.js:8:18:8:23 | files1 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:7:43:7:48 | files1 | stored value |
| xss-through-filenames.js:26:19:26:24 | files1 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value |
| xss-through-filenames.js:33:19:33:24 | files2 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value |
| xss-through-filenames.js:37:19:37:24 | files3 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value |

View File

@@ -0,0 +1 @@
Security/CWE-079/StoredXss.ql

View File

@@ -0,0 +1,40 @@
var http = require('http');
var fs = require('fs');
var express = require('express');
express().get('/', function(req, res) {
fs.readdir("/myDir", function (error, files1) {
res.send(files1); // NOT OK
});
});
/**
* The essence of a real world vulnerability.
*/
http.createServer(function (req, res) {
function format(files2) {
var files3 = [];
files2.sort(sort).forEach(function (file) {
files3.push('<li>' + file + '</li>');
});
return files3.join('');
}
fs.readdir("/myDir", function (error, files1) {
res.write(files1); // NOT OK
var dirs = [];
var files2 = [];
files1.forEach(function (file) {
files2.push(file);
});
res.write(files2); // NOT OK
var files3 = format(files2);
res.write(files3); // NOT OK
});
});