diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f5860ef..5cb5922a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,7 @@ name: CI -# SECURITY NOTE: This workflow uses pull_request_target which has access to secrets. -# This is needed because tests require access to external services with credentials. -# `pull_request_target` will always run without manual approval, even if "Require approval for all external contributors" is enabled in the repo settings. -# Therefore we implement a "safe to test" label that must be manually added once we have checked that the diff is safe. -# For PRs from forks, secrets are only provided when the "safe to test" label is present. -# This allows maintainers to safely test external contributions while preventing -# malicious actors from accessing secrets. +# SECURITY: Uses environment protection for external PRs instead of unsafe "safe to test" labels. +# Environment protection provides secure manual approval tied to specific commits, +# eliminating race conditions and ensuring maintainer review before secrets access. on: push: branches: [main] @@ -13,37 +9,73 @@ on: - "**.md" - ".changeset/**" pull_request_target: - types: [opened, synchronize, reopened, labeled] + types: [opened, synchronize, reopened] paths-ignore: - "**.md" - ".changeset/**" pull_request: types: [opened, synchronize, reopened] - paths: - - .github/workflows/ci.yml + paths-ignore: + - "**.md" + - ".changeset/**" -concurrency: ${{ github.workflow }}--${{ github.ref }} +concurrency: + group: ${{ github.workflow }}--${{ github.event_name == 'pull_request_target' && format('pr#{0}', github.event.pull_request.number) || github.ref }} + cancel-in-progress: true permissions: - pull-requests: write + contents: read + pull-requests: read jobs: - main: - name: Node.js 20 + # Basic validation job - runs for all PRs without secrets + basic-validation: + name: Build and lint runs-on: ubuntu-latest - # Only run tests with secrets if: - # 1. This is a push to main, OR - # 2. PR is from the same repository (trusted), OR - # 3. PR has the "safe to test" label (maintainer approved) + if: github.event_name == 'pull_request' + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci --no-fund --no-audit + + - name: Build + run: npm run build + + - name: Check formatting + run: npm run format:check + + - name: Run linters + run: npm run lint + + # Integration tests with secrets - requires approval for external PRs + tests: + name: Tests + runs-on: ubuntu-latest + # SECURITY: Use environment protection for external contributors + environment: ${{ github.event.pull_request.head.repo.full_name != github.repository && 'external-testing' || '' }} + # Run tests with secrets for: + # 1. Push to main (trusted), OR + # 2. PR from same repository (trusted) + # For external PRs: environment protection requires manual approval if: | - github.event_name == 'push' || - github.event.pull_request.head.repo.full_name == github.repository || - contains(github.event.pull_request.labels.*.name, 'safe to test') + github.event_name == 'push' || + (github.event.pull_request.head.repo.full_name == github.repository) steps: - name: Checkout sources uses: actions/checkout@v4 with: + # Environment protection provides security - we can safely checkout PR code ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Decrypt keyfile @@ -52,9 +84,11 @@ jobs: KEYFILE_PASSPHRASE: ${{secrets.KEYFILE_PASSPHRASE}} - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 20.19 + node-version: 20.x + cache: npm + cache-dependency-path: package-lock.json - name: Install dependencies run: npm ci --no-fund --no-audit @@ -62,12 +96,6 @@ jobs: - name: Build run: npm run build - - name: Check formatting - run: npm run format:check - - - name: Run linters - run: npm run lint - - name: Run tests run: npm run test env: diff --git a/bb.test b/bb.test deleted file mode 100644 index 3f8cad41..00000000 --- a/bb.test +++ /dev/null @@ -1 +0,0 @@ -bugbounty: pwned by Nisimi, Orca Security