From 7b5a26e84ed9cc5541799e191cee5c158f0ee31e Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Fri, 24 Jul 2026 17:03:32 -0700 Subject: [PATCH 1/3] Fix missing-workflow-permissions alerts and integrate zizmor Resolves 6 of the 7 open code scanning alerts (https://github.com/rubyatscale/visualize_packs/security/code-scanning), all actions/missing-workflow-permissions findings on workflows that had no explicit permissions block and so defaulted to the repository's token permissions: - ci.yml: root-level `contents: read` (checkout + bundle exec only, across all three jobs) - cd.yml: `contents: write` on the shared-config caller job - the called reusable workflow pushes a release tag (discourse/publish-rubygems-action) and creates a GitHub release (`gh release create`), both of which need contents:write - stale.yml: `issues: write` + `pull-requests: write` - required by actions/stale to label/close stale issues and PRs - triage.yml: `issues: write` - the called reusable workflow's own `label_issues` job requires this from the caller The 7th alert (js/functionality-from-untrusted-source, the insecure `http://d3js.org/d3.v3.min.js` CDN script tag in d3_graph_generator/public/index.html) is already fixed on the open Vite migration branch (#112) via removing that dead script tag entirely; it isn't duplicated here since that PR deletes the file this alert points at. Also adds .github/workflows/zizmor.yml, copied from shared-config's existing zizmor integration (the org's canonical pattern - this is the first repo to adopt it). Running it locally against this repo's own workflows surfaced additional real findings beyond CodeQL's actions coverage, all fixed here so the new job starts green instead of red: - unpinned `actions/checkout@v4` / `ruby/setup-ruby@v1` in ci.yml - pinned to SHA (matching the convention already used in codeql.yml) - missing `persist-credentials: false` on checkout steps in ci.yml and codeql.yml - stale/incorrect version comments on already-pinned SHAs in ci.yml, codeql.yml, and the copied zizmor.yml - e.g. a SHA commented "# v6" that's actually tagged v7.0.0 today (floating major tags move), and "# v3.37.3" on a SHA that's actually v4.37.3 (a leftover from an earlier find-and-replace pass across the org). Caught by zizmor's ref-version-mismatch audit in online mode (--gh-token), not offline. - three findings suppressed with `# zizmor: ignore[...]` comments for patterns that are deliberate, org-wide conventions rather than bugs: workflow_run-triggered CD (dangerous-triggers), unpinned @main references to shared-config's own reusable workflows (unpinned-uses - pinning these would defeat the point of centralizing them, since shared-config updates wouldn't propagate), and secrets: inherit on the cd.yml caller (secrets-inherit - matches shared-config's own precedent for this exact pattern). Verified with `zizmor .github/workflows/` locally, both offline and with --gh-token (online mode catches the ref-version-mismatch findings that offline mode misses): 0 findings, all ignores/suppressions resolving correctly. --- .github/workflows/cd.yml | 6 ++++-- .github/workflows/ci.yml | 21 +++++++++++++++------ .github/workflows/codeql.yml | 6 ++++-- .github/workflows/stale.yml | 5 ++++- .github/workflows/triage.yml | 4 +++- .github/workflows/zizmor.yml | 23 +++++++++++++++++++++++ 6 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 8cb675f..57a2a16 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,6 +1,6 @@ name: CD -on: +on: # zizmor: ignore[dangerous-triggers] deploy-after-CI-passes is the standard rubyatscale release pattern; the called workflow only tags/publishes on main workflow_run: workflows: [CI] types: [completed] @@ -8,5 +8,7 @@ on: jobs: call-workflow-from-shared-config: - uses: rubyatscale/shared-config/.github/workflows/cd.yml@main + permissions: + contents: write + uses: rubyatscale/shared-config/.github/workflows/cd.yml@main # zizmor: ignore[unpinned-uses,secrets-inherit] internal reusable workflow tracked at @main by convention so shared-config updates propagate automatically; environments are managed by callers secrets: inherit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfd0670..0ac7dd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,9 @@ on: - main pull_request: +permissions: + contents: read + jobs: rspec: runs-on: ubuntu-latest @@ -19,9 +22,11 @@ jobs: BUNDLE_GEMFILE: Gemfile name: "RSpec tests: Ruby ${{ matrix.ruby }}" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Set up Ruby ${{ matrix.ruby }} - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@9eb537ca036ebaed86729dcb9309076e4c5c3b74 # v1.314.0 with: bundler-cache: true ruby-version: ${{ matrix.ruby }} @@ -39,9 +44,11 @@ jobs: BUNDLE_GEMFILE: Gemfile name: "Visual Regression Suite: Ruby ${{ matrix.ruby }}" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Set up Ruby ${{ matrix.ruby }} - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@9eb537ca036ebaed86729dcb9309076e4c5c3b74 # v1.314.0 with: bundler-cache: true ruby-version: ${{ matrix.ruby }} @@ -51,9 +58,11 @@ jobs: name: "Type Check" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@9eb537ca036ebaed86729dcb9309076e4c5c3b74 # v1.314.0 with: bundler-cache: true ruby-version: '4.0' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2872630..bbe15b4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,14 +33,16 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v3.37.3 + uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v3.37.3 + uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 0287d52..4d49144 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -5,4 +5,7 @@ on: - cron: '0 0 * * *' jobs: call-workflow-from-shared-config: - uses: rubyatscale/shared-config/.github/workflows/stale.yml@main + permissions: + issues: write + pull-requests: write + uses: rubyatscale/shared-config/.github/workflows/stale.yml@main # zizmor: ignore[unpinned-uses] internal reusable workflow tracked at @main by convention so shared-config updates propagate automatically diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 74bb1d9..7613548 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -6,4 +6,6 @@ on: - opened jobs: call-workflow-from-shared-config: - uses: rubyatscale/shared-config/.github/workflows/triage.yml@main + permissions: + issues: write + uses: rubyatscale/shared-config/.github/workflows/triage.yml@main # zizmor: ignore[unpinned-uses] internal reusable workflow tracked at @main by convention so shared-config updates propagate automatically diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..461b902 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,23 @@ +name: GitHub Actions Security Analysis + +on: + push: + branches: [main] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Run zizmor + uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 From 3f982cc32be4e7cce1aa92c9ae238e9f5ed64b2e Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Fri, 24 Jul 2026 22:20:26 -0700 Subject: [PATCH 2/3] Group GitHub Actions dependabot updates into a single monthly PR Matches the existing bundler grouping in this same file: one PR a month for all github-actions bumps together, instead of a separate PR per action. Especially relevant now that this PR pins several actions to a SHA (actions/checkout, ruby/setup-ruby, github/codeql-action, zizmorcore/zizmor-action) for the first time, which is what makes them trackable by dependabot's github-actions ecosystem in the first place. --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 09f4568..2d98e9b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,11 @@ updates: bundler: patterns: - "*" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + github-actions: + patterns: + - "*" From 68d1a8aeb061f3668fe3af20f0d8358af7584767 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Fri, 24 Jul 2026 22:27:01 -0700 Subject: [PATCH 3/3] Add cooldown to dependabot config, fixing zizmor's new alert The new zizmor.yml added earlier in this PR caught its own dependabot-cooldown finding on the github-actions entry this PR just added (alert #11), and the same check flags the pre-existing bundler entry too - it just never ran against this repo before since zizmor.yml didn't exist until this PR. Fixed both with zizmor --fix=safe, matching shared-config's own dependabot.yml convention (cooldown: default-days: 7). --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2d98e9b..3239b02 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,8 @@ updates: bundler: patterns: - "*" + cooldown: + default-days: 7 - package-ecosystem: "github-actions" directory: "/" schedule: @@ -16,3 +18,5 @@ updates: github-actions: patterns: - "*" + cooldown: + default-days: 7