1414 */
1515
1616import javascript
17+ import semmle.javascript.security.IncompleteBlacklistSanitizer as IncompleteBlacklistSanitizer
1718
1819/** A URL scheme that can be used to represent executable code. */
1920class DangerousScheme extends string {
@@ -55,6 +56,21 @@ DataFlow::SourceNode schemeOf(DataFlow::Node url) {
5556 )
5657}
5758
59+ /**
60+ * A chain of replace calls that replaces one or more dangerous schemes.
61+ */
62+ class SchemeReplacementChain extends IncompleteBlacklistSanitizer:: StringReplaceCallSequence {
63+ SchemeReplacementChain ( ) { this .getAMember ( ) .getAReplacedString ( ) instanceof DangerousScheme }
64+
65+ /**
66+ * Gets the source node that the replacement happens on.
67+ * The result is the receiver of the first call in the chain.
68+ */
69+ DataFlow:: Node getReplacementSource ( ) {
70+ result = this .getReceiver + ( ) and not result instanceof DataFlow:: MethodCallNode
71+ }
72+ }
73+
5874/** Gets a data-flow node that checks `nd` against the given `scheme`. */
5975DataFlow:: Node schemeCheck ( DataFlow:: Node nd , DangerousScheme scheme ) {
6076 // check of the form `nd.startsWith(scheme)`
@@ -73,6 +89,11 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
7389 schemeOf ( nd ) .flowsTo ( candidate )
7490 )
7591 or
92+ exists ( SchemeReplacementChain chain | result = chain |
93+ scheme = chain .getAMember ( ) .getAReplacedString ( ) and
94+ nd = chain .getReplacementSource ( )
95+ )
96+ or
7697 // propagate through trimming, case conversion, and regexp replace
7798 exists ( DataFlow:: MethodCallNode stringop |
7899 stringop .getMethodName ( ) .matches ( "trim%" ) or
0 commit comments