Skip to content

Bug: IsCompatible returns true for two invalid/unparseable semver strings #34302

@AkshatRaj00

Description

@AkshatRaj00

Bug Report

Package: pkg/semverutil
File: pkg/semverutil/semverutil.go
Function: IsCompatible


Summary

IsCompatible silently returns true when both input strings are invalid semver — because semver.Major returns an empty string "" for invalid input, and "" == "" evaluates to true.

This means two completely garbage version strings are reported as "compatible", which can cause incorrect pin-upgrade decisions in the workflow linter.


Code

func IsCompatible(pinVersion, requestedVersion string) bool {
    pinVersion = EnsureVPrefix(pinVersion)
    requestedVersion = EnsureVPrefix(requestedVersion)

    pinMajor := semver.Major(pinVersion)           // returns "" for invalid input
    requestedMajor := semver.Major(requestedVersion) // returns "" for invalid input

    compatible := pinMajor == requestedMajor        // "" == "" → true ❌
    ...
    return compatible
}

Reproduction

result := semverutil.IsCompatible("not-a-version", "also-not-a-version")
fmt.Println(result) // prints: true  ← should be false

Similarly:

result := semverutil.IsCompatible("", "")
fmt.Println(result) // prints: true  ← should be false

Expected Behavior

IsCompatible should return false if either input is not a valid semver string. Compatibility is only meaningful between two parseable versions.


Proposed Fix

Add an explicit validity guard before comparing majors:

func IsCompatible(pinVersion, requestedVersion string) bool {
    pinVersion = EnsureVPrefix(pinVersion)
    requestedVersion = EnsureVPrefix(requestedVersion)

    // Guard: both versions must be valid semver before comparing majors.
    // semver.Major returns "" for invalid input, causing "" == "" to
    // incorrectly report two invalid versions as compatible.
    if !semver.IsValid(pinVersion) || !semver.IsValid(requestedVersion) {
        semverLog.Printf("IsCompatible: one or both versions are invalid: pin=%s, requested=%s",
            pinVersion, requestedVersion)
        return false
    }

    pinMajor := semver.Major(pinVersion)
    requestedMajor := semver.Major(requestedVersion)

    compatible := pinMajor == requestedMajor
    semverLog.Printf("Checking semver compatibility: pin=%s (major=%s), requested=%s (major=%s) -> %v",
        pinVersion, pinMajor, requestedVersion, requestedMajor, compatible)

    return compatible
}

Impact

Scenario Current Expected
IsCompatible("", "") true false
IsCompatible("not-a-version", "not-a-version") true false
IsCompatible("vfoo", "vbar") true false
IsCompatible("v5.0.0", "v5") true true
IsCompatible("v5.0.0", "v6") false false

Severity: Medium — any caller passing user-supplied or externally-fetched version strings that fail parsing will get a wrong compatibility result, potentially causing the linter to skip a version upgrade warning it should have raised.


Found during manual code review of pkg/semverutil/semverutil.go. The existing test suite in semverutil_test.go does not include invalid-input cases for IsCompatible.

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions