Enforce protected GitHub checkout baseline

This commit is contained in:
pulse-triage[bot]
2026-08-31 13:21:25 +01:00
parent d19398d82a
commit 69cbe5f3b8
40 changed files with 180 additions and 71 deletions
+6
View File
@@ -7,6 +7,12 @@ smoke suite. Remote actions and reusable workflows must use full commit SHAs,
container actions must use SHA-256 digests, and GitHub-hosted runners must use
dated image labels rather than moving `-latest` aliases.
Checkout pins additionally belong to a reviewed allowlist whose current floor
includes GitHub's fail-closed fork-PR protection for privileged events. The
audit prohibits `pull_request_target` entirely and rejects checkout's
`allow-unsafe-pr-checkout` opt-out; privileged work must remain isolated from
pull-request code rather than bypassing the upstream guard.
Every `actions/checkout` step must also set `persist-credentials` explicitly.
Use `false` unless a later command in the same job performs an authenticated
Git write. The small number of write-path exceptions use `true` with the
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+6 -6
View File
@@ -33,7 +33,7 @@ jobs:
GITLEAKS_LINUX_X64_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb"
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -73,7 +73,7 @@ jobs:
code: ${{ steps.filter.outputs.code }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -114,7 +114,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -186,7 +186,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -237,7 +237,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
# test_gitleaks_ignore.py resolves historical fingerprints against
@@ -270,7 +270,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -268,7 +268,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -372,7 +372,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -667,7 +667,7 @@ jobs:
echo "container_artifact_name=release-container-payload-${GITHUB_SHA}-${VERSION}" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
working-directory: repos/pulse
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -25,14 +25,14 @@ jobs:
working-directory: repos/pulse
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 1
path: repos/pulse
- name: Checkout pulse-pro evidence repo
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
repository: rcourtman/pulse-pro
token: ${{ secrets.WORKFLOW_PAT }}
@@ -41,7 +41,7 @@ jobs:
path: repos/pulse-pro
- name: Checkout pulse-enterprise evidence repo
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
repository: rcourtman/pulse-enterprise
token: ${{ secrets.WORKFLOW_PAT }}
@@ -50,7 +50,7 @@ jobs:
path: repos/pulse-enterprise
- name: Checkout pulse-mobile evidence repo
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
repository: rcourtman/pulse-mobile
token: ${{ secrets.WORKFLOW_PAT }}
@@ -30,7 +30,7 @@ jobs:
private-key: ${{ secrets.PULSE_TRIAGE_APP_PRIVATE_KEY }}
- name: Check out triage helper
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
sparse-checkout: |
@@ -38,7 +38,7 @@ jobs:
test "${GITHUB_WORKFLOW_SHA}" = "${EXPECTED_SOURCE_SHA}"
- name: Checkout exact release source
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -121,7 +121,7 @@ jobs:
test "${GITHUB_WORKFLOW_SHA}" = "${EXPECTED_SOURCE_SHA}"
- name: Checkout exact release source
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
+13 -13
View File
@@ -139,7 +139,7 @@ jobs:
echo "Version: ${VERSION}, Tag: ${TAG}, Prerelease: ${IS_PRERELEASE}, Branch: ${SOURCE_BRANCH}, HistoricalBackfillOnly: ${HISTORICAL_ASSET_BACKFILL_ONLY}"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -281,7 +281,7 @@ jobs:
steps:
- name: Checkout release trust control
if: ${{ github.event.inputs.draft_only != 'true' && needs.prepare.outputs.historical_asset_backfill_only != 'true' }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -345,7 +345,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -380,7 +380,7 @@ jobs:
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -419,7 +419,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -456,7 +456,7 @@ jobs:
FRONTEND_DIST: frontend-modern/dist
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -495,7 +495,7 @@ jobs:
FRONTEND_DIST: frontend-modern/dist
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -660,7 +660,7 @@ jobs:
PULSE_E2E_BOOTSTRAP_TOKEN: 0123456789abcdef0123456789abcdef0123456789abcdef
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -760,7 +760,7 @@ jobs:
steps:
- name: Checkout repository
if: ${{ needs.prepare.outputs.visual_capture_count != '0' }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -810,7 +810,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: true # required: authenticated git writes
fetch-depth: 0
@@ -1251,7 +1251,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -1646,7 +1646,7 @@ jobs:
secure_runtime_qualification_run_url: ${{ steps.secure_runtime_qualification.outputs.run_url }}
steps:
- name: Checkout release integrity control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -2014,7 +2014,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout release integrity control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+2 -2
View File
@@ -36,7 +36,7 @@ jobs:
timeout-minutes: 20
steps:
- name: Checkout release verification control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -171,7 +171,7 @@ jobs:
helm show chart "${CHART_PATH}"
- name: Check out current Pages index
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: gh-pages
path: gh-pages
+1 -1
View File
@@ -100,7 +100,7 @@ jobs:
contents: write
steps:
- name: Checkout repository (for README key extraction)
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -24,7 +24,7 @@ jobs:
private-key: ${{ secrets.PULSE_TRIAGE_APP_PRIVATE_KEY }}
- name: Check out triage helper
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
sparse-checkout: |
@@ -25,7 +25,7 @@ jobs:
private-key: ${{ secrets.PULSE_TRIAGE_APP_PRIVATE_KEY }}
- name: Check out triage helper
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
sparse-checkout: |
@@ -51,7 +51,7 @@ jobs:
steps:
- name: Checkout pinned source
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -42,7 +42,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+1 -1
View File
@@ -64,7 +64,7 @@ jobs:
echo "Promoting floating tags for ${TAG} (prerelease: ${PRERELEASE})"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+2 -2
View File
@@ -54,7 +54,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -244,7 +244,7 @@ jobs:
control_plane_digest: ${{ steps.proof.outputs.control_plane_digest }}
steps:
- name: Checkout release verification control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+1 -1
View File
@@ -94,7 +94,7 @@ jobs:
echo "is_prerelease=$IS_PRERELEASE" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -27,7 +27,7 @@ jobs:
VERSION: ${{ inputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
# A reusable workflow inherits github.sha from its caller. Do not
# accept a caller-controlled checkout ref: the caller event commit is
@@ -48,7 +48,7 @@ jobs:
echo "release_id=${release_id}" >> "$GITHUB_OUTPUT"
- name: Checkout exact release tag
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout exact recovery implementation
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -32,7 +32,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout release control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+3 -3
View File
@@ -58,7 +58,7 @@ jobs:
helm_chart_digest: ${{ steps.marker.outputs.helm_chart_digest }}
steps:
- name: Checkout release integrity control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -214,7 +214,7 @@ jobs:
helm_chart_digest: ${{ needs.await_activation_commit.outputs.helm_chart_digest }}
steps:
- name: Checkout release control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 1
persist-credentials: true # required: authenticated git writes
@@ -472,7 +472,7 @@ jobs:
contents: write
steps:
- name: Checkout release control
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 1
persist-credentials: true # required: authenticated git writes
+1 -1
View File
@@ -121,7 +121,7 @@ jobs:
echo "[OK] Dry run executing on branch ${GITHUB_REF_NAME}"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+4 -4
View File
@@ -31,7 +31,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout release verification controls
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -291,7 +291,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -318,7 +318,7 @@ jobs:
- 'tests/integration/mock-github-server'
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -353,7 +353,7 @@ jobs:
directory: 'tests/integration'
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+3 -3
View File
@@ -41,7 +41,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -82,7 +82,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -235,7 +235,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+2 -2
View File
@@ -74,7 +74,7 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -181,7 +181,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
+2 -2
View File
@@ -129,7 +129,7 @@ jobs:
echo "Resolved demo deployment: tag=${TAG}, target=${TARGET}, environment=${ENVIRONMENT_NAME}"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -275,7 +275,7 @@ jobs:
echo "[OK] Stable demo mutation is bound to committed ${TAG} convergence run ${CONVERGENCE_RUN_ID}."
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
@@ -72,7 +72,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
@@ -7262,3 +7262,15 @@ The node drawer may use `Node.LinkedAgentID` to label Pulse coverage, but that
presentation does not combine agent lifecycle with package evidence. The
polling states are pinned independently in
`internal/monitoring/node_pending_updates_evidence_test.go`.
### Native agent lifecycle proof preserves protected checkout semantics
The native Linux and Windows agent lifecycle jobs obtain their source through
the reviewed immutable `actions/checkout` v6.1.0 pin. That baseline refuses
fork pull-request checkout on privileged events unless a workflow explicitly
opts out; Pulse prohibits that opt-out and the `pull_request_target` trigger.
A dependency refresh must update the central workflow-trust allowlist and the
native lifecycle workflow proof together, so lifecycle qualification cannot
silently execute under a weaker checkout boundary.
`scripts/check_workflow_trust.py`, `scripts/tests/test_workflow_trust.py`, and
`scripts/installtests/install_ps1_test.go` pin that relationship.
@@ -4712,3 +4712,17 @@ The opt-in belongs only to the generated systemd service. Container and
terminal output remain unprefixed, and the logger tees the unmodified record to
the rotating file sink and authenticated live-log broadcaster. Installer and
logging tests pin the unit directives, level mapping, and sink isolation.
### Release automation preserves protected checkout semantics
Every repository checkout in build, packaging, publication, qualification,
recovery, and deployment automation uses the reviewed immutable
`actions/checkout` v6.1.0 pin. That baseline refuses fork pull-request checkout
on privileged events unless a workflow explicitly opts out; Pulse prohibits
that opt-out and the `pull_request_target` trigger. Dependency refreshes must
update the central workflow-trust allowlist and its regression proof together,
so a routine pin change cannot silently remove this release-automation trust
boundary. `scripts/check_workflow_trust.py`,
`scripts/tests/test_workflow_trust.py`, and
`scripts/installtests/build_release_assets_test.go` pin the policy and the
release-workflow integration.
+43 -1
View File
@@ -43,6 +43,12 @@ SECRET_CONTEXT_TOKEN_RE = re.compile(r"(?<![\w.])secrets\b")
# configuration mechanism. Confidential credentials have no PR exception.
NON_CONFIDENTIAL_PULL_REQUEST_SECRETS = frozenset({"PULSE_LICENSE_PUBLIC_KEY"})
CHECKOUT_PREFIX = "actions/checkout@"
# v6.1.0 backports checkout's fail-closed fork-PR protection for privileged
# pull_request_target and workflow_run events. Keep this exact-pin allowlist
# reviewable: a dependency refresh must not silently discard that boundary.
PROTECTED_CHECKOUT_PINS = frozenset(
{"d23441a48e516b6c34aea4fa41551a30e30af803"}
)
WRITE_CREDENTIAL_RATIONALE = "# required: authenticated git writes"
PERMISSIONS_RE = re.compile(r"^(\s*)permissions\s*:\s*(.*?)\s*$")
JOBS_RE = re.compile(r"^(\s*)jobs\s*:\s*$")
@@ -198,6 +204,16 @@ def audit_workflow(path: Path) -> list[Finding]:
lines = path.read_text(encoding="utf-8").splitlines()
findings = _audit_runner_job_timeouts(path, lines)
if _has_trigger(lines, "pull_request_target"):
findings.append(
Finding(
path,
1,
"pull_request_target is prohibited; use pull_request or isolate "
"privileged work from pull-request code",
)
)
if _has_trigger(lines, "pull_request"):
for index, line in enumerate(lines):
code = line.split("#", 1)[0]
@@ -326,9 +342,35 @@ def audit_workflow(path: Path) -> list[Finding]:
# applying checkout-specific credential controls.
if not dependency.lower().startswith(CHECKOUT_PREFIX):
continue
if ref not in PROTECTED_CHECKOUT_PINS:
findings.append(
Finding(
path,
line_number,
"checkout pin is outside the reviewed privileged-event "
"protection baseline",
)
)
checkout_block = _checkout_block(lines, index)
unsafe_pr_settings = [
(block_index, block_line)
for block_index, block_line in checkout_block
if re.match(r"^\s*allow-unsafe-pr-checkout\s*:", block_line)
]
for setting_index, setting in unsafe_pr_settings:
if not re.match(
r"^\s*allow-unsafe-pr-checkout\s*:\s*false(?:\s|$)", setting
):
findings.append(
Finding(
path,
setting_index + 1,
"checkout must not opt out of privileged-event PR protection",
)
)
credential_settings = [
(block_index, block_line)
for block_index, block_line in _checkout_block(lines, index)
for block_index, block_line in checkout_block
if re.match(r"^\s*persist-credentials\s*:", block_line)
]
if len(credential_settings) != 1:
@@ -1337,7 +1337,7 @@ func TestBackfillReleaseWorkflowRepairsPublishedAssetsWithoutRebuilds(t *testing
`workflow_dispatch:`,
`contents: write`,
`runs-on: ubuntu-24.04`,
`uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3`,
`uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0`,
`uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0`,
`SYFT_VERSION="1.42.4"`,
`SYFT_ARCHIVE="syft_${SYFT_VERSION}_linux_amd64.tar.gz"`,
+1
View File
@@ -110,6 +110,7 @@ func TestNativeWindowsSelfTestDoesNotPreseedLifecycleState(t *testing.T) {
workflow := string(content)
required := []string{
`uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0`,
`node-version: '24'`,
`$selfTestStateDir = Join-Path $env:RUNNER_TEMP 'pulse-agent-self-test'`,
`$selfTestLogFile = Join-Path $selfTestStateDir 'pulse-agent.log'`,
+36 -2
View File
@@ -20,6 +20,7 @@ SPEC.loader.exec_module(workflow_trust)
PIN = "a" * 40
DIGEST = "b" * 64
CHECKOUT_PIN = next(iter(workflow_trust.PROTECTED_CHECKOUT_PINS))
class WorkflowTrustTest(unittest.TestCase):
@@ -38,7 +39,7 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@{PIN}
- uses: actions/checkout@{CHECKOUT_PIN}
with:
persist-credentials: false
- uses: owner/action/path@{PIN} # v1
@@ -161,11 +162,44 @@ steps:
any("must set persist-credentials" in finding for finding in mixed_case)
)
def test_accepts_documented_authenticated_git_write(self) -> None:
def test_requires_reviewed_protected_checkout_pin(self) -> None:
findings = self.audit(
f"""permissions: {{}}
steps:
- uses: actions/checkout@{PIN}
with:
persist-credentials: false
"""
)
self.assertTrue(
any(
"privileged-event protection baseline" in finding
for finding in findings
)
)
def test_rejects_privileged_pr_trigger_and_checkout_opt_out(self) -> None:
findings = self.audit(
f"""on:
pull_request_target:
permissions: {{}}
steps:
- uses: actions/checkout@{CHECKOUT_PIN}
with:
persist-credentials: false
allow-unsafe-pr-checkout: true
"""
)
self.assertTrue(
any("pull_request_target is prohibited" in finding for finding in findings)
)
self.assertTrue(any("must not opt out" in finding for finding in findings))
def test_accepts_documented_authenticated_git_write(self) -> None:
findings = self.audit(
f"""permissions: {{}}
steps:
- uses: actions/checkout@{CHECKOUT_PIN}
with:
persist-credentials: true # required: authenticated git writes
"""