Skip to content

Commit 21b36c0

Browse files
authored
feat: support checking if versions exist in crates.io (#391)
This adds support for checking that crates.io packages exist in the repository --------- Signed-off-by: Gareth Jones <[email protected]>
1 parent 3418df2 commit 21b36c0

File tree

3 files changed

+117
-1
lines changed

3 files changed

+117
-1
lines changed

tools/osv-linter/internal/pkgchecker/ecosystems.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func VersionsExistInEcosystem(pkg string, versions []string, ecosystem string) e
133133
case "CRAN":
134134
return nil
135135
case "crates.io":
136-
return nil
136+
return versionsExistInCrates(pkg, versions)
137137
case "Debian":
138138
return nil
139139
case "GIT":

tools/osv-linter/internal/pkgchecker/ecosystems_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,70 @@ package pkgchecker
22

33
import "testing"
44

5+
func Test_versionsExistInCrates(t *testing.T) {
6+
t.Parallel()
7+
8+
type args struct {
9+
pkg string
10+
versions []string
11+
}
12+
tests := []struct {
13+
name string
14+
args args
15+
wantErr bool
16+
}{
17+
{
18+
name: "multiple_versions_which_all_exist",
19+
args: args{
20+
pkg: "defmt",
21+
versions: []string{"0.0.0", "0.3.0", "0.3.100-rc.1", "1.0.0-rc.1", "1.0.1"},
22+
},
23+
wantErr: false,
24+
},
25+
{
26+
name: "multiple_versions_with_one_that_does_not_exist",
27+
args: args{
28+
pkg: "defmt",
29+
versions: []string{"1.1", "0.3.6-beta", "1.1.2"},
30+
},
31+
wantErr: true,
32+
},
33+
{
34+
name: "an_invalid_version",
35+
args: args{
36+
pkg: "defmt",
37+
versions: []string{"!"},
38+
},
39+
wantErr: true,
40+
},
41+
{
42+
name: "an_invalid_package",
43+
args: args{
44+
pkg: "!",
45+
versions: []string{"1.0.0"},
46+
},
47+
wantErr: true,
48+
},
49+
{
50+
name: "a_package_that_does_not_exit",
51+
args: args{
52+
pkg: "not-a-real-package-hopefully",
53+
versions: []string{"1.0.0"},
54+
},
55+
wantErr: true,
56+
},
57+
}
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
t.Parallel()
61+
62+
if err := versionsExistInCrates(tt.args.pkg, tt.args.versions); (err != nil) != tt.wantErr {
63+
t.Errorf("versionsExistInCrates() error = %v, wantErr %v", err, tt.wantErr)
64+
}
65+
})
66+
}
67+
}
68+
569
func Test_versionsExistInGo(t *testing.T) {
670
type args struct {
771
pkg string

tools/osv-linter/internal/pkgchecker/version_check.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,58 @@ import (
1515
"golang.org/x/mod/semver"
1616
)
1717

18+
// Confirm that all specified versions of a package exist in crates.io.
19+
func versionsExistInCrates(pkg string, versions []string) error {
20+
packageInstanceURL := fmt.Sprintf("%s/%s", EcosystemBaseURLs["crates.io"], pkg)
21+
22+
resp, err := faulttolerant.Get(packageInstanceURL)
23+
if err != nil {
24+
return fmt.Errorf("unable to validate package: %v", err)
25+
}
26+
defer resp.Body.Close()
27+
if resp.StatusCode != http.StatusOK {
28+
return fmt.Errorf("unable to validate package: %q for %s", resp.Status, packageInstanceURL)
29+
}
30+
31+
// Parse the known versions from the JSON.
32+
respJSON, err := io.ReadAll(resp.Body)
33+
if err != nil {
34+
return fmt.Errorf("unable to retrieve JSON for %q: %v", pkg, err)
35+
}
36+
// Fetch all known versions of package.
37+
versionsInRepository := []string{}
38+
releases := gjson.GetBytes(respJSON, "versions")
39+
releases.ForEach(func(key, value gjson.Result) bool {
40+
versionsInRepository = append(versionsInRepository, value.Get("num").String())
41+
return true // keep iterating.
42+
})
43+
// Determine which referenced versions are missing.
44+
versionsMissing := []string{}
45+
for _, versionToCheckFor := range versions {
46+
versionFound := false
47+
vc, err := semantic.Parse(versionToCheckFor, "crates.io")
48+
if err != nil {
49+
versionsMissing = append(versionsMissing, versionToCheckFor)
50+
continue
51+
}
52+
for _, pkgversion := range versionsInRepository {
53+
if r, err := vc.CompareStr(pkgversion); r == 0 && err == nil {
54+
versionFound = true
55+
break
56+
}
57+
}
58+
if versionFound {
59+
continue
60+
}
61+
versionsMissing = append(versionsMissing, versionToCheckFor)
62+
}
63+
if len(versionsMissing) > 0 {
64+
return &MissingVersionsError{Package: pkg, Ecosystem: "crates.io", Missing: versionsMissing, Known: versionsInRepository}
65+
}
66+
67+
return nil
68+
}
69+
1870
// Confirm that all specified versions of a package exist in Go.
1971
func versionsExistInGo(pkg string, versions []string) error {
2072
if pkg == "stdlib" || pkg == "toolchain" {

0 commit comments

Comments
 (0)