Merge branch 'main' into js/shared-dataflow-merge-main

This commit is contained in:
Asger F
2024-11-20 14:05:03 +01:00
2341 changed files with 169482 additions and 106842 deletions

View File

@@ -25,7 +25,7 @@ app.get('/check-with-axios', req => {
} else {
axios.get(baseURL + req.params.tainted); // OK
}
// Blacklists are not safe
if (!req.query.tainted.match(/^[/\.%]+$/)) {
axios.get("test.com/" + req.query.tainted); // SSRF
@@ -39,8 +39,29 @@ app.get('/check-with-axios', req => {
}
axios.get("test.com/" + req.query.tainted); // OK - False Positive
if (req.query.tainted.matchAll(/^[0-9a-z]+$/g)) { // letters and numbers
axios.get("test.com/" + req.query.tainted); // OK
}
if (req.query.tainted.matchAll(/^[0-9a-z\-_]+$/g)) { // letters, numbers, - and _
axios.get("test.com/" + req.query.tainted); // OK
}
});
const isValidPath = path => path.match(/^[0-9a-z]+$/);
const isInBlackList = path => path.match(/^[/\.%]+$/);
app.get('/check-with-axios', req => {
const baseURL = "test.com/"
if (isValidPathMatchAll(req.params.tainted) ) {
axios.get(baseURL + req.params.tainted); // OK
}
if (!isValidPathMatchAll(req.params.tainted) ) {
axios.get(baseURL + req.params.tainted); // NOT OK - SSRF
} else {
axios.get(baseURL + req.params.tainted); // OK
}
});
const isValidPathMatchAll = path => path.matchAll(/^[0-9a-z]+$/g);

View File

@@ -96,4 +96,16 @@
sink(["source"].filter((x) => x).pop()); // NOT OK
sink(["source"].filter((x) => !!x).pop()); // NOT OK
var arr8 = [];
arr8 = arr8.toSpliced(0, 0, "source");
sink(arr8.pop()); // NOT OK
var arr8_variant = [];
arr8_variant = arr8_variant.toSpliced(0, 0, "safe", "source");
arr8_variant.pop();
sink(arr8_variant.pop()); // NOT OK
var arr8_spread = [];
arr8_spread = arr8_spread.toSpliced(0, 0, ...arr);
sink(arr8_spread.pop()); // NOT OK
});

View File

@@ -0,0 +1,3 @@
| tst.js:1:12:1:38 | '^http: ... le.com' | is a regular expression |
| tst.js:4:37:4:43 | 'regex' | is a regular expression |
| tst.js:9:37:9:43 | 'regex' | is a regular expression |

View File

@@ -0,0 +1,4 @@
import javascript
from RegExpPatternSource regex
select regex, "is a regular expression"

View File

@@ -0,0 +1,11 @@
new RegExp('^http://test\.example.com'); // NOT OK
function detectRegexViaSplice(string) {
let found = getMyThing().search('regex'); // NOT OK
arr.splice(found, 1);
};
function detectRegexViaToSpliced(string) {
let found = getMyThing().search('regex'); // NOT OK
arr.toSpliced(found, 1);
};

View File

@@ -38,4 +38,40 @@ function test(x, y) {
let j = [];
j[j.length] = source();
sink(j); // NOT OK
let k = [];
let kSpliced = k.toSpliced(x, y, source());
sink(k); // OK
sink(kSpliced); // NOT OK
let l = [];
l = l.toSpliced(x, y, source());
sink(l); // NOT OK
let m = [];
m = m.toSpliced(q, source(), y);
sink(m); // OK
let n = [];
n = n.toSpliced(source(), x);
sink(n); // OK
let o = [];
o = o.toSpliced(x, source());
sink(o); // OK
let p = [];
p = p.toSpliced(source(), x, y);
sink(p); // OK
let q = [];
q.splice(x, y, ...source());
sink(q); // NOT OK
let r = [];
let rSpliced = r.toSpliced(x, y, ...source());
sink(rSpliced); // NOT OK
sink(r); // OK
r = r.toSpliced(x, y, ...source());
sink(r); // NOT OK
}

