Scanning a djust app
Publish a CycloneDX SBOM of your app's vendored assets and Python-linked Rust crates, and scan it with Trivy, Syft, grype and OSV-Scanner.
On this page
djust can write a CycloneDX SBOM of every third-party asset it and your app declare (see Vendoring third-party JS), so container scanners can match a vendored JS package against a published advisory the same way they already do for Python and OS packages. This is ADR-040.
Setting up the app SBOM
-
Set
DJUST_SBOM_PATHto a full file path outside every directory your web server serves — outsideSTATIC_ROOT,MEDIA_ROOT, everySTATICFILES_DIRSentry, and anything your reverse proxy or object storage exposes directly. djust only knows about the Django-side directories; check the rest against your own web server/CDN configuration. Name the file so it ends in.cdx.json—osv-scanneronly recognizes a CycloneDX file with that extension:# settings.py DJUST_SBOM_PATH = "/var/lib/myapp/sbom/djust-assets.cdx.json"
Unset (the default), nothing is written and
manage.py check --deploywarns withdjust.B011. Pointed inside a served directory, startup fails withdjust.B012andcollectstaticrefuses to write it. djust does not reject another file name, so check the suffix yourself: the file must end in.cdx.jsonfor scanners to find it. -
Make sure
"djust"is listed above"django.contrib.staticfiles"inINSTALLED_APPS. The SBOM is written by acollectstaticcommand override, and Django only lets an earlier app override a later app's management command. WithDJUST_SBOM_PATHset and the order wrong,manage.py check --deployfails withdjust.B013; with noDJUST_SBOM_PATHset, an app in the old order sees no new check output at all —djust.B013only fires once there's an SBOM to write. -
Run
collectstaticas part of your build or deploy step. It writes the SBOM after collecting static files.manage.py check --deployalso verifies the file on disk still matches the currently declared assets (djust.B014) — useful as a build-time guard against a stale SBOM shipped in an image after a dependency bump.
You can also print or write the same document on demand, without running
collectstatic:
manage.py djust_sbom -o /var/lib/myapp/sbom/djust-assets.cdx.json
The app SBOM's root component is named after DJUST_SBOM_NAME when it's
set, or the first component of ROOT_URLCONF otherwise (for example
myapp, for myapp.urls).
Exact commands
Use these exactly — some scanner subcommands silently ignore an embedded SBOM in a way that looks like "nothing to report" rather than an error.
# Trivy: scan a filesystem root or a built image, never `trivy fs` (repo
# mode) — it does not read an embedded *.cdx.json the way `rootfs`/`image` do.
trivy rootfs /app
trivy image myapp:latest
# Syft: needs the SBOM cataloger explicitly enabled to read *.cdx.json files.
syft dir:/app --select-catalogers +sbom-cataloger
# grype: scan an SBOM file directly.
grype sbom:/var/lib/myapp/sbom/djust-assets.cdx.json
# OSV-Scanner: the file must end in .cdx.json (see setup step 1).
osv-scanner scan source -L /var/lib/myapp/sbom/djust-assets.cdx.json
djust's own CI uses the same OSV-Scanner command against
python/djust/djust.cdx.json, djust's own distribution SBOM (see
.github/workflows/vendor.yml). That python/ prefix is only djust's own
source layout — it's where the file is generated and committed in the
djust repository. Once you pip install djust, the same file ships in two
places: inside the installed package directory (<site-packages>/djust/djust.cdx.json)
and under the wheel's PEP 770 metadata
(<site-packages>/djust-<version>.dist-info/sboms/djust.cdx.json). Find
the installed copy without knowing your site-packages path:
python -c "import djust, pathlib; print(pathlib.Path(djust.__file__).with_name('djust.cdx.json'))"
Known advisories that are accepted for
now (unmaintained, not vulnerable) are listed in osv-scanner.toml with a
reason and an ignoreUntil date — the same policy .cargo/audit.toml
already applies to Rust advisories: unmaintained warns, a real
vulnerability fails. An expired entry starts failing CI again until it's
re-reviewed. A weekly scheduled workflow reruns the same scan against the
latest tag of each supported release line and files one issue per
advisory per line.
Coverage limits
pip-audit, Dependabot and Snyk do not see vendored JS. They read Python package metadata and lockfiles, not bundled browser code. The SBOM this guide describes exists specifically to close that gap for scanners that do read CycloneDX (Trivy, Syft, grype, OSV-Scanner).- Dependabot can still be fed the SBOM, through
GitHub's dependency submission API —
submit the document
manage.py djust_sbomprints as part of your CI workflow.
Python dependencies
This SBOM covers browser assets (and, for djust's own distribution SBOM,
the Rust crates linked into the extension) — it does not cover your
Python environment. Use cyclonedx-py
to generate an SBOM of your installed Python packages; run both tools in
CI if you want full coverage.
See also
- Vendoring third-party JS — manifests, integrity,
and
{% djust_asset %}. - Error Codes — every
djust.B0xxcheck, including B011–B014 (the app SBOM). - ADR-040 — the design.