Security scanning in CI/CD pipelines is no longer optional—it's essential. But poorly implemented security checks can slow down deployments and frustrate development teams. After implementing security scanning for 100+ organizations, here's how to do it right.

The Security-First CI/CD Pipeline

A well-designed security pipeline should catch issues early without blocking development velocity. Here's our proven approach:

1

Code Commit

Pre-commit hooks for basic checks

2

Build & Test

SAST, dependency scanning

3

Security Scan

Infrastructure, container, secrets

4

Deploy

Runtime monitoring

1. Pre-commit Security Hooks

Catch issues before they enter your repository with pre-commit hooks. This prevents secrets and vulnerable code from being committed in the first place.

.pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files

  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']

  - repo: https://github.com/terraform-docs/terraform-docs
    rev: v0.16.0
    hooks:
      - id: terraform-docs
        args: [--config-file, .terraform-docs.yml]

  - repo: https://github.com/aquasecurity/tfsec
    rev: v1.28.1
    hooks:
      - id: tfsec
        args: [--soft-fail]

Setup Commands:

# Install pre-commit
pip install pre-commit

# Install hooks
pre-commit install

# Run on all files
pre-commit run --all-files

# Update secrets baseline
detect-secrets scan --update .secrets.baseline

2. GitHub Actions Security Pipeline

Here's a complete GitHub Actions workflow that runs security scans without blocking deployments for low-risk issues.

.github/workflows/security.yml
name: Security Scan

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

jobs:
  security-scan:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      actions: read
      contents: read

    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'

    - name: Install dependencies
      run: npm ci

    # SAST - Static Application Security Testing
    - name: Run CodeQL Analysis
      uses: github/codeql-action/init@v3
      with:
        languages: javascript, python

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3

    # Dependency scanning
    - name: Run npm audit
      run: npm audit --audit-level moderate
      continue-on-error: true

    # Infrastructure scanning
    - name: Run Trivy filesystem scan
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'

    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: 'trivy-results.sarif'

    # Container scanning
    - name: Build Docker image
      run: docker build -t ${github.repository}:${github.sha} .

    - name: Run Trivy container scan
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: '${github.repository}:${github.sha}'
        format: 'table'
        exit-code: '0'

    # Terraform security scanning
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v3

    - name: Terraform Format Check
      run: terraform fmt -check
      working-directory: ./infrastructure

    - name: Run TFSec
      uses: aquasecurity/tfsec-action@v1.0.3
      with:
        working_directory: ./infrastructure
        soft_fail: true

    # Security report
    - name: Generate security report
      if: always()
      run: |
        echo "## Security Scan Results" >> $GITHUB_STEP_SUMMARY
        echo "| Tool | Status |" >> $GITHUB_STEP_SUMMARY
        echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
        echo "| CodeQL | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
        echo "| Trivy | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
        echo "| TFSec | ✅ Passed |" >> $GITHUB_STEP_SUMMARY

3. GitLab CI Security Pipeline

For GitLab users, here's an equivalent pipeline using GitLab's built-in security features.

.gitlab-ci.yml
stages:
  - security

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"

include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml
  - template: Security/Secret-Detection.gitlab-ci.yml

# Custom security jobs
security:terraform:
  stage: security
  image: hashicorp/terraform:1.5.0
  before_script:
    - cd infrastructure
  script:
    - terraform init
    - terraform validate
    - terraform plan
  only:
    - merge_requests
    - main

security:tfsec:
  stage: security
  image: aquasec/tfsec:latest
  script:
    - tfsec ./infrastructure --format sarif --out tfsec-results.sarif
  artifacts:
    reports:
      sast: tfsec-results.sarif
  only:
    - merge_requests
    - main

security:trivy:
  stage: security
  image: aquasec/trivy:latest
  script:
    - trivy fs --format sarif --output trivy-results.sarif .
  artifacts:
    reports:
      sast: trivy-results.sarif
  only:
    - merge_requests
    - main

4. AWS-Specific Security Scanning

For AWS environments, integrate cloud-specific security scanning into your pipeline.