View File

@@ -57,4 +57,19 @@ function test() {
}
tagged`foo ${"safe"} bar ${x} baz`;
sink(x.reverse()); // NOT OK
sink(x.toSpliced()); // NOT OK
sink(x.toSorted()) // NOT OK
const xSorted = x.toSorted();
sink(xSorted) // NOT OK
sink(x.toReversed()) // NOT OK
const xReversed = x.toReversed();
sink(xReversed) // NOT OK
sink(x.with()) // NOT OK
const xWith = x.with();
sink(xWith) // NOT OK
}

View File

@@ -0,0 +1,5 @@
<template>
<Blah :colonProp="x" @atProp="x" />
<Blah :colonField.field="x" />
</template>
<script></script>

View File

@@ -76,6 +76,7 @@ component
| single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue |
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue |
| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue |
| special-syntax.vue:0:0:0:0 | special-syntax.vue |
| tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) |
| tst.js:12:1:16:2 | new Vue ... \\t}),\\n}) |
| tst.js:18:1:27:2 | Vue.com ... }\\n\\t}\\n}) |
@@ -126,6 +127,10 @@ templateElement
| single-file-component-5.vue:2:5:18:9 | <p>...</> |
| single-file-component-5.vue:4:1:16:9 | <script>...</> |
| single-file-component-5.vue:17:1:18:8 | <style>...</> |
| special-syntax.vue:1:1:4:11 | <template>...</> |
| special-syntax.vue:2:3:2:37 | <blah>...</> |
| special-syntax.vue:3:3:3:32 | <blah>...</> |
| special-syntax.vue:5:1:5:17 | <script>...</> |
xssSink
| compont-with-route.vue:2:8:2:21 | v-html=dataA |
| single-component-file-1.vue:2:8:2:21 | v-html=dataA |
@@ -161,3 +166,15 @@ remoteFlowSource
| router.js:30:5:30:14 | from.query |
| router.js:34:5:34:12 | to.query |
| router.js:35:5:35:14 | from.query |
parseErrors
attribute
| compont-with-route.vue:2:8:2:21 | v-html=dataA | v-html |
| single-component-file-1.vue:2:8:2:21 | v-html=dataA | v-html |
| single-file-component-2.vue:2:8:2:21 | v-html=dataA | v-html |
| single-file-component-3.vue:2:8:2:21 | v-html=dataA | v-html |
| single-file-component-3.vue:4:9:4:49 | src=./single-file-component-3-script.js | src |
| single-file-component-4.vue:2:8:2:21 | v-html=dataA | v-html |
| single-file-component-5.vue:2:8:2:21 | v-html=dataA | v-html |
| special-syntax.vue:2:9:2:22 | :colonProp=x | :colonProp |
| special-syntax.vue:2:24:2:34 | @atProp=x | @atProp |
| special-syntax.vue:3:9:3:29 | :colonField.field=x | :colonField.field |

View File

@@ -20,3 +20,7 @@ query predicate templateElement(Vue::Template::Element template) { any() }
query predicate xssSink(DomBasedXss::Sink s) { any() }
query RemoteFlowSource remoteFlowSource() { any() }
query predicate parseErrors(JSParseError err) { exists(err) }
query predicate attribute(HTML::Attribute attrib, string name) { attrib.getName() = name }

View File

@@ -0,0 +1,3 @@
| default |
| remote |
| request |

View File

