Concepts

Format-preserving patches

A POM is a file a human maintains, not a machine artefact. Argus changes only the bytes that have to change and leaves everything else (comments, spacing, ordering) untouched.

The serialisation problem

The obvious way to edit a pom.xml is to parse it into an XML document, change a node, and write the document back out. It is also the wrong way. Every XML serialiser makes its own choices about attribute quoting, self-closing tags, namespace declarations, blank lines, and the order of sibling elements. Round-tripping a POM through one rewrites the whole file, so a one-line version bump arrives as a diff that touches hundreds of lines. Worse, most serialisers discard comments and collapse the careful whitespace that makes a POM readable. A reviewer can no longer see what actually changed, and the tool has quietly editorialised over work a human did by hand.

Byte-level edits

Argus never serialises a tree. It parses your POM to learn the exact byte offsets of the elements it cares about (the span of a <version>, the bounds of a <dependencyManagement> block) and then expresses each remediation as a small set of byte-range replacements. Those edits are applied back-to-front, so replacing one span never shifts the offsets of the spans not yet applied. The result is the original file with surgical substitutions: the bytes Argus touched are different, and every other byte is identical.

pom.xml (before)
  <!-- pinned for the 2.x line; do not touch without asking infra -->
  <properties>
    <commons.text.version>1.9</commons.text.version>
  </properties>
pom.xml (after)
  <!-- pinned for the 2.x line; do not touch without asking infra -->
  <properties>
    <!-- PATCH|BUMP|CVE-2022-42889|Bumped commons-text from 1.9 to 1.10.0 to fix vulnerability. -->
    <commons.text.version>1.10.0</commons.text.version>
  </properties>

Matching your style

When Argus has to insert new structure (synthesising a <dependencyManagement> block, or adding an <exclusion>), it first inspects the surrounding lines to learn how your file is indented. It detects the indentation of the enclosing element and the step your file uses for children, then emits the new nodes to match. Whether your POM uses two spaces, four, or tabs, the inserted lines line up with what is already there, so the patch reads as if you had typed it yourself.

The annotation comment

Every edit Argus makes is preceded by a structured comment so the change is self-documenting in the diff and in the file forever after. The format is a pipe-delimited record:

<!-- PATCH|STRATEGY|ADVISORY|human-readable reason -->

The strategy field is one of BUMP, OVERRIDE, EXCLUDE, ADD, or MODULE_COMPANION (a dependency a platform upgrade proved necessary, 3.1), matching the mechanism that produced the edit. A reviewer reading the merge request sees not just what moved but why: which advisory drove the change and how Argus chose to apply it.

When a single fix clears many advisories (typically an anchor upgrade that resolves dozens of transitive findings at once), the advisory field is capped to keep the comment readable: it inlines the first few ids and summarises the rest as +N-more. The cap is ARGUS_MAX_COMMENT_CVES (default 6; set 0 to inline all). The complete advisory list is always preserved in the merge-request body and report.json, and the cap does not affect Argus' ability to later detect and remove the override once it becomes redundant.

The comments are a working ledger: please leave them with their changes

The annotations are not just documentation. They are how Argus recognises its own changes on every later run: the standing cleanup pass reads them to decide when an override, exclusion, or property pin has become unnecessary (for example when the platform BOM catches up and now manages the safe version natively), and then removes the comment and its change together, automatically. A POM patched by Argus tends back toward its original shape over time; the comments are the bookkeeping that makes that possible.

Two practical rules for developers:

  • Keeping the fix? Keep the comment. Deleting a comment while leaving the change in place orphans it: Argus can no longer tell the pin is its own, so it will never clean it up: the override outlives its purpose forever, and Argus treats it as a deliberate developer decision from then on. Nothing breaks, but you lose the automatic hygiene.
  • Taking a fix over manually? Delete the comment and the change as one unit. If you decide to bump the dependency yourself (a newer version, a different approach), remove Argus’s edit and its comment together. That is fully supported and expected: from that point the coordinate is yours, and Argus respects developer-authored versions (it will raise a developer’s own dependencyManagement pin in place rather than working around it, and it never re-adds a change you removed).

Every generated merge request carries a one-line reminder of these rules, so reviewers see them at the moment they matter.

Why it matters

A small, legible diff is a reviewable diff. Because Argus changes only what it must, a human can approve a remediation in seconds: the version moved, the annotation explains it, nothing else is different. That is what makes the tool trustworthy to run unattended. See How Argus works for where patching sits in the wider pipeline, and Merge requests for how patches become reviewable changes.