AWS Security Pipeline
# AWS CloudFormation security scanning
- name: Run cfn-lint
  run: |
    pip install cfn-lint
    cfn-lint infrastructure/cloudformation/*.yaml

# AWS CDK security scanning
- name: Run CDK security scan
  run: |
    npm install -g cdk-nag
    cdk-nag infrastructure/

# AWS IAM policy validation
- name: Validate IAM policies
  run: |
    aws iam validate-policy --policy-document file://policies/admin-policy.json

# AWS Config rules validation
- name: Check AWS Config rules
  run: |
    aws configservice describe-config-rules --query 'ConfigRules[?ConfigRuleState==`ACTIVE`]'

5. Performance Optimization

Security scanning can slow down your pipeline. Here's how to optimize for speed:

🚀 Parallel Execution

Run security scans in parallel with tests to reduce total pipeline time.

📦 Caching

Cache dependencies and scan results to avoid redundant work.

🎯 Incremental Scanning

Only scan changed files and directories to reduce scan time.

⚡ Fast Failures

Fail fast on critical issues, allow warnings to pass through.

Optimized GitHub Actions Workflow:

name: Optimized Security Scan

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

jobs:
  security-scan:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        scan-type: [sast, dependency, infrastructure, container]
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Cache dependencies
      uses: actions/cache@v3
      with:
        path: ~/.npm
        key: ${runner.os}-node-${hashFiles('**/package-lock.json')}

    - name: Run SAST
      if: matrix.scan-type == 'sast'
      run: |
        # Only scan changed files
        git diff --name-only HEAD~1 | grep -E '.(js|ts|py)$' | xargs -I {} npm audit {}

    - name: Run Dependency Scan
      if: matrix.scan-type == 'dependency'
      run: |
        npm audit --audit-level high --json > audit-results.json
        # Process results...

    - name: Run Infrastructure Scan
      if: matrix.scan-type == 'infrastructure'
      run: |
        # Only scan changed Terraform files
        find . -name "*.tf" -newer .git/HEAD | xargs tfsec

    - name: Run Container Scan
      if: matrix.scan-type == 'container'
      run: |
        docker build -t test-image .
        trivy image test-image --severity HIGH,CRITICAL

6. Security Gate Strategy

Implement smart security gates that block deployments for critical issues but allow low-risk warnings to pass through.

🚨 Critical Issues

  • Hardcoded secrets
  • Critical vulnerabilities
  • Misconfigured IAM policies

Action: Block deployment

⚠️ High Issues

  • High-severity vulnerabilities
  • Outdated dependencies
  • Security misconfigurations

Action: Require approval

ℹ️ Medium Issues

  • Medium-severity vulnerabilities
  • Code quality issues
  • Best practice violations

Action: Log and notify

7. Monitoring and Alerting

Set up monitoring to track security scan results and alert on critical issues.

Security Monitoring Setup
# Slack notification for security issues
- name: Notify on security issues
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: failure
    text: 'Security scan failed for ${github.ref}'
    webhook_url: ${secrets.SLACK_WEBHOOK}

# GitHub Security tab integration
- name: Upload security results
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: 'security-results.sarif'

# Custom security dashboard
- name: Update security dashboard
  run: |
    # Send metrics to your monitoring system
    curl -X POST https://api.your-monitoring.com/security-metrics       -H "Authorization: Bearer ${secrets.MONITORING_TOKEN}"       -d '{"repository": "${github.repository}", "scan_results": "..."}'

Common Pitfalls to Avoid

❌ Running All Scans on Every Commit

Use incremental scanning and only run full scans on main branch or release candidates.

❌ Blocking on Every Warning

Implement severity-based gates. Don't block deployments for low-priority issues.

❌ Not Caching Dependencies

Cache scan tools and dependencies to reduce pipeline execution time.

❌ Ignoring False Positives

Configure scan tools to suppress known false positives and reduce noise.

Next Steps

Start with basic pre-commit hooks and gradually add more comprehensive scanning as your team becomes comfortable with the process. Focus on high-impact, low-effort improvements first.

Need Help Implementing Security Scanning?

Our team has helped 100+ organizations implement security scanning in their CI/CD pipelines. Get our free AWS CIS checklist with Terraform fixes to accelerate your security automation.

Get Free Security Assessment