Integration
Merge requests & reconciliation
Qualified patches become GitLab merge requests: one per fix, on their own branch, labelled for triage. And on every run, Argus closes the ones that no longer apply.
One merge request per fix
Each remediation becomes its own merge request. That keeps every change independently reviewable and mergeable: you can take the critical fix today and leave the low-severity one for next week, without untangling them. The merge request carries the patched, format-preserving POM, so the diff a reviewer sees is just the lines that had to change, each annotated with the advisory that drove it.
What a reviewer sees
The title leads with the vulnerability, not the version bump ([Security] Fix CVE-2022-25857 in snakeyaml (1.33)), so it reads as security work that needs attention rather than routine maintenance. The description is written for a human reviewer, not a machine. It opens with a single plain-language sentence covering what changed and why, followed by a compact facts table (advisory, severity, package, version), the advisory summary, and, for a transitive finding, the dependency graph. Every fact appears once. Argus deliberately keeps its own internals out of the description: you won’t see strategy codes, Maven mechanics, or build-validation logs. A major version bump adds a short Review before merging note so it gets the extra attention it deserves.
Each merge request closes with a one-line footer (Produced by Argus v(version) — automated dependency remediation), so anyone landing on the MR knows which tool and version opened it. Operational detail Argus needs for idempotency and debugging (the remediation key and the build-validation result) is kept in HTML comments at the very bottom, invisible in the rendered description.
Branches and labels
Every merge request is opened on its own source branch, named to read like the fix itself: it leads with the vulnerability and the affected artifact (security/<cve>-<artifact>), never an internal strategy name. Aggregated fixes lead with the primary advisory plus a count and a short stable hash; cleanup-only MRs live under maintenance/. The branch prefix is argus by default; set it with --branch-prefix or ARGUS_BRANCH_PREFIX. Merge requests are labelled security and argus by default, plus a severity label derived from the advisory, so your board can sort them at a glance:
argus/security/cve-2022-42889-commons-text # single fix: CVE + artifact
argus/security/cve-2022-25857-plus-3-a1b2c3 # aggregate: lead CVE + count
argus/maintenance/redundant-overrides-2026-06-11 # cleanup-only MR
labels: security, argus, criticalBreaking changes
When the only safe version is a major bump, the strategy ladder marks the remediation as breaking. That signal flows through to the merge request as an extra label (“Breaking Change” by default, set with --breaking-change-label), so a reviewer knows to look harder before merging. Argus never refuses a breaking fix; it surfaces it honestly and leaves the judgement to a human. The same warning and label apply when a major update is bundled into an aggregate merge request: the description lists each major update and asks for a build and test check before merging.
The dependency graph
Every merge request for a transitive finding carries a dependency graph drawn from the real SBOM path: application root, through the shared intermediaries, to the remediated vulnerable package. Blue is the application root, grey is a shared transitive node, and red is the vulnerable dependency the fix targets. The chain is never guessed from the target package alone; it reflects how the dependency is actually pulled in.
Argus renders this graph as an interactive SVG, uploads it to your GitLab project, and embeds it in the merge request description. Reviewers can zoom with the mouse wheel and pan by dragging, which keeps the graph legible even when a grand-slam remediation pulls dozens of paths into one picture. A layered layout with crossing reduction and fanned-out edges keeps dense graphs readable rather than collapsing into a tangle.
PDF security report
With ARGUS_PDF_REPORT enabled (the pdf_report component input, off by default), Argus attaches a polished, multi-page PDF to every merge request, for Maven projects and for npm projects (where support is early). It opens with a cover and an executive summary (a severity-distribution chart and a findings table), gives each advisory its own “what was found / how it was fixed” page with the dependency path, and closes with a short methodology section. The report is rendered browserlessly in pure Go (no headless Chrome), using the Argus design language with embedded fonts, so it looks identical on every machine.
Each finding page carries an advisory detail block: cross-reference identifiers (CVE / GHSA / OSV), severity with the CVSS vector, the affected version range, and two real-world risk signals. Exploitation flags whether the CVE is in CISA’s Known Exploited Vulnerabilities catalog and shows its EPSS probability, turning a static “High” into evidence of whether it is actually being exploited. Reachability notes whether the vulnerable package is a direct dependency or transitive, and how deep. A Provenance row appears when the advisory record did not come from OSV: “Declared in config.yml” for a declared advisory, the same annotation the merge request carries. These feeds are fetched best-effort and cached alongside the advisory data; when a CVE has no public score, the row is omitted. In an egress-controlled environment, ARGUS_EXPLOIT_INTEL=off keeps the report but skips both feeds. See Network access.
Because the report carries the full advisory detail, the merge-request body no longer repeats the long vulnerability list at the bottom: the CVE links in the table at the top remain the quick in-MR record, and the PDF is there when an engineer wants the whole story. Running with --no-publish writes a combined report.pdf into the output directory instead of attaching it.
The PDF carries bookmarks and document metadata for fast navigation, is named <repo>-<mr-iid>.pdf so it is self-describing in a document store, and links back to the merge request it accompanies. When the validation strategy ran the project’s tests (Maven surefire/failsafe, or an npm script that emits JUnit), Argus adds a Build verification section with a pass/fail summary and a per-suite table, so a governance gate can fast-track the change once tests are green.
Policy appendix
Teams running Argus under a governance process often need the same standing text on every merge request: a compliance notice, a link to an internal remediation policy, an approvals checklist, or where to ask questions. Point ARGUS_MR_APPENDIX (or --mr-appendix) at a Markdown file and Argus appends its contents to the description of every merge request it opens, below a horizontal rule, after the technical detail. The same appendix is applied to security, aggregate and single remediations (Maven and npm) and to cleanup merge requests, so coverage is uniform.
The file is read once per run and inserted as-is. It is your own trusted content, so it renders natively on GitLab and Argus does not rewrite it. The feature is best-effort: if the path is unset there is no appendix, and a missing or empty file logs a warning rather than blocking the merge request. Keep it reasonably sized, because the appendix counts toward GitLab’s merge-request description limit (~1 MB).
Grand-slam aggregation
Sometimes a single version move clears several advisories at once: a library with multiple CVEs, or a set of dependencies bound to one shared property. Rather than open near-duplicate merge requests, Argus can aggregate them into one “grand-slam” remediation that resolves the whole group in a single change. This is on by default and controlled with ARGUS_GRAND_SLAM; set it off to force one merge request per advisory.
Since 2.9.0 Argus qualifies the aggregate first: when more than one fix is pending it runs mvn verify on the combined change once, up front, and if that passes it publishes the single grand-slam merge request and skips re-qualifying each fix individually. Only if the aggregate fails to build does it fall back to qualifying each fix on its own to find the publishable subset. On a project with many fixes this collapses the qualification phase from one Maven build per fix to a single build. See qualification concurrency.
The publication budget
The first run against a long-neglected project can find a great many fixes. ARGUS_MAX_MRS caps how many merge requests a single run opens, so you can drain the backlog in reviewable batches across successive runs rather than all at once. Left unset, there is no cap.
Reconciliation
Argus is stateful about its own output. On every run it reconciles: merge requests it previously opened that no longer correspond to outstanding work (because the dependency was upgraded, the advisory was withdrawn, or a human merged the fix) are closed, so the set of open Argus merge requests always reflects reality. It only ever touches merge requests it owns; anything a person opened is left strictly alone. A merge request that was merged earlier suppresses a new one for the same remediation only while the target branch still carries it; if the fix was reverted or superseded, Argus publishes it again rather than treating the old merge as final.
Two policies fine-tune the edges. ARGUS_RECONCILIATION_STRATEGY is safe by default, which means a merge request merely deferred by the ARGUS_MAX_MRS budget is not closed; reset is the more aggressive setting an operator can authorise. ARGUS_CLEANUP_ONLY_POLICY decides whether a run that finds only stale-override cleanup (and no new fixes) still publishes that work or skips it.
Reconciliation and stale-override cleanup run for both Maven and npm projects, so a repository of either ecosystem is tidied the same way.
Branch pruning
Closing a merge request does not delete its source branch, and a merged branch lingers unless the project removes it automatically, so the argus/… branches accumulate. With ARGUS_PRUNE_BRANCHES enabled (default off), a successful reconciled run also deletes Argus-owned branches that have no open merge request. A branch is removed only when it sits under the managed branch prefix, is neither the default nor a protected branch, is not part of the work just published, and its tip commit was authored by Argus (the message carries the generated commit prefix and the via Argus marker). A commit a human pushed on top changes the tip and blocks deletion, so in-flight or hand-edited work is never lost.