This project demonstrates a lightweight but realistic Software Supply Chain Security workflow using open-source tooling.
The workflow covers:
- SBOM (Software Bill of Materials) generation
- Dependency graph analysis
- Vulnerability scanning
- CVE correlation
- VEX (Vulnerability Exploitability eXchange) integration
- CI/CD pipeline integration concepts
- EU CRA (Cyber Resilience Act) aligned practices
The implementation uses:
- Python (
pipenv) - Node.js (
npm) - CycloneDX
- Trivy
- OpenVEX / vexctl
This project aims to demonstrate:
- What software components exist?
- What vulnerabilities exist in those components?
- Are those vulnerabilities actually exploitable?
- How can this be automated in CI/CD?
An SBOM is an inventory of software components used in an application.
It includes:
- direct dependencies
- transitive dependencies
- versions
- package identifiers
- dependency relationships
SBOM answers:
"What software is inside my product?"
Vulnerability scanners compare package versions against known vulnerability databases.
Examples:
- CVE
- GHSA
- NVD
- vendor advisories
This answers:
"What known vulnerabilities exist in my software?"
VEX adds exploitability and applicability context.
This answers:
"Does this vulnerability actually affect MY product/runtime/build?"
Without VEX:
Package exists → Alert fires
With VEX:
Package exists
+
Is vulnerable code reachable?
+
Feature enabled?
+
Platform affected?
+
Exploit path possible?
+----------------+
| Source Code |
+--------+-------+
|
v
+----------------------+
| Dependency Managers |
| pipenv / npm |
+----------+-----------+
|
v
+-------------------+
| CycloneDX SBOM |
| python-sbom.json |
+---------+--------+
|
v
+-------------------+
| Trivy Scanner |
| vulnerability.json|
+---------+---------+
|
v
+-------------------+
| OpenVEX / vexctl |
| vex.json |
+---------+---------+
|
v
+-------------------+
| Final Security |
| Decision Layer |
+-------------------+
Website:
Python tool docs:
https://cyclonedx-bom-tool.readthedocs.io/en/latest/
Purpose:
- Generate SBOMs
- Export dependency relationships
- Produce standardized software inventory
Website:
Purpose:
- Vulnerability scanning
- SBOM scanning
- filesystem scanning
- VEX consumption
Supports:
- OS packages
- containers
- Python
- npm
- Go
- Java
- IaC
- secrets
Website:
Purpose:
- vulnerability exploitability assertions
- machine-readable applicability decisions
GitHub:
https://github.com/openvex/vexctl
Purpose:
- create/manage OpenVEX documents
pip install pipenv cyclonedx-bom pipdeptreepipenv install requestspipenv graphExample:
requests==2.32.3
├── charset-normalizer
├── idna
├── urllib3
└── certifi
On macOS:
cyclonedx-py environment --output-format json --output-file python-sbom.jsonworked correctly.
Inside Docker/CI:
- dependencies were missing
- virtualenv mismatch occurred
- cyclonedx-py was executing outside the active Pipenv environment
This command:
which cyclonedx-pyreturned:
/usr/local/bin/cyclonedx-py
while the active Python environment was:
/root/.local/share/virtualenvs/...
Meaning:
- the executable existed outside the active venv
- dependency enumeration was incomplete
pipenv installexport PATH="$(pipenv --venv)/bin:$PATH"source "$(pipenv --venv)/bin/activate"which pythonpython -m pip listpython -c "import requests; print(requests.__version__)"This failed:
python -m cyclonedx-pybecause Python modules use underscores.
Correct module name:
python -m cyclonedx_pypython -m cyclonedx_py environment \
--output-format json \
--output-file python-sbom.jsonThe SBOM included:
Example:
{
"name": "requests",
"version": "2.32.3"
}Example:
{
"ref": "requests==2.32.3",
"dependsOn": [
"certifi==2026.4.22",
"charset-normalizer==3.4.7",
"idna==3.13",
"urllib3==2.7.0"
]
}This is the critical relationship mapping.
trivy fs . --format json --output vulnerability.jsonContains:
Example:
{
"Name": "requests",
"Version": "2.32.3"
}This section is inventory metadata only.
It does NOT contain dependency relationships.
Those exist in the SBOM.
Example:
{
"VulnerabilityID": "CVE-2024-47081",
"PkgName": "requests",
"InstalledVersion": "2.32.3",
"FixedVersion": "2.32.4",
"Severity": "MEDIUM"
}Example:
"VendorSeverity": {
"alma": 2,
"amazon": 2,
"ghsa": 2,
"redhat": 2
}Meaning:
- different vendors classify severity differently
- numeric values map roughly to:
- 0 = unknown
- 1 = low
- 2 = medium
- 3 = high
- 4 = critical
This allows:
- vendor-specific risk interpretation
- distro-specific security decisions
vexctl create \
--product "pkg:pypi/requests@2.32.3" \
--vuln "CVE-2024-47081" \
--status "under_investigation" \
--file vex.jsonKnown issue.
Analysis pending.
Do not suppress.
Confirmed exploitable/impactful.
Remediation required.
Issue remediated.
Patched or mitigated.
Most important VEX state.
Used when:
- vulnerable code unreachable
- feature disabled
- platform mismatch
- exploit impossible in runtime
{
"@context": "https://openvex.dev/ns/v0.2.0",
"statements": [
{
"vulnerability": {
"name": "CVE-2024-47081"
},
"products": [
{
"@id": "pkg:pypi/requests@2.32.3"
}
],
"status": "under_investigation"
}
]
}trivy fs . \
--vex /tmp/vex.json \
--format json \
--output final-report.jsonUsing:
"status": "under_investigation"does NOT suppress the vulnerability.
This is expected behavior.
Why?
Because:
under_investigation != resolved
It is merely:
- workflow state
- analyst state
- pending review state
Answers:
"What software exists?"
Answers:
"What CVEs exist?"
Answers:
"Does this vulnerability actually matter for MY product?"
stages:
- sbom
- scan
- vex
- final
script:
- pipenv install
- export PATH="$(pipenv --venv)/bin:$PATH"
- source "$(pipenv --venv)/bin/activate"
# Generate SBOM
- python -m cyclonedx_py environment \
--output-format json \
--output-file python-sbom.json
# Scan vulnerabilities
- trivy fs . \
--format json \
--output vulnerability.json
# Apply VEX
- cp ./vex.json /tmp/vex.json
# Final report
- trivy fs . \
--vex /tmp/vex.json \
--format json \
--output final-report.jsonPotential workflow:
SBOM
+
Vulnerability Scan
+
Static Analysis
+
Reachability Analysis
+
Runtime Context
+
Human Approval
=
Generated VEX
Possible future additions:
- Syft
- Grype
- Dependency Track
- GUAC
- Sigstore
- Cosign
- SLSA
- in-toto attestations
- SARIF export
- policy engines
- reachability analysis
- runtime telemetry correlation
This workflow aligns closely with:
- software inventory management
- vulnerability management
- remediation tracking
- supply chain transparency
- secure-by-design practices
- continuous monitoring
- exploitability assessment
All of which are central themes in the EU Cyber Resilience Act.
This project evolved from:
"Generate an SBOM"
into a realistic miniature supply-chain security platform workflow involving:
- dependency management
- SBOM generation
- vulnerability intelligence
- exploitability context
- CI/CD integration
- security decision automation
This represents real-world AppSec / Product Security engineering foundations.