Skip to content
Docs ·Vulnerability management

Configure

Vulnerability management

See every vulnerable dependency across your repositories, prioritized by exploitability and reachability, routed to the owning team, and fixed with one-click pull requests.

MergeHarbor reads the lockfiles and manifests of every enabled repository, checks each package version against its vulnerability database, and keeps the results current: when a new advisory is published, the repositories it affects show it within the hour, without a new scan. Vulnerability management is the Security add-on: $15 per active developer per month ($12 billed annually), sold separately on every plan (Essentials, Team, Advanced and Enterprise) with a 14-day trial. No plan includes it; on Enterprise it is added to your invoice. It counts the same active developers as your plan. If the add-on ends, your vulnerability data stays read-only and exportable for 30 days, then it is deleted. Searching the vulnerability database is free on every plan.

The Vulnerabilities area

Vulnerabilities in the sidebar covers open-source dependency risk. Code security (secrets, risky code, configuration, scans and the attack surface) is the separate Security area; see Security scans. It has six sections:

Section What it is for
Overview What to fix first and who owns it: the top risks in priority order, counts by severity, known exploited and reachable, SLA breaches and the trend.
Findings Dependency, license and risky-dependency findings, with filters, saved views and grouping. Exceptions are a view of the list.
Fixes Fix pull requests and the upgrades that would clear findings, before any pull request exists.
Inventory Packages, Licenses and SBOMs: what your repositories and services depend on.
Advisories The vulnerability database: search any advisory, in every plan.
Settings Alerts and tickets (with the inventory scope), SLA, Exceptions and License policies.

Who owns what (business units, teams and services) is set once under Settings → Ownership and used by both areas.

Inventories start on their own

The first inventory of a repository starts as soon as it is connected or enabled, and is read again after every index of the default branch (each push re-indexes it), and daily (or weekly) after that. Dependency files are parsed as data on MergeHarbor’s workers from the mirror the index already holds: nothing is installed or run, no model is called, and no credits are used. While the first inventories run, Overview shows how many repositories are done and what each one held.

A repository is covered when any of its dependency files can be read, with or without a lockfile. Not covered yet on Overview lists the rest with the reason: no dependency files, only files of an ecosystem that isn’t supported (such as build.sbt), or manifests whose versions couldn’t be resolved (each with an example and why). Commit a supported manifest or lockfile, or import an SBOM for it. Empty repositories are read again on their first push.

What counts as your code

Dependency files in test fixtures, examples and documentation aren’t what you ship: a fixture build.gradle pinning an old log4j is test input, not a vulnerability. By default MergeHarbor skips dependency files under testdata/, test-data/, fixtures/, __fixtures__/, __tests__/, src/test/, tests/, spec/ and test-project folders such as App.Tests/, under examples/, samples/ and demo/, under docs/, and generated output. Coverage lists them as skipped, with a count per reason. Vendored copies under vendor/ and third_party/ ship with your product, so they are read and their findings are labelled Vendored.

.gitattributes linguist-vendored, linguist-generated and linguist-documentation attributes override these path rules. Change the defaults in Vulnerabilities → Settings → Alerts and tickets → Inventory: turn on Test fixtures, examples and docs, add include or exclude globs, or override one repository. A repository can also set them in its root .mergeharbor.yml:

security:
  paths:
    include: ["examples/payments/**"]
    exclude: ["legacy/**"]
    scan_examples_and_tests: false

Include globs win over exclude globs and over the defaults. When a path leaves the scope, its open findings are resolved as out of scope, and the finding’s timeline records why. Test-only dependencies always score lower as development dependencies: Maven test scope, Gradle testImplementation and testRuntimeOnly, .NET test projects and npm devDependencies.

Repositories without lockfiles

Many projects, especially Java and .NET ones, have manifests but no lockfile. MergeHarbor reads those manifests the way the build tool would, without running it, and labels every package with how exact its version is:

Label Where the version comes from Exact?
Lockfile A lockfile Yes
Version from the manifest An exact version written in pom.xml, build.gradle, a .csproj and so on, including versions set by properties, dependencyManagement, parent POMs, BOMs, version catalogs and central package management Yes, as declared
Estimated: resolved from a range A range such as ^4.17.0 or >=2.30,<3, resolved to the version it picks today from the registry No
Estimated transitive dependency A dependency of your dependencies, from the deps.dev dependency graph of each direct dependency No

Estimated versions say so on the finding (“Estimated: the build may resolve differently. Add a lockfile or import an SBOM for exact results”) and lower its risk score slightly. Versions the build manages (Maven dependencyManagement, Gradle constraints and platforms, NuGet transitive pinning) replace the graph’s estimate.

