forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathActions.qll
More file actions
376 lines (298 loc) · 12.5 KB
/
Actions.qll
File metadata and controls
376 lines (298 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/**
* PENDING DEPRECATION. Models for GitHub Actions workflow files are part of the actions qlpack now.
*
* Libraries for modeling GitHub Actions workflow files written in YAML.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
import javascript
/**
* Libraries for modeling GitHub Actions workflow files written in YAML.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
module Actions {
/** A YAML node in a GitHub Actions workflow or a custom composite action file. */
private class Node extends YamlNode {
Node() {
exists(File f |
f = this.getLocation().getFile() and
(
f.getRelativePath().regexpMatch("(^|.*/)\\.github/workflows/.*\\.ya?ml$")
or
f.getBaseName() = ["action.yml", "action.yaml"]
)
)
}
}
/**
* A custom composite action. This is a mapping at the top level of an Actions YAML action file.
* See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions.
*/
class CompositeAction extends Node, YamlDocument, YamlMapping {
CompositeAction() {
this.getFile().getBaseName() = ["action.yml", "action.yaml"] and
this.lookup("runs").(YamlMapping).lookup("using").(YamlScalar).getValue() = "composite"
}
/** Gets the `runs` mapping. */
Runs getRuns() { result = this.lookup("runs") }
}
/**
* An `runs` mapping in a custom composite action YAML.
* See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs
*/
class Runs extends StepsContainer {
CompositeAction action;
Runs() { action.lookup("runs") = this }
/** Gets the action that this `runs` mapping is in. */
CompositeAction getAction() { result = action }
/** Gets the `using` mapping. */
Using getUsing() { result = this.lookup("using") }
}
/**
* The parent class of the class that can contain `steps` mappings. (`Job` or `Runs` currently.)
*/
abstract class StepsContainer extends YamlNode, YamlMapping {
/** Gets the sequence of `steps` within this YAML node. */
YamlSequence getSteps() { result = this.lookup("steps") }
}
/**
* A `using` mapping in a custom composite action YAML.
*/
class Using extends YamlNode, YamlScalar {
Runs runs;
Using() { runs.lookup("using") = this }
/** Gets the `runs` mapping that this `using` mapping is in. */
Runs getRuns() { result = runs }
}
/**
* An Actions workflow. This is a mapping at the top level of an Actions YAML workflow file.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
class Workflow extends Node, YamlDocument, YamlMapping {
/** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */
YamlMapping getJobs() { result = this.lookup("jobs") }
/** Gets the 'global' `env` mapping in this workflow. */
WorkflowEnv getEnv() { result = this.lookup("env") }
/** Gets the name of the workflow. */
string getName() { result = this.lookup("name").(YamlString).getValue() }
/** Gets the name of the workflow file. */
string getFileName() { result = this.getFile().getBaseName() }
/** Gets the `on:` in this workflow. */
On getOn() { result = this.lookup("on") }
/** Gets the job within this workflow with the given job ID. */
Job getJob(string jobId) { result.getWorkflow() = this and result.getId() = jobId }
}
/**
* An Actions On trigger within a workflow.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#on.
*/
class On extends YamlNode, YamlMappingLikeNode {
Workflow workflow;
On() { workflow.lookup("on") = this }
/** Gets the workflow that this trigger is in. */
Workflow getWorkflow() { result = workflow }
}
/** A common class for `env` in workflow, job or step. */
abstract class Env extends YamlNode, YamlMapping { }
/** A workflow level `env` mapping. */
class WorkflowEnv extends Env {
Workflow workflow;
WorkflowEnv() { workflow.lookup("env") = this }
/** Gets the workflow this field belongs to. */
Workflow getWorkflow() { result = workflow }
}
/** A job level `env` mapping. */
class JobEnv extends Env {
Job job;
JobEnv() { job.lookup("env") = this }
/** Gets the job this field belongs to. */
Job getJob() { result = job }
}
/** A step level `env` mapping. */
class StepEnv extends Env {
Step step;
StepEnv() { step.lookup("env") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
}
/**
* An Actions job within a workflow.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs.
*/
class Job extends StepsContainer {
string jobId;
Workflow workflow;
Job() { this = workflow.getJobs().lookup(jobId) }
/**
* Gets the ID of this job, as a string.
* This is the job's key within the `jobs` mapping.
*/
string getId() { result = jobId }
/**
* Gets the ID of this job, as a YAML scalar node.
* This is the job's key within the `jobs` mapping.
*/
YamlString getIdNode() { workflow.getJobs().maps(result, this) }
/** Gets the human-readable name of this job, if any, as a string. */
string getName() { result = this.getNameNode().getValue() }
/** Gets the human-readable name of this job, if any, as a YAML scalar node. */
YamlString getNameNode() { result = this.lookup("name") }
/** Gets the step at the given index within this job. */
Step getStep(int index) { result.getJob() = this and result.getIndex() = index }
/** Gets the `env` mapping in this job. */
JobEnv getEnv() { result = this.lookup("env") }
/** Gets the workflow this job belongs to. */
Workflow getWorkflow() { result = workflow }
/** Gets the value of the `if` field in this job, if any. */
JobIf getIf() { result.getJob() = this }
/** Gets the value of the `runs-on` field in this job. */
JobRunson getRunsOn() { result.getJob() = this }
}
/**
* An `if` within a job.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif.
*/
class JobIf extends YamlNode, YamlScalar {
Job job;
JobIf() { job.lookup("if") = this }
/** Gets the step this field belongs to. */
Job getJob() { result = job }
}
/**
* A `runs-on` within a job.
* See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on.
*/
class JobRunson extends YamlNode, YamlScalar {
Job job;
JobRunson() { job.lookup("runs-on") = this }
/** Gets the step this field belongs to. */
Job getJob() { result = job }
}
/**
* A step within an Actions job.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps.
*/
class Step extends YamlNode, YamlMapping {
int index;
StepsContainer parent;
Step() { this = parent.getSteps().getElement(index) }
/** Gets the 0-based position of this step within the sequence of `steps`. */
int getIndex() { result = index }
/** Gets the `job` this step belongs to, if the step belongs to a `job` in a workflow. Has no result if the step belongs to `runs` in a custom composite action. */
Job getJob() { result = parent }
/** Gets the `runs` this step belongs to, if the step belongs to a `runs` in a custom composite action. Has no result if the step belongs to a `job` in a workflow. */
Runs getRuns() { result = parent }
/** Gets the value of the `uses` field in this step, if any. */
Uses getUses() { result.getStep() = this }
/** Gets the value of the `run` field in this step, if any. */
Run getRun() { result.getStep() = this }
/** Gets the value of the `if` field in this step, if any. */
StepIf getIf() { result.getStep() = this }
/** Gets the value of the `env` field in this step, if any. */
StepEnv getEnv() { result = this.lookup("env") }
/** Gets the ID of this step, if any. */
string getId() { result = this.lookup("id").(YamlString).getValue() }
}
/**
* An `if` within a step.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsif.
*/
class StepIf extends YamlNode, YamlScalar {
Step step;
StepIf() { step.lookup("if") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
}
/**
* Gets a regular expression that parses an `owner/repo@version` reference within a `uses` field in an Actions job step.
* The capture groups are:
* 1: The owner of the repository where the Action comes from, e.g. `actions` in `actions/checkout@v2`
* 2: The name of the repository where the Action comes from, e.g. `checkout` in `actions/checkout@v2`.
* 3: The version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`.
*/
private string usesParser() { result = "([^/]+)/([^/@]+)@(.+)" }
/**
* A `uses` field within an Actions job step, which references an action as a reusable unit of code.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsuses.
*
* For example:
* ```
* uses: actions/checkout@v2
* ```
*
* Does not handle local repository references, e.g. `.github/actions/action-name`.
*/
class Uses extends YamlNode, YamlScalar {
Step step;
Uses() { step.lookup("uses") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
/** Gets the owner and name of the repository where the Action comes from, e.g. `actions/checkout` in `actions/checkout@v2`. */
string getGitHubRepository() {
result =
this.getValue().regexpCapture(usesParser(), 1) + "/" +
this.getValue().regexpCapture(usesParser(), 2)
}
/** Gets the version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`. */
string getVersion() { result = this.getValue().regexpCapture(usesParser(), 3) }
}
/**
* A `with` field within an Actions job step, which references an action as a reusable unit of code.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepswith.
*
* For example:
* ```
* with:
* arg1: 1
* arg2: abc
* ```
*/
class With extends YamlNode, YamlMapping {
Step step;
With() { step.lookup("with") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
}
/**
* A `ref:` field within an Actions `with:` specific to `actions/checkout` action.
*
* For example:
* ```
* uses: actions/checkout@v2
* with:
* ref: ${{ github.event.pull_request.head.sha }}
* ```
*/
class Ref extends YamlNode, YamlString {
With with;
Ref() { with.lookup("ref") = this }
/** Gets the `with` field this field belongs to. */
With getWith() { result = with }
}
/**
* Holds if `${{ e }}` is a GitHub Actions expression evaluated within this YAML string.
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions.
* Only finds simple expressions like `${{ github.event.comment.body }}`, where the expression contains only alphanumeric characters, underscores, dots, or dashes.
* Does not identify more complicated expressions like `${{ fromJSON(env.time) }}`, or ${{ format('{{Hello {0}!}}', github.event.head_commit.author.name) }}
*/
string getASimpleReferenceExpression(YamlString node) {
// We use `regexpFind` to obtain *all* matches of `${{...}}`,
// not just the last (greedy match) or first (reluctant match).
result =
node.getValue()
.regexpFind("\\$\\{\\{\\s*[A-Za-z0-9_\\[\\]\\*\\(\\)\\.\\-]+\\s*\\}\\}", _, _)
.regexpCapture("\\$\\{\\{\\s*([A-Za-z0-9_\\[\\]\\*\\((\\)\\.\\-]+)\\s*\\}\\}", 1)
}
/** Extracts the 'name' part from env.name */
bindingset[name]
string getEnvName(string name) { result = name.regexpCapture("env\\.([A-Za-z0-9_]+)", 1) }
/**
* A `run` field within an Actions job step, which runs command-line programs using an operating system shell.
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
*/
class Run extends YamlNode, YamlString {
Step step;
Run() { step.lookup("run") = this }
/** Gets the step that executes this `run` command. */
Step getStep() { result = step }
}
}