@@ -0,0 +1,7 @@
private import codeql.threatmodels.ThreatModels
from string kind
where
knownThreatModel(kind) and
currentThreatModel(kind)
select kind

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,38 @@
import javascript
import testUtilities.InlineExpectationsTest
class TestSourcesConfiguration extends TaintTracking::Configuration {
TestSourcesConfiguration() { this = "TestSources" }
override predicate isSource(DataFlow::Node source) { source instanceof ThreatModelSource }
override predicate isSink(DataFlow::Node sink) {
exists(CallExpr call |
call.getAnArgument() = sink.asExpr() and
call.getCalleeName() = "SINK"
)
}
}
private module InlineTestSources implements TestSig {
string getARelevantTag() { result in ["hasFlow", "threat-source"] }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node sink |
any(TestSourcesConfiguration c).hasFlow(_, sink) and
value = "" and
location = sink.getLocation() and
tag = "hasFlow" and
element = sink.toString()
)
or
exists(ThreatModelSource source |
value = source.getThreatModel() and
location = source.getLocation() and
tag = "threat-source" and
element = source.toString()
)
}
}
import MakeTest<InlineTestSources>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
import 'dummy';
var x = process.env['foo']; // $ threat-source=environment
SINK(x); // $ hasFlow
var y = process.argv[2]; // $ threat-source=commandargs
SINK(y); // $ hasFlow
// Accessing command line arguments using yargs
// https://www.npmjs.com/package/yargs/v/17.7.2
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const argv = yargs(hideBin(process.argv)).argv; // $ threat-source=commandargs
SINK(argv.foo); // $ hasFlow
// older version
// https://www.npmjs.com/package/yargs/v/7.1.2
const yargsOld = require('yargs');
const argvOld = yargsOld.argv; // $ threat-source=commandargs
SINK(argvOld.foo); // $ hasFlow
// Accessing command line arguments using yargs-parser
const yargsParser = require('yargs-parser');
const src = process.argv.slice(2); // $ threat-source=commandargs
const parsedArgs = yargsParser(src);
SINK(parsedArgs.foo); // $ hasFlow
// Accessing command line arguments using minimist
const minimist = require('minimist');
const args = minimist(process.argv.slice(2)); // $ threat-source=commandargs
SINK(args.foo); // $ hasFlow
// Accessing command line arguments using commander
const { Command } = require('commander'); // $ SPURIOUS: threat-source=commandargs
const program = new Command();
program.parse(process.argv); // $ threat-source=commandargs
SINK(program.opts().foo); // $ hasFlow SPURIOUS: threat-source=commandargs
// ------ reading from database ------
// Accessing database using mysql
const mysql = require('mysql');
const connection = mysql.createConnection({host: 'localhost'});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) { // $ threat-source=database
if (error) throw error;
SINK(results); // $ hasFlow
SINK(results[0]); // $ hasFlow
SINK(results[0].solution); // $ hasFlow
});
// ------ reading from file ------
// Accessing file contents using fs
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => { // $ threat-source=file
SINK(data); // $ hasFlow
});
// Accessing file contents using fs.readFileSync
const fileContent = fs.readFileSync('file.txt', 'utf8'); // $ threat-source=file
SINK(fileContent); // $ hasFlow
// Accessing file contents using fs.promises
fs.promises.readFile('file.txt', 'utf8').then((data) => { // $ threat-source=file
SINK(data); // $ hasFlow
});
// Accessing file contents using fs.createReadStream
const readStream = fs.createReadStream('file.txt');
readStream.on('data', (chunk) => { // $ threat-source=file
SINK(chunk); // $ hasFlow
});
const data = readStream.read(); // $ threat-source=file
SINK(data); // $ hasFlow
// using readline
const readline = require('readline');
const rl_file = readline.createInterface({
input: fs.createReadStream('file.txt')
});
rl_file.on("line", (line) => { // $ SPURIOUS: threat-source=stdin MISSING: threat-source=file
SINK(line); // $ hasFlow
});
// ------ reading from stdin ------
// Accessing stdin using process.stdin
process.stdin.on('data', (data) => { // $ threat-source=stdin
SINK(data); // $ hasFlow
});
const stdin_line = process.stdin.read(); // $ threat-source=stdin
SINK(stdin_line); // $ hasFlow
// Accessing stdin using readline
const readline = require('readline');
const rl_stdin = readline.createInterface({
input: process.stdin
});
rl_stdin.question('<question>', (answer) => { // $ threat-source=stdin
SINK(answer); // $ hasFlow
});
function handler(answer) { // $ threat-source=stdin
SINK(answer); // $ hasFlow
}
rl_stdin.question('<question>', handler);
rl_stdin.on("line", (line) => { // $ threat-source=stdin
SINK(line); // $ hasFlow
});

