From cc1062dac4fdb2808a819359451430b1be504f38 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 13 May 2026 22:58:09 +0000 Subject: [PATCH 1/5] ci: add zizmor security analysis for GitHub Actions workflows Introduces a zizmor workflow that statically analyzes our Actions workflows for common security issues (template injection, excessive token scope, etc.). Findings are uploaded as SARIF so they surface in the repository's Security tab. --- .github/workflows/zizmor.yml | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 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 00000000..42fa05a8 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,37 @@ +name: GitHub Actions Security Analysis + +on: + push: + branches: + - main + pull_request: + +permissions: {} + +jobs: + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Run zizmor + run: uvx zizmor --format=sarif . > results.sarif + env: + GH_TOKEN: ${{ github.token }} + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + category: zizmor From a3749132c932807ba3bb2e7ebb547a8fc2ade23c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 13 May 2026 23:00:32 +0000 Subject: [PATCH 2/5] ci(zizmor): pin third-party actions to commit SHAs Addresses zizmor's own unpinned-uses findings on the new workflow by pinning actions/checkout, astral-sh/setup-uv, and github/codeql-action/upload-sarif to specific commit SHAs with version comments. --- .github/workflows/zizmor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 42fa05a8..58e9b58c 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -18,12 +18,12 @@ jobs: actions: read steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - name: Run zizmor run: uvx zizmor --format=sarif . > results.sarif @@ -31,7 +31,7 @@ jobs: GH_TOKEN: ${{ github.token }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@1521896cd211af95be3f02edf6f436e10b819c27 # v3.35.4 with: sarif_file: results.sarif category: zizmor From 298eb5f421da124d4aa8c62e4a9c77427e57edb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 13 May 2026 23:10:33 +0000 Subject: [PATCH 3/5] ci(zizmor): switch to official zizmorcore/zizmor-action The maintained action handles uv install, zizmor invocation, and SARIF upload to GitHub Advanced Security in a single step. Drops a few third-party action references from this workflow. --- .github/workflows/zizmor.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 58e9b58c..e95dcdf7 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -22,16 +22,5 @@ jobs: with: persist-credentials: false - - name: Install uv - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - - name: Run zizmor - run: uvx zizmor --format=sarif . > results.sarif - env: - GH_TOKEN: ${{ github.token }} - - - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@1521896cd211af95be3f02edf6f436e10b819c27 # v3.35.4 - with: - sarif_file: results.sarif - category: zizmor + uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 From d302632640b7d79d8f1528e4c0f9fdebb7111e37 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 13 May 2026 23:20:08 +0000 Subject: [PATCH 4/5] ci: address zizmor findings across all workflows - Make the zizmor workflow enforcing: switch to annotations-only mode so non-zero zizmor exit fails the check (was advisory via SARIF). - ci.common.yml: move templated inputs (package_name, queue_backend) into env vars to close template-injection findings; pin actions/checkout and actions/setup-node to SHAs; set persist-credentials: false on checkout. - ci.yml: add deny-by-default top-level permissions and explicit per-job permissions; pin fastify/github-action-merge-dependabot. - ensure-labels.yml: add deny-by-default top-level permissions and scope ensure_labels job to pull-requests: read. - publish.yml: add deny-by-default top-level permissions; pin actions/checkout, actions/setup-node, and dorny/paths-filter to SHAs; set persist-credentials: false on the detect-changes checkout; move templated steps.pr-info / steps.filter outputs into env vars to close template-injection findings. --- .github/workflows/ci.common.yml | 30 ++++++++++++++++++++--------- .github/workflows/ci.yml | 10 +++++++++- .github/workflows/ensure-labels.yml | 4 ++++ .github/workflows/publish.yml | 26 ++++++++++++++++--------- .github/workflows/zizmor.yml | 5 +++-- 5 files changed, 54 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.common.yml b/.github/workflows/ci.common.yml index 4d1fe279..3350fba6 100644 --- a/.github/workflows/ci.common.yml +++ b/.github/workflows/ci.common.yml @@ -13,14 +13,19 @@ on: type: string default: 'fauxqs' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Setup Node ${{ inputs.node_version }} - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ inputs.node_version }} package-manager-cache: false @@ -29,24 +34,31 @@ jobs: run: npm install --ignore-scripts - name: Build TS - run: npm run build -- --filter=${{ inputs.package_name }} + env: + PACKAGE_NAME: ${{ inputs.package_name }} + run: npm run build -- --filter="$PACKAGE_NAME" - name: Run lint - run: npm run lint -- --filter=${{ inputs.package_name }} + env: + PACKAGE_NAME: ${{ inputs.package_name }} + run: npm run lint -- --filter="$PACKAGE_NAME" - name: Docker start - run: npm run docker:start:ci -- --filter=${{ inputs.package_name }} env: + PACKAGE_NAME: ${{ inputs.package_name }} QUEUE_BACKEND: ${{ inputs.queue_backend }} + run: npm run docker:start:ci -- --filter="$PACKAGE_NAME" - name: Run Tests - run: | - echo "::notice::Running ${{ inputs.package_name }} with QUEUE_BACKEND=${{ inputs.queue_backend }}" - npm run test:ci -- --filter=${{ inputs.package_name }} env: + PACKAGE_NAME: ${{ inputs.package_name }} QUEUE_BACKEND: ${{ inputs.queue_backend }} + run: | + echo "::notice::Running $PACKAGE_NAME with QUEUE_BACKEND=$QUEUE_BACKEND" + npm run test:ci -- --filter="$PACKAGE_NAME" - name: Docker stop - run: npm run docker:stop:ci -- --filter=${{ inputs.package_name }} env: + PACKAGE_NAME: ${{ inputs.package_name }} QUEUE_BACKEND: ${{ inputs.queue_backend }} + run: npm run docker:stop:ci -- --filter="$PACKAGE_NAME" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8042d848..029b7de9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,10 +6,14 @@ on: - main pull_request: +permissions: {} + jobs: changed-files-job: name: Get changed packages runs-on: ubuntu-latest + permissions: + contents: read outputs: packages: ${{ steps.detect.outputs.packages }} aws_packages: ${{ steps.detect.outputs.aws_packages }} @@ -82,6 +86,8 @@ jobs: general: needs: [changed-files-job] if: needs.changed-files-job.outputs.packages != '[]' + permissions: + contents: read strategy: matrix: node-version: [22.x, 24.x] @@ -94,6 +100,8 @@ jobs: aws-packages: needs: [changed-files-job] if: needs.changed-files-job.outputs.aws_packages != '[]' + permissions: + contents: read strategy: matrix: node-version: [22.x, 24.x] @@ -113,6 +121,6 @@ jobs: pull-requests: write contents: write steps: - - uses: fastify/github-action-merge-dependabot@v3 + - uses: fastify/github-action-merge-dependabot@59fc8817458fac20df8884576cfe69dbb77c9a07 # v3.9.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ensure-labels.yml b/.github/workflows/ensure-labels.yml index 7d29a83e..2a506924 100644 --- a/.github/workflows/ensure-labels.yml +++ b/.github/workflows/ensure-labels.yml @@ -12,10 +12,14 @@ on: - labeled - unlabeled +permissions: {} + jobs: ensure_labels: name: Ensure PR has proper labeling runs-on: ubuntu-latest + permissions: + pull-requests: read steps: - name: Check one of required labels are set uses: docker://agilepathway/pull-request-label-checker:v1.6.65 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4c98090d..942ae555 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,6 +10,8 @@ concurrency: group: release-main cancel-in-progress: false +permissions: {} + jobs: detect-changes: runs-on: ubuntu-latest @@ -30,9 +32,10 @@ jobs: echo "bump=patch" >> $GITHUB_OUTPUT echo "should_publish=false" >> $GITHUB_OUTPUT - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - name: Check if should skip id: skip-check @@ -103,16 +106,20 @@ jobs: - name: Debug commit info if: steps.skip-check.outputs.should_skip != 'true' && steps.pr-info.outputs.should_publish == 'true' + env: + COMMIT_SHA: ${{ github.sha }} + PREVIOUS_SHA: ${{ github.event.before }} + PR_NUMBER: ${{ steps.pr-info.outputs.pr_number }} run: | - echo "Commit SHA: ${{ github.sha }}" - echo "Previous SHA: ${{ github.event.before }}" - echo "PR Number: ${{ steps.pr-info.outputs.pr_number }}" + echo "Commit SHA: $COMMIT_SHA" + echo "Previous SHA: $PREVIOUS_SHA" + echo "PR Number: $PR_NUMBER" echo "Files changed:" - git diff --name-only ${{ github.event.before }}..${{ github.sha }} || echo "git diff failed" + git diff --name-only "$PREVIOUS_SHA".."$COMMIT_SHA" || echo "git diff failed" - id: filter if: steps.skip-check.outputs.should_skip != 'true' && steps.pr-info.outputs.should_publish == 'true' - uses: dorny/paths-filter@v4 + uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 with: base: ${{ github.event.before }} ref: ${{ github.sha }} @@ -169,6 +176,8 @@ jobs: - name: Build dynamic matrix id: build-matrix if: steps.skip-check.outputs.should_skip != 'true' && steps.pr-info.outputs.should_publish == 'true' + env: + CHANGED: ${{ steps.filter.outputs.changes }} run: | # Package mapping: filterKey -> name, npmName declare -A PKG_NAMES=( @@ -186,7 +195,6 @@ jobs: ["pkg_schemas"]="schemas" ) - CHANGED='${{ steps.filter.outputs.changes }}' echo "Changed filters: $CHANGED" # Build matrix JSON array @@ -270,7 +278,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 token: ${{ github.token }} @@ -285,7 +293,7 @@ jobs: run: git pull --ff-only origin main - name: Setup Node - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24.x registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index e95dcdf7..03e1f772 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -13,9 +13,7 @@ jobs: name: zizmor runs-on: ubuntu-latest permissions: - security-events: write contents: read - actions: read steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -24,3 +22,6 @@ jobs: - name: Run zizmor uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + with: + advanced-security: false + annotations: true From 177d328f945bc75bb1db9ebd8e25ec182eca47be Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 14 May 2026 06:38:12 +0000 Subject: [PATCH 5/5] ci(dependabot): add 7-day update cooldown Closes zizmor's dependabot-cooldown findings (the CI run scans the whole repo, including .github/dependabot.yml, which the local .github/workflows-only run did not). The cooldown gives recently published versions time to be vetted before Dependabot opens an update PR. --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f696f5ce..c766b0fd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,12 +5,16 @@ updates: schedule: interval: 'monthly' open-pull-requests-limit: 10 + cooldown: + default-days: 7 - package-ecosystem: 'npm' directory: '/' schedule: interval: 'weekly' open-pull-requests-limit: 10 + cooldown: + default-days: 7 ignore: - dependency-name: "eslint" - dependency-name: "eslint-plugin-vitest"