Github Actions

Integrate BlockSecOps into your GitHub workflow. - GitHub repository - BlockSecOps account with API access - API key created --- 1. Go to Settings → API Keys...

Last updated: January 14, 2026

GitHub Actions

Integrate BlockSecOps into your GitHub workflow.

Prerequisites

  • GitHub repository
  • BlockSecOps account with API access
  • API key created

Setup

1. Create API Key

  1. Go to SettingsAPI Keys
  2. Click Create Key
  3. Name: "GitHub Actions"
  4. Permissions: Scans, Contracts
  5. Copy the key

2. Add Secret

  1. Go to GitHub repo → Settings → Secrets
  2. Click New repository secret
  3. Name: BLOCKSECOPS_API_KEY
  4. Value: Your API key
  5. Save

Basic Workflow

Create .github/workflows/security-scan.yml:

name: Security Scan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  security-scan:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install dependencies
        run: |
          npm install -g @blocksecops/cli

      - name: Run security scan
        env:
          BLOCKSECOPS_API_KEY: ${{ secrets.BLOCKSECOPS_API_KEY }}
        run: |
          blocksecops scan ./contracts --preset standard

      - name: Check results
        env:
          BLOCKSECOPS_API_KEY: ${{ secrets.BLOCKSECOPS_API_KEY }}
        run: |
          blocksecops check --fail-on critical

Using the API Directly

If not using the CLI:

name: Security Scan (API)

on:
  push:
    paths:
      - 'contracts/**'

jobs:
  scan:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Create archive
        run: |
          cd contracts
          zip -r ../contracts.zip .

      - name: Upload and scan
        env:
          API_KEY: ${{ secrets.BLOCKSECOPS_API_KEY }}
          API_URL: https://api.blocksecops.com/api/v1
        run: |
          # Upload
          UPLOAD_RESPONSE=$(curl -s -X POST "$API_URL/contracts/upload" \
            -H "Authorization: Bearer $API_KEY" \
            -F "[email protected]")

          CONTRACT_ID=$(echo $UPLOAD_RESPONSE | jq -r '.id')

          # Start scan
          SCAN_RESPONSE=$(curl -s -X POST "$API_URL/scans" \
            -H "Authorization: Bearer $API_KEY" \
            -H "Content-Type: application/json" \
            -d "{\"contract_id\": \"$CONTRACT_ID\", \"preset\": \"standard\"}")

          SCAN_ID=$(echo $SCAN_RESPONSE | jq -r '.id')

          # Wait for completion
          while true; do
            STATUS=$(curl -s "$API_URL/scans/$SCAN_ID" \
              -H "Authorization: Bearer $API_KEY" | jq -r '.status')

            if [[ "$STATUS" == "completed" ]]; then
              break
            elif [[ "$STATUS" == "failed" ]]; then
              echo "Scan failed"
              exit 1
            fi

            echo "Waiting... ($STATUS)"
            sleep 15
          done

          # Get results
          RESULTS=$(curl -s "$API_URL/scans/$SCAN_ID/results" \
            -H "Authorization: Bearer $API_KEY")

          # Check for critical
          CRITICAL=$(echo $RESULTS | jq '.summary.critical')
          if [[ "$CRITICAL" -gt 0 ]]; then
            echo "::error::Found $CRITICAL critical vulnerabilities!"
            exit 1
          fi

          echo "Scan passed!"

PR Comment Integration

Post results as PR comment:

- name: Post PR comment
  if: github.event_name == 'pull_request'
  uses: actions/github-script@v7
  with:
    script: |
      const results = JSON.parse(process.env.RESULTS);

      const body = `## Security Scan Results

      | Severity | Count |
      |----------|-------|
      | Critical | ${results.summary.critical} |
      | High | ${results.summary.high} |
      | Medium | ${results.summary.medium} |
      | Low | ${results.summary.low} |

      [View full results](https://app.blocksecops.com/scans/${results.scan_id})
      `;

      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: body
      });

Conditional Scanning

Only on Contract Changes

on:
  push:
    paths:
      - 'contracts/**'
      - 'src/**/*.sol'

Skip for Documentation

jobs:
  scan:
    if: |
      !contains(github.event.head_commit.message, '[skip ci]') &&
      !contains(github.event.head_commit.message, '[docs]')

Matrix Scanning

Scan multiple projects:

jobs:
  scan:
    strategy:
      matrix:
        project: [token, vault, governance]

    steps:
      - name: Scan ${{ matrix.project }}
        run: |
          blocksecops scan ./contracts/${{ matrix.project }}

Caching

Speed up builds with caching:

- name: Cache scan results
  uses: actions/cache@v4
  with:
    path: .blocksecops-cache
    key: scan-${{ hashFiles('contracts/**') }}
    restore-keys: |
      scan-

Status Checks

Required Status Check

  1. Go to repo Settings → Branches
  2. Add branch protection rule
  3. Require status checks
  4. Select "Security Scan"

Status Badge

Add to README:

![Security Scan](https://github.com/org/repo/actions/workflows/security-scan.yml/badge.svg)

Scheduled Scans

Run nightly deep scans:

on:
  schedule:
    - cron: '0 2 * * *'  # 2 AM daily

jobs:
  nightly-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deep scan
        run: blocksecops scan --preset deep

Troubleshooting

Action Fails with Auth Error

  • Verify secret name matches
  • Check API key is valid
  • Ensure key has required permissions

Scan Takes Too Long

  • Use quick preset for PRs
  • Cache results for unchanged files
  • Consider async scanning with webhooks

Rate Limit Exceeded

  • Add delays between API calls
  • Check your plan's limits
  • Use webhooks instead of polling

Next Steps