diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index a1547fb..ab890b6 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -20,20 +20,17 @@ on: # https://docs.github.com/en/enterprise-cloud@latest/code-security/supply-chain-security/understanding-your-software-supply-chain/using-the-dependency-submission-api permissions: contents: read - # Write permissions for pull-requests are required for using the `comment-summary-in-pr` option, comment out if you aren't using this option - pull-requests: write jobs: dependency-review: runs-on: ubuntu-latest steps: - name: 'Checkout repository' - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: 'Dependency Review' - uses: actions/dependency-review-action@v4 + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4 # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. - with: - comment-summary-in-pr: always + # No PR comment output to keep permissions read-only. # fail-on-severity: moderate # deny-licenses: GPL-1.0-or-later, LGPL-2.0-or-later # retry-on-snapshot-warnings: true diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 4cdc3b8..114e07b 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -6,19 +6,22 @@ on: push: branches: - main +permissions: {} jobs: sonarcloud: runs-on: ubuntu-latest + environment: org-prod permissions: + id-token: write contents: read pull-requests: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - - uses: actions/setup-python@v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.12' @@ -28,11 +31,43 @@ jobs: - name: Run tests with coverage run: make coverage-sonar + - name: Skip SonarCloud on untrusted fork PR + if: ${{ github.event_name == 'pull_request' && (github.event.pull_request.head.repo.fork || !contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association)) }} + run: | + echo "Skipping SonarCloud token retrieval for untrusted PR context." + echo "Fork PRs and non-collaborator authors do not receive Vault-backed token access." + + - name: Azure login (OIDC) + if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.fork == false && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association)) }} + uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + + - name: Read SonarCloud token from Key Vault + if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.fork == false && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association)) }} + id: sonar_token + shell: bash + run: | + set -euo pipefail + SONAR_TOKEN="$(az keyvault secret show \ + --vault-name "${{ vars.AZURE_KEYVAULT_NAME }}" \ + --name "sonar-cloud-token" \ + --query value -o tsv)" + if [ -z "${SONAR_TOKEN}" ]; then + echo "Key Vault secret sonar-cloud-token is empty." + exit 1 + fi + echo "::add-mask::$SONAR_TOKEN" + echo "value=$SONAR_TOKEN" >> "$GITHUB_OUTPUT" + - name: SonarCloud scan - uses: SonarSource/sonarcloud-github-action@ffc3010689be73b8e5ae0c57ce35968afd7909e8 + if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.fork == false && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association)) }} + uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 # v7.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_TOKEN: ${{ steps.sonar_token.outputs.value }} with: args: > -Dsonar.host.url=https://sonarcloud.io @@ -43,10 +78,11 @@ jobs: -Dsonar.enableIssueAnnotation=true - name: SonarCloud quality gate + if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.fork == false && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association)) }} uses: SonarSource/sonarqube-quality-gate-action@cf038b0e0cdecfa9e56c198bbb7d21d751d62c3b with: scanMetadataReportFile: dist/quality/sonar/scannerwork/report-task.txt env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_TOKEN: ${{ steps.sonar_token.outputs.value }} SONAR_HOST_URL: https://sonarcloud.io timeout-minutes: 5