Vulnerabilities
The packages recorded on disk copies, container images, serverless functions and Git repositories, their matching against NVD, KEV, EPSS and distribution advisories, the risk score and the “Vulnerabilities” page.
The Vulnerabilities module records the packages installed on your machines, in your container images, in your serverless functions and in your Git repositories, then compares them with the catalogue of known vulnerabilities. One finding is raised per CVE and package on each resource, with a risk score that combines severity, exploitation probability, observed exploitation and the resource's exposure.
Where the packages come from
| Source | How the inventory is made |
|---|---|
| Machine disks | On the disk copy described in Hosts, Trivy walks the mounted filesystem (system and language packages). On a Windows disk, installed programs are read from the SOFTWARE registry hive (Uninstall keys, 64 and 32 bit), with the Windows version. |
| Container images | The Kubernetes module records the images of each cluster and analyses them with Trivy, with the cluster's pull secrets and the account's registry credentials. |
| Serverless functions and containers | The serverless function scanner downloads the function's code package and inventories it with Trivy, which reads installed packages (node_modules, *.dist-info, Go binaries); a function deployed from an image is analysed as an image, with the account's registry credentials. |
| Git repositories | At each scan of a connected repository, the lock files are read: package-lock.json, yarn.lock, go.sum, requirements.txt, Pipfile.lock, Cargo.lock, Gemfile.lock, composer.lock. The node_modules, vendor, .terraform, dist and build directories are skipped. |
For a repository, the dependency files the module does not read yet are named in the result rather than ignored: poetry.lock, pdm.lock, pom.xml, gradle.lockfile, packages.lock.json and paket.lock (NuGet), mix.lock, pubspec.lock, Podfile.lock, conan.lock, vcpkg.json, Chart.lock, flake.lock, go.work.sum, bun.lockb, and a Cargo.toml without Cargo.lock. The ctadel/sca status set on the pull request then says so in plain words; see Infrastructure as code. The scan of a pull request or of a side branch feeds the request's comment and status, but neither writes nor closes the repository's findings, which describe the default branch.
Each package is sent with its name, its full version (epoch, version and revision for a distribution package), its source package and its ecosystem, along with the operating system family and version of the source.
The catalogue
Ctadel keeps the vulnerability catalogue in your instance and refreshes it every 24 hours:
| Source | Content | Use |
|---|---|---|
| NVD (NIST API 2.0) | Every CVE, with its affected products (CPE), version bounds, CVSS score and vector | Full download on first start, then the CVEs modified since the previous refresh |
| CISA KEV | Vulnerabilities with observed exploitation, and those used by ransomware | "KEV" column and filter; exploitation probability floor |
| EPSS (FIRST) | The 30-day exploitation probability and percentile of each CVE | "EPSS" column and filter; risk score |
| Alpine SecDB | Fixed versions per Alpine branch | Distribution advisories |
| Debian Security Tracker | The state of each CVE per Debian release and source package | Distribution advisories |
| Ubuntu Security Notices | Fixed versions per Ubuntu release and binary package | Distribution advisories |
NVD configurations that require an old or specific system (Gentoo, Slackware, Mandrake, Mandriva, a kernel before version 3) are set aside to avoid false positives.
Matching
For each package of a source, Ctadel looks up the CVEs whose product carries the same name (package name, name without the lib prefix or -dev suffix, Maven artifact, source package), then checks in order:
- The ecosystem. The package type is mapped to a catalogue ecosystem:
deb,rpm,apk,windows,jar,go,python,npm,gem,rust,nuget,composer. A Python package does not match a CVE of a Node module with the same name. - The version. The installed version is compared with the CVE's bounds (
FIXED:fixed version,RANGE:interval, or exact version). An empty,latestor*version cannot be placed in a range and never matches. Two versions from clearly different branches are not compared. - The target system. A CVE that only applies to one system (Ubuntu 22.04, Debian 12) is kept only when the source runs on it; Debian point releases count as the same release.
- Distribution advisories. For a distribution package, the Alpine, Debian or Ubuntu advisory is authoritative: when the distribution has marked the CVE fixed for the installed version or above (a backported fix without an upstream version change), or never affected for that release, the finding is not raised. Debian and Ubuntu versions are then compared the way
dpkgdoes (3.0.11-1~deb12u1comes before3.0.11-1).
The same CVE and package pair is counted once per source. Findings of the source that no longer come up are closed with the status FIXED; a FIXED finding whose package reappears is reopened; a finding a user set to IGNORED stays ignored. While the catalogue is not fully loaded, no finding is closed: a missing match proves nothing.
Version comparison
The general comparison reads a version as numeric and alphabetic segments, with the RPM epoch (1:2.34.1-6.el8), OpenSSL letter suffixes (1.1.0j), distribution revisions (1.2.3-1ubuntu2) and pre-release markers (2.0.0-rc1 comes before 2.0.0). A shorter version sorts before its zero-padded form: 1.2 comes before 1.2.0, as in rpm and dpkg. This is the right order for distribution packages, which are the bulk of what is compared; it is not the order of npm, PyPI or Maven, where 1.2 and 1.2.0 are the same release. This gap is known and accepted: a package from those ecosystems reported at 1.2 against a fix at 1.2.0 is flagged vulnerable when it is already patched.
Risk score and exposure
The "Risk" column shows a score from 0 to 100 computed as follows: up to 35 points for the CVSS score (CVSS / 10 × 35), up to 30 points for EPSS (EPSS × 30), 25 points when the CVE is in KEV, 10 points when the resource is exposed. The list is sorted by this score.
Exposure comes from the graph: Ctadel regularly lists the resources reachable from the Internet (machines, Kubernetes workloads, the images they run) and marks their findings as exposed. It also records on each CVE of the graph the CVSS attack vector (network, adjacent, local, physical) and an exploitation probability: EPSS, raised to 0.9 for a CVE in KEV and to 0.95 for a CVE used by ransomware. The Attack paths module reads these values.
The "Vulnerabilities" page
The "Vulnerabilities" page (address /vuln) lists the open findings of the active project with the columns "Risk", "Severity", "CVE ID", "KEV", "EPSS", "Package", "Asset", "Fix" and "Age". Filters cover "Severity", "KEV", "EPSS", "Fix Available", "Package", "Asset" and "Status"; grouping covers "Severity", "Package" or "Asset". The "Fix" column gives the fixed version for the package concerned, as the catalogue knows it; it is empty when the CVE has no fix bound for that package. The "Age" column counts the days since the first observation. The "Export CSV" button exports the filtered list. The page shows at most 2,000 findings. Clicking a row opens the detail panel, described in Reading a finding.

