Skip to content

Commit b324dad

Browse files
committed
Handle workspace-root execution paths
1 parent 857758f commit b324dad

3 files changed

Lines changed: 60 additions & 10 deletions

File tree

actions/ql/src/Security/CWE-829/UntrustedCheckoutCritical.ql

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,22 @@ private class ActionsCheckoutPathInput extends NormalizableFilepath {
3131
}
3232
}
3333

34+
bindingset[path]
35+
private string normalizeWorkspaceExecutionPath(string path) {
36+
// Only GitHub's workspace variable has known path provenance; leave other variables unresolved.
37+
if path.indexOf("$GITHUB_WORKSPACE/") = 0
38+
then result = path.regexpReplaceAll("^\\$GITHUB_WORKSPACE/", "GITHUB_WORKSPACE/")
39+
else
40+
if path.indexOf("${GITHUB_WORKSPACE}/") = 0
41+
then result = path.regexpReplaceAll("^\\$\\{GITHUB_WORKSPACE\\}/", "GITHUB_WORKSPACE/")
42+
else result = path
43+
}
44+
3445
private class ExecutionPathInput extends NormalizableFilepath {
3546
ExecutionPathInput() {
36-
exists(LocalScriptExecutionRunStep step | this = trimQuotes(step.getRawPath()))
47+
exists(LocalScriptExecutionRunStep step |
48+
this = normalizeWorkspaceExecutionPath(trimQuotes(step.getRawPath()))
49+
)
3750
or
3851
exists(LocalActionUsesStep step | this = step.getCallee())
3952
}
@@ -57,19 +70,27 @@ private string getNormalizedActionsCheckoutPath(PRHeadCheckoutStep checkout) {
5770

5871
bindingset[path]
5972
private string getNormalizedExecutionPath(string path) {
60-
exists(ExecutionPathInput executionPath, string normalized |
61-
executionPath = trimQuotes(path) and
73+
exists(ExecutionPathInput executionPath, string rawPath, string normalized |
74+
rawPath = trimQuotes(path) and
75+
executionPath = normalizeWorkspaceExecutionPath(rawPath) and
6276
not executionPath.regexpMatch(".*\\$\\{\\{.*") and
6377
normalized = executionPath.getNormalizedPath() and
6478
not normalized.matches("/%") and
6579
normalized != ".." and
6680
not normalized.matches("../%") and
67-
if normalized = "."
68-
then result = "GITHUB_WORKSPACE"
69-
else (
70-
normalized.regexpMatch("^[^$/~].*") and
71-
result = "GITHUB_WORKSPACE/" + normalized
72-
)
81+
if
82+
rawPath.indexOf("$GITHUB_WORKSPACE/") = 0 or
83+
rawPath.indexOf("${GITHUB_WORKSPACE}/") = 0
84+
then
85+
(normalized = "GITHUB_WORKSPACE" or normalized.matches("GITHUB_WORKSPACE/%")) and
86+
result = normalized
87+
else
88+
if normalized = "."
89+
then result = "GITHUB_WORKSPACE"
90+
else (
91+
normalized.regexpMatch("^[^$/~].*") and
92+
result = "GITHUB_WORKSPACE/" + normalized
93+
)
7394
)
7495
}
7596

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,21 @@ jobs:
186186
ref: ${{ github.event.pull_request.head.sha }}
187187
path: candidate
188188
- run: bash ./GITHUB_WORKSPACE/candidate/build.sh
189+
190+
workspace-variable-prefixes:
191+
runs-on: ubuntu-latest
192+
steps:
193+
- uses: actions/checkout@v4.2.2
194+
with:
195+
repository: ${{ github.event.pull_request.head.repo.full_name }}
196+
ref: ${{ github.event.pull_request.head.sha }}
197+
path: candidate
198+
- run: bash $GITHUB_WORKSPACE/candidate/build.sh
199+
- run: bash $GITHUB_WORKSPACE/candidate-sibling/build.sh
200+
- run: bash $GITHUB_WORKSPACE/candidate/../trusted.sh
201+
- run: bash $GITHUB_WORKSPACE/../candidate/build.sh
202+
- run: bash $OTHER_WORKSPACE/candidate/build.sh
203+
- run: bash ${GITHUB_WORKSPACE}/candidate/build.sh
204+
- run: bash ${GITHUB_WORKSPACE}/candidate-sibling/build.sh
205+
- run: bash ${GITHUB_WORKSPACE}/candidate/../trusted.sh
206+
- run: bash ${GITHUB_WORKSPACE}/../candidate/build.sh

actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,16 @@ edges
355355
| .github/workflows/untrusted_checkout_paths.yml:153:9:158:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:158:9:160:2 | Run Step |
356356
| .github/workflows/untrusted_checkout_paths.yml:163:9:168:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:168:9:170:2 | Run Step |
357357
| .github/workflows/untrusted_checkout_paths.yml:173:9:178:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:178:9:180:2 | Run Step |
358-
| .github/workflows/untrusted_checkout_paths.yml:183:9:188:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:188:9:188:56 | Run Step |
358+
| .github/workflows/untrusted_checkout_paths.yml:183:9:188:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:188:9:190:2 | Run Step |
359+
| .github/workflows/untrusted_checkout_paths.yml:193:9:198:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:198:9:199:6 | Run Step |
360+
| .github/workflows/untrusted_checkout_paths.yml:198:9:199:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:199:9:200:6 | Run Step |
361+
| .github/workflows/untrusted_checkout_paths.yml:199:9:200:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:200:9:201:6 | Run Step |
362+
| .github/workflows/untrusted_checkout_paths.yml:200:9:201:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:201:9:202:6 | Run Step |
363+
| .github/workflows/untrusted_checkout_paths.yml:201:9:202:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:202:9:203:6 | Run Step |
364+
| .github/workflows/untrusted_checkout_paths.yml:202:9:203:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:203:9:204:6 | Run Step |
365+
| .github/workflows/untrusted_checkout_paths.yml:203:9:204:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:204:9:205:6 | Run Step |
366+
| .github/workflows/untrusted_checkout_paths.yml:204:9:205:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:205:9:206:6 | Run Step |
367+
| .github/workflows/untrusted_checkout_paths.yml:205:9:206:6 | Run Step | .github/workflows/untrusted_checkout_paths.yml:206:9:206:60 | Run Step |
359368
| .github/workflows/untrusted_checkout_permission_check_reusable2.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable2.yml:16:9:22:2 | Run Step |
360369
| .github/workflows/untrusted_checkout_permission_check_reusable.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable.yml:16:9:22:2 | Run Step |
361370
| .github/workflows/untrusted_checkout_permission_check_reusable_level2.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable_level2.yml:16:9:22:2 | Run Step |
@@ -424,5 +433,7 @@ edges
424433
| .github/workflows/untrusted_checkout_paths.yml:110:9:115:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:110:9:115:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:115:9:119:2 | Uses Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_paths.yml:4:3:4:21 | pull_request_target | pull_request_target |
425434
| .github/workflows/untrusted_checkout_paths.yml:153:9:158:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:153:9:158:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:158:9:160:2 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_paths.yml:4:3:4:21 | pull_request_target | pull_request_target |
426435
| .github/workflows/untrusted_checkout_paths.yml:163:9:168:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:163:9:168:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:168:9:170:2 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_paths.yml:4:3:4:21 | pull_request_target | pull_request_target |
436+
| .github/workflows/untrusted_checkout_paths.yml:193:9:198:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:193:9:198:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:198:9:199:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_paths.yml:4:3:4:21 | pull_request_target | pull_request_target |
437+
| .github/workflows/untrusted_checkout_paths.yml:193:9:198:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:193:9:198:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:203:9:204:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_paths.yml:4:3:4:21 | pull_request_target | pull_request_target |
427438
| .github/workflows/untrusted_checkout_permissions_check.yml:36:9:41:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:36:9:41:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:41:9:41:22 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permissions_check.yml:2:3:2:21 | pull_request_target | pull_request_target |
428439
| .github/workflows/workflow_run_untrusted_checkout_path.yml:12:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_path.yml:12:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_path.yml:16:9:16:40 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/workflow_run_untrusted_checkout_path.yml:4:3:4:14 | workflow_run | workflow_run |

0 commit comments

Comments
 (0)