Skip to content

Commit 45d959d

Browse files
author
Alvaro Muñoz
committed
Initial implementation
1 parent 70a7bb1 commit 45d959d

File tree

455 files changed

+3801
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

455 files changed

+3801
-0
lines changed

build-dbs.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
rm -rf ql/src/test-db || true
3+
rm -rf ql/lib/test-db || true
4+
codeql database create ql/src/test-db -l yaml -s ql/src/test
5+
codeql database create ql/lib/test-db -l yaml -s ql/lib/test

codeql-workspace.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
provide:
2+
- "**/ql/src/qlpack.yml"
3+
- "**/ql/lib/qlpack.yml"

ql/lib/actions.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import codeql.actions.Ast

ql/lib/codeql-pack.lock.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
lockVersion: 1.0.0
3+
dependencies:
4+
codeql/controlflow:
5+
version: 0.1.7
6+
codeql/dataflow:
7+
version: 0.1.7
8+
codeql/ssa:
9+
version: 0.2.7
10+
codeql/typetracking:
11+
version: 0.2.7
12+
codeql/util:
13+
version: 0.2.7
14+
codeql/yaml:
15+
version: 0.2.7
16+
compiled: false

ql/lib/codeql/Locations.qll

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/** Provides classes for working with locations. */
2+
3+
import files.FileSystem
4+
5+
bindingset[loc]
6+
pragma[inline_late]
7+
private string locationToString(Location loc) {
8+
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
9+
loc.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
10+
result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn
11+
)
12+
}
13+
14+
/**
15+
* A location as given by a file, a start line, a start column,
16+
* an end line, and an end column.
17+
*
18+
* For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
19+
*/
20+
class Location extends @location_default {
21+
/** Gets the file for this location. */
22+
File getFile() { locations_default(this, result, _, _, _, _) }
23+
24+
/** Gets the 1-based line number (inclusive) where this location starts. */
25+
int getStartLine() { locations_default(this, _, result, _, _, _) }
26+
27+
/** Gets the 1-based column number (inclusive) where this location starts. */
28+
int getStartColumn() { locations_default(this, _, _, result, _, _) }
29+
30+
/** Gets the 1-based line number (inclusive) where this location ends. */
31+
int getEndLine() { locations_default(this, _, _, _, result, _) }
32+
33+
/** Gets the 1-based column number (inclusive) where this location ends. */
34+
int getEndColumn() { locations_default(this, _, _, _, _, result) }
35+
36+
/** Gets the number of lines covered by this location. */
37+
int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 }
38+
39+
/** Gets a textual representation of this element. */
40+
pragma[inline]
41+
string toString() { result = locationToString(this) }
42+
43+
/**
44+
* Holds if this element is at the specified location.
45+
* The location spans column `startcolumn` of line `startline` to
46+
* column `endcolumn` of line `endline` in file `filepath`.
47+
* For more information, see
48+
* [Providing locations in CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
49+
*/
50+
predicate hasLocationInfo(
51+
string filepath, int startline, int startcolumn, int endline, int endcolumn
52+
) {
53+
exists(File f |
54+
locations_default(this, f, startline, startcolumn, endline, endcolumn) and
55+
filepath = f.getAbsolutePath()
56+
)
57+
}
58+
59+
/** Holds if this location starts strictly before the specified location. */
60+
pragma[inline]
61+
predicate strictlyBefore(Location other) {
62+
this.getStartLine() < other.getStartLine()
63+
or
64+
this.getStartLine() = other.getStartLine() and this.getStartColumn() < other.getStartColumn()
65+
}
66+
}
67+
68+
/** An entity representing an empty location. */
69+
class EmptyLocation extends Location {
70+
EmptyLocation() { this.hasLocationInfo("", 0, 0, 0, 0) }
71+
}

