Skip to content

Fix false-positive vulnerability detection for Go pseudo-versions#14591

Draft
Copilot wants to merge 3 commits into
mainfrom
copilot/research-go-pseudo-versions
Draft

Fix false-positive vulnerability detection for Go pseudo-versions#14591
Copilot wants to merge 3 commits into
mainfrom
copilot/research-go-pseudo-versions

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 31, 2026

What are you trying to accomplish?

Go pseudo-versions like v1.4.0-rc1.0.20250929141436-dad09ab05b71 produce false-positive security alerts when a module doesn't follow Go's /vN import path convention. For example, github.com/hyperledger/fabric has v3.x tags but no /v3 path, so Go anchors the pseudo-version to the highest v1.x tag. The GoModules::Version class strips this to base 1.4.0, which satisfies advisory ranges like < 2.2.14 — triggering a vulnerability alert for code that's actually past the fix.

Root cause: SecurityAdvisory#vulnerable? compares the synthetic semver base against advisory ranges. This is correct for tagged releases but unreliable for pseudo-versions when the advisory's fix is in a different major version.

Fix: When the dependency version is a pseudo-version, filter out advisories whose safe (fix) versions are all in a different major version than the pseudo-version's base. This preserves vulnerability detection for:

  • Same-major advisories (reliable semver comparison)
  • Advisories with no safe_versions (conservatively flagged)
  • Multi-stream advisories where at least one fix matches the pseudo-version's major

Anything you want to highlight for special attention from reviewers?

The key design decision is when to skip an advisory for pseudo-versions. The approach uses the advisory's safe_versions major as the discriminator:

# advisory has safe_versions: [">= 2.2.14"] and pseudo-version base major is 1
# → all fix versions are in major 2, not major 1 → skip advisory
advisory.safe_versions.all? do |req|
  req.requirements.all? { |_op, version| version.segments.first != pseudo_major }
end

If safe_versions is empty (only vulnerable_versions specified), we conservatively treat the pseudo-version as vulnerable since we can't determine the fix version's major. This is a tradeoff — it avoids false negatives for advisories that don't specify fix versions.

The existing test for v0.0.0-20180826012351-8a410e7b638d with advisory < 1.0.1 (no safe_versions) continues to pass — that pseudo-version is correctly flagged as vulnerable.

Also consolidated the duplicated PSEUDO_VERSION_REGEX from LatestVersionFinder and PackageDetailsFetcher into Version.pseudo_version?. The regex in PackageDetailsFetcher was dead code.

How will you know you've accomplished your goal?

New test cases cover the key scenarios:

  • Cross-major advisory fix (v1.4.0-rc1.0... + safe >= 2.2.14) → vulnerable? returns false
  • Same-major advisory fix (v1.2.0-0... + safe >= 1.3.0) → returns true
  • No safe versions (v1.4.0-rc1.0... + only vulnerable < 2.2.14) → returns true (conservative)
  • Multi-stream advisory (safe >= 1.5.0 OR >= 2.2.14) → returns true (fix exists in same major)
  • Existing pseudo-version vulnerability test (v0.0.0-...) still passes

All 110 tests pass (0 failures, 6 pre-existing pending). Rubocop clean.

Checklist

  • I have run the complete test suite to ensure all tests and linters pass.
  • I have thoroughly tested my code changes to ensure they work as expected, including adding additional tests for new functionality.
  • I have written clear and descriptive commit messages.
  • I have provided a detailed description of the changes in the pull request, including the problem it addresses, how it fixes the problem, and any relevant details about the implementation.
  • I have ensured that the code is well-documented and easy to understand.

Copilot AI and others added 3 commits March 28, 2026 03:09
When a Go module doesn't follow the /vN import path convention
(e.g., github.com/hyperledger/fabric has v3.x tags but no /v3
path), Go generates pseudo-versions anchored to the highest
reachable v1.x tag. The synthetic v1.x semver would incorrectly
match advisories whose fix is in a different major version.

This change:
- Adds Version.pseudo_version? for centralized detection
- Overrides vulnerable? in GoModules::UpdateChecker to skip
  advisories whose safe (fix) versions are all in a different
  major version than the pseudo-version's base
- Preserves existing behavior for pseudo-versions without
  safe_versions and same-major advisories
- Removes duplicated PSEUDO_VERSION_REGEX from LatestVersionFinder
  and PackageDetailsFetcher in favor of Version.pseudo_version?

Agent-Logs-Url: https://github.com/dependabot/dependabot-core/sessions/92a8f914-3fa6-4fc0-aafb-6d907139e1b1

Co-authored-by: Nishnha <12107187+Nishnha@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants