Skip to main content

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

FormatOwnerCharacteristics
CycloneDXOWASPSecurity-focused, lightweight
SPDXLinux FoundationStrong license management support
SWIDISO/IEC 19770-2Primarily 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: log4j was 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

DatabaseURLCharacteristics
NVDnvd.nist.govUS government-managed, CVE-numbered
OSVosv.devOpen source vulnerability focused
GitHub Advisoriesgithub.com/advisoriesGitHub-managed, easy SCA integration
JVNDBjvndb.jvn.jpJapan 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
caution

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

ToolTargetSBOM GenerationVulnerability ScanFormats
TrivyContainer/FS/.gitCycloneDX, SPDX
SyftContainer/FSCycloneDX, SPDX
GrypeContainer/SBOM-
CycloneDX (.NET)NuGetCycloneDX, SPDX
pip-auditPythonCycloneDX, SPDX
npm auditnpm-
@cyclonedx/npmnpmCycloneDX
OWASP Dependency-CheckMulti-languageCycloneDX, 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 ScoreSeverityResponse Timeline
9.0 - 10.0CRITICALImmediate (within 24 hours)
7.0 - 8.9HIGHPriority (within 1 week)
4.0 - 6.9MEDIUMPlanned (next sprint)
0.1 - 3.9LOWAddress when possible
Recommended CI/CD Integration
  • 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

References