Skill v1.0.0
currentLLM-judged scan95/100name: implementing-devsecops-security-scanning description: 'Integrates SAST, DAST, and SCA into CI/CD pipelines using Semgrep for SAST, Trivy for SCA and container scanning, OWASP ZAP for DAST, and Gitleaks for secrets detection. Use when setting up automated security scanning in CI/CD, shifting security left, meeting compliance mandates (SOC 2, PCI-DSS, ISO 27001), or gating deployments on critical vulnerabilities.
' domain: cybersecurity subdomain: application-security tags:
- devsecops
- SAST
- DAST
- SCA
- semgrep
- trivy
- owasp-zap
- gitleaks
- CI-CD
- shift-left
version: 1.0.0 author: mahipal license: Apache-2.0 nist_csf:
- PR.PS-01
- PR.PS-04
- ID.RA-01
- PR.DS-10
mitre_attack:
- T1078
- T1190
- T1059
- T1610
- T1611
Implementing DevSecOps Security Scanning
When to Use
- Setting up automated security scanning in a new or existing CI/CD pipeline
- Shifting security left by catching vulnerabilities before code reaches production
- Meeting compliance requirements (SOC 2, PCI-DSS, ISO 27001) that mandate automated security testing
- Integrating SAST, DAST, and SCA together to achieve comprehensive application security coverage
- Establishing security gates that block deployments containing critical or high-severity vulnerabilities
Do not use as a replacement for manual penetration testing. Automated scanning catches common vulnerability patterns but cannot replace human-driven security assessments for business logic flaws and complex attack chains.
Prerequisites
- CI/CD platform: GitHub Actions, GitLab CI, Jenkins, or Azure DevOps
- Container runtime (Docker) for running scanning tools
- A staging environment URL for DAST scanning (DAST cannot test static code)
- Repository access with permissions to modify CI/CD workflow files
- Tool-specific requirements:
- Semgrep: free for open-source rulesets (
p/security-audit,p/owasp-top-ten) - Trivy: free, no account required
- OWASP ZAP: free, Docker image available
- Gitleaks: free, no account required
Workflow
Step 1: Add Secrets Detection with Gitleaks
Secrets detection runs first because leaked credentials are the highest-priority finding. Add to .github/workflows/security.yml:
name: DevSecOps Security Pipelineon:push:branches: [main, develop]pull_request:branches: [main]jobs:secrets-scan:name: Secrets Detection (Gitleaks)runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4with:fetch-depth: 0 # Full history for scanning all commits- name: Run Gitleaksuses: gitleaks/gitleaks-action@v2env:GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Configure .gitleaks.toml in the repository root for custom rules and allowlists:
[extend]useDefault = true[allowlist]description = "Global allowlist"paths = ['''\.gitleaks\.toml''','''test/fixtures/.*''','''docs/examples/.*'''][[rules]]id = "custom-internal-api-key"description = "Internal API key pattern"regex = '''INTERNAL_KEY_[A-Za-z0-9]{32}'''tags = ["internal", "api-key"]
Step 2: Add SAST Scanning with Semgrep
Semgrep performs static code analysis to find security vulnerabilities, bugs, and code patterns:
sast-scan:name: SAST (Semgrep)runs-on: ubuntu-latestcontainer:image: semgrep/semgrepsteps:- uses: actions/checkout@v4- name: Run Semgrep SAST scanrun: |semgrep scan \--config p/security-audit \--config p/owasp-top-ten \--config p/secrets \--severity ERROR \--error \--json \--output semgrep-results.json \.- name: Upload SAST resultsif: always()uses: actions/upload-artifact@v4with:name: semgrep-resultspath: semgrep-results.json
For custom rules, create .semgrep/custom-rules.yml:
rules:- id: no-exec-user-inputpatterns:- pattern: exec($INPUT)- pattern-not: exec("...")message: >User input passed to exec(). This is a command injection vulnerability.severity: ERRORlanguages: [python]metadata:cwe: "CWE-78: OS Command Injection"owasp: "A03:2021 - Injection"- id: no-raw-sql-queriespatterns:- pattern: cursor.execute(f"...")- pattern: cursor.execute("..." + ...)message: >SQL query built with string concatenation or f-strings. Use parameterized queries.severity: ERRORlanguages: [python]metadata:cwe: "CWE-89: SQL Injection"owasp: "A03:2021 - Injection"
Step 3: Add SCA Scanning with Trivy
Trivy scans dependencies, container images, IaC files, and generates SBOM:
sca-scan:name: SCA & Container Scan (Trivy)runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- name: Run Trivy filesystem scan (dependencies)uses: aquasecurity/trivy-action@0.28.0with:scan-type: 'fs'scan-ref: '.'severity: 'CRITICAL,HIGH'exit-code: '1'format: 'json'output: 'trivy-fs-results.json'- name: Run Trivy IaC scan (Terraform, CloudFormation)uses: aquasecurity/trivy-action@0.28.0with:scan-type: 'config'scan-ref: '.'severity: 'CRITICAL,HIGH'exit-code: '1'format: 'json'output: 'trivy-iac-results.json'- name: Upload SCA resultsif: always()uses: actions/upload-artifact@v4with:name: trivy-resultspath: trivy-*.jsoncontainer-scan:name: Container Image Scan (Trivy)runs-on: ubuntu-latestneeds: [sast-scan] # Build image only after SAST passessteps:- uses: actions/checkout@v4- name: Build Docker imagerun: docker build -t app:${{ github.sha }} .- name: Scan container imageuses: aquasecurity/trivy-action@0.28.0with:image-ref: 'app:${{ github.sha }}'severity: 'CRITICAL,HIGH'exit-code: '1'format: 'json'output: 'trivy-image-results.json'- name: Generate SBOMuses: aquasecurity/trivy-action@0.28.0with:image-ref: 'app:${{ github.sha }}'format: 'cyclonedx'output: 'sbom.json'- name: Upload SBOMuses: actions/upload-artifact@v4with:name: sbompath: sbom.json
Step 4: Add DAST Scanning with OWASP ZAP
DAST runs against a deployed staging environment. It is slower than SAST/SCA and should run asynchronously or on a schedule:
dast-scan:name: DAST (OWASP ZAP)runs-on: ubuntu-latestneeds: [deploy-staging] # Must run after app is deployed to stagingsteps:- uses: actions/checkout@v4- name: Run ZAP Baseline Scan (fast, suitable for CI)uses: zaproxy/action-baseline@v0.14.0with:target: ${{ vars.STAGING_URL }}rules_file_name: '.zap/rules.tsv'cmd_options: '-a -j'# For nightly full scans, use action-full-scan instead:# - name: Run ZAP Full Scan (comprehensive, 30-60 min)# uses: zaproxy/action-full-scan@v0.12.0# with:# target: ${{ vars.STAGING_URL }}
Create .zap/rules.tsv to configure alert thresholds:
10010 IGNORE (Cookie No HttpOnly Flag - acceptable for non-sensitive cookies)10011 IGNORE (Cookie Without Secure Flag - staging uses HTTP)90033 WARN (Loosely Scoped Cookie)10038 FAIL (Content Security Policy Header Not Set)40012 FAIL (Cross Site Scripting - Reflected)40014 FAIL (Cross Site Scripting - Persistent)40018 FAIL (SQL Injection)90019 FAIL (Server Side Code Injection)90020 FAIL (Remote OS Command Injection)
Step 5: Aggregate Results and Enforce Security Gates
Create a summary job that aggregates all scan results and enforces pass/fail gates:
security-gate:name: Security Gateruns-on: ubuntu-latestneeds: [secrets-scan, sast-scan, sca-scan, container-scan]if: always()steps:- name: Check scan resultsrun: |echo "Checking security scan results..."# Fail the pipeline if any upstream job failedif [[ "${{ needs.secrets-scan.result }}" == "failure" ]]; thenecho "BLOCKED: Secrets detected in repository"exit 1fiif [[ "${{ needs.sast-scan.result }}" == "failure" ]]; thenecho "BLOCKED: SAST found critical/high vulnerabilities"exit 1fiif [[ "${{ needs.sca-scan.result }}" == "failure" ]]; thenecho "BLOCKED: SCA found critical/high vulnerable dependencies"exit 1fiif [[ "${{ needs.container-scan.result }}" == "failure" ]]; thenecho "BLOCKED: Container image has critical/high vulnerabilities"exit 1fiecho "All security gates passed"
Step 6: Configure Branch Protection Rules
Enforce the security pipeline as a required status check:
GitHub Repository > Settings > Branches > Branch Protection RulesBranch name pattern: mainRequire status checks to pass before merging: EnabledRequired status checks:- Secrets Detection (Gitleaks)- SAST (Semgrep)- SCA & Container Scan (Trivy)- Security GateRequire branches to be up to date before merging: Enabled
Step 7: Set Up Developer Feedback Loop
Configure pre-commit hooks so developers catch issues before pushing:
# .pre-commit-config.yamlrepos:- repo: https://github.com/gitleaks/gitleaksrev: v8.22.1hooks:- id: gitleaks- repo: https://github.com/semgrep/semgreprev: v1.102.0hooks:- id: semgrepargs: ['--config', 'p/security-audit', '--config', 'p/owasp-top-ten', '--error']
Install and activate pre-commit:
pip install pre-commitpre-commit installpre-commit run --all-files # Test against existing codebase
Key Concepts
| Term | Definition | |
|---|---|---|
| SAST (Static Application Security Testing) | Analyzes source code without executing it to find security vulnerabilities; runs fast, catches issues early, but cannot find runtime flaws | |
| DAST (Dynamic Application Security Testing) | Tests a running application by sending requests and analyzing responses; finds runtime issues but requires a deployed environment | |
| SCA (Software Composition Analysis) | Scans project dependencies against vulnerability databases (NVD, GitHub Advisory) to find known-vulnerable libraries | |
| SBOM (Software Bill of Materials) | Machine-readable inventory of all components and dependencies in an application, used for vulnerability tracking and compliance | |
| Shift Left | Security practice of moving security testing earlier in the SDLC, from post-deployment to pre-commit and CI stages | |
| Security Gate | A CI/CD pipeline checkpoint that blocks deployment if security scan results exceed defined severity thresholds | |
| Pre-commit Hook | Local Git hook that runs security checks before code is committed, providing the fastest developer feedback loop |
Verification
- [ ] Gitleaks blocks commits and PRs containing hardcoded secrets (test with a dummy API key)
- [ ] Semgrep scan runs on every PR and reports findings as annotations or comments
- [ ] Trivy filesystem scan detects a known-vulnerable dependency (test by adding a vulnerable package)
- [ ] Trivy container scan runs successfully against the built Docker image
- [ ] SBOM is generated and stored as a build artifact in CycloneDX or SPDX format
- [ ] OWASP ZAP baseline scan runs against the staging URL without crashing
- [ ] Security gate job blocks merges to main when any scan finds critical/high severity issues
- [ ] Branch protection rules enforce required status checks before merge
- [ ] Pre-commit hooks catch secrets and SAST findings locally before push
- [ ] Developer documentation explains how to interpret scan results and fix common findings