Skip to content

Commit 754e40b

Browse files
committed
Optimize Actions event source matching
Bind event-property and event-context matching to source expressions so code-injection queries avoid materializing global source/event relations.
1 parent 761de92 commit 754e40b

1 file changed

Lines changed: 42 additions & 22 deletions

File tree

actions/ql/lib/codeql/actions/dataflow/FlowSources.qll

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,29 +52,41 @@ class GitHubCtxSource extends RemoteFlowSource {
5252
override string getEventName() { result = event }
5353
}
5454

55+
bindingset[expression]
56+
private predicate untrustedEventProperty(Expression expression, string kind) {
57+
exists(string regexp |
58+
untrustedEventPropertiesDataModel(regexp, kind) and
59+
not kind = "json" and
60+
normalizeExpr(expression.getExpression()).regexpMatch("(?i)\\s*" + wrapRegexp(regexp) + ".*")
61+
)
62+
}
63+
64+
bindingset[expression, event]
65+
private predicate expressionContainsEventContext(Expression expression, string event) {
66+
exists(string contextPrefix |
67+
contextTriggerDataModel(event, contextPrefix) and
68+
normalizeExpr(expression.getExpression()).matches("%" + contextPrefix + "%")
69+
)
70+
}
71+
5572
class GitHubEventCtxSource extends RemoteFlowSource {
5673
string flag;
5774
string context;
5875
string event;
5976

6077
GitHubEventCtxSource() {
61-
exists(Expression e, string regexp |
78+
exists(Expression e |
6279
this.asExpr() = e and
6380
context = e.getExpression() and
6481
(
6582
// the context is available for the job trigger events
6683
event = e.getATriggerEvent().getName() and
67-
exists(string context_prefix |
68-
contextTriggerDataModel(event, context_prefix) and
69-
normalizeExpr(context).matches("%" + context_prefix + "%")
70-
)
84+
expressionContainsEventContext(e, event)
7185
or
7286
not exists(e.getATriggerEvent()) and
7387
event = "unknown"
7488
) and
75-
untrustedEventPropertiesDataModel(regexp, flag) and
76-
not flag = "json" and
77-
normalizeExpr(context).regexpMatch("(?i)\\s*" + wrapRegexp(regexp) + ".*")
89+
untrustedEventProperty(e, flag)
7890
)
7991
}
8092

@@ -177,32 +189,40 @@ class GitHubEventPathSource extends RemoteFlowSource, CommandSource {
177189
override Run getEnclosingRun() { result = run }
178190
}
179191

192+
bindingset[expression, event]
193+
private predicate jsonSourceForEvent(Expression expression, string event) {
194+
exists(string context, string regexp, string contextPrefix |
195+
context = expression.getExpression() and
196+
untrustedEventPropertiesDataModel(regexp, _) and
197+
contextTriggerDataModel(event, contextPrefix) and
198+
normalizeExpr(context).matches("%" + contextPrefix + "%") and
199+
normalizeExpr(context).regexpMatch("(?i).*" + wrapJsonRegexp(regexp) + ".*")
200+
)
201+
or
202+
exists(string context, string regexp, string kind |
203+
context = expression.getExpression() and
204+
untrustedEventPropertiesDataModel(regexp, kind) and
205+
contextTriggerDataModel(event, _) and
206+
normalizeExpr(context).regexpMatch("(?i).*" + wrapJsonRegexp("\\bgithub.event\\b") + ".*")
207+
)
208+
}
209+
180210
class GitHubEventJsonSource extends RemoteFlowSource {
181211
string flag;
182212
string event;
183213

184214
GitHubEventJsonSource() {
185-
exists(Expression e, string context, string regexp |
215+
exists(Expression e |
186216
this.asExpr() = e and
187-
context = e.getExpression() and
188-
untrustedEventPropertiesDataModel(regexp, _) and
189217
(
190218
// only contexts for the triggering events are considered tainted.
191219
// eg: for `pull_request`, we only consider `github.event.pull_request`
192220
event = e.getEnclosingWorkflow().getATriggerEvent().getName() and
193-
exists(string context_prefix |
194-
contextTriggerDataModel(event, context_prefix) and
195-
normalizeExpr(context).matches("%" + context_prefix + "%")
196-
) and
197-
normalizeExpr(context).regexpMatch("(?i).*" + wrapJsonRegexp(regexp) + ".*")
198-
or
199-
// github.event is tainted for all triggers
200-
event = e.getEnclosingWorkflow().getATriggerEvent().getName() and
201-
contextTriggerDataModel(e.getEnclosingWorkflow().getATriggerEvent().getName(), _) and
202-
normalizeExpr(context).regexpMatch("(?i).*" + wrapJsonRegexp("\\bgithub.event\\b") + ".*")
221+
jsonSourceForEvent(e, event)
203222
or
204223
not exists(e.getATriggerEvent()) and
205-
event = "unknown"
224+
event = "unknown" and
225+
exists(string regexp, string kind | untrustedEventPropertiesDataModel(regexp, kind))
206226
) and
207227
flag = "json"
208228
)

0 commit comments

Comments
 (0)