Dependency vulnerabilities

Project → Vulnerabilities lists the known vulnerabilities in the dependencies of your tracked branches and versions, and lets you decide on each one once for the whole project. The server finds them by matching each target’s dependency list with its own copy of the Trivy database (The vulnerability database). A scan does not have to run.

The tab sits between Issues and Dependencies. Everything on this page is in every edition except three sections, which need Business or Enterprise: exploited, alerts and export.

When the tab shows

The tab shows when both switches are on:

Switch Where
Vulnerability monitoring Settings → Vulnerability database, for the instance
Track vulnerabilities in this project Project → Settings → Vulnerability monitoring

With either one off, the tab and the badge are hidden. Decisions still work and are kept, and decisions made meanwhile apply when you switch monitoring on again.

The header names the date of the data and the time of the last check, such as “Vulnerability data from Oct 29, 2026 · checked Oct 30, 2026, 9:00 AM”.

Some states replace the table:

The tab says When
“No vulnerability data yet.” the server has no database. Instance admins get a link to Settings → Vulnerability database. Everyone else is told to ask an instance admin
“Vulnerability monitoring is off for this project.” a link to the tab is opened while a switch is off. Organization and project admins get a link to the project settings
“Not checked yet.” the server has not checked the tracked targets yet. It checks them after the next database update or analysis

A project with no released versions shows the CI snippets of Record a version on each tag.

By version

By version is the default view. It has one row per tracked target:

Column Meaning
Branch or version the name, with a main tag on the main branch, Pinned for a pinned version and “No dependency list” for a target without one
Critical, High, Medium the open vulnerabilities of that severity. Low and Unknown show in the list only
New “N new”: see New vulnerabilities

A count leaves out the vulnerabilities that a decision closed (Not affected, Won’t fix) and the ones that .trivyignore ignores (The .trivyignore file).

Select a row to see that target’s vulnerabilities. Its counts show beside its name.

Versions that are not tracked fold into one line, such as “2.1.9 · 2.0.5 · 14 more — not tracked”. Show lists them with “Not tracked” and the counts they kept. Hide folds them again.

The menu of a version row changes how the version is tracked. It needs the permission to change the project’s settings (Organization admin and Project admin, Roles).

Menu entry Effect
Pin the version is tracked, whatever the rule says
Unpin removes the pin: the rule decides
Mark as unsupported the version is never tracked
Track automatically removes the mark: the rule decides

The marks are the ones of Releases and versions.

By vulnerability

By vulnerability shows one row per vulnerability across all tracked targets. Select a row of By version to see the rows of one target.

On a row Meaning
severity, id the severity (Critical, High, Medium, Low or Unknown), also the colour of the row’s left edge, and the vulnerability’s id
package the package, its installed versions, and “Fix x.y.z” with the version that fixes it, or “No fix yet”
status Open, Not affected or Won’t fix
title the advisory’s title, on at most two lines
In / Not in the targets that have the vulnerability, and the tracked ones that do not
“Found ” when Qualor first saw it. “ without a scan” means the server found it after a new database, with no new analysis
“Ignored in .trivyignore” the row is dimmed and does not count
the decision a triaged row is dimmed. Its reason, or “Reason not given”, its comment, who decided and when show under the title

Two filters narrow the list:

Filter Options
Status All, Open, Not affected, Won’t fix
Severity All, Critical, High, Medium, Low, Unknown

Exploited and exploit probability (Business and Enterprise)

With the vuln.priority feature the tab shows which vulnerabilities attackers use already. The data comes from the CISA Known Exploited Vulnerabilities catalog (KEV) and from the FIRST Exploit Prediction Scoring System (EPSS). Without the feature the tab shows none of it.

Where What it shows
Exploited column of By version the open vulnerabilities of the target that are in KEV. A count leaves out decided and ignored ones, like the other counts. It shows 0 when there are none
Exploited (KEV) badge on a row the vulnerability is in KEV. The tooltip gives the date CISA added it, and says “Known to be used in ransomware” when KEV marks it so
EPSS on a row the probability that the vulnerability is exploited in the next 30 days, such as “EPSS 42.0%”. Below 0.1% it reads “<0.1%”. The tooltip adds the percentile
Exploited only filter lists only the rows with the badge. It is kept in the URL as exploited=true
order of the rows open rows first, then the others. Within each group, exploited rows come first, and both parts run by severity

KEV and EPSS cover CVE ids only. A vulnerability with another id, such as a GHSA id without a CVE alias, has no badge and no EPSS. The vulnerability database lists the sources and how the server keeps them up to date.

New vulnerabilities

A target shows “N new” for the vulnerabilities that need a decision. A vulnerability counts as new when it is all of these:

  • open, with no decision that closes it;
  • not ignored by .trivyignore;
  • found by the server after its first re-check that followed the target’s latest analysis.
Target When “new” clears
A branch with its next analysis, or when the vulnerability is triaged
A version when the vulnerability is triaged, or the version stops being tracked. A rescan of the same tag also resets it

Right after you upgrade to 0.15, nothing is new: what the server finds first is the baseline. A branch or version tracked for the first time shows nothing new from its first re-check either. A version that is tracked again keeps its earlier baseline, so the vulnerabilities found while it was untracked show as new.

The badge “New vulnerabilities in main and 2 versions” shows in the projects list and on the project page. It names the main branch, the number of branches and the number of versions with new vulnerabilities, such as “New vulnerabilities in 3 versions”. It links to the tab.

The projects list

The Vulnerabilities column of Projects shows each project at a glance.

It shows Meaning
“7 Critical”, “23 High” the open Critical and High vulnerabilities of the main branch. “No critical or high” when there are none
“1 exploited” the open vulnerabilities in KEV, on the main branch and the tracked versions, each id once. Business and Enterprise only
“5 releases” the tracked versions with an open vulnerability. It links to the tab
the badge see New vulnerabilities
“–” monitoring is off for the project, or the main branch has no dependency list yet

Open means without a decision that closes it, and not ignored by .trivyignore.

Alerts (Business and Enterprise)

With the vuln.alerts feature Qualor tells you about new vulnerabilities in the branches and versions it tracks, without you opening the tab.

Where What it does
New vulnerabilities in tracked versions in the project’s Watch menu, and a column in Settings → Notifications turns the two emails on or off for that watch. A new watch has it on. Without the feature the box is disabled and shows a Business pill
Digest email once a day at 06:00 UTC: the new High and Critical vulnerabilities that are open and not ignored
Exploited email at once, when a vulnerability is in the CISA KEV catalog
Slack and Teams the exploited vulnerabilities at once, and a section in the weekly digest (Slack and Microsoft Teams notifications)
vulnerabilities.found webhook event the same list as the digest, for generic webhooks (Webhooks)

Only vulnerabilities found after the alert baseline count. The emails, their limits and the baseline in detail are in Vulnerability emails. Every email has a link that stops them for all your watches.

Statuses and reasons

Triage sets a status for a vulnerability in a package:

Status Meaning Reason Comment
Open needs a decision no optional
Not affected (false positive) the project is not exposed required required
Affected, won’t fix the project is exposed and accepts it no required

A comment has at most 2000 characters. The decision records who made it and when.

The reason says why the project is not affected. The reasons are the five VEX justifications, in plain words:

Reason in the UI OpenVEX justification CycloneDX justification (in the export)
The package is not in the build component_not_present code_not_present
The vulnerable code is not in the build vulnerable_code_not_present code_not_present
The vulnerable code is never called vulnerable_code_not_in_execute_path code_not_reachable
An attacker cannot control the input vulnerable_code_cannot_be_controlled_by_adversary requires_environment
Another control already protects it inline_mitigations_already_exist protected_by_mitigating_control

