From 43fe85a8e5d652d14068f74a424bb330b495f163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Babi=C4=87?= Date: Mon, 17 Aug 2026 16:26:00 -0700 Subject: [PATCH 1/3] fix: [SC-66825] stop persisting git credentials in the S3 backup workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backup.yml checks out the repo and then mirrors the whole workspace, including .git/, to narrative-backups-prod. Without persist-credentials: false, actions/checkout leaves the job token in .git/config as an extraheader, so every daily backup wrote a GitHub credential into S3. Also switches the GITHUB_TOKEN env var from secrets.GITHUB_TOKEN to the github.token context. The context form always resolves in a called workflow, so callers no longer need 'secrets: inherit' to make it work — which lets backup-daily.yml drop 'secrets: inherit' (zizmor secrets-inherit) and scope itself to permissions: contents: read (zizmor excessive-permissions). Callers are unaffected: backup.yml declares no inputs and no secrets, so its call signature is unchanged. All 84 live callers remain valid whether or not they pass 'secrets: inherit'. sc-66825 --- .github/workflows/backup-daily.yml | 6 +++++- .github/workflows/backup.yml | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backup-daily.yml b/.github/workflows/backup-daily.yml index dafeb31..699b1db 100644 --- a/.github/workflows/backup-daily.yml +++ b/.github/workflows/backup-daily.yml @@ -5,9 +5,13 @@ on: schedule: - cron: 0 6 * * * +# backup.yml only needs the automatic token (referenced as `github.token`, so no +# secrets have to be passed); AWS access comes from the runner's instance profile. +permissions: + contents: read + jobs: s3-backup-daily: # Local ref: always runs the version of backup.yml from this repo's own commit, # so it is inherently pinned. Other repos must pin to a full commit SHA instead. uses: ./.github/workflows/backup.yml - secrets: inherit diff --git a/.github/workflows/backup.yml b/.github/workflows/backup.yml index 153a8b6..8f63a49 100644 --- a/.github/workflows/backup.yml +++ b/.github/workflows/backup.yml @@ -13,14 +13,22 @@ env: # `sbt assembly` to fail due to e.g. shapeless having special characters like `λ` in class names. # See https://app.shortcut.com/narrativeio/story/19136 LC_ALL: en_US.UTF-8 - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # `github.token` rather than `secrets.GITHUB_TOKEN`: the context form resolves in a + # called workflow without the caller passing anything, so callers do not need + # `secrets: inherit` (see secrets-inherit remediation in backup-daily.yml). + GITHUB_TOKEN: ${{ github.token }} AWS_REGION: us-east-1 jobs: s3-backup: runs-on: self-hosted steps: + # persist-credentials: false is load-bearing here, not just lint compliance: the + # s3-backup step below mirrors the whole workspace (including .git/) to S3, so a + # persisted token would be written into .git/config in the backup bucket. - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Configure AWS credentials id: configure-aws-credentials uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1 From b996f099381ee0ad8e168e565453835b6dd914ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Babi=C4=87?= Date: Mon, 17 Aug 2026 16:26:08 -0700 Subject: [PATCH 2/3] ci: [SC-66825] run zizmor on push and pull requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modeled on narrative-skills-marketplace/.github/workflows/zizmor.yml (PR #108): permissions: {} at the top level, per-job permissions scoped to what upload-sarif needs, and SARIF published to code scanning. common-github hosts reusable workflows consumed by 82 repos, so a regression here has org-wide blast radius — this catches it at PR time. sc-66825 --- .github/workflows/zizmor.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..97500e7 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,26 @@ +name: GitHub Actions Security Analysis with zizmor 🌈 + +on: + push: + branches: ["main"] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + name: Run zizmor 🌈 + runs-on: ubuntu-latest + permissions: + security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files. + contents: read # Only needed for private repos. Needed to clone the repo. + actions: read # Only needed for private repos. Needed for upload-sarif to read workflow run info. + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Run zizmor 🌈 + uses: zizmorcore/zizmor-action@6fc4b006235f201fdab3722e17240ab420d580e5 # v0.6.1 From 9634f57c7bf8c55bc1b6c6ad24281952fac36e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Babi=C4=87?= Date: Mon, 17 Aug 2026 16:26:08 -0700 Subject: [PATCH 3/3] ci: [SC-66825] scope release-please permissions to the job contents: write and pull-requests: write were granted at the workflow level, so they would apply to any job added later. Moves them onto the release-please job with a comment per scope, leaving permissions: {} at the top level (zizmor excessive-permissions, pedantic persona). No behavior change: the workflow has a single job. sc-66825 --- .github/workflows/release-please.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 057fb1d..8e223a5 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -5,13 +5,14 @@ on: branches: - main -permissions: - contents: write - pull-requests: write +permissions: {} jobs: release-please: runs-on: ubuntu-latest + permissions: + contents: write # release-please pushes the release branch and creates tags/releases. + pull-requests: write # release-please opens and updates the release PR. steps: - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 with: