Docs › Vulnerability fixing

Vulnerability fixing

mrsentry traces every High/Critical CVE in your image back to the manifest you own. It applies the smallest change that fixes it and proves the fix worked before delivering it.

A vulnerable transitive dependency, jackson-databind 2.13.3, reached through spring-boot-starter-json 2.7.2. Preferred fix: bump the direct parent. Otherwise pin the transitive version. orders-api your project spring-boot-starter-json 2.7.2 · direct jackson-databind 2.13.3 · transitive CVE-2022-42003 · fixed in 2.13.4.2 ① Preferred: bump the direct parent spring-boot-starter-json 2.7.2 → 2.7.5 already pulls jackson-databind 2.13.4.2 (checked against Maven Central) ② Otherwise: pin the transitive version <dependencyManagement> jackson-databind 2.13.4.2, then re-resolve the graph to prove it took effect

From CVE to dependency path

mrsentry builds a dependency graph for every project in the repository and finds every path from your project to the vulnerable package:

EcosystemGraph comes from
Mavenmvn dependency:tree (multi-module aware)
Gradlegradle dependencies --configuration runtimeClasspath
npmpackage-lock.json v1–v3
pnpmpnpm-lock.yaml v5–v9
yarnyarn.lock, classic and Berry
Poetrypoetry.lock
uvuv.lock
piprequirements*.txt, including pip-compile # via comments

The overview shows each path, for example orders-api → spring-boot-starter-json:2.7.2 → jackson-databind:2.13.3, and the manifest it belongs to.

Choosing the fix

  1. Target version. The smallest version listed as fixed that stays on the installed major line. Major upgrades happen only with fix.allow_major_upgrades: true or --allow-major.
  2. Direct dependency: bump it in the manifest, keeping your range operator (^, ~, >=).
  3. Transitive dependency with fix.strategy: prefer-direct-upgrade (the default): mrsentry checks registry metadata from Maven Central, npm and PyPI (or fix.registry_mirrors) for the smallest newer version of the direct parent that brings in a fixed version. If one exists, it bumps the parent.
  4. Otherwise, pin the transitive version with the ecosystem's own override mechanism. Verification also falls back to a pin when a parent bump turns out not to fix it.

What changes in each ecosystem

EcosystemDirect dependencyTransitive pinLockfile regenerated with
Mavenliteral <version> or the ${property} it uses<dependencyManagement> entry, which overrides parents and BOMsnone (re-resolved with dependency:tree)
Gradlecoordinates in the build file or libs.versions.tomldependencies { constraints { … because("CVE…") } }none
npmpackage.json rangeoverridesnpm install --package-lock-only
yarnpackage.json rangeresolutionsyarn install (classic) or --mode update-lockfile (Berry)
pnpmpackage.json rangepnpm.overridespnpm install --lockfile-only
Poetryconstraint in pyproject.tomlexplicit lower-bound dependencypoetry lock
uvproject.dependenciestool.uv.constraint-dependenciesuv lock --upgrade-package
pip== pin in requirementsadded pinnone

Edits preserve formatting: comments and key order in TOML/JSON, and indentation in pom.xml. The fix diff touches only the lines it needs to.

Verify, then repair

  1. Re-resolve

    The dependency graphs are rebuilt from the regenerated lockfiles and trees. Every target must now be at a fixed version, or the fix doesn't count.

  2. Build and test

    The commands in fix.verify run per ecosystem, with secrets scrubbed from the environment and a timeout of fix.verify_timeout_s. For example:

    fix:
      verify:
        maven: mvn -B -q -DskipITs verify
        gradle: ./gradlew build -x integrationTest
        npm: npm ci
        poetry: poetry install --no-root
      build_env_passthrough: [NEXUS_USER, NEXUS_PASSWORD, NPM_TOKEN]   # for settings.xml / .npmrc
  3. AI repair

    If a verify command fails and fix.allow_llm_repair is on, the fix_repair model gets the failure output and sandboxed tools: read and search, plus edit_file, write_file and run_shell limited to fix.shell_allowlist. It fixes compile or test breakage caused by the upgrade, but isn't allowed to revert the upgrade. It loops up to fix.max_repair_iterations times, re-verifying after each attempt.

  4. Escalate

    If verification still fails, a new MR opens as Draft with the mrsentry::needs-human label and the failure log attached.

Delivery modes

A fix MR opened by mrsentry with a table of upgraded packages, how each was fixed, and verification status
A new-mr delivery: package table, how each was fixed, verification status and scan profile.
ModeBehaviour
new-mrBranch <fix.branch_prefix>/<image>-<fingerprint>, one MR per CVE set, targeting fix.target_branch (default: the project's default branch). Re-runs update the same MR, and a different CVE set supersedes and closes older bot MRs. Labels come from fix.labels. fix.draft_mr forces Draft.
existing-mrA fast-forward commit on the MR's source branch, never a force push. The head commit is checked first. Fork MRs get the patch posted as a comment. If the source branch is protected, a fix MR targeting it is opened instead.

Commits are authored as fix.commit_author_name <fix.commit_author_email> and carry the Mrsentry-Fix: <fingerprint> trailer that acts as the loop guard.

Running it by hand

# new MR from the latest image scan
mrsentry fix --image orders-service --mode new-mr --project payments/orders-service

# push into MR !42, using a scan report from an earlier job
mrsentry fix --mode existing-mr --mr 42 --scan-report mrsentry-out/mrsentry-report.json

# see what would change without pushing
mrsentry fix --image orders-service:1.14.2 --mode new-mr --dry-run