Section 08

Supply Chain & Third-Party Risk

Modern systems depend on hundreds of third-party components — open-source libraries, SaaS integrations, cloud services, and outsourced development. A TRA is incomplete without assessing these dependencies. This section covers SBOM analysis, vendor tiering, OSS risk scoring, and Nth-party cascading risk.

Related Tools & Sections

See CI/CD & Supply Chain for pipeline-specific attack patterns.

Supply Chain Risk Landscape

Software Supply Chain Attack Surface

flowchart LR subgraph upstream["Upstream"] OSS["Open Source\nPackages"] REG["Package\nRegistries"] VENDOR["Commercial\nVendors"] end subgraph build["Build & Deploy"] CI["CI/CD\nPipeline"] ART["Artifact\nRegistry"] IAC["IaC\nTemplates"] end subgraph runtime["Runtime"] APP["Application"] SAAS["SaaS\nIntegrations"] API["Third-Party\nAPIs"] end OSS -->|"dependency"| CI REG -->|"pull"| CI VENDOR -->|"SDK/agent"| CI CI -->|"artifact"| ART IAC -->|"deploy"| APP ART -->|"deploy"| APP APP -->|"integrate"| SAAS APP -->|"call"| API style OSS fill:#ff8800,stroke:#000,color:#000 style CI fill:#22d3ee,stroke:#000,color:#000 style APP fill:#a855f7,stroke:#000,color:#000

SBOM Analysis

A Software Bill of Materials (SBOM) is the foundation of supply chain risk assessment. Two major standards exist — use both for comprehensive coverage.

SPDX (Linux Foundation)

  • • ISO/IEC 5962:2021 standard
  • • Focus: license compliance + security
  • • Formats: SPDX, JSON, RDF, YAML
  • • Strong license expression support
  • • Used by: Linux kernel, NTIA guidance

CycloneDX (OWASP)

  • • OWASP flagship project
  • • Focus: security + vulnerability tracking
  • • Formats: XML, JSON, Protocol Buffers
  • • Built-in VEX (Vulnerability Exploitability eXchange)
  • • Used by: OWASP, DHS CISA, commercial tools
sbom_risk_analyzer.py
python
# SBOM Risk Analysis Script
# Analyzes CycloneDX SBOM for supply chain risk indicators

import json
from dataclasses import dataclass, field
from datetime import datetime, timezone


@dataclass
class DependencyRisk:
    name: str
    version: str
    risk_score: float = 0.0
    risk_factors: list = field(default_factory=list)


def analyze_sbom(sbom_path: str) -> list[DependencyRisk]:
    """Analyze SBOM for supply chain risk indicators."""
    with open(sbom_path, "r") as f:
        sbom = json.load(f)

    findings = []
    components = sbom.get("components", [])

    for comp in components:
        risk = DependencyRisk(
            name=comp.get("name", "unknown"),
            version=comp.get("version", "unknown"),
        )

        # Check for known vulnerabilities
        vulns = comp.get("vulnerabilities", [])
        if vulns:
            critical = sum(
                1 for v in vulns
                if v.get("severity", "").lower() == "critical"
            )
            risk.risk_score += critical * 25 + len(vulns) * 5
            risk.risk_factors.append(
                f"{len(vulns)} known vulns ({critical} critical)"
            )

        # Check for outdated components
        licenses = comp.get("licenses", [])
        if not licenses:
            risk.risk_score += 10
            risk.risk_factors.append("No license declared")
        else:
            for lic in licenses:
                license_id = lic.get("license", {}).get("id", "")
                if license_id in ("AGPL-3.0", "GPL-3.0-only"):
                    risk.risk_score += 15
                    risk.risk_factors.append(
                        f"Copyleft license: {license_id}"
                    )

        # Check supplier information
        supplier = comp.get("supplier", {})
        if not supplier:
            risk.risk_score += 10
            risk.risk_factors.append("No supplier information")

        # Flag components without hashes
        hashes = comp.get("hashes", [])
        if not hashes:
            risk.risk_score += 15
            risk.risk_factors.append("No integrity hashes")

        if risk.risk_factors:
            findings.append(risk)

    # Sort by risk score descending
    findings.sort(key=lambda r: r.risk_score, reverse=True)
    return findings