What is read:

  • Maven pom.xml: ${property} interpolation, dependencyManagement, BOM imports (scope=import), parent POMs in the repository and multi-module reactors (a package is reported once per reactor). Parent and BOM POMs outside the repository are read from Maven Central (or your mirror) and cached. test and provided dependencies are development dependencies.
  • Gradle build.gradle and build.gradle.kts (without gradle.lockfile): string and map notation, variables from ext, val/def and gradle.properties, version catalogs (gradle/libs.versions.toml), platforms and BOMs, the Spring Boot plugin’s BOM, constraints and dynamic versions such as 1.+. A version computed by code is listed as unresolved with the expression.
  • .NET: PackageReference in .csproj, .fsproj and .vbproj, MSBuild properties and Directory.Build.props, central package management (Directory.Packages.props) and packages.config (without packages.lock.json).
  • npm package.json, Python requirements*.txt, pyproject.toml (PEP 621, Poetry, PDM), setup.cfg, Pipfile and a literal install_requires in setup.py, Rust Cargo.toml, Ruby Gemfile and PHP composer.json, when their lockfile is missing.

Transitive dependencies are estimated for Maven, Gradle, npm, PyPI, NuGet and Cargo. Ruby and PHP projects without a lockfile list their direct dependencies only. On an installation with AIR_GAPPED=true, no registry is reached: only versions written in the repository are read, transitive dependencies of projects without a lockfile aren’t estimated, and the coverage page says so. Parent and BOM POMs can still be read from an in-network Maven mirror (VULNDB_MAVEN_URL).

See vulnerabilities

Open Vulnerabilities → Findings for the whole organization, or the Vulnerabilities tab of a repository. Each finding is one advisory affecting one package version in one manifest, with:

  • Severity and the CVSS score, exploit likelihood (the EPSS probability of exploitation in the next 30 days) and a Known exploited badge when the vulnerability is in CISA’s catalog. Severity comes from the advisory’s own CVSS score. Linux distribution advisories often publish none, so they take the CVSS score of the CVE they link, shown as “Score from CVE-…”. Without one, the publisher’s own rating (such as Red Hat’s “Important”) is used and labelled as the vendor’s rating. An advisory with no rating at all shows Not rated.
  • Priority (P1 to P4), from a 0 to 100 risk score. The score starts from the severity and adds or removes points for exploit likelihood, reachability, whether the package is a direct, transitive or development dependency, whether a fix exists, and the owning service’s exposure and criticality. The finding lists every factor with its points. As a guide: known exploited or reachable critical vulnerabilities are P1, a high-severity direct dependency used at runtime is P2, high transitive or medium direct is P3, and development-only or not reachable findings are P4.
  • Advisory: the ecosystem’s own record (GO for Go, PYSEC or GHSA for PyPI, RUSTSEC or GHSA for Rust, GHSA for npm, Maven and NuGet), with its title. Other databases publishing the same vulnerability for the package are listed as Also published as. Container image and operating system advisories never apply to language packages. When two findings turn out to be the same vulnerability in the same package and manifest, the older one keeps its history and the other is marked Duplicate with a link to it.
  • Reachable, Not reachable or Unknown. When an advisory names the vulnerable functions, MergeHarbor looks for calls to them in the repository’s code graph. When it names a whole Go package (for example an unmaintained one), it checks whether any code uses that package. It says Unknown when the advisory names neither or the repository is not indexed yet. It never guesses. Not reachable findings rank lower but stay in the list; filter them out if you prefer.
  • Direct or transitive, with the path from the dependency your project declares to the vulnerable package.
  • The fixed version and the minimal safe upgrade: the lowest version that clears every known advisory of the package. A major-version upgrade is flagged.

Group the list by advisory, package, team, business unit, service or repository. Grouping by advisory shows each vulnerability once with every place it appears. Filter by severity, priority, known exploitation, reachability, fix availability, SLA status or owner, and save the filters as a view for yourself or your organization.

Supported lockfiles: package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml, Pipfile.lock, poetry.lock, uv.lock, go.mod, Cargo.lock, Gemfile.lock, composer.lock, gradle.lockfile and NuGet packages.lock.json. Supported manifests: pom.xml, build.gradle, build.gradle.kts, gradle/libs.versions.toml, *.csproj, *.fsproj, *.vbproj, Directory.Packages.props, packages.config, package.json, requirements*.txt, pyproject.toml, setup.cfg, setup.py, Pipfile, Cargo.toml, Gemfile and composer.json. For anything else, such as container images, import an SBOM.

