@@ -2,7 +2,11 @@ package fetch
22
33import (
44 "context"
5+ "errors"
56 "testing"
7+
8+ "github.com/git-pkgs/registries"
9+ "github.com/git-pkgs/registries/client"
610)
711
812func TestResolveWithoutRegistry (t * testing.T ) {
@@ -118,6 +122,111 @@ func TestEncodeGoModule(t *testing.T) {
118122 }
119123}
120124
125+ func TestCheckMetadataURL (t * testing.T ) {
126+ tests := []struct {
127+ url string
128+ ok bool
129+ }{
130+ {"https://files.pythonhosted.org/packages/ab/cd/pkg-1.0.tar.gz" , true },
131+ {"https://registry.npmjs.org/pkg/-/pkg-1.0.0.tgz" , true },
132+ {"https://github.com/owner/repo/archive/v1.0.tar.gz" , true },
133+
134+ {"http://files.pythonhosted.org/pkg.tar.gz" , false },
135+ {"file:///etc/passwd" , false },
136+ {"file://etc/passwd" , false },
137+ {"gopher://evil.example/" , false },
138+ {"ftp://ftp.example.com/pkg.tar.gz" , false },
139+ {"javascript:alert(1)" , false },
140+ {"https://" , false },
141+ {"https:///path/only" , false },
142+ {"//169.254.169.254/latest/meta-data/" , false },
143+ {"169.254.169.254/latest/meta-data/" , false },
144+ {"" , false },
145+ {"\x00 https://evil" , false },
146+ }
147+
148+ for _ , tt := range tests {
149+ err := checkMetadataURL (tt .url )
150+ if tt .ok && err != nil {
151+ t .Errorf ("checkMetadataURL(%q) = %v, want nil" , tt .url , err )
152+ }
153+ if ! tt .ok {
154+ if err == nil {
155+ t .Errorf ("checkMetadataURL(%q) = nil, want error" , tt .url )
156+ } else if ! errors .Is (err , ErrUnsafeURL ) {
157+ t .Errorf ("checkMetadataURL(%q) error not ErrUnsafeURL: %v" , tt .url , err )
158+ }
159+ }
160+ }
161+ }
162+
163+ type fakeRegistry struct {
164+ versions []registries.Version
165+ }
166+
167+ func (f * fakeRegistry ) Ecosystem () string { return "fake" }
168+ func (f * fakeRegistry ) URLs () client.URLBuilder { return & client.BaseURLs {} } //nolint:ireturn // satisfying Registry interface
169+ func (f * fakeRegistry ) FetchVersions (ctx context.Context , name string ) ([]registries.Version , error ) {
170+ return f .versions , nil
171+ }
172+
173+ func TestResolveFromMetadataRejectsUnsafeURL (t * testing.T ) {
174+ tests := []struct {
175+ name string
176+ field string
177+ url string
178+ }{
179+ {"file scheme via download_url" , "download_url" , "file:///etc/passwd" },
180+ {"http via tarball" , "tarball" , "http://169.254.169.254/latest/meta-data/" },
181+ {"empty host via download_url" , "download_url" , "https:///just/a/path" },
182+ }
183+
184+ for _ , tt := range tests {
185+ t .Run (tt .name , func (t * testing.T ) {
186+ reg := & fakeRegistry {
187+ versions : []registries.Version {{
188+ Number : "1.0.0" ,
189+ Metadata : map [string ]any {tt .field : tt .url },
190+ }},
191+ }
192+ r := NewResolver ()
193+ r .RegisterRegistry (reg )
194+
195+ info , err := r .Resolve (context .Background (), "fake" , "pkg" , "1.0.0" )
196+ if ! errors .Is (err , ErrUnsafeURL ) {
197+ t .Fatalf ("Resolve = (%v, %v), want ErrUnsafeURL" , info , err )
198+ }
199+ })
200+ }
201+ }
202+
203+ func TestResolveFromMetadataAcceptsSafeURL (t * testing.T ) {
204+ want := "https://files.pythonhosted.org/packages/ab/cd/pkg-1.0.tar.gz"
205+ reg := & fakeRegistry {
206+ versions : []registries.Version {{
207+ Number : "1.0.0" ,
208+ Integrity : "sha256-deadbeef" ,
209+ Metadata : map [string ]any {"download_url" : want },
210+ }},
211+ }
212+ r := NewResolver ()
213+ r .RegisterRegistry (reg )
214+
215+ info , err := r .Resolve (context .Background (), "fake" , "pkg" , "1.0.0" )
216+ if err != nil {
217+ t .Fatalf ("Resolve failed: %v" , err )
218+ }
219+ if info .URL != want {
220+ t .Errorf ("URL = %q, want %q" , info .URL , want )
221+ }
222+ if info .Integrity != "sha256-deadbeef" {
223+ t .Errorf ("Integrity = %q, want sha256-deadbeef" , info .Integrity )
224+ }
225+ if info .Filename != "pkg-1.0.tar.gz" {
226+ t .Errorf ("Filename = %q, want pkg-1.0.tar.gz" , info .Filename )
227+ }
228+ }
229+
121230func TestFilenameFromURL (t * testing.T ) {
122231 tests := []struct {
123232 url string
0 commit comments