Skip to content

Add /v5 and /v6 major version suffix to the module path so advisories and scanners can match releases #4485

Description

@stevenvegt

Problem

The module path is github.com/nuts-foundation/nuts-node while releases are tagged v5.x and v6.x. Go module rules say a module with a go.mod file must carry the major version in its path from v2 on, so the Go module proxy (proxy.golang.org, the source of truth for which versions of a module exist) rejects every v5 and v6 tag as a version of this module. The highest version the ecosystem knows is v1.1.0.

This breaks security advisories, not just builds. A scanner match is "installed version falls inside the advisory's vulnerable range", and that range is stored as boundaries: an introduced version and a fixed version. GitHub and the Go vulnerability database only store boundaries that the proxy confirms are real versions of the module. Our advisory GHSA-9hmg-827w-9rhj (CVE-2026-41164) was filed as <= v6.2.2, fixed in v6.2.3 and <= v5.4.30, fixed in v5.4.31. Since none of those are versions of the module, it is stored as:

  • GitHub global advisory (used by Trivy, Grype, Docker Scout): <= 1.1.0, no fix. The range was clamped to the newest version the proxy knows.
  • Go vulnerability database GO-2026-5291 (used by govulncheck, OSV): all versions from 0, no fix.

Consequences, verified with Trivy and Syft against the 6.2.10 image and a locally built binary:

  • Trivy and Grype never match a v6 image at any version, because 6.x is above the stored <= 1.1.0 range. A customer running 6.1.0 gets a clean scan. This is a false negative on a real CVE.
  • govulncheck and OSV-based tools flag every version, including fixed ones, and no release can make that go away.
  • Downstream repos (nuts-knooppunt, nuts-node-private, data-viewer, and others) can only depend on pseudo-versions such as v1.0.1-0.20260713064648-0b7d3a55aed2, so Dependabot and govulncheck cannot reason about their nuts-node dependency either.

The pending pipeline changes (ldflags version key, -buildvcs=false, tag-derived version) make the binary report the correct version to scanners. They cannot fix the advisory side: the databases must be able to store our fix versions as boundaries, and with the current module path they cannot. Build-time import rewriting, attached SBOMs and image labels have the same limitation, because they change what the binary says, not what the databases can store.

Proposal

Change the module path to github.com/nuts-foundation/nuts-node/v6 and rewrite all internal imports (about 2000 files, mechanical, with gomajor path from github.com/icholy/gomajor or sed). Apply it per maintained branch:

  • master: /v6 now, /v7 on the commit that starts the next major.
  • V6.2: /v6, so the next 6.2.x patch is the first release advisories can name as a fix.
  • V5.4: /v5, only if 5.4.x fix versions still need to appear in advisories.

No v6/ subdirectory: majors are already separated by release branch, so changing go.mod and imports in place is the correct form.

After the first post-rename tag exists on the proxy, edit the repository advisory (Security, Advisories) so the package field reads github.com/nuts-foundation/nuts-node/v6 with that tag as the patched version, and request the same correction on the global advisory. Once Go stamps the version itself from the tag, -buildvcs=false and the ldflags version key can be removed again.

Costs

  • One mechanical PR per maintained branch, repeated at every major (roughly every two years so far).
  • Every downstream importer must update its import paths once. Until they do, nothing breaks for them, but they cannot receive any newer version: Go treats a new major as a new import path.
  • Existing tags stay invalid module versions, because the go.mod at those commits lacks the suffix. Only tags created on commits after the rename become resolvable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions