Skip to free scan

Smart Contract Security in GitHub Actions: A Working CI Setup

Firepan Security TeamOctober 29, 2026

Short answer: Smart contract CI security means running automated analysis on every pull request so known vulnerability patterns are caught before merge. A solid baseline is three jobs: Slither static analysis on every PR with results uploaded to GitHub code scanning, your Foundry tests including invariant tests on every PR, and longer Echidna fuzzing campaigns on a schedule. The workflows below use the official, maintained actions for each tool.

Why Run Security Checks in CI

Manual review happens a few times in a protocol's life; code changes every day. CI closes that gap for the class of bugs automated tools are good at — known patterns and broken invariants — and leaves reviewers free to spend their time on design and economics. It also gives you a history: when a finding first appeared, which PR introduced it, and when it was fixed.

What CI will not do is tell you your protocol is safe. Treat it as a regression net, not an audit. For the trade-offs between tools, see our smart contract security tools comparison and the hands-on Slither, Mythril, and Echidna workflow.

Job 1: Slither on Every Pull Request

The crytic/slither-action compiles your project (Foundry, Hardhat, and other frameworks are detected automatically) and runs Slither. Writing SARIF and uploading it to GitHub code scanning puts findings inline on the PR diff and in the repository's Security tab.

# .github/workflows/slither.yml
name: Slither

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read
  security-events: write

jobs:
  slither:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Run Slither
        uses: crytic/slither-action@v0.4.2
        id: slither
        with:
          sarif: results.sarif
          fail-on: none

      - name: Upload SARIF to code scanning
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: ${{ steps.slither.outputs.sarif }}

fail-on: none lets the upload step run even when Slither reports findings. Once you have triaged the existing backlog, switch to fail-on: high (or medium) so new findings at that severity block the merge. If your config file sets fail thresholds, use fail-on: config.

Triage before you enforce. Turning on a hard failure against an untriaged codebase guarantees people learn to ignore the check. Review the first run, fix real issues, and suppress accepted ones with // slither-disable-next-line <detector> and a comment explaining why.

Job 2: Foundry Tests and Invariants on Every Pull Request

If you use Foundry, your invariant tests are often the most valuable security check you own, because they encode rules specific to your protocol.

# .github/workflows/test.yml
name: Tests

on:
  pull_request:
  push:
    branches: [main]

jobs:
  forge-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1

      - name: Build
        run: forge build --sizes

      - name: Test (unit, fuzz, invariant)
        run: forge test -vvv
        env:
          FOUNDRY_PROFILE: ci

Define a ci profile in foundry.toml with modest fuzz.runs and invariant.runs so PR checks finish in minutes, and a heavier profile for scheduled runs.

Job 3: Scheduled Echidna Campaigns

Fuzzing finds more the longer it runs, which makes it a poor fit for every PR and a good fit for a nightly schedule. The crytic/echidna-action wraps the Echidna Docker image.

# .github/workflows/echidna.yml
name: Echidna

on:
  schedule:
    - cron: "0 3 * * *"
  workflow_dispatch:

jobs:
  echidna:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1

      - name: Build
        run: forge build --build-info

      - name: Run Echidna
        uses: crytic/echidna-action@v2
        with:
          files: .
          contract: VaultEchidnaTest
          config: echidna.yaml
          crytic-args: --ignore-compile

The --ignore-compile argument reuses the Foundry build from the previous step, because the action's container does not include your toolchain. Echidna exits non-zero when a property is falsified, so a failing nightly run shows up in the Actions tab with a minimized reproduction in the log.

Optional: Mythril on Critical Contracts

Mythril is slow enough that running it across a whole repository on every PR is rarely worth it. If you want it, run it on a short list of high-value contracts, with explicit bounds, on a schedule or a release branch:

      - name: Mythril (bounded)
        run: |
          docker run --rm -v "$PWD":/src mythril/myth \
            analyze /src/src/Vault.sol --solv 0.8.24 -t 2 --execution-timeout 300

Setting Thresholds That Survive Contact with Developers

  • Start in report-only mode. Upload findings, fail nothing, and triage the baseline.
  • Block on new high-severity findings first. Tighten to medium once the team trusts the signal.
  • Make suppressions reviewable. Inline suppressions belong in the diff where reviewers can see the justification.
  • Keep PR checks fast. Under about ten minutes total, or people will merge around them. Push long campaigns to scheduled jobs.
  • Pin action versions and update them deliberately, like any other dependency.

Where Firepan Fits

Firepan connects to repositories through the Firepan GitHub App. Once installed, the repository events and branches your installation is configured to send can trigger repeatable Firepan analysis, so findings can be compared as the code changes — see repository-connected analysis. Results include evidence tied to files and code paths, and the analysis includes best-effort Slither corroboration alongside HOUND AI review. It complements the open-source jobs above rather than replacing them; keep Foundry, Echidna, and any Mythril runs in your own workflows.

To try it without installing anything, request a free public-repository scan.

Frequently Asked Questions

Q: Will security checks slow down my pull requests?

A: Slither typically adds a minute or two including compilation. Keep fuzz and invariant run counts modest on PRs and move long fuzzing campaigns to a scheduled workflow so PR feedback stays fast.


Q: Should Slither fail the build?

A: Eventually, yes — but only after you have triaged the existing findings. Start with fail-on none and SARIF upload, fix or suppress the backlog, then enforce fail-on high for new findings.


Q: How do I see Slither findings on the pull request itself?

A: Write SARIF output from the Slither action and upload it with github/codeql-action/upload-sarif. GitHub code scanning then annotates the diff and lists alerts in the Security tab. The workflow needs the security-events: write permission.


Q: Should I run Echidna on every pull request?

A: Usually not. Fuzzing improves with run time, so short PR runs find little. Run Foundry invariant tests with modest settings on PRs and full Echidna campaigns nightly or before releases.


Q: Does this work for Vyper?

A: Slither supports Vyper projects. Foundry and Echidna are primarily used with Solidity, so check each tool's documentation for your Vyper setup.

Sources

Firepan

Scan Your Contracts Now

Run a free scan — results in minutes, no credit card required.

Run Free Scan →