From 02dc7679aec7f7e3edd727051fe47552feb4197c Mon Sep 17 00:00:00 2001 From: 1sgtpepper Date: Wed, 22 Jul 2026 00:15:17 +0800 Subject: [PATCH 1/2] Fix untrusted checkout path normalization --- .../security/UntrustedCheckoutQuery.qll | 4 +- .../2026-07-21-untrusted-checkout-paths.md | 4 ++ .../workflows/untrusted_checkout_paths.yml | 42 +++++++++++++++++++ .../workflow_run_untrusted_checkout_path.yml | 16 +++++++ .../UntrustedCheckoutCritical.expected | 10 +++++ 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 actions/ql/src/change-notes/2026-07-21-untrusted-checkout-paths.md create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/workflow_run_untrusted_checkout_path.yml diff --git a/actions/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll b/actions/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll index 9668fce2ae00..eb814294e6b9 100644 --- a/actions/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll +++ b/actions/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll @@ -259,7 +259,7 @@ class ActionsMutableRefCheckout extends MutableRefCheckoutStep instanceof UsesSt override string getPath() { if exists(this.(UsesStep).getArgument("path")) - then result = this.(UsesStep).getArgument("path") + then result = normalizePath(this.(UsesStep).getArgument("path")) else result = "GITHUB_WORKSPACE/" } } @@ -294,7 +294,7 @@ class ActionsSHACheckout extends SHACheckoutStep instanceof UsesStep { override string getPath() { if exists(this.(UsesStep).getArgument("path")) - then result = this.(UsesStep).getArgument("path") + then result = normalizePath(this.(UsesStep).getArgument("path")) else result = "GITHUB_WORKSPACE/" } } diff --git a/actions/ql/src/change-notes/2026-07-21-untrusted-checkout-paths.md b/actions/ql/src/change-notes/2026-07-21-untrusted-checkout-paths.md new file mode 100644 index 000000000000..95685ca43e4f --- /dev/null +++ b/actions/ql/src/change-notes/2026-07-21-untrusted-checkout-paths.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `actions/untrusted-checkout/critical` query now recognizes local scripts and actions run from an explicit relative `actions/checkout` path. diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml new file mode 100644 index 000000000000..d8ac3e5133e9 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml @@ -0,0 +1,42 @@ +name: Untrusted checkout paths + +on: + issue_comment: + types: [created] + +jobs: + bare-path: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + with: + ref: ${{ github.head_ref }} + path: checkout + - run: ./checkout/build.sh + + dot-relative-path: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + with: + ref: ${{ github.head_ref }} + path: ./checkout-dot + - run: bash ./checkout-dot/build.sh + + trailing-slash-path: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + with: + ref: ${{ github.head_ref }} + path: checkout-action/ + - uses: ./checkout-action/.github/actions/build + + nested-quoted-path: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + with: + ref: ${{ github.head_ref }} + path: "a/b" + - run: bash ./a/b/build.sh diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/workflow_run_untrusted_checkout_path.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/workflow_run_untrusted_checkout_path.yml new file mode 100644 index 000000000000..d9396afad90e --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/workflow_run_untrusted_checkout_path.yml @@ -0,0 +1,16 @@ +name: Workflow run untrusted checkout path + +on: + workflow_run: + workflows: [Test] + types: [completed] + +jobs: + nested-path: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + with: + ref: ${{ github.event.workflow_run.head_sha }} + path: nested/checkout + - run: ./nested/checkout/build.sh diff --git a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index b6c349bd64fe..2d5484ef5e0e 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -338,6 +338,10 @@ edges | .github/workflows/untrusted_checkout_6.yml:17:9:21:6 | Uses Step | .github/workflows/untrusted_checkout_6.yml:21:9:23:23 | Run Step | | .github/workflows/untrusted_checkout_no_needs.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_no_needs.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:31:9:31:23 | Run Step | +| .github/workflows/untrusted_checkout_paths.yml:11:9:15:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:15:9:17:2 | Run Step | +| .github/workflows/untrusted_checkout_paths.yml:20:9:24:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:24:9:26:2 | Run Step | +| .github/workflows/untrusted_checkout_paths.yml:29:9:33:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:33:9:35:2 | Uses Step | +| .github/workflows/untrusted_checkout_paths.yml:38:9:42:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:42:9:42:33 | Run Step | | .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 | | .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 | | .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 | @@ -350,6 +354,7 @@ edges | .github/workflows/workflow_run_untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_2.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_2.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_3.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_3.yml:16:9:18:31 | 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 | #select | .github/actions/dangerous-git-checkout/action.yml:6:7:11:4 | Uses Step | .github/actions/dangerous-git-checkout/action.yml:6:7:11:4 | Uses Step | .github/workflows/untrusted_checkout3.yml:13:9:13:23 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout3.yml:4:3:4:14 | workflow_run | workflow_run | | .github/workflows/auto_ci.yml:20:9:27:6 | Uses Step | .github/workflows/auto_ci.yml:20:9:27:6 | Uses Step | .github/workflows/auto_ci.yml:32:9:37:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | pull_request_target | @@ -394,4 +399,9 @@ edges | .github/workflows/untrusted_checkout.yml:8:9:11:6 | Uses Step | .github/workflows/untrusted_checkout.yml:8:9:11:6 | Uses Step | .github/workflows/untrusted_checkout.yml:15:9:18:2 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/untrusted_checkout.yml:23:9:26:6 | Uses Step | .github/workflows/untrusted_checkout.yml:23:9:26:6 | Uses Step | .github/workflows/untrusted_checkout.yml:30:9:32:23 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:31:9:31:23 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_no_needs.yml:2:3:2:21 | pull_request_target | pull_request_target | +| .github/workflows/untrusted_checkout_paths.yml:11:9:15:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:11:9:15:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:15:9:17: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:15 | issue_comment | issue_comment | +| .github/workflows/untrusted_checkout_paths.yml:20:9:24:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:20:9:24:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:24:9:26: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:15 | issue_comment | issue_comment | +| .github/workflows/untrusted_checkout_paths.yml:29:9:33:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:29:9:33:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:33:9:35: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:15 | issue_comment | issue_comment | +| .github/workflows/untrusted_checkout_paths.yml:38:9:42:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:38:9:42:6 | Uses Step | .github/workflows/untrusted_checkout_paths.yml:42:9:42:33 | 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:15 | issue_comment | issue_comment | | .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 | +| .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 | From d0263736477a4b8d1ac1ccc527122415dc9f13c5 Mon Sep 17 00:00:00 2001 From: 1sgtpepper Date: Wed, 22 Jul 2026 00:34:46 +0800 Subject: [PATCH 2/2] Clarify nested checkout path coverage --- .../CWE-829/.github/workflows/untrusted_checkout_paths.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml index d8ac3e5133e9..afef8cd538b1 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_paths.yml @@ -32,11 +32,11 @@ jobs: path: checkout-action/ - uses: ./checkout-action/.github/actions/build - nested-quoted-path: + nested-path: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4.2.2 with: ref: ${{ github.head_ref }} - path: "a/b" + path: a/b - run: bash ./a/b/build.sh