From 64713f913b38bb4354d29ebede7e64b98b90c327 Mon Sep 17 00:00:00 2001 From: Jacek Date: Tue, 16 Jun 2026 08:37:16 -0500 Subject: [PATCH] ci(repo): harden workflow shell-injection surface (SDK-79) Move attacker-influenced ${{ }} expressions out of run: blocks and into env in the two fork-PR-reachable ci.yml guard steps (check-permissions echoes github.actor/github.triggering_actor/the permission level; require-changeset compares github.event.pull_request.user.login), so a crafted actor name or PR author login can no longer be interpolated into the shell. Drop the unused actions: write from e2e-staging.yml (no step consumes it; contents: read is enough). Annotate the labeler workflow_run trigger as an intentional, guarded handshake so zizmor's dangerous-triggers audit does not flag it. Closes the High-confidence zizmor template-injection and excessive-permissions findings in these files; zizmor adoption as a CI check is tracked with Security (SEC-307). --- .changeset/sdk-79-template-injection-fixes.md | 2 ++ .github/workflows/ci.yml | 14 ++++++++++---- .github/workflows/e2e-staging.yml | 1 - .github/workflows/labeler-apply.yml | 7 ++++++- 4 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 .changeset/sdk-79-template-injection-fixes.md diff --git a/.changeset/sdk-79-template-injection-fixes.md b/.changeset/sdk-79-template-injection-fixes.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/sdk-79-template-injection-fixes.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6e639c157e..99c0ad1559f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,10 +39,14 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check User Permission if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && steps.checkAccess.outputs.require-result == 'false' }} + env: + TRIGGERING_ACTOR: ${{ github.triggering_actor }} + USER_PERMISSION: ${{ steps.checkAccess.outputs.user-permission }} + ACTOR: ${{ github.actor }} run: | - echo "${{ github.triggering_actor }} does not have permissions on this repo." - echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" - echo "Job originally triggered by ${{ github.actor }}" + echo "$TRIGGERING_ACTOR does not have permissions on this repo." + echo "Current permission level is $USER_PERMISSION" + echo "Job originally triggered by $ACTOR" exit 1 pre-checks: @@ -84,8 +88,10 @@ jobs: - name: Require Changeset if: ${{ github.event_name != 'merge_group' && github.event.pull_request.draft == false }} + env: + PR_AUTHOR: ${{ github.event.pull_request.user.login }} run: | - if [[ "${{ github.event.pull_request.user.login }}" = "clerk-cookie" || "${{ github.event.pull_request.user.login }}" = "renovate[bot]" ]]; then + if [[ "$PR_AUTHOR" = "clerk-cookie" || "$PR_AUTHOR" = "renovate[bot]" ]]; then echo 'Skipping'; exit 0; else diff --git a/.github/workflows/e2e-staging.yml b/.github/workflows/e2e-staging.yml index 0a2257779ca..9dd6c1b5146 100644 --- a/.github/workflows/e2e-staging.yml +++ b/.github/workflows/e2e-staging.yml @@ -30,7 +30,6 @@ on: permissions: contents: read - actions: write concurrency: # Key on the clerk_go commit being validated rather than the (effectively always "main") diff --git a/.github/workflows/labeler-apply.yml b/.github/workflows/labeler-apply.yml index 9ace7dbc33d..d6bbaf35049 100644 --- a/.github/workflows/labeler-apply.yml +++ b/.github/workflows/labeler-apply.yml @@ -8,7 +8,12 @@ name: Labeler (apply) # trigger while preserving fork-PR labeling (SDK-80). on: - workflow_run: + # Privileged trigger, intentionally: this is the trusted half of the labeler + # handshake and never runs PR-controlled code. The apply job validates the + # artifact PR number against the trigger run's head_sha and head repository + # before using it (see the Resolve-and-verify step below and SDK-80). zizmor's + # dangerous-triggers audit can't see that guard, so suppress it here. + workflow_run: # zizmor: ignore[dangerous-triggers] workflows: [Labeler] types: - completed