Back to KB
Difficulty
Intermediate
Read Time
8 min

Dependency Vulnerability Scanning

By Codcompass Team¡¡8 min read

Dependency Vulnerability Scanning

Current Situation Analysis

Modern software supply chains are overwhelmingly composed of third-party code. Industry telemetry consistently shows that 80% to 90% of a typical application's codebase originates from external package registries, open-source repositories, or internal shared libraries. Dependency vulnerability scanning—often categorized under Software Composition Analysis (SCA)—is the practice of identifying known security flaws, license violations, and malicious artifacts within these external components.

Despite its criticality, SCA remains systematically mismanaged across development organizations. The primary pain point is not tool scarcity; it is operational friction. Teams treat dependency scanning as a periodic compliance checkbox rather than a continuous engineering practice. This happens for three structural reasons:

  1. CI/CD Pipeline Friction: Scanners that run synchronously in pull requests often add 3–8 minutes to build times. Engineers bypass gates or disable checks to maintain deployment velocity.
  2. Alert Fatigue & False Positives: Vulnerability databases (NVD, OSV, GitHub Advisories) contain noisy entries. Many reported CVEs affect only specific execution paths, require non-default configurations, or are mitigated by runtime environment controls. Without risk-based filtering, teams drown in low-signal alerts.
  3. Ownership Ambiguity: Security teams own the scanner, platform teams own the CI, and engineering teams own the code. When a vulnerability is flagged, no single group is accountable for triage, exception management, or remediation.

Data confirms the operational gap. The Snyk 2023 State of Open Source Security Report found that 74% of organizations experienced a production incident directly traceable to a vulnerable dependency. GitHub's 2023 Octoverse data indicates the average mean time to remediate (MTTR) for known CVEs exceeds 200 days. Sonatype's supply chain telemetry shows a 650% year-over-year increase in dependency poisoning attempts, including typosquatting, dependency confusion, and compromised maintainer accounts. The industry has shifted left in theory but remains reactive in practice.

WOW Moment: Key Findings

The difference between reporting vulnerabilities and engineering secure supply chains lies in policy enforcement and artifact correlation. Organizations that couple scanning with Software Bill of Materials (SBOM) generation and automated policy gates see exponential reductions in exposure window and remediation cost.

ApproachMTTD (days)False Positive RateAvg Remediation Cost
Manual/Ad-hoc180-24015-25%$15,000-$25,000
CI-Integrated (No Policy)30-4540-55%$8,000-$12,000
CI-Integrated + Policy + SBOM3-78-12%$1,500-$3,000

This finding matters because it decouples scanning from dashboard consumption. Manual or ad-hoc scanning relies on human review, which scales linearly and degrades under alert volume. CI integration without policy gates produces noise that engineers ignore. Adding policy enforcement transforms scanning from a reporting mechanism into a deterministic gate. SBOM correlation ensures findings are tied to exact build artifacts, enabling precise rollback, patch targeting, and compliance auditing. The cost delta reflects reduced incident response overhead, fewer emergency hotfixes, and lower engineering burnout from false positives.

Core Solution

A production-grade dependency scanning pipeline requires four components: artifact generation, vulnerability evaluation, policy enforcement, and remediation routing. The following architecture uses open-source tooling to maintain auditability and avoid vendor lock-in.

Architecture Decisions

  1. SBOM-First Scanning: Direct lockfile parsing (package-lock.json, yarn.lock, go.sum) is fast but loses context. Generating an SPDX or CycloneDX SBOM before scanning preserves exact version pins, transitive relationships, and build metadata. SBOMs also satisfy emerging regulatory requirements (EO 14028, EU CRA).
  2. Policy-as-Code Gates: Hardcoded severity thresholds fail in practice. A policy engine evaluates scan results against contextual rules (e.g., allow CVSS < 7.0 if no public exploit, block if package is unmaintained, permit exceptions via signed allowlist).
  3. Asynchronous Reporting: Fail the PR only on actionable findings. Post-merge, push full results to a centralized database for trend analysis, compliance reporting, and automated remediation PR generation.
  4. TypeScript Integration Layer: Engineers maintain tooling in their primary language. A lightweight TypeScript parser standardizes output, applies risk scoring, and interfaces with CI orchestration.

Step-by-Step Implementation

1. Generate SBOM

Use syft to create a CycloneDX SBOM from your build artifact or source tree:

syft dir:. -o cyclonedx-json > sbom.cdx.json

