Skip to main content
โšก Calmops

Supply Chain Security: SBOM, Dependency Scanning, and Software Security

Supply Chain Security: SBOM, Dependency Scanning, and Software Security

TL;DR: This guide covers securing your software supply chain. Learn to generate SBOMs, scan dependencies for vulnerabilities, implement software signing, and prevent supply chain attacks.


Introduction

Software supply chain attacks have increased 300% in recent years. Attackers target:

  • Dependencies - Compromise upstream packages
  • Build systems - Inject malicious code during build
  • Package repositories - Upload malicious packages
  • Development tools - Compromise IDEs and tooling

A comprehensive supply chain security program includes:

  1. SBOM generation - Know what’s in your software
  2. Dependency scanning - Find vulnerable components
  3. Software signing - Verify authenticity
  4. Secure build pipelines - Prevent tampering

Software Bill of Materials (SBOM)

SBOM Formats

Format Description Tools
SPDX Linux Foundation standard spdx-builder
CycloneDX OWASP standard cyclonedx
SWID ISO standard swidtools

Generating SBOMs

Python SBOM with CycloneDX

# Install cyclonedx-bom
pip install cyclonedx-bom

# Generate SBOM
cyclonedx-py -r -o bom.xml

Node.js SBOM

# Using npm
npm install -g @cyclonedx/bom

# Generate
cyclonedx-npm -o bom.xml

SBOM Generation Code

from cyclonedx.model import (
    Bom,
    Component,
    ComponentType,
    Dependency,
    Property
)
from cyclonedx.parser import Parser
from cyclonedx.writer import Writer

class SBOMGenerator:
    def __init__(self, project_path: str):
        self.project_path = project_path
        self.components = []
        
    def generate_sbom(self, output_format: str = 'json') -> Bom:
        """Generate SBOM for the project"""
        bom = Bom()
        
        # Add main component
        main_component = Component(
            name='my-application',
            version='1.0.0',
            component_type=ComponentType.APPLICATION
        )
        bom.components.append(main_component)
        
        # Add dependencies
        dependencies = self._scan_dependencies()
        for dep in dependencies:
            component = Component(
                name=dep['name'],
                version=dep['version'],
                component_type=ComponentType.LIBRARY,
                purl=dep['purl']
            )
            bom.components.append(component)
            bom.dependencies.append(Dependency(ref=dep['purl']))
        
        return bom
    
    def _scan_dependencies(self) -> list:
        """Scan project dependencies"""
        # Implementation for different package managers
        return []
    
    def export(self, bom: Bom, format: str, filename: str):
        """Export SBOM to file"""
        writer = Writer()
        writer.write(bom, filename, format)

Dependency Vulnerability Scanning

Trivy for Container Scanning

# Scan container image
trivy image --security-checks vuln,config myapp:latest

# Scan filesystem
trivy fs --security-checks vuln,secret /path/to/project

# Scan Git repository
trivy repo --security-checks vuln https://github.com/user/repo

Python Dependency Scanning

# Using safety
pip install safety
safety check

# Using pip-audit
pip install pip-audit
pip-audit

Dependency Scan Integration

import subprocess
import json

