SBOM Upload: Price Your Software Supply-Chain Risk in Euros
The Nisura Team ·
Most of the code your company runs, nobody at your company wrote. It arrives as open-source libraries: a logging framework, an HTTP client, a date picker, each pulling in dozens more. When one of them carries a known, exploited vulnerability, it is just as much a way in as an unpatched firewall. The difference is that you usually can't see it from the outside.
A software bill of materials (SBOM) makes it visible. Upload one to Nisura and every pinned dependency is checked against public vulnerability data, weighted by how likely each flaw is to be exploited, and folded into your Expected Annual Loss in euros. This guide covers what an SBOM is, how to generate one in a few minutes, how to upload it, and exactly how the engine prices it, with a worked example you can reproduce.
What an SBOM is
An SBOM is an ingredient list for software: every third-party package, its exact version and, ideally, a package URL (purl) such as pkg:npm/lodash@4.17.20. The purl says which ecosystem a package comes from (npm, PyPI, Maven…), which is what makes it possible to look up its known vulnerabilities precisely. The EU Cyber Resilience Act will require manufacturers of products with digital elements to keep one; for everyone else it is simply the fastest way to answer “are we running anything known to be exploited?”
There are two standards, both open:
- CycloneDX (OWASP), as JSON or XML.
- SPDX (Linux Foundation, ISO/IEC 5962), as JSON, tag-value, YAML or RDF.
Nisura accepts CycloneDX JSON and SPDX JSON, plus the lockfiles most projects already have: package-lock.json, composer.lock, requirements.txt with pinned versions, and package.json as a last resort (it only lists version ranges, so fewer packages can be priced).
How to generate one
No install: export it from GitHub
In any repository: Insights → Dependency graph → Export SBOM. You get an SPDX JSON file built from the repo's manifests and lockfiles.
From the command line, with free open-source tools
Syft (Anchore), cdxgen (OWASP CycloneDX) and Trivy (Aqua) are all Apache-2.0-licensed and need no account:
# Syft: a project folder, or the Docker image you actually deploy
syft dir:. -o cyclonedx-json > sbom.json
syft registry.example.com/portal:1.4 -o spdx-json > sbom.spdx.json
# cdxgen: detects the languages in a repository
npx @cyclonedx/cdxgen -o sbom.json
# Trivy: a folder or an image
trivy fs --format cyclonedx -o sbom.json .Built into your package manager
npm sbom --sbom-format cyclonedx > sbom.json # npm 10+
cyclonedx-py environment > sbom.json # Python (pip install cyclonedx-bom)
mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom # Maven → target/bom.jsonThree tips for a result the engine can actually price:
- Scan what is deployed. An SBOM of the production container image is closer to reality than one of a developer's checkout.
- Generate it from a lockfile. A package without an exact version can't be matched to a vulnerability, so Nisura lists it as “not priceable” instead of guessing.
- Use JSON. CycloneDX XML and SPDX tag-value aren't accepted on upload.
How to upload it to Nisura
- Open the Simulator and scroll to the Supply chain card.
- Click Upload SBOM and pick the file. Nisura detects the format from its contents.
- The simulation re-runs automatically and the card shows the euro amount your dependencies add, the vulnerable packages, and how many were priced.
MSSP and insurance partners can upload one per client from Clients, and it is priced into that client's next simulation.
Because an SBOM describes proprietary software, the upload is available to accounts with an active subscription and a verified domain. What leaves Nisura is only what the lookups need: package names and versions to OSV.dev and deps.dev, and CVE IDs to FIRST for EPSS scores.
The SBOM doesn't replace what the external scan already found. The scan sees the JavaScript your public website ships; the SBOM usually covers an application's backend. Nisura counts both together, and a library that appears in both is priced once.
How the engine prices an SBOM
Four steps, all built on public, citable data rather than a vendor's black box:
- Match each package to its known vulnerabilities. Every pinned dependency is queried against OSV.dev, Google's open vulnerability database that aggregates the GitHub Advisory Database, PyPA, Go, RustSec and others. OSV does the version-range matching, so a returned vulnerability applies to the exact version you run.
- Weight each one by how likely it is to be exploited. Every CVE gets its live EPSS score from FIRST: the probability it's exploited in the wild within 30 days. To keep one noisy library from dominating, at most three CVEs per package are priced, and those are the three with the highest EPSS, so Log4Shell never gets crowded out by minor bugs.
- Turn it into a likelihood for your company. Each vulnerability becomes one attack vector with an annual probability of EPSS × 0.15 × M. The 0.15 scales a global exploitation rate down to one organisation, and M is the reduction from your security controls. The vectors combine with the rest of your exposure (scanned CVEs, the credential/RDP path) as 1 − ∏(1 − p): an attacker needs only one working way in, so likelihood saturates instead of piling up with the number of findings.
- Multiply by what a breach would cost you. The resulting annual likelihood (ARO) times your Single Loss Expectancy, the same euro figure used everywhere in Nisura, built from Eurostat wages, Damodaran Europe margins and your NIS2/GDPR exposure, gives the Expected Annual Loss. The engine also reports the marginal euros the dependencies add, so the supply-chain figure is never double-counted against the rest.
Worked example: a vulnerable SBOM, priced
Take a fictional company, Musterwerk GmbH: an Austrian manufacturer with 120 employees, €20M revenue, MFA and EDR in place. Its customer portal ships with six dependencies, some of them years out of date. You can download this demo SBOM (CycloneDX JSON) and upload it to your own account.
The figures below are the engine's actual output, with EPSS scores as of 21 October 2026. EPSS is updated daily, so a re-run will differ slightly.
Step 1 — what OSV finds
All six packages were priced; five carry known vulnerabilities.
| Package | Version | Known vulns | Worst CVSS |
|---|---|---|---|
log4j-core | 2.14.1 | 7 | 10.0 |
lodash | 4.17.20 | 5 | 9.8 |
axios | 0.21.1 | 24 | 7.5 |
requests | 2.19.0 | 10 | 7.5 |
jquery | 3.4.1 | 2 | 6.1 |
express | 4.21.2 | 0 | — |
Note that axios has the most vulnerabilities but adds almost nothing, while jQuery, with two medium-severity bugs, matters a lot. Counting vulnerabilities, or sorting by CVSS, would get the priorities backwards. Exploitation likelihood is what separates them.
Step 2 — each CVE becomes a probability
MFA and EDR together give Musterwerk a control factor of M = 0.3575. So Log4Shell, with an EPSS of 1.0, becomes 1.0 × 0.15 × 0.3575 = 5.36% a year. The fourteen priced vectors:
| CVE | Package | EPSS | Annual probability |
|---|---|---|---|
| CVE-2021-44228 (Log4Shell) | log4j-core | 1.000 | 5.36% |
| CVE-2021-45105 | log4j-core | 1.000 | 5.36% |
| CVE-2021-45046 | log4j-core | 0.9998 | 5.36% |
| CVE-2020-11022 | jquery | 0.992 | 5.32% |
| CVE-2020-11023 | jquery | 0.849 | 4.55% |
| CVE-2021-23337 | lodash | 0.213 | 1.14% |
| CVE-2021-3749 | axios | 0.085 | 0.46% |
| CVE-2018-18074 | requests | 0.074 | 0.40% |
| CVE-2020-28500 | lodash | 0.073 | 0.39% |
| 5 more (EPSS 0.01–0.03) | lodash, axios, requests | ≤0.030 | 0.05–0.16% each |
Step 3 — combine them
Together the fourteen vectors give a 25.6% chance a year that at least one of them is used. Without the SBOM, Musterwerk's only modelled path is credential compromise at 2.24%. Combined: 1 − (1 − 0.256) × (1 − 0.0224) = 27.3%.
Step 4 — into euros
Musterwerk's Single Loss Expectancy is €728,464: €192,960 in lost labour during downtime, €75,504 in lost gross margin, €180,000 for forensics, and €280,000 of NIS2 exposure (an important entity, 1.4% of turnover).
| Annual likelihood | Expected Annual Loss | |
|---|---|---|
| Without the SBOM | 2.24% | €16,318 |
| With the SBOM | 27.3% | €198,944 |
| Of which: dependencies | €182,625 |
Six libraries in one portal take this company's expected loss from about €16k to about €199k a year.
Step 5 — what fixing each one is worth
The same model tells you where an upgrade pays off, by re-running it with each package upgraded to a version without the priced vulnerabilities (the card links each package to its advisory, which names the fixed version):
| Fix | Expected Annual Loss after | Saving per year |
|---|---|---|
| Upgrade log4j-core | €103,741 | €95,202 |
| Upgrade jquery | €142,514 | €56,429 |
| Upgrade lodash | €189,943 | €8,999 |
| Upgrade axios | €195,367 | €3,576 |
| Upgrade requests | €195,686 | €3,257 |
| log4j-core and jquery together | €37,167 | €161,776 |
Two upgrades remove over 80% of the risk; the other three are housekeeping. (The individual savings don't add up to the total because likelihood saturates: fixing one path makes the remaining ones count for more.) This is the same remediation-in-euros logic Nisura applies to the rest of your exposure.
Beyond vulnerabilities: maintenance health
For each package Nisura also shows its licence, the latest available version, whether the maintainers have deprecated it, and its OpenSSF Scorecard (a 0–10 rating of the source project's security practices), via Google's free deps.dev (data under CC-BY 4.0). It flags deprecated packages, packages with no release in two years, and Scorecards below 4/10. These signals are shown, not priced: there is no cited source yet that turns “unmaintained” into a breach probability, and we don't put numbers into the euro figure without one.
What the number does and doesn't claim
- Presence, not reachability. The engine prices a vulnerable library because it's there. It doesn't analyse whether your code calls the vulnerable function. That's deliberate, since a library you don't call today may be called tomorrow, but it means the figure is an upper-leaning estimate for code paths you know are unused.
- Exact versions only. Packages without a pinned version are counted and listed as “not priceable”, never guessed.
- Ecosystem coverage follows OSV: npm, PyPI, Maven, Go, NuGet, RubyGems, crates.io and Packagist. Maintenance health covers the same minus Packagist.
Generate an SBOM, upload it, and the supply-chain line in your Simulator stops being a blind spot. New to the numbers? Start with what cyber risk quantification is.
Frequently asked questions
What file formats can I upload to Nisura?
CycloneDX JSON and SPDX JSON SBOMs, plus package-lock.json, composer.lock, requirements.txt with pinned versions, and package.json as a last resort. CycloneDX XML and SPDX tag-value are not accepted; most tools can export JSON instead.
How do I generate an SBOM?
The quickest way is GitHub: Insights → Dependency graph → Export SBOM gives an SPDX JSON file. From the command line, the free open-source tools Syft, cdxgen and Trivy generate CycloneDX or SPDX JSON from a project folder or a container image, and npm 10+ has a built-in npm sbom command.
How does Nisura turn an SBOM into euros?
Each pinned package is matched against OSV.dev for known vulnerabilities, each CVE gets its live EPSS exploitation probability from FIRST, and each becomes an attack vector with annual probability EPSS × 0.15 × your control factor. The vectors combine with the rest of your exposure as 1 − ∏(1 − p) and are multiplied by your Single Loss Expectancy to give Expected Annual Loss. At most three CVEs per package are priced, the three with the highest EPSS.
Does Nisura check whether my code actually uses the vulnerable function?
No. The engine prices a vulnerable library because it is present; it does not analyse reachability. Packages without an exact version are listed as not priceable rather than guessed.