Limits
- The module only sees inventoried packages. A binary built without package metadata, software installed by hand outside the package manager, or a dependency vendored into the code are not recorded. On Windows, only programs registered under the
Uninstallkeys are recorded; Windows updates (KB) are not. - For a Git repository, only the eight lock files named above are read; Maven, Gradle, Poetry, NuGet, Helm and the others are reported as not analysed.
- The
1.2before1.2.0comparison produces false positives for npm, PyPI and Maven packages whose version is written without a trailing zero. - Distribution advisories cover only Alpine, Debian and Ubuntu. On Red Hat, SUSE, Amazon Linux and the others, a backported fix without an upstream version change stays reported.
- Matching relies on NVD product names (CPE); a package whose name differs from the NVD product is not matched, and a product with the same name in another ecosystem is ruled out by the ecosystem filter but not by the name filter alone.
- The full catalogue is downloaded on the instance's first start; until it is loaded, existing findings are not closed and new scans are only partly matched.
- An image that could not be pulled, or whose analysis exceeded four minutes, has no package recorded and therefore no vulnerability; see Kubernetes.
- Exposure is only known for the resources the graph links to the Internet; a resource exposed through a path the graph does not model does not get its 10 points.
- No notification is sent for a new vulnerability; notifications cover the other finding types.