ql/lib/codeql/actions/Ast.qll

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
private import codeql.actions.ast.internal.Actions
2+
private import codeql.Locations
3+
4+
class AstNode instanceof YamlNode {
5+
AstNode getParentNode() {
6+
if exists(YamlMapping m | m.maps(_, this))
7+
then exists(YamlMapping m | m.maps(result, this))
8+
else result = super.getParentNode()
9+
}
10+
11+
AstNode getAChildNode() {
12+
if this instanceof YamlMapping
13+
then this.(YamlMapping).maps(result, _)
14+
else
15+
if this instanceof YamlCollection
16+
then result = super.getChildNode(_)
17+
else
18+
if this instanceof YamlScalar and exists(YamlMapping m | m.maps(this, _))
19+
then exists(YamlMapping m | m.maps(this, result))
20+
else none()
21+
}
22+
23+
AstNode getChildNodeByOrder(int i) {
24+
result =
25+
rank[i](Expression child, Location l |
26+
child = this.getAChildNode() and
27+
child.getLocation() = l
28+
|
29+
child
30+
order by
31+
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
32+
)
33+
}
34+
35+
string toString() { result = super.toString() }
36+
37+
string getAPrimaryQlClass() { result = super.getAPrimaryQlClass() }
38+
39+
Location getLocation() { result = super.getLocation() }
40+
}
41+
42+
class Statement extends AstNode {
43+
// narrow down to something that is a statement
44+
// A statement is a group of expressions and/or statements that you design to carry out a task or an action.
45+
// Any statement that can return a value is automatically qualified to be used as an expression.
46+
}
47+
48+
class Expression extends Statement {
49+
// narrow down to something that is an expression
50+
// An expression is any word or group of words or symbols that is a value. In programming, an expression is a value, or anything that executes and ends up being a value.
51+
}
52+
53+
/**
54+
* A Job is a collection of steps that run in an execution environment.
55+
*/
56+
class JobStmt extends Statement instanceof Actions::Job {
57+
/**
58+
* Gets the ID of this job, as a string.
59+
* This is the job's key within the `jobs` mapping.
60+
*/
61+
string getId() { result = super.getId() }
62+
63+
/** Gets the human-readable name of this job, if any, as a string. */
64+
string getName() {
65+
result = super.getId()
66+
or
67+
not exists(string s | s = super.getId()) and result = "unknown"
68+
}
69+
70+
/** Gets the step at the given index within this job. */
71+
StepStmt getStep(int index) { result = super.getStep(index) }
72+
73+
/** Gets any steps that are defined within this job. */
74+
StepStmt getAStep() { result = super.getStep(_) }
75+
76+
JobStmt getNeededJob() {
77+
exists(Actions::Needs needs |
78+
needs.getJob() = this and
79+
result = needs.getANeededJob().(JobStmt)
80+
)
81+
}
82+
83+
Expression getJobOutputExpr(string varName) {
84+
this.(Actions::Job)
85+
.lookup("outputs")
86+
.(YamlMapping)
87+
.maps(any(YamlScalar a | a.getValue() = varName), result)
88+
}
89+
90+
JobOutputStmt getJobOutputStmt() { result = this.(Actions::Job).lookup("outputs") }
91+
92+
Statement getSuccNode(int i) {
93+
result =
94+
rank[i](Expression child, Location l |
95+
(child = this.getAStep() or child = this.getJobOutputStmt()) and
96+
l = child.getLocation()
97+
|
98+
child
99+
order by
100+
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
101+
)
102+
}
103+
}
104+
105+
class JobOutputStmt extends Statement instanceof YamlMapping {
106+
JobStmt job;
107+
108+
JobOutputStmt() { job.(YamlMapping).lookup("outputs") = this }
109+
110+
StepOutputAccessExpr getSuccNode(int i) { result = this.(YamlMapping).getValueNode(i) }
111+
}
112+
113+
/**
114+
* A Step is a single task that can be executed as part of a job.
115+
*/
116+
class StepStmt extends Statement instanceof Actions::Step {
117+
string getId() { result = super.getId() }
118+
119+
string getName() {
120+
result = super.getId()
121+
or
122+
not exists(string s | s = super.getId()) and result = "unknown"
123+
}
124+
125+
JobStmt getJob() { result = super.getJob() }
126+
127+
abstract AstNode getSuccNode(int i);
128+
}
129+
130+
/**
131+
* A Uses step represents a call to an action that is defined in a GitHub repository.
132+
*/
133+
class UsesExpr extends StepStmt, Expression {
134+
Actions::Uses uses;
135+
136+
UsesExpr() { uses.getStep() = this }
137+
138+
string getTarget() { result = uses.getGitHubRepository() }
139+
140+
string getVersion() { result = uses.getVersion() }
141+
142+
Expression getArgument(string key) {
143+
exists(Actions::With with |
144+
with.getStep() = this and
145+
result = with.lookup(key)
146+
)
147+
}
148+
149+
Expression getArgumentByOrder(int i) {
150+
exists(Actions::With with |
151+
with.getStep() = uses.getStep() and
152+
result =
153+
rank[i](Expression child, Location l |
154+
child = with.lookup(_) and l = child.getLocation()
155+
|
156+
child
157+
order by
158+
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
159+
)
160+
)
161+
}
162+
163+
Expression getAnArgument() {
164+
exists(Actions::With with |
165+
with.getStep() = this and
166+
result = with.lookup(_)
167+
)
168+
}
169+
170+
override AstNode getSuccNode(int i) { result = this.getArgumentByOrder(i) }
171+
}
172+
173+
/**
174+
* An argument passed to a UsesExpr.
175+
*/
176+
class ArgumentExpr extends Expression {
177+
UsesExpr uses;
178+
179+
ArgumentExpr() { this = uses.getAnArgument() }
180+
}
181+
182+
/**
183+
* A Run step represents a call to an inline script or executable on the runner machine.
184+
*/
185+
class RunExpr extends StepStmt {
186+
Actions::Run scriptExpr;
187+
188+
RunExpr() { scriptExpr.getStep() = this }
189+
190+
Expression getScriptExpr() { result = scriptExpr }
191+
192+
string getScript() { result = scriptExpr.getValue() }
193+
194+
override AstNode getSuccNode(int i) { result = this.getScriptExpr() and i = 0 }
195+
}
196+
197+
/**
198+
* A YAML string containing a workflow expression.
199+
*/
200+
class ExprAccessExpr extends Expression instanceof YamlString {
201+
string expr;
202+
203+
ExprAccessExpr() { expr = Actions::getASimpleReferenceExpression(this) }
204+
205+
string getExpression() { result = expr }
206+
207+
JobStmt getJob() { result.getAChildNode*() = this }
208+
}
209+
210+
/**
211+
* A ExprAccessExpr where the expression references a step output.
212+
* eg: `${{ steps.changed-files.outputs.all_changed_files }}`
213+
*/
214+
class StepOutputAccessExpr extends ExprAccessExpr {
215+
string stepId;
216+
string varName;
217+
218+
StepOutputAccessExpr() {
219+
stepId =
220+
this.getExpression().regexpCapture("steps\\.([A-Za-z0-9_-]+)\\.outputs\\.[A-Za-z0-9_-]+", 1) and
221+
varName =
222+
this.getExpression().regexpCapture("steps\\.[A-Za-z0-9_-]+\\.outputs\\.([A-Za-z0-9_-]+)", 1)
223+
}
224+
225+
string getStepId() { result = stepId }
226+
227+
string getVarName() { result = varName }
228+
229+
StepStmt getStep() { result.getId() = stepId }
230+
}
231+
232+
/**
233+
* A ExprAccessExpr where the expression references a job output.
234+
* eg: `${{ needs.job1.outputs.foo}}`
235+
*/
236+
class JobOutputAccessExpr extends ExprAccessExpr {
237+
string jobId;
238+
string varName;
239+
240+
JobOutputAccessExpr() {
241+
jobId =
242+
this.getExpression().regexpCapture("needs\\.([A-Za-z0-9_-]+)\\.outputs\\.[A-Za-z0-9_-]+", 1) and
243+
varName =
244+
this.getExpression().regexpCapture("needs\\.[A-Za-z0-9_-]+\\.outputs\\.([A-Za-z0-9_-]+)", 1)
245+
}
246+
247+
string getVarName() { result = varName }
248+
249+
Expression getOutputExpr() {
250+
exists(JobStmt job |
251+
job.getId() = jobId and
252+
job.getLocation().getFile() = this.getLocation().getFile() and
253+
job.getJobOutputExpr(varName) = result
254+
)
255+
}
256+
}

ql/lib/codeql/actions/Cfg.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** Provides classes representing the control flow graph. */
2+
3+
private import codeql.actions.controlflow.internal.Cfg as CfgInternal
4+
import CfgInternal::Completion
5+
import CfgInternal::CfgScope
6+
import CfgInternal::CfgImpl
7+

ql/lib/codeql/actions/DataFlow.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Provides classes for performing local (intra-procedural) and
3+
* global (inter-procedural) data flow analyses.
4+
*/
5+
module DataFlow {
6+
private import codeql.dataflow.DataFlow
7+
private import codeql.actions.dataflow.internal.DataFlowImplSpecific
8+
import DataFlowMake<ActionsDataFlow>
9+
import codeql.actions.dataflow.internal.DataFlowPublic
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Provides classes for performing local (intra-procedural) and
3+
* global (inter-procedural) taint-tracking analyses.
4+
*/
5+
module TaintTracking {
6+
private import codeql.actions.dataflow.internal.DataFlowImplSpecific
7+
private import codeql.actions.dataflow.internal.TaintTrackingImplSpecific
8+
private import codeql.dataflow.TaintTracking
9+
import TaintFlowMake<ActionsDataFlow, ActionsTaintTracking>
10+
}

0 commit comments

Comments
 (0)