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

- Name
- Youngju Kim
- @fjvbn20031
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
- What Is DevSecOps
- Shift-Left Security Strategy
- SAST - Static Analysis
- DAST - Dynamic Analysis
- SCA - Software Composition Analysis
- Container Security
- Secret Detection
- Supply Chain Security - SBOM and SLSA
- Policy-as-Code
- GitHub Actions Integrated Security Pipeline
- Practice Quiz
- 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:
| Problem | Impact |
|---|---|
| Late discovery | Fix costs increase 30x |
| Bottleneck | Security team reviews every project |
| Developer apathy | "Security is the security team's job" mindset |
| Release delays | Entire schedule delayed when vulnerabilities found |
| Context gap | Security 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:
- Everyone is responsible for security: Security isn't just the security team's job
- Automation first: Use automated tools instead of manual reviews
- Fast feedback: Security feedback at the point developers write code
- Continuous improvement: Measure security metrics and continuously improve
- Shared responsibility: Knowledge sharing between dev, ops, and security teams
1.3 DevSecOps Tool Ecosystem Overview
┌─────────────────────────────────────────────────────────────┐
│ DevSecOps Pipeline │
├──────────┬──────────┬──────────┬──────────┬────────────────┤
│ Plan │ Code │ Build │ Test │ Deploy/Run │
├──────────┼──────────┼──────────┼──────────┼────────────────┤
│ Threat │ SAST │ SCA │ DAST │ Runtime │
│ Modeling │ Semgrep │ Snyk │ ZAP │ Protection │
│ │ CodeQL │ Trivy │ Burp │ Falco │
│ Security │ SonarQube│ OSV │ Nuclei │ Container │
│ Require │ │ │ │ Security │
│ ments │ Secret │ SBOM │ Fuzzing │ Network │
│ │ Detect │ Signing │ │ 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 Stage | Relative Cost | Example |
|---|---|---|
| Requirements | 1x | Missing security requirement found |
| Design | 5x | Architecture vulnerability found |
| Implementation | 10x | Code-level vulnerability found |
| Testing | 20x | Found during QA |
| Production | 30x | Found in production |
| Post-incident | 100x+ | 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 - Spoofing │
│ T - Tampering │
│ R - Repudiation │
│ I - Information Disclosure │
│ D - Denial of Service │
│ E - 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
| Feature | SonarQube | Semgrep | CodeQL |
|---|---|---|---|
| Price | Community free | OSS free | OSS free |
| Languages | 30+ | 30+ | 10+ |
| Custom rules | Java DSL | YAML | QL Language |
| CI integration | Good | Very good | GitHub optimized |
| Speed | Medium | Very fast | Slow (DB build) |
| Accuracy | High | High | Very high |
| Learning curve | Medium | Low | High |
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
| Characteristic | SAST | DAST |
|---|---|---|
| Analysis timing | Build time | Runtime |
| Source code required | Yes | No |
| Execution environment needed | No | Yes |
| False positive rate | High | Low |
| Coverage | Entire codebase | Reachable paths |
| Vulnerability types | Code level | Runtime/config |
| Feedback speed | Fast | Slow |
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 /install /usr/local
COPY . /app
WORKDIR /app
USER appuser
# 7. Support read-only filesystem
EXPOSE 8080
HEALTHCHECK \
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
| Incident | Year | Impact |
|---|---|---|
| SolarWinds | 2020 | 18,000+ organizations affected |
| Codecov | 2021 | CI script tampering |
| ua-parser-js | 2021 | npm package hijacking |
| Log4Shell | 2021 | Global Java app impact |
| xz utils | 2024 | Linux 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 0 │ No security guarantees │
│ SLSA Level 1 │ Build process documented (provenance)│
│ SLSA Level 2 │ Hosted build, signed provenance │
│ SLSA Level 3 │ Isolated 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:
| Metric | Description | Target |
|---|---|---|
| MTTR | Mean time to remediate | Critical: within 24 hours |
| Vulnerability density | Vulnerabilities per 1000 LOC | Below 0.5 |
| Scan coverage | Repos with security scanning | 100% |
| False positive rate | False positives among all findings | Below 20% |
| Pipeline pass rate | Security gate pass rate | Above 85% |
| Dependency freshness | Dependencies on latest version | Above 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:
- Minimal base images: Use distroless, alpine, or slim images to reduce attack surface
- Multi-stage builds: Don't include build tools in the final image
- Non-root user: Use the USER directive to run as a non-root user
- Image signing: Sign and verify images with cosign
- 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:
- Secret detection (GitLeaks) - fastest and most critical
- SAST (Semgrep/CodeQL) - code-level vulnerabilities
- SCA (Snyk/Dependabot) - dependency vulnerabilities
- IaC scanning (Trivy/Conftest) - infrastructure config validation
- Container security (Trivy) - scan after image build
- SBOM generation - create artifact inventory
- DAST (ZAP) - runtime testing after staging deployment
- Security gate - aggregate all results
Secret detection and SAST can run in parallel. DAST can only run after deployment.
12. References
- OWASP DevSecOps Guideline
- NIST Secure Software Development Framework (SSDF)
- SLSA - Supply-chain Levels for Software Artifacts
- Sigstore - Software Signing
- Semgrep Registry
- GitHub Security Lab
- Trivy Documentation
- Snyk Learn - Security Education
- OWASP Top 10 (2021)
- CycloneDX SBOM Standard
- OPA - Open Policy Agent
- Kyverno Documentation
- CISA SBOM Resources
- Google SLSA GitHub Generator