View File

@@ -25,3 +25,4 @@
| tst-IncompleteHostnameRegExp.js:53:14:53:35 | test.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:53:13:53:36 | 'test.' ... e.com$' | here |
| tst-IncompleteHostnameRegExp.js:55:14:55:38 | ^http://test.example.com | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:55:13:55:39 | '^http: ... le.com' | here |
| tst-IncompleteHostnameRegExp.js:59:5:59:20 | foo.example\\.com | This regular expression has an unescaped '.' before 'example\\.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:59:2:59:32 | /^(foo. ... ever)$/ | here |
| tst-IncompleteHostnameRegExp.js:61:18:61:41 | ^http://test.example.com | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:61:17:61:42 | "^http: ... le.com" | here |

View File

@@ -57,4 +57,6 @@
/^http:\/\/(..|...)\.example\.com\/index\.html/; // OK, wildcards are intentional
/^http:\/\/.\.example\.com\/index\.html/; // OK, the wildcard is intentional
/^(foo.example\.com|whatever)$/; // kinda OK - one disjunction doesn't even look like a hostname
if (s.matchAll("^http://test.example.com")) {} // NOT OK
});

View File

@@ -59,3 +59,12 @@
| tst-UnanchoredUrlRegExp.js:26:3:26:22 | "^https?://good.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
| tst-UnanchoredUrlRegExp.js:35:2:35:32 | /https? ... 0-9]+)/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
| tst-UnanchoredUrlRegExp.js:77:11:77:32 | /vimeo\\ ... 0-9]+)/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
| tst-UnanchoredUrlRegExp.js:111:50:111:68 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
| tst-UnanchoredUrlRegExp.js:112:61:112:79 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
| tst-UnanchoredUrlRegExp.js:113:50:113:69 | "^https?://good.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
| tst-UnanchoredUrlRegExp.js:114:50:114:72 | /^https ... d.com/g | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
| tst-UnanchoredUrlRegExp.js:115:50:115:94 | "(^http ... 2.com)" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
| tst-UnanchoredUrlRegExp.js:116:50:116:93 | "(https ... e.com)" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
| tst-UnanchoredUrlRegExp.js:117:50:117:59 | "good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
| tst-UnanchoredUrlRegExp.js:118:50:118:68 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
| tst-UnanchoredUrlRegExp.js:119:50:119:73 | "https? ... m:8080" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |

View File

@@ -105,4 +105,27 @@
/\.com|\.org/; // OK, has no domain name
/example\.com|whatever/; // OK, the other disjunction doesn't match a hostname
// MatchAll test cases:
// Vulnerable patterns
if ("http://evil.com/?http://good.com".matchAll("https?://good.com")) {} // NOT OK
if ("http://evil.com/?http://good.com".matchAll(new RegExp("https?://good.com"))) {} // NOT OK
if ("http://evil.com/?http://good.com".matchAll("^https?://good.com")) {} // NOT OK - missing post-anchor
if ("http://evil.com/?http://good.com".matchAll(/^https?:\/\/good.com/g)) {} // NOT OK - missing post-anchor
if ("http://evil.com/?http://good.com".matchAll("(^https?://good1.com)|(^https?://good2.com)")) {} // NOT OK - missing post-anchor
if ("http://evil.com/?http://good.com".matchAll("(https?://good.com)|(^https?://goodie.com)")) {} // NOT OK - missing post-anchor
if ("http://evil.com/?http://good.com".matchAll("good.com")) {} // NOT OK - missing protocol
if ("http://evil.com/?http://good.com".matchAll("https?://good.com")) {} // NOT OK
if ("http://evil.com/?http://good.com".matchAll("https?://good.com:8080")) {} // NOT OK
// Non-vulnerable patterns
if ("something".matchAll("other")) {} // OK
if ("something".matchAll("x.commissary")) {} // OK
if ("http://evil.com/?http://good.com".matchAll("^https?://good.com$")) {} // OK
if ("http://evil.com/?http://good.com".matchAll(new RegExp("^https?://good.com$"))) {} // OK
if ("http://evil.com/?http://good.com".matchAll("^https?://good.com/$")) {} // OK
if ("http://evil.com/?http://good.com".matchAll(/^https?:\/\/good.com\/$/)) {} // OK
if ("http://evil.com/?http://good.com".matchAll("(^https?://good1.com$)|(^https?://good2.com$)")) {} // OK
if ("http://evil.com/?http://good.com".matchAll("(https?://good.com$)|(^https?://goodie.com$)")) {} // OK
});