def generate_risk_report(findings: list[DependencyRisk]) -> str:
    """Generate supply chain risk summary report."""
    lines = ["Supply Chain Risk Analysis Report"]
    lines.append("=" * 40)
    lines.append(
        f"Analysis Date: "
        f"{datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}"
    )
    lines.append(f"Components Analyzed: {len(findings)} with findings")
    lines.append("")

    high_risk = [f for f in findings if f.risk_score >= 50]
    medium_risk = [f for f in findings if 20 <= f.risk_score < 50]
    low_risk = [f for f in findings if f.risk_score < 20]

    lines.append(f"HIGH RISK:   {len(high_risk)} components")
    lines.append(f"MEDIUM RISK: {len(medium_risk)} components")
    lines.append(f"LOW RISK:    {len(low_risk)} components")

    for risk_level, items in [
        ("HIGH", high_risk),
        ("MEDIUM", medium_risk),
    ]:
        if items:
            lines.append(f"\n--- {risk_level} RISK ---")
            for item in items[:10]:
                lines.append(
                    f"  {item.name}@{item.version} "
                    f"(score: {item.risk_score})"
                )
                for factor in item.risk_factors:
                    lines.append(f"    - {factor}")

    return "\n".join(lines)
# SBOM Risk Analysis Script
# Analyzes CycloneDX SBOM for supply chain risk indicators

import json
from dataclasses import dataclass, field
from datetime import datetime, timezone


@dataclass
class DependencyRisk:
    name: str
    version: str
    risk_score: float = 0.0
    risk_factors: list = field(default_factory=list)


def analyze_sbom(sbom_path: str) -> list[DependencyRisk]:
    """Analyze SBOM for supply chain risk indicators."""
    with open(sbom_path, "r") as f:
        sbom = json.load(f)

    findings = []
    components = sbom.get("components", [])

    for comp in components:
        risk = DependencyRisk(
            name=comp.get("name", "unknown"),
            version=comp.get("version", "unknown"),
        )

        # Check for known vulnerabilities
        vulns = comp.get("vulnerabilities", [])
        if vulns:
            critical = sum(
                1 for v in vulns
                if v.get("severity", "").lower() == "critical"
            )
            risk.risk_score += critical * 25 + len(vulns) * 5
            risk.risk_factors.append(
                f"{len(vulns)} known vulns ({critical} critical)"
            )

        # Check for outdated components
        licenses = comp.get("licenses", [])
        if not licenses:
            risk.risk_score += 10
            risk.risk_factors.append("No license declared")
        else:
            for lic in licenses:
                license_id = lic.get("license", {}).get("id", "")
                if license_id in ("AGPL-3.0", "GPL-3.0-only"):
                    risk.risk_score += 15
                    risk.risk_factors.append(
                        f"Copyleft license: {license_id}"
                    )

        # Check supplier information
        supplier = comp.get("supplier", {})
        if not supplier:
            risk.risk_score += 10
            risk.risk_factors.append("No supplier information")

        # Flag components without hashes
        hashes = comp.get("hashes", [])
        if not hashes:
            risk.risk_score += 15
            risk.risk_factors.append("No integrity hashes")

        if risk.risk_factors:
            findings.append(risk)

    # Sort by risk score descending
    findings.sort(key=lambda r: r.risk_score, reverse=True)
    return findings


