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.
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:
| Ecosystem | Graph comes from |
|---|---|
| Maven | mvn dependency:tree (multi-module aware) |
| Gradle | gradle dependencies --configuration runtimeClasspath |
| npm | package-lock.json v1–v3 |
| pnpm | pnpm-lock.yaml v5–v9 |
| yarn | yarn.lock, classic and Berry |
| Poetry | poetry.lock |
| uv | uv.lock |
| pip | requirements*.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
- Target version. The smallest version listed as fixed that stays on the installed major line. Major upgrades happen only with
fix.allow_major_upgrades: trueor--allow-major. - Direct dependency: bump it in the manifest, keeping your range operator (
^,~,>=). - Transitive dependency with
fix.strategy: prefer-direct-upgrade(the default): mrsentry checks registry metadata from Maven Central, npm and PyPI (orfix.registry_mirrors) for the smallest newer version of the direct parent that brings in a fixed version. If one exists, it bumps the parent. - 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
| Ecosystem | Direct dependency | Transitive pin | Lockfile regenerated with |
|---|---|---|---|
| Maven | literal <version> or the ${property} it uses | <dependencyManagement> entry, which overrides parents and BOMs | none (re-resolved with dependency:tree) |
| Gradle | coordinates in the build file or libs.versions.toml | dependencies { constraints { … because("CVE…") } } | none |
| npm | package.json range | overrides | npm install --package-lock-only |
| yarn | package.json range | resolutions | yarn install (classic) or --mode update-lockfile (Berry) |
| pnpm | package.json range | pnpm.overrides | pnpm install --lockfile-only |
| Poetry | constraint in pyproject.toml | explicit lower-bound dependency | poetry lock |
| uv | project.dependencies | tool.uv.constraint-dependencies | uv lock --upgrade-package |
| pip | == pin in requirements | added pin | none |
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
- 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.
- Build and test
The commands in
fix.verifyrun per ecosystem, with secrets scrubbed from the environment and a timeout offix.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 - AI repair
If a verify command fails and
fix.allow_llm_repairis on, thefix_repairmodel gets the failure output and sandboxed tools: read and search, plusedit_file,write_fileandrun_shelllimited tofix.shell_allowlist. It fixes compile or test breakage caused by the upgrade, but isn't allowed to revert the upgrade. It loops up tofix.max_repair_iterationstimes, re-verifying after each attempt. - Escalate
If verification still fails, a new MR opens as Draft with the
mrsentry::needs-humanlabel and the failure log attached.
Delivery modes

new-mr delivery: package table, how each was fixed, verification status and scan profile.| Mode | Behaviour |
|---|---|
new-mr | Branch <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-mr | A 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