@@ -11,16 +11,22 @@ module TaintedPath {
11
11
* A data flow source for tainted-path vulnerabilities.
12
12
*/
13
13
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 ( ) }
16
19
}
17
20
18
21
/**
19
22
* A data flow sink for tainted-path vulnerabilities.
20
23
*/
21
24
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 ( ) }
24
30
}
25
31
26
32
/**
@@ -38,16 +44,16 @@ module TaintedPath {
38
44
predicate blocksExpr ( boolean outcome , Expr e ) { none ( ) }
39
45
40
46
/**
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`.
42
48
*/
43
- predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) { none ( ) }
49
+ predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) { none ( ) }
44
50
45
51
/** DEPRECATED. Use `blocksExpr` instead. */
46
52
deprecated predicate sanitizes ( boolean outcome , Expr e ) { this .blocksExpr ( outcome , e ) }
47
53
48
54
/** DEPRECATED. Use `blocksExpr` instead. */
49
55
deprecated predicate sanitizes ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
50
- this .blocksExpr ( outcome , e , label )
56
+ this .blocksExpr ( outcome , e , Label :: toFlowState ( label ) )
51
57
}
52
58
}
53
59
@@ -65,7 +71,23 @@ module TaintedPath {
65
71
66
72
deprecated class BarrierGuardNode = BarrierGuard ;
67
73
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 {
69
91
/**
70
92
* A string indicating if a path is normalized, that is, whether internal `../` components
71
93
* have been removed.
@@ -81,6 +103,90 @@ module TaintedPath {
81
103
Relativeness ( ) { this = "relative" or this = "absolute" }
82
104
}
83
105
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
+
84
190
/**
85
191
* A flow label representing a Posix path.
86
192
*
@@ -380,14 +486,14 @@ module TaintedPath {
380
486
class StartsWithDotDotSanitizer extends BarrierGuard instanceof StringOps:: StartsWith {
381
487
StartsWithDotDotSanitizer ( ) { isDotDotSlashPrefix ( super .getSubstring ( ) ) }
382
488
383
- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
489
+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
384
490
// Sanitize in the false case for:
385
491
// .startsWith(".")
386
492
// .startsWith("..")
387
493
// .startsWith("../")
388
494
outcome = super .getPolarity ( ) .booleanNot ( ) and
389
495
e = super .getBaseString ( ) .asExpr ( ) and
390
- exists ( Label :: PosixPath posixPath | posixPath = label |
496
+ exists ( FlowState :: PosixPath posixPath | posixPath = state |
391
497
posixPath .isNormalized ( ) and
392
498
posixPath .isRelative ( )
393
499
)
@@ -422,10 +528,10 @@ module TaintedPath {
422
528
not startsWith .getSubstring ( ) .getStringValue ( ) = "/"
423
529
}
424
530
425
- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
531
+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
426
532
outcome = startsWith .getPolarity ( ) and
427
533
e = startsWith .getBaseString ( ) .asExpr ( ) and
428
- exists ( Label :: PosixPath posixPath | posixPath = label |
534
+ exists ( FlowState :: PosixPath posixPath | posixPath = state |
429
535
posixPath .isAbsolute ( ) and
430
536
posixPath .isNormalized ( )
431
537
)
@@ -457,9 +563,9 @@ module TaintedPath {
457
563
) // !x.startsWith("/home") does not guarantee that x is not absolute
458
564
}
459
565
460
- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
566
+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
461
567
e = operand .asExpr ( ) and
462
- exists ( Label :: PosixPath posixPath | posixPath = label |
568
+ exists ( FlowState :: PosixPath posixPath | posixPath = state |
463
569
outcome = polarity and posixPath .isRelative ( )
464
570
or
465
571
negatable = true and
@@ -475,10 +581,10 @@ module TaintedPath {
475
581
class ContainsDotDotSanitizer extends BarrierGuard instanceof StringOps:: Includes {
476
582
ContainsDotDotSanitizer ( ) { isDotDotSlashPrefix ( super .getSubstring ( ) ) }
477
583
478
- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
584
+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
479
585
e = super .getBaseString ( ) .asExpr ( ) and
480
586
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
482
588
}
483
589
}
484
590
@@ -488,10 +594,10 @@ module TaintedPath {
488
594
class ContainsDotDotRegExpSanitizer extends BarrierGuard instanceof StringOps:: RegExpTest {
489
595
ContainsDotDotRegExpSanitizer ( ) { super .getRegExp ( ) .getAMatchedString ( ) = [ "." , ".." , "../" ] }
490
596
491
- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
597
+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
492
598
e = super .getStringOperand ( ) .asExpr ( ) and
493
599
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
495
601
}
496
602
}
497
603
@@ -590,11 +696,11 @@ module TaintedPath {
590
696
)
591
697
}
592
698
593
- override predicate blocksExpr ( boolean outcome , Expr e , DataFlow :: FlowLabel label ) {
699
+ override predicate blocksExpr ( boolean outcome , Expr e , FlowState state ) {
594
700
(
595
701
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 ( )
598
704
or
599
705
onlyNormalizedAbsolutePaths = false
600
706
) and
@@ -661,12 +767,12 @@ module TaintedPath {
661
767
private class FsPathSinkWithoutUpwardNavigation extends FsPathSink {
662
768
FsPathSinkWithoutUpwardNavigation ( ) { fileSystemAccess .isUpwardNavigationRejected ( this ) }
663
769
664
- override DataFlow :: FlowLabel getAFlowLabel ( ) {
770
+ override FlowState getAFlowState ( ) {
665
771
// The protection is ineffective if the ../ segments have already
666
772
// cancelled out against the intended root dir using path.join or similar.
667
773
// Only flag normalized paths, as this corresponds to the output
668
774
// of a normalizing call that had a malicious input.
669
- result .( Label :: PosixPath ) .isNormalized ( )
775
+ result .( FlowState :: PosixPath ) .isNormalized ( )
670
776
}
671
777
}
672
778
@@ -760,17 +866,26 @@ module TaintedPath {
760
866
}
761
867
762
868
/**
763
- * Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities .
869
+ * DEPRECATED. Use `isAdditionalFlowStep` instead .
764
870
*/
765
- predicate isAdditionalTaintedPathFlowStep (
871
+ deprecated predicate isAdditionalTaintedPathFlowStep (
766
872
DataFlow:: Node src , DataFlow:: Node dst , DataFlow:: FlowLabel srclabel ,
767
873
DataFlow:: FlowLabel dstlabel
768
874
) {
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 )
770
885
or
771
886
// 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
774
889
(
775
890
TaintTracking:: uriStep ( src , dst )
776
891
or
@@ -814,8 +929,8 @@ module TaintedPath {
814
929
exists ( StringSplitCall mcn | dst = mcn and mcn .getBaseString ( ) = src |
815
930
if mcn .getSeparator ( ) = "/"
816
931
then
817
- srclabel .( Label :: PosixPath ) .canContainDotDotSlash ( ) and
818
- dstlabel instanceof Label :: SplitPath
932
+ srclabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( ) and
933
+ dstlabel instanceof FlowState :: SplitPath
819
934
else srclabel = dstlabel
820
935
)
821
936
or
@@ -825,30 +940,30 @@ module TaintedPath {
825
940
name = "pop" or
826
941
name = "shift"
827
942
) and
828
- srclabel instanceof Label :: SplitPath and
829
- dstlabel .( Label :: PosixPath ) .canContainDotDotSlash ( )
943
+ srclabel instanceof FlowState :: SplitPath and
944
+ dstlabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( )
830
945
or
831
946
(
832
947
name = "slice" or
833
948
name = "splice" or
834
949
name = "concat"
835
950
) and
836
- dstlabel instanceof Label :: SplitPath and
837
- srclabel instanceof Label :: SplitPath
951
+ dstlabel instanceof FlowState :: SplitPath and
952
+ srclabel instanceof FlowState :: SplitPath
838
953
or
839
954
name = "join" and
840
955
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 ( )
843
958
)
844
959
or
845
960
// prefix.concat(path)
846
961
exists ( DataFlow:: MethodCallNode mcn |
847
962
mcn .getMethodName ( ) = "concat" and mcn .getAnArgument ( ) = src
848
963
|
849
964
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
852
967
)
853
968
or
854
969
// reading unknown property of split path
@@ -862,8 +977,8 @@ module TaintedPath {
862
977
binop .getAnOperand ( ) .getIntValue ( ) = 1 and
863
978
binop .getAnOperand ( ) .( PropAccess ) .getPropertyName ( ) = "length"
864
979
) and
865
- srclabel instanceof Label :: SplitPath and
866
- dstlabel .( Label :: PosixPath ) .canContainDotDotSlash ( )
980
+ srclabel instanceof FlowState :: SplitPath and
981
+ dstlabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( )
867
982
)
868
983
or
869
984
exists ( API:: CallNode call | call = API:: moduleImport ( "slash" ) .getACall ( ) |
@@ -884,14 +999,14 @@ module TaintedPath {
884
999
src = join .getASpreadArgument ( ) and
885
1000
dst = join and
886
1001
(
887
- srclabel .( Label :: PosixPath ) .canContainDotDotSlash ( )
1002
+ srclabel .( FlowState :: PosixPath ) .canContainDotDotSlash ( )
888
1003
or
889
- srclabel instanceof Label :: SplitPath
1004
+ srclabel instanceof FlowState :: SplitPath
890
1005
) and
891
- dstlabel .( Label :: PosixPath ) .isNormalized ( ) and
1006
+ dstlabel .( FlowState :: PosixPath ) .isNormalized ( ) and
892
1007
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 ( )
895
1010
)
896
1011
}
897
1012
@@ -900,7 +1015,8 @@ module TaintedPath {
900
1015
* standard taint step `src -> dst` should be suppressed.
901
1016
*/
902
1017
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
904
1020
) {
905
1021
// path.normalize() and similar
906
1022
exists ( NormalizingPathCall call |
0 commit comments