From 42acba133606ef9ed045753d5b258bf1f235d345 Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Sat, 18 Apr 2026 07:54:23 -0500 Subject: [PATCH] Resolve zizmor security warnings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit permissions: {} added at the workflow level to six workflows. Without this, GitHub falls back to the default token permissions (typically read/write on contents), which is broader than needed. Job-level permissions already existed and are unaffected. Explanatory comments added to all job-level permission entries. Zizmor's undocumented-permissions audit requires each permission to explain why it's needed, so a future reader knows if it's still necessary. concurrency: blocks added to add_member.yml, member-verification.yml, and zizmor.yml. Without concurrency limits, if two issues are opened in quick succession, two runs of add_member.yml could execute simultaneously. Both would check out the same branch state, both would try to create a branch named add-user/ (or race to push to main), and both would try to create a pull request — likely causing one to fail with a git conflict or a "PR already exists" error. For member-verification.yml, two PRs opened at the same time would just run independently and correctly — there's no shared state between them. The concurrency key is ${{ github.event.pull_request.number }}, so only runs for the same PR would be deduplicated. In practice the trigger is types: [opened] so there can only ever be one opened event per PR, making the concurrency block essentially a no-op for this workflow. It's still correct to have it as a safeguard. For zizmor.yml, a push to main followed immediately by another push could queue two runs scanning the same or nearly-identical code. The concurrency block with cancel-in-progress: true cancels the older run in favour of the newer one, avoiding redundant work. No correctness risk, just efficiency. name: added to the unnamed verify-new-member job in member-verification.yml. environment: production added to jobs that reference TERRAFORM_MANAGEMENT_GITHUB_TOKEN. Zizmor's secrets-outside-env audit flags secrets used without a GitHub Environment, as environments enable access controls and audit trails around sensitive credentials. The environment was created with no protection rules so the apply workflows remain fully automated. \# zizmor: ignore[artipacked] added to the checkout step in add_member.yml. persist-credentials: true is intentional there — the job performs git push after checking out, so credentials must persist. The finding is suppressed rather than worked around. --- .github/workflows/add_member.yml | 19 +++++++++++++------ .github/workflows/member-verification.yml | 15 +++++++++++---- .github/workflows/members-apply.yml | 5 ++++- .github/workflows/members-plan.yml | 7 +++++-- .github/workflows/repos-apply.yml | 5 ++++- .github/workflows/repos-plan.yml | 7 +++++-- .github/workflows/zizmor.yml | 6 +++++- 7 files changed, 47 insertions(+), 17 deletions(-) diff --git a/.github/workflows/add_member.yml b/.github/workflows/add_member.yml index bb21ea7..4563d1f 100644 --- a/.github/workflows/add_member.yml +++ b/.github/workflows/add_member.yml @@ -6,16 +6,23 @@ on: - opened - reopened +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.event.issue.number }} + cancel-in-progress: false + jobs: add-member: if: contains(github.event.issue.labels.*.name, 'New member') name: "Add new member" runs-on: ubuntu-latest + environment: production permissions: - id-token: write - contents: write - pull-requests: write - issues: write + id-token: write # Required for OIDC authentication + contents: write # Required to push the new member branch + pull-requests: write # Required to create a pull request + issues: write # Required to comment on the issue env: ISSUE_NUMBER: ${{ github.event.issue.number }} ISSUE_USER: ${{ github.event.issue.user.login }} @@ -24,7 +31,7 @@ jobs: steps: - name: Setup python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.12' @@ -33,7 +40,7 @@ jobs: with: # This is the default, but it's required since we are performing # Git operations later on. - persist-credentials: true + persist-credentials: true # zizmor: ignore[artipacked] - name: Get username to add id: get_username diff --git a/.github/workflows/member-verification.yml b/.github/workflows/member-verification.yml index d512f46..787ed76 100644 --- a/.github/workflows/member-verification.yml +++ b/.github/workflows/member-verification.yml @@ -4,11 +4,18 @@ on: pull_request: types: [opened] +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + jobs: verify-new-member: + name: Verify new member runs-on: ubuntu-latest permissions: - pull-requests: write + pull-requests: write # Required to comment on and label pull requests timeout-minutes: 3 steps: @@ -19,7 +26,7 @@ jobs: - name: Check PR title format id: check-title - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const title = context.payload.pull_request.title; @@ -32,7 +39,7 @@ jobs: - name: Check user account age if: steps.check-title.outputs.result == 'continue' id: check-user - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: MIN_ACCOUNT_AGE_MONTHS: ${{ vars.MIN_ACCOUNT_AGE_MONTHS || '3' }} # Default to 3 months if not set with: @@ -80,7 +87,7 @@ jobs: - name: Add label if new account if: steps.check-user.outputs.result == 'new-account' && steps.check-title.outputs.result == 'continue' - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | await github.rest.issues.addLabels({ diff --git a/.github/workflows/members-apply.yml b/.github/workflows/members-apply.yml index 39058b4..92d39a8 100644 --- a/.github/workflows/members-apply.yml +++ b/.github/workflows/members-apply.yml @@ -10,6 +10,8 @@ on: - '.github/workflows/members-apply.yml' - '.github/workflows/members-plan.yml' +permissions: {} + concurrency: group: terraform-actions-members @@ -17,10 +19,11 @@ jobs: org-apply-changes: name: "Apply org membership changes" runs-on: ubuntu-latest + environment: production permissions: contents: read - pull-requests: write + pull-requests: write # Required to post terraform plan comments timeout-minutes: 10 steps: - name: Checkout code diff --git a/.github/workflows/members-plan.yml b/.github/workflows/members-plan.yml index 27dc45a..204dbbc 100644 --- a/.github/workflows/members-plan.yml +++ b/.github/workflows/members-plan.yml @@ -9,6 +9,8 @@ on: - '.github/workflows/members-apply.yml' # Do not trigger the plan action when it's been changed since this action has write permissions +permissions: {} + concurrency: group: terraform-actions-members @@ -18,7 +20,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - pull-requests: write + pull-requests: write # Required to post terraform fmt check comments timeout-minutes: 4 steps: - name: Checkout code @@ -35,8 +37,9 @@ jobs: name: "Plan org membership changes and list them in a PR" runs-on: ubuntu-latest needs: ["format-terraform-code"] + environment: production permissions: - pull-requests: write + pull-requests: write # Required to post terraform plan comments contents: read timeout-minutes: 10 steps: diff --git a/.github/workflows/repos-apply.yml b/.github/workflows/repos-apply.yml index 9e75054..01a0e6b 100644 --- a/.github/workflows/repos-apply.yml +++ b/.github/workflows/repos-apply.yml @@ -10,6 +10,8 @@ on: - '.github/workflows/repos-apply.yml' - '.github/workflows/repos-plan.yml' +permissions: {} + concurrency: group: terraform-actions-repos @@ -17,10 +19,11 @@ jobs: repos-apply-changes: name: "Apply org-repositories changes" runs-on: ubuntu-latest + environment: production permissions: contents: read - pull-requests: write + pull-requests: write # Required to post terraform plan comments timeout-minutes: 10 steps: - name: Checkout code diff --git a/.github/workflows/repos-plan.yml b/.github/workflows/repos-plan.yml index 13dde0e..5f8eff5 100644 --- a/.github/workflows/repos-plan.yml +++ b/.github/workflows/repos-plan.yml @@ -9,6 +9,8 @@ on: - '.github/workflows/repos-apply.yml' # Do not trigger the plan action when it's been changed since this action has write permissions +permissions: {} + concurrency: group: terraform-actions-repos @@ -18,7 +20,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - pull-requests: write + pull-requests: write # Required to post terraform fmt check comments timeout-minutes: 4 steps: - name: Checkout code @@ -35,8 +37,9 @@ jobs: name: "Plan org-repositories changes and list them in a PR" runs-on: ubuntu-latest needs: ["format-terraform-code"] + environment: production permissions: - pull-requests: write + pull-requests: write # Required to post terraform plan comments contents: read timeout-minutes: 10 steps: diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index b568524..7f115a0 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -8,12 +8,16 @@ on: permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: zizmor: name: Run zizmor 🌈 runs-on: ubuntu-latest permissions: - security-events: write + security-events: write # Required to upload SARIF results to GitHub Security contents: read # only needed for private repos actions: read # only needed for private repos timeout-minutes: 3