fix(git-id-switcher): replace broken Snyk badge with static shield (#… #1427
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: Security Checks | |
| on: | |
| push: | |
| branches: [main] | |
| # Removed paths filter to improve SAST coverage (Scorecard check) | |
| # All commits now trigger security checks including CodeQL | |
| pull_request: | |
| branches: [main] | |
| # Removed paths filter to improve SAST coverage (Scorecard check) | |
| schedule: | |
| # Run daily at 00:00 UTC | |
| - cron: '0 0 * * *' | |
| branch_protection_rule: | |
| workflow_dispatch: | |
| permissions: {} | |
| jobs: | |
| security-audit: | |
| name: Security Audit | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| defaults: | |
| run: | |
| working-directory: extensions/git-id-switcher | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| # Install at root level for npm workspaces | |
| - name: Install dependencies | |
| working-directory: . | |
| run: npm ci | |
| # 2-tier dev audit: high → CI fail, moderate → warn only | |
| # Prevents Dependabot PRs from being blocked by moderate-level advisories | |
| # while ensuring high/critical vulnerabilities are never missed | |
| - name: Run npm audit (dev, high+critical = fail) | |
| run: npm audit --audit-level=high | |
| - name: Run npm audit (dev, moderate = warn only) | |
| run: npm audit --audit-level=moderate | |
| continue-on-error: true | |
| - name: Run npm audit (production only) | |
| run: npm audit --omit=dev --audit-level=high | |
| lint-security: | |
| name: Security Linting | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| defaults: | |
| run: | |
| working-directory: extensions/git-id-switcher | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| # Install at root level for npm workspaces | |
| - name: Install dependencies | |
| working-directory: . | |
| run: npm ci | |
| - name: Run ESLint | |
| run: npm run lint | |
| - name: Check for dangerous patterns | |
| run: | | |
| echo "Checking for dangerous exec() usage..." | |
| if grep -rn "exec(" src/ --include="*.ts" | grep -v "execFile" | grep -v "secureExec" | grep -v "\.exec(" | grep -v ".test.ts"; then | |
| echo "❌ Found potentially dangerous exec() calls" | |
| exit 1 | |
| fi | |
| echo "✅ No dangerous exec() patterns found" | |
| echo "Checking for string interpolation in commands..." | |
| if grep -rn 'execAsync.*`' src/ --include="*.ts" | grep -v ".test.ts"; then | |
| echo "❌ Found string interpolation in command execution" | |
| exit 1 | |
| fi | |
| echo "✅ No string interpolation in commands" | |
| security-tests: | |
| name: Security Tests | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| defaults: | |
| run: | |
| working-directory: extensions/git-id-switcher | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| # Install at root level for npm workspaces | |
| - name: Install dependencies | |
| working-directory: . | |
| run: npm ci | |
| - name: Compile TypeScript | |
| run: npm run compile | |
| - name: Run security tests (includes fuzzing) | |
| run: npm run test:security | |
| codeql-analysis: | |
| name: CodeQL Analysis | |
| runs-on: ubuntu-latest | |
| # Only run when results can be uploaded (skip forked PRs) | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | |
| permissions: | |
| actions: read | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4 | |
| with: | |
| languages: typescript | |
| config-file: ./.github/codeql-config.yml | |
| - name: Autobuild | |
| uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4 | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4 | |
| with: | |
| category: "/language:typescript" | |
| semgrep: | |
| name: Semgrep SAST | |
| runs-on: ubuntu-latest | |
| # Skip forked PRs (no SARIF upload permission) | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Run Semgrep | |
| uses: semgrep/semgrep-action@713efdd345f3035192eaa63f56867b88e63e4e5d # v1 (v0.58.0, verified 2026-03-17) | |
| with: | |
| config: >- | |
| p/typescript | |
| p/security-audit | |
| p/secrets | |
| socket-security: | |
| name: Socket.dev Security | |
| runs-on: ubuntu-latest | |
| # Only run on pull requests (analyzes dependency changes) | |
| if: github.event_name == 'pull_request' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Socket Security Review | |
| uses: SocketDev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 | |
| with: | |
| mode: firewall | |
| scorecard: | |
| name: OpenSSF Scorecard | |
| runs-on: ubuntu-latest | |
| # Only run on main branch (push, schedule) - not on PRs | |
| if: github.event_name != 'pull_request' | |
| permissions: | |
| contents: read | |
| security-events: write | |
| id-token: write | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Run Scorecard analysis | |
| uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 | |
| with: | |
| results_file: results.sarif | |
| results_format: sarif | |
| publish_results: true | |
| - name: Upload Scorecard results to Security tab | |
| uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4 | |
| with: | |
| sarif_file: results.sarif | |
| # Separate job: scorecard-action requires all steps in its job to use `uses` only. | |
| # Shell script steps cause workflow verification failure on result publication. | |
| scorecard-threshold: | |
| name: Scorecard Threshold Gate | |
| runs-on: ubuntu-latest | |
| needs: scorecard | |
| # Only run on main branch (push, schedule) - not on PRs | |
| if: github.event_name != 'pull_request' | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 | |
| with: | |
| egress-policy: audit | |
| - name: Check Scorecard score threshold | |
| env: | |
| SCORECARD_THRESHOLD: ${{ vars.SCORECARD_THRESHOLD || '7.0' }} | |
| run: | | |
| REPO="${{ github.repository }}" | |
| # Fetch latest published Scorecard results from REST API | |
| HTTP_CODE=$(curl -s -o scorecard-api.json -w "%{http_code}" \ | |
| "https://api.scorecard.dev/projects/github.com/${REPO}") | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::warning::Could not fetch Scorecard results (HTTP ${HTTP_CODE}). Skipping threshold check." | |
| echo "This may happen on the first run before results are published." | |
| exit 0 | |
| fi | |
| SCORE=$(jq -r '.score // empty' scorecard-api.json) | |
| if [ -z "$SCORE" ]; then | |
| echo "::warning::No score found in API response. Skipping threshold check." | |
| exit 0 | |
| fi | |
| echo "## OpenSSF Scorecard Results" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "**Overall score: ${SCORE} / 10.0** (threshold: ${SCORECARD_THRESHOLD})" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "| Check | Score | Status |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "|-------|-------|--------|" >> "$GITHUB_STEP_SUMMARY" | |
| jq -r '.checks[] | "| \(.name) | \(if .score < 0 then "N/A" else "\(.score)/10" end) | \(if .score < 0 then "➖" elif .score >= 7 then "✅" elif .score >= 4 then "⚠️" else "❌" end) |"' \ | |
| scorecard-api.json | sort >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Overall score: ${SCORE} / 10.0 (threshold: ${SCORECARD_THRESHOLD})" | |
| echo "" | |
| echo "Individual check scores:" | |
| jq -r '.checks[] | " \(if .score < 0 then "➖" elif .score >= 7 then "✅" elif .score >= 4 then "⚠️" else "❌" end) \(.name): \(if .score < 0 then "N/A" else "\(.score)/10" end)"' \ | |
| scorecard-api.json | sort | |
| echo "" | |
| # Compare scores using awk (bash does not support float comparison) | |
| BELOW=$(echo "${SCORE} ${SCORECARD_THRESHOLD}" | awk '{print ($1 < $2) ? "1" : "0"}') | |
| if [ "$BELOW" = "1" ]; then | |
| echo "::error::Scorecard score ${SCORE} is below threshold ${SCORECARD_THRESHOLD}" | |
| echo "" | |
| echo "Checks needing attention (score < 7):" | |
| jq -r '.checks[] | select(.score >= 0 and .score < 7) | " ❌ \(.name): \(.score)/10 — \(.reason)"' scorecard-api.json | |
| exit 1 | |
| fi | |
| echo "✅ Scorecard score ${SCORE} meets threshold ${SCORECARD_THRESHOLD}" |