Skip to content
Published on

The Complete DevSecOps Guide 2025: Shift-Left Security, SAST/DAST/SCA, Container Security

Authors

TL;DR

  • Shift-Left Security: Applying security early in development reduces fix costs by 30x
  • SAST: Detect code-level vulnerabilities with SonarQube, Semgrep, CodeQL
  • DAST: Discover runtime vulnerabilities with OWASP ZAP, Burp Suite
  • SCA: Auto-patch dependency vulnerabilities with Snyk, Dependabot
  • Container Security: Scan images with Trivy, Grype; use minimal base images
  • Supply Chain: Generate SBOMs, achieve SLSA levels, sign with Sigstore
  • Secret Detection: Detect committed secrets with GitLeaks, TruffleHog
  • GitHub Actions: Automate all stages with an integrated security pipeline

Table of Contents

  1. What Is DevSecOps
  2. Shift-Left Security Strategy
  3. SAST - Static Analysis
  4. DAST - Dynamic Analysis
  5. SCA - Software Composition Analysis
  6. Container Security
  7. Secret Detection
  8. Supply Chain Security - SBOM and SLSA
  9. Policy-as-Code
  10. GitHub Actions Integrated Security Pipeline
  11. Practice Quiz
  12. References

1. What Is DevSecOps

1.1 Limitations of Traditional Security

In traditional software development, security was added at the very end. Security teams would review right before deployment. The problems with this approach are clear:

ProblemImpact
Late discoveryFix costs increase 30x
BottleneckSecurity team reviews every project
Developer apathy"Security is the security team's job" mindset
Release delaysEntire schedule delayed when vulnerabilities found
Context gapSecurity team doesn't understand code intent

1.2 Core Principles of DevSecOps

DevSecOps is a portmanteau of Development + Security + Operations. It is both a culture and a practice that integrates security throughout the entire software development lifecycle (SDLC).

Five Core Principles:

  1. Everyone is responsible for security: Security isn't just the security team's job
  2. Automation first: Use automated tools instead of manual reviews
  3. Fast feedback: Security feedback at the point developers write code
  4. Continuous improvement: Measure security metrics and continuously improve
  5. Shared responsibility: Knowledge sharing between dev, ops, and security teams

1.3 DevSecOps Tool Ecosystem Overview

┌─────────────────────────────────────────────────────────────┐
DevSecOps Pipeline├──────────┬──────────┬──────────┬──────────┬────────────────┤
PlanCodeBuildTestDeploy/Run├──────────┼──────────┼──────────┼──────────┼────────────────┤
ThreatSASTSCADASTRuntimeModelingSemgrepSnykZAPProtection│          │ CodeQLTrivyBurpFalcoSecuritySonarQube│ OSVNucleiContainerRequire  │          │          │          │ Security│ ments    │ SecretSBOMFuzzingNetwork│          │ DetectSigning  │          │ Policy├──────────┼──────────┼──────────┼──────────┼────────────────┤
Policy-as-Code (OPA / Kyverno / Conftest)└─────────────────────────────────────────────────────────────┘

2. Shift-Left Security Strategy

2.1 What Is Shift-Left?

Shift-Left is the strategy of moving security activities to the left (earlier stages) of the development process.

Traditional approach:
Plan -> Code -> Build -> Test -> Deploy -> [Security Review] -> Release
                                            ^ Found here = high cost

Shift-Left approach:
Plan -> [Security] -> Code -> [Security] -> Build -> [Security] -> Test -> Deploy
  ^ Start here = low cost

2.2 Shift-Left ROI Analysis

According to research from the IBM Systems Sciences Institute, defect fix costs increase exponentially based on when they are discovered.

Discovery StageRelative CostExample
Requirements1xMissing security requirement found
Design5xArchitecture vulnerability found
Implementation10xCode-level vulnerability found
Testing20xFound during QA
Production30xFound in production
Post-incident100x+Data breach incident

2.3 Implementing Shift-Left

