From c26973958d88c910dc1785790acb8057abb5c953 Mon Sep 17 00:00:00 2001 From: Albert Mavashev Date: Sat, 2 May 2026 21:57:14 -0400 Subject: [PATCH] ops(security): demote security-events:write to job level + fix downloadThenRun Two Scorecard high-severity findings addressed: 1. pr-container-scan.yml: TokenPermissionsID HIGH -- 'topLevel security-events permission set to write'. Same fix pattern as cycles-server PR #144: top-level drops to read-all, security-events: write moves to the scan job (only step that needs it is the Trivy SARIF upload). 2. release.yml line 150: PinnedDependenciesID 'downloadThenRun not pinned by hash'. The pattern was 'curl ... | python -c ...' which Scorecard's heuristic flags as a supply-chain risk regardless of the source URL (here it's a localhost smoke probe, not external). Refactor to capture the body first, then pipe to python -- same behavior, no footgun pattern. --- .github/workflows/pr-container-scan.yml | 7 ++++--- .github/workflows/release.yml | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr-container-scan.yml b/.github/workflows/pr-container-scan.yml index 67e737a..1d7b266 100644 --- a/.github/workflows/pr-container-scan.yml +++ b/.github/workflows/pr-container-scan.yml @@ -18,14 +18,15 @@ on: - '**/pom.xml' - '.github/workflows/pr-container-scan.yml' -permissions: - contents: read - security-events: write # for Trivy SARIF upload to Security tab +permissions: read-all jobs: scan: runs-on: ubuntu-latest timeout-minutes: 20 + permissions: + contents: read + security-events: write # required for Trivy SARIF upload to Security tab steps: - name: Checkout diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3bf166b..4cbea7b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -147,7 +147,12 @@ jobs: env: VERSION: ${{ needs.build-and-push.outputs.version }} run: | - served=$(curl -s http://localhost:7979/actuator/info | python -c "import sys,json; print(json.load(sys.stdin).get('build',{}).get('version','?'))") + # Avoid `curl | python` (Scorecard's downloadThenRun heuristic flags + # any download piped directly into an interpreter, regardless of the + # source). Capture the body first, then parse — same behavior, no + # supply-chain footgun pattern. + info=$(curl -s http://localhost:7979/actuator/info) + served=$(echo "$info" | python -c "import sys,json; print(json.load(sys.stdin).get('build',{}).get('version','?'))") if [ "$served" != "$VERSION" ]; then echo "ERROR: image reports version=$served but tag is $VERSION" exit 1