Merge pull request #3197 from erik-krogh/NormalPathSanitizer

Approved by asgerf
This commit is contained in:
semmle-qlci 2020-04-03 16:33:18 +01:00 коммит произвёл GitHub
Родитель 676da02118 9c2053168b
Коммит a8098a2b2d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 192 добавлений и 1 удалений

Просмотреть файл

@ -206,6 +206,20 @@ module TaintedPath {
dstlabel.isNormalized()
)
or
// foo.replace(/(\.\.\/)*/, "") and similar
exists(DotDotSlashPrefixRemovingReplace call |
src = call.getInput() and
dst = call.getOutput()
|
// the 4 possible combinations of normalized + relative for `srclabel`, and the possible values for `dstlabel` in each case.
srclabel.isNonNormalized() and srclabel.isRelative() // raw + relative -> any()
or
srclabel.isNormalized() and srclabel.isAbsolute() and srclabel = dstlabel // normalized + absolute -> normalized + absolute
or
srclabel.isNonNormalized() and srclabel.isAbsolute() and dstlabel.isAbsolute() // raw + absolute -> raw/normalized + absolute
// normalized + relative -> none()
)
or
// path.join()
exists(DataFlow::CallNode join, int n |
join = NodeJSLib::Path::moduleMember("join").getACall()

Просмотреть файл

@ -225,7 +225,8 @@ module TaintedPath {
term.getAMatchedString() = "/" or
term.getAMatchedString() = "." or
term.getAMatchedString() = ".."
)
) and
not this instanceof DotDotSlashPrefixRemovingReplace
}
/**
@ -239,6 +240,57 @@ module TaintedPath {
DataFlow::Node getOutput() { result = output }
}
/**
* A call that removes all instances of "../" in the prefix of the string.
*/
class DotDotSlashPrefixRemovingReplace extends DataFlow::CallNode {
DataFlow::Node input;
DataFlow::Node output;
DotDotSlashPrefixRemovingReplace() {
this.getCalleeName() = "replace" and
input = getReceiver() and
output = this and
exists(RegExpLiteral literal, RegExpTerm term |
getArgument(0).getALocalSource().asExpr() = literal and
(term instanceof RegExpStar or term instanceof RegExpPlus) and
term.getChild(0) = getADotDotSlashMatcher()
|
literal.getRoot() = term
or
exists(RegExpSequence seq | seq.getNumChild() = 2 and literal.getRoot() = seq |
seq.getChild(0) instanceof RegExpCaret and
seq.getChild(1) = term
)
)
}
/**
* Gets the input path to be sanitized.
*/
DataFlow::Node getInput() { result = input }
/**
* Gets the path where prefix "../" has been removed.
*/
DataFlow::Node getOutput() { result = output }
}
/**
* Gets a RegExpTerm that matches a variation of "../".
*/
private RegExpTerm getADotDotSlashMatcher() {
result.getAMatchedString() = "../"
or
exists(RegExpSequence seq | seq = result |
seq.getChild(0).getConstantValue() = "." and
seq.getChild(1).getConstantValue() = "." and
seq.getAChild().getAMatchedString() = "/"
)
or
exists(RegExpGroup group | result = group | group.getChild(0) = getADotDotSlashMatcher())
}
/**
* A call that removes all "." or ".." from a path, without also removing all forward slashes.
*/

Просмотреть файл

@ -1277,6 +1277,48 @@ nodes
| TaintedPath.js:186:29:186:57 | path.re ... /g, '') |
| TaintedPath.js:186:29:186:57 | path.re ... /g, '') |
| TaintedPath.js:186:29:186:57 | path.re ... /g, '') |
| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:202:50:202:53 | path |
| normalizedPaths.js:11:7:11:27 | path |
| normalizedPaths.js:11:7:11:27 | path |
| normalizedPaths.js:11:7:11:27 | path |
@ -4385,6 +4427,22 @@ edges
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:186:29:186:32 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:201:40:201:43 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:7:173:48 | path | TaintedPath.js:202:50:202:53 | path |
| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query |
| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query |
| TaintedPath.js:173:14:173:37 | url.par ... , true) | TaintedPath.js:173:14:173:43 | url.par ... ).query |
@ -4561,6 +4619,62 @@ edges
| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') |
| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') |
| TaintedPath.js:186:29:186:32 | path | TaintedPath.js:186:29:186:57 | path.re ... /g, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:43 | path | TaintedPath.js:201:40:201:73 | path.re ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:201:40:201:73 | path.re ... +/, '') | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:29:202:54 | pathMod ... e(path) | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| TaintedPath.js:202:50:202:53 | path | TaintedPath.js:202:29:202:54 | pathMod ... e(path) |
| normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path |
| normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path |
| normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path |
@ -6391,6 +6505,8 @@ edges
| TaintedPath.js:184:29:184:53 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:184:29:184:53 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value |
| TaintedPath.js:185:29:185:51 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:185:29:185:51 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value |
| TaintedPath.js:186:29:186:57 | path.re ... /g, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:186:29:186:57 | path.re ... /g, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value |
| TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:201:29:201:73 | "prefix ... +/, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value |
| TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | TaintedPath.js:173:24:173:30 | req.url | TaintedPath.js:202:29:202:84 | pathMod ... +/, '') | This path depends on $@. | TaintedPath.js:173:24:173:30 | req.url | a user-provided value |
| normalizedPaths.js:13:19:13:22 | path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:13:19:13:22 | path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value |
| normalizedPaths.js:14:19:14:29 | './' + path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:14:19:14:29 | './' + path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value |
| normalizedPaths.js:15:19:15:38 | path + '/index.html' | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:15:19:15:38 | path + '/index.html' | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value |

Просмотреть файл

@ -191,4 +191,13 @@ var server = http.createServer(function(req, res) {
res.write(fs.readFileSync(path.replace(/\./g, ''))); // OK
res.write(fs.readFileSync(path.replace(/\.\.|BLA/g, ''))); // OK
}
// removing of "../" from prefix.
res.write(fs.readFileSync("prefix" + pathModule.normalize(path).replace(/^(\.\.[\/\\])+/, ''))); // OK
res.write(fs.readFileSync("prefix" + pathModule.normalize(path).replace(/(\.\.[\/\\])+/, ''))); // OK
res.write(fs.readFileSync("prefix" + pathModule.normalize(path).replace(/(\.\.\/)+/, ''))); // OK
res.write(fs.readFileSync("prefix" + pathModule.normalize(path).replace(/(\.\.\/)*/, ''))); // OK
res.write(fs.readFileSync("prefix" + path.replace(/^(\.\.[\/\\])+/, ''))); // NOT OK - not normalized
res.write(fs.readFileSync(pathModule.normalize(path).replace(/^(\.\.[\/\\])+/, ''))); // NOT OK (can be absolute)
});