Skip to content

Commit bb028e4

Browse files
author
Alvaro Muñoz
committed
Add Cache Poisoning Query
1 parent addedd0 commit bb028e4

File tree

8 files changed

+173
-0
lines changed

8 files changed

+173
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import actions
2+
3+
abstract class CacheWritingStep extends Step { }
4+
5+
class CacheActionUsesStep extends CacheWritingStep, UsesStep {
6+
CacheActionUsesStep() { this.getCallee() = "actions/cache" }
7+
}
8+
9+
class CacheActionSaveUsesStep extends CacheWritingStep, UsesStep {
10+
CacheActionSaveUsesStep() { this.getCallee() = "actions/cache/save" }
11+
}
12+
13+
class SetupJavaUsesStep extends CacheWritingStep, UsesStep {
14+
SetupJavaUsesStep() {
15+
this.getCallee() = "actions/setup-java" and
16+
(
17+
exists(this.getArgument("cache")) or
18+
exists(this.getArgument("cache-dependency-path"))
19+
)
20+
}
21+
}
22+
23+
class SetupGoUsesStep extends CacheWritingStep, UsesStep {
24+
SetupGoUsesStep() { this.getCallee() = "actions/setup-go" }
25+
}
26+
27+
class SetupNodeUsesStep extends CacheWritingStep, UsesStep {
28+
SetupNodeUsesStep() {
29+
this.getCallee() = "actions/setup-node" and
30+
(
31+
exists(this.getArgument("cache")) or
32+
exists(this.getArgument("cache-dependency-path"))
33+
)
34+
}
35+
}
36+
37+
class SetupPythonUsesStep extends CacheWritingStep, UsesStep {
38+
SetupPythonUsesStep() {
39+
this.getCallee() = "actions/setup-python" and
40+
(
41+
exists(this.getArgument("cache")) or
42+
exists(this.getArgument("cache-dependency-path"))
43+
)
44+
}
45+
}
46+
47+
class SetupDotnetUsesStep extends CacheWritingStep, UsesStep {
48+
SetupDotnetUsesStep() {
49+
this.getCallee() = "actions/setup-dotnet" and
50+
(
51+
exists(this.getArgument("cache")) or
52+
exists(this.getArgument("cache-dependency-path"))
53+
)
54+
}
55+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @name Cache Poisoning
3+
* @description The cache can be poisoned by untrusted code, leading to a cache poisoning attack.
4+
* @kind problem
5+
* @problem.severity error
6+
* @precision high
7+
* @security-severity 9.3
8+
* @id actions/cache-poisoning
9+
* @tags actions
10+
* security
11+
* external/cwe/cwe-349
12+
*/
13+
14+
import actions
15+
import codeql.actions.security.UntrustedCheckoutQuery
16+
import codeql.actions.security.CachePoisoningQuery
17+
18+
from Workflow w, PRHeadCheckoutStep checkout, LocalJob j
19+
where
20+
// TODO: (require to collect trigger types)
21+
// - add push to default branch?
22+
// - exclude pull_request_target when branches_ignore includes default branch or when branches does not include the default branch
23+
w.hasTriggerEvent([
24+
"check_run", "check_suite", "delete", "discussion", "discussion_comment", "fork", "gollum",
25+
"issue_comment", "issues", "label", "milestone", "project", "project_card", "project_column",
26+
"public", "pull_request_comment", "pull_request_target", "repository_dispatch", "schedule",
27+
"watch", "workflow_run"
28+
]) and
29+
// Workflow is privileged
30+
w.isPrivileged() and
31+
// The workflow checkouts untrusted code from a pull request
32+
j = w.getAJob() and
33+
j.getAStep() = checkout and
34+
// The checkout step is followed by a cache writing step
35+
j.getAStep() instanceof CacheWritingStep
36+
select checkout, "Potential cache poisoning on privileged workflow."
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
on:
2+
issue_comment:
3+
types: [created]
4+
5+
jobs:
6+
pr-comment:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: xt0rted/pull-request-comment-branch@v2
10+
id: comment-branch
11+
12+
- uses: actions/checkout@v3
13+
if: success()
14+
with:
15+
ref: ${{ steps.comment-branch.outputs.head_sha }}
16+
17+
- uses: actions/cache@v2
18+
with:
19+
path: ./poison
20+
key: poison_key
21+
- run: |
22+
cat poison
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Cache Poisoning
2+
3+
on: pull_request_target
4+
5+
jobs:
6+
poison:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v3
10+
with:
11+
ref: ${{ github.event.pull_request.head.sha }}
12+
- uses: actions/cache@v2
13+
with:
14+
path: ./poison
15+
key: poison_key
16+
- run: |
17+
cat poison
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Cache Poisoning
2+
3+
on: pull_request_target
4+
5+
jobs:
6+
poison:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v3
10+
with:
11+
ref: ${{ github.event.pull_request.head.sha }}
12+
- uses: actions/setup-java@v2
13+
with:
14+
distribution: 'zulu'
15+
java-version: '21'
16+
cache: 'gradle'
17+
cache-dependency-path: |
18+
sub-project/*.gradle*
19+
sub-project/**/gradle-wrapper.properties
20+
- run: |
21+
java HelloWorldApp.java
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Cache Poisoning
2+
3+
on: pull_request_target
4+
5+
jobs:
6+
poison:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v3
10+
with:
11+
ref: ${{ github.event.pull_request.head.sha }}
12+
- uses: actions/setup-java@v2
13+
with:
14+
distribution: 'zulu'
15+
java-version: '21'
16+
- run: |
17+
java HelloWorldApp.java
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| .github/workflows/test1.yml:12:9:17:6 | Uses Step | Potential cache poisoning on privileged workflow. |
2+
| .github/workflows/test2.yml:9:9:12:6 | Uses Step | Potential cache poisoning on privileged workflow. |
3+
| .github/workflows/test3.yml:9:9:12:6 | Uses Step | Potential cache poisoning on privileged workflow. |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Security/CWE-349/CachePoisoning.ql
2+

0 commit comments

Comments
 (0)