fix(security): harden argument validation and risk detection in SecurityPolicy #6654
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json | |
| # add this workflow to a repo to fix lock files for Renovate PRs | |
| name: Fix Renovate | |
| on: | |
| issue_comment: | |
| types: [created, edited] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.issue.number || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| lock-update: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| permissions: | |
| contents: read | |
| if: >- | |
| false && | |
| startsWith(github.event.comment.body, '/fix-lock') && | |
| github.event.issue.pull_request != null && | |
| ( | |
| github.event.comment.author_association == 'OWNER' || | |
| github.event.comment.author_association == 'MEMBER' || | |
| github.event.comment.author_association == 'COLLABORATOR' | |
| ) | |
| env: | |
| PR: ${{ github.event.issue.pull_request.html_url }} | |
| steps: | |
| - name: 📡 Get PR Data | |
| if: github.event_name == 'issue_comment' && github.event.issue.pull_request != null | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| id: get-pr-data | |
| with: | |
| # language=javascript | |
| script: | | |
| console.log(`Get PR info: ${context.repo.owner}/${context.repo.repo}#${context.issue.number}`) | |
| const {data: pr} = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.issue.number | |
| }) | |
| console.log(`PR info: ${JSON.stringify(pr)}`) | |
| const prInfo = { | |
| num: context.issue.number, | |
| branchName: pr.head.ref, | |
| commit: pr.head.sha, | |
| committer: pr.head.user.login, | |
| repo: pr.head.repo.full_name | |
| } | |
| const baseRepo = `${context.repo.owner}/${context.repo.repo}` | |
| if (prInfo.repo !== baseRepo) { | |
| core.setFailed(`PR must come from ${baseRepo}, got ${prInfo.repo}`) | |
| return | |
| } | |
| if (prInfo.committer === 'github-actions[bot]' || prInfo.committer === 'dependabot[bot]' || prInfo.committer === 'renovate[bot]') { | |
| console.log(`PR is from ${prInfo.committer}`) | |
| } else { | |
| console.log(`PR is not from ${prInfo.committer}`) | |
| core.setFailed(`PR is from ${prInfo.committer} is not allowed`) | |
| } | |
| core.setOutput('head_repo', prInfo.repo) | |
| core.setOutput('head_branch', prInfo.branchName) | |
| core.setOutput('head_sha', prInfo.commit) | |
| core.setOutput('committer', prInfo.committer) | |
| - name: 📡 Use PR Branch | |
| run: | | |
| echo "The PR branch is ${{ steps.get-pr-data.outputs.head_branch }}" | |
| echo "The PR commit is ${{ steps.get-pr-data.outputs.head_sha }}" | |
| echo "The repository is ${{ steps.get-pr-data.outputs.head_repo }}" | |
| - name: ✈ Checkout PR commit | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 1 | |
| repository: ${{ steps.get-pr-data.outputs.head_repo }} | |
| ref: ${{ steps.get-pr-data.outputs.head_sha }} | |
| - name: 🔍 Verify checked-out commit | |
| run: | | |
| EXPECTED_SHA="${{ steps.get-pr-data.outputs.head_sha }}" | |
| ACTUAL_SHA="$(git rev-parse HEAD)" | |
| echo "Expected SHA: ${EXPECTED_SHA}" | |
| echo "Actual SHA: ${ACTUAL_SHA}" | |
| if [[ "${ACTUAL_SHA}" != "${EXPECTED_SHA}" ]]; then | |
| echo "Checked-out commit does not match expected PR SHA." | |
| exit 1 | |
| fi | |
| - name: 📦 Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 | |
| with: | |
| version: 10 | |
| - name: 📦 Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: "24" | |
| cache: "pnpm" | |
| cache-dependency-path: | | |
| pnpm-lock.yaml | |
| clients/web/pnpm-lock.yaml | |
| clients/web/apps/docs/pnpm-lock.yaml | |
| clients/web/apps/marketing/pnpm-lock.yaml | |
| - name: ☕ Setup Java | |
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 | |
| with: | |
| java-version: "25" | |
| distribution: "corretto" | |
| - name: 🦀 Setup Rust toolchain | |
| run: rustup toolchain install 1.92 --profile minimal --component rustfmt --component clippy | |
| - name: 🐘 Setup Gradle | |
| uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 | |
| with: | |
| gradle-version: wrapper | |
| cache-read-only: false | |
| - name: 🔧 Make Gradle wrapper executable | |
| run: chmod +x ./gradlew | |
| - name: 🔧 Show Gradle version | |
| run: ./gradlew --version | |
| - name: 🔐 Re-validate PR head SHA before write actions | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| EXPECTED_SHA: ${{ steps.get-pr-data.outputs.head_sha }} | |
| EXPECTED_REPO: ${{ steps.get-pr-data.outputs.head_repo }} | |
| with: | |
| # language=javascript | |
| script: | | |
| const expectedSha = process.env.EXPECTED_SHA | |
| const expectedRepo = process.env.EXPECTED_REPO | |
| // Fetch the current PR head from the API to detect any changes on the remote branch | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.issue.number, | |
| }) | |
| const currentRepo = pr.head.repo.full_name | |
| const currentSha = pr.head.sha | |
| core.info(`Expected repo/SHA: ${expectedRepo} @ ${expectedSha}`) | |
| core.info(`Current repo/SHA (remote): ${currentRepo} @ ${currentSha}`) | |
| // Also ensure that the locally checked-out commit matches the expected SHA | |
| const { execSync } = require('node:child_process') | |
| const localSha = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim() | |
| core.info(`Current repo/SHA (local HEAD): ${expectedRepo} @ ${localSha}`) | |
| const remoteChanged = currentRepo !== expectedRepo || currentSha !== expectedSha | |
| const localChanged = localSha !== expectedSha | |
| if (remoteChanged || localChanged) { | |
| core.setFailed( | |
| `PR head changed after approval/check (expected ${expectedRepo}@${expectedSha}, got remote ${currentRepo}@${currentSha}, local HEAD ${expectedRepo}@${localSha}). Re-run /fix-lock.` | |
| ) | |
| } | |
| - name: 🔒 Write all Gradle locks | |
| run: ./gradlew writeLocksAll --no-parallel | |
| - name: 📝 Get Commit Message | |
| run: | | |
| COMMIT_MSG=$(git log --format=%s -n 1 "${{ steps.get-pr-data.outputs.head_sha }}") | |
| echo "COMMIT_MSG=${COMMIT_MSG}" >> "$GITHUB_ENV" | |
| - name: 💾 Commit file changes (only for bots) | |
| env: | |
| SKIP_GIT_HOOKS: "1" | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add -A | |
| git diff --cached --quiet || git commit -m "${COMMIT_MSG}" | |
| - name: 🔐 Re-validate PR head SHA before push | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| EXPECTED_SHA: ${{ steps.get-pr-data.outputs.head_sha }} | |
| EXPECTED_REPO: ${{ steps.get-pr-data.outputs.head_repo }} | |
| with: | |
| # language=javascript | |
| script: | | |
| const expectedSha = process.env.EXPECTED_SHA | |
| const expectedRepo = process.env.EXPECTED_REPO | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.issue.number, | |
| }) | |
| const currentRepo = pr.head.repo.full_name | |
| const currentSha = pr.head.sha | |
| core.info(`Expected repo/SHA: ${expectedRepo} @ ${expectedSha}`) | |
| core.info(`Current repo/SHA (remote): ${currentRepo} @ ${currentSha}`) | |
| // Local HEAD intentionally not checked: it now includes our lock-file | |
| // commit and will differ from expectedSha. Only remote ref matters. | |
| const { execSync } = require('node:child_process') | |
| const localSha = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim() | |
| core.info(`Current repo/SHA (local HEAD): ${expectedRepo} @ ${localSha}`) | |
| if (currentRepo !== expectedRepo || currentSha !== expectedSha) { | |
| core.setFailed( | |
| `PR head changed after approval/check (expected ${expectedRepo}@${expectedSha}, got remote ${currentRepo}@${currentSha}). Re-run /fix-lock.` | |
| ) | |
| } | |
| - name: 🚀 Push lockfile updates | |
| env: | |
| SKIP_GIT_HOOKS: "1" | |
| run: git push --force-with-lease=refs/heads/${{ steps.get-pr-data.outputs.head_branch }}:${{ steps.get-pr-data.outputs.head_sha }} origin HEAD:${{ steps.get-pr-data.outputs.head_branch }} | |
| - name: 💬 Add reaction to trigger comment | |
| uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| comment-id: ${{ github.event.comment.id }} | |
| reactions: hooray |