Skip to main content
Back to Blog
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 vulns

SCA Tools Comparison

ToolFree TierLanguagesCI IntegrationLicense Detection
SnykLimited15+ExcellentYes
DependabotFull10+GitHub nativeLimited
OWASP Dep-CheckFull8+GoodNo
TrivyFull10+ExcellentYes
RenovateFull20+ExcellentNo

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=medium

Trivy 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.txt

Trivy 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 Attack

Nuclei 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.txt

Custom 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 block

Vulnerability 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: \\\

Share this article