Skip to content

Commit 70131e6

Browse files
authored
Merge pull request #3598 from asger-semmle/js/regexp-test
Approved by esbena
2 parents b48fe6a + 8342981 commit 70131e6

File tree

9 files changed

+348
-8
lines changed

9 files changed

+348
-8
lines changed

javascript/ql/src/semmle/javascript/Expr.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,11 @@ class EqualityTest extends @equalitytest, Comparison {
13551355
(this instanceof NEqExpr or this instanceof StrictNEqExpr) and
13561356
result = false
13571357
}
1358+
1359+
/**
1360+
* Holds if the equality operator is strict (`===` or `!==`).
1361+
*/
1362+
predicate isStrict() { this instanceof StrictEqExpr or this instanceof StrictNEqExpr }
13581363
}
13591364

13601365
/**

javascript/ql/src/semmle/javascript/StringOps.qll

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,4 +629,153 @@ module StringOps {
629629
class HtmlConcatenationLeaf extends ConcatenationLeaf {
630630
HtmlConcatenationLeaf() { getRoot() instanceof HtmlConcatenationRoot }
631631
}
632+
633+
/**
634+
* A data flow node whose boolean value indicates whether a regexp matches a given string.
635+
*
636+
* For example, the condition of each of the following `if`-statements are `RegExpTest` nodes:
637+
* ```js
638+
* if (regexp.test(str)) { ... }
639+
* if (regexp.exec(str) != null) { ... }
640+
* if (str.matches(regexp)) { ... }
641+
* ```
642+
*
643+
* Note that `RegExpTest` represents a boolean-valued expression or one
644+
* that is coerced to a boolean, which is not always the same as the call that performs the
645+
* regexp-matching. For example, the `exec` call below is not itself a `RegExpTest`,
646+
* but the `match` variable in the condition is:
647+
* ```js
648+
* let match = regexp.exec(str);
649+
* if (!match) { ... } // <--- 'match' is the RegExpTest
650+
* ```
651+
*/
652+
class RegExpTest extends DataFlow::Node {
653+
RegExpTest::Range range;
654+
655+
RegExpTest() { this = range }
656+
657+
/**
658+
* Gets the AST of the regular expression used in the test, if it can be seen locally.
659+
*/
660+
RegExpTerm getRegExp() {
661+
result = getRegExpOperand().getALocalSource().(DataFlow::RegExpCreationNode).getRoot()
662+
or
663+
result = range.getRegExpOperand(true).asExpr().(StringLiteral).asRegExp()
664+
}
665+
666+
/**
667+
* Gets the data flow node corresponding to the regular expression object used in the test.
668+
*
669+
* In some cases this represents a string value being coerced to a RegExp object.
670+
*/
671+
DataFlow::Node getRegExpOperand() { result = range.getRegExpOperand(_) }
672+
673+
/**
674+
* Gets the data flow node corresponding to the string being tested against the regular expression.
675+
*/
676+
DataFlow::Node getStringOperand() { result = range.getStringOperand() }
677+
678+
/**
679+
* Gets the return value indicating that the string matched the regular expression.
680+
*
681+
* For example, for `regexp.exec(str) == null`, the polarity is `false`, and for
682+
* `regexp.exec(str) != null` the polarity is `true`.
683+
*/
684+
boolean getPolarity() { result = range.getPolarity() }
685+
}
686+
687+
/**
688+
* Companion module to the `RegExpTest` class.
689+
*/
690+
module RegExpTest {
691+
/**
692+
* A data flow node whose boolean value indicates whether a regexp matches a given string.
693+
*
694+
* This class can be extended to contribute new kinds of `RegExpTest` nodes.
695+
*/
696+
abstract class Range extends DataFlow::Node {
697+
/**
698+
* Gets the data flow node corresponding to the regular expression object used in the test.
699+
*/
700+
abstract DataFlow::Node getRegExpOperand(boolean coerced);
701+
702+
/**
703+
* Gets the data flow node corresponding to the string being tested against the regular expression.
704+
*/
705+
abstract DataFlow::Node getStringOperand();
706+
707+
/**
708+
* Gets the return value indicating that the string matched the regular expression.
709+
*/
710+
boolean getPolarity() { result = true }
711+
}
712+
713+
private class TestCall extends Range, DataFlow::MethodCallNode {
714+
TestCall() { getMethodName() = "test" }
715+
716+
override DataFlow::Node getRegExpOperand(boolean coerced) {
717+
result = getReceiver() and coerced = false
718+
}
719+
720+
override DataFlow::Node getStringOperand() { result = getArgument(0) }
721+
}
722+
723+
private class MatchesCall extends Range, DataFlow::MethodCallNode {
724+
MatchesCall() { getMethodName() = "matches" }
725+
726+
override DataFlow::Node getRegExpOperand(boolean coerced) {
727+
result = getArgument(0) and coerced = true
728+
}
729+
730+
override DataFlow::Node getStringOperand() { result = getReceiver() }
731+
}
732+
733+
private class ExecCall extends DataFlow::MethodCallNode {
734+
ExecCall() { getMethodName() = "exec" }
735+
}
736+
737+
private predicate isCoercedToBoolean(Expr e) {
738+
e = any(ConditionGuardNode guard).getTest()
739+
or
740+
e = any(LogNotExpr n).getOperand()
741+
}
742+
743+
/**
744+
* Holds if `e` evaluating to `polarity` implies that `operand` is not null.
745+
*/
746+
private predicate impliesNotNull(Expr e, Expr operand, boolean polarity) {
747+
exists(EqualityTest test, Expr other |
748+
e = test and
749+
polarity = test.getPolarity().booleanNot() and
750+
test.hasOperands(other, operand) and
751+
SyntacticConstants::isNullOrUndefined(other) and
752+
not (
753+
// 'exec() === undefined' doesn't work
754+
other instanceof SyntacticConstants::UndefinedConstant and
755+
test.isStrict()
756+
)
757+
)
758+
or
759+
isCoercedToBoolean(e) and
760+
operand = e and
761+
polarity = true
762+
}
763+
764+
private class ExecTest extends Range, DataFlow::ValueNode {
765+
ExecCall exec;
766+
boolean polarity;
767+
768+
ExecTest() {
769+
exists(Expr use | exec.flowsToExpr(use) | impliesNotNull(astNode, use, polarity))
770+
}
771+
772+
override DataFlow::Node getRegExpOperand(boolean coerced) {
773+
result = exec.getReceiver() and coerced = false
774+
}
775+
776+
override DataFlow::Node getStringOperand() { result = exec.getArgument(0) }
777+
778+
override boolean getPolarity() { result = polarity }
779+
}
780+
}
632781
}

javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,16 @@ module Shared {
7878
* A sanitizer guard that checks for the existence of HTML chars in a string.
7979
* E.g. `/["'&<>]/.exec(str)`.
8080
*/
81-
class ContainsHTMLGuard extends SanitizerGuard, DataFlow::MethodCallNode {
82-
DataFlow::RegExpCreationNode regExp;
83-
81+
class ContainsHTMLGuard extends SanitizerGuard, StringOps::RegExpTest {
8482
ContainsHTMLGuard() {
85-
this.getMethodName() = ["test", "exec"] and
86-
this.getReceiver().getALocalSource() = regExp and
87-
regExp.getRoot() instanceof RegExpCharacterClass and
88-
forall(string s | s = ["\"", "&", "<", ">"] | regExp.getRoot().getAMatchedString() = s)
83+
exists(RegExpCharacterClass regExp |
84+
regExp = getRegExp() and
85+
forall(string s | s = ["\"", "&", "<", ">"] | regExp.getAMatchedString() = s)
86+
)
8987
}
9088

9189
override predicate sanitizes(boolean outcome, Expr e) {
92-
outcome = false and e = this.getArgument(0).asExpr()
90+
outcome = getPolarity().booleanNot() and e = this.getStringOperand().asExpr()
9391
}
9492
}
9593

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
regexpTest
2+
| tst.js:6:9:6:28 | /^[a-z]+$/.test(str) |
3+
| tst.js:7:9:7:36 | /^[a-z] ... != null |
4+
| tst.js:8:9:8:28 | /^[a-z]+$/.exec(str) |
5+
| tst.js:9:9:9:31 | str.mat ... -z]+$/) |
6+
| tst.js:10:9:10:31 | str.mat ... -z]+$") |
7+
| tst.js:12:9:12:24 | regexp.test(str) |
8+
| tst.js:13:9:13:32 | regexp. ... != null |
9+
| tst.js:14:9:14:24 | regexp.exec(str) |
10+
| tst.js:15:9:15:27 | str.matches(regexp) |
11+
| tst.js:18:9:18:13 | match |
12+
| tst.js:19:10:19:14 | match |
13+
| tst.js:20:9:20:21 | match == null |
14+
| tst.js:21:9:21:21 | match != null |
15+
| tst.js:22:9:22:13 | match |
16+
| tst.js:25:23:25:27 | match |
17+
| tst.js:29:21:29:36 | regexp.test(str) |
18+
| tst.js:33:21:33:39 | str.matches(regexp) |
19+
| tst.js:40:9:40:37 | regexp. ... defined |
20+
#select
21+
| tst.js:6:9:6:28 | /^[a-z]+$/.test(str) | tst.js:6:10:6:17 | ^[a-z]+$ | tst.js:6:9:6:18 | /^[a-z]+$/ | tst.js:6:25:6:27 | str | true |
22+
| tst.js:7:9:7:36 | /^[a-z] ... != null | tst.js:7:10:7:17 | ^[a-z]+$ | tst.js:7:9:7:18 | /^[a-z]+$/ | tst.js:7:25:7:27 | str | true |
23+
| tst.js:8:9:8:28 | /^[a-z]+$/.exec(str) | tst.js:8:10:8:17 | ^[a-z]+$ | tst.js:8:9:8:18 | /^[a-z]+$/ | tst.js:8:25:8:27 | str | true |
24+
| tst.js:9:9:9:31 | str.mat ... -z]+$/) | tst.js:9:22:9:29 | ^[a-z]+$ | tst.js:9:21:9:30 | /^[a-z]+$/ | tst.js:9:9:9:11 | str | true |
25+
| tst.js:10:9:10:31 | str.mat ... -z]+$") | tst.js:10:22:10:29 | ^[a-z]+$ | tst.js:10:21:10:30 | "^[a-z]+$" | tst.js:10:9:10:11 | str | true |
26+
| tst.js:12:9:12:24 | regexp.test(str) | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:12:9:12:14 | regexp | tst.js:12:21:12:23 | str | true |
27+
| tst.js:13:9:13:32 | regexp. ... != null | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:13:9:13:14 | regexp | tst.js:13:21:13:23 | str | true |
28+
| tst.js:14:9:14:24 | regexp.exec(str) | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:14:9:14:14 | regexp | tst.js:14:21:14:23 | str | true |
29+
| tst.js:15:9:15:27 | str.matches(regexp) | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:15:21:15:26 | regexp | tst.js:15:9:15:11 | str | true |
30+
| tst.js:18:9:18:13 | match | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:17:17:17:22 | regexp | tst.js:17:29:17:31 | str | true |
31+
| tst.js:19:10:19:14 | match | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:17:17:17:22 | regexp | tst.js:17:29:17:31 | str | true |
32+
| tst.js:20:9:20:21 | match == null | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:17:17:17:22 | regexp | tst.js:17:29:17:31 | str | false |
33+
| tst.js:21:9:21:21 | match != null | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:17:17:17:22 | regexp | tst.js:17:29:17:31 | str | true |
34+
| tst.js:22:9:22:13 | match | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:17:17:17:22 | regexp | tst.js:17:29:17:31 | str | true |
35+
| tst.js:25:23:25:27 | match | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:17:17:17:22 | regexp | tst.js:17:29:17:31 | str | true |
36+
| tst.js:29:21:29:36 | regexp.test(str) | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:29:21:29:26 | regexp | tst.js:29:33:29:35 | str | true |
37+
| tst.js:33:21:33:39 | str.matches(regexp) | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:33:33:33:38 | regexp | tst.js:33:21:33:23 | str | true |
38+
| tst.js:40:9:40:37 | regexp. ... defined | tst.js:3:17:3:24 | ^[a-z]+$ | tst.js:40:9:40:14 | regexp | tst.js:40:21:40:23 | str | false |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import javascript
2+
3+
query StringOps::RegExpTest regexpTest() { any() }
4+
5+
from StringOps::RegExpTest test
6+
select test, test.getRegExp(), test.getRegExpOperand(), test.getStringOperand(), test.getPolarity()
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'dummy';
2+
3+
const regexp = /^[a-z]+$/;
4+
5+
function f(str) {
6+
if (/^[a-z]+$/.test(str)) {}
7+
if (/^[a-z]+$/.exec(str) != null) {}
8+
if (/^[a-z]+$/.exec(str)) {}
9+
if (str.matches(/^[a-z]+$/)) {}
10+
if (str.matches("^[a-z]+$")) {}
11+
12+
if (regexp.test(str)) {}
13+
if (regexp.exec(str) != null) {}
14+
if (regexp.exec(str)) {}
15+
if (str.matches(regexp)) {}
16+
17+
let match = regexp.exec(str);
18+
if (match) {}
19+
if (!match) {}
20+
if (match == null) {}
21+
if (match != null) {}
22+
if (match && match[1] == "") {}
23+
24+
something({
25+
someOption: !!match
26+
});
27+
28+
something({
29+
someOption: regexp.test(str)
30+
});
31+
32+
something({
33+
someOption: str.matches(regexp)
34+
});
35+
36+
something({
37+
someOption: regexp.exec(str) // not recognized as RegExpTest
38+
})
39+
40+
if (regexp.exec(str) == undefined) {}
41+
if (regexp.exec(str) === undefined) {} // not recognized as RegExpTest
42+
}

javascript/ql/test/query-tests/Security/CWE-079/Xss.expected

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,24 @@ nodes
8484
| react-native.js:8:18:8:24 | tainted |
8585
| react-native.js:9:27:9:33 | tainted |
8686
| react-native.js:9:27:9:33 | tainted |
87+
| sanitiser.js:16:7:16:27 | tainted |
88+
| sanitiser.js:16:17:16:27 | window.name |
89+
| sanitiser.js:16:17:16:27 | window.name |
90+
| sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
91+
| sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
92+
| sanitiser.js:23:29:23:35 | tainted |
93+
| sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
94+
| sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
95+
| sanitiser.js:30:29:30:35 | tainted |
96+
| sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
97+
| sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
98+
| sanitiser.js:33:29:33:35 | tainted |
99+
| sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
100+
| sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
101+
| sanitiser.js:38:29:38:35 | tainted |
102+
| sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
103+
| sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
104+
| sanitiser.js:45:29:45:35 | tainted |
87105
| stored-xss.js:2:39:2:55 | document.location |
88106
| stored-xss.js:2:39:2:55 | document.location |
89107
| stored-xss.js:2:39:2:62 | documen ... .search |
@@ -514,6 +532,23 @@ edges
514532
| react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted |
515533
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
516534
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
535+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:23:29:23:35 | tainted |
536+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:30:29:30:35 | tainted |
537+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:33:29:33:35 | tainted |
538+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:38:29:38:35 | tainted |
539+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:45:29:45:35 | tainted |
540+
| sanitiser.js:16:17:16:27 | window.name | sanitiser.js:16:7:16:27 | tainted |
541+
| sanitiser.js:16:17:16:27 | window.name | sanitiser.js:16:7:16:27 | tainted |
542+
| sanitiser.js:23:29:23:35 | tainted | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
543+
| sanitiser.js:23:29:23:35 | tainted | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
544+
| sanitiser.js:30:29:30:35 | tainted | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
545+
| sanitiser.js:30:29:30:35 | tainted | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
546+
| sanitiser.js:33:29:33:35 | tainted | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
547+
| sanitiser.js:33:29:33:35 | tainted | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
548+
| sanitiser.js:38:29:38:35 | tainted | sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
549+
| sanitiser.js:38:29:38:35 | tainted | sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
550+
| sanitiser.js:45:29:45:35 | tainted | sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
551+
| sanitiser.js:45:29:45:35 | tainted | sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
517552
| stored-xss.js:2:39:2:55 | document.location | stored-xss.js:2:39:2:62 | documen ... .search |
518553
| stored-xss.js:2:39:2:55 | document.location | stored-xss.js:2:39:2:62 | documen ... .search |
519554
| stored-xss.js:2:39:2:62 | documen ... .search | stored-xss.js:5:20:5:52 | session ... ssion') |
@@ -834,6 +869,11 @@ edges
834869
| optionalSanitizer.js:45:18:45:56 | sanitiz ... target | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value |
835870
| react-native.js:8:18:8:24 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:18:8:24 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value |
836871
| react-native.js:9:27:9:33 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:9:27:9:33 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value |
872+
| sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |
873+
| sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |
874+
| sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |
875+
| sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |
876+
| sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |
837877
| stored-xss.js:5:20:5:52 | session ... ssion') | stored-xss.js:2:39:2:55 | document.location | stored-xss.js:5:20:5:52 | session ... ssion') | Cross-site scripting vulnerability due to $@. | stored-xss.js:2:39:2:55 | document.location | user-provided value |
838878
| stored-xss.js:8:20:8:48 | localSt ... local') | stored-xss.js:3:35:3:51 | document.location | stored-xss.js:8:20:8:48 | localSt ... local') | Cross-site scripting vulnerability due to $@. | stored-xss.js:3:35:3:51 | document.location | user-provided value |
839879
| stored-xss.js:12:20:12:54 | "<a hre ... ar</a>" | stored-xss.js:3:35:3:51 | document.location | stored-xss.js:12:20:12:54 | "<a hre ... ar</a>" | Cross-site scripting vulnerability due to $@. | stored-xss.js:3:35:3:51 | document.location | user-provided value |

javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,24 @@ nodes
8484
| react-native.js:8:18:8:24 | tainted |
8585
| react-native.js:9:27:9:33 | tainted |
8686
| react-native.js:9:27:9:33 | tainted |
87+
| sanitiser.js:16:7:16:27 | tainted |
88+
| sanitiser.js:16:17:16:27 | window.name |
89+
| sanitiser.js:16:17:16:27 | window.name |
90+
| sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
91+
| sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
92+
| sanitiser.js:23:29:23:35 | tainted |
93+
| sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
94+
| sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
95+
| sanitiser.js:30:29:30:35 | tainted |
96+
| sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
97+
| sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
98+
| sanitiser.js:33:29:33:35 | tainted |
99+
| sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
100+
| sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
101+
| sanitiser.js:38:29:38:35 | tainted |
102+
| sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
103+
| sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
104+
| sanitiser.js:45:29:45:35 | tainted |
87105
| stored-xss.js:2:39:2:55 | document.location |
88106
| stored-xss.js:2:39:2:55 | document.location |
89107
| stored-xss.js:2:39:2:62 | documen ... .search |
@@ -518,6 +536,23 @@ edges
518536
| react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted |
519537
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
520538
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
539+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:23:29:23:35 | tainted |
540+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:30:29:30:35 | tainted |
541+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:33:29:33:35 | tainted |
542+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:38:29:38:35 | tainted |
543+
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:45:29:45:35 | tainted |
544+
| sanitiser.js:16:17:16:27 | window.name | sanitiser.js:16:7:16:27 | tainted |
545+
| sanitiser.js:16:17:16:27 | window.name | sanitiser.js:16:7:16:27 | tainted |
546+
| sanitiser.js:23:29:23:35 | tainted | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
547+
| sanitiser.js:23:29:23:35 | tainted | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' |
548+
| sanitiser.js:30:29:30:35 | tainted | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
549+
| sanitiser.js:30:29:30:35 | tainted | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' |
550+
| sanitiser.js:33:29:33:35 | tainted | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
551+
| sanitiser.js:33:29:33:35 | tainted | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' |
552+
| sanitiser.js:38:29:38:35 | tainted | sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
553+
| sanitiser.js:38:29:38:35 | tainted | sanitiser.js:38:21:38:44 | '<b>' + ... '</b>' |
554+
| sanitiser.js:45:29:45:35 | tainted | sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
555+
| sanitiser.js:45:29:45:35 | tainted | sanitiser.js:45:21:45:44 | '<b>' + ... '</b>' |
521556
| stored-xss.js:2:39:2:55 | document.location | stored-xss.js:2:39:2:62 | documen ... .search |
522557
| stored-xss.js:2:39:2:55 | document.location | stored-xss.js:2:39:2:62 | documen ... .search |
523558
| stored-xss.js:2:39:2:62 | documen ... .search | stored-xss.js:5:20:5:52 | session ... ssion') |

0 commit comments

Comments
 (0)