github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/common/cpe/java_test.go (about)

     1  package cpe
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/anchore/syft/syft/pkg"
    11  )
    12  
    13  func Test_productsFromArtifactAndGroupIDs(t *testing.T) {
    14  	tests := []struct {
    15  		groupIDs   []string
    16  		artifactID string
    17  		expected   []string
    18  	}{
    19  		{
    20  			groupIDs:   []string{"org.sonatype.nexus"},
    21  			artifactID: "nexus-extender",
    22  			expected:   []string{"nexus", "nexus-extender"},
    23  		},
    24  		{
    25  			groupIDs: []string{"org.sonatype.nexus"},
    26  			expected: []string{"nexus"},
    27  		},
    28  		{
    29  			groupIDs:   []string{"org.jenkins-ci.plugins"},
    30  			artifactID: "ant",
    31  			expected:   []string{"ant"},
    32  		},
    33  		{
    34  			groupIDs:   []string{"org.jenkins-ci.plugins"},
    35  			artifactID: "antisamy-markup-formatter",
    36  			expected:   []string{"antisamy-markup-formatter"},
    37  		},
    38  		{
    39  			groupIDs:   []string{"io.jenkins.plugins"},
    40  			artifactID: "aws-global-configuration",
    41  			expected:   []string{"aws-global-configuration"},
    42  		},
    43  		{
    44  			groupIDs:   []string{"com.cloudbees.jenkins.plugins"},
    45  			artifactID: "cloudbees-servicenow-jenkins-plugin",
    46  			expected:   []string{"cloudbees-servicenow-jenkins-plugin"},
    47  		},
    48  		{
    49  			groupIDs:   []string{"com.atlassian.confluence.plugins"},
    50  			artifactID: "confluence-mobile-plugin",
    51  			expected:   []string{"confluence-mobile-plugin"},
    52  		},
    53  		{
    54  			groupIDs:   []string{"com.atlassian.confluence.plugins"},
    55  			artifactID: "confluence-view-file-macro",
    56  			expected:   []string{"confluence-view-file-macro"},
    57  		},
    58  		{
    59  			groupIDs:   []string{"com.google.guava"},
    60  			artifactID: "failureaccess",
    61  			expected:   []string{"failureaccess"},
    62  		},
    63  	}
    64  	for _, test := range tests {
    65  		t.Run(strings.Join(test.groupIDs, ",")+":"+test.artifactID, func(t *testing.T) {
    66  			actual := productsFromArtifactAndGroupIDs(test.artifactID, test.groupIDs)
    67  			assert.ElementsMatch(t, test.expected, actual, "different products")
    68  		})
    69  	}
    70  }
    71  
    72  func Test_candidateProductsForJava(t *testing.T) {
    73  	tests := []struct {
    74  		name     string
    75  		pkg      pkg.Package
    76  		expected []string
    77  	}{
    78  		{
    79  			name: "duplicate groupID in artifactID field",
    80  			pkg: pkg.Package{
    81  				Metadata: pkg.JavaArchive{
    82  					PomProperties: &pkg.JavaPomProperties{
    83  						GroupID:    "org.sonatype.nexus",
    84  						ArtifactID: "org.sonatype.nexus",
    85  					},
    86  				},
    87  			},
    88  			expected: []string{"nexus"},
    89  		},
    90  		{
    91  			name: "detect groupID-like value in artifactID field",
    92  			pkg: pkg.Package{
    93  				Metadata: pkg.JavaArchive{
    94  					PomProperties: &pkg.JavaPomProperties{
    95  						ArtifactID: "org.sonatype.nexus",
    96  					},
    97  				},
    98  			},
    99  			expected: []string{"nexus"},
   100  		},
   101  	}
   102  	for _, test := range tests {
   103  		t.Run(test.name, func(t *testing.T) {
   104  			actual := candidateProductsForJava(test.pkg)
   105  			assert.ElementsMatch(t, test.expected, actual, "different products")
   106  		})
   107  	}
   108  }
   109  
   110  func Test_vendorsFromGroupIDs(t *testing.T) {
   111  	tests := []struct {
   112  		groupID  string
   113  		expected []string
   114  	}{
   115  		{
   116  			groupID:  "org.sonatype.nexus",
   117  			expected: []string{"sonatype", "nexus"},
   118  		},
   119  		{
   120  			groupID:  "org.jenkins-ci.plugins",
   121  			expected: []string{"jenkins-ci"},
   122  		},
   123  		{
   124  			groupID:  "io.jenkins.plugins",
   125  			expected: []string{"jenkins"},
   126  		},
   127  		{
   128  			groupID:  "com.cloudbees.jenkins.plugins",
   129  			expected: []string{"cloudbees", "jenkins"},
   130  		},
   131  		{
   132  			groupID:  "com.atlassian.confluence.plugins",
   133  			expected: []string{"atlassian", "confluence"},
   134  		},
   135  		{
   136  			groupID:  "com.google.guava",
   137  			expected: []string{"google", "guava"},
   138  		},
   139  	}
   140  	for _, test := range tests {
   141  		t.Run(test.groupID, func(t *testing.T) {
   142  			assert.ElementsMatch(t, test.expected, vendorsFromGroupIDs([]string{test.groupID}).values(), "different vendors")
   143  		})
   144  	}
   145  }
   146  
   147  func Test_groupIDsFromJavaPackage(t *testing.T) {
   148  	tests := []struct {
   149  		name    string
   150  		pkg     pkg.Package
   151  		expects []string
   152  	}{
   153  		{
   154  			name: "go case",
   155  			pkg: pkg.Package{
   156  				Metadata: pkg.JavaArchive{
   157  					PomProperties: &pkg.JavaPomProperties{
   158  						GroupID: "io.jenkins-ci.plugin.thing;version='[2,3)'",
   159  					},
   160  				},
   161  			},
   162  			expects: []string{"io.jenkins-ci.plugin.thing"},
   163  		},
   164  		{
   165  			name: "from artifactID",
   166  			pkg: pkg.Package{
   167  				Metadata: pkg.JavaArchive{
   168  					PomProperties: &pkg.JavaPomProperties{
   169  						ArtifactID: "io.jenkins-ci.plugin.thing; version='[2,3)' ; org.something.else",
   170  					},
   171  				},
   172  			},
   173  			expects: []string{"io.jenkins-ci.plugin.thing"},
   174  		},
   175  		{
   176  			name: "from main Extension-Name field",
   177  			pkg: pkg.Package{
   178  				Metadata: pkg.JavaArchive{
   179  					Manifest: &pkg.JavaManifest{
   180  						Main: map[string]string{
   181  							"Extension-Name": "io.jenkins-ci.plugin.thing",
   182  						},
   183  					},
   184  				},
   185  			},
   186  			expects: []string{"io.jenkins-ci.plugin.thing"},
   187  		},
   188  		{
   189  			name: "from named section Extension-Name field",
   190  			pkg: pkg.Package{
   191  				Metadata: pkg.JavaArchive{
   192  					Manifest: &pkg.JavaManifest{
   193  						NamedSections: map[string]map[string]string{
   194  							"section": {
   195  								"Extension-Name": "io.jenkins-ci.plugin.thing",
   196  							},
   197  						},
   198  					},
   199  				},
   200  			},
   201  			expects: []string{"io.jenkins-ci.plugin.thing"},
   202  		},
   203  		{
   204  			name: "from main field - tier 1",
   205  			pkg: pkg.Package{
   206  				Metadata: pkg.JavaArchive{
   207  					Manifest: &pkg.JavaManifest{
   208  						Main: map[string]string{
   209  							// positive cases
   210  							// tier 1
   211  							"Extension-Name":           "io.jenkins-ci.plugin.1",
   212  							"Specification-Vendor":     "io.jenkins-ci.plugin.2",
   213  							"Implementation-Vendor":    "io.jenkins-ci.plugin.3",
   214  							"Bundle-SymbolicName":      "io.jenkins-ci.plugin.4",
   215  							"Implementation-Vendor-Id": "io.jenkins-ci.plugin.5",
   216  							"Implementation-Title":     "io.jenkins-ci.plugin.6",
   217  							"Bundle-Activator":         "io.jenkins-ci.plugin.7",
   218  							// tier 2
   219  							"Automatic-Module-Name": "io.jenkins-ci.plugin.8",
   220  							"Main-Class":            "io.jenkins-ci.plugin.9",
   221  							"Package":               "io.jenkins-ci.plugin.10",
   222  						},
   223  					},
   224  				},
   225  			},
   226  			expects: []string{
   227  				"io.jenkins-ci.plugin.1",
   228  				"io.jenkins-ci.plugin.2",
   229  				"io.jenkins-ci.plugin.3",
   230  				"io.jenkins-ci.plugin.4",
   231  				"io.jenkins-ci.plugin.5",
   232  				"io.jenkins-ci.plugin.6",
   233  				"io.jenkins-ci.plugin.7",
   234  			},
   235  		},
   236  		{
   237  			name: "from main field - tier 2",
   238  			pkg: pkg.Package{
   239  				Metadata: pkg.JavaArchive{
   240  					Manifest: &pkg.JavaManifest{
   241  						Main: map[string]string{
   242  							// positive cases
   243  							"Automatic-Module-Name": "io.jenkins-ci.plugin.8",
   244  							"Main-Class":            "io.jenkins-ci.plugin.9",
   245  							"Package":               "io.jenkins-ci.plugin.10",
   246  						},
   247  					},
   248  				},
   249  			},
   250  			expects: []string{
   251  				"io.jenkins-ci.plugin.8",
   252  				"io.jenkins-ci.plugin.9",
   253  				"io.jenkins-ci.plugin.10",
   254  			},
   255  		},
   256  		{
   257  			name: "from main field - negative cases",
   258  			pkg: pkg.Package{
   259  				Metadata: pkg.JavaArchive{
   260  					Manifest: &pkg.JavaManifest{
   261  						Main: map[string]string{
   262  							// negative cases
   263  							"Extension-Name": "not.a-group.id",
   264  							"bogus":          "io.jenkins-ci.plugin.please-dont-find-me",
   265  						},
   266  					},
   267  				},
   268  			},
   269  			expects: nil,
   270  		},
   271  		{
   272  			name: "from named section field - tier 1",
   273  			pkg: pkg.Package{
   274  				Metadata: pkg.JavaArchive{
   275  					Manifest: &pkg.JavaManifest{
   276  						NamedSections: map[string]map[string]string{
   277  							"section": {
   278  								// positive cases
   279  								// tier 1
   280  								"Extension-Name":           "io.jenkins-ci.plugin.1",
   281  								"Specification-Vendor":     "io.jenkins-ci.plugin.2",
   282  								"Implementation-Vendor":    "io.jenkins-ci.plugin.3",
   283  								"Bundle-SymbolicName":      "io.jenkins-ci.plugin.4",
   284  								"Implementation-Vendor-Id": "io.jenkins-ci.plugin.5",
   285  								"Implementation-Title":     "io.jenkins-ci.plugin.6",
   286  								"Bundle-Activator":         "io.jenkins-ci.plugin.7",
   287  								// tier 2
   288  								"Automatic-Module-Name": "io.jenkins-ci.plugin.8",
   289  								"Main-Class":            "io.jenkins-ci.plugin.9",
   290  								"Package":               "io.jenkins-ci.plugin.10",
   291  							},
   292  						},
   293  					},
   294  				},
   295  			},
   296  			expects: []string{
   297  				"io.jenkins-ci.plugin.1",
   298  				"io.jenkins-ci.plugin.2",
   299  				"io.jenkins-ci.plugin.3",
   300  				"io.jenkins-ci.plugin.4",
   301  				"io.jenkins-ci.plugin.5",
   302  				"io.jenkins-ci.plugin.6",
   303  				"io.jenkins-ci.plugin.7",
   304  			},
   305  		},
   306  		{
   307  			name: "from named section field - negative cases",
   308  			pkg: pkg.Package{
   309  				Metadata: pkg.JavaArchive{
   310  					Manifest: &pkg.JavaManifest{
   311  						NamedSections: map[string]map[string]string{
   312  							"section": {
   313  								// negative cases
   314  								"Extension-Name": "not.a-group.id",
   315  								"bogus":          "io.jenkins-ci.plugin.please-dont-find-me",
   316  							},
   317  						},
   318  					},
   319  				},
   320  			},
   321  			expects: nil,
   322  		},
   323  		{
   324  			name: "no manifest or pom info",
   325  			pkg: pkg.Package{
   326  				Metadata: pkg.JavaArchive{},
   327  			},
   328  			expects: nil,
   329  		},
   330  		{
   331  			name:    "no java info",
   332  			pkg:     pkg.Package{},
   333  			expects: nil,
   334  		},
   335  	}
   336  	for _, test := range tests {
   337  		t.Run(test.name, func(t *testing.T) {
   338  			assert.ElementsMatch(t, test.expects, GroupIDsFromJavaPackage(test.pkg))
   339  		})
   340  	}
   341  }
   342  
   343  func Test_artifactIDFromJavaPackage(t *testing.T) {
   344  	tests := []struct {
   345  		name    string
   346  		pkg     pkg.Package
   347  		expects string
   348  	}{
   349  		{
   350  			name: "go case",
   351  			pkg: pkg.Package{
   352  				Metadata: pkg.JavaArchive{
   353  					PomProperties: &pkg.JavaPomProperties{
   354  						ArtifactID: "cloudbees-installation-manager",
   355  					},
   356  				},
   357  			},
   358  			expects: "cloudbees-installation-manager",
   359  		},
   360  		{
   361  			name: "ignore groupID-like things",
   362  			pkg: pkg.Package{
   363  				Metadata: pkg.JavaArchive{
   364  					PomProperties: &pkg.JavaPomProperties{
   365  						ArtifactID: "io.jenkins-ci.plugin.thing",
   366  					},
   367  				},
   368  			},
   369  			expects: "",
   370  		},
   371  		{
   372  			name:    "no java info",
   373  			pkg:     pkg.Package{},
   374  			expects: "",
   375  		},
   376  	}
   377  	for _, test := range tests {
   378  		t.Run(test.name, func(t *testing.T) {
   379  			assert.Equal(t, test.expects, artifactIDFromJavaPackage(test.pkg))
   380  		})
   381  	}
   382  }
   383  
   384  func Test_vendorsFromJavaManifestNames(t *testing.T) {
   385  	tests := []struct {
   386  		name    string
   387  		pkg     pkg.Package
   388  		expects []string
   389  	}{
   390  		{
   391  			name: "from manifest named section fields",
   392  			pkg: pkg.Package{
   393  				Metadata: pkg.JavaArchive{
   394  					Manifest: &pkg.JavaManifest{
   395  						NamedSections: map[string]map[string]string{
   396  							"section": {
   397  								// positive cases
   398  								"Specification-Vendor":  "Alex Goodman",
   399  								"Implementation-Vendor": "William Goodman",
   400  							},
   401  						},
   402  					},
   403  				},
   404  			},
   405  			expects: []string{"alex_goodman", "william_goodman"},
   406  		},
   407  		{
   408  			name: "from manifest named section fields - negative cases",
   409  			pkg: pkg.Package{
   410  				Metadata: pkg.JavaArchive{
   411  					Manifest: &pkg.JavaManifest{
   412  						NamedSections: map[string]map[string]string{
   413  							"section": {
   414  								// negative cases
   415  								"Specification-Vendor":     "io.jenkins-ci.plugin.thing",
   416  								"Implementation-Vendor-ID": "William Goodman",
   417  							},
   418  						},
   419  					},
   420  				},
   421  			},
   422  			expects: nil,
   423  		},
   424  	}
   425  	for _, test := range tests {
   426  		t.Run(test.name, func(t *testing.T) {
   427  			assert.ElementsMatch(t, test.expects, vendorsFromJavaManifestNames(test.pkg).values())
   428  		})
   429  	}
   430  }
   431  
   432  func Test_groupIDsFromJavaManifest(t *testing.T) {
   433  	tests := []struct {
   434  		name     string
   435  		manifest pkg.JavaManifest
   436  		expected []string
   437  	}{
   438  		{
   439  			name:     "spring-security-core",
   440  			manifest: pkg.JavaManifest{},
   441  			expected: []string{"org.springframework.security"},
   442  		},
   443  		{
   444  			name:     "spring-web",
   445  			manifest: pkg.JavaManifest{},
   446  			expected: []string{"org.springframework"},
   447  		},
   448  		{
   449  			name: "spring-foo",
   450  			manifest: pkg.JavaManifest{
   451  				Main: map[string]string{
   452  					"Implementation-Vendor": "org.foo",
   453  				},
   454  			},
   455  			expected: []string{"org.foo"},
   456  		},
   457  	}
   458  
   459  	for _, test := range tests {
   460  		t.Run(test.name, func(t *testing.T) {
   461  			got := groupIDsFromJavaManifest(test.name, &test.manifest)
   462  			require.Equal(t, test.expected, got)
   463  		})
   464  	}
   465  }