SBOM and Vulnerability Scanning
What is SBOM (Software Bill of Materials)?
SBOM (Software Bill of Materials) is an inventory of all components, libraries, and dependencies that make up a piece of software — essentially a "parts list" for software.
Similar to a manufacturing BOM, an SBOM makes the "ingredients" of your software explicit.
Application
├── Framework (e.g., ASP.NET Core 9.0)
├── Library A v1.2.3
│ └── Transitive dependency A1 v0.5.0
├── Library B v2.0.1
│ ├── Transitive dependency B1 v1.0.0
│ └── Transitive dependency B2 v3.1.2
└── ...(dozens to hundreds of components)
Major SBOM Formats
| Format | Owner | Characteristics |
|---|---|---|
| CycloneDX | OWASP | Security-focused, lightweight |
| SPDX | Linux Foundation | Strong license management support |
| SWID | ISO/IEC 19770-2 | Primarily for enterprise asset management |
In today's security context, CycloneDX and SPDX are the dominant formats.
The Relationship Between SBOM and Security
Why SBOM Matters
In May 2021, US President Biden issued Executive Order 14028 on Improving the Nation's Cybersecurity, which mandated SBOM provision for software sold to federal agencies. This sparked global interest in SBOMs.
Lessons from Log4Shell (CVE-2021-44228)
The Log4Shell vulnerability discovered in late 2021 is a classic example that demonstrates the importance of SBOMs.
- Impact scope: Virtually all Java-based applications
- The problem:
log4jwas often included as a transitive dependency, making it difficult to track - With SBOM: Affected applications can be identified in minutes
- Without SBOM: Manual investigation of every service may take weeks
SCA (Software Composition Analysis)
SCA (Software Composition Analysis) is the technology for generating SBOMs and automatically detecting vulnerabilities in dependencies.
Major Vulnerability Databases
| Database | URL | Characteristics |
|---|---|---|
| NVD | nvd.nist.gov | US government-managed, CVE-numbered |
| OSV | osv.dev | Open source vulnerability focused |
| GitHub Advisories | github.com/advisories | GitHub-managed, easy SCA integration |
| JVNDB | jvndb.jvn.jp | Japan IPA-managed |
.NET Vulnerability Scanning
dotnet list package --vulnerable
A built-in vulnerability check feature in the .NET CLI.
# Check direct dependencies for vulnerabilities
dotnet list package --vulnerable
# Check including transitive dependencies (recommended)
dotnet list package --vulnerable --include-transitive
# Example output
# Project `MyApp` has the following vulnerable packages
# [net9.0]:
# Top-level Package Requested Resolved Severity Advisory URL
# > Newtonsoft.Json 13.0.1 13.0.1 High https://github.com/...
SBOM Generation with CycloneDX
# Install CycloneDX .NET tool
dotnet tool install --global CycloneDX
# Generate SBOM (CycloneDX JSON format)
dotnet CycloneDX MyProject.csproj -o ./sbom -j
# Generate SBOM (SPDX format)
dotnet CycloneDX MyProject.csproj -o ./sbom --SpdxVersion 2.3
Example of generated bom.json (excerpt):
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"timestamp": "2026-03-23T10:00:00Z",
"component": {
"type": "application",
"name": "MyApp",
"version": "1.0.0"
}
},
"components": [
{
"type": "library",
"name": "Newtonsoft.Json",
"version": "13.0.3",
"purl": "pkg:nuget/Newtonsoft.Json@13.0.3",
"licenses": [{ "license": { "id": "MIT" } }]
}
]
}
GitHub Dependabot Integration
Configure automatic NuGet package updates in .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "security"
CI/CD Integration (GitHub Actions)
name: Security Scan - .NET
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 0 * * 1' # Every Monday
jobs:
vulnerability-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.x'
- name: Restore dependencies
run: dotnet restore
- name: Check for vulnerable packages
run: dotnet list package --vulnerable --include-transitive
- name: Install CycloneDX
run: dotnet tool install --global CycloneDX
- name: Generate SBOM
run: dotnet CycloneDX ./MyProject.csproj -o ./sbom -j
- name: Upload SBOM artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: ./sbom/bom.json
Docker / Container Vulnerability Scanning
Container images include both OS packages and language packages, making SBOM and vulnerability scanning especially important.
Trivy
Trivy is an all-in-one security scanner from Aqua Security.
# Scan an image
trivy image myapp:latest
# Show only CRITICAL and HIGH
trivy image --severity CRITICAL,HIGH myapp:latest
# Generate SBOM (CycloneDX format)
trivy image --format cyclonedx --output sbom.json myapp:latest
# Generate SBOM (SPDX format)
trivy image --format spdx-json --output sbom.spdx.json myapp:latest
# Scan filesystem (source code directory)
trivy fs --security-checks vuln,secret ./
# Scan configuration files (Dockerfile, Kubernetes YAML, etc.)
trivy config ./
Example output:
myapp:latest (ubuntu 22.04)
=========================
Total: 5 (CRITICAL: 1, HIGH: 4)
┌───────────────┬──────────────────┬──────────┬────────────────────┬───────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │
├───────────────┼──────────────────┼──────────┼────────────────────┼───────────────────┤
│ openssl │ CVE-2024-XXXX │ CRITICAL │ 3.0.2-0ubuntu1.13 │ 3.0.2-0ubuntu1.14 │
│ libssl3 │ CVE-2024-XXXX │ HIGH │ 3.0.2-0ubuntu1.13 │ 3.0.2-0ubuntu1.14 │
└───────────────┴──────────────────┴──────────┴────────────────────┴───────────────────┘
Grype
Grype is a container and SBOM scanner from Anchore.
# Scan an image
grype myapp:latest
# Scan an SBOM file (re-scan an SBOM generated by Trivy, etc.)
grype sbom:./sbom.json
# Use Syft for SBOM generation
syft myapp:latest -o cyclonedx-json > sbom.json
syft myapp:latest -o spdx-json > sbom.spdx.json
Dockerfile Best Practices
Minimizing vulnerabilities in your Dockerfile:
# BAD: Large base image (large attack surface)
FROM ubuntu:22.04
# GOOD: Distroless image (no OS packages, minimal)
FROM gcr.io/distroless/dotnet-runtime:9
# GOOD: Alpine (lightweight, fewer vulnerabilities)
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine
# Multi-stage build to minimize production image
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS runtime
WORKDIR /app
# Run as non-root user (security hardening)
USER $APP_UID
COPY --from=build /app .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Container Scanning in GitHub Actions
name: Container Security Scan
on:
push:
branches: [main]
jobs:
container-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail build if CRITICAL or HIGH found
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
- name: Generate SBOM
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'cyclonedx'
output: 'sbom.json'
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: container-sbom
path: sbom.json
Node.js Vulnerability Scanning
npm audit
The built-in vulnerability checker in npm.
# Check for vulnerabilities
npm audit
# Auto-fix (updates within compatible version ranges)
npm audit fix
# Fix including breaking changes (use with caution)
npm audit fix --force
# JSON output (for CI/CD integration)
npm audit --json
# Example output
# found 3 vulnerabilities (1 moderate, 2 high)
# Run `npm audit fix` to fix them, or `npm audit` for details
npm audit fix --force can trigger unintended major version upgrades.
Always review the changes before running this command.
SBOM Generation
# Install CycloneDX Node.js tool
npm install -g @cyclonedx/cyclonedx-npm
# Generate SBOM
cyclonedx-npm --output-file sbom.json
# npm-sbom (available in Node.js v22+)
npm sbom --sbom-format cyclonedx
npm sbom --sbom-format spdx
Socket Security
Socket is a specialized tool for detecting supply chain attacks in npm packages.
# Install
npm install -g @socketsecurity/cli
# Scan
socket scan npm
Node.js Scanning in GitHub Actions
name: Security Scan - Node.js
on:
push:
branches: [main]
pull_request:
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Run npm audit
run: npm audit --audit-level=high
# audit-level: low/moderate/high/critical
- name: Generate SBOM
run: |
npm install -g @cyclonedx/cyclonedx-npm
cyclonedx-npm --output-file sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: nodejs-sbom
path: sbom.json
Python Vulnerability Scanning
pip-audit
The official vulnerability scanner managed by the Python Packaging Authority (PyPA).
# Install
pip install pip-audit
# Scan current virtual environment
pip-audit
# Scan requirements.txt directly
pip-audit -r requirements.txt
# Output as SBOM (CycloneDX)
pip-audit -r requirements.txt -f cyclonedx -o sbom.json
# Output as SBOM (SPDX)
pip-audit -r requirements.txt -f spdx-json -o sbom.spdx.json
# Example output
# Name Version ID Fix Versions
# ------------- -------- -------------- ------------
# requests 2.27.1 GHSA-j8r2-6x86 2.28.0
Safety
# Install
pip install safety
# Scan dependencies (requirements.txt)
safety check -r requirements.txt
# Scan installed packages
safety check
# JSON output
safety check --json
Bandit (Static Analysis)
Bandit is a static analysis tool for detecting security bugs in Python code (SAST, not SCA).
# Install
pip install bandit
# Scan a directory
bandit -r ./src
# Show only HIGH severity
bandit -r ./src -l -ll
# JSON report
bandit -r ./src -f json -o bandit-report.json
Examples of vulnerabilities Bandit detects:
# BAD: SQL injection risk
query = "SELECT * FROM users WHERE id = " + user_id
cursor.execute(query) # B608: Possible SQL injection
# GOOD: Parameterized query
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
# BAD: Insecure deserialization
import pickle
data = pickle.loads(user_input) # B301: Pickle and modules that wrap it
# BAD: Weak hash algorithm
import hashlib
hashlib.md5(password) # B303: Use of MD5 is insecure
Python Scanning in GitHub Actions
name: Security Scan - Python
on:
push:
branches: [main]
pull_request:
jobs:
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pip-audit bandit
- name: Run pip-audit (SCA)
run: pip-audit -r requirements.txt
- name: Generate SBOM
run: pip-audit -r requirements.txt -f cyclonedx -o sbom.json
- name: Run Bandit (SAST)
run: bandit -r ./src -f json -o bandit-report.json
continue-on-error: true # Save results as artifact
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: python-sbom
path: sbom.json
- name: Upload Bandit report
uses: actions/upload-artifact@v4
with:
name: bandit-report
path: bandit-report.json
Tool Comparison and Summary
SBOM and SCA Tool Comparison
| Tool | Target | SBOM Generation | Vulnerability Scan | Formats |
|---|---|---|---|---|
| Trivy | Container/FS/.git | ✅ | ✅ | CycloneDX, SPDX |
| Syft | Container/FS | ✅ | ❌ | CycloneDX, SPDX |
| Grype | Container/SBOM | ❌ | ✅ | - |
| CycloneDX (.NET) | NuGet | ✅ | ❌ | CycloneDX, SPDX |
| pip-audit | Python | ✅ | ✅ | CycloneDX, SPDX |
| npm audit | npm | ❌ | ✅ | - |
| @cyclonedx/npm | npm | ✅ | ❌ | CycloneDX |
| OWASP Dependency-Check | Multi-language | ✅ | ✅ | CycloneDX, SPDX |
OWASP Dependency-Check (Multi-language)
A general-purpose SCA tool supporting multiple languages and ecosystems.
# Run with Docker
docker run --rm \
-v $(pwd):/src \
-v $(pwd)/reports:/report \
owasp/dependency-check \
--project "MyProject" \
--scan /src \
--format HTML \
--out /report
# In GitHub Actions
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'MyProject'
path: '.'
format: 'SARIF'
out: 'reports'
args: >
--failOnCVSS 7
--enableRetired
CVSS Score and Response Priority
Guidelines for prioritizing response based on CVSS (Common Vulnerability Scoring System) scores:
| CVSS Score | Severity | Response Timeline |
|---|---|---|
| 9.0 - 10.0 | CRITICAL | Immediate (within 24 hours) |
| 7.0 - 8.9 | HIGH | Priority (within 1 week) |
| 4.0 - 6.9 | MEDIUM | Planned (next sprint) |
| 0.1 - 3.9 | LOW | Address when possible |
- Pull requests: Block on CRITICAL/HIGH with
npm audit --audit-level=high, etc. - Scheduled scans: Generate SBOMs weekly/monthly to detect newly discovered vulnerabilities
- Container builds: Run Trivy scan after every image build