@@ -20,6 +20,7 @@ package deps
2020import (
2121 "bufio"
2222 "embed"
23+ "fmt"
2324 "io"
2425 "io/fs"
2526 "net/http"
@@ -353,3 +354,132 @@ func TestGemspecIgnoresCommentedRuntimeDependencies(t *testing.T) {
353354 t .Fatalf ("expected 0 dependencies when runtime deps are only commented, got %d" , got )
354355 }
355356}
357+
358+ func TestFetchInstalledLicense (t * testing.T ) {
359+ gemHome := t .TempDir ()
360+ specsDir := filepath .Join (gemHome , "specifications" )
361+ if err := os .MkdirAll (specsDir , 0755 ); err != nil {
362+ t .Fatal (err )
363+ }
364+
365+ createGemspec := func (filename , name , version , license string ) {
366+ content := fmt .Sprintf (`
367+ Gem::Specification.new do |s|
368+ s.name = "%s"
369+ s.version = "%s"
370+ s.licenses = ["%s"]
371+ end
372+ ` , name , version , license )
373+ if err := os .WriteFile (filepath .Join (specsDir , filename ), []byte (content ), 0644 ); err != nil {
374+ t .Fatal (err )
375+ }
376+ }
377+
378+ createGemspec ("foo-1.0.0.gemspec" , "foo" , "1.0.0" , "MIT" )
379+ createGemspec ("foo-2.0.0.gemspec" , "foo" , "2.0.0" , "GPL-3.0" )
380+ createGemspec ("foo-bar-1.0.0.gemspec" , "foo-bar" , "1.0.0" , "Apache-2.0" )
381+ createGemspec ("bar-1.0.0.gemspec" , "bar" , "1.0.0" , "BSD-3-Clause" )
382+ // Invalid version string in filename
383+ createGemspec ("foo-invalid.gemspec" , "foo" , "invalid" , "WTFPL" )
384+
385+ t .Setenv ("GEM_HOME" , gemHome )
386+
387+ tests := []struct {
388+ name string
389+ version string
390+ want string
391+ }{
392+ {"foo" , "1.0.0" , "MIT" },
393+ {"foo" , "2.0.0" , "GPL-3.0" },
394+ {"foo-bar" , "1.0.0" , "Apache-2.0" },
395+ {"bar" , "1.0.0" , "BSD-3-Clause" },
396+ {"foo" , "" , "MIT" }, // Should find first available version (1.0.0 comes before 2.0.0)
397+ {"foo" , "3.0.0" , "" }, // Not found
398+ {"unknown" , "1.0.0" , "" },
399+ {"foo" , "invalid" , "" }, // Invalid version requested, regex won't match
400+ }
401+
402+ for _ , tt := range tests {
403+ t .Run (fmt .Sprintf ("%s-%s" , tt .name , tt .version ), func (t * testing.T ) {
404+ got := fetchInstalledLicense (tt .name , tt .version )
405+ if got != tt .want {
406+ t .Errorf ("fetchInstalledLicense(%q, %q) = %q, want %q" , tt .name , tt .version , got , tt .want )
407+ }
408+ })
409+ }
410+ }
411+
412+ func TestRubyGemfileLockResolver_PathTraversal (t * testing.T ) {
413+ resolver := new (GemfileLockResolver )
414+ dir := t .TempDir ()
415+
416+ // Create a directory outside the project
417+ outsideDir := t .TempDir ()
418+ // Create a gemspec in outsideDir
419+ outsideGemspec := filepath .Join (outsideDir , "evil.gemspec" )
420+ if err := os .WriteFile (outsideGemspec , []byte (`
421+ Gem::Specification.new do |s|
422+ s.name = "evil"
423+ s.version = "1.0.0"
424+ s.licenses = ["Evil-License"]
425+ end
426+ ` ), 0644 ); err != nil {
427+ t .Fatal (err )
428+ }
429+
430+ // Calculate relative path from dir to outsideDir
431+ relPath , err := filepath .Rel (dir , outsideDir )
432+ if err != nil {
433+ t .Fatal (err )
434+ }
435+
436+ lockContent := fmt .Sprintf (`
437+ PATH
438+ remote: %s
439+ specs:
440+ evil (1.0.0)
441+
442+ GEM
443+ remote: https://gem.coop/
444+ specs:
445+
446+ PLATFORMS
447+ ruby
448+
449+ DEPENDENCIES
450+ evil!
451+
452+ BUNDLED WITH
453+ 2.4.10
454+ ` , relPath )
455+
456+ lock := filepath .Join (dir , "Gemfile.lock" )
457+ if err := writeFileRuby (lock , lockContent ); err != nil {
458+ t .Fatal (err )
459+ }
460+
461+ cfg := & ConfigDeps {Files : []string {lock }}
462+ report := Report {}
463+ if err := resolver .Resolve (lock , cfg , & report ); err != nil {
464+ t .Fatal (err )
465+ }
466+
467+ found := false
468+ for _ , r := range report .Resolved {
469+ if r .Dependency == "evil" {
470+ found = true
471+ if r .LicenseSpdxID == "Evil-License" {
472+ t .Errorf ("Path traversal succeeded! Found license from outside directory." )
473+ }
474+ }
475+ }
476+ // If it's skipped, that's also fine (means it didn't resolve license)
477+ for _ , r := range report .Skipped {
478+ if r .Dependency == "evil" {
479+ found = true
480+ }
481+ }
482+ if ! found {
483+ t .Errorf ("Dependency 'evil' was not found in report" )
484+ }
485+ }
0 commit comments