Step 1: Security Champions Program

Designate a security champion in each development team.

# security-champions.yaml
teams:
  - name: "Backend Team"
    champion: "alice@company.com"
    responsibilities:
      - "PR security reviews"
      - "SAST result triage"
      - "Monthly security training"
    training:
      - "OWASP Top 10"
      - "Secure Coding Practices"
      - "Threat Modeling Basics"
  - name: "Frontend Team"
    champion: "bob@company.com"
    responsibilities:
      - "XSS/CSRF prevention review"
      - "Dependency vulnerability management"
      - "CSP header management"

Step 2: Threat Modeling

Perform threat modeling using the STRIDE model during the design phase.

STRIDE Threat Model:
┌─────────────────────────────────────────┐
S - SpoofingT - TamperingR - RepudiationI - Information DisclosureD - Denial of ServiceE - Elevation of Privilege└─────────────────────────────────────────┘

Step 3: IDE Security Plugins

Provide security feedback directly in the developer's IDE.

// .vscode/settings.json
{
  "semgrep.scan.autoScan": true,
  "semgrep.scan.configuration": [
    "p/owasp-top-ten",
    "p/typescript",
    "p/react"
  ],
  "sonarlint.connectedMode.project": {
    "connectionId": "my-sonarqube",
    "projectKey": "my-project"
  }
}

3. SAST - Static Analysis

3.1 What Is SAST?

SAST (Static Application Security Testing) analyzes source code, bytecode, or binaries without execution to find security vulnerabilities.

Key vulnerabilities detected by SAST:

  • SQL Injection
  • Cross-Site Scripting (XSS)
  • Path Traversal
  • Hardcoded secrets
  • Insecure cryptography
  • Buffer overflows

3.2 SonarQube Setup

# docker-compose.yml - SonarQube local setup
version: "3.8"
services:
  sonarqube:
    image: sonarqube:community
    ports:
      - "9000:9000"
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonar_data:/opt/sonarqube/data
      - sonar_logs:/opt/sonarqube/logs
      - sonar_extensions:/opt/sonarqube/extensions
    depends_on:
      - db
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: sonarqube
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  sonar_data:
  sonar_logs:
  sonar_extensions:
  postgres_data:
# sonar-project.properties
sonar.projectKey=my-app
sonar.projectName=My Application
sonar.projectVersion=1.0
sonar.sources=src
sonar.tests=tests
sonar.language=ts
sonar.sourceEncoding=UTF-8
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.qualitygate.wait=true

3.3 Semgrep in Practice

Semgrep is a lightweight and fast SAST tool with easy custom rule authoring.

# .semgrep.yml - Custom security rules
rules:
  - id: no-eval-usage
    patterns:
      - pattern: eval(...)
    message: "eval() usage poses code injection risk"
    severity: ERROR
    languages: [javascript, typescript]
    metadata:
      owasp: "A03:2021 Injection"
      cwe: "CWE-95"

  - id: no-innerHTML
    patterns:
      - pattern: $EL.innerHTML = $VAL
    message: "Direct innerHTML assignment creates XSS risk. Use textContent or DOMPurify"
    severity: WARNING
    languages: [javascript, typescript]

  - id: sql-injection-risk
    patterns:
      - pattern: |
          $DB.query(`... ${$VAR} ...`)
      - pattern: |
          $DB.query("..." + $VAR + "...")
    message: "SQL injection risk! Use parameterized queries"
    severity: ERROR
    languages: [javascript, typescript]
    fix: |
      $DB.query("... $1 ...", [$VAR])

  - id: no-hardcoded-secrets
    patterns:
      - pattern: |
          const $KEY = "AKIA..."
      - pattern: |
          const $KEY = "sk-..."
    message: "Hardcoded secret detected. Use environment variables"
    severity: ERROR
    languages: [javascript, typescript]
# Run Semgrep
semgrep --config auto --config .semgrep.yml src/

# Run specific rulesets only
semgrep --config p/owasp-top-ten src/
semgrep --config p/typescript src/
semgrep --config p/react src/

