Back to Blog

Securing Your CI/CD Pipeline: A 15-Point DevSecOps Checklist for 2026

A compromised CI/CD pipeline can give attackers the keys to your entire production environment. Learn how to implement DevSecOps practices with this comprehensive security checklist covering dependency scanning, SAST, DAST, secrets management, and more.

Published

14 min read

Reading time

Securing Your CI/CD Pipeline: A 15-Point DevSecOps Checklist for 2026

The Colonial Pipeline ransomware attack. The SolarWinds supply chain breach. The Codecov bash uploader compromise. What do these headline-grabbing security incidents have in common? They all leveraged weaknesses in the software supply chain and CI/CD infrastructure.

Your CI/CD pipeline isn't just a convenience for developers—it's a critical piece of infrastructure that, if compromised, can give attackers direct access to your production environment, your secrets, and your customers' data. Yet many organizations treat pipeline security as an afterthought, focusing their security efforts on production systems while leaving their build and deployment infrastructure vulnerable.

DevSecOps is the practice of integrating security into every phase of the development lifecycle, with particular emphasis on automating security checks in the CI/CD pipeline. This article provides a comprehensive checklist for securing your CI/CD pipeline, covering everything from dependency scanning to infrastructure hardening.

Why CI/CD Security Matters

Your CI/CD pipeline has access to:

  • Production credentials and secrets (database passwords, API keys, cloud credentials)
  • Source code repositories (including proprietary algorithms and business logic)
  • Container registries and artifact repositories (the software you ship to customers)
  • Production deployment infrastructure (the ability to push code to live systems)

A compromised pipeline can lead to:

  • Supply chain attacks (injecting malicious code into your artifacts)
  • Data breaches (stealing production credentials)
  • Credential theft (harvesting developer tokens and keys)
  • Ransomware deployment (encrypting production systems)
  • Intellectual property theft (exfiltrating source code)

According to the 2023 State of the Software Supply Chain report, 96% of vulnerabilities in applications are from open-source dependencies, and attackers increasingly target CI/CD systems as a force multiplier.

The DevSecOps Security Layers

Securing a CI/CD pipeline requires a defense-in-depth approach across multiple layers:

graph TD
    A[Source Code] -->|Code Commit| B[Version Control Security]
    B -->|Trigger Build| C[Build Environment Security]
    C -->|Run Analysis| D[Static Analysis SAST]
    C -->|Scan Dependencies| E[Dependency Scanning]
    C -->|Check Secrets| F[Secrets Detection]
    C -->|Build Artifact| G[Artifact Signing]
    G -->|Deploy to Test| H[Dynamic Analysis DAST]
    H -->|Security Tests Pass| I[Container Scanning]
    I -->|Infrastructure Check| J[IaC Security]
    J -->|Deploy to Production| K[Runtime Security]

    style D fill:#f9d5e5
    style E fill:#f9d5e5
    style F fill:#f9d5e5
    style H fill:#eeeeee
    style I fill:#eeeeee
    style J fill:#c5e1a5
    style K fill:#c5e1a5

The Complete DevSecOps Checklist

1. Source Control and Repository Security

Access Control

  • Enable multi-factor authentication (MFA) for all developers
  • Implement least-privilege access (reviewers vs. committers vs. admins)
  • Require signed commits (GPG or SSH signatures)
  • Enable branch protection rules (require reviews, status checks)
  • Restrict who can approve and merge to protected branches

Repository Configuration

  • Disable force pushes to main branches
  • Require linear history (no merge commits on protected branches)
  • Enable secret scanning (GitHub Advanced Security, GitLab Secret Detection)
  • Configure CODEOWNERS for security-critical files
  • Audit repository access quarterly

Example: GitHub Branch Protection Rules

# .github/branch-protection.yml
branch-protection:
  main:
    required_status_checks:
      strict: true
      contexts:
        - 'security/sast'
        - 'security/dependency-scan'
        - 'security/secret-scan'
        - 'test/unit'
        - 'test/integration'
    required_pull_request_reviews:
      required_approving_review_count: 2
      dismiss_stale_reviews: true
      require_code_owner_reviews: true
    enforce_admins: true
    required_signatures: true

2. Dependency Management and Scanning

Dependency Scanning

  • Scan dependencies for known vulnerabilities (CVEs)
  • Fail builds on high/critical vulnerabilities
  • Automatically create PRs for dependency updates
  • Monitor for malicious packages (typosquatting)
  • Verify package checksums and signatures

