config.yml
Declared advisories
Act on a CVE your scanner flagged that your advisory sources do not carry yet. A declaration behaves like any OSV advisory for exactly as long as no source carries the record, then the source record takes over automatically.
What it is for
Argus reasons from OSV.dev, and OSV has a publication pipeline: an advisory can sit upstream unreviewed, with no package mapping, for weeks. In that window a GitLab dependency-scanning dashboard (which uses GitLab’s own analyst-curated database) can flag a critical finding on the very POM Argus maintains, while Argus correctly reports the project clean. You have the facts the scanner showed you: coordinate, affected range, fixed version. declare_advisories is how you hand those facts to Argus.
If the project is on GitLab Ultimate, the gitlab advisory source reads those same facts from the vulnerability report without a hand-written entry. A declaration is still the right tool when the finding’s solution names no fixed version, when the project’s tier has no vulnerability API, or when the advisory came from somewhere other than GitLab.
A declared advisory flows through the entire engine exactly like an OSV one: the strategy ladder plans a fix, anchor and parent upgrades can consolidate it, the patch is qualified with Maven, cleanup keeps the fix load-bearing while the declaration stands, and the merge request explains itself. It is injected at every discovery site, so a declared fix can never be reclaimed by a later pass that cannot see the declaration.
The shape of a declaration
| Property | Default | Description |
|---|---|---|
idstring | — | The advisory identifier: CVE-YYYY-NNNN or GHSA-xxxx-xxxx-xxxx. Required. Repeat the id across entries for multiple ranges or packages. |
packagestring | — | The affected groupId:artifactId. Required. |
introducedstring | "0" | The first affected version. Empty means affected from the beginning, matching OSV range semantics. |
fixedstring | — | The first version that clears the advisory (exclusive bound). Required: a declaration without a fix version cannot plan a remediation. |
severitystring | UNKNOWN | One of CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN. Drives merge-request labels and the PDF report. |
summarystring | — | Free text shown in reports and merge-request copy. Say where the claim came from. |
expiresdate | never | An optional YYYY-MM-DD hard stop. The entry stays live through the stated date, then is skipped with a warning: a forced review date. |
# config.yml — Argus 3.5
version: 3
declare_advisories:
- id: CVE-2026-59083
package: org.apache.tomcat.embed:tomcat-embed-core
introduced: "11.0.0-M1"
fixed: "11.0.24"
severity: CRITICAL
summary: "RewriteValve security constraint bypass (from our dependency-scanning finding; OSV has no record yet)"
expires: 2026-12-01
# The same CVE on a second supported release line: repeat the id.
- id: CVE-2026-59083
package: org.apache.tomcat.embed:tomcat-embed-core
introduced: "10.1.0-M1"
fixed: "10.1.57"OSV always wins
The moment OSV publishes the same advisory (matched per id, aliases included, so a declared CVE is superseded by an OSV GHSA that lists it), the declaration is dropped for that run and OSV’s ranges, fix versions, severity, and withdrawn status become authoritative. Precedence is deliberately per advisory, not per package: OSV knowing other advisories for the same coordinate never suppresses the one it lacks.
Knowing when a declaration is spent
Every run answers “is this entry still doing anything?” with three signals:
- Superseded. OSV now publishes the id: a warning tells you to delete the entry, and
report.jsonlists it undersuperseded_declarations. - Inert. The package resolves at or above
fixedeverywhere (the fix merged, or a platform bump carried it): the run logs “matches nothing; safe to delete”. - Expired. The
expiresdate passed: skipped with a warning naming the date.
Interplay with the rest of the config
- Offlimits, ceilings and couplings gate declared fixes exactly as they gate OSV ones. A declared advisory whose package is off-limits produces no fix; the run warns so the contradiction is visible.
ignore_advisoriesanddeclare_advisoriesnaming the same id is refused at load: suppressing and declaring one advisory is a contradiction to resolve, not to guess about.- Maven only in this milestone, matching the rest of the policy surface; npm follows when npm policy does.
- A non-empty block is the
configentry ofadvisory_sources. It is part of the default list and appended automatically to a custom list that omits it, so declarations are never silently dropped.
Where the provenance shows
A remediation justified by your own configuration must say so everywhere a reviewer looks. Declared findings carry "source": "config" in report.json, merge-request rows are annotated (declared in config.yml) and link CVE ids to NVD and GHSA ids to GitHub’s advisory database (their osv.dev pages do not exist yet), the SARIF and PDF report carry the same note, and the run summary counts active and superseded declarations.