Change status on a row opens the dialog. It needs the permission to triage issues (Maintainer and above). A Viewer reads the tab and sees no Change status button.

One decision, a wide scope

A decision belongs to a package name and a vulnerability id. It covers the whole project: the main branch, all branches and all versions. The ecosystem is not part of the key.

Applies to narrows a decision to one branch or version. That decision overrides the project’s decision on that target, and nowhere else. When you save a project-wide decision from a target’s view, the dialog can remove that target’s override in the same step.

Decisions are written whether monitoring is on or off.

Trivy issues on the Issues tab

A Trivy vulnerability is also an issue of the Trivy engine. Both places use the same decisions.

You do this What happens
Set a Trivy issue on main or on a tracked branch to Won’t fix or False positive the project’s decision changes. If that branch has a decision of its own, that decision changes instead
Reopen such an issue the decision becomes Open
Resolve such an issue nothing is written. The next dependency list shows whether the package changed
Triage a Trivy issue on another branch or in a merge request the issue changes and no decision is written

The other direction:

  • A project decision sets the matching Trivy issues on main and on tracked branches to its status. An Open decision reopens the ones that are False positive or Won’t fix.
  • On other branches and in merge requests, a decision that is not Open sets the open Trivy issues to that status.
  • A new Trivy issue takes the decision at ingestion when it is not Open.
  • A decision never changes resolved and closed issues. When an analysis finds such an issue again on main or a tracked branch, it reopens with the decision’s status, Open included.
  • A branch with its own decision is not changed by a project-wide decision.

The status dialog of the Issues tab asks for a Reason when you set False positive on such an issue. It says “Select a reason.” until you do. A Trivy issue without a package name that Qualor can read writes no decision and asks for no reason.

The decisions also change the gate: Won’t fix and false positive issues do not count (Issue statuses).

Migrated decisions

On upgrade, each False positive and Won’t fix Trivy issue on main became a project decision with no reason. When several issues share a package and vulnerability, the latest one counts, and its comment becomes the decision’s comment. The tab shows “Reason not given” and the Issues tab asks for a reason at the next edit.

Export SBOM and VEX (Business and Enterprise)

With the sbom feature the tab downloads a software bill of materials (SBOM) and a vulnerability exploitability exchange (VEX) document for one branch or version. Export SBOM and Export VEX sit in the header of the tab. Each opens a menu: choose the Target, then a format.

Button Formats Target must have
Export SBOM CycloneDX 1.6 (JSON), SPDX 2.3 (JSON) a dependency list
Export VEX CycloneDX VEX, OpenVEX a finished vulnerability check, with monitoring on

A menu lists only the targets that can be exported: the tracked ones, then the untracked versions. A button with no such target is not shown. Without the feature the tab shows neither button.

What an SBOM holds

An SBOM lists every package of the target’s dependency list, sorted by name, version and ecosystem.

  • A package is one entry. Packages that share a purl, such as one package in an npm and a yarn lockfile, are one entry.
  • A package without a purl is still listed.
  • The document names the project and the target, and carries the time it was made and Qualor as its tool.

Licences come from the dependency list. A package with several licences has all of them.

Format Licences
CycloneDX one SPDX expression joined with AND when every licence is a valid SPDX id or expression, else one name per licence. No licence, no licenses entry
SPDX licenseDeclared is the licences joined with AND. Free text becomes a LicenseRef-… whose text is in hasExtractedLicensingInfos. No licence is NOASSERTION. licenseConcluded is always NOASSERTION

What a VEX holds

A VEX has one entry for each vulnerability in each package of the target, with the target’s decision. A decision that applies to the target alone wins over the project-wide one. Vulnerabilities that .trivyignore ignores are left out. KEV and EPSS are not in the file.

A checked target with no findings gives a VEX with no entries. For OpenVEX that is an empty statements list, which the OpenVEX schema does not accept, because it asks for at least one statement. CycloneDX VEX has no such limit.

