@@ -16,6 +16,29 @@ private import semmle.python.dataflow.new.BarrierGuards
1616 * vulnerabilities, as well as extension points for adding your own.
1717 */
1818module UrlRedirect {
19+ /**
20+ * A state value to track whether the untrusted data may contain backslashes.
21+ */
22+ abstract class FlowState extends string {
23+ bindingset [ this ]
24+ FlowState ( ) { any ( ) }
25+ }
26+
27+ /**
28+ * A state value signifying that the untrusted data may contain backslashes.
29+ */
30+ class MayContainBackslashes extends UrlRedirect:: FlowState {
31+ MayContainBackslashes ( ) { this = "MayContainBackslashes" }
32+ }
33+
34+ /**
35+ * A state value signifying that any backslashes in the untrusted data have
36+ * been eliminated, but no other sanitization has happened.
37+ */
38+ class NoBackslashes extends UrlRedirect:: FlowState {
39+ NoBackslashes ( ) { this = "NoBackslashes" }
40+ }
41+
1942 /**
2043 * A data flow source for "URL redirection" vulnerabilities.
2144 */
@@ -29,7 +52,28 @@ module UrlRedirect {
2952 /**
3053 * A sanitizer for "URL redirection" vulnerabilities.
3154 */
32- abstract class Sanitizer extends DataFlow:: Node { }
55+ abstract class Sanitizer extends DataFlow:: Node {
56+ /**
57+ * Holds if this sanitizer sanitizes flow in the given state.
58+ */
59+ abstract predicate sanitizes ( FlowState state ) ;
60+ }
61+
62+ /**
63+ * An additional flow step for "URL redirection" vulnerabilities.
64+ */
65+ abstract class AdditionalFlowStep extends DataFlow:: Node {
66+ /**
67+ * Holds if there should be an additional flow step from `nodeFrom` in `stateFrom`
68+ * to `nodeTo` in `stateTo`.
69+ *
70+ * For example, a call to `replace` that replaces backslashes with forward slashes
71+ * takes flow from `MayContainBackslashes` to `NoBackslashes`.
72+ */
73+ abstract predicate step (
74+ DataFlow:: Node nodeFrom , FlowState stateFrom , DataFlow:: Node nodeTo , FlowState stateTo
75+ ) ;
76+ }
3377
3478 /**
3579 * A source of remote user input, considered as a flow source.
@@ -57,10 +101,46 @@ module UrlRedirect {
57101 string_concat .getRight ( ) = this .asCfgNode ( )
58102 )
59103 }
104+
105+ override predicate sanitizes ( FlowState state ) {
106+ // sanitize all flow states
107+ any ( )
108+ }
109+ }
110+
111+ /**
112+ * A call that replaces backslashes with forward slashes or eliminates them
113+ * altogether, considered as a partial sanitizer, as well as an additional
114+ * flow step.
115+ */
116+ class ReplaceBackslashesSanitizer extends Sanitizer , AdditionalFlowStep , DataFlow:: MethodCallNode {
117+ DataFlow:: Node receiver ;
118+
119+ ReplaceBackslashesSanitizer ( ) {
120+ this .calls ( receiver , "replace" ) and
121+ this .getArg ( 0 ) .asExpr ( ) .( StrConst ) .getText ( ) = "\\" and
122+ this .getArg ( 1 ) .asExpr ( ) .( StrConst ) .getText ( ) in [ "/" , "" ]
123+ }
124+
125+ override predicate sanitizes ( FlowState state ) { state instanceof MayContainBackslashes }
126+
127+ override predicate step (
128+ DataFlow:: Node nodeFrom , FlowState stateFrom , DataFlow:: Node nodeTo , FlowState stateTo
129+ ) {
130+ nodeFrom = receiver and
131+ stateFrom instanceof MayContainBackslashes and
132+ nodeTo = this and
133+ stateTo instanceof NoBackslashes
134+ }
60135 }
61136
62137 /**
63138 * A comparison with a constant string, considered as a sanitizer-guard.
64139 */
65- class StringConstCompareAsSanitizerGuard extends Sanitizer , StringConstCompareBarrier { }
140+ class StringConstCompareAsSanitizerGuard extends Sanitizer , StringConstCompareBarrier {
141+ override predicate sanitizes ( FlowState state ) {
142+ // sanitize all flow states
143+ any ( )
144+ }
145+ }
66146}
0 commit comments