Vittorio Pinti

Shift-Left DevSecOps with GitHub Actions

DevSecOps means catching vulnerabilities on every pull request, not at the end. Learn how to shift security left with GitHub Actions.

4 min read
Cover for Shift-Left DevSecOps with GitHub Actions

For a long time, security was something that happened at the very end: the code was finished, then a security team reviewed it, often finding problems that were expensive to fix so late. "Shift-left" is the idea of moving those checks earlier — ideally onto every pull request, so problems are caught while they're still cheap to fix. This is the core practice behind DevSecOps.

The good news is that if you already use GitHub Actions for tests, adding security checks is a small step. Let's look at what's worth automating and how.

Table of contents

  1. What shift-left DevSecOps really means
  2. Dependency and secret scanning
  3. Static analysis (SAST)
  4. A minimal secure pipeline
  5. Failing the build on purpose

What shift-left DevSecOps really means

Picture your development timeline running left (writing code) to right (running in production). Traditionally, security checks sat far to the right. Shift-left simply means moving them to the left — into the pull request, before code is merged.

The benefit is feedback speed. A developer who sees "this dependency has a known vulnerability" while opening a PR can fix it in minutes. The same issue found months later in production is a costly incident.

Dependency and secret scanning

Two of the highest-value, lowest-effort checks:

  • Dependency scanning looks at your libraries for known vulnerabilities (the kind you'd find with npm audit or composer audit). Most of your code's risk lives in code you didn't write.
  • Secret scanning checks that no passwords, API keys, or tokens were accidentally committed. A leaked key in git history is one of the most common real-world breaches.

Both can run automatically on every push.

Static analysis (SAST)

SAST stands for Static Application Security Testing. It reads your source code (without running it) and flags risky patterns — things like SQL built from string concatenation, or user input reaching a dangerous function. Think of it as a spell-checker for security bugs. It won't catch everything, but it catches a lot for very little effort. If you build APIs, pair SAST with the OWASP API Security Top 10 to cover the risks static analysis tends to miss.

A minimal secure pipeline

Here's a small GitHub Actions workflow that runs tests plus a dependency audit on every pull request:

name: CI

on:
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: composer install --no-interaction

      - name: Run tests
        run: ./vendor/bin/phpunit

      - name: Audit dependencies
        run: composer audit

That last step will make the job fail if a dependency has a known vulnerability. You can add secret scanning and a SAST action as extra steps in the same way.

Failing the build on purpose

The key mindset shift: a security finding should fail the build, just like a failing test. If checks only produce warnings that nobody reads, they add no protection. When a vulnerability blocks the merge, it gets fixed.

Start gentle if you have an older codebase — you might begin with warnings, fix the existing issues, then flip the switch to make them blocking. The goal is a pipeline where "it merged" also means "it passed our security checks".

Conclusion

You don't need a dedicated security team to write safer software. By moving a few automated checks into your pull requests, you turn DevSecOps from a rare, stressful event into a quiet, everyday habit. Start with dependency and secret scanning today, add static analysis next, and let your pipeline quietly protect every change you ship. For more on this topic, browse the rest of my DevOps articles.

Resources