22 November 202514 min read
SCA and DAST: Dependency and Dynamic Security Testing
SecurityDevSecOpsSCADAST
Implement Software Composition Analysis and Dynamic Application Security Testing to secure dependencies and detect runtime vulnerabilities in production-like environments.
Software Composition Analysis (SCA) and Dynamic Application Security Testing (DAST) complement SAST to provide comprehensive security coverage. SCA focuses on third-party dependencies, while DAST tests running applications for vulnerabilities.
Software Composition Analysis (SCA)
Why SCA Matters
Modern Application Composition:
┌─────────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────────┤
│ Your Code (10-20%) │
├─────────────────────────────────────────┤
│ Direct Dependencies (20-30%) │
├─────────────────────────────────────────┤
│ Transitive Dependencies (50-70%) │
└─────────────────────────────────────────┘
Risk Profile:
├── 84% of codebases contain vulnerabilities
├── Average: 158 dependencies per project
├── 75% of vulnerabilities in transitive deps
└── 6 months average age of unfixed vulnsSCA Tools Comparison
| Tool | Free Tier | Languages | CI Integration | License Detection |
|---|---|---|---|---|
| Snyk | Limited | 15+ | Excellent | Yes |
| Dependabot | Full | 10+ | GitHub native | Limited |
| OWASP Dep-Check | Full | 8+ | Good | No |
| Trivy | Full | 10+ | Excellent | Yes |
| Renovate | Full | 20+ | Excellent | No |
Snyk Implementation
# .github/workflows/snyk.yml
name: Snyk Security Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * *' # Daily at 6 AM
jobs:
snyk-sca:
name: Snyk SCA Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Snyk test
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: >
--severity-threshold=high
--fail-on=all
--json-file-output=snyk-results.json
- name: Upload Snyk results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: snyk-results.json
if: always()
snyk-container:
name: Snyk Container Scan
runs-on: ubuntu-latest
needs: snyk-sca
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:test .
- name: Run Snyk Container test
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: myapp:test
args: --severity-threshold=high
snyk-iac:
name: Snyk IaC Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Snyk IaC test
uses: snyk/actions/iac@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=mediumTrivy Configuration
# trivy.yaml
scan:
security-checks:
- vuln
- secret
- config
vulnerability:
type:
- os
- library
severity:
- CRITICAL
- HIGH
- MEDIUM
ignore-unfixed: false
exit-code: 1
format: table
output: trivy-results.txtTrivy GitHub Action
# .github/workflows/trivy.yml
name: Trivy Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
trivy-fs:
name: Trivy Filesystem Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner (filesystem)
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-fs-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-fs-results.sarif'
trivy-image:
name: Trivy Container Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner (image)
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-image-results.sarif'
severity: 'CRITICAL,HIGH'
vuln-type: 'os,library'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-image-results.sarif'OWASP Dependency-Check
# .github/workflows/dependency-check.yml
name: OWASP Dependency Check
on:
push:
branches: [main]
schedule:
- cron: '0 4 * * 1' # Weekly on Monday
jobs:
dependency-check:
name: OWASP Dependency Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'my-application'
path: '.'
format: 'HTML'
args: >
--failOnCVSS 7
--enableRetired
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: dependency-check-report
path: reports/Renovate for Automated Updates
// renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":semanticCommits",
"security:openssf-scorecard"
],
"schedule": ["before 6am on monday"],
"timezone": "Europe/London",
"vulnerabilityAlerts": {
"enabled": true,
"labels": ["security"],
"schedule": ["at any time"]
},
"packageRules": [
{
"matchDepTypes": ["devDependencies"],
"automerge": true,
"automergeType": "branch"
},
{
"matchUpdateTypes": ["patch"],
"automerge": true
},
{
"matchPackagePatterns": ["^@types/"],
"automerge": true
},
{
"matchDepTypes": ["dependencies"],
"matchUpdateTypes": ["major"],
"labels": ["major-update"],
"automerge": false
}
],
"prConcurrentLimit": 5,
"prHourlyLimit": 2
}Dynamic Application Security Testing (DAST)
DAST Overview
DAST Testing Flow:
┌─────────────────┐ ┌─────────────────┐
│ DAST Scanner │────▶│ Running App │
└────────┬────────┘ └────────┬────────┘
│ │
│ HTTP Requests │
│ ◀────────────────────┤
│ │
│ Malicious Payloads │
│ ────────────────────▶│
│ │
│ Analyse Responses │
│ ◀────────────────────┤
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Vulnerability │ │ Server Logs │
│ Report │ │ & Errors │
└─────────────────┘ └─────────────────┘OWASP ZAP Implementation
# .github/workflows/zap.yml
name: OWASP ZAP DAST Scan
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * 0' # Weekly on Sunday
env:
TARGET_URL: https://staging.example.com
jobs:
zap-baseline:
name: ZAP Baseline Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: ${{ env.TARGET_URL }}
rules_file_name: 'zap-rules.tsv'
cmd_options: '-a'
- name: Upload ZAP Report
uses: actions/upload-artifact@v4
with:
name: zap-baseline-report
path: report_html.html
zap-full-scan:
name: ZAP Full Scan
runs-on: ubuntu-latest
needs: zap-baseline
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: ZAP Full Scan
uses: zaproxy/action-full-scan@v0.10.0
with:
target: ${{ env.TARGET_URL }}
rules_file_name: 'zap-rules.tsv'
cmd_options: '-a -j'
- name: Upload ZAP Report
uses: actions/upload-artifact@v4
with:
name: zap-full-report
path: report_html.html
zap-api-scan:
name: ZAP API Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: ZAP API Scan
uses: zaproxy/action-api-scan@v0.7.0
with:
target: ${{ env.TARGET_URL }}/api/openapi.json
format: openapi
cmd_options: '-a'ZAP Rules Configuration
# zap-rules.tsv
# Rule ID Alert Threshold Attack Strength
10010 IGNORE # Cookie No HttpOnly Flag (handled elsewhere)
10011 IGNORE # Cookie Without Secure Flag (dev environment)
10015 WARN # Incomplete or No Cache-control Header
10017 FAIL # Cross-Domain JavaScript Source File Inclusion
10020 FAIL # X-Frame-Options Header
10021 FAIL # X-Content-Type-Options Header
10038 FAIL # Content Security Policy Header
40012 FAIL # Cross Site Scripting (Reflected)
40014 FAIL # Cross Site Scripting (Persistent)
40018 FAIL # SQL Injection
40019 FAIL # SQL Injection (MySQL)
40020 FAIL # SQL Injection (Hypersonic)
40021 FAIL # SQL Injection (Oracle)
40022 FAIL # SQL Injection (PostgreSQL)
90019 WARN # Server Side Code Injection
90020 FAIL # Remote OS Command Injection
90023 FAIL # XML External Entity AttackNuclei for Template-Based Scanning
# .github/workflows/nuclei.yml
name: Nuclei DAST Scan
on:
schedule:
- cron: '0 3 * * 0' # Weekly
workflow_dispatch:
jobs:
nuclei-scan:
name: Nuclei Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Nuclei
uses: projectdiscovery/nuclei-action@main
with:
target: https://staging.example.com
templates: cves,vulnerabilities,misconfiguration
output: nuclei-results.txt
sarif-export: nuclei-results.sarif
flags: '-severity critical,high,medium -stats'
- name: Upload Nuclei results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: nuclei-results.sarif
- name: Upload report artifact
uses: actions/upload-artifact@v4
with:
name: nuclei-report
path: nuclei-results.txtCustom Nuclei Templates
# custom-templates/api-security.yaml
id: api-authentication-bypass
info:
name: API Authentication Bypass Check
author: security-team
severity: high
http:
- raw:
- |
GET /api/v1/admin/users HTTP/1.1
Host: {{Hostname}}
- |
GET /api/v1/admin/users HTTP/1.1
Host: {{Hostname}}
Authorization: Bearer invalid_token
- |
GET /api/v1/admin/users HTTP/1.1
Host: {{Hostname}}
Authorization: Bearer null
matchers-condition: or
matchers:
- type: status
status:
- 200
- type: word
words:
- '"users"'
- '"email"'
condition: and
---
id: sensitive-data-exposure
info:
name: Sensitive Data Exposure Check
severity: medium
http:
- method: GET
path:
- "{{BaseURL}}/api/v1/users/me"
- "{{BaseURL}}/api/v1/profile"
matchers:
- type: regex
regex:
- '"password"\\s*:'
- '"ssn"\\s*:'
- '"credit_card"\\s*:'
- '"api_key"\\s*:'Integrated Security Pipeline
Combined SCA + DAST Pipeline
# .github/workflows/security-complete.yml
name: Complete Security Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # Daily
jobs:
# Stage 1: SCA - Check dependencies
sca-scan:
name: SCA Scan
runs-on: ubuntu-latest
outputs:
vulnerabilities: ${{ steps.snyk.outputs.vulnerabilities }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Snyk SCA
id: snyk
uses: snyk/actions/node@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --json-file-output=snyk-sca.json
- name: Trivy filesystem scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'sarif'
output: 'trivy-fs.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload SCA results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-fs.sarif'
# Stage 2: Build and push to staging
build-staging:
name: Build Staging
needs: sca-scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:staging .
- name: Trivy image scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:staging'
format: 'sarif'
output: 'trivy-image.sarif'
severity: 'CRITICAL,HIGH'
- name: Push to staging
run: |
# Push to container registry
docker tag myapp:staging registry.example.com/myapp:staging
docker push registry.example.com/myapp:staging
# Stage 3: Deploy to staging
deploy-staging:
name: Deploy to Staging
needs: build-staging
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: |
# Deployment commands
kubectl set image deployment/myapp myapp=registry.example.com/myapp:staging
- name: Wait for deployment
run: kubectl rollout status deployment/myapp --timeout=300s
# Stage 4: DAST scan against staging
dast-scan:
name: DAST Scan
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: 'https://staging.example.com'
rules_file_name: 'zap-rules.tsv'
- name: Nuclei Scan
uses: projectdiscovery/nuclei-action@main
with:
target: 'https://staging.example.com'
templates: 'cves,vulnerabilities,misconfiguration'
sarif-export: nuclei.sarif
- name: Upload DAST results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: nuclei.sarif
# Stage 5: Security gate
security-gate:
name: Security Gate
needs: [sca-scan, dast-scan]
runs-on: ubuntu-latest
steps:
- name: Evaluate security posture
run: |
echo "Security checks completed"
echo "SCA: Passed"
echo "DAST: Passed"
echo "Ready for production"Vulnerability Management
Severity Classification
CVSS Score Mapping:
├── Critical (9.0-10.0)
│ ├── Action: Fix within 24 hours
│ ├── Examples: RCE, SQL injection
│ └── Block: Deployment blocked
├── High (7.0-8.9)
│ ├── Action: Fix within 7 days
│ ├── Examples: XSS, Auth bypass
│ └── Block: Deployment blocked
├── Medium (4.0-6.9)
│ ├── Action: Fix within 30 days
│ ├── Examples: Information disclosure
│ └── Block: Warning only
└── Low (0.1-3.9)
├── Action: Fix within 90 days
├── Examples: Best practice violations
└── Block: No blockVulnerability Tracking
// vulnerability-tracker.ts
interface Vulnerability {
id: string;
source: 'SCA' | 'DAST' | 'SAST';
severity: 'critical' | 'high' | 'medium' | 'low';
cvss: number;
package?: string;
version?: string;
fixedVersion?: string;
remediation: string;
discoveredAt: Date;
dueDate: Date;
status: 'open' | 'in-progress' | 'fixed' | 'accepted-risk';
}
function calculateDueDate(severity: string): Date {
const now = new Date();
const dueDays = {
critical: 1,
high: 7,
medium: 30,
low: 90
};
now.setDate(now.getDate() + dueDays[severity]);
return now;
}
async function createJiraTicket(vuln: Vulnerability): Promise<string> {
const response = await fetch('/api/jira/issues', {
method: 'POST',
body: JSON.stringify({
project: 'SEC',
issueType: 'Security Vulnerability',
summary: \\\