| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package semver |
| |
| import ( |
| "sort" |
| |
| "golang.org/x/vuln/internal/osv" |
| ) |
| |
| func Affects(a []osv.Range, v string) bool { |
| if len(a) == 0 { |
| // No ranges implies all versions are affected |
| return true |
| } |
| var semverRangePresent bool |
| for _, r := range a { |
| if r.Type != osv.RangeTypeSemver { |
| continue |
| } |
| semverRangePresent = true |
| if ContainsSemver(r, v) { |
| return true |
| } |
| } |
| // If there were no semver ranges present we |
| // assume that all semvers are affected, similarly |
| // to how to we assume all semvers are affected |
| // if there are no ranges at all. |
| return !semverRangePresent |
| } |
| |
| // ContainsSemver checks if semver version v is in the |
| // range encoded by ar. If ar is not a semver range, |
| // returns false. A range is interpreted as a left-closed |
| // and right-open interval. |
| // |
| // Assumes that |
| // - exactly one of Introduced or Fixed fields is set |
| // - ranges in ar are not overlapping |
| // - beginning of time is encoded with .Introduced="0" |
| // - no-fix is not an event, as opposed to being an |
| // event where Introduced="" and Fixed="" |
| func ContainsSemver(ar osv.Range, v string) bool { |
| if ar.Type != osv.RangeTypeSemver { |
| return false |
| } |
| if len(ar.Events) == 0 { |
| return true |
| } |
| |
| // Strip and then add the semver prefix so we can support bare versions, |
| // versions prefixed with 'v', and versions prefixed with 'go'. |
| v = canonicalizeSemverPrefix(v) |
| |
| // Sort events by semver versions. Event for beginning |
| // of time, if present, always comes first. |
| sort.SliceStable(ar.Events, func(i, j int) bool { |
| e1 := ar.Events[i] |
| v1 := e1.Introduced |
| if v1 == "0" { |
| // -inf case. |
| return true |
| } |
| if e1.Fixed != "" { |
| v1 = e1.Fixed |
| } |
| |
| e2 := ar.Events[j] |
| v2 := e2.Introduced |
| if v2 == "0" { |
| // -inf case. |
| return false |
| } |
| if e2.Fixed != "" { |
| v2 = e2.Fixed |
| } |
| |
| return Less(v1, v2) |
| }) |
| |
| var affected bool |
| for _, e := range ar.Events { |
| if !affected && e.Introduced != "" { |
| affected = e.Introduced == "0" || !Less(v, e.Introduced) |
| } else if affected && e.Fixed != "" { |
| affected = Less(v, e.Fixed) |
| } |
| } |
| |
| return affected |
| } |