View File

@@ -407,3 +407,25 @@ app.get('/join-spread', (req, res) => {
fs.readFileSync(pathModule.join('foo', ...req.query.x.split('/'))); // NOT OK
fs.readFileSync(pathModule.join(...req.query.x.split('/'))); // NOT OK
});
app.get('/dotdot-matchAll-regexp', (req, res) => {
let path = pathModule.normalize(req.query.x);
if (pathModule.isAbsolute(path))
return;
fs.readFileSync(path); // NOT OK
if (!path.matchAll(/\./)) {
fs.readFileSync(path); // OK
}
if (!path.matchAll(/\.\./)) {
fs.readFileSync(path); // OK
}
if (!path.matchAll(/\.\.\//)) {
fs.readFileSync(path); // OK
}
if (!path.matchAll(/\.\.\/foo/)) {
fs.readFileSync(path); // NOT OK
}
if (!path.matchAll(/(\.\.\/|\.\.\\)/)) {
fs.readFileSync(path); // OK
}
});

View File

@@ -0,0 +1,17 @@
nodes
| test.js:4:5:4:29 | temp |
| test.js:4:12:4:22 | process.env |
| test.js:4:12:4:22 | process.env |
| test.js:4:12:4:29 | process.env['foo'] |
| test.js:7:14:7:61 | 'SELECT ... + temp |
| test.js:7:14:7:61 | 'SELECT ... + temp |
| test.js:7:58:7:61 | temp |
edges
| test.js:4:5:4:29 | temp | test.js:7:58:7:61 | temp |
| test.js:4:12:4:22 | process.env | test.js:4:12:4:29 | process.env['foo'] |
| test.js:4:12:4:22 | process.env | test.js:4:12:4:29 | process.env['foo'] |
| test.js:4:12:4:29 | process.env['foo'] | test.js:4:5:4:29 | temp |
| test.js:7:58:7:61 | temp | test.js:7:14:7:61 | 'SELECT ... + temp |
| test.js:7:58:7:61 | temp | test.js:7:14:7:61 | 'SELECT ... + temp |
#select
| test.js:7:14:7:61 | 'SELECT ... + temp | test.js:4:12:4:22 | process.env | test.js:7:14:7:61 | 'SELECT ... + temp | This query string depends on a $@. | test.js:4:12:4:22 | process.env | user-provided value |

View File

@@ -0,0 +1,6 @@
extensions:
- addsTo:
pack: codeql/threat-models
extensible: threatModelConfiguration
data:
- ["local", true, 0]

View File

@@ -0,0 +1 @@
Security/CWE-089/SqlInjection.ql

View File

@@ -0,0 +1,9 @@
const mysql = require('mysql');
const pool = mysql.createPool(getConfig());
let temp = process.env['foo'];
pool.getConnection(function(err, connection) {
connection.query({
sql: 'SELECT * FROM `books` WHERE `author` = ' + temp, // NOT OK
}, function(error, results, fields) {});
});

View File

@@ -116,4 +116,15 @@ const server4 = http.createServer((req, res) => {
});
server.start();
}
});
});
const serverMatchAll = http.createServer((req, res) => {
let username = url.parse(req.url, true).query.username;
let otherStr = username.matchAll(/.*/g)[0]; // BAD
console.log(otherStr);
});
const serverMatchAl2l = http.createServer((req, res) => {
const result = url.parse(req.url, true).query.username.matchAll(/(\d+)/g); // BAD
console.log("First captured group:", RegExp.$1);
});