# JSON output for CI
semgrep --config auto --json --output results.json src/

3.4 Advanced CodeQL Queries

GitHub CodeQL is a powerful tool that converts code into a database for querying.

// codeql-queries/sql-injection.ql
/**
 * @name SQL injection in query string
 * @description User input included directly in SQL query
 * @kind path-problem
 * @problem.severity error
 * @security-severity 9.8
 * @precision high
 * @id js/sql-injection
 * @tags security
 *       external/cwe/cwe-089
 */

import javascript
import DataFlow::PathGraph

class SqlInjectionConfig extends TaintTracking::Configuration {
  SqlInjectionConfig() { this = "SqlInjectionConfig" }

  override predicate isSource(DataFlow::Node source) {
    exists(Express::RequestInputAccess input |
      source = input
    )
  }

  override predicate isSink(DataFlow::Node sink) {
    exists(DatabaseAccess query |
      sink = query.getAQueryArgument()
    )
  }
}

from SqlInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
  "This query includes user input from $@.",
  source.getNode(), "user input"

3.5 SAST Tool Comparison

FeatureSonarQubeSemgrepCodeQL
PriceCommunity freeOSS freeOSS free
Languages30+30+10+
Custom rulesJava DSLYAMLQL Language
CI integrationGoodVery goodGitHub optimized
SpeedMediumVery fastSlow (DB build)
AccuracyHighHighVery high
Learning curveMediumLowHigh

4. DAST - Dynamic Analysis

4.1 What Is DAST?

DAST (Dynamic Application Security Testing) detects vulnerabilities by simulating real attacks against a running application. Since it doesn't require source code, it's also called "black-box testing."

4.2 OWASP ZAP Automation

# zap-automation.yaml
env:
  contexts:
    - name: "My Web App"
      urls:
        - "https://staging.example.com"
      includePaths:
        - "https://staging.example.com/.*"
      excludePaths:
        - "https://staging.example.com/logout"
      authentication:
        method: "form"
        parameters:
          loginUrl: "https://staging.example.com/login"
          loginRequestData: "username=test&password=test123"

jobs:
  - type: spider
    parameters:
      context: "My Web App"
      maxDuration: 5
      maxDepth: 10

  - type: spiderAjax
    parameters:
      context: "My Web App"
      maxDuration: 5

  - type: passiveScan-wait
    parameters:
      maxDuration: 10

  - type: activeScan
    parameters:
      context: "My Web App"
      maxRuleDurationInMins: 5
      policy: "API-Scan"

  - type: report
    parameters:
      template: "modern"
      reportDir: "/zap/reports"
      reportFile: "zap-report"
      reportTitle: "ZAP Security Report"
    risks:
      - high
      - medium
      - low
# Run ZAP automated scan with Docker
docker run --rm -v $(pwd):/zap/wrk:rw \
  -t zaproxy/zap-stable \
  zap.sh -cmd -autorun /zap/wrk/zap-automation.yaml

# API scan (OpenAPI spec based)
docker run --rm -v $(pwd):/zap/wrk:rw \
  -t zaproxy/zap-stable \
  zap-api-scan.py \
    -t https://staging.example.com/openapi.json \
    -f openapi \
    -r api-report.html

4.3 DAST vs SAST Comparison

CharacteristicSASTDAST
Analysis timingBuild timeRuntime
Source code requiredYesNo
Execution environment neededNoYes
False positive rateHighLow
CoverageEntire codebaseReachable paths
Vulnerability typesCode levelRuntime/config
Feedback speedFastSlow

5. SCA - Software Composition Analysis

5.1 Why SCA Matters

Over 80% of modern applications consist of open-source code. Log4Shell (CVE-2021-44228) demonstrated that a single open-source library vulnerability can have worldwide impact.

5.2 Snyk Configuration

