Merge pull request #6433 from asgerf/js/tainted-url-suffix

Approved by erik-krogh
This commit is contained in:
CodeQL CI
2021-08-12 00:28:46 -07:00
committed by GitHub
14 changed files with 292 additions and 36 deletions

View File

@@ -0,0 +1,4 @@
lgtm,codescanning
* The `js/xss` query now reports fewer false positives in cases where
`location.hash` flows to a jQuery `$()` call in a way that preserves
the `#` prefix.

View File

@@ -34,7 +34,7 @@ module StringConcatenation {
or
exists(DataFlow::ArrayCreationNode array, DataFlow::MethodCallNode call |
call = array.getAMethodCall("join") and
call.getArgument(0).mayHaveStringValue("") and
(call.getArgument(0).mayHaveStringValue("") or call.getNumArgument() = 0) and
(
// step from array element to array
result = array.getElement(n) and

View File

@@ -802,4 +802,11 @@ module StringOps {
override boolean getPolarity() { result = polarity }
}
}
/**
* Gets the name of a string method which performs substring extraction.
*
* These are `substring`, `substr`, and `slice`.
*/
string substringMethodName() { result = ["substring", "substr", "slice"] }
}

View File

@@ -0,0 +1,114 @@
/**
* Provides a flow label for reasoning about URLs with a tainted query and fragment part,
* which we collectively refer to as the "suffix" of the URL.
*/
import javascript
/**
* Provides a flow label for reasoning about URLs with a tainted query and fragment part,
* which we collectively refer to as the "suffix" of the URL.
*/
module TaintedUrlSuffix {
private import DataFlow
/**
* The flow label representing a URL with a tainted query and fragment part.
*
* Can also be accessed using `TaintedUrlSuffix::label()`.
*/
class TaintedUrlSuffixLabel extends FlowLabel {
TaintedUrlSuffixLabel() { this = "tainted-url-suffix" }
}
/**
* Gets the flow label representing a URL with a tainted query and fragment part.
*/
FlowLabel label() { result instanceof TaintedUrlSuffixLabel }
/** Holds for `pred -> succ` is a step of form `x -> x.p` */
private predicate isSafeLocationProp(DataFlow::PropRead read) {
// Ignore properties that refer to the scheme, domain, port, auth, or path.
exists(string name | name = read.getPropertyName() |
name = "protocol" or
name = "scheme" or
name = "host" or
name = "hostname" or
name = "domain" or
name = "origin" or
name = "port" or
name = "path" or
name = "pathname" or
name = "username" or
name = "password" or
name = "auth"
)
}
/**
* Holds if there is a flow step `src -> dst` involving the URL suffix taint label.
*
* This handles steps through string operations, promises, URL parsers, and URL accessors.
*/
predicate step(Node src, Node dst, FlowLabel srclbl, FlowLabel dstlbl) {
// Inherit all ordinary taint steps except `x -> x.p` steps
srclbl = label() and
dstlbl = label() and
TaintTracking::sharedTaintStep(src, dst) and
not isSafeLocationProp(dst)
or
// Transition from URL suffix to full taint when extracting the query/fragment part.
srclbl = label() and
dstlbl.isTaint() and
(
exists(MethodCallNode call, string name |
src = call.getReceiver() and
dst = call and
name = call.getMethodName()
|
// Substring that is not a prefix
name = StringOps::substringMethodName() and
not call.getArgument(0).getIntValue() = 0
or
// Split around '#' or '?' and extract the suffix
name = "split" and
call.getArgument(0).getStringValue() = ["#", "?"] and
not exists(call.getAPropertyRead("0")) // Avoid false flow to the prefix
or
// Replace '#' and '?' with nothing
name = "replace" and
call.getArgument(0).getStringValue() = ["#", "?"] and
call.getArgument(1).getStringValue() = ""
or
// The `get` call in `url.searchParams.get(x)` and `url.hashParams.get(x)`
// The step should be safe since nothing else reachable by this flow label supports a method named 'get'.
name = "get"
or
// Methods on URL objects from the Closure library
name = "getDecodedQuery"
or
name = "getFragment"
or
name = "getParameterValue"
or
name = "getParameterValues"
or
name = "getQueryData"
)
or
exists(PropRead read |
src = read.getBase() and
dst = read and
// Unlike the `search` property, the `query` property from `url.parse` does not include the `?`.
read.getPropertyName() = "query"
)
or
// Assume calls to regexp.exec always extract query/fragment parameters.
exists(MethodCallNode call |
call = any(RegExpLiteral re).flow().(DataFlow::SourceNode).getAMethodCall("exec") and
src = call.getArgument(0) and
dst = call
)
)
}
}

View File

@@ -69,7 +69,7 @@ module ClientSideUrlRedirect {
// exclude all splits where only the prefix is accessed, which is safe for url-redirects.
not exists(PropAccess pacc | mce = pacc.getBase() | pacc.getPropertyName() = "0")
or
(methodName = "substring" or methodName = "substr" or methodName = "slice") and
methodName = StringOps::substringMethodName() and
// exclude `location.href.substring(0, ...)` and similar, which can
// never refer to the query string
not mce.getArgument(0).(NumberLiteral).getIntValue() = 0

View File

@@ -4,6 +4,7 @@
*/
import javascript
private import semmle.javascript.security.TaintedUrlSuffix
module DomBasedXss {
import DomBasedXssCustomizations::DomBasedXss
@@ -61,26 +62,14 @@ module DomBasedXss {
not source = [DOM::locationRef(), DOM::locationRef().getAPropertyRead()] and
label.isTaint()
or
source = DOM::locationSource() and
label.isData() // Require transformation before reaching sink
or
source = DOM::locationRef().getAPropertyRead(["hash", "search"]) and
label.isData() // Require transformation before reaching sink
source = [DOM::locationSource(), DOM::locationRef().getAPropertyRead(["hash", "search"])] and
label = TaintedUrlSuffix::label()
}
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
sink instanceof JQueryHtmlOrSelectorSink and label.isTaint()
}
override predicate isAdditionalFlowStep(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
TaintTracking::sharedTaintStep(pred, succ) and
predlbl.isData() and
succlbl.isTaint()
}
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node)
or
@@ -91,15 +80,16 @@ module DomBasedXss {
guard instanceof SanitizerGuard
}
override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) {
DomBasedXss::isOptionallySanitizedEdge(pred, succ)
override predicate isAdditionalFlowStep(
DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
) {
TaintedUrlSuffix::step(src, trg, inlbl, outlbl)
or
// Avoid stepping from location -> location.hash, as the .hash is already treated as a source
// (with a different flow label)
exists(DataFlow::PropRead read |
read = DOM::locationRef().getAPropertyRead(["hash", "search"]) and
pred = read.getBase() and
succ = read
exists(DataFlow::Node operator |
StringConcatenation::taintStep(src, trg, operator, _) and
StringConcatenation::getOperand(operator, 0).getStringValue() = "<" + any(string s) and
inlbl = TaintedUrlSuffix::label() and
outlbl.isTaint()
)
}
}

View File

@@ -734,15 +734,9 @@ module TaintedPath {
exists(DataFlow::MethodCallNode mcn, string name |
srclabel = dstlabel and dst = mcn and mcn.calls(src, name)
|
exists(string substringMethodName |
substringMethodName = "substr" or
substringMethodName = "substring" or
substringMethodName = "slice"
|
name = substringMethodName and
// to avoid very dynamic transformations, require at least one fixed index
exists(mcn.getAnArgument().asExpr().getIntValue())
)
name = StringOps::substringMethodName() and
// to avoid very dynamic transformations, require at least one fixed index
exists(mcn.getAnArgument().asExpr().getIntValue())
or
exists(string argumentlessMethodName |
argumentlessMethodName = "toLocaleLowerCase" or

View File

@@ -100,9 +100,7 @@ module PolynomialReDoS {
root instanceof RegExpCharacterClassEscape
)
or
exists(string name | name = "slice" or name = "substring" or name = "substr" |
this.(DataFlow::MethodCallNode).getMethodName() = name
)
this.(DataFlow::MethodCallNode).getMethodName() = StringOps::substringMethodName()
}
}

View File

@@ -36,7 +36,10 @@
| tst.js:53:10:53:34 | `one ${ ... three` |
| tst.js:53:19:53:23 | two |
| tst.js:71:14:71:18 | "two" |
| tst.js:77:15:77:37 | ["one", ... three"] |
| tst.js:77:23:77:27 | "two" |
| tst.js:79:12:79:16 | array |
| tst.js:79:12:79:23 | array.join() |
| tst.js:87:5:87:14 | x += 'two' |
| tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x |

View File

@@ -36,7 +36,10 @@
| tst.js:53:10:53:34 | `one ${ ... three` |
| tst.js:53:19:53:23 | two |
| tst.js:71:14:71:18 | "two" |
| tst.js:77:15:77:37 | ["one", ... three"] |
| tst.js:77:23:77:27 | "two" |
| tst.js:79:12:79:16 | array |
| tst.js:79:12:79:23 | array.join() |
| tst.js:87:5:87:14 | x += 'two' |
| tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x |

View File

@@ -41,6 +41,8 @@ concatenation
| tst.js:49:10:49:20 | xs.join("") |
| tst.js:53:10:53:34 | `one ${ ... three` |
| tst.js:61:10:61:34 | `first ... } last` |
| tst.js:77:15:77:37 | ["one", ... three"] |
| tst.js:79:12:79:23 | array.join() |
| tst.js:87:5:87:14 | x |
| tst.js:87:5:87:14 | x += 'two' |
| tst.js:89:3:89:14 | x |
@@ -126,6 +128,10 @@ concatenationOperand
| tst.js:61:23:61:23 | x |
| tst.js:61:27:61:27 | x |
| tst.js:61:29:61:33 | last |
| tst.js:77:15:77:37 | ["one", ... three"] |
| tst.js:77:16:77:20 | "one" |
| tst.js:77:23:77:27 | "two" |
| tst.js:77:30:77:36 | "three" |
| tst.js:87:5:87:5 | x |
| tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x |
@@ -210,6 +216,9 @@ concatenationLeaf
| tst.js:61:23:61:23 | x |
| tst.js:61:27:61:27 | x |
| tst.js:61:29:61:33 | last |
| tst.js:77:16:77:20 | "one" |
| tst.js:77:23:77:27 | "two" |
| tst.js:77:30:77:36 | "three" |
| tst.js:87:5:87:5 | x |
| tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x |
@@ -334,6 +343,11 @@ concatenationNode
| tst.js:61:23:61:23 | x |
| tst.js:61:27:61:27 | x |
| tst.js:61:29:61:33 | last |
| tst.js:77:15:77:37 | ["one", ... three"] |
| tst.js:77:16:77:20 | "one" |
| tst.js:77:23:77:27 | "two" |
| tst.js:77:30:77:36 | "three" |
| tst.js:79:12:79:23 | array.join() |
| tst.js:87:5:87:5 | x |
| tst.js:87:5:87:14 | x |
| tst.js:87:5:87:14 | x += 'two' |
@@ -454,6 +468,10 @@ operand
| tst.js:61:10:61:34 | `first ... } last` | 2 | tst.js:61:23:61:23 | x |
| tst.js:61:10:61:34 | `first ... } last` | 3 | tst.js:61:27:61:27 | x |
| tst.js:61:10:61:34 | `first ... } last` | 4 | tst.js:61:29:61:33 | last |
| tst.js:77:15:77:37 | ["one", ... three"] | 0 | tst.js:77:16:77:20 | "one" |
| tst.js:77:15:77:37 | ["one", ... three"] | 1 | tst.js:77:23:77:27 | "two" |
| tst.js:77:15:77:37 | ["one", ... three"] | 2 | tst.js:77:30:77:36 | "three" |
| tst.js:79:12:79:23 | array.join() | 0 | tst.js:77:15:77:37 | ["one", ... three"] |
| tst.js:87:5:87:14 | x | 0 | tst.js:87:5:87:5 | x |
| tst.js:87:5:87:14 | x | 1 | tst.js:87:10:87:14 | 'two' |
| tst.js:87:5:87:14 | x += 'two' | 0 | tst.js:87:5:87:5 | x |
@@ -518,6 +536,8 @@ nextLeaf
| tst.js:61:19:61:19 | x | tst.js:61:23:61:23 | x |
| tst.js:61:23:61:23 | x | tst.js:61:27:61:27 | x |
| tst.js:61:27:61:27 | x | tst.js:61:29:61:33 | last |
| tst.js:77:16:77:20 | "one" | tst.js:77:23:77:27 | "two" |
| tst.js:77:23:77:27 | "two" | tst.js:77:30:77:36 | "three" |
| tst.js:87:5:87:5 | x | tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x | tst.js:89:8:89:14 | 'three' |
| tst.js:95:7:95:7 | x | tst.js:95:16:95:20 | 'two' |

View File

@@ -222,6 +222,31 @@ nodes
| jquery.js:16:38:16:52 | window.location |
| jquery.js:16:38:16:52 | window.location |
| jquery.js:16:38:16:63 | window. ... tring() |
| jquery.js:18:7:18:33 | hash |
| jquery.js:18:14:18:33 | window.location.hash |
| jquery.js:18:14:18:33 | window.location.hash |
| jquery.js:21:5:21:8 | hash |
| jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:22:5:22:8 | hash |
| jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:23:5:23:8 | hash |
| jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:24:5:24:8 | hash |
| jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:27:5:27:8 | hash |
| jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:28:5:28:26 | window. ... .search |
| jquery.js:28:5:28:26 | window. ... .search |
| jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jquery.js:34:13:34:16 | hash |
| jwt-server.js:7:9:7:35 | taint |
| jwt-server.js:7:17:7:35 | req.param("wobble") |
| jwt-server.js:7:17:7:35 | req.param("wobble") |
@@ -959,6 +984,30 @@ edges
| jquery.js:16:38:16:52 | window.location | jquery.js:16:38:16:63 | window. ... tring() |
| jquery.js:16:38:16:63 | window. ... tring() | jquery.js:16:19:16:64 | decodeU ... ring()) |
| jquery.js:16:38:16:63 | window. ... tring() | jquery.js:16:19:16:64 | decodeU ... ring()) |
| jquery.js:18:7:18:33 | hash | jquery.js:21:5:21:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:22:5:22:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:23:5:23:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:24:5:24:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:27:5:27:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:34:13:34:16 | hash |
| jquery.js:18:14:18:33 | window.location.hash | jquery.js:18:7:18:33 | hash |
| jquery.js:18:14:18:33 | window.location.hash | jquery.js:18:7:18:33 | hash |
| jquery.js:21:5:21:8 | hash | jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:21:5:21:8 | hash | jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:22:5:22:8 | hash | jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:22:5:22:8 | hash | jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:23:5:23:8 | hash | jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:23:5:23:8 | hash | jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:24:5:24:8 | hash | jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:24:5:24:8 | hash | jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:27:5:27:8 | hash | jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:27:5:27:8 | hash | jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:34:13:34:16 | hash | jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jquery.js:34:13:34:16 | hash | jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jwt-server.js:7:9:7:35 | taint | jwt-server.js:9:16:9:20 | taint |
| jwt-server.js:7:17:7:35 | req.param("wobble") | jwt-server.js:7:9:7:35 | taint |
| jwt-server.js:7:17:7:35 | req.param("wobble") | jwt-server.js:7:9:7:35 | taint |
@@ -1493,6 +1542,13 @@ edges
| jquery.js:14:19:14:58 | decodeU ... n.hash) | jquery.js:14:38:14:57 | window.location.hash | jquery.js:14:19:14:58 | decodeU ... n.hash) | Cross-site scripting vulnerability due to $@. | jquery.js:14:38:14:57 | window.location.hash | user-provided value |
| jquery.js:15:19:15:60 | decodeU ... search) | jquery.js:15:38:15:59 | window. ... .search | jquery.js:15:19:15:60 | decodeU ... search) | Cross-site scripting vulnerability due to $@. | jquery.js:15:38:15:59 | window. ... .search | user-provided value |
| jquery.js:16:19:16:64 | decodeU ... ring()) | jquery.js:16:38:16:52 | window.location | jquery.js:16:19:16:64 | decodeU ... ring()) | Cross-site scripting vulnerability due to $@. | jquery.js:16:38:16:52 | window.location | user-provided value |
| jquery.js:21:5:21:21 | hash.substring(1) | jquery.js:18:14:18:33 | window.location.hash | jquery.js:21:5:21:21 | hash.substring(1) | Cross-site scripting vulnerability due to $@. | jquery.js:18:14:18:33 | window.location.hash | user-provided value |
| jquery.js:22:5:22:25 | hash.su ... (1, 10) | jquery.js:18:14:18:33 | window.location.hash | jquery.js:22:5:22:25 | hash.su ... (1, 10) | Cross-site scripting vulnerability due to $@. | jquery.js:18:14:18:33 | window.location.hash | user-provided value |
| jquery.js:23:5:23:18 | hash.substr(1) | jquery.js:18:14:18:33 | window.location.hash | jquery.js:23:5:23:18 | hash.substr(1) | Cross-site scripting vulnerability due to $@. | jquery.js:18:14:18:33 | window.location.hash | user-provided value |
| jquery.js:24:5:24:17 | hash.slice(1) | jquery.js:18:14:18:33 | window.location.hash | jquery.js:24:5:24:17 | hash.slice(1) | Cross-site scripting vulnerability due to $@. | jquery.js:18:14:18:33 | window.location.hash | user-provided value |
| jquery.js:27:5:27:25 | hash.re ... #', '') | jquery.js:18:14:18:33 | window.location.hash | jquery.js:27:5:27:25 | hash.re ... #', '') | Cross-site scripting vulnerability due to $@. | jquery.js:18:14:18:33 | window.location.hash | user-provided value |
| jquery.js:28:5:28:43 | window. ... ?', '') | jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') | Cross-site scripting vulnerability due to $@. | jquery.js:28:5:28:26 | window. ... .search | user-provided value |
| jquery.js:34:5:34:25 | '<b>' + ... '</b>' | jquery.js:18:14:18:33 | window.location.hash | jquery.js:34:5:34:25 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | jquery.js:18:14:18:33 | window.location.hash | user-provided value |
| jwt-server.js:11:19:11:29 | decoded.foo | jwt-server.js:7:17:7:35 | req.param("wobble") | jwt-server.js:11:19:11:29 | decoded.foo | Cross-site scripting vulnerability due to $@. | jwt-server.js:7:17:7:35 | req.param("wobble") | user-provided value |
| nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | HTML injection vulnerability due to $@. | nodemailer.js:13:50:13:66 | req.query.message | user-provided value |
| optionalSanitizer.js:6:18:6:23 | target | optionalSanitizer.js:2:16:2:39 | documen ... .search | optionalSanitizer.js:6:18:6:23 | target | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:2:16:2:39 | documen ... .search | user-provided value |

View File

@@ -222,6 +222,31 @@ nodes
| jquery.js:16:38:16:52 | window.location |
| jquery.js:16:38:16:52 | window.location |
| jquery.js:16:38:16:63 | window. ... tring() |
| jquery.js:18:7:18:33 | hash |
| jquery.js:18:14:18:33 | window.location.hash |
| jquery.js:18:14:18:33 | window.location.hash |
| jquery.js:21:5:21:8 | hash |
| jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:22:5:22:8 | hash |
| jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:23:5:23:8 | hash |
| jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:24:5:24:8 | hash |
| jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:27:5:27:8 | hash |
| jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:28:5:28:26 | window. ... .search |
| jquery.js:28:5:28:26 | window. ... .search |
| jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jquery.js:34:13:34:16 | hash |
| jwt-server.js:7:9:7:35 | taint |
| jwt-server.js:7:17:7:35 | req.param("wobble") |
| jwt-server.js:7:17:7:35 | req.param("wobble") |
@@ -977,6 +1002,30 @@ edges
| jquery.js:16:38:16:52 | window.location | jquery.js:16:38:16:63 | window. ... tring() |
| jquery.js:16:38:16:63 | window. ... tring() | jquery.js:16:19:16:64 | decodeU ... ring()) |
| jquery.js:16:38:16:63 | window. ... tring() | jquery.js:16:19:16:64 | decodeU ... ring()) |
| jquery.js:18:7:18:33 | hash | jquery.js:21:5:21:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:22:5:22:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:23:5:23:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:24:5:24:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:27:5:27:8 | hash |
| jquery.js:18:7:18:33 | hash | jquery.js:34:13:34:16 | hash |
| jquery.js:18:14:18:33 | window.location.hash | jquery.js:18:7:18:33 | hash |
| jquery.js:18:14:18:33 | window.location.hash | jquery.js:18:7:18:33 | hash |
| jquery.js:21:5:21:8 | hash | jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:21:5:21:8 | hash | jquery.js:21:5:21:21 | hash.substring(1) |
| jquery.js:22:5:22:8 | hash | jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:22:5:22:8 | hash | jquery.js:22:5:22:25 | hash.su ... (1, 10) |
| jquery.js:23:5:23:8 | hash | jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:23:5:23:8 | hash | jquery.js:23:5:23:18 | hash.substr(1) |
| jquery.js:24:5:24:8 | hash | jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:24:5:24:8 | hash | jquery.js:24:5:24:17 | hash.slice(1) |
| jquery.js:27:5:27:8 | hash | jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:27:5:27:8 | hash | jquery.js:27:5:27:25 | hash.re ... #', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:28:5:28:26 | window. ... .search | jquery.js:28:5:28:43 | window. ... ?', '') |
| jquery.js:34:13:34:16 | hash | jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jquery.js:34:13:34:16 | hash | jquery.js:34:5:34:25 | '<b>' + ... '</b>' |
| jwt-server.js:7:9:7:35 | taint | jwt-server.js:9:16:9:20 | taint |
| jwt-server.js:7:17:7:35 | req.param("wobble") | jwt-server.js:7:9:7:35 | taint |
| jwt-server.js:7:17:7:35 | req.param("wobble") | jwt-server.js:7:9:7:35 | taint |

View File

@@ -14,4 +14,22 @@ function test() {
elm.innerHTML = decodeURIComponent(window.location.hash); // NOT OK
elm.innerHTML = decodeURIComponent(window.location.search); // NOT OK
elm.innerHTML = decodeURIComponent(window.location.toString()); // NOT OK
let hash = window.location.hash;
$(hash); // OK - start with '#'
$(hash.substring(1)); // NOT OK
$(hash.substring(1, 10)); // NOT OK
$(hash.substr(1)); // NOT OK
$(hash.slice(1)); // NOT OK
$(hash.substring(0, 10)); // OK
$(hash.replace('#', '')); // NOT OK
$(window.location.search.replace('?', '')); // NOT OK
$(hash.replace('!', '')); // OK
$(hash.replace('blah', '')); // OK
$(hash + 'blah'); // OK
$('blah' + hash); // OK - does not start with '<'
$('<b>' + hash + '</b>'); // NOT OK
}