Tools Comparison

Tool Strengths Language Support CI/CD Integration Cost
Snyk Good UI, actionable advice 10+ languages Excellent Free tier + paid
Dependabot Native GitHub integration Multiple GitHub Actions Free
npm audit Built into npm JavaScript/Node Easy Free
OWASP Dependency-Check Open source, comprehensive Java, .NET, more Good Free
Trivy Container + code scanning Multiple Excellent Free

Example: GitHub Actions Dependency Scanning

# .github/workflows/dependency-scan.yml
name: Dependency Scanning

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]
  schedule:
    - cron: '0 6 * * 1' # Weekly Monday 6am

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

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Run npm audit
        run: |
          npm audit --audit-level=high --json > npm-audit.json
          npm audit --audit-level=high
        continue-on-error: true

      - name: Snyk Security Scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high --fail-on=all

      - name: Upload Snyk results to GitHub Code Scanning
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: snyk.sarif

3. Static Application Security Testing (SAST)

SAST Implementation

  • Run SAST on every pull request
  • Scan for OWASP Top 10 vulnerabilities
  • Check for hardcoded secrets and credentials
  • Enforce secure coding standards
  • Integrate findings into code review process

Example: Semgrep SAST Pipeline

# .github/workflows/sast.yml
name: Static Application Security Testing

on: [pull_request, push]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    container:
      image: returntocorp/semgrep
    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        run: |
          semgrep scan --config=auto \
            --config=p/owasp-top-ten \
            --config=p/security-audit \
            --error \
            --sarif --output=semgrep.sarif \
            --json --output=semgrep.json
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

      - name: Upload SARIF to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: semgrep.sarif

      - name: Check for critical findings
        run: |
          CRITICAL=$(jq '[.results[] | select(.extra.severity == "ERROR")] | length' semgrep.json)
          if [ $CRITICAL -gt 0 ]; then
            echo "❌ Found $CRITICAL critical security issues"
            exit 1
          fi

4. Secrets Management

Secrets Security

  • Never commit secrets to version control
  • Use a secrets management service (Vault, AWS Secrets Manager, Azure Key Vault)
  • Rotate secrets regularly (at least quarterly)
  • Use short-lived, scoped credentials
  • Scan commits for accidentally committed secrets

Example: Secrets Detection with TruffleHog

# .github/workflows/secrets-scan.yml
name: Secrets Scanning

on: [push, pull_request]

jobs:
  trufflehog:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Full history for better detection

      - name: TruffleHog Secrets Scan
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./
          base: ${{ github.event.repository.default_branch }}
          head: HEAD
          extra_args: --only-verified --fail

Example: Vault Integration in CI/CD

# .github/workflows/deploy.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Import Secrets from Vault
        uses: hashicorp/vault-action@v2
        with:
          url: https://vault.company.com
          method: jwt
          role: ci-cd-role
          secrets: |
            secret/data/production/db password | DB_PASSWORD ;
            secret/data/production/api-keys stripe | STRIPE_KEY

      - name: Deploy Application
        env:
          DATABASE_URL: 'postgres://user:${{ env.DB_PASSWORD }}@db.prod.com/app'
          STRIPE_API_KEY: ${{ env.STRIPE_KEY }}
        run: ./scripts/deploy.sh

5. Container and Image Security

Container Security

  • Scan container images for vulnerabilities
  • Use minimal base images (Alpine, distroless)
  • Don't run containers as root
  • Sign and verify container images
  • Scan images in registries regularly

Example: Trivy Container Scanning

# .github/workflows/container-scan.yml
name: Container Security Scan

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

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

      - name: Build Docker 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'
          exit-code: '1' # Fail on vulnerabilities

      - name: Upload Trivy results to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

      - name: Sign Image with Cosign
        if: github.ref == 'refs/heads/main'
        run: |
          cosign sign --key cosign.key myapp:${{ github.sha }}
        env:
          COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}

6. Dynamic Application Security Testing (DAST)

DAST Implementation

  • Run DAST against deployed test environments
  • Scan for runtime vulnerabilities (injection, XSS, etc.)
  • Test authentication and authorization
  • Perform API security testing
  • Schedule regular production scans

Example: OWASP ZAP in CI/CD

