|
| 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" |
0 commit comments