Fix vulnerabilities

  • Open fix PR opens a pull request that upgrades the package to its minimal safe version. What it edits depends on the manifest:

    • npm, Yarn and pnpm (package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml): a direct dependency gets its version raised in package.json. For a transitive one, the pull request upgrades the direct dependency that brings it in when a newer version of that dependency pulls a fixed one. Otherwise it forces the fixed version with npm overrides, Yarn resolutions or pnpm pnpm.overrides.
    • Go (go.mod): the module’s version is raised in go.mod, including modules listed as // indirect.
    • pip (requirements*.txt): the package is pinned to the fixed version, and the pin is added when the file doesn’t list it.
    • npm without a lockfile (package.json): a direct dependency’s range is raised; a transitive one is forced with npm overrides.
    • Maven (pom.xml): the pull request edits where the version is defined: the dependency’s <version>, the property it uses, or its dependencyManagement entry, in whichever POM of the repository defines it. When a parent POM outside the repository sets the version through a property (such as Spring Boot’s snakeyaml.version), the property is overridden in the reactor’s top POM. A transitive dependency, or one managed by an imported BOM, gets an explicit dependencyManagement entry in the top POM, which takes precedence.
    • Gradle (build.gradle, build.gradle.kts): the coordinate string or map notation, the variable that holds the version (ext, val/def, gradle.properties), or the version catalog entry ([versions] or an inline library version). A transitive dependency, or one managed by a platform, gets a dependencies { constraints { … } } entry in the build file.
    • .NET: the Version (or VersionOverride) of the PackageReference, the PackageVersion in Directory.Packages.props, the MSBuild property that holds the version, or the entry in packages.config. A transitive dependency gets a central PackageVersion with CentralPackageTransitivePinningEnabled turned on, or a PackageReference in the project without central package management.

    The pull request doesn’t regenerate lockfiles. Its description gives the command to run, such as npm install or go mod tidy, notes when a property or catalog entry also sets other packages’ versions, and says when the version it replaces was estimated. Fix pull requests don’t edit Cargo, Bundler, Composer, Poetry, uv or Pipenv manifests (Cargo.toml, Gemfile, composer.json, pyproject.toml, setup.cfg, Pipfile), nor versions built from several variables (such as "${v}-jre"). For those, MergeHarbor shows the package-manager command to run, if one exists, or explains what to change, and opens no pull request.

    Fix pull requests open in the background, one per repository, so a batch over hundreds of repositories, or a fix in a very large repository, never times out. The dialog and the Fixes page show each repository live: queued, working (with the current step), opened with a link, or why it couldn’t be opened and what to do, with Retry. You can close the dialog or reload the page; progress continues. MergeHarbor reads the files it edits from its own copy of the repository and sends the code host only the changed files, so the size of the repository doesn’t matter. Retrying reuses the branch and pull request already created. Fix pull requests work on GitHub, GitLab, Bitbucket Cloud, Bitbucket Data Center, Gitea and Azure DevOps.

    When a fix pull request can’t be opened, the reason says what to change:

    • Permission missing: the MergeHarbor app or token can’t write to the repository. On GitHub, accept the updated App permissions (contents and pull requests: write); elsewhere, give the connection’s token write access.
    • Branch protection: a branch rule or ruleset refused the fix branch. Allow branches matching mergeharbor/fix-*, or exempt the MergeHarbor app.
    • Repository archived: unarchive it to open fix pull requests.
    • Copy not ready: MergeHarbor is still copying the repository. It’s refreshed automatically; retry in a few minutes.
  • File ticket creates a Jira issue or an Azure Boards work item with the details, the due date and a link back, the same way you create one from a review finding. Its priority follows the severity. Turn on automatic tickets in Vulnerabilities → Settings → Alerts and tickets (tracker, issue type and projects; connect Jira or Azure Boards in Integrations first) to file one for every new finding at or above a severity. Filing tickets needs write access: a Jira connected read-only shows Reconnect with write access, which asks Atlassian for write:jira-work only (it doesn’t turn on comments after reviews). Azure Boards needs vso.work_write on the app registration, or a token with Work Items: Read & write.

  • Assign a finding to a person, or move it through Triaged and In progress.

When a fix lands on the default branch, the finding closes by itself and the timeline records the version that fixed it.

Ownership

Findings go to the team that owns them:

  1. Create business units and assign your teams to them under Settings → Ownership. Teams can come from your identity provider or code host.
  2. Add services (applications, libraries, jobs) with their owning team, repositories (or a folder of a monorepo), exposure and criticality. Exposure and criticality feed the risk score.
  3. Repositories that no service claims are routed by their CODEOWNERS file. Suggested owners proposes a team for each unmapped repository, from CODEOWNERS and repository names; Create service turns a suggestion into a service.

Each team can list contacts (engineering manager, tech lead, security champion and others) for escalations.

Alerts

Add the New vulnerability, SLA breached and Exception requested events to a notification rule to get them in Slack, Microsoft Teams, Google Chat, Discord, PagerDuty or email. Choose the severity and the exploit likelihood (the top 10% or top 1% most likely to be exploited) that alert in Vulnerabilities → Settings → Alerts and tickets; known exploited vulnerabilities always alert.