# .github/workflows/dast.yml
name: Dynamic Application Security Testing

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 2 * * *' # Daily at 2am

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

      - name: Deploy to Test Environment
        run: |
          ./scripts/deploy-test.sh
          echo "TEST_URL=https://test.myapp.com" >> $GITHUB_ENV

      - name: Wait for deployment
        run: |
          timeout 300 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $TEST_URL)" != "200" ]]; do sleep 5; done' || false

      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.10.0
        with:
          target: ${{ env.TEST_URL }}
          rules_file_name: '.zap/rules.tsv'
          cmd_options: '-a -j'

      - name: ZAP Full Scan
        uses: zaproxy/action-full-scan@v0.8.0
        with:
          target: ${{ env.TEST_URL }}
          rules_file_name: '.zap/rules.tsv'
          cmd_options: '-a -j'
          allow_issue_writing: false

      - name: Upload ZAP Report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: zap-report
          path: |
            report_html.html
            report_json.json

7. Infrastructure as Code (IaC) Security

IaC Security

  • Scan IaC for misconfigurations
  • Enforce security policies (no public S3 buckets, encrypted databases)
  • Version control all infrastructure code
  • Require reviews for infrastructure changes
  • Validate against compliance frameworks (CIS, SOC2)

Example: Checkov IaC Scanning

# .github/workflows/iac-scan.yml
name: Infrastructure Security 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
          output_format: sarif
          output_file_path: checkov.sarif
          soft_fail: false
          download_external_modules: true

      - name: Upload Checkov results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: checkov.sarif

8. CI/CD Infrastructure Hardening

Build Environment Security

  • Use ephemeral build agents (destroy after each build)
  • Isolate build jobs (containers, VMs)
  • Use least-privilege service accounts
  • Audit runner access and permissions
  • Monitor for suspicious build activity

Example: Self-Hosted Runner Security (GitHub Actions)

#!/bin/bash
# Self-hosted runner hardening script

# 1. Create dedicated user (non-root)
sudo useradd -m -s /bin/bash github-runner
sudo usermod -aG docker github-runner

# 2. Install runner as service
cd /home/github-runner
curl -o actions-runner-linux.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf ./actions-runner-linux.tar.gz
sudo chown -R github-runner:github-runner /home/github-runner

# 3. Configure with ephemeral flag
sudo -u github-runner ./config.sh \
  --url https://github.com/myorg/myrepo \
  --token $RUNNER_TOKEN \
  --name prod-runner-1 \
  --labels production,secure \
  --ephemeral \
  --disableupdate

# 4. Install and start service
sudo ./svc.sh install github-runner
sudo ./svc.sh start

# 5. Configure firewall (allow only necessary outbound)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 10.0.0.0/8 to any port 22
sudo ufw enable

# 6. Enable audit logging
sudo auditctl -w /home/github-runner -p wa -k github-runner

9. Monitoring and Alerting

Security Monitoring

  • Monitor pipeline execution logs
  • Alert on failed security checks
  • Track security metrics (vulnerabilities found/fixed)
  • Log all deployment events
  • Monitor for unauthorized access attempts

Example: Security Metrics Dashboard

// monitoring/security-metrics.ts
interface SecurityMetrics {
  vulnerabilitiesFound: {
    critical: number;
    high: number;
    medium: number;
    low: number;
  };
  vulnerabilitiesFixed: {
    critical: number;
    high: number;
    medium: number;
    low: number;
  };
  meanTimeToRemediate: {
    critical: number; // hours
    high: number;
    medium: number;
  };
  securityTestsPassed: number;
  securityTestsFailed: number;
  secretsDetected: number;
  deploymentBlockedBySecurity: number;
}

async function collectSecurityMetrics(startDate: Date, endDate: Date): Promise<SecurityMetrics> {
  const snykResults = await getSnykFindings(startDate, endDate);
  const sastResults = await getSASTFindings(startDate, endDate);
  const dastResults = await getDASTFindings(startDate, endDate);

  return {
    vulnerabilitiesFound: aggregateVulnerabilities([snykResults, sastResults, dastResults]),
    vulnerabilitiesFixed: calculateFixRate(snykResults, sastResults),
    meanTimeToRemediate: calculateMTTR(snykResults),
    securityTestsPassed: countPassedTests(),
    securityTestsFailed: countFailedTests(),
    secretsDetected: getSecretsDetectionCount(),
    deploymentBlockedBySecurity: getBlockedDeployments(),
  };
}

10. Compliance and Policies

Policy Enforcement

  • Define security policies (dependency age, vulnerability SLA)
  • Automate policy enforcement with policy-as-code
  • Maintain audit trail of all pipeline changes
  • Document security controls for compliance
  • Regular security audits of CI/CD infrastructure

