sbomviz is a Python web app that ingests an SPDX SBOM JSON file and presents a human-friendly report with:
- Document metadata (name, SPDX version, creators, creation date)
- Summary cards for package, relationship, relationship-type, and supplier counts
- A searchable package inventory (name, version, SPDX ID, supplier)
- A searchable dependency/relationship table with color-coded relationship types
- A Tailwind CSS dark-themed UI with responsive layout
This project must be developed and run only in containers.
- Use the Chainguard-based container defined in
Dockerfile - Do not install Python dependencies on the host machine
- Do not run
pythonorflaskcommands on the host - Use Docker Compose commands below for all execution
- Docker with Compose support
chainctlauthenticated to Chainguard
This project installs Python and JavaScript dependencies from Chainguard Libraries. You need pull tokens for both ecosystems before building or starting containers.
Obtain Python auth variables from Chainguard:
eval "$(chainctl auth pull-token --output env --repository=python --parent=example)"Quick check:
echo "$CHAINGUARD_PYTHON_IDENTITY_ID"
echo "$CHAINGUARD_PYTHON_TOKEN"Python packages are installed during the sbomviz image build from:
https://libraries.cgr.dev/python/simple/as the only package index (no PyPI fallback)- credentials from
CHAINGUARD_PYTHON_IDENTITY_IDandCHAINGUARD_PYTHON_TOKEN
Important: these credentials may include URL-special characters. The Dockerfile URL-encodes both values before constructing PIP_INDEX_URL.
Obtain JavaScript auth variables from Chainguard:
eval "$(chainctl auth pull-token --output env --repository=javascript --parent=example)"Quick check:
echo "$CHAINGUARD_JAVASCRIPT_IDENTITY_ID"
echo "$CHAINGUARD_JAVASCRIPT_TOKEN"JavaScript packages are installed from:
https://libraries.cgr.dev/javascript/as the npm registry (no npmjs.org fallback)- credentials from
CHAINGUARD_JAVASCRIPT_IDENTITY_IDandCHAINGUARD_JAVASCRIPT_TOKEN
The project generates a local .npmrc at build/watch time via scripts/configure-npm-libraries.sh. This file is gitignored because it contains credentials.
If package-lock.json was created against the public npm registry, update integrity hashes before the first Chainguard install:
eval "$(chainctl auth pull-token --output env --repository=javascript --parent=example)"
docker compose run --rm --no-deps css "./scripts/configure-npm-libraries.sh"
rm -rf node_modules
chainctl libraries update-hashes --replace package-lock.json
docker compose run --rm --no-deps css "npm install"rm -rf node_modules is a host filesystem cleanup (not an npm command). chainctl libraries update-hashes --replace also runs on the host and replaces npmjs.org integrity hashes with Chainguard checksums (--replace is required for package-lock.json, which only stores one hash per package).
If npm install still reports EINTEGRITY for a package listed as "not in Chainguard", remove the lockfile and regenerate it entirely from the Chainguard registry:
rm -rf package-lock.json node_modules
docker compose run --rm --no-deps css "npm install"If a previous container run left root-owned files in node_modules, fix ownership once on the host:
sudo chown -R "$(id -u)":"$(id -g)" node_modulesOr delete node_modules on the host and reinstall.
Optional: store all four values in a local .env file:
cat > .env <<EOF
DOCKER_UID=$(id -u)
DOCKER_GID=$(id -g)
CHAINGUARD_PYTHON_IDENTITY_ID=your-python-identity-id
CHAINGUARD_PYTHON_TOKEN=your-python-token
CHAINGUARD_JAVASCRIPT_IDENTITY_ID=your-javascript-identity-id
CHAINGUARD_JAVASCRIPT_TOKEN=your-javascript-token
EOFDOCKER_UID and DOCKER_GID map the container user to your host user so bind-mounted files (.npmrc, node_modules, etc.) remain writable.
If any value is empty, run the matching eval "$(chainctl auth pull-token ...)" command in the same terminal where you run Docker Compose.
Note: only build/start commands require these variables. Other Docker Compose commands like docker compose down do not require them.
Security notes:
- Do not hardcode tokens in committed files
- Keep all
CHAINGUARD_*credentials in environment variables or a local.envfile only - Re-run the
chainctl auth pull-tokencommands when token values expire
docker compose up --buildThe app is available at http://localhost:8000.
Compose starts two services: the Flask app and a Tailwind CSS watcher that rebuilds static/styles.css when templates change.
docker compose downTo clear tokens from your current shell session:
unset CHAINGUARD_PYTHON_IDENTITY_ID
unset CHAINGUARD_PYTHON_TOKEN
unset CHAINGUARD_JAVASCRIPT_IDENTITY_ID
unset CHAINGUARD_JAVASCRIPT_TOKENUpload one of:
samples/python.spdxsamples/static.spdx
Then try the client-side search boxes on the report page:
- Dependencies — filter by from/to package, relationship type, or SPDX ID (e.g.
glibc,CONTAINS,SPDXRef-Package-apk) - Packages — filter by name, version, SPDX ID, or supplier
The suite lives in tests/ and uses pytest. Per the container-only policy,
run it inside a container rather than on the host:
docker run --rm -v "$PWD":/app -w /app python:3.12-slim \
sh -c "pip install -r requirements-dev.txt && python -m pytest"What is covered:
tests/test_parser.py- unit tests for SPDX parsing, package labelling, and dependency filtering insbomviz/parser.pytests/test_app.py- route/integration tests for the Flask app (upload validation, redirects, report rendering, and 404 handling)
CI runs the same suite on every push and pull request via
.github/workflows/tests.yml (Python 3.11, 3.12, and 3.13).
app.py- Flask app with upload and report routessbomviz/parser.py- SPDX parsing and dependency filtering logictemplates/base.html- shared layout (header, footer, logo)templates/index.html- upload pagetemplates/report.html- SBOM report with summary cards and searchable tablesstatic/logo_sbomviz.png- app logostatic/src/input.css- Tailwind CSS sourcestatic/styles.css- compiled Tailwind output (generated)package.json- Tailwind build scripts (run inside containers only)scripts/configure-npm-libraries.sh- writes.npmrcfor Chainguard JavaScript Librariesdocker-compose.yml- Flask app and Tailwind CSS watcher servicesDockerfile- multi-stage Chainguard image (CSS build, Python deps, distroless runtime)samples/- test SBOM filestests/- pytest suite for the parser and Flask routesrequirements-dev.txt- test dependencies (Flask + pytest)pyproject.toml- pytest configuration.github/workflows/tests.yml- CI workflow that runs the test suiteAGENTS.md- agent execution rules, including container-only policy
