fix(oauth): MCP /mcp returns 401 (not 500) for invalid/expired tokens #219
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: CI | |
| on: | |
| push: | |
| branches: [main, dev] | |
| pull_request: | |
| branches: [main, dev] | |
| workflow_dispatch: | |
| env: | |
| NODE_VERSION: "22" | |
| # Opt into Node.js 24 runner for JavaScript actions ahead of the June 2026 default. | |
| # See: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/ | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| jobs: | |
| typecheck: | |
| name: TypeScript Compile | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| - run: npm ci | |
| - run: npx tsc --noEmit | |
| test: | |
| name: Unit + Integration Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| - run: npm ci | |
| - name: Run Vitest (ephemeral server, in-memory DB, RM_TEST_MODE=1) | |
| run: npm test | |
| security: | |
| name: Security Checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Verify .env files are gitignored | |
| run: | | |
| if ! grep -q '\.env' .gitignore 2>/dev/null; then | |
| echo "::error::.env is not in .gitignore" | |
| exit 1 | |
| fi | |
| - name: Verify no sensitive files are tracked | |
| run: | | |
| SENSITIVE=(.env .env.local .env.production credentials.json serviceAccountKey.json) | |
| for f in "${SENSITIVE[@]}"; do | |
| if git ls-files --error-unmatch "$f" 2>/dev/null; then | |
| echo "::error::Sensitive file '${f}' is tracked by git -- remove it and rotate credentials" | |
| exit 1 | |
| fi | |
| done | |
| - name: Scan for hardcoded secrets | |
| run: | | |
| PATTERNS=( | |
| 'AKIA[0-9A-Z]{16}' | |
| 'sk-[a-zA-Z0-9]{20,}' | |
| 'sk_live_[a-zA-Z0-9]{20,}' | |
| 'rk_live_[a-zA-Z0-9]{20,}' | |
| 'ghp_[a-zA-Z0-9]{36}' | |
| 'gho_[a-zA-Z0-9]{36}' | |
| 'github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}' | |
| 'glpat-[a-zA-Z0-9\-]{20,}' | |
| 'xox[bpors]-[a-zA-Z0-9\-]+' | |
| 'SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}' | |
| 'rm_live_[a-f0-9]{48}' | |
| ) | |
| FAILED=0 | |
| for pattern in "${PATTERNS[@]}"; do | |
| MATCHES=$(grep -rEn "$pattern" \ | |
| --include='*.ts' --include='*.js' --include='*.mjs' \ | |
| --include='*.json' --include='*.yml' --include='*.yaml' \ | |
| --include='*.md' --include='*.env.*' \ | |
| --exclude-dir=node_modules --exclude-dir=dist \ | |
| --exclude-dir=.git --exclude='package-lock.json' \ | |
| --exclude='ci.yml' --exclude='security.yml' \ | |
| . 2>/dev/null || true) | |
| if [ -n "$MATCHES" ]; then | |
| echo "::error::Potential secret matching: ${pattern:0:20}..." | |
| echo "$MATCHES" | head -5 | |
| FAILED=1 | |
| fi | |
| done | |
| if [ "$FAILED" -eq 1 ]; then | |
| exit 1 | |
| fi | |
| audit: | |
| name: Dependency Audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| - run: npm ci | |
| # Temporarily allow moderate until vuln fix PR lands (tracked in TEST_PLAN.md phase 7). | |
| # Critical/high still block. | |
| - run: npm audit --production --audit-level=high |