From f56e0ac750dc5809366d52853ba99e959bf13a03 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Tue, 29 Sep 2026 11:47:13 -0700 Subject: [PATCH 1/4] Make zizmor.yml a reusable workflow_call workflow Gem repos can now call zizmor from here instead of carrying their own copy, as codeql.yml did in #29. zizmor-self-scan.yml keeps the push and PR triggers and calls it locally, so shared-config still scans itself, with the same Security tab upload as before. The advanced-security input defaults to true, the zizmor-action default. Setting it to false reports findings as annotations and fails the job, which a required check needs: zizmor exits 0 whenever it writes SARIF. The job has no permissions block so that it takes the caller's, since the two modes need different ones. --- .github/workflows/zizmor-self-scan.yml | 17 +++++++++++++ .github/workflows/zizmor.yml | 26 ++++++++++++-------- README.md | 33 +++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/zizmor-self-scan.yml diff --git a/.github/workflows/zizmor-self-scan.yml b/.github/workflows/zizmor-self-scan.yml new file mode 100644 index 0000000..052f318 --- /dev/null +++ b/.github/workflows/zizmor-self-scan.yml @@ -0,0 +1,17 @@ +name: GitHub Actions Security Analysis (self-scan) + +on: + push: + branches: [main] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + permissions: + actions: read + contents: read + security-events: write + uses: ./.github/workflows/zizmor.yml diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 8735b5c..be599d9 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -1,23 +1,29 @@ name: GitHub Actions Security Analysis on: - push: - branches: [main] - pull_request: - branches: ["**"] - -permissions: {} + workflow_call: + inputs: + advanced-security: + description: >- + true uploads the results to the repository's Security tab. false reports them as + annotations and fails the job on any finding, which is what a required check needs, + since zizmor exits 0 when it writes SARIF. + required: false + type: boolean + default: true jobs: + # No permissions block, so the job gets the caller's: contents: read, plus actions: read and + # security-events: write when advanced-security is true. zizmor: + name: zizmor runs-on: ubuntu-latest - permissions: - security-events: write - contents: read - actions: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run zizmor uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2 + with: + advanced-security: ${{ inputs.advanced-security }} + annotations: ${{ !inputs.advanced-security }} diff --git a/README.md b/README.md index 5aee035..e75fd06 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,13 @@ These workflows are called from individual gem repos via `uses: rubyatscale/shar | **CD** (`cd.yml`) | Publishes the gem to RubyGems and creates a GitHub Release on successful main builds. | | **Stale** (`stale.yml`) | Marks issues and PRs as stale after 180 days of inactivity, then closes them after 7 more days. | | **Triage** (`triage.yml`) | Labels new issues with `triage`. | +| **zizmor** (`zizmor.yml`) | Runs the [zizmor](https://github.com/zizmorcore/zizmor) security linter against the calling repo's workflows, actions, and Dependabot config. | ### Repository workflows | Workflow | Description | |----------|-------------| -| **zizmor** (`zizmor.yml`) | Runs the [zizmor](https://github.com/zizmorcore/zizmor) security linter against all workflow files on every push and PR. | +| **zizmor self-scan** (`zizmor-self-scan.yml`) | Runs `zizmor.yml` against this repo on every push and PR. | ## Usage @@ -46,6 +47,36 @@ jobs: | `test-command` | `bundle exec rspec` | Command to run tests | | `linter-command` | `bundle exec rubocop` | Command to run the linter | +### zizmor + +By default the results go to the repository's Security tab, and the job passes whatever zizmor finds. To make zizmor a required check, set `advanced-security: false`: findings are then reported as annotations and fail the job. The job takes its permissions from the caller. + +```yaml +# .github/workflows/zizmor.yml +name: zizmor + +on: + push: + branches: [main] + pull_request: + +permissions: {} + +jobs: + zizmor: + permissions: + contents: read + uses: rubyatscale/shared-config/.github/workflows/zizmor.yml@main # zizmor: ignore[unpinned-uses] internal reusable workflow tracked at @main by convention + with: + advanced-security: false +``` + +With the default `advanced-security: true`, grant `actions: read` and `security-events: write` as well. + +| Input | Default | Description | +|-------|---------|-------------| +| `advanced-security` | `true` | Upload results to the Security tab (`true`), or annotate and fail on findings (`false`) | + ### Required secrets The **CD** workflow requires the following secrets in the calling repo: From 0362c020028d3bd6a2fc9872bba72313ab63c2f3 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Tue, 29 Sep 2026 12:05:26 -0700 Subject: [PATCH 2/4] Bump zizmor-action to v0.6.4 v0.6.4 makes zizmor 1.30.1 the default. The v0.6.2 pin still ran 1.29.0, and rubyfmt-action is already on v0.6.3, so moving it onto this workflow would otherwise be a downgrade. action.sh is unchanged between the two; action.yml only bumps its upload-sarif pin. --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index be599d9..c28c6cd 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -23,7 +23,7 @@ jobs: with: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2 + uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4 with: advanced-security: ${{ inputs.advanced-security }} annotations: ${{ !inputs.advanced-security }} From 5952f2b8b91339a3c13f02950e5755b345ee05f1 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Tue, 29 Sep 2026 12:06:30 -0700 Subject: [PATCH 3/4] Document CodeQL and the zizmor check name in the README The README never picked up codeql.yml or codeql-self-scan.yml from #29. Both are now in the tables, with a caller example and the languages input. The zizmor section now names the check to require (zizmor / zizmor with the example), notes that actions: read is only needed in private repos, and warns that callers tracking @main pick up zizmor-action upgrades, and any new audits, at once. The job comment in zizmor.yml now says what callers should grant instead of reading as a guarantee. --- .github/workflows/zizmor.yml | 4 ++-- README.md | 41 ++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index c28c6cd..52aba5d 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -13,8 +13,8 @@ on: default: true jobs: - # No permissions block, so the job gets the caller's: contents: read, plus actions: read and - # security-events: write when advanced-security is true. + # No permissions block, so the job gets the caller's. Callers should grant contents: read, plus + # security-events: write (and actions: read in a private repo) when advanced-security is true. zizmor: name: zizmor runs-on: ubuntu-latest diff --git a/README.md b/README.md index e75fd06..14f66bb 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,14 @@ These workflows are called from individual gem repos via `uses: rubyatscale/shar | **CD** (`cd.yml`) | Publishes the gem to RubyGems and creates a GitHub Release on successful main builds. | | **Stale** (`stale.yml`) | Marks issues and PRs as stale after 180 days of inactivity, then closes them after 7 more days. | | **Triage** (`triage.yml`) | Labels new issues with `triage`. | +| **CodeQL** (`codeql.yml`) | Runs [CodeQL](https://codeql.github.com) analysis for the given languages and uploads the results to the Security tab. | | **zizmor** (`zizmor.yml`) | Runs the [zizmor](https://github.com/zizmorcore/zizmor) security linter against the calling repo's workflows, actions, and Dependabot config. | ### Repository workflows | Workflow | Description | |----------|-------------| +| **CodeQL self-scan** (`codeql-self-scan.yml`) | Runs `codeql.yml` against this repo's workflows on pushes and PRs to main, and weekly. | | **zizmor self-scan** (`zizmor-self-scan.yml`) | Runs `zizmor.yml` against this repo on every push and PR. | ## Usage @@ -47,9 +49,42 @@ jobs: | `test-command` | `bundle exec rspec` | Command to run tests | | `linter-command` | `bundle exec rubocop` | Command to run the linter | +### CodeQL + +The job requests `actions: read`, `contents: read` and `security-events: write`, so the calling job must grant all three. + +```yaml +# .github/workflows/codeql.yml +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '30 1 * * 0' + +permissions: {} + +jobs: + analyze: + permissions: + actions: read + contents: read + security-events: write + uses: rubyatscale/shared-config/.github/workflows/codeql.yml@main # zizmor: ignore[unpinned-uses] internal reusable workflow tracked at @main by convention + with: + languages: '["actions","ruby"]' +``` + +| Input | Default | Description | +|-------|---------|-------------| +| `languages` | (required) | JSON array of [CodeQL languages](https://codeql.github.com/docs/codeql-overview/supported-languages-and-frameworks/) to analyze, e.g. `'["actions","ruby"]'` | + ### zizmor -By default the results go to the repository's Security tab, and the job passes whatever zizmor finds. To make zizmor a required check, set `advanced-security: false`: findings are then reported as annotations and fail the job. The job takes its permissions from the caller. +By default the results go to the repository's Security tab, and the job passes whatever zizmor finds. To make zizmor a required check, set `advanced-security: false`: findings are then reported as annotations and fail the job. The check is named ` / zizmor`, so require `zizmor / zizmor` with the example below. The job takes its permissions from the caller. ```yaml # .github/workflows/zizmor.yml @@ -71,7 +106,9 @@ jobs: advanced-security: false ``` -With the default `advanced-security: true`, grant `actions: read` and `security-events: write` as well. +With the default `advanced-security: true`, grant `security-events: write` as well, plus `actions: read` in a private repo. + +Callers track `@main`, so when this repo upgrades zizmor-action, new audits can start failing the check in every caller at once. | Input | Default | Description | |-------|---------|-------------| From 57f64cfd396e1661d0e0a84db12629cb911c6e46 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Tue, 29 Sep 2026 12:17:46 -0700 Subject: [PATCH 4/4] Call the self-scan workflows with the $/ self-repository syntax zizmor 1.30 (now the default via zizmor-action v0.6.4) flags ./ calls to in-repo workflows with its self-repository audit. $/ resolves to this repository at the commit that is running, like ./ does, so a PR that edits zizmor.yml or codeql.yml is still tested against its own version. GitHub also treats $/ references as pinned under the policy requiring full-length SHA pins, which ./ references are not. codeql-self-scan.yml gets the same change, since it would otherwise gain the same alert on main once this merges. actionlint 1.7.12 doesn't recognize $/ yet (rhysd/actionlint#711). --- .github/workflows/codeql-self-scan.yml | 2 +- .github/workflows/zizmor-self-scan.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-self-scan.yml b/.github/workflows/codeql-self-scan.yml index 03dc5d2..3929460 100644 --- a/.github/workflows/codeql-self-scan.yml +++ b/.github/workflows/codeql-self-scan.yml @@ -16,6 +16,6 @@ jobs: actions: read contents: read security-events: write - uses: ./.github/workflows/codeql.yml + uses: $/.github/workflows/codeql.yml with: languages: '["actions"]' diff --git a/.github/workflows/zizmor-self-scan.yml b/.github/workflows/zizmor-self-scan.yml index 052f318..23a98d9 100644 --- a/.github/workflows/zizmor-self-scan.yml +++ b/.github/workflows/zizmor-self-scan.yml @@ -14,4 +14,4 @@ jobs: actions: read contents: read security-events: write - uses: ./.github/workflows/zizmor.yml + uses: $/.github/workflows/zizmor.yml