1- private import actions
2- private import codeql.actions.DataFlow
31private import codeql.actions.dataflow.ExternalFlow
42private import codeql.actions.security.ArtifactPoisoningQuery
53
@@ -22,152 +20,218 @@ abstract class RemoteFlowSource extends SourceNode {
2220}
2321
2422bindingset [ 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+ |
2736 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
2837 )
2938}
3039
3140bindingset [ 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+ |
3449 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
3550 )
3651}
3752
3853bindingset [ context]
39- private predicate isExternalUserControlledPullRequest ( string context ) {
54+ private predicate textEvent ( string context ) {
4055 exists ( string reg |
4156 reg =
4257 [
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
4973 ]
5074 |
5175 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
5276 )
5377}
5478
5579bindingset [ 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+ )
5894}
5995
6096bindingset [ 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+ )
63123}
64124
65125bindingset [ context]
66- private predicate isExternalUserControlledGollum ( string context ) {
126+ private predicate labelEvent ( string context ) {
67127 exists ( string reg |
68128 reg =
69129 [
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" ,
72133 ]
73134 |
74135 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
75136 )
76137}
77138
78139bindingset [ context]
79- private predicate isExternalUserControlledCommit ( string context ) {
140+ private predicate emailEvent ( string context ) {
80141 exists ( string reg |
81142 reg =
82143 [
83- "github\\.event\\.commits\\[[0-9]+\\]\\.message" , "github\\.event\\.head_commit\\.message" ,
144+ // email
145+ // `echo${IFS}hello`@domain.com
84146 "github\\.event\\.head_commit\\.author\\.email" ,
85- "github\\.event\\.head_commit\\.author\\.name" ,
86147 "github\\.event\\.head_commit\\.committer\\.email" ,
87- "github\\.event\\.head_commit\\.committer\\.name" ,
88148 "github\\.event\\.commits\\[[0-9]+\\]\\.author\\.email" ,
89- "github\\.event\\.commits\\[[0-9]+\\]\\.author\\.name" ,
90149 "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" ,
92152 ]
93153 |
94154 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
95155 )
96156}
97157
98158bindingset [ context]
99- private predicate isExternalUserControlledDiscussion ( string context ) {
159+ private predicate usernameEvent ( string context ) {
100160 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+ ]
102172 |
103173 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
104174 )
105175}
106176
107177bindingset [ context]
108- private predicate isExternalUserControlledWorkflowRun ( string context ) {
178+ private predicate pathEvent ( string context ) {
109179 exists ( string reg |
110180 reg =
111181 [
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" ,
125184 ]
126185 |
127186 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
128187 )
129188}
130189
131190bindingset [ context]
132- private predicate isExternalUserControlledRepositoryDispatch ( string context ) {
191+ private predicate jsonEvent ( string context ) {
133192 exists ( string reg |
134- reg = [ "github\\.event\\.client_payload\\[[0-9]+\\]" , "github\\.event\\.client_payload" , ]
193+ reg =
194+ [
195+ // json
196+ "github\\.event" ,
197+ ]
135198 |
136199 Utils:: normalizeExpr ( context ) .regexpMatch ( Utils:: wrapRegexp ( reg ) )
137200 )
138201}
139202
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 ;
146205
147- private class EventSource extends RemoteFlowSource {
148206 EventSource ( ) {
149207 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"
161225 )
162226 }
163227
164- override string getSourceType ( ) { result = "User-controlled events" }
228+ override string getSourceType ( ) { result = flag }
165229}
166230
167231/**
168232 * A Source of untrusted data defined in a MaD specification
169233 */
170- private class ExternallyDefinedSource extends RemoteFlowSource {
234+ class ExternallyDefinedSource extends RemoteFlowSource {
171235 string sourceType ;
172236
173237 ExternallyDefinedSource ( ) { externallyDefinedSource ( this , sourceType , _) }
@@ -178,19 +242,19 @@ private class ExternallyDefinedSource extends RemoteFlowSource {
178242/**
179243 * An input for a Composite Action
180244 */
181- private class CompositeActionInputSource extends RemoteFlowSource {
245+ class CompositeActionInputSource extends RemoteFlowSource {
182246 CompositeAction c ;
183247
184248 CompositeActionInputSource ( ) { c .getAnInput ( ) = this .asExpr ( ) }
185249
186- override string getSourceType ( ) { result = "Composite action input" }
250+ override string getSourceType ( ) { result = "input" }
187251}
188252
189253/**
190254 * A downloadeded artifact.
191255 */
192- private class ArtifactToOptionSource extends RemoteFlowSource {
193- ArtifactToOptionSource ( ) { this .asExpr ( ) instanceof UntrustedArtifactDownloadStep }
256+ private class ArtifactSource extends RemoteFlowSource {
257+ ArtifactSource ( ) { this .asExpr ( ) instanceof UntrustedArtifactDownloadStep }
194258
195- override string getSourceType ( ) { result = "Step output from Artifact " }
259+ override string getSourceType ( ) { result = "artifact " }
196260}
0 commit comments