CI/CD Pipeline Security Scanning â SAST, DAST, SCA, and Container Scanning Explained
In this tutorial, you'll learn about CI/CD Pipeline Security Scanning. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
CI/CD security scanning integrates automated security checks into the software delivery pipeline, catching vulnerabilities, misconfigurations, and secrets before they reach production, transforming security from a gate at the end into a continuous practice throughout development.
What You'll Learn
Why It Matters
Waiting until the end of a release cycle for a security review is the most expensive and least effective approach. Vulnerabilities found in production cost 100x more to fix than those caught during development. CI/CD pipelines that include SAST (Static Application Security Testing), DAST (Dynamic Analysis), SCA (Software Composition Analysis), and container scanning catch issues automatically on every commit, reducing mean-time-to-remediate from weeks to minutes.
Real-World Use
DodaTech's DevSecOps pipeline runs 12 security scans on every Pull Request to Durga Antivirus Pro: SAST with Semgrep, SCA with Trivy, secrets detection with Gitleaks, container scanning with Grype, Infrastructure as Code scanning with Checkov, and DAST with ZAP on the staging environment. Failed scans block PRs from merging.
flowchart LR
A["Developer Push/PR"] --> B["Source Code Scan"]
B --> C{"SAST: Semgrep"}
B --> D{"Secrets: Gitleaks"}
B --> E{"SCA: Trivy"}
C -->|Fail| F["Block Merge"]
D -->|Fail| F
E -->|High/Critical| F
C -->|Pass| G["Build Container"]
D -->|Pass| G
E -->|Pass| G
G --> H["Container Scan"]
H --> I{"Grype + Trivy"}
I -->|Critical| F
I -->|Pass| J["Deploy to Staging"]
J --> K["DAST: ZAP Scan"]
K --> L{"Vulnerabilities?"}
L -->|Yes| F
L -->|No| M["Deploy to Production"]
style F fill:#CC3333,color:#fff
style M fill:#269539,color:#fff
Prerequisites: Familiarity with CI/CD pipelines (GitHub Actions, GitLab CI, or Jenkins), basic Docker knowledge, and understanding of common vulnerability types (OWASP Top 10).
Pipeline Security Scan Types
| Scan Type | What It Detects | When It Runs | Tools |
|---|---|---|---|
| SAST | Code-level vulnerabilities (XSS, SQLi, RCE) | On every commit | Semgrep, CodeQL, SonarQube |
| SCA | Vulnerable open-source dependencies | On every commit | Trivy, Snyk, Dependabot |
| Secret Detection | Hard-coded credentials, API keys, tokens | On every commit | Gitleaks, TruffleHog |
| Container Scan | OS/CVE vulnerabilities in images | After build | Grype, Trivy, Clair |
| IaC Scan | Cloud misconfigurations | On terraform/helm changes | Checkov, tfsec, KICS |
| DAST | Runtime vulnerabilities | On staging deployment | ZAP, Burp Suite |
Implementing SAST with Semgrep
# .github/workflows/semgrep.yml
name: Semgrep SAST
on:
pull_request:
paths:
- "src/**"
- "api/**"
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
with:
config: p/default
publishToken: ${{ secrets.SEMGREP_APP_TOKEN }}
generateSarif: true
- name: Upload SARIF to GitHub
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep.sarif
Expected behavior: When a Pull Request modifies code in the src/ or api/ directories, Semgrep runs the default ruleset (1000+ rules covering OWASP Top 10, CWE Top 25). Findings appear as annotations on the PR diff. Rules tagged security block the PR, while style warnings are informational.
# Run Semgrep locally
semgrep --config=p/default --error \
--exclude=tests/ --exclude=*.test.js src/
# Expected output:
# Running 1760 rules...
# 100%|ââââââââââââââââââââââââââââââââââââââââ| 1760/1760
# Findings:
# src/routes/users.js:45
# severity:error rule:javascript.express.security.audit
# SQL query built from user input. Use parameterized statements.
# Details: https://semgrep.dev/r/javascript.express.security.audit
#
# src/utils/auth.js:12
# severity:error rule:javascript.lang.security.audit
# Hard-coded credential detected.
#
# Ran 1760 rules on 45 files: 2 findings
Secret Detection with Gitleaks
# .github/workflows/gitleaks.yml
name: Secret Scanning
on: [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 }}
Expected behavior: Gitleaks scans the entire Git history for secrets. If a commit containing an AWS access key, GitHub token, or private SSH key is detected, the scan fails and the PR receives a comment identifying the file and line.
# Scan a Git repository locally
gitleaks detect --source . --verbose
# Expected output:
# â
# â˛ââą gitleaks
# â
# Finding: gitlab-ci-token = "glpat-xxxxxxxxxxxxxxxxxxxx"
# Secret: glpat-xxxxxxxxxxxxxxxxxxxx
# RuleID: gitlab-pat
# File: .gitlab-ci.yml
# Line: 15
# Fingerprint: abc123...
#
# 1 high severity finding detected.
# 0 other findings.
Container Image Scanning with Trivy
# .github/workflows/container-scan.yml
name: Container Scan
on:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t app:latest .
- name: Scan with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: app:latest
format: table
exit-code: 1
severity: CRITICAL,HIGH
ignore-unfixed: true
Expected behavior: Trivy scans the built Docker image for OS package vulnerabilities and application library vulnerabilities. If any critical or high severity vulnerabilities are found, the action fails with exit code 1, preventing the vulnerable image from being published.
# Scan a Docker image locally
trivy image myapp:latest --severity CRITICAL,HIGH --ignore-unfixed
# Expected output:
# myapp:latest (debian 12.0)
# ===========================
# Total: 3 (CRITICAL: 1, HIGH: 2)
#
# +---------------+------------------+----------+-----------+--------------+
# | LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED | FIXED VERSION|
# +---------------+------------------+----------+-----------+--------------+
# | libopenssl3 | CVE-2024-12345 | CRITICAL | 3.1.0-1 | 3.1.1-1 |
# | curl | CVE-2024-67890 | HIGH | 7.88.1 | 7.88.2 |
# | libxml2 | CVE-2024-11111 | HIGH | 2.10.2 | 2.10.3 |
# +---------------+------------------+----------+-----------+--------------+
Infrastructure as Code Scanning with Checkov
# .github/workflows/iac-scan.yml
name: IaC Security Scan
on:
pull_request:
paths:
- "terraform/**"
- "helm/**"
jobs:
checkov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@v12
with:
directory: .
framework: terraform,helm,kubernetes
quiet: true
soft_fail: false
Expected behavior: Checkov scans all Terraform, Helm, and Kubernetes files for misconfigurations. An S3 bucket without encryption enabled is flagged. A Kubernetes Pod with privileged: true is flagged. IAM policies with "Effect": "*" are flagged. Failing checks prevent the PR from merging.
# Scan a Terraform directory locally
checkov -d terraform/ --framework terraform
# Expected output:
# _ _
# | |__ _ _| |_ ___ __
# | '_ \| | | | __| / __|/ _ \
# | |_) | |_| | |_ _\__ \ __/
# |_.__/ \__,_|\__(_)___/\___|
#
# terraform scan results:
# Passed checks: 45, Failed checks: 3, Skipped checks: 0
#
# Check: CKV_AWS_111: "Ensure IAM policies does not allow 'effect: *'"
# PASSED
# Check: CKV_AWS_115: "Ensure S3 bucket has public access block"
# FAILED for resource: aws_s3_bucket.data_bucket
# File: s3.tf:1-20
# Guide: https://docs.bridgecrew.io/docs/s3_16-block-public-access
Common Errors
False positives overwhelming developers: Without tuning, security tools produce many false positives. Developers develop alert fatigue and ignore real findings. Configure severity thresholds, maintain a
.semgrepignoreor.trivyignorefor known false positives, and use baseline files to track existing findings separately from new ones.Scanning only the main branch: Scanning only on push to main misses vulnerabilities introduced during development. Run SAST and SCA on every PR so issues are caught before merge. Block PRs with critical findings to enforce shift-left.
Container scans after deployment to production: Scanning a container image after it has been pushed to production means vulnerable code is already running. Scan images before pushing to the registry, and enforce -- if scan fails, image is not published.
Not updating vulnerability databases: Trivy, Semgrep, and other tools rely on vulnerability databases that are updated daily. CI/CD caches can cause stale databases. Configure weekly or daily database updates and ensure the CI runner fetches the latest database before scanning.
Treating all findings with equal priority: A medium-severity finding in a test file is not the same as a critical finding in production code. Categorize by severity and location. Block only critical/high in production paths, flag medium findings as warnings, and allow informational findings to be ignored.
Practice Questions
What is the difference between SAST and DAST? Answer: SAST (Static Application Security Testing) analyzes source code without executing it, finding vulnerabilities early in development. DAST (Dynamic Application Security Testing) tests the running application by sending HTTP requests and analyzing responses, catching runtime issues like authentication bypasses and server misconfigurations that SAST might miss.
Why is SCA (Software Composition Analysis) important in modern development? Answer: Modern applications use 80%+ open-source libraries. SCA tracks these dependencies, identifies known vulnerabilities (CVEs), checks license compliance, and alerts when a library version is no longer maintained. Without SCA, teams unknowingly ship vulnerable dependencies.
How does a secrets scanner differentiate between a real secret and a test token? Answer: Tools like Gitleaks use entropy analysis and regex patterns. High-entropy strings that match known patterns (AWS keys start with AKIA, GitHub tokens start with ghp_) are flagged. Entropy alone generates false positives (commit hashes, UUIDs). Combining regex patterns with entropy thresholds reduces false positives.
What is the purpose of SARIF format in security scanning? Answer: SARIF (Static Analysis Results Interchange Format) is a standard format for Static Analysis results. GitHub, GitLab, and Azure DevOps support SARIF to display findings as code annotations, track them over time, and prevent new findings from being introduced.
Challenge
Create a complete CI/CD security pipeline for a Node.js application with the following stages: SAST using Semgrep with the p/<a href="/programming-languages/javascript/">JavaScript</a> ruleset, SCA using Trivy with a trivy.yaml config that excludes test dependencies, secret scanning with Gitleaks scanning only the diff (not full history), container scanning with Grype on the Docker image, IaC scanning with Checkov for any Terraform files, and a security gate that blocks the pipeline if any critical finding is discovered. The pipeline should run on pull requests and push to main. Implement a .trivyignore file to suppress known false positives in the devDependencies.
Mini Project
Build a production-grade security scanning platform: set up a GitHub repository with a Node.js application, write a Dockerfile with multi-stage builds, create a .github/workflows/security.yml workflow that runs Semgrep SAST (with p/default and p/<a href="/programming-languages/javascript/">JavaScript</a> rulesets), Gitleaks secret scanning, Trivy SCA scanning (both OS and language-specific packages), Trivy container scanning on the built image, Checkov IaC scanning on any Terraform/HCL files, and Dependency Review to block known malicious packages, configure all tools to upload SARIF results to GitHub code scanning, set branch protection rules requiring all security checks to pass before merging, introduce a test vulnerability (a hard-coded API key and an outdated dependency with a known CVE), verify the pipeline catches both, and create a dashboard in GitHub Insights showing security findings over time.
Related Resources
| Resource | Description |
|---|---|
| DevSecOps Pipeline | Integrating security early |
| Container Security | Securing containerized apps |
| GitHub Actions | Building CI/CD workflows |
| Compliance Standards | Security compliance frameworks |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro