@@ -11,16 +11,22 @@ module TaintedPath {
1111 * A data flow source for tainted-path vulnerabilities.
1212 */
1313 abstract class Source extends DataFlow:: Node {
14- /** Gets a flow label denoting the type of value for which this is a source. */
15- DataFlow:: FlowLabel getAFlowLabel ( ) { result instanceof Label:: PosixPath }
14+ /** Gets a flow state denoting the type of value for which this is a source. */
15+ FlowState getAFlowState ( ) { result instanceof FlowState:: PosixPath }
16+
17+ /** DEPRECATED. Use `getAFlowState()` instead. */
18+ deprecated DataFlow:: FlowLabel getAFlowLabel ( ) { result = this .getAFlowState ( ) .toFlowLabel ( ) }
1619 }
1720
1821 /**
1922 * A data flow sink for tainted-path vulnerabilities.
2023 */
2124 abstract class Sink extends DataFlow:: Node {
22- /** Gets a flow label denoting the type of value for which this is a sink. */
23- DataFlow:: FlowLabel getAFlowLabel ( ) { result instanceof Label:: PosixPath }
25+ /** Gets a flow state denoting the type of value for which this is a sink. */
26+ FlowState getAFlowState ( ) { result instanceof FlowState:: PosixPath }
27+
28+ /** DEPRECATED. Use `getAFlowState()` instead. */
29+ deprecated DataFlow:: FlowLabel getAFlowLabel ( ) { result = this .getAFlowState ( ) .toFlowLabel ( ) }
2430 }
2531
2632 /**
@@ -38,16 +44,16 @@ module TaintedPath {
3844 predicate blocksExpr ( boolean outcome , Expr e ) { none ( ) }
3945
4046 /**
41- * Holds if this node acts as a barrier for `label `, blocking further flow from `e` if `this` evaluates to `outcome`.
47+ * Holds if this node acts as a barrier for `state `, blocking further flow from `e` if `this` evaluates to `outcome`.
4248 */
43- predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) { none ( ) }
49+ predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) { none ( ) }
4450
4551 /** DEPRECATED. Use `blocksExpr` instead. */
4652 deprecated predicate sanitizes ( boolean outcome , Expr e ) { this .blocksExpr ( outcome , e ) }
4753
4854 /** DEPRECATED. Use `blocksExpr` instead. */
4955 deprecated predicate sanitizes ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
50- this .blocksExpr ( outcome , e , label )
56+ this .blocksExpr ( outcome , e , Label :: toFlowState ( label ) )
5157 }
5258 }
5359
@@ -65,7 +71,23 @@ module TaintedPath {
6571
6672 deprecated class BarrierGuardNode = BarrierGuard ;
6773
68- module Label {
74+ private newtype TFlowState =
75+ TPosixPath ( FlowState:: Normalization normalization , FlowState:: Relativeness relativeness ) or
76+ TSplitPath ( )
77+
78+ private class FlowStateImpl extends TFlowState {
79+ /** Gets a string representation of this flow state. */
80+ abstract string toString ( ) ;
81+
82+ /** DEPRECATED. Gets the corresponding flow label, for backwards compatibility. */
83+ abstract deprecated DataFlow:: FlowLabel toFlowLabel ( ) ;
84+ }
85+
86+ /** The flow state to associate with a tainted value. See also `FlowState::PosixPath`. */
87+ final class FlowState = FlowStateImpl ;
88+
89+ /** Module containing details of individual flow states. */
90+ module FlowState {
6991 /**
7092 * A string indicating if a path is normalized, that is, whether internal `../` components
7193 * have been removed.
@@ -81,6 +103,90 @@ module TaintedPath {
81103 Relativeness ( ) { this = "relative" or this = "absolute" }
82104 }
83105
106+ /**
107+ * A flow state representing a Posix path.
108+ *
109+ * There are currently four flow states, representing the different combinations of
110+ * normalization and absoluteness.
111+ */
112+ class PosixPath extends FlowStateImpl , TPosixPath {
113+ Normalization normalization ;
114+ Relativeness relativeness ;
115+
116+ PosixPath ( ) { this = TPosixPath ( normalization , relativeness ) }
117+
118+ /** Gets a string indicating whether this path is normalized. */
119+ Normalization getNormalization ( ) { result = normalization }
120+
121+ /** Gets a string indicating whether this path is relative. */
122+ Relativeness getRelativeness ( ) { result = relativeness }
123+
124+ /** Holds if this path is normalized. */
125+ predicate isNormalized ( ) { normalization = "normalized" }
126+
127+ /** Holds if this path is not normalized. */
128+ predicate isNonNormalized ( ) { normalization = "raw" }
129+
130+ /** Holds if this path is relative. */
131+ predicate isRelative ( ) { relativeness = "relative" }
132+
133+ /** Holds if this path is relative. */
134+ predicate isAbsolute ( ) { relativeness = "absolute" }
135+
136+ /** Gets the path label with normalized flag set to true. */
137+ PosixPath toNormalized ( ) {
138+ result .isNormalized ( ) and
139+ result .getRelativeness ( ) = this .getRelativeness ( )
140+ }
141+
142+ /** Gets the path label with normalized flag set to true. */
143+ PosixPath toNonNormalized ( ) {
144+ result .isNonNormalized ( ) and
145+ result .getRelativeness ( ) = this .getRelativeness ( )
146+ }
147+
148+ /** Gets the path label with absolute flag set to true. */
149+ PosixPath toAbsolute ( ) {
150+ result .isAbsolute ( ) and
151+ result .getNormalization ( ) = this .getNormalization ( )
152+ }
153+
154+ /** Gets the path label with absolute flag set to true. */
155+ PosixPath toRelative ( ) {
156+ result .isRelative ( ) and
157+ result .getNormalization ( ) = this .getNormalization ( )
158+ }
159+
160+ /** Holds if this path may contain `../` components. */
161+ predicate canContainDotDotSlash ( ) {
162+ // Absolute normalized path is the only combination that cannot contain `../`.
163+ not ( this .isNormalized ( ) and this .isAbsolute ( ) )
164+ }
165+
166+ override string toString ( ) { result = normalization + "-" + relativeness + "-posix-path" }
167+
168+ deprecated override Label:: PosixPath toFlowLabel ( ) {
169+ result .getNormalization ( ) = normalization and result .getRelativeness ( ) = relativeness
170+ }
171+ }
172+
173+ /**
174+ * A flow label representing an array of path elements that may include "..".
175+ */
176+ class SplitPath extends FlowStateImpl , TSplitPath {
177+ override string toString ( ) { result = "splitPath" }
178+
179+ deprecated override Label:: SplitPath toFlowLabel ( ) { any ( ) }
180+ }
181+ }
182+
183+ deprecated module Label {
184+ FlowState toFlowState ( DataFlow:: FlowLabel label ) { result .toFlowLabel ( ) = label }
185+
186+ class Normalization = FlowState:: Normalization ;
187+
188+ class Relativeness = FlowState:: Relativeness ;
189+
84190 /**
85191 * A flow label representing a Posix path.
86192 *
@@ -380,14 +486,14 @@ module TaintedPath {
380486 class StartsWithDotDotSanitizer extends BarrierGuard instanceof StringOps:: StartsWith {
381487 StartsWithDotDotSanitizer ( ) { isDotDotSlashPrefix ( super .getSubstring ( ) ) }
382488
383- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
489+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
384490 // Sanitize in the false case for:
385491 // .startsWith(".")
386492 // .startsWith("..")
387493 // .startsWith("../")
388494 outcome = super .getPolarity ( ) .booleanNot ( ) and
389495 e = super .getBaseString ( ) .asExpr ( ) and
390- exists ( Label :: PosixPath posixPath | posixPath = label |
496+ exists ( FlowState :: PosixPath posixPath | posixPath = state |
391497 posixPath .isNormalized ( ) and
392498 posixPath .isRelative ( )
393499 )
@@ -422,10 +528,10 @@ module TaintedPath {
422528 not startsWith .getSubstring ( ) .getStringValue ( ) = "/"
423529 }
424530
425- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
531+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
426532 outcome = startsWith .getPolarity ( ) and
427533 e = startsWith .getBaseString ( ) .asExpr ( ) and
428- exists ( Label :: PosixPath posixPath | posixPath = label |
534+ exists ( FlowState :: PosixPath posixPath | posixPath = state |
429535 posixPath .isAbsolute ( ) and
430536 posixPath .isNormalized ( )
431537 )
@@ -457,9 +563,9 @@ module TaintedPath {
457563 ) // !x.startsWith("/home") does not guarantee that x is not absolute
458564 }
459565
460- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
566+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
461567 e = operand .asExpr ( ) and
462- exists ( Label :: PosixPath posixPath | posixPath = label |
568+ exists ( FlowState :: PosixPath posixPath | posixPath = state |
463569 outcome = polarity and posixPath .isRelative ( )
464570 or
465571 negatable = true and
@@ -475,10 +581,10 @@ module TaintedPath {
475581 class ContainsDotDotSanitizer extends BarrierGuard instanceof StringOps:: Includes {
476582 ContainsDotDotSanitizer ( ) { isDotDotSlashPrefix ( super .getSubstring ( ) ) }
477583
478- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
584+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
479585 e = super .getBaseString ( ) .asExpr ( ) and
480586 outcome = super .getPolarity ( ) .booleanNot ( ) and
481- label . ( Label :: PosixPath ) .canContainDotDotSlash ( ) // can still be bypassed by normalized absolute path
587+ state . ( FlowState :: PosixPath ) .canContainDotDotSlash ( ) // can still be bypassed by normalized absolute path
482588 }
483589 }
484590
@@ -488,10 +594,10 @@ module TaintedPath {
488594 class ContainsDotDotRegExpSanitizer extends BarrierGuard instanceof StringOps:: RegExpTest {
489595 ContainsDotDotRegExpSanitizer ( ) { super .getRegExp ( ) .getAMatchedString ( ) = [ "." , ".." , "../" ] }
490596
491- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
597+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
492598 e = super .getStringOperand ( ) .asExpr ( ) and
493599 outcome = super .getPolarity ( ) .booleanNot ( ) and
494- label . ( Label :: PosixPath ) .canContainDotDotSlash ( ) // can still be bypassed by normalized absolute path
600+ state . ( FlowState :: PosixPath ) .canContainDotDotSlash ( ) // can still be bypassed by normalized absolute path
495601 }
496602 }
497603
@@ -590,11 +696,11 @@ module TaintedPath {
590696 )
591697 }
592698
593- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
699+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
594700 (
595701 onlyNormalizedAbsolutePaths = true and
596- label . ( Label :: PosixPath ) .isNormalized ( ) and
597- label . ( Label :: PosixPath ) .isAbsolute ( )
702+ state . ( FlowState :: PosixPath ) .isNormalized ( ) and
703+ state . ( FlowState :: PosixPath ) .isAbsolute ( )
598704 or
599705 onlyNormalizedAbsolutePaths = false
600706 ) and
@@ -661,12 +767,12 @@ module TaintedPath {
661767 private class FsPathSinkWithoutUpwardNavigation extends FsPathSink {
662768 FsPathSinkWithoutUpwardNavigation ( ) { fileSystemAccess .isUpwardNavigationRejected ( this ) }
663769
664- override DataFlow :: FlowLabel getAFlowLabel ( ) {
770+ override FlowState getAFlowState ( ) {
665771 // The protection is ineffective if the ../ segments have already
666772 // cancelled out against the intended root dir using path.join or similar.
667773 // Only flag normalized paths, as this corresponds to the output
668774 // of a normalizing call that had a malicious input.
669- result .( Label :: PosixPath ) .isNormalized ( )
775+ result .( FlowState :: PosixPath ) .isNormalized ( )
670776 }
671777 }
672778
@@ -760,17 +866,26 @@ module TaintedPath {
760866 }
761867
762868 /**
763- * Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities .
869+ * DEPRECATED. Use `isAdditionalFlowStep` instead .
764870 */
765- predicate isAdditionalTaintedPathFlowStep (
871+ deprecated predicate isAdditionalTaintedPathFlowStep (
766872 DataFlow:: Node src , DataFlow:: Node dst , DataFlow:: FlowLabel srclabel ,
767873 DataFlow:: FlowLabel dstlabel
768874 ) {
769- isPosixPathStep ( src , dst , srclabel , dstlabel )
875+ isAdditionalFlowStep ( src , Label:: toFlowState ( srclabel ) , dst , Label:: toFlowState ( dstlabel ) )
876+ }
877+
878+ /**
879+ * Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities.
880+ */
881+ predicate isAdditionalFlowStep (
882+ DataFlow:: Node src , FlowState srclabel , DataFlow:: Node dst , FlowState dstlabel
883+ ) {
884+ isPosixPathStep ( src , srclabel , dst , dstlabel )
770885 or
771886 // Ignore all preliminary sanitization after decoding URI components
772- srclabel instanceof Label :: PosixPath and
773- dstlabel instanceof Label :: PosixPath and
887+ srclabel instanceof FlowState :: PosixPath and
888+ dstlabel instanceof FlowState :: PosixPath and
774889 (
775890 TaintTracking:: uriStep ( src , dst )
776891 or
@@ -814,8 +929,8 @@ module TaintedPath {
814929 exists ( StringSplitCall mcn | dst = mcn and mcn .getBaseString ( ) = src |
815930 if mcn .getSeparator ( ) = "/"
816931 then
817- srclabel .( Label :: PosixPath ) .canContainDotDotSlash ( ) and
818- dstlabel instanceof Label :: SplitPath
932+ srclabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( ) and
933+ dstlabel instanceof FlowState :: SplitPath
819934 else srclabel = dstlabel
820935 )
821936 or
@@ -825,30 +940,30 @@ module TaintedPath {
825940 name = "pop" or
826941 name = "shift"
827942 ) and
828- srclabel instanceof Label :: SplitPath and
829- dstlabel .( Label :: PosixPath ) .canContainDotDotSlash ( )
943+ srclabel instanceof FlowState :: SplitPath and
944+ dstlabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( )
830945 or
831946 (
832947 name = "slice" or
833948 name = "splice" or
834949 name = "concat"
835950 ) and
836- dstlabel instanceof Label :: SplitPath and
837- srclabel instanceof Label :: SplitPath
951+ dstlabel instanceof FlowState :: SplitPath and
952+ srclabel instanceof FlowState :: SplitPath
838953 or
839954 name = "join" and
840955 mcn .getArgument ( 0 ) .mayHaveStringValue ( "/" ) and
841- srclabel instanceof Label :: SplitPath and
842- dstlabel .( Label :: PosixPath ) .canContainDotDotSlash ( )
956+ srclabel instanceof FlowState :: SplitPath and
957+ dstlabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( )
843958 )
844959 or
845960 // prefix.concat(path)
846961 exists ( DataFlow:: MethodCallNode mcn |
847962 mcn .getMethodName ( ) = "concat" and mcn .getAnArgument ( ) = src
848963 |
849964 dst = mcn and
850- dstlabel instanceof Label :: SplitPath and
851- srclabel instanceof Label :: SplitPath
965+ dstlabel instanceof FlowState :: SplitPath and
966+ srclabel instanceof FlowState :: SplitPath
852967 )
853968 or
854969 // reading unknown property of split path
@@ -862,8 +977,8 @@ module TaintedPath {
862977 binop .getAnOperand ( ) .getIntValue ( ) = 1 and
863978 binop .getAnOperand ( ) .( PropAccess ) .getPropertyName ( ) = "length"
864979 ) and
865- srclabel instanceof Label :: SplitPath and
866- dstlabel .( Label :: PosixPath ) .canContainDotDotSlash ( )
980+ srclabel instanceof FlowState :: SplitPath and
981+ dstlabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( )
867982 )
868983 or
869984 exists ( API:: CallNode call | call = API:: moduleImport ( "slash" ) .getACall ( ) |
@@ -884,14 +999,14 @@ module TaintedPath {
884999 src = join .getASpreadArgument ( ) and
8851000 dst = join and
8861001 (
887- srclabel .( Label :: PosixPath ) .canContainDotDotSlash ( )
1002+ srclabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( )
8881003 or
889- srclabel instanceof Label :: SplitPath
1004+ srclabel instanceof FlowState :: SplitPath
8901005 ) and
891- dstlabel .( Label :: PosixPath ) .isNormalized ( ) and
1006+ dstlabel .( FlowState :: PosixPath ) .isNormalized ( ) and
8921007 if isRelative ( join .getArgument ( 0 ) .getStringValue ( ) )
893- then dstlabel .( Label :: PosixPath ) .isRelative ( )
894- else dstlabel .( Label :: PosixPath ) .isAbsolute ( )
1008+ then dstlabel .( FlowState :: PosixPath ) .isRelative ( )
1009+ else dstlabel .( FlowState :: PosixPath ) .isAbsolute ( )
8951010 )
8961011 }
8971012
@@ -900,7 +1015,8 @@ module TaintedPath {
9001015 * standard taint step `src -> dst` should be suppressed.
9011016 */
9021017 private predicate isPosixPathStep (
903- DataFlow:: Node src , DataFlow:: Node dst , Label:: PosixPath srclabel , Label:: PosixPath dstlabel
1018+ DataFlow:: Node src , FlowState:: PosixPath srclabel , DataFlow:: Node dst ,
1019+ FlowState:: PosixPath dstlabel
9041020 ) {
9051021 // path.normalize() and similar
9061022 exists ( NormalizingPathCall call |
0 commit comments