Harden auth, SSRF, and dependency security #446
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
| name: Playwright E2E Tests | |
| # Runs on all pushes and PRs to staging branch | |
| # Includes browser caching for faster subsequent runs | |
| on: | |
| push: | |
| branches: [staging] | |
| pull_request: | |
| branches: [staging] | |
| jobs: | |
| test: | |
| timeout-minutes: 15 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v1 | |
| with: | |
| bun-version: latest | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Get installed Playwright version | |
| id: playwright-version | |
| run: | | |
| PLAYWRIGHT_VERSION=$(node -p "require('./package.json').devDependencies['@playwright/test']") | |
| echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT | |
| - name: Cache Playwright browsers | |
| uses: actions/cache@v4 | |
| id: playwright-cache | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }} | |
| - name: Install Playwright Browsers | |
| if: steps.playwright-cache.outputs.cache-hit != 'true' | |
| run: bunx playwright install --with-deps chromium | |
| - name: Install Playwright system dependencies | |
| if: steps.playwright-cache.outputs.cache-hit == 'true' | |
| run: bunx playwright install-deps chromium | |
| - name: Initialize test database | |
| run: bun run db:test:init | |
| env: | |
| NODE_ENV: test | |
| DB_PATH: test.db | |
| - name: Seed test database | |
| run: bun run db:test:seed | |
| env: | |
| NODE_ENV: test | |
| DB_PATH: test.db | |
| - name: Build application | |
| run: bun run build | |
| env: | |
| NODE_ENV: test | |
| DB_PATH: test.db | |
| GITHUB_CLIENT_ID: test_client | |
| GITHUB_CLIENT_SECRET: test_secret | |
| GITHUB_AUTHORIZATION_CALLBACK_URL: http://localhost:4173/auth/callback | |
| SEED_DATABASE: none | |
| ANTHROPIC_API_KEY: mock_anthropic_key | |
| YOUTUBE_API_KEY: mock_youtube_key | |
| GITHUB_TOKEN: mock_github_token | |
| PLUNK_API_SECRET_KEY: mock_plunk_key | |
| PLUNK_API_URL: http://localhost:3001 | |
| STRIPE_SECRET_KEY: sk_test_mock_stripe_key | |
| STRIPE_WEBHOOK_SECRET: whsec_mock_webhook_secret | |
| - name: Run Playwright tests | |
| id: playwright | |
| run: bunx playwright test 2>&1 | tee test-output.log | |
| continue-on-error: true | |
| env: | |
| NODE_ENV: test | |
| DB_PATH: test.db | |
| GITHUB_CLIENT_ID: test_client | |
| GITHUB_CLIENT_SECRET: test_secret | |
| GITHUB_AUTHORIZATION_CALLBACK_URL: http://localhost:4173/auth/callback | |
| SEED_DATABASE: none | |
| ANTHROPIC_API_KEY: mock_anthropic_key | |
| YOUTUBE_API_KEY: mock_youtube_key | |
| GITHUB_TOKEN: mock_github_token | |
| PLUNK_API_SECRET_KEY: mock_plunk_key | |
| PLUNK_API_URL: http://localhost:3001 | |
| STRIPE_SECRET_KEY: sk_test_mock_stripe_key | |
| STRIPE_WEBHOOK_SECRET: whsec_mock_webhook_secret | |
| - name: Parse test results | |
| if: always() | |
| id: test-results | |
| env: | |
| PLAYWRIGHT_OUTCOME: ${{ steps.playwright.outcome }} | |
| run: | | |
| if [ -f test-output.log ]; then | |
| # Parse the summary line from Playwright output (e.g., "65 passed (12.4s)") | |
| PASSED=$(grep -oP '\d+(?= passed)' test-output.log | tail -1 || true) | |
| FAILED=$(grep -oP '\d+(?= failed)' test-output.log | tail -1 || true) | |
| PASSED=${PASSED:-0} | |
| FAILED=${FAILED:-0} | |
| TOTAL=$((PASSED + FAILED)) | |
| else | |
| PASSED="0" | |
| FAILED="0" | |
| TOTAL="0" | |
| fi | |
| echo "total=$TOTAL" >> $GITHUB_OUTPUT | |
| echo "passed=$PASSED" >> $GITHUB_OUTPUT | |
| echo "failed=$FAILED" >> $GITHUB_OUTPUT | |
| echo "outcome=$PLAYWRIGHT_OUTCOME" >> $GITHUB_OUTPUT | |
| echo "Playwright Step Outcome: $PLAYWRIGHT_OUTCOME" | |
| echo "Test Results: Passed=$PASSED, Failed=$FAILED, Total=$TOTAL" | |
| - name: Comment PR with test results | |
| if: github.event_name == 'pull_request' && always() | |
| uses: actions/github-script@v7 | |
| env: | |
| TOTAL: ${{ steps.test-results.outputs.total }} | |
| PASSED: ${{ steps.test-results.outputs.passed }} | |
| FAILED: ${{ steps.test-results.outputs.failed }} | |
| OUTCOME: ${{ steps.test-results.outputs.outcome }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| with: | |
| script: | | |
| const total = process.env.TOTAL || '0'; | |
| const passed = process.env.PASSED || '0'; | |
| const failed = process.env.FAILED || '0'; | |
| const outcome = process.env.OUTCOME || 'unknown'; | |
| const runUrl = process.env.RUN_URL; | |
| const passed_int = parseInt(passed); | |
| const failed_int = parseInt(failed); | |
| const success = outcome === 'success' && failed_int === 0 && passed_int > 0; | |
| const icon = success ? '✅' : '❌'; | |
| const status = success ? 'All Tests Passed' : 'Tests Failed'; | |
| const body = `## ${icon} Playwright Test Results\n\n` + | |
| `**Status:** ${status}\n\n` + | |
| `- 🧪 Step Outcome: ${outcome}\n` + | |
| `- ✅ Passed: ${passed}\n` + | |
| `- ❌ Failed: ${failed}\n` + | |
| `- 📊 Total: ${total}\n\n` + | |
| `[View detailed HTML report](${runUrl})`; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: body | |
| }); | |
| - name: Upload Playwright Report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: playwright-report | |
| path: playwright-report/ | |
| retention-days: 30 | |
| - name: Upload Test Results | |
| uses: actions/upload-artifact@v4 | |
| if: failure() | |
| with: | |
| name: test-results | |
| path: test-results/ | |
| retention-days: 30 | |
| - name: Fail if tests failed | |
| if: always() | |
| run: | | |
| OUTCOME="${{ steps.test-results.outputs.outcome }}" | |
| TOTAL="${{ steps.test-results.outputs.total }}" | |
| FAILED="${{ steps.test-results.outputs.failed }}" | |
| if [ "$OUTCOME" != "success" ]; then | |
| echo "❌ Playwright test step failed (outcome: $OUTCOME)" | |
| exit 1 | |
| fi | |
| if [ "${TOTAL:-0}" -eq 0 ]; then | |
| echo "❌ No Playwright tests were executed" | |
| exit 1 | |
| fi | |
| if [ "$FAILED" != "0" ]; then | |
| echo "❌ $FAILED test(s) failed" | |
| exit 1 | |
| fi | |
| echo "✅ Playwright tests passed" |