DevSecOpsCI/CDSecurity

Shifting Left: Integrating Security into CI/CD

Sven Nellemann
Jan 20248 min read

Introduction

The traditional approach to software security—bolting it on at the end of the development cycle—is fundamentally broken. When security vulnerabilities are discovered in production, the cost to fix them is exponentially higher than if they'd been caught during development. This is where "shifting left" comes in: moving security practices earlier in the software development lifecycle (SDLC).

In this article, I'll walk you through implementing a comprehensive security scanning pipeline that catches vulnerabilities before they reach production.

What Does "Shifting Left" Actually Mean?

The term comes from visualising the software development lifecycle as a timeline flowing left to right:

Planning → Development → Testing → Deployment → Production
   ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
         "Shifting Left" = Moving security HERE

Instead of waiting until the testing or deployment phase to think about security, we integrate security checks throughout—starting from the moment code is written.

The Security Scanning Stack

A robust CI/CD security pipeline typically includes several types of scanning:

1. Static Application Security Testing (SAST)

SAST tools analyse your source code without executing it, looking for patterns that indicate security vulnerabilities.

Popular Tools:

  • **Semgrep** - Fast, open-source, supports many languages
  • **SonarQube** - Comprehensive code quality and security
  • **Snyk Code** - Developer-friendly with IDE integration
  • **CodeQL** - GitHub's powerful semantic code analysis
  • Example: Semgrep in GitHub Actions

    name: SAST Scan
    on: [push, pull_request]
    
    jobs:
      semgrep:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          
          - name: Run Semgrep
            uses: returntocorp/semgrep-action@v1
            with:
              config: >-
                p/security-audit
                p/secrets
                p/owasp-top-ten

    2. Software Composition Analysis (SCA)

    Most modern applications are built on open-source dependencies. SCA tools scan these dependencies for known vulnerabilities.

    Popular Tools:

  • **Snyk** - Industry leader with excellent developer experience
  • **Dependabot** - Native to GitHub, automatic PRs for updates
  • **OWASP Dependency-Check** - Open-source, language-agnostic
  • **Trivy** - Fast, comprehensive, and free
  • Example: Dependency Scanning with Snyk

    name: Dependency Scan
    on: [push, pull_request]
    
    jobs:
      snyk:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          
          - name: Run Snyk to check for vulnerabilities
            uses: snyk/actions/node@master
            env:
              SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
            with:
              args: --severity-threshold=high

    3. Secret Detection

    Accidentally committed secrets are one of the most common security incidents. Catching them before they're pushed is critical.

    Popular Tools:

  • **GitLeaks** - Fast, configurable, great for CI/CD
  • **TruffleHog** - Entropy-based detection
  • **detect-secrets** - Yelp's auditable baseline approach
  • **GitHub Secret Scanning** - Native GitHub feature
  • Example: GitLeaks Pre-commit Hook

    # .pre-commit-config.yaml
    repos:
      - repo: https://github.com/gitleaks/gitleaks
        rev: v8.18.1
        hooks:
          - id: gitleaks

    In CI/CD:

    name: Secret Scan
    on: [push, pull_request]
    
    jobs:
      gitleaks:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
            with:
              fetch-depth: 0
              
          - name: Run Gitleaks
            uses: gitleaks/gitleaks-action@v2
            env:
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    4. Container Image Scanning

    If you're deploying containers, scanning images for vulnerabilities in the base OS and installed packages is essential.

    Popular Tools:

  • **Trivy** - Comprehensive, fast, and free
  • **Grype** - Anchore's open-source scanner
  • **Snyk Container** - Great integration with development workflow
  • **Clair** - CoreOS's original container scanner
  • Example: Trivy Container Scanning

    name: Container Scan
    on:
      push:
        branches: [main]
    
    jobs:
      trivy:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          
          - name: Build image
            run: docker build -t myapp:${{ github.sha }} .
            
          - name: Run Trivy vulnerability scanner
            uses: aquasecurity/trivy-action@master
            with:
              image-ref: myapp:${{ github.sha }}
              format: 'sarif'
              output: 'trivy-results.sarif'
              severity: 'CRITICAL,HIGH'
              
          - name: Upload Trivy scan results
            uses: github/codeql-action/upload-sarif@v2
            with:
              sarif_file: 'trivy-results.sarif'

    5. Infrastructure as Code (IaC) Scanning

    Your Terraform, CloudFormation, or Kubernetes manifests can contain security misconfigurations.

    Popular Tools:

  • **Checkov** - Comprehensive IaC scanner
  • **tfsec** - Terraform-focused security scanner
  • **KICS** - Checkmarx's open-source IaC scanner
  • **Terrascan** - Policy as code for IaC
  • Example: Checkov for Terraform

    name: IaC Scan
    on: [push, pull_request]
    
    jobs:
      checkov:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          
          - name: Run Checkov
            uses: bridgecrewio/checkov-action@master
            with:
              directory: terraform/
              framework: terraform
              soft_fail: false

    Building a Complete Pipeline

    Here's how to combine all these tools into a comprehensive security pipeline:

    name: Security Pipeline
    on:
      push:
        branches: [main, develop]
      pull_request:
        branches: [main]
    
    jobs:
      secrets:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
            with:
              fetch-depth: 0
          - uses: gitleaks/gitleaks-action@v2
            env:
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    
      sast:
        runs-on: ubuntu-latest
        needs: secrets
        steps:
          - uses: actions/checkout@v4
          - uses: returntocorp/semgrep-action@v1
            with:
              config: p/security-audit
    
      sca:
        runs-on: ubuntu-latest
        needs: secrets
        steps:
          - uses: actions/checkout@v4
          - uses: snyk/actions/node@master
            env:
              SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
    
      container:
        runs-on: ubuntu-latest
        needs: [sast, sca]
        steps:
          - uses: actions/checkout@v4
          - name: Build image
            run: docker build -t app:${{ github.sha }} .
          - uses: aquasecurity/trivy-action@master
            with:
              image-ref: app:${{ github.sha }}
              severity: CRITICAL,HIGH
              exit-code: 1

    Handling Findings: The Developer Experience

    Security scanning is only useful if developers act on the findings. Here's how to make that happen:

    1. Fail Fast, But Not Too Fast

    Start with warning-only mode, then gradually increase strictness.

    2. Provide Context

    Use SARIF format to integrate findings directly into the GitHub Security tab.

    3. Enable Self-Service Fixes

    Many tools provide auto-fix capabilities.

    4. Create a Security Champion Programme

    Designate developers in each team who triage security findings and help teammates.

    Metrics That Matter

    Track these metrics to measure your shift-left success:

    | Metric | Target | Why It Matters |

    |--------|--------|----------------|

    | Mean Time to Remediate (MTTR) | < 7 days for high severity | Shows responsiveness |

    | Vulnerability Escape Rate | < 5% | Measures pipeline effectiveness |

    | False Positive Rate | < 10% | Indicates tool configuration quality |

    | Developer Satisfaction | > 4/5 | Security shouldn't slow you down |

    Common Pitfalls to Avoid

    1. Alert Fatigue

    Too many findings overwhelm developers. Start with critical/high severity only.

    2. Blocking Everything

    If your pipeline blocks every PR, developers will work around it.

    3. No Baseline

    Create a baseline of known issues and only block on new findings.

    4. Ignoring Developer Feedback

    Security tools are only useful if developers use them.

    Conclusion

    Shifting security left isn't just about adding tools to your pipeline—it's about creating a culture where security is everyone's responsibility. Start small, iterate based on feedback, and remember that the goal is to empower developers to write secure code, not to create barriers.

    Next Steps

    1. Assess your current state: What security scanning do you have today?

    2. Pick one tool: Start with secret detection—it's fast and high-value

    3. Iterate: Add more scanning types over time

    4. Measure: Track your metrics and adjust based on data

    Security is a journey, not a destination. Start shifting left today.

    ---

    *Have questions about implementing security in your CI/CD pipeline? Feel free to [reach out](#contact)—I'd love to help.*