Skip to content

Commit 5f1884a

Browse files
author
Alvaro Muñoz
committed
feat(queries): Add new queries to report path traversal via artifact poisoning
1 parent 483f622 commit 5f1884a

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @name Artifact Poisoning (Path Traversal).
3+
* @description An attacker may be able to poison the workflow's artifacts and influence on consequent steps.
4+
* @kind problem
5+
* @problem.severity error
6+
* @precision very-high
7+
* @security-severity 9
8+
* @id actions/artifact-poisoning/path-traversal
9+
* @tags actions
10+
* security
11+
* experimental
12+
* external/cwe/cwe-829
13+
*/
14+
15+
import actions
16+
import codeql.actions.security.PoisonableSteps
17+
18+
from UsesStep download
19+
where
20+
download.getCallee() = "actions/download-artifact" and
21+
download.getCallee() = "actions/download-artifact" and
22+
(
23+
download.getVersion() =
24+
[
25+
"4.1.6", "4.1.5", "4.1.4", "4.1.3", "4.1.2", "4.1.1", "4.1.0", "4.0.0", "3.0.2", "3.0.1",
26+
"3.0.0", "3", "3-node20", "2.1.1", "2.1.0", "2.0.10", "2.0.9", "2.0.8", "2.0.7", "2.0.6",
27+
"2.0.5", "2.0.4", "2.0.3", "2.0.2", "2.0.1", "2.0", "2", "1.0.0", "1", "1.0.0",
28+
]
29+
or
30+
download
31+
.getVersion()
32+
.matches([
33+
"9c19ed7f", "8caf195a", "c850b930", "87c55149", "eaceaf80", "6b208ae0", "f44cd7b4",
34+
"7a1cd321", "9bc31d5c", "9782bd6a", "fb598a63", "9bc31d5c", "246d7188", "cbed621e",
35+
"f023be2c", "3be87be1", "158ca71f", "4a7a7112", "f144d3c3", "f8e41fbf", "c3f5d00c",
36+
"b3cedea9", "80d2d402", "381af06b", "1ac47ba4", "1de1dea8", "cbed621e", "18f0f591",
37+
"18f0f591", "18f0f591",
38+
] + "%")
39+
) and
40+
(
41+
// exists a poisonable upload artifact in the same workflow
42+
exists(UsesStep checkout, PoisonableStep poison, UsesStep upload |
43+
download.getEnclosingWorkflow().getAJob().(LocalJob).getAStep() = checkout and
44+
download.getEnclosingJob().isPrivilegedExternallyTriggerable() and
45+
checkout.getCallee() = "actions/checkout" and
46+
checkout.getAFollowingStep() = poison and
47+
poison.getAFollowingStep() = upload and
48+
upload.getCallee() = "actions/upload-artifact"
49+
)
50+
or
51+
// upload artifact is not used in the same workflow
52+
not exists(UsesStep upload |
53+
download.getEnclosingWorkflow().getAJob().(LocalJob).getAStep() = upload
54+
)
55+
)
56+
select download, "Potential artifact poisoning"

ql/test/query-tests/Security/CWE-829/.github/workflows/artifactpoisoning81.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
permissions:
2626
contents: write
2727
steps:
28-
- uses: actions/download-artifact@v4
28+
- uses: actions/download-artifact@v3
2929
with:
3030
name: results
3131
- run: python test.py
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | Potential artifact poisoning |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Security/CWE-829/ArtifactPoisoningPathTraversal.ql
2+

0 commit comments

Comments
 (0)