Skip to content

Commit 4b9cec7

Browse files
author
Alvaro Muñoz
authored
Merge pull request #17 from GitHubSecurityLab/reusable_workflow_models
feat(reusable-workflow-models): Reusable workflow MaD
2 parents c84e64e + 010d7df commit 4b9cec7

File tree

6 files changed

+67
-26
lines changed

6 files changed

+67
-26
lines changed

ql/lib/codeql/actions/controlflow/internal/Cfg.qll

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -243,21 +243,9 @@ private class JobTree extends StandardPreOrderTree instanceof JobStmt {
243243
}
244244
}
245245

246-
private class StepUsesTree extends StandardPreOrderTree instanceof StepUsesExpr {
247-
override ControlFlowTree getChildNode(int i) {
248-
result =
249-
rank[i](Expression child, Location l |
250-
(child = super.getArgumentExpr(_) or child = super.getEnvExpr(_)) and
251-
l = child.getLocation()
252-
|
253-
child
254-
order by
255-
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
256-
)
257-
}
258-
}
246+
private class UsesExprTree extends LeafTree instanceof UsesExpr { }
259247

260-
private class JobUsesTree extends StandardPreOrderTree instanceof JobUsesExpr {
248+
private class UsesTree extends StandardPreOrderTree instanceof UsesExpr {
261249
override ControlFlowTree getChildNode(int i) {
262250
result =
263251
rank[i](Expression child, Location l |

ql/lib/codeql/actions/dataflow/internal/DataFlowPrivate.qll

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class DataFlowExpr extends Cfg::Node {
5858
}
5959

6060
/**
61-
* A call corresponds to a Uses steps where a 3rd party action or a reusable workflow gets called
61+
* A call corresponds to a Uses steps where a 3rd party action or a reusable workflow get called
6262
*/
6363
class DataFlowCall instanceof Cfg::Node {
6464
DataFlowCall() { super.getAstNode() instanceof UsesExpr }
@@ -180,6 +180,23 @@ predicate stepsCtxLocalStep(Node nodeFrom, Node nodeTo) {
180180
)
181181
}
182182

183+
/**
184+
* Holds if there is a local flow step between a ${{ needs.xxx.outputs.yyy }} expression accesing a job output field
185+
* and the step output itself. But only for those cases where the job (needs) output is defined externally in a MaD Source
186+
* specification. The reason for this is that we don't currently have a way to specify that a source starts with a
187+
* non-empty access path so we cannot write a Source that stores the taint in a Content, we can only do that for steps
188+
* (storeStep). The easiest thing is to add this local flow step that simulates a read step from the source node for a specific
189+
* field name.
190+
*/
191+
predicate needsCtxLocalStep(Node nodeFrom, Node nodeTo) {
192+
exists(UsesExpr astFrom, NeedsCtxAccessExpr astTo |
193+
externallyDefinedSource(nodeFrom, _, "output." + astTo.getFieldName()) and
194+
astFrom = nodeFrom.asExpr() and
195+
astTo = nodeTo.asExpr() and
196+
astTo.getRefExpr() = astFrom
197+
)
198+
}
199+
183200
/**
184201
* Holds if there is a local flow step between a ${{}} expression accesing an input variable and the input itself
185202
* e.g. ${{ inputs.foo }}
@@ -215,6 +232,7 @@ predicate envCtxLocalStep(Node nodeFrom, Node nodeTo) {
215232
pragma[nomagic]
216233
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
217234
stepsCtxLocalStep(nodeFrom, nodeTo) or
235+
needsCtxLocalStep(nodeFrom, nodeTo) or
218236
inputsCtxLocalStep(nodeFrom, nodeTo) or
219237
envCtxLocalStep(nodeFrom, nodeTo)
220238
}

ql/lib/codeql/actions/dataflow/internal/DataFlowPublic.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ class ParameterNode extends ExprNode {
6666
InputExpr getInputExpr() { result = input }
6767
}
6868

69+
/**
70+
* A call to a data flow callable (Uses).
71+
*/
72+
class CallNode extends ExprNode {
73+
private DataFlowCall call;
74+
75+
CallNode() { this.getCfgNode() instanceof DataFlowCall }
76+
77+
string getCallee() { result = this.getCfgNode().(DataFlowCall).getName() }
78+
}
79+
6980
/**
7081
* An argument to a Uses step (call).
7182
*/

ql/lib/ext/TEST-RW-MODELS.model.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extensions:
2+
- addsTo:
3+
pack: githubsecuritylab/actions-all
4+
extensible: summaryModel
5+
data:
6+
- ["octo-org/this-repo/.github/workflows/workflow.yml", "*", "input.config-path", "output.workflow-output", "taint"]
7+
- ["octo-org/summary-repo/.github/workflows/workflow.yml", "*", "input.config-path", "output.workflow-output", "taint"]
8+
- addsTo:
9+
pack: githubsecuritylab/actions-all
10+
extensible: sourceModel
11+
data:
12+
- ["octo-org/source-repo/.github/workflows/workflow.yml", "*", "output.workflow-output", "*", "Foo"]
13+
- addsTo:
14+
pack: githubsecuritylab/actions-all
15+
extensible: sinkModel
16+
data:
17+
- ["octo-org/sink-repo/.github/workflows/workflow.yml", "*", "input.config-path", "expression-injection"]

ql/lib/test/test.ql

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,9 @@ query predicate nonOrphanVarAccesses(ExprAccessExpr va, string var, AstNode pare
4343

4444
query predicate parentNodes(AstNode child, AstNode parent) { child.getParentNode() = parent }
4545

46-
query predicate cfgNodes(Cfg::Node n) {
47-
//any()
48-
n.getAstNode() instanceof OutputsStmt
49-
}
46+
query predicate cfgNodes(Cfg::Node n) { any() }
5047

51-
query predicate dfNodes(DataFlow::Node e) {
52-
e.getLocation().getFile().getBaseName() = "argus_case_study.yml"
53-
}
48+
query predicate dfNodes(DataFlow::Node e) { any() }
5449

5550
query predicate exprNodes(DataFlow::ExprNode e) { any() }
5651

@@ -69,3 +64,7 @@ query predicate sources(string action, string version, string output, string tri
6964
query predicate summaries(string action, string version, string input, string output, string kind) {
7065
summaryModel(action, version, input, output, kind)
7166
}
67+
68+
query predicate calls(DataFlow::CallNode call, string callee) { callee = call.getCallee() }
69+
70+
query predicate needs(DataFlow::ExprNode e) { e.asExpr() instanceof NeedsCtxAccessExpr }

ql/src/test/.github/workflows/calling_workflow.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ jobs:
88
uses: octo-org/this-repo/.github/workflows/reusable_workflow.yml@172239021f7ba04fe7327647b213799853a9eb89
99
with:
1010
config-path: ${{ github.event.pull_request.head.ref }}
11-
secrets: inherit
1211
call2:
1312
uses: ./.github/workflows/reusable_workflow.yml
1413
with:
1514
config-path: ${{ github.event.pull_request.head.ref }}
16-
secrets: inherit
1715
call3:
18-
uses: octo-org/another-repo/.github/workflows/workflow.yml@v1
16+
uses: octo-org/summary-repo/.github/workflows/workflow.yml@v1
17+
with:
18+
config-path: ${{ github.event.pull_request.head.ref }}
19+
call4:
20+
uses: octo-org/source-repo/.github/workflows/workflow.yml@v1
21+
call5:
22+
uses: octo-org/sink-repo/.github/workflows/workflow.yml@v1
1923
with:
2024
config-path: ${{ github.event.pull_request.head.ref }}
21-
secrets: inherit
2225

2326
job1:
2427
runs-on: ubuntu-latest
@@ -36,3 +39,8 @@ jobs:
3639
needs: call3
3740
steps:
3841
- run: echo ${{ needs.call3.outputs.workflow-output }}
42+
job4:
43+
runs-on: ubuntu-latest
44+
needs: call4
45+
steps:
46+
- run: echo ${{ needs.call4.outputs.workflow-output }}

0 commit comments

Comments
 (0)