def generate_risk_report(findings: list[DependencyRisk]) -> str:
    """Generate supply chain risk summary report."""
    lines = ["Supply Chain Risk Analysis Report"]
    lines.append("=" * 40)
    lines.append(
        f"Analysis Date: "
        f"{datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}"
    )
    lines.append(f"Components Analyzed: {len(findings)} with findings")
    lines.append("")

    high_risk = [f for f in findings if f.risk_score >= 50]
    medium_risk = [f for f in findings if 20 <= f.risk_score < 50]
    low_risk = [f for f in findings if f.risk_score < 20]

    lines.append(f"HIGH RISK:   {len(high_risk)} components")
    lines.append(f"MEDIUM RISK: {len(medium_risk)} components")
    lines.append(f"LOW RISK:    {len(low_risk)} components")

    for risk_level, items in [
        ("HIGH", high_risk),
        ("MEDIUM", medium_risk),
    ]:
        if items:
            lines.append(f"\n--- {risk_level} RISK ---")
            for item in items[:10]:
                lines.append(
                    f"  {item.name}@{item.version} "
                    f"(score: {item.risk_score})"
                )
                for factor in item.risk_factors:
                    lines.append(f"    - {factor}")

    return "\n".join(lines)

NIST SSDF (SP 800-218)

The Secure Software Development Framework defines practices that software producers should follow. Use SSDF as an assessment checklist when evaluating vendor security maturity.

Practice Group Focus Key Assessment Questions
PO — Prepare the Organization Security governance, roles, tooling Does the vendor have a documented SDLC? Security team? Training program?
PS — Protect the Software Code integrity, access controls, artifact security Are builds reproducible? Artifacts signed? Source access controlled?
PW — Produce Well-Secured Software Secure design, code review, testing Threat modeling done? SAST/DAST in pipeline? Dependency scanning?
RV — Respond to Vulnerabilities Vulnerability disclosure, patching, communication SLAs for critical patches? Public disclosure policy? CVE assignment?

Vendor Risk Tiering

Not all vendors require the same assessment depth. Tier vendors based on data access, integration depth, and business criticality to allocate assessment effort efficiently.

Tier 1 — Critical (Full Assessment)

Vendors with direct access to sensitive data or critical business processes.

  • Criteria: Processes PII/PHI/PCI data, has network access, single point of failure
  • Assessment: Full security questionnaire (SIG/CAIQ), SOC 2 Type II review, pentest results, on-site audit, SBOM review
  • Review frequency: Annual + triggered by incidents
  • Examples: Cloud infrastructure (AWS/Azure), payment processor, HRIS, EHR system

Tier 2 — High (Standard Assessment)

Vendors with limited data access or non-critical integrations.

  • Criteria: Internal data access, API integration, indirect customer impact
  • Assessment: Security questionnaire, SOC 2 report review, contract review
  • Review frequency: Annual
  • Examples: Project management SaaS, CI/CD platform, monitoring tools

Tier 3 — Standard (Light Assessment)

Vendors with no data access or minimal integration.

  • Criteria: No sensitive data, website-only tools, replaceable
  • Assessment: Self-attestation, public security page review, contract clauses
  • Review frequency: Biennial or on renewal
  • Examples: Marketing analytics, design tools, office supplies

OSS Criticality Scoring

Open-source dependencies require different risk assessment than commercial vendors. Evaluate them on health metrics rather than questionnaires.

Risk Factor Low Risk Medium Risk High Risk
Maintainer count 5+ active maintainers 2–4 maintainers Single maintainer
Last commit < 30 days 30–180 days > 180 days
Security policy SECURITY.md + disclosure process Issue-based reporting No security policy
OpenSSF Scorecard Score ≥ 7/10 Score 4–6/10 Score < 4/10 or N/A
Signed releases GPG/Sigstore signed Checksum only No verification

Nth-Party Cascading Risk

Your vendors also have vendors (4th parties), creating cascading dependency chains. A breach at a 4th party can propagate through your entire supply chain.

Nth-Party Risk Cascade

flowchart TD YOU["Your Organization"] --> V1["Tier 1 Vendor\n(Direct Contract)"] YOU --> V2["Tier 2 Vendor\n(API Integration)"] V1 --> V1A["4th Party:\nCloud Provider"] V1 --> V1B["4th Party:\nSaaS Dependency"] V2 --> V2A["4th Party:\nData Processor"] V1A --> V1AA["5th Party:\nCDN / DNS"] V1B --> V1BA["5th Party:\nAuth Provider"] V2A --> V2AA["5th Party:\nInfra Provider"] style YOU fill:#ff8800,stroke:#000,color:#000 style V1 fill:#22d3ee,stroke:#000,color:#000 style V2 fill:#22d3ee,stroke:#000,color:#000 style V1A fill:#a855f7,stroke:#000,color:#000 style V1B fill:#a855f7,stroke:#000,color:#000 style V2A fill:#a855f7,stroke:#000,color:#000 style V1AA fill:#ec4899,stroke:#000,color:#000 style V1BA fill:#ec4899,stroke:#000,color:#000 style V2AA fill:#ec4899,stroke:#000,color:#000

Concentration Risk

When multiple vendors depend on the same 4th party (e.g., AWS us-east-1, Cloudflare, or Okta), you have concentration risk. A single 4th-party outage or breach impacts multiple vendor relationships simultaneously. Map these shared dependencies during your TRA.

SaaS Risk Assessment

saas-risk-checklist.txt
text
SaaS Vendor Risk Assessment Checklist
═════════════════════════════════════

DATA SECURITY
  □ Where is data stored? (region, jurisdiction)
  □ Encryption at rest (algorithm, key management)
  □ Encryption in transit (TLS version, cipher suites)
  □ Data isolation model (single-tenant vs multi-tenant)
  □ Data retention and deletion policies
  □ Right to audit clause in contract

ACCESS CONTROL
  □ SSO/SAML/OIDC integration available?
  □ SCIM provisioning supported?
  □ IP allowlisting capability
  □ Role-based access control granularity
  □ MFA enforcement options
  □ Session management controls

INCIDENT RESPONSE
  □ Documented incident response plan?
  □ Breach notification SLA (e.g., 72 hours for GDPR)
  □ Customer communication process
  □ Post-incident report provided?
  □ Insurance coverage

COMPLIANCE & ASSURANCE
  □ SOC 2 Type II report (current period)
  □ ISO 27001 certification (current)
  □ Penetration test results (redacted OK)
  □ GDPR / CCPA compliance attestation
  □ Industry-specific (PCI, HIPAA, FedRAMP)

EXIT STRATEGY
  □ Data export format and mechanism
  □ Data deletion confirmation upon termination
  □ Transition assistance period
  □ API data extraction capability
  □ Contract termination notice period
SaaS Vendor Risk Assessment Checklist
═════════════════════════════════════

DATA SECURITY
  □ Where is data stored? (region, jurisdiction)
  □ Encryption at rest (algorithm, key management)
  □ Encryption in transit (TLS version, cipher suites)
  □ Data isolation model (single-tenant vs multi-tenant)
  □ Data retention and deletion policies
  □ Right to audit clause in contract

ACCESS CONTROL
  □ SSO/SAML/OIDC integration available?
  □ SCIM provisioning supported?
  □ IP allowlisting capability
  □ Role-based access control granularity
  □ MFA enforcement options
  □ Session management controls

INCIDENT RESPONSE
  □ Documented incident response plan?
  □ Breach notification SLA (e.g., 72 hours for GDPR)
  □ Customer communication process
  □ Post-incident report provided?
  □ Insurance coverage

COMPLIANCE & ASSURANCE
  □ SOC 2 Type II report (current period)
  □ ISO 27001 certification (current)
  □ Penetration test results (redacted OK)
  □ GDPR / CCPA compliance attestation
  □ Industry-specific (PCI, HIPAA, FedRAMP)

EXIT STRATEGY
  □ Data export format and mechanism
  □ Data deletion confirmation upon termination
  □ Transition assistance period
  □ API data extraction capability
  □ Contract termination notice period

Section Summary

Key Takeaways

  • • SBOMs (SPDX/CycloneDX) are foundational for dependency risk analysis
  • • NIST SSDF provides a vendor security maturity assessment framework
  • • Tier vendors by data access and criticality to allocate assessment effort
  • • OSS dependencies need health metric evaluation, not questionnaires
  • • Map Nth-party dependencies to identify concentration risk

Next Steps