Skip to content

CI/CD Pipeline Security Scanning — SAST, DAST, SCA, and Container Scanning Explained

DodaTech Updated 2026-06-22 9 min read

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
â„šī¸ Info

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

  1. 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 .semgrepignore or .trivyignore for known false positives, and use baseline files to track existing findings separately from new ones.

  2. 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.

  3. 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.

  4. 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.

  5. 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

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