class DependencyScanner:
    def __init__(self, language: str):
        self.language = language
        
    def scan(self, project_path: str) -> list:
        """Scan dependencies for vulnerabilities"""
        if self.language == 'python':
            return self._scan_python(project_path)
        elif self.language == 'javascript':
            return self._scan_javascript(project_path)
        elif self.language == 'go':
            return self._scan_go(project_path)
        return []
    
    def _scan_python(self, project_path: str) -> list:
        """Scan Python dependencies"""
        result = subprocess.run(
            ['pip-audit', '--format', 'json'],
            capture_output=True,
            cwd=project_path
        )
        
        if result.returncode == 0:
            data = json.loads(result.stdout)
            return self._parse_pip_audit(data)
        return []
    
    def _parse_pip_audit(self, data: dict) -> list:
        """Parse pip-audit results"""
        vulnerabilities = []
        
        for dep in data.get('dependencies', []):
            for vuln in dep.get('vulns', []):
                vulnerabilities.append({
                    'package': dep['name'],
                    'version': dep['version'],
                    'vulnerability': vuln['id'],
                    'severity': vuln['severity'],
                    'description': vuln.get('description', '')
                })
        
        return vulnerabilities
    
    def _scan_javascript(self, project_path: str) -> list:
        """Scan JavaScript dependencies"""
        result = subprocess.run(
            ['npm', 'audit', '--json'],
            capture_output=True,
            cwd=project_path
        )
        
        data = json.loads(result.stdout)
        # Parse npm audit results
        return []
    
    def _scan_go(self, project_path: str) -> list:
        """Scan Go dependencies"""
        result = subprocess.run(
            ['govulncheck', './...'],
            capture_output=True,
            cwd=project_path
        )
        return []

Software Signing

Sigstore Integration

# Install cosign
cosign generate-key-pair

# Sign container image
cosign sign myregistry/myimage:latest

# Verify signature
cosign verify myregistry/myimage:latest

Signing Artifacts

import subprocess

class SoftwareSigner:
    def __init__(self, key_path: str):
        self.key_path = key_path
        
    def sign_file(self, file_path: str) -> str:
        """Sign a file using cosign"""
        result = subprocess.run(
            ['cosign', 'sign-blob', '--key', self.key_path, file_path],
            capture_output=True,
            text=True
        )
        
        return result.stdout.strip()
    
    def verify_signature(self, file_path: str, cert_path: str) -> bool:
        """Verify file signature"""
        result = subprocess.run(
            ['cosign', 'verify-blob', '--key', cert_path, file_path],
            capture_output=True
        )
        
        return result.returncode == 0

Secure Build Pipelines

GitHub Actions Workflow

name: Secure Build

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run Trivy scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'
          
      - name: Upload results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'
      
      - name: Dependency Review
        uses: actions/dependency-review-action@v3
      
      - name: SBOM Generation
        uses:CycloneDX/gh-action-create-sbom@v1

Build Integrity Verification

class BuildIntegrity:
    def __init__(self):
        self.measurements = []
        
    def add_measurement(self, step: str, hash: str):
        """Add build step measurement"""
        self.measurements.append({
            'step': step,
            'hash': hash,
            'timestamp': datetime.utcnow()
        })
    
    def generate_provenance(self) -> dict:
        """Generate build provenance"""
        return {
            'buildType': 'https://buildtools.google.com/build#generic',
            'invocation': {
                'configuration': {...}
            },
            'runDetails': {
                'builder': {...},
                'buildNumber': '123'
            },
            'materials': [...],
            'runEntries': self.measurements
        }

Package Repository Security

Private Registry Configuration

# requirements.txt with pinned versions
requests==2.31.0
flask==3.0.0
numpy==1.26.0

Verify Package Integrity

import hashlib
import subprocess

class PackageVerifier:
    def __init__(self):
        self.trusted_hash = 'abc123...'  # Pre-configured trusted hash
        
    def verify_package(self, package_path: str) -> bool:
        """Verify package integrity"""
        with open(package_path, 'rb') as f:
            package_hash = hashlib.sha256(f.read()).hexdigest()
        
        return package_hash == self.trusted_hash
    
    def verify_with_pgp(self, package_path: str, signature_path: str) -> bool:
        """Verify package with PGP signature"""
        result = subprocess.run(
            ['gpg', '--verify', signature_path, package_path],
            capture_output=True
        )
        
        return result.returncode == 0

Conclusion

Supply chain security requires defense in depth:

  1. SBOM generation - Know all components in your software
  2. Continuous scanning - Find vulnerabilities in dependencies
  3. Software signing - Verify authenticity and integrity
  4. Secure pipelines - Prevent build-time tampering
  5. Package verification - Validate packages from registries

External Resources


Comments