# .snyk - Snyk policy file
version: v1.5
ignore:
  SNYK-JS-LODASH-590103:
    - '*':
        reason: "We don't use the affected function"
        expires: "2026-06-01T00:00:00.000Z"
patch: {}

language-settings:
  javascript:
    severity-threshold: high
# Snyk CLI usage
snyk test                    # Vulnerability check
snyk monitor                 # Register for continuous monitoring
snyk test --severity-threshold=high  # High severity only
snyk code test               # SAST scan
snyk container test myimage:latest  # Container scan
snyk iac test terraform/     # IaC scan

5.3 Dependabot Configuration

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "America/New_York"
    open-pull-requests-limit: 10
    reviewers:
      - "security-team"
    labels:
      - "dependencies"
      - "security"
    commit-message:
      prefix: "deps"
      include: "scope"
    groups:
      security-patches:
        patterns:
          - "*"
        update-types:
          - "security"

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "docker"
      - "dependencies"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "ci"
      - "dependencies"

5.4 Advanced Renovate Configuration

// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base",
    ":semanticCommits",
    "group:allNonMajor",
    "schedule:weekends"
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"],
    "schedule": ["at any time"]
  },
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "automerge": true,
      "automergeType": "pr",
      "platformAutomerge": true
    },
    {
      "matchPackagePatterns": ["eslint", "prettier"],
      "groupName": "linting tools",
      "automerge": true
    },
    {
      "matchPackagePatterns": ["^@types/"],
      "groupName": "type definitions",
      "automerge": true
    }
  ],
  "prConcurrentLimit": 5,
  "prHourlyLimit": 2
}

6. Container Security

6.1 Why Container Security Matters

Container images bundle OS packages, runtimes, and application dependencies. A single image can contain hundreds of vulnerabilities.

6.2 Trivy Image Scanning

# Basic image scan
trivy image myapp:latest

# Filter by severity
trivy image --severity CRITICAL,HIGH myapp:latest

# Only fixable vulnerabilities
trivy image --ignore-unfixed myapp:latest

# Generate SBOM
trivy image --format cyclonedx --output sbom.json myapp:latest

# JSON output with exit code for CI
trivy image --format json --output results.json \
  --exit-code 1 --severity CRITICAL myapp:latest

# Filesystem scan (before build)
trivy fs --security-checks vuln,secret,config .

# Kubernetes manifest scan
trivy config k8s/

6.3 Writing Secure Dockerfiles

# BAD - Security anti-patterns
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3
COPY . /app
RUN pip install -r requirements.txt
USER root
EXPOSE 8080
CMD ["python3", "app.py"]
# GOOD - Security best practices
# 1. Use minimal base image
FROM python:3.12-slim AS builder

