Skip to content

Commit 80e7a64

Browse files
committed
feat: support checking if versions exist in NPM
Signed-off-by: Gareth Jones <[email protected]>
1 parent c463400 commit 80e7a64

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
@@ -155,7 +155,7 @@ func VersionsExistInEcosystem(pkg string, versions []string, ecosystem string) e
155155
case "MinimOS":
156156
return nil
157157
case "npm":
158-
return nil
158+
return versionsExistInNpm(pkg, versions)
159159
case "NuGet":
160160
return nil
161161
case "openSUSE":

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,70 @@ func Test_versionsExistInGo(t *testing.T) {
3838
}
3939
}
4040

41+
func Test_versionsExistInNpm(t *testing.T) {
42+
t.Parallel()
43+
44+
type args struct {
45+
pkg string
46+
versions []string
47+
}
48+
tests := []struct {
49+
name string
50+
args args
51+
wantErr bool
52+
}{
53+
{
54+
name: "multiple_versions_which_all_exist",
55+
args: args{
56+
pkg: "semver",
57+
versions: []string{"1.0.1", "2.0.0-beta", "5.7.1"},
58+
},
59+
wantErr: false,
60+
},
61+
{
62+
name: "multiple_versions_with_one_that_does_not_exist",
63+
args: args{
64+
pkg: "semver",
65+
versions: []string{"1.1", "2.0.0-beta1", "3.1.5", "5.1rc1"},
66+
},
67+
wantErr: true,
68+
},
69+
{
70+
name: "an_invalid_version",
71+
args: args{
72+
pkg: "semver",
73+
versions: []string{"!"},
74+
},
75+
wantErr: true,
76+
},
77+
{
78+
name: "an_invalid_package",
79+
args: args{
80+
pkg: "!",
81+
versions: []string{"1.0.0"},
82+
},
83+
wantErr: true,
84+
},
85+
{
86+
name: "a_package_that_does_not_exit",
87+
args: args{
88+
pkg: "not-a-real-package-hopefully",
89+
versions: []string{"1.0.0"},
90+
},
91+
wantErr: true,
92+
},
93+
}
94+
for _, tt := range tests {
95+
t.Run(tt.name, func(t *testing.T) {
96+
t.Parallel()
97+
98+
if err := versionsExistInNpm(tt.args.pkg, tt.args.versions); (err != nil) != tt.wantErr {
99+
t.Errorf("versionsExistInNpm() error = %v, wantErr %v", err, tt.wantErr)
100+
}
101+
})
102+
}
103+
}
104+
41105
func Test_versionsExistInPackagist(t *testing.T) {
42106
t.Parallel()
43107

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,58 @@ func goVersionsExist(versions []string) error {
129129
return nil
130130
}
131131

132+
// Confirm that all specified versions of a package exist in npm.
133+
func versionsExistInNpm(pkg string, versions []string) error {
134+
packageInstanceURL := fmt.Sprintf("%s/%s", EcosystemBaseURLs["npm"], pkg)
135+
136+
resp, err := faulttolerant.Get(packageInstanceURL)
137+
if err != nil {
138+
return fmt.Errorf("unable to validate package: %v", err)
139+
}
140+
defer resp.Body.Close()
141+
if resp.StatusCode != http.StatusOK {
142+
return fmt.Errorf("unable to validate package: %q for %s", resp.Status, packageInstanceURL)
143+
}
144+
145+
// Parse the known versions from the JSON.
146+
respJSON, err := io.ReadAll(resp.Body)
147+
if err != nil {
148+
return fmt.Errorf("unable to retrieve JSON for %q: %v", pkg, err)
149+
}
150+
// Fetch all known versions of package.
151+
versionsInRepository := []string{}
152+
releases := gjson.GetBytes(respJSON, "versions.@keys")
153+
releases.ForEach(func(key, value gjson.Result) bool {
154+
versionsInRepository = append(versionsInRepository, value.String())
155+
return true // keep iterating.
156+
})
157+
// Determine which referenced versions are missing.
158+
versionsMissing := []string{}
159+
for _, versionToCheckFor := range versions {
160+
versionFound := false
161+
vc, err := semantic.Parse(versionToCheckFor, "npm")
162+
if err != nil {
163+
versionsMissing = append(versionsMissing, versionToCheckFor)
164+
continue
165+
}
166+
for _, pkgversion := range versionsInRepository {
167+
if r, err := vc.CompareStr(pkgversion); r == 0 && err == nil {
168+
versionFound = true
169+
break
170+
}
171+
}
172+
if versionFound {
173+
continue
174+
}
175+
versionsMissing = append(versionsMissing, versionToCheckFor)
176+
}
177+
if len(versionsMissing) > 0 {
178+
return &MissingVersionsError{Package: pkg, Ecosystem: "npm", Missing: versionsMissing, Known: versionsInRepository}
179+
}
180+
181+
return nil
182+
}
183+
132184
// Confirm that all specified versions of a package exist in Packagist.
133185
func versionsExistInPackagist(pkg string, versions []string) error {
134186
packageInstanceURL := fmt.Sprintf("%s/%s.json", EcosystemBaseURLs["Packagist"], pkg)

0 commit comments

Comments
 (0)