1
- private import actions
2
- private import codeql.actions.DataFlow
3
1
private import codeql.actions.dataflow.ExternalFlow
4
2
private import codeql.actions.security.ArtifactPoisoningQuery
5
3
@@ -22,152 +20,218 @@ abstract class RemoteFlowSource extends SourceNode {
22
20
}
23
21
24
22
bindingset [ context]
25
- private predicate isExternalUserControlled ( string context ) {
26
- exists ( string reg | reg = "github\\.event" |
23
+ private predicate titleEvent ( string context ) {
24
+ exists ( string reg |
25
+ reg =
26
+ [
27
+ // title
28
+ "github\\.event\\.issue\\.title" , // issue
29
+ "github\\.event\\.pull_request\\.title" , // pull request
30
+ "github\\.event\\.discussion\\.title" , // discussion
31
+ "github\\.event\\.pages\\[[0-9]+\\]\\.page_name" ,
32
+ "github\\.event\\.pages\\[[0-9]+\\]\\.title" ,
33
+ "github\\.event\\.workflow_run\\.display_title" , // The event-specific title associated with the run or the run-name if set, or the value of run-name if it is set in the workflow.
34
+ ]
35
+ |
27
36
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
28
37
)
29
38
}
30
39
31
40
bindingset [ context]
32
- private predicate isExternalUserControlledIssue ( string context ) {
33
- exists ( string reg | reg = [ "github\\.event\\.issue\\.title" , "github\\.event\\.issue\\.body" ] |
41
+ private predicate urlEvent ( string context ) {
42
+ exists ( string reg |
43
+ reg =
44
+ [
45
+ // url
46
+ "github\\.event\\.pull_request\\.head\\.repo\\.homepage" ,
47
+ ]
48
+ |
34
49
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
35
50
)
36
51
}
37
52
38
53
bindingset [ context]
39
- private predicate isExternalUserControlledPullRequest ( string context ) {
54
+ private predicate textEvent ( string context ) {
40
55
exists ( string reg |
41
56
reg =
42
57
[
43
- "github\\.event\\.pull_request\\.title" , "github\\.event\\.pull_request\\.body" ,
44
- "github\\.event\\.pull_request\\.head\\.label" ,
45
- "github\\.event\\.pull_request\\.head\\.repo\\.default_branch" ,
46
- "github\\.event\\.pull_request\\.head\\.repo\\.description" ,
47
- "github\\.event\\.pull_request\\.head\\.repo\\.homepage" ,
48
- "github\\.event\\.pull_request\\.head\\.ref" , "github\\.head_ref"
58
+ // text
59
+ "github\\.event\\.issue\\.body" , // body
60
+ "github\\.event\\.pull_request\\.body" , // body
61
+ "github\\.event\\.discussion\\.body" , // body
62
+ "github\\.event\\.review\\.body" , // body
63
+ "github\\.event\\.comment\\.body" , // body
64
+ "github\\.event\\.commits\\[[0-9]+\\]\\.message" , // messsage
65
+ "github\\.event\\.head_commit\\.message" , // message
66
+ "github\\.event\\.workflow_run\\.head_commit\\.message" , // message
67
+ "github\\.event\\.pull_request\\.head\\.repo\\.description" , // description
68
+ "github\\.event\\.workflow_run\\.head_repository\\.description" , // description
69
+ "github\\.event\\.client_payload\\[[0-9]+\\]" , // payload
70
+ "github\\.event\\.client_payload" , // payload
71
+ "github\\.event\\.inputs\\[[0-9]+\\]" , // input
72
+ "github\\.event\\.inputs" , // input
49
73
]
50
74
|
51
75
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
52
76
)
53
77
}
54
78
55
79
bindingset [ context]
56
- private predicate isExternalUserControlledReview ( string context ) {
57
- Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( "github\\.event\\.review\\.body" ) )
80
+ private predicate repoNameEvent ( string context ) {
81
+ exists ( string reg |
82
+ reg =
83
+ [
84
+ // repo name
85
+ // Owner: All characters must be either a hyphen (-) or alphanumeric
86
+ // Repo: All code points must be either a hyphen (-), an underscore (_), a period (.), or an ASCII alphanumeric code point
87
+ "github\\.event\\.workflow_run\\.pull_requests\\[[0-9]+\\]\\.head\\.repo\\.name" , // repo name
88
+ "github\\.event\\.workflow_run\\.head_repository\\.name" , // repo name
89
+ "github\\.event\\.workflow_run\\.head_repository\\.full_name" , // nwo
90
+ ]
91
+ |
92
+ Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
93
+ )
58
94
}
59
95
60
96
bindingset [ context]
61
- private predicate isExternalUserControlledComment ( string context ) {
62
- Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( "github\\.event\\.comment\\.body" ) )
97
+ private predicate branchEvent ( string context ) {
98
+ exists ( string reg |
99
+ reg =
100
+ [
101
+ // branch
102
+ // https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names
103
+ // - They can include slash / for hierarchical (directory) grouping, but no slash-separated component can begin with a dot . or end with the sequence .lock.
104
+ // - They must contain at least one /
105
+ // - They cannot have two consecutive dots .. anywhere.
106
+ // - They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
107
+ // - They cannot have question-mark ?, asterisk *, or open bracket [ anywhere.
108
+ // - They cannot begin or end with a slash / or contain multiple consecutive slashes
109
+ // - They cannot end with a dot .
110
+ // - They cannot contain a sequence @{
111
+ // - They cannot be the single character @
112
+ // - They cannot contain a \
113
+ // eg: zzz";echo${IFS}"hello";# would be a valid branch name
114
+ "github\\.event\\.pull_request\\.head\\.repo\\.default_branch" ,
115
+ "github\\.event\\.pull_request\\.head\\.ref" , "github\\.head_ref" ,
116
+ "github\\.event\\.workflow_run\\.head_branch" ,
117
+ "github\\.event\\.workflow_run\\.head_branch" ,
118
+ "github\\.event\\.workflow_run\\.pull_requests\\[[0-9]+\\]\\.head\\.ref" ,
119
+ ]
120
+ |
121
+ Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
122
+ )
63
123
}
64
124
65
125
bindingset [ context]
66
- private predicate isExternalUserControlledGollum ( string context ) {
126
+ private predicate labelEvent ( string context ) {
67
127
exists ( string reg |
68
128
reg =
69
129
[
70
- "github\\.event\\.pages\\[[0-9]+\\]\\.page_name" ,
71
- "github\\.event\\.pages\\[[0-9]+\\]\\.title"
130
+ // label
131
+ // - They cannot contain a escaping \
132
+ "github\\.event\\.pull_request\\.head\\.label" ,
72
133
]
73
134
|
74
135
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
75
136
)
76
137
}
77
138
78
139
bindingset [ context]
79
- private predicate isExternalUserControlledCommit ( string context ) {
140
+ private predicate emailEvent ( string context ) {
80
141
exists ( string reg |
81
142
reg =
82
143
[
83
- "github\\.event\\.commits\\[[0-9]+\\]\\.message" , "github\\.event\\.head_commit\\.message" ,
144
+ // email
145
+ // `echo${IFS}hello`@domain.com
84
146
"github\\.event\\.head_commit\\.author\\.email" ,
85
- "github\\.event\\.head_commit\\.author\\.name" ,
86
147
"github\\.event\\.head_commit\\.committer\\.email" ,
87
- "github\\.event\\.head_commit\\.committer\\.name" ,
88
148
"github\\.event\\.commits\\[[0-9]+\\]\\.author\\.email" ,
89
- "github\\.event\\.commits\\[[0-9]+\\]\\.author\\.name" ,
90
149
"github\\.event\\.commits\\[[0-9]+\\]\\.committer\\.email" ,
91
- "github\\.event\\.commits\\[[0-9]+\\]\\.committer\\.name" ,
150
+ "github\\.event\\.workflow_run\\.head_commit\\.author\\.email" ,
151
+ "github\\.event\\.workflow_run\\.head_commit\\.committer\\.email" ,
92
152
]
93
153
|
94
154
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
95
155
)
96
156
}
97
157
98
158
bindingset [ context]
99
- private predicate isExternalUserControlledDiscussion ( string context ) {
159
+ private predicate usernameEvent ( string context ) {
100
160
exists ( string reg |
101
- reg = [ "github\\.event\\.discussion\\.title" , "github\\.event\\.discussion\\.body" ]
161
+ reg =
162
+ [
163
+ // username
164
+ // All characters must be either a hyphen (-) or alphanumeric
165
+ "github\\.event\\.head_commit\\.author\\.name" ,
166
+ "github\\.event\\.head_commit\\.committer\\.name" ,
167
+ "github\\.event\\.commits\\[[0-9]+\\]\\.author\\.name" ,
168
+ "github\\.event\\.commits\\[[0-9]+\\]\\.committer\\.name" ,
169
+ "github\\.event\\.workflow_run\\.head_commit\\.author\\.name" ,
170
+ "github\\.event\\.workflow_run\\.head_commit\\.committer\\.name" ,
171
+ ]
102
172
|
103
173
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
104
174
)
105
175
}
106
176
107
177
bindingset [ context]
108
- private predicate isExternalUserControlledWorkflowRun ( string context ) {
178
+ private predicate pathEvent ( string context ) {
109
179
exists ( string reg |
110
180
reg =
111
181
[
112
- "github\\.event\\.workflow\\.path" , "github\\.event\\.workflow_run\\.head_branch" ,
113
- "github\\.event\\.workflow_run\\.display_title" ,
114
- "github\\.event\\.workflow_run\\.head_branch" ,
115
- "github\\.event\\.workflow_run\\.head_repository\\.description" ,
116
- "github\\.event\\.workflow_run\\.head_repository\\.full_name" ,
117
- "github\\.event\\.workflow_run\\.head_repository\\.name" ,
118
- "github\\.event\\.workflow_run\\.head_commit\\.message" ,
119
- "github\\.event\\.workflow_run\\.head_commit\\.author\\.email" ,
120
- "github\\.event\\.workflow_run\\.head_commit\\.author\\.name" ,
121
- "github\\.event\\.workflow_run\\.head_commit\\.committer\\.email" ,
122
- "github\\.event\\.workflow_run\\.head_commit\\.committer\\.name" ,
123
- "github\\.event\\.workflow_run\\.pull_requests\\[[0-9]+\\]\\.head\\.ref" ,
124
- "github\\.event\\.workflow_run\\.pull_requests\\[[0-9]+\\]\\.head\\.repo\\.name" ,
182
+ // filename
183
+ "github\\.event\\.workflow\\.path" ,
125
184
]
126
185
|
127
186
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
128
187
)
129
188
}
130
189
131
190
bindingset [ context]
132
- private predicate isExternalUserControlledRepositoryDispatch ( string context ) {
191
+ private predicate jsonEvent ( string context ) {
133
192
exists ( string reg |
134
- reg = [ "github\\.event\\.client_payload\\[[0-9]+\\]" , "github\\.event\\.client_payload" , ]
193
+ reg =
194
+ [
195
+ // json
196
+ "github\\.event" ,
197
+ ]
135
198
|
136
199
Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
137
200
)
138
201
}
139
202
140
- bindingset [ context]
141
- private predicate isExternalUserControlledWorkflowDispatch ( string context ) {
142
- exists ( string reg | reg = [ "github\\.event\\.inputs\\[[0-9]+\\]" , "github\\.event\\.inputs" , ] |
143
- Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
144
- )
145
- }
203
+ class EventSource extends RemoteFlowSource {
204
+ string flag ;
146
205
147
- private class EventSource extends RemoteFlowSource {
148
206
EventSource ( ) {
149
207
exists ( Expression e , string context | this .asExpr ( ) = e and context = e .getExpression ( ) |
150
- isExternalUserControlled ( context ) or
151
- isExternalUserControlledIssue ( context ) or
152
- isExternalUserControlledPullRequest ( context ) or
153
- isExternalUserControlledReview ( context ) or
154
- isExternalUserControlledComment ( context ) or
155
- isExternalUserControlledGollum ( context ) or
156
- isExternalUserControlledCommit ( context ) or
157
- isExternalUserControlledDiscussion ( context ) or
158
- isExternalUserControlledWorkflowRun ( context ) or
159
- isExternalUserControlledRepositoryDispatch ( context ) or
160
- isExternalUserControlledWorkflowDispatch ( context )
208
+ titleEvent ( context ) and flag = "title"
209
+ or
210
+ urlEvent ( context ) and flag = "url"
211
+ or
212
+ textEvent ( context ) and flag = "text"
213
+ or
214
+ branchEvent ( context ) and flag = "branch"
215
+ or
216
+ labelEvent ( context ) and flag = "label"
217
+ or
218
+ emailEvent ( context ) and flag = "email"
219
+ or
220
+ usernameEvent ( context ) and flag = "username"
221
+ or
222
+ pathEvent ( context ) and flag = "filename"
223
+ or
224
+ jsonEvent ( context ) and flag = "json"
161
225
)
162
226
}
163
227
164
- override string getSourceType ( ) { result = "User-controlled events" }
228
+ override string getSourceType ( ) { result = flag }
165
229
}
166
230
167
231
/**
168
232
* A Source of untrusted data defined in a MaD specification
169
233
*/
170
- private class ExternallyDefinedSource extends RemoteFlowSource {
234
+ class ExternallyDefinedSource extends RemoteFlowSource {
171
235
string sourceType ;
172
236
173
237
ExternallyDefinedSource ( ) { externallyDefinedSource ( this , sourceType , _) }
@@ -178,19 +242,19 @@ private class ExternallyDefinedSource extends RemoteFlowSource {
178
242
/**
179
243
* An input for a Composite Action
180
244
*/
181
- private class CompositeActionInputSource extends RemoteFlowSource {
245
+ class CompositeActionInputSource extends RemoteFlowSource {
182
246
CompositeAction c ;
183
247
184
248
CompositeActionInputSource ( ) { c .getAnInput ( ) = this .asExpr ( ) }
185
249
186
- override string getSourceType ( ) { result = "Composite action input" }
250
+ override string getSourceType ( ) { result = "input" }
187
251
}
188
252
189
253
/**
190
254
* A downloadeded artifact.
191
255
*/
192
- private class ArtifactToOptionSource extends RemoteFlowSource {
193
- ArtifactToOptionSource ( ) { this .asExpr ( ) instanceof UntrustedArtifactDownloadStep }
256
+ private class ArtifactSource extends RemoteFlowSource {
257
+ ArtifactSource ( ) { this .asExpr ( ) instanceof UntrustedArtifactDownloadStep }
194
258
195
- override string getSourceType ( ) { result = "Step output from Artifact " }
259
+ override string getSourceType ( ) { result = "artifact " }
196
260
}
0 commit comments