From a40695d6e1f77356f2cdea3e0b97c2c26ebf36b6 Mon Sep 17 00:00:00 2001 From: Paul Abbazia <136359239+paul-abb@users.noreply.github.com> Date: Tue, 4 Aug 2026 16:28:41 -0400 Subject: [PATCH 1/4] security: least-privilege permissions + SHA-pin actions in continuous_integration.yml VULNMGMT-1068 / VULNMGMT-1069 --- .github/workflows/continuous_integration.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index eb73a06..007d893 100755 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -2,6 +2,9 @@ name: Continuous Integration on: [pull_request] +permissions: + contents: read + jobs: build: @@ -11,9 +14,9 @@ jobs: python-version: [3.9] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@0717577d45739eb3c851188b29f50ed6c0b2194e # v2 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@e9aba2c848f5ebd159c070c61ea2c4e2b122355e # v2 with: python-version: ${{ matrix.python-version }} @@ -65,11 +68,11 @@ jobs: if: always() - name: Upload pytest test results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: notebook-test-results path: examples/junit/notebook-test-results.xml - name: Download all workflow run artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 if: always() From 416a76d6d1948c80ad8d71051d6ea727c8c00984 Mon Sep 17 00:00:00 2001 From: Paul Abbazia <136359239+paul-abb@users.noreply.github.com> Date: Tue, 4 Aug 2026 16:28:42 -0400 Subject: [PATCH 2/4] security: least-privilege permissions + SHA-pin actions in publish.yml VULNMGMT-1068 / VULNMGMT-1069 --- .github/workflows/publish.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cb84b95..fc20014 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,9 @@ on: push: branches: main +permissions: + contents: write # quarto publish pushes the rendered site to gh-pages + jobs: build-deploy: runs-on: ubuntu-latest @@ -12,13 +15,13 @@ jobs: contents: write steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3 - name: Set up Quarto - uses: quarto-dev/quarto-actions/setup@v2 + uses: quarto-dev/quarto-actions/setup@8a96df13519ee81fd526f2dfca5962811136661b # v2 - name: Render and Publish - uses: quarto-dev/quarto-actions/publish@v2 + uses: quarto-dev/quarto-actions/publish@8a96df13519ee81fd526f2dfca5962811136661b # v2 with: target: gh-pages env: From ea464e461fbc28de7197037e4ee88945e05d5bde Mon Sep 17 00:00:00 2001 From: Paul Abbazia <136359239+paul-abb@users.noreply.github.com> Date: Tue, 4 Aug 2026 19:23:53 -0400 Subject: [PATCH 3/4] security: make publish.yml workflow-level permissions read-only The workflow-level default was contents: write, which inverts the goal of this PR: any job added to this workflow later would silently inherit write. The build-deploy job already declares contents: write at job level, and job-level permissions override workflow-level, so quarto's gh-pages push is unaffected. Only the inherited default changes. --- .github/workflows/publish.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc20014..1330e9d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,14 +5,16 @@ on: push: branches: main +# Workflow-level default is read-only so any job added later starts least-privileged. +# The one job that needs to push is granted write explicitly below. permissions: - contents: write # quarto publish pushes the rendered site to gh-pages + contents: read jobs: build-deploy: runs-on: ubuntu-latest permissions: - contents: write + contents: write # quarto publish pushes the rendered site to gh-pages steps: - name: Check out repository uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3 From 8806f33fa263d15e4473b12708738947f96d5a76 Mon Sep 17 00:00:00 2001 From: Paul Abbazia Date: Tue, 4 Aug 2026 21:13:40 -0400 Subject: [PATCH 4/4] ci: raise checkout/setup-python pins to current majors The pins in this branch were taken at whatever major each workflow already used: checkout v2 and setup-python v2 in continuous_integration.yml, and checkout v3 in publish.yml. Pinning to a commit freezes that exact code permanently, so pinning an old major is worse than the floating tag it replaced -- a floating @v2 still picks up v2 patch and security releases, whereas the pinned commit picks up nothing and has no upgrade path except a manual edit. The runners already warn that the Node 20 runtime these actions use is being deprecated, so the frozen pins would eventually fail closed. checkout -> 11d5960a326750d5838078e36cf38b85af677262 (v4, v4.4.0) setup-python -> a26af69be951a213d495a4c3e4e4022e16d87065 (v5, v5.6.0) Both SHAs were checked against the upstream tag refs rather than copied from a comment. The setup-python bump is safe for this matrix specifically: the YAML-float hazard where an unquoted 3.10 parses as 3.1 applies to 3.10 and above, and this matrix is [3.9]. --- .github/workflows/continuous_integration.yml | 4 ++-- .github/workflows/publish.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 007d893..fedeac4 100755 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -14,9 +14,9 @@ jobs: python-version: [3.9] steps: - - uses: actions/checkout@0717577d45739eb3c851188b29f50ed6c0b2194e # v2 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Set up Python - uses: actions/setup-python@e9aba2c848f5ebd159c070c61ea2c4e2b122355e # v2 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1330e9d..b28ae24 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,7 +17,7 @@ jobs: contents: write # quarto publish pushes the rendered site to gh-pages steps: - name: Check out repository - uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Set up Quarto uses: quarto-dev/quarto-actions/setup@8a96df13519ee81fd526f2dfca5962811136661b # v2