2. Scan SBOM

Run trivy against the SBOM. Trivy queries multiple vulnerability databases (NVD, GitHub Advisories, OSV) and returns structured JSON:

trivy sbom ./sbom.cdx.json --format json --output trivy-results.json

3. Policy Evaluation & TypeScript Parser

Create a deterministic gate that parses results, applies risk filters, and exits with appropriate status codes.

// scan-policy.ts
import { readFileSync } from "fs";
import { exit } from "process";

interface TrivyResult {
  Results: Array<{
    Target: string;
    Vulnerabilities?: Array<{
      Severity: string;
      CVSS?: Record<string, { V3Score?: number }>;
      VulnerabilityID: string;
      PkgName: string;
      InstalledVersion: string;
      FixedVersion?: string;
      Title: string;
    }>;
  }>;
}

interface PolicyConfig {
  maxSeverity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
  allowlist: string[];
  requireFix: boolean;
}

function evaluateScan(results: TrivyResult, policy: PolicyConfig): number {
  const severityRank: Record<string, num

ber> = { UNKNOWN: 0, LOW: 1, MEDIUM: 2, HIGH: 3, CRITICAL: 4, };

const threshold = severityRank[policy.maxSeverity]; let exitCode = 0;

for (const target of results.Results) { if (!target.Vulnerabilities) continue;

for (const vuln of target.Vulnerabilities) {
  if (policy.allowlist.includes(vuln.VulnerabilityID)) continue;

  const score = severityRank[vuln.Severity] ?? 0;
  const cvss3 = vuln.CVSS?.nvd?.V3Score ?? 0;
  const isExploitable = cvss3 >= 7.0 || vuln.Severity === "CRITICAL";

  if (score >= threshold) {
    if (policy.requireFix && !vuln.FixedVersion) {
      console.error(`[BLOCKED] ${vuln.VulnerabilityID} in ${vuln.PkgName}@${vuln.InstalledVersion}: No fix available`);
      exitCode = 1;
    } else if (isExploitable) {
      console.error(`[BLOCKED] ${vuln.VulnerabilityID} in ${vuln.PkgName}: CVSS ${cvss3} exceeds threshold`);
      exitCode = 1;
    } else {
      console.warn(`[WARN] ${vuln.VulnerabilityID} in ${vuln.PkgName}@${vuln.InstalledVersion}`);
    }
  }
}

}

return exitCode; }