View File

@@ -1 +1,2 @@
Security/CWE-611/Xxe.ql
query: Security/CWE-611/Xxe.ql
postprocess: testUtilities/InlineExpectationsTestQuery.ql

View File

@@ -1,5 +1,5 @@
function test() {
var src = document.location.search;
var src = document.location.search; // $ Source=search
if (window.DOMParser) {
// OK: DOMParser only expands internal general entities
@@ -8,10 +8,10 @@ function test() {
var parser;
try {
// NOT OK: XMLDOM expands external entities by default
(new ActiveXObject("Microsoft.XMLDOM")).loadXML(src);
(new ActiveXObject("Microsoft.XMLDOM")).loadXML(src); // $ Alert=search
} catch (e) {
// NOT OK: MSXML expands external entities by default
(new ActiveXObject("Msxml2.DOMDocument")).loadXML(src);
(new ActiveXObject("Msxml2.DOMDocument")).loadXML(src); // $ Alert=search
}
}
}

View File

@@ -1,20 +1,20 @@
const express = require('express');
const libxmljs = require('libxmljs');
express().get('/some/path', function(req) {
express().get('/some/path', function (req) {
// NOT OK: unguarded entity expansion
libxmljs.parseXml(req.param("some-xml"), { noent: true });
libxmljs.parseXml(req.param("some-xml"), { noent: true }); // $ Alert
});
express().post('/some/path', function(req, res) {
express().post('/some/path', function (req, res) {
// NOT OK: unguarded entity expansion
libxmljs.parseXml(req.param("some-xml"), { noent: true });
libxmljs.parseXml(req.param("some-xml"), { noent: true }); // $ Alert
// NOT OK: unguarded entity expansion
libxmljs.parseXmlString(req.param("some-xml"), {noent:true})
libxmljs.parseXmlString(req.param("some-xml"), { noent: true }) // $ Alert
// NOT OK: unguarded entity expansion
libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:true})
libxmljs.parseXmlString(req.files.products.data.toString('utf8'), { noent: true })// $ Source=files $ Alert=files
// OK - no entity expansion
libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:false})
libxmljs.parseXmlString(req.files.products.data.toString('utf8'), { noent: false })
});

View File

@@ -1,7 +1,7 @@
const express = require('express');
const libxmljs = require('libxmljs');
express().get('/some/path', function(req) {
express().get('/some/path', function (req) {
const parser = new libxmljs.SaxParser();
parser.parseString(req.param("some-xml")); // NOT OK: the SAX parser expands external entities by default
parser.parseString(req.param("some-xml")); // $ Alert: the SAX parser expands external entities by default
});

View File

@@ -1,7 +1,7 @@
const express = require('express');
const libxmljs = require('libxmljs');
express().get('/some/path', function(req) {
express().get('/some/path', function (req) {
const parser = new libxmljs.SaxPushParser();
parser.push(req.param("some-xml")); // NOT OK: the SAX parser expands external entities by default
parser.push(req.param("some-xml")); // $ Alert: the SAX parser expands external entities by default
});

View File

@@ -0,0 +1,21 @@
/**
* @kind test-postprocess
*/
private import javascript
private import codeql.util.test.InlineExpectationsTest as T
private import internal.InlineExpectationsTestImpl
import T::TestPostProcessing
import T::TestPostProcessing::Make<Impl, Input>
private module Input implements T::TestPostProcessing::InputSig<Impl> {
string getRelativeUrl(Location location) {
exists(File f, int startline, int startcolumn, int endline, int endcolumn |
location.hasLocationInfo(_, startline, startcolumn, endline, endcolumn) and
f = location.getFile()
|
result =
f.getRelativePath() + ":" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn
)
}
}