# 2. Clean cache after package install
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc && \
    rm -rf /var/lib/apt/lists/*

# 3. Separate build stage
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# 4. Minimize final image
FROM python:3.12-slim

# 5. Non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 6. Copy only build artifacts
COPY --from=builder /install /usr/local
COPY --chown=appuser:appuser . /app

WORKDIR /app
USER appuser

# 7. Support read-only filesystem
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1

CMD ["python3", "app.py"]

6.4 Grype and Docker Scout

# Grype - Image vulnerability scanning
grype myapp:latest
grype myapp:latest --only-fixed
grype myapp:latest -o json > grype-results.json

# Docker Scout - Docker Desktop integration
docker scout cves myapp:latest
docker scout recommendations myapp:latest
docker scout quickview myapp:latest

6.5 Container Security Checklist

Container Security Checklist:

Image Build:
  [x] Minimal base image (distroless, alpine, slim)
  [x] Multi-stage build
  [x] Non-root user execution
  [x] Remove unnecessary packages
  [x] .dockerignore configured
  [x] Image signing (cosign)

Runtime:
  [x] Read-only filesystem
  [x] Resource limits (CPU, memory)
  [x] Minimize capabilities
  [x] seccomp/AppArmor profiles
  [x] Network policies applied

Registry:
  [x] Use private registry
  [x] Image vulnerability scanning
  [x] Tag immutability
  [x] Image signature verification

7. Secret Detection

7.1 The Reality of Leaked Secrets

According to GitHub, millions of secrets are committed to public repositories every year. Leaked API keys, passwords, and certificates can be exploited within minutes.

7.2 GitLeaks Configuration

# .gitleaks.toml
title = "Custom Gitleaks Configuration"

[extend]
useDefault = true

[[rules]]
id = "custom-api-key"
description = "Custom API Key Pattern"
regex = '''(?i)api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9]{32,}['"]'''
entropy = 3.5
secretGroup = 0
tags = ["api", "key"]

[[rules]]
id = "private-key-file"
description = "Private Key File Reference"
regex = '''(?i)(private[_-]?key|ssh[_-]?key)\s*[:=]\s*['"].*\.(pem|key|p12)['"]'''
tags = ["key", "private"]

[allowlist]
description = "Global Allowlist"
paths = [
  '''\.gitleaks\.toml''',
  '''test/.*''',
  '''.*_test\.go''',
  '''.*\.test\.(ts|js)'''
]
regexes = [
  '''EXAMPLE_.*''',
  '''REPLACE_ME'''
]
# Run GitLeaks
gitleaks detect --source . --verbose
gitleaks detect --source . --report-format json --report-path leaks.json

# Scan entire Git history
gitleaks detect --source . --log-opts="--all"

# pre-commit hook
gitleaks protect --staged --verbose

7.3 TruffleHog Usage

# Full repository scan
trufflehog git file://. --only-verified

# Scan entire GitHub organization
trufflehog github --org=my-org --only-verified

# Docker image scan
trufflehog docker --image=myapp:latest

# S3 bucket scan
trufflehog s3 --bucket=my-bucket

7.4 Pre-commit Hook Setup

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

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: detect-private-key
      - id: check-added-large-files
        args: ['--maxkb=500']

  - repo: https://github.com/returntocorp/semgrep
    rev: v1.50.0
    hooks:
      - id: semgrep
        args: ['--config', 'p/secrets', '--error']

8. Supply Chain Security - SBOM and SLSA

8.1 Supply Chain Attack Case Studies

IncidentYearImpact
SolarWinds202018,000+ organizations affected
Codecov2021CI script tampering
ua-parser-js2021npm package hijacking
Log4Shell2021Global Java app impact
xz utils2024Linux backdoor attempt

8.2 Generating SBOMs

An SBOM (Software Bill of Materials) is a list of all components included in software.

# Generate CycloneDX format SBOM
# npm project
npx @cyclonedx/cyclonedx-npm --output-file sbom.json

# Python project
cyclonedx-py requirements requirements.txt -o sbom.json

# Container image
trivy image --format cyclonedx -o sbom.json myapp:latest
syft myapp:latest -o cyclonedx-json > sbom.json

# SPDX format
syft myapp:latest -o spdx-json > sbom-spdx.json
// SBOM example (CycloneDX abbreviated)
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "version": 1,
  "metadata": {
    "component": {
      "type": "application",
      "name": "my-web-app",
      "version": "2.1.0"
    }
  },
  "components": [
    {
      "type": "library",
      "name": "express",
      "version": "4.18.2",
      "purl": "pkg:npm/express@4.18.2",
      "licenses": [{ "license": { "id": "MIT" } }]
    },
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21"
    }
  ]
}

8.3 SLSA Framework

SLSA (Supply-chain Levels for Software Artifacts) is a framework for measuring supply chain security maturity.

SLSA Levels:
┌──────────────┬─────────────────────────────────────┐
SLSA Level 0No security guarantees              │
SLSA Level 1Build process documented (provenance)SLSA Level 2Hosted build, signed provenance      │
SLSA Level 3Isolated build, tamper-resistant      │
└──────────────┴─────────────────────────────────────┘

8.4 Signing Artifacts with Sigstore

# Sign container image with cosign
cosign sign --key cosign.key myregistry/myapp:v1.0.0

# Keyless signing (OIDC-based)
cosign sign myregistry/myapp:v1.0.0

# Verify signature
cosign verify --key cosign.pub myregistry/myapp:v1.0.0

# Attach SBOM
cosign attach sbom --sbom sbom.json myregistry/myapp:v1.0.0

# Create artifact attestation
cosign attest --predicate provenance.json \
  --type slsaprovenance \
  myregistry/myapp:v1.0.0

9. Policy-as-Code

9.1 OPA (Open Policy Agent)

# policy/kubernetes.rego
package kubernetes.admission

# Deny root containers
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  container.securityContext.runAsUser == 0
  msg := sprintf("Container '%s' runs as root", [container.name])
}

# Deny latest tag
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  endswith(container.image, ":latest")
  msg := sprintf("Container '%s' uses latest tag", [container.name])
}

# Require resource limits
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.resources.limits
  msg := sprintf("Container '%s' has no resource limits", [container.name])
}

# Require read-only root filesystem
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.securityContext.readOnlyRootFilesystem
  msg := sprintf("Container '%s' doesn't use read-only filesystem", [container.name])
}

9.2 Kyverno Policies

# kyverno-policies.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-team-label
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "All Pods must have a 'team' label"
        pattern:
          metadata:
            labels:
              team: "?*"

---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-privileged-containers
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Privileged containers are not allowed"
        pattern:
          spec:
            containers:
              - securityContext:
                  privileged: "false"

9.3 IaC Validation with Conftest

# Validate Terraform with Conftest
conftest test terraform/ --policy policy/

# Validate Dockerfile
conftest test Dockerfile --policy policy/docker/

# Validate Kubernetes manifests
conftest test k8s/ --policy policy/kubernetes/
# policy/docker/dockerfile.rego
package main

deny[msg] {
  input[i].Cmd == "from"
  val := input[i].Value
  contains(val[0], ":latest")
  msg := "Using latest tag in FROM is prohibited"
}

deny[msg] {
  input[i].Cmd == "user"
  val := input[i].Value
  val[0] == "root"
  msg := "USER root is prohibited"
}

10. GitHub Actions Integrated Security Pipeline

10.1 Comprehensive Security Workflow

# .github/workflows/security.yml
name: Security Pipeline

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

permissions:
  contents: read
  security-events: write
  pull-requests: write

jobs:
  # Stage 1: Secret Detection
  secret-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: GitLeaks Scan
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # Stage 2: SAST
  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep Scan
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/typescript
            p/react
          generateSarif: "1"

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

  # Stage 3: SCA (Dependency Scan)
  sca:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

  # Stage 4: Container Security
  container-security:
    runs-on: ubuntu-latest
    needs: [sast]
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker Image
        run: docker build -t myapp:test .

      - name: Trivy Image Scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:test'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

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

  # Stage 5: SBOM Generation
  sbom:
    runs-on: ubuntu-latest
    needs: [container-security]
    steps:
      - uses: actions/checkout@v4

      - name: Generate SBOM
        uses: anchore/sbom-action@v0
        with:
          image: myapp:test
          format: cyclonedx-json
          output-file: sbom.json

      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json

  # Stage 6: IaC Scan
  iac-security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Trivy IaC Scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-iac.sarif'

      - name: Upload IaC SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-iac.sarif'
        if: always()

  # Stage 7: DAST (after staging deployment)
  dast:
    runs-on: ubuntu-latest
    needs: [container-security]
    if: github.ref == 'refs/heads/main'
    steps:
      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.10.0
        with:
          target: 'https://staging.example.com'
          rules_file_name: '.zap-rules.tsv'
          fail_action: 'warn'

  # Final: Security Gate
  security-gate:
    runs-on: ubuntu-latest
    needs: [secret-scan, sast, sca, container-security, iac-security]
    steps:
      - name: Security Gate Check
        run: |
          echo "All security checks passed!"
          echo "Pipeline completed at $(date)"

10.2 GitHub Advanced Security

# .github/workflows/codeql.yml
name: CodeQL Analysis

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 4 * * 1'

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    strategy:
      matrix:
        language: ['javascript', 'typescript']
    steps:
      - uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          queries: +security-extended,security-and-quality

      - name: Autobuild
        uses: github/codeql-action/autobuild@v3

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
        with:
          category: "/language:${{ matrix.language }}"

10.3 Security Pipeline Metrics

Key metrics to track in a successful DevSecOps program:

MetricDescriptionTarget
MTTRMean time to remediateCritical: within 24 hours
Vulnerability densityVulnerabilities per 1000 LOCBelow 0.5
Scan coverageRepos with security scanning100%
False positive rateFalse positives among all findingsBelow 20%
Pipeline pass rateSecurity gate pass rateAbove 85%
Dependency freshnessDependencies on latest versionAbove 80%

11. Practice Quiz

Q1: What is the difference between SAST and DAST?

Answer:

  • SAST statically analyzes source code to find vulnerabilities. It runs during the build phase and doesn't require an execution environment.
  • DAST simulates attacks against a running application. It requires a runtime environment and can work without source code.
  • SAST has a higher false positive rate but can analyze the entire codebase, while DAST has a lower false positive rate but only analyzes reachable paths.
  • Using both is ideal, and this combination is sometimes called IAST (Interactive AST).
Q2: Why does Shift-Left security reduce costs?

Answer:

  • Defect fix costs increase exponentially the later they are discovered.
  • Discovery at requirements costs 1x, design 5x, implementation 10x, testing 20x, production 30x, post-incident 100x+.
  • Shift-Left moves security to the early (left) stages of development, finding defects sooner and reducing costs.
  • Additionally, developers receive feedback while writing code, reducing context-switching costs.
Q3: Why is SBOM important for supply chain security?

Answer:

  • An SBOM is a list of all components (libraries, frameworks, etc.) included in software.
  • When vulnerabilities like Log4Shell are discovered, SBOMs enable immediate identification of affected systems.
  • SBOMs are also essential for license compliance verification.
  • US Executive Order 14028 mandated SBOM submission for government software suppliers.
  • CycloneDX and SPDX are the two primary SBOM standard formats.
Q4: What are 5 best practices for creating secure container images?

Answer:

  1. Minimal base images: Use distroless, alpine, or slim images to reduce attack surface
  2. Multi-stage builds: Don't include build tools in the final image
  3. Non-root user: Use the USER directive to run as a non-root user
  4. Image signing: Sign and verify images with cosign
  5. Regular scanning: Regularly scan images with Trivy/Grype

Additional: read-only filesystem, minimize capabilities, remove unnecessary packages

Q5: What is the recommended execution order for a security pipeline in GitHub Actions?

Answer:

Recommended order:

  1. Secret detection (GitLeaks) - fastest and most critical
  2. SAST (Semgrep/CodeQL) - code-level vulnerabilities
  3. SCA (Snyk/Dependabot) - dependency vulnerabilities
  4. IaC scanning (Trivy/Conftest) - infrastructure config validation
  5. Container security (Trivy) - scan after image build
  6. SBOM generation - create artifact inventory
  7. DAST (ZAP) - runtime testing after staging deployment
  8. Security gate - aggregate all results

Secret detection and SAST can run in parallel. DAST can only run after deployment.


12. References

  1. OWASP DevSecOps Guideline
  2. NIST Secure Software Development Framework (SSDF)
  3. SLSA - Supply-chain Levels for Software Artifacts
  4. Sigstore - Software Signing
  5. Semgrep Registry
  6. GitHub Security Lab
  7. Trivy Documentation
  8. Snyk Learn - Security Education
  9. OWASP Top 10 (2021)
  10. CycloneDX SBOM Standard
  11. OPA - Open Policy Agent
  12. Kyverno Documentation
  13. CISA SBOM Resources
  14. Google SLSA GitHub Generator