vulndb/internal/audit: allow "" as a valid module version to check Top level packages can have module "" version. We want to consider them as valid versions for which all vulns should be applicable. Using just semver checking does not work, so this CL addresses that. Change-Id: I07479909d4c663416d6e91f0e10bb9fe87e413b5 Reviewed-on: https://go-review.googlesource.com/c/exp/+/342552 Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Trust: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/vulndb/internal/audit/detect.go b/vulndb/internal/audit/detect.go index 5c37973..8e1c0d0 100644 --- a/vulndb/internal/audit/detect.go +++ b/vulndb/internal/audit/detect.go
@@ -127,14 +127,27 @@ func (mv ModuleVulnerabilities) Filter(os, arch string) ModuleVulnerabilities { var filteredMod ModuleVulnerabilities for _, mod := range mv { + module := mod.mod + modVersion := module.Version + if module.Replace != nil { + modVersion = module.Replace.Version + } var filteredVulns []*osv.Entry for _, v := range mod.vulns { - if matchesPlatform(os, arch, v.EcosystemSpecific) { + // A module version is affected if + // - it is incuded in one of the affected version ranges + // - module version is "" + // The latter means the module version is not available, which + // should happen only for top-level packages for which we want + // to be more conservative. + // TODO: issue warning for "" cases above? + affectsVersion := modVersion == "" || v.Affects.AffectsSemver(modVersion) + if affectsVersion && matchesPlatform(os, arch, v.EcosystemSpecific) { filteredVulns = append(filteredVulns, v) } } filteredMod = append(filteredMod, modVulns{ - mod: mod.mod, + mod: module, vulns: filteredVulns, }) }
diff --git a/vulndb/internal/audit/detect_test.go b/vulndb/internal/audit/detect_test.go index 43db9dc..32cccba 100644 --- a/vulndb/internal/audit/detect_test.go +++ b/vulndb/internal/audit/detect_test.go
@@ -32,9 +32,9 @@ Version: "v1.0.0", }, vulns: []*osv.Entry{ - {ID: "a"}, - {ID: "b", EcosystemSpecific: osv.GoSpecific{GOOS: []string{"windows", "linux"}}}, - {ID: "c", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"arm64", "amd64"}}}, + {ID: "a", Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v2.0.0"}}}}, + {ID: "b", EcosystemSpecific: osv.GoSpecific{GOOS: []string{"windows", "linux"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Introduced: "v1.0.1"}}}}, + {ID: "c", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"arm64", "amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Introduced: "v1.0.0", Fixed: "v1.0.1"}}}}, {ID: "d", EcosystemSpecific: osv.GoSpecific{GOOS: []string{"windows"}}}, }, }, @@ -46,10 +46,20 @@ vulns: []*osv.Entry{ {ID: "e", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"arm64"}}}, {ID: "f", EcosystemSpecific: osv.GoSpecific{GOOS: []string{"linux"}}}, - {ID: "g", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}}, + {ID: "g", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Introduced: "v0.0.1", Fixed: "v2.0.1"}}}}, {ID: "h", EcosystemSpecific: osv.GoSpecific{GOOS: []string{"windows"}, GOARCH: []string{"amd64"}}}, }, }, + { + mod: &packages.Module{ + Path: "example.mod/c", + }, + vulns: []*osv.Entry{ + {ID: "i", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Introduced: "v0.0.0"}}}}, + {ID: "j", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v3.0.0"}}}}, + {ID: "k"}, + }, + }, } filtered := mv.Filter("linux", "amd64") @@ -61,9 +71,8 @@ Version: "v1.0.0", }, vulns: []*osv.Entry{ - {ID: "a"}, - {ID: "b", EcosystemSpecific: osv.GoSpecific{GOOS: []string{"windows", "linux"}}}, - {ID: "c", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"arm64", "amd64"}}}, + {ID: "a", Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v2.0.0"}}}}, + {ID: "c", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"arm64", "amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Introduced: "v1.0.0", Fixed: "v1.0.1"}}}}, }, }, { @@ -73,9 +82,18 @@ }, vulns: []*osv.Entry{ {ID: "f", EcosystemSpecific: osv.GoSpecific{GOOS: []string{"linux"}}}, - {ID: "g", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}}, + {ID: "g", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Introduced: "v0.0.1", Fixed: "v2.0.1"}}}}, }, }, + { + mod: &packages.Module{ + Path: "example.mod/c", + }, + vulns: []*osv.Entry{ + {ID: "i", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Introduced: "v0.0.0"}}}}, + {ID: "j", EcosystemSpecific: osv.GoSpecific{GOARCH: []string{"amd64"}}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v3.0.0"}}}}, + {ID: "k"}}, + }, } if !reflect.DeepEqual(filtered, expected) { t.Fatalf("Filter returned unexpected results, got:\n%s\nwant:\n%s", moduleVulnerabilitiesToString(filtered), moduleVulnerabilitiesToString(expected))
diff --git a/vulndb/internal/audit/vulnerability.go b/vulndb/internal/audit/vulnerability.go index 142bb57..def73d1 100644 --- a/vulndb/internal/audit/vulnerability.go +++ b/vulndb/internal/audit/vulnerability.go
@@ -18,29 +18,19 @@ mv := ModuleVulnerabilities{} for _, mod := range modules { modPath := mod.Path - modVersion := mod.Version if mod.Replace != nil { modPath = mod.Replace.Path - modVersion = mod.Replace.Version } vulns, err := client.Get([]string{modPath}) if err != nil { return nil, err } - // TODO(rolandshoemaker): we may want to consider moving this functionality into - // ModuleVulnerabilities.Filter, consolidating the filtering logic in one place. - var filteredVulns []*osv.Entry - for _, v := range vulns { - if v.Affects.AffectsSemver(modVersion) { - filteredVulns = append(filteredVulns, v) - } - } - if len(filteredVulns) == 0 { + if len(vulns) == 0 { continue } mv = append(mv, modVulns{ mod: mod, - vulns: filteredVulns, + vulns: vulns, }) } return mv, nil
diff --git a/vulndb/internal/audit/vulnerability_test.go b/vulndb/internal/audit/vulnerability_test.go index 79a1189..06783e7 100644 --- a/vulndb/internal/audit/vulnerability_test.go +++ b/vulndb/internal/audit/vulnerability_test.go
@@ -23,18 +23,15 @@ func TestFetchVulnerabilities(t *testing.T) { mc := &mockClient{ ret: map[string][]*osv.Entry{ - "example.mod/a": { - {ID: "a", Package: osv.Package{Name: "example.mod/a"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v2.0.0"}}}}, - {ID: "b", Package: osv.Package{Name: "example.mod/a"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v1.0.0"}}}}, - }, - "example.mod/b": {{ID: "c", Package: osv.Package{Name: "example.mod/b"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v1.0.0"}}}}}, + "example.mod/a": {{ID: "a", Package: osv.Package{Name: "example.mod/a"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v2.0.0"}}}}}, + "example.mod/b": {{ID: "b", Package: osv.Package{Name: "example.mod/b"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v1.1.1"}}}}}, "example.mod/d": {{ID: "c", Package: osv.Package{Name: "example.mod/d"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v2.0.0"}}}}}, }, } mv, err := FetchVulnerabilities(mc, []*packages.Module{ {Path: "example.mod/a", Version: "v1.0.0"}, - {Path: "example.mod/b", Version: "v1.0.0"}, + {Path: "example.mod/b", Version: "v1.0.4"}, {Path: "example.mod/c", Replace: &packages.Module{Path: "example.mod/d", Version: "v1.0.0"}, Version: "v2.0.0"}, }) if err != nil { @@ -49,6 +46,12 @@ }, }, { + mod: &packages.Module{Path: "example.mod/b", Version: "v1.0.4"}, + vulns: []*osv.Entry{ + {ID: "b", Package: osv.Package{Name: "example.mod/b"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v1.1.1"}}}}, + }, + }, + { mod: &packages.Module{Path: "example.mod/c", Replace: &packages.Module{Path: "example.mod/d", Version: "v1.0.0"}, Version: "v2.0.0"}, vulns: []*osv.Entry{ {ID: "c", Package: osv.Package{Name: "example.mod/d"}, Affects: osv.Affects{Ranges: []osv.AffectsRange{{Type: osv.TypeSemver, Fixed: "v2.0.0"}}}},