1
1
import actions
2
- import codeql.actions.DataFlow
2
+ private import codeql.actions.DataFlow
3
+ private import codeql.actions.TaintTracking
3
4
4
- string getStepCWD ( ) {
5
- // TODO: This should be the path of the git command.
6
- // Read if from the step's CWD, workspace or look for a cd command.
7
- result = "?"
5
+ /**
6
+ * A taint-tracking configuration for PR HEAD references flowing
7
+ * into actions/checkout's ref argument.
8
+ */
9
+ private module ActionsMutableRefCheckoutConfig implements DataFlow:: ConfigSig {
10
+ predicate isSource ( DataFlow:: Node source ) {
11
+ // `ref` argument contains the PR id/number or head ref
12
+ exists ( Expression e |
13
+ source .asExpr ( ) = e and
14
+ (
15
+ containsHeadRef ( e .getExpression ( ) ) or
16
+ containsPullRequestNumber ( e .getExpression ( ) )
17
+ )
18
+ )
19
+ or
20
+ // 3rd party actions returning the PR head ref
21
+ exists ( StepsExpression e , UsesStep step |
22
+ source .asExpr ( ) = e and
23
+ e .getStepId ( ) = step .getId ( ) and
24
+ (
25
+ step .getCallee ( ) = "eficode/resolve-pr-refs" and e .getFieldName ( ) = "head_ref"
26
+ or
27
+ step .getCallee ( ) = "xt0rted/pull-request-comment-branch" and e .getFieldName ( ) = "head_ref"
28
+ or
29
+ step .getCallee ( ) = "alessbell/pull-request-comment-branch" and e .getFieldName ( ) = "head_ref"
30
+ or
31
+ step .getCallee ( ) = "gotson/pull-request-comment-branch" and e .getFieldName ( ) = "head_ref"
32
+ or
33
+ step .getCallee ( ) = "potiuk/get-workflow-origin" and
34
+ e .getFieldName ( ) = [ "sourceHeadBranch" , "pullRequestNumber" ]
35
+ or
36
+ step .getCallee ( ) = "github/branch-deploy" and e .getFieldName ( ) = [ "ref" , "fork_ref" ]
37
+ )
38
+ )
39
+ }
40
+
41
+ predicate isSink ( DataFlow:: Node sink ) {
42
+ exists ( Uses uses |
43
+ uses .getCallee ( ) = "actions/checkout" and
44
+ uses .getArgumentExpr ( "ref" ) = sink .asExpr ( )
45
+ )
46
+ }
47
+ }
48
+
49
+ module ActionsMutableRefCheckoutFlow = TaintTracking:: Global< ActionsMutableRefCheckoutConfig > ;
50
+
51
+ private module ActionsSHACheckoutConfig implements DataFlow:: ConfigSig {
52
+ predicate isSource ( DataFlow:: Node source ) {
53
+ // `ref` argument contains the PR head/merge commit sha
54
+ exists ( Expression e |
55
+ source .asExpr ( ) = e and
56
+ containsHeadSHA ( e .getExpression ( ) )
57
+ )
58
+ or
59
+ // 3rd party actions returning the PR head sha
60
+ exists ( StepsExpression e , UsesStep step |
61
+ source .asExpr ( ) = e and
62
+ e .getStepId ( ) = step .getId ( ) and
63
+ (
64
+ step .getCallee ( ) = "eficode/resolve-pr-refs" and e .getFieldName ( ) = "head_sha"
65
+ or
66
+ step .getCallee ( ) = "xt0rted/pull-request-comment-branch" and e .getFieldName ( ) = "head_sha"
67
+ or
68
+ step .getCallee ( ) = "alessbell/pull-request-comment-branch" and e .getFieldName ( ) = "head_sha"
69
+ or
70
+ step .getCallee ( ) = "gotson/pull-request-comment-branch" and e .getFieldName ( ) = "head_sha"
71
+ or
72
+ step .getCallee ( ) = "potiuk/get-workflow-origin" and
73
+ e .getFieldName ( ) = [ "sourceHeadSha" , "mergeCommitSha" ]
74
+ )
75
+ )
76
+ }
77
+
78
+ predicate isSink ( DataFlow:: Node sink ) {
79
+ exists ( Uses uses |
80
+ uses .getCallee ( ) = "actions/checkout" and
81
+ uses .getArgumentExpr ( "ref" ) = sink .asExpr ( )
82
+ )
83
+ }
8
84
}
9
85
86
+ module ActionsSHACheckoutFlow = TaintTracking:: Global< ActionsSHACheckoutConfig > ;
87
+
10
88
bindingset [ s]
11
89
predicate containsPullRequestNumber ( string s ) {
12
90
exists (
@@ -73,6 +151,12 @@ predicate containsHeadRef(string s) {
73
151
)
74
152
}
75
153
154
+ private string getStepCWD ( ) {
155
+ // TODO: This should be the path of the git command.
156
+ // Read if from the step's CWD, workspace or look for a cd command.
157
+ result = "?"
158
+ }
159
+
76
160
/** Checkout of a Pull Request HEAD */
77
161
abstract class PRHeadCheckoutStep extends Step {
78
162
abstract string getPath ( ) ;
@@ -89,35 +173,9 @@ class ActionsMutableRefCheckout extends MutableRefCheckoutStep instanceof UsesSt
89
173
ActionsMutableRefCheckout ( ) {
90
174
this .getCallee ( ) = "actions/checkout" and
91
175
(
92
- // ref argument contains the PR id/number or head ref/sha
93
- exists ( Expression e |
94
- (
95
- containsHeadRef ( e .getExpression ( ) ) or
96
- containsPullRequestNumber ( e .getExpression ( ) )
97
- ) and
98
- DataFlow:: hasLocalFlowExpr ( e , this .getArgumentExpr ( "ref" ) )
99
- )
100
- or
101
- // 3rd party actions returning the PR head sha/ref
102
- exists ( UsesStep step |
103
- (
104
- step .getCallee ( ) =
105
- [
106
- "eficode/resolve-pr-refs" , "xt0rted/pull-request-comment-branch" ,
107
- "alessbell/pull-request-comment-branch" , "gotson/pull-request-comment-branch"
108
- ] and
109
- // TODO: This should be read step of the head_sha or head_ref output vars
110
- this .getArgument ( "ref" ) .regexpMatch ( ".*(head_ref).*" )
111
- or
112
- step .getCallee ( ) = "potiuk/get-workflow-origin" and
113
- // TODO: This should be read step of the ref output var
114
- this .getArgument ( "ref" ) .matches ( "%." + [ "sourceHeadBranch" , "pullRequestNumber" ] )
115
- or
116
- step .getCallee ( ) = "github/branch-deploy" and
117
- // TODO: This should be read step of the ref output var
118
- this .getArgument ( "ref" ) .matches ( "%.ref%" )
119
- ) and
120
- DataFlow:: hasLocalFlowExpr ( step , this .getArgumentExpr ( "ref" ) )
176
+ exists ( ActionsMutableRefCheckoutFlow:: PathNode sink |
177
+ ActionsMutableRefCheckoutFlow:: flowPath ( _, sink ) and
178
+ sink .getNode ( ) .asExpr ( ) = this .getArgumentExpr ( "ref" )
121
179
)
122
180
or
123
181
// heuristic base on the step id and field name
@@ -159,27 +217,9 @@ class ActionsSHACheckout extends SHACheckoutStep instanceof UsesStep {
159
217
ActionsSHACheckout ( ) {
160
218
this .getCallee ( ) = "actions/checkout" and
161
219
(
162
- // ref argument contains the PR id/number or head ref/sha
163
- exists ( Expression e |
164
- containsHeadSHA ( e .getExpression ( ) ) and
165
- DataFlow:: hasLocalFlowExpr ( e , this .getArgumentExpr ( "ref" ) )
166
- )
167
- or
168
- // 3rd party actions returning the PR head sha/ref
169
- exists ( UsesStep step |
170
- (
171
- step .getCallee ( ) =
172
- [
173
- "eficode/resolve-pr-refs" , "xt0rted/pull-request-comment-branch" ,
174
- "alessbell/pull-request-comment-branch" , "gotson/pull-request-comment-branch"
175
- ] and
176
- this .getArgument ( "ref" ) .regexpMatch ( ".*(head_sha).*" )
177
- or
178
- step .getCallee ( ) = "potiuk/get-workflow-origin" and
179
- // TODO: This should be read step of the ref output var
180
- this .getArgument ( "ref" ) .matches ( "%." + [ "sourceHeadSha" , "mergeCommitSha" ] )
181
- ) and
182
- DataFlow:: hasLocalFlowExpr ( step , this .getArgumentExpr ( "ref" ) )
220
+ exists ( ActionsSHACheckoutFlow:: PathNode sink |
221
+ ActionsSHACheckoutFlow:: flowPath ( _, sink ) and
222
+ sink .getNode ( ) .asExpr ( ) = this .getArgumentExpr ( "ref" )
183
223
)
184
224
or
185
225
// heuristic base on the step id and field name
0 commit comments