From 8b8a6ed85a92a47b32b3c499e882f92e65e3aabb Mon Sep 17 00:00:00 2001 From: 07souravkunda Date: Wed, 26 Aug 2026 14:32:12 +0530 Subject: [PATCH] ci: harden release workflows (SHA-pin actions, least-privilege token, TLS timestamper) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supply-chain hardening for the CI/CD workflows that build, sign and publish the BrowserStackLocal NuGet package. CWE-829 — pin third-party actions to immutable commit SHAs: cd.yml and ci.yml referenced actions/checkout, microsoft/setup-msbuild, actions/setup-dotnet, actions/setup-java and actions/upload-artifact by mutable tag. A tag can be repointed by whoever controls the action repo, which would run their code in the release job alongside NUGET_API_KEY and the GCP KMS signing credentials. Each ref is now a 40-char commit SHA with the version kept as a trailing comment. The pins resolve to exactly what the tags pointed at when this change was made, so the runner executes the same action code as before. Adds .github/dependabot.yml (github-actions, weekly) so the pins stay current. CWE-732 — declare least-privilege GITHUB_TOKEN permissions: Neither workflow declared a permissions block, so the token inherited the org/repo default. Both now declare `contents: read` at workflow level, so they fail closed regardless of that setting. No step needs more: checkout needs contents:read, publishing authenticates with NUGET_API_KEY, signing with GCP_SA_KEY, and upload-artifact@v4 uses the Actions runtime token. CWE-311 — request the signing timestamp over TLS: scripts/sign_nupkg.sh contacted the RFC 3161 timestamp authority over plain HTTP. Switched to https://timestamp.sectigo.com — same authority and policy OID, verified to issue tokens over TLS. No functional change: no C# source is touched and no packaged bytes change. Co-Authored-By: Claude Opus 5 (1M context) --- .github/dependabot.yml | 12 ++++++++++++ .github/workflows/cd.yml | 16 +++++++++++----- .github/workflows/ci.yml | 14 ++++++++++---- scripts/sign_nupkg.sh | 2 +- 4 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5d0a699 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# Keeps the commit-SHA pins in .github/workflows/ maintained. +# Pinning actions to an immutable SHA (rather than a mutable tag) is what stops a +# compromised or re-pointed action tag from executing in the release job; Dependabot +# is what stops those pins from going stale. +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "ci" diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a08221b..b8661c2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,6 +2,12 @@ name: .NET package CD on: [workflow_dispatch] +# Least privilege: no step in this workflow writes to the repo, +# releases or packages via GITHUB_TOKEN (publishing uses NUGET_API_KEY, +# signing uses GCP_SA_KEY). Fail closed regardless of the org default. +permissions: + contents: read + defaults: run: working-directory: BrowserStackLocal @@ -11,9 +17,9 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@0717577d45739eb3c851188b29f50ed6c0b2194e # v2 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1.0.2 - name: Build BrowserStackLocal run: | msbuild BrowserStackLocal -t:restore -p:Configuration=Release @@ -23,7 +29,7 @@ jobs: msbuild BrowserStackLocalIntegrationTests -t:restore -p:Configuration=Release msbuild BrowserStackLocalIntegrationTests -t:build -p:Configuration=Release - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@55ec9447dda3d1cf6bd587150f3262f30ee10815 # v3 with: dotnet-version: 6.0.x - name: Setup GCP credentials @@ -40,7 +46,7 @@ jobs: run: python3 -m pip install google-auth requests shell: bash - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4 with: distribution: 'temurin' java-version: '17' @@ -58,7 +64,7 @@ jobs: ./scripts/sign_nupkg.sh shell: bash - name: Save artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: BrowserStackLocal.nupkg path: .\BrowserStackLocal\BrowserStackLocal\bin\Release\*.nupkg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e49b687..2954cdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,12 @@ name: .NET package CI on: [workflow_dispatch] +# Least privilege: no step in this workflow writes to the repo, +# releases or packages via GITHUB_TOKEN (publishing uses NUGET_API_KEY, +# signing uses GCP_SA_KEY). Fail closed regardless of the org default. +permissions: + contents: read + defaults: run: working-directory: BrowserStackLocal @@ -11,9 +17,9 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@0717577d45739eb3c851188b29f50ed6c0b2194e # v2 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1.0.2 - name: Build BrowserStackLocal run: | msbuild BrowserStackLocal -t:restore -p:Configuration=Release @@ -23,7 +29,7 @@ jobs: msbuild BrowserStackLocalIntegrationTests -t:restore -p:Configuration=Release msbuild BrowserStackLocalIntegrationTests -t:build -p:Configuration=Release - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@55ec9447dda3d1cf6bd587150f3262f30ee10815 # v3 with: dotnet-version: 6.0.x - name: Run Integration Tests @@ -34,7 +40,7 @@ jobs: - name: Pack NuGet Package run: msbuild BrowserStackLocal -t:pack -p:Configuration=Release - name: Save artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: BrowserStackLocal.nupkg path: .\BrowserStackLocal\BrowserStackLocal\bin\Release\*.nupkg diff --git a/scripts/sign_nupkg.sh b/scripts/sign_nupkg.sh index 836b080..2f5c7e8 100755 --- a/scripts/sign_nupkg.sh +++ b/scripts/sign_nupkg.sh @@ -37,7 +37,7 @@ for NUPKG in BrowserStackLocal/BrowserStackLocal/bin/Release/*.nupkg; do --keystore "projects/browserstack-production/locations/us-east1/keyRings/prod-comodo-win-cert-keyring" \ --alias "prod-comodo-win-cert-key/cryptoKeyVersions/1" \ --certfile comodo_signing_cert.crt \ - --tsaurl http://timestamp.sectigo.com \ + --tsaurl https://timestamp.sectigo.com \ "$NUPKG" SIGNED_ANY=1 done