const scanResults = JSON.parse(readFileSync("trivy-results.json", "utf-8")) as TrivyResult; const policy: PolicyConfig = { maxSeverity: "HIGH", allowlist: ["CVE-2023-44487"], // Example: HTTP/2 rapid reset (mitigated at infra) requireFix: true, };

exit(evaluateScan(scanResults, policy));


#### 4. CI Orchestration
Chain the steps in your pipeline. Fail on `exitCode !== 0`. Archive SBOM and scan results for audit trails.

```yaml
# .github/workflows/dependency-scan.yml
name: Dependency Vulnerability Scan
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: |
          curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.50.0
          curl -sfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
      - run: syft dir:. -o cyclonedx-json > sbom.cdx.json
      - run: trivy sbom ./sbom.cdx.json --format json --output trivy-results.json
      - run: npx ts-node scan-policy.ts
      - uses: actions/upload-artifact@v4
        with:
          name: supply-chain-artifacts
          path: |
            sbom.cdx.json
            trivy-results.json

This architecture decouples detection from enforcement. The scanner remains stateless. Policy lives in code. Artifacts are versioned. Engineers receive deterministic feedback. Security teams retain auditability.

Pitfall Guide

  1. Scanning Only Direct Dependencies Lockfile parsers often miss transitive packages or fail to resolve deduplicated versions. Always scan the final build artifact or generated SBOM. Transitive dependencies account for ~70% of exploited vulnerabilities.

  2. Treating CVSS Scores as Absolute Truth CVSS measures theoretical exploitability, not runtime risk. A CVSS 9.8 vulnerability in a build-time-only tool or a disabled module poses zero production risk. Implement context-aware scoring that factors execution environment, network exposure, and mitigation controls.

  3. No Exception/Allowlist Process Hard blocking every finding breaks CI. Maintain a signed, time-bound allowlist with documented risk acceptance. Rotate exceptions quarterly. Auto-expire allowances tied to planned remediation sprints.

  4. Scanning Only on Merge to Main Vulnerabilities introduced in feature branches accumulate until integration. Run lightweight scans on pull requests. Defer full SBOM generation and policy evaluation to main branch pushes to balance speed and coverage.

  5. Ignoring License Compliance Alongside Security Dependency scanning without license evaluation creates legal exposure. Pair security scans with license classification (MIT, Apache-2.0, GPL-3.0, SSPL). Enforce organizational license policies in the same gate.

  6. Storing Scan Results Without Version Correlation Dashboards that show "current vulnerabilities" lose historical context. Store results alongside commit SHA, SBOM hash, and build ID. Enable trend analysis, rollback validation, and compliance reporting.

  7. Relying Solely on SaaS Dashboards Vendor platforms abstract away policy logic and create data silos. Keep scan engines open-source. Export results to your internal database. Maintain control over thresholds, exceptions, and remediation routing.

Best Practice: Implement a risk-based triage matrix. Classify findings by exploit availability, affected component type (runtime vs build-time), network exposure, and fix availability. Route high-confidence, actionable findings to automated remediation PRs. Route ambiguous findings to security review queues.

Production Bundle

Action Checklist

  • Generate CycloneDX SBOM for every build artifact using syft or cyclonedx-cli
  • Run trivy or grype against the SBOM, not raw lockfiles
  • Implement a TypeScript or policy-as-code gate that filters by severity, CVSS, and allowlist
  • Configure CI to fail only on actionable findings; warn on informational items
  • Archive SBOM and scan results with commit SHA and build ID for audit trails
  • Establish a quarterly exception review process with documented risk acceptance
  • Integrate license classification into the same scanning gate
  • Route high-confidence findings to automated dependency update PRs

Decision Matrix

ScenarioRecommended ApproachWhyCost Impact
Early-stage startup (1-5 devs)CLI scanning in PR with hardcoded severity gateMinimal overhead, immediate visibility, no platform debtLow (engineering hours only)
Mid-size team (10-50 devs)CI-integrated scanning + SBOM + allowlist policyBalances speed with compliance, reduces alert fatigueMedium (pipeline maintenance + triage)
Enterprise/regulatedFull SCA platform + policy-as-code + SBOM versioning + automated remediationMeets audit requirements, scales across hundreds of repos, enables supply chain attestationHigh (platform licensing + dedicated SRE/security)
Open-source projectPublic vulnerability scanning + automated dependabot/renovate PRsCommunity trust, transparent security posture, low maintenanceLow (automation-driven)
Legacy monolith with 10k+ depsPhased rollout: scan build artifacts first, then lockfiles, then enforce policyPrevents CI paralysis, enables incremental remediationMedium-High (remediation sprint allocation)

Configuration Template

trivy-config.yaml

severity:
  - HIGH
  - CRITICAL
ignore-unfixed: false
exit-code: 1
format: json
output: trivy-results.json
db:
  skip-update: false
  light: true
scan:
  security-checks: vuln,license
  timeout: 5m

.github/workflows/dependency-scan.yml (Production-ready)

name: Dependency Vulnerability Scan
on:
  pull_request:
    branches: [main, release/**]
  push:
    branches: [main]

jobs:
  supply-chain-scan:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci --ignore-scripts
      - name: Install scanners
        run: |
          curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.50.0
          curl -sfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
      - name: Generate SBOM
        run: syft dir:. -o cyclonedx-json > sbom.cdx.json
      - name: Scan vulnerabilities
        run: trivy sbom ./sbom.cdx.json --config trivy-config.json
      - name: Evaluate policy
        run: npx ts-node --esm scan-policy.ts
      - name: Upload artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: supply-chain-${{ github.sha }}
          path: |
            sbom.cdx.json
            trivy-results.json
          retention-days: 90

Quick Start Guide

  1. Install scanners: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin and curl -sfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
  2. Generate SBOM: syft dir:. -o cyclonedx-json > sbom.cdx.json
  3. Run scan: trivy sbom ./sbom.cdx.json --format json --output trivy-results.json
  4. Evaluate with policy: npx ts-node scan-policy.ts (exits 0 on pass, 1 on block)
  5. Add to CI: Copy the workflow template, commit, and verify PR status checks pass/fail deterministically

Dependency vulnerability scanning is not a tooling problem. It is an engineering discipline. Treat dependencies as first-class citizens in your build pipeline, enforce policy through code, and correlate findings with immutable artifacts. The supply chain will not secure itself, but your pipeline can.

Sources

  • • ai-generated