github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/internal/cpegenerate/java_test.go (about)

     1  package cpegenerate
     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: "clean # suffixes",
   166  			pkg: pkg.Package{
   167  				Metadata: pkg.JavaArchive{
   168  					PomProperties: &pkg.JavaPomProperties{
   169  						GroupID: "org.elasticsearch.plugin#parent-join;6.8.15",
   170  					},
   171  				},
   172  			},
   173  			expects: []string{"org.elasticsearch.plugin"},
   174  		},
   175  		{
   176  			name: "from artifactID",
   177  			pkg: pkg.Package{
   178  				Metadata: pkg.JavaArchive{
   179  					PomProperties: &pkg.JavaPomProperties{
   180  						ArtifactID: "io.jenkins-ci.plugin.thing; version='[2,3)' ; org.something.else",
   181  					},
   182  				},
   183  			},
   184  			expects: []string{"io.jenkins-ci.plugin.thing"},
   185  		},
   186  		{
   187  			name: "from main Extension-Name field",
   188  			pkg: pkg.Package{
   189  				Metadata: pkg.JavaArchive{
   190  					Manifest: &pkg.JavaManifest{
   191  						Main: pkg.KeyValues{
   192  							{
   193  								Key:   "Extension-Name",
   194  								Value: "io.jenkins-ci.plugin.thing",
   195  							},
   196  						},
   197  					},
   198  				},
   199  			},
   200  			expects: []string{"io.jenkins-ci.plugin.thing"},
   201  		},
   202  		{
   203  			name: "from named section Extension-Name field",
   204  			pkg: pkg.Package{
   205  				Metadata: pkg.JavaArchive{
   206  					Manifest: &pkg.JavaManifest{
   207  						Sections: []pkg.KeyValues{
   208  							{
   209  								{
   210  									Key:   "Name",
   211  									Value: "section",
   212  								},
   213  								{
   214  									Key:   "Extension-Name",
   215  									Value: "io.jenkins-ci.plugin.thing",
   216  								},
   217  							},
   218  						},
   219  					},
   220  				},
   221  			},
   222  			expects: []string{"io.jenkins-ci.plugin.thing"},
   223  		},
   224  		{
   225  			name: "from main field - tier 1",
   226  			pkg: pkg.Package{
   227  				Metadata: pkg.JavaArchive{
   228  					Manifest: &pkg.JavaManifest{
   229  						Main: []pkg.KeyValue{
   230  							// positive cases
   231  							// tier 1
   232  							{Key: "Extension-Name", Value: "io.jenkins-ci.plugin.1"},
   233  							{Key: "Specification-Vendor", Value: "io.jenkins-ci.plugin.2"},
   234  							{Key: "Implementation-Vendor", Value: "io.jenkins-ci.plugin.3"},
   235  							{Key: "Bundle-SymbolicName", Value: "io.jenkins-ci.plugin.4"},
   236  							{Key: "Implementation-Vendor-Id", Value: "io.jenkins-ci.plugin.5"},
   237  							{Key: "Implementation-Title", Value: "io.jenkins-ci.plugin.6"},
   238  							{Key: "Bundle-Activator", Value: "io.jenkins-ci.plugin.7"},
   239  							// tier 2
   240  							{Key: "Automatic-Module-Name", Value: "io.jenkins-ci.plugin.8"},
   241  							{Key: "Main-Class", Value: "io.jenkins-ci.plugin.9"},
   242  							{Key: "Package", Value: "io.jenkins-ci.plugin.10"},
   243  						},
   244  					},
   245  				},
   246  			},
   247  			expects: []string{
   248  				"io.jenkins-ci.plugin.1",
   249  				"io.jenkins-ci.plugin.2",
   250  				"io.jenkins-ci.plugin.3",
   251  				"io.jenkins-ci.plugin.4",
   252  				"io.jenkins-ci.plugin.5",
   253  				"io.jenkins-ci.plugin.6",
   254  				"io.jenkins-ci.plugin.7",
   255  			},
   256  		},
   257  		{
   258  			name: "from main field - tier 2",
   259  			pkg: pkg.Package{
   260  				Metadata: pkg.JavaArchive{
   261  					Manifest: &pkg.JavaManifest{
   262  						Main: []pkg.KeyValue{
   263  							// positive cases
   264  							{Key: "Automatic-Module-Name", Value: "io.jenkins-ci.plugin.8"},
   265  							{Key: "Main-Class", Value: "io.jenkins-ci.plugin.9"},
   266  							{Key: "Package", Value: "io.jenkins-ci.plugin.10"},
   267  						},
   268  					},
   269  				},
   270  			},
   271  			expects: []string{
   272  				"io.jenkins-ci.plugin.8",
   273  				"io.jenkins-ci.plugin.9",
   274  				"io.jenkins-ci.plugin.10",
   275  			},
   276  		},
   277  		{
   278  			name: "from main field - negative cases",
   279  			pkg: pkg.Package{
   280  				Metadata: pkg.JavaArchive{
   281  					Manifest: &pkg.JavaManifest{
   282  						Main: []pkg.KeyValue{
   283  							// negative cases
   284  							{Key: "Extension-Name", Value: "not.a-group.id"},
   285  							{Key: "bogus", Value: "io.jenkins-ci.plugin.please-dont-find-me"},
   286  						},
   287  					},
   288  				},
   289  			},
   290  			expects: nil,
   291  		},
   292  		{
   293  			name: "from named section field - tier 1",
   294  			pkg: pkg.Package{
   295  				Metadata: pkg.JavaArchive{
   296  					Manifest: &pkg.JavaManifest{
   297  						Sections: []pkg.KeyValues{
   298  							{
   299  								{
   300  									Key:   "Name",
   301  									Value: "section",
   302  								},
   303  								// positive cases
   304  								// tier 1
   305  								{
   306  									Key:   "Extension-Name",
   307  									Value: "io.jenkins-ci.plugin.1",
   308  								},
   309  								{
   310  									Key:   "Specification-Vendor",
   311  									Value: "io.jenkins-ci.plugin.2",
   312  								},
   313  								{
   314  									Key:   "Implementation-Vendor",
   315  									Value: "io.jenkins-ci.plugin.3",
   316  								},
   317  								{
   318  									Key:   "Bundle-SymbolicName",
   319  									Value: "io.jenkins-ci.plugin.4",
   320  								},
   321  								{
   322  									Key:   "Implementation-Vendor-Id",
   323  									Value: "io.jenkins-ci.plugin.5",
   324  								},
   325  								{
   326  									Key:   "Implementation-Title",
   327  									Value: "io.jenkins-ci.plugin.6",
   328  								},
   329  								{
   330  									Key:   "Bundle-Activator",
   331  									Value: "io.jenkins-ci.plugin.7",
   332  								},
   333  								// tier 2
   334  								{
   335  									Key:   "Automatic-Module-Name",
   336  									Value: "io.jenkins-ci.plugin.8",
   337  								},
   338  								{
   339  									Key:   "Main-Class",
   340  									Value: "io.jenkins-ci.plugin.9",
   341  								},
   342  								{
   343  									Key:   "Package",
   344  									Value: "io.jenkins-ci.plugin.10",
   345  								},
   346  							},
   347  						},
   348  					},
   349  				},
   350  			},
   351  			expects: []string{
   352  				"io.jenkins-ci.plugin.1",
   353  				"io.jenkins-ci.plugin.2",
   354  				"io.jenkins-ci.plugin.3",
   355  				"io.jenkins-ci.plugin.4",
   356  				"io.jenkins-ci.plugin.5",
   357  				"io.jenkins-ci.plugin.6",
   358  				"io.jenkins-ci.plugin.7",
   359  			},
   360  		},
   361  		{
   362  			name: "from named section field - negative cases",
   363  			pkg: pkg.Package{
   364  				Metadata: pkg.JavaArchive{
   365  					Manifest: &pkg.JavaManifest{
   366  						Sections: []pkg.KeyValues{
   367  							{
   368  								{
   369  									Key:   "Name",
   370  									Value: "section",
   371  								},
   372  								{
   373  									Key:   "Extension-Name",
   374  									Value: "not.a-group.id",
   375  								},
   376  								{
   377  									Key:   "bogus",
   378  									Value: "io.jenkins-ci.plugin.please-dont-find-me",
   379  								},
   380  							},
   381  						},
   382  					},
   383  				},
   384  			},
   385  			expects: nil,
   386  		},
   387  		{
   388  			name: "no manifest or pom info",
   389  			pkg: pkg.Package{
   390  				Metadata: pkg.JavaArchive{},
   391  			},
   392  			expects: nil,
   393  		},
   394  		{
   395  			name:    "no java info",
   396  			pkg:     pkg.Package{},
   397  			expects: nil,
   398  		},
   399  	}
   400  	for _, test := range tests {
   401  		t.Run(test.name, func(t *testing.T) {
   402  			assert.ElementsMatch(t, test.expects, GroupIDsFromJavaPackage(test.pkg))
   403  		})
   404  	}
   405  }
   406  
   407  func Test_artifactIDFromJavaPackage(t *testing.T) {
   408  	tests := []struct {
   409  		name    string
   410  		pkg     pkg.Package
   411  		expects string
   412  	}{
   413  		{
   414  			name: "go case",
   415  			pkg: pkg.Package{
   416  				Metadata: pkg.JavaArchive{
   417  					PomProperties: &pkg.JavaPomProperties{
   418  						ArtifactID: "cloudbees-installation-manager",
   419  					},
   420  				},
   421  			},
   422  			expects: "cloudbees-installation-manager",
   423  		},
   424  		{
   425  			name: "ignore groupID-like things",
   426  			pkg: pkg.Package{
   427  				Metadata: pkg.JavaArchive{
   428  					PomProperties: &pkg.JavaPomProperties{
   429  						ArtifactID: "io.jenkins-ci.plugin.thing",
   430  					},
   431  				},
   432  			},
   433  			expects: "",
   434  		},
   435  		{
   436  			name:    "no java info",
   437  			pkg:     pkg.Package{},
   438  			expects: "",
   439  		},
   440  	}
   441  	for _, test := range tests {
   442  		t.Run(test.name, func(t *testing.T) {
   443  			assert.Equal(t, test.expects, artifactIDFromJavaPackage(test.pkg))
   444  		})
   445  	}
   446  }
   447  
   448  func Test_vendorsFromJavaManifestNames(t *testing.T) {
   449  	tests := []struct {
   450  		name    string
   451  		pkg     pkg.Package
   452  		expects []string
   453  	}{
   454  		{
   455  			name: "from manifest named section fields",
   456  			pkg: pkg.Package{
   457  				Metadata: pkg.JavaArchive{
   458  					Manifest: &pkg.JavaManifest{
   459  						Sections: []pkg.KeyValues{
   460  							{
   461  								{
   462  									Key:   "Name",
   463  									Value: "section",
   464  								},
   465  								// positive cases
   466  								{
   467  									Key:   "Specification-Vendor",
   468  									Value: "Alex Goodman",
   469  								},
   470  								{
   471  									Key:   "Implementation-Vendor",
   472  									Value: "William Goodman",
   473  								},
   474  							},
   475  						},
   476  					},
   477  				},
   478  			},
   479  			expects: []string{"alex_goodman", "william_goodman"},
   480  		},
   481  		{
   482  			name: "from manifest named section fields - negative cases",
   483  			pkg: pkg.Package{
   484  				Metadata: pkg.JavaArchive{
   485  					Manifest: &pkg.JavaManifest{
   486  						Sections: []pkg.KeyValues{
   487  							{
   488  								{
   489  									Key:   "Name",
   490  									Value: "section",
   491  								},
   492  								// negative cases
   493  
   494  								{
   495  									Key:   "Specification-Vendor",
   496  									Value: "io.jenkins-ci.plugin.thing",
   497  								},
   498  								{
   499  									Key:   "Implementation-Vendor-ID",
   500  									Value: "William Goodman",
   501  								},
   502  							},
   503  						},
   504  					},
   505  				},
   506  			},
   507  			expects: nil,
   508  		},
   509  	}
   510  	for _, test := range tests {
   511  		t.Run(test.name, func(t *testing.T) {
   512  			assert.ElementsMatch(t, test.expects, vendorsFromJavaManifestNames(test.pkg).values())
   513  		})
   514  	}
   515  }
   516  
   517  func Test_groupIDsFromJavaManifest(t *testing.T) {
   518  	tests := []struct {
   519  		name     string
   520  		manifest pkg.JavaManifest
   521  		expected []string
   522  	}{
   523  		{
   524  			name:     "spring-security-core",
   525  			manifest: pkg.JavaManifest{},
   526  			expected: []string{"org.springframework.security"},
   527  		},
   528  		{
   529  			name:     "spring-web",
   530  			manifest: pkg.JavaManifest{},
   531  			expected: []string{"org.springframework"},
   532  		},
   533  		{
   534  			name: "spring-foo",
   535  			manifest: pkg.JavaManifest{
   536  				Main: []pkg.KeyValue{
   537  					{
   538  						Key:   "Implementation-Vendor",
   539  						Value: "org.foo",
   540  					},
   541  				},
   542  			},
   543  			expected: []string{"org.foo"},
   544  		},
   545  	}
   546  
   547  	for _, test := range tests {
   548  		t.Run(test.name, func(t *testing.T) {
   549  			got := groupIDsFromJavaManifest(test.name, &test.manifest)
   550  			require.Equal(t, test.expected, got)
   551  		})
   552  	}
   553  }