How do I add security scanning to GitHub Actions?

Direct answer

Add a workflow that runs on pull requests, scan only the diff rather than the whole repository, and fail the build on new high-severity findings only. The mistake that kills these setups is failing builds on everything a scanner finds on day one, which on any existing codebase means every pull request breaks and the team disables it within a fortnight.

Muhammad HasanUpdated

The technical part of this is short. The part that determines whether it's still running in three months is a configuration decision most guides skip.

The workflow

Scanning belongs on pull requests, so findings arrive while the author still has the change in their head.

name: Security
on:
  pull_request:
    branches: [main]

jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0     # full history, so diff scanning works
      - name: Run scan
        run: # your scanner here

fetch-depth: 0 matters more than it looks. Without full history the runner can't diff against the base branch, so you can't scan only what changed.

Scan the diff, not the repository

A full scan of a large codebase can take several minutes and returns the same findings every time. A diff scan looks at what the pull request changed and typically completes in seconds.

Diff on pull requests, full scan on a nightly schedule against main. That keeps the developer-facing check fast and still gives you complete coverage.

on:
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * *'

What to fail the build on

This is the decision that determines whether the check survives.

Turn a scanner on against an existing codebase and it will find things, hundreds on a mature repository. If the workflow fails on all of them, every pull request goes red from the first day, the failures have nothing to do with what anyone changed, and within a fortnight someone adds continue-on-error: true and the check becomes decorative.

Instead:

  • Fail on new high and critical findings introduced by the pull request

  • Warn as a comment on new medium and low findings

  • Ignore at the gate everything that already existed, and track it as a backlog

Existing findings still matter. They just aren't the current author's problem, and blocking their work on them teaches the team to bypass the check.

Budget the time

Anything over about two minutes on a pull request and people start pushing without waiting for it.

Practical levers: diff scanning rather than full, caching, running the scan in parallel with tests rather than after them, and scoping out generated code, vendored dependencies and fixtures.

The bit that isn't the scanner's problem

A working pipeline gives you findings on every pull request. Whether they get fixed is a separate question, and it's where most of these setups quietly fail. The check runs, the comments appear, the backlog grows, and nothing changes.

That gap is what Kolega works on: findings arrive with a reviewable fix attached rather than as a description of a problem someone else has to solve.

Go deeper

Kolega for DevOps

Related answers

See what your own repository returns

Connect a repo and run a scan. No credit card, no pipeline changes.

Get started for free