Status CycloneDX analysis OpenVEX status
Open state: in_triage under_investigation
Not affected (false positive) state: not_affected and a justification not_affected and a justification
Affected, won’t fix state: exploitable and response: [will_not_fix] affected and an action_statement

The reason of a not-affected decision becomes the justification of each format, as the reasons table shows.

The comment and the author of a decision travel with it.

Part CycloneDX OpenVEX
Comment analysis.detail impact_statement for not affected, action_statement for won’t fix
Who decided the property qualor:decided-by status_notes: “Decided by name.”
When analysis.lastUpdated the statement’s timestamp

A migrated decision has no reason, so it has no justification, and its comment says why. When OpenVEX needs a statement and the decision has no comment, the file says “No reason was recorded.” for not affected and “No action is planned.” for won’t fix. An entry with no decision has no author and no date. An Open decision, made by a reopen or as an override for one target, carries its author, date and (CycloneDX) comment like any other.

Which file a tool reads

Tool Reads
Trivy the OpenVEX file, with either SBOM: trivy sbom <sbom> --vex <file>
Dependency-Track the CycloneDX SBOM, imported first, then the CycloneDX VEX, imported into the same project

Dependency-Track applies a VEX decision only to a finding it holds under the same source and id. The VEX names a vulnerability by its CVE when it has one, with the source NVD. A finding that Dependency-Track takes from OSV or GitHub Advisories has a GHSA id instead, so its decision is not applied.

Download from CI

The same files come from the API. A project analysis token downloads the SBOM and VEX of its own project, so a release job needs no personal token. The parameters and an example are in SBOM and VEX export.

The .trivyignore file

The scanner sends the ids that the repository’s .trivyignore ignores, with their exp: dates (The vulnerability database). A vulnerability ignored there shows dimmed with “Ignored in .trivyignore”. It is not counted and is never new.

The rate limit

A decision counts as 1, plus 1 for each issue it changes, against the per-user budget of issue changes. The Issues tab uses the same budget: 120 changes a minute, with a burst of 500. Beyond it the API answers 429 RATE_LIMITED with a Retry-After header, and a decision is not saved.

The API

Route Needs Answers
GET /api/v0/projects/:id/vulnerabilities/targets read access to the project the tracked targets with their counts and new, the untracked versions, the data date and the last check
GET /api/v0/projects/:id/vulnerabilities read access the vulnerabilities, one per package and id. Query: target (branch:<id> or version:<id>), status (all, open, false_positive, wont_fix), severity, exploited=true (needs vuln.priority, else 403), page, pageSize (50 by default, at most 100). Empty while monitoring is off
PUT /api/v0/projects/:id/vulnerability-decisions issue.triage the decision and appliedIssues, the number of issues it changed

A decision is a body like this:

q -X PUT "$QUALOR_URL/api/v0/projects/<project id>/vulnerability-decisions" -d '{
  "packageName":"lodash","vulnerabilityId":"CVE-2021-23337","scope":{"kind":"project"},
  "status":"false_positive","reason":"vulnerable_code_not_in_execute_path",
  "comment":"Only used in a build script"}'
  • scope is {"kind":"project"}, {"kind":"branch","id":"…"} or {"kind":"version","id":"…"}. A merge request has no decisions.
  • reason is one of the five OpenVEX values above. It is required for false_positive and refused for any other status.
  • replaceOverrideOf removes a target’s decision in the same step. It needs a project-wide scope.
  • The pair must exist in the project, or the answer is 404.

POST /issues/{id}/transition and POST /issues/bulk-transition accept the same reason.

Code When
REASON_REQUIRED (422, body.reason) a false positive that writes a project decision has no reason
COMMENT_REQUIRED (422, body.comment) Not affected or Won’t fix has no comment

The MCP tool qualor_transition_issue accepts an optional reason too.

A decision is an audit event, vulnerability.decision_changed (Roles and the audit log, Enterprise).