Example: Open Policy Agent (OPA) Policy

# policies/deployment.rego
package deployment

# Deny deployment if critical vulnerabilities exist
deny[msg] {
  input.vulnerabilities.critical > 0
  msg = sprintf("Deployment blocked: %d critical vulnerabilities found", [input.vulnerabilities.critical])
}

# Deny if dependencies are too old
deny[msg] {
  some dep in input.dependencies
  days_old := time.now_ns() - dep.last_updated_ns
  days_old > (90 * 24 * 60 * 60 * 1000000000)
  msg = sprintf("Dependency %s is %d days old (max 90 days)", [dep.name, days_old / (24*60*60*1000000000)])
}

# Deny if image not signed
deny[msg] {
  not input.image.signed
  msg = "Container image must be signed with Cosign"
}

# Require security tests to pass
deny[msg] {
  input.security_tests.sast.passed == false
  msg = "SAST security tests failed"
}

deny[msg] {
  input.security_tests.dast.passed == false
  msg = "DAST security tests failed"
}

Security Testing Maturity Model

Organizations typically progress through stages of CI/CD security maturity:

Stage Characteristics Tools Deployment Frequency
Level 1: Ad-hoc Manual security reviews, no automation Manual code review Weekly/monthly
Level 2: Basic Dependency scanning, basic SAST npm audit, Dependabot Daily
Level 3: Automated SAST, DAST, dependency scanning, secrets detection Snyk, Semgrep, TruffleHog Multiple/day
Level 4: Integrated All Level 3 + container scanning, IaC scanning, signed artifacts Trivy, Checkov, Cosign Continuous
Level 5: Advanced Level 4 + runtime protection, policy-as-code, security chaos engineering OPA, Falco, Chaos Mesh Continuous

Common Pitfalls and How to Avoid Them

1. Security Theater

  • Problem: Running security tools but ignoring findings
  • Solution: Fail builds on critical/high vulnerabilities, track remediation SLAs

2. Alert Fatigue

  • Problem: Too many low-severity findings overwhelm teams
  • Solution: Start with critical/high only, tune false positives, use risk-based prioritization

3. Slowing Down Development

  • Problem: Security checks add significant time to pipelines
  • Solution: Run fast checks on PR, comprehensive scans nightly; parallelize where possible

4. Secret Sprawl

  • Problem: Secrets scattered across environment variables, config files, CI/CD tools
  • Solution: Centralize in a secrets manager, use short-lived credentials, implement secret rotation

5. Orphaned Security Findings

  • Problem: Security tools create tickets that no one acts on
  • Solution: Assign ownership, integrate with existing ticketing, enforce SLAs

Implementing Your DevSecOps Transformation

Phase 1: Foundation (Weeks 1-4)

  1. Enable branch protection and MFA
  2. Add dependency scanning (npm audit or Snyk)
  3. Implement basic secrets scanning
  4. Document current state and gaps

Phase 2: Automation (Weeks 5-12)

  1. Add SAST to PR checks
  2. Implement container scanning
  3. Set up DAST for staging deployments
  4. Create security metrics dashboard

Phase 3: Maturity (Months 4-6)

  1. Add IaC security scanning
  2. Implement policy-as-code
  3. Sign and verify artifacts
  4. Automate security remediation where possible

Phase 4: Excellence (Ongoing)

  1. Continuous monitoring and improvement
  2. Regular security training for developers
  3. Chaos engineering for security
  4. Contribution to security tooling and policies

Conclusion

Securing your CI/CD pipeline isn't a one-time project—it's an ongoing practice that evolves with your organization and the threat landscape. The checklist in this article provides a roadmap, but remember:

  • Start small: Implement high-impact, low-effort controls first
  • Automate extensively: Manual security reviews don't scale
  • Measure progress: Track security metrics and trends
  • Foster culture: Make security everyone's responsibility, not just the security team's
  • Iterate continuously: Security is never "done"

The most successful DevSecOps implementations treat security as an enabler of velocity, not an inhibitor. When done right, automated security checks catch issues earlier (when they're cheaper to fix), reduce risk, and actually speed up delivery by preventing production security incidents.

Ready to add comprehensive security testing to your CI/CD pipeline? Sign up for ScanlyApp and integrate automated QA and security checks into your deployment workflow today.

Related articles: Also see adding DAST scans as a security gate in your CI/CD pipeline, the continuous testing foundation your security gates sit on top of, and de-risking deployments once your security pipeline is in place.

Related Posts