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

     1  package java
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/go-test/deep"
     9  	"github.com/stretchr/testify/assert"
    10  
    11  	"github.com/anchore/syft/syft/pkg"
    12  )
    13  
    14  func TestParseJavaManifest(t *testing.T) {
    15  	tests := []struct {
    16  		fixture  string
    17  		expected pkg.JavaManifest
    18  	}{
    19  		{
    20  			fixture: "test-fixtures/manifest/small",
    21  			expected: pkg.JavaManifest{
    22  				Main: map[string]string{
    23  					"Manifest-Version": "1.0",
    24  				},
    25  			},
    26  		},
    27  		{
    28  			fixture: "test-fixtures/manifest/standard-info",
    29  			expected: pkg.JavaManifest{
    30  				Main: map[string]string{
    31  					"Name":                   "the-best-name",
    32  					"Manifest-Version":       "1.0",
    33  					"Specification-Title":    "the-spec-title",
    34  					"Specification-Version":  "the-spec-version",
    35  					"Specification-Vendor":   "the-spec-vendor",
    36  					"Implementation-Title":   "the-impl-title",
    37  					"Implementation-Version": "the-impl-version",
    38  					"Implementation-Vendor":  "the-impl-vendor",
    39  				},
    40  			},
    41  		},
    42  		{
    43  			fixture: "test-fixtures/manifest/extra-info",
    44  			expected: pkg.JavaManifest{
    45  				Main: map[string]string{
    46  					"Manifest-Version": "1.0",
    47  					"Archiver-Version": "Plexus Archiver",
    48  					"Created-By":       "Apache Maven 3.6.3",
    49  				},
    50  				NamedSections: map[string]map[string]string{
    51  					"thing-1": {
    52  						"Built-By": "?",
    53  					},
    54  					"1": {
    55  						"Build-Jdk":  "14.0.1",
    56  						"Main-Class": "hello.HelloWorld",
    57  					},
    58  				},
    59  			},
    60  		},
    61  		{
    62  			fixture: "test-fixtures/manifest/extra-empty-lines",
    63  			expected: pkg.JavaManifest{
    64  				Main: map[string]string{
    65  					"Manifest-Version": "1.0",
    66  					"Archiver-Version": "Plexus Archiver",
    67  					"Created-By":       "Apache Maven 3.6.3",
    68  				},
    69  				NamedSections: map[string]map[string]string{
    70  					"thing-1": {
    71  						"Built-By": "?",
    72  					},
    73  					"thing-2": {
    74  						"Built-By": "someone!",
    75  					},
    76  					"2": {
    77  						"Other": "things",
    78  					},
    79  					"3": {
    80  						"Last": "item",
    81  					},
    82  				},
    83  			},
    84  		},
    85  		{
    86  			fixture: "test-fixtures/manifest/continuation",
    87  			expected: pkg.JavaManifest{
    88  				Main: map[string]string{
    89  					"Manifest-Version": "1.0",
    90  					"Plugin-ScmUrl":    "https://github.com/jenkinsci/plugin-pom/example-jenkins-plugin",
    91  				},
    92  			},
    93  		},
    94  		{
    95  			// regression test, we should always keep the full version
    96  			fixture: "test-fixtures/manifest/version-with-date",
    97  			expected: pkg.JavaManifest{
    98  				Main: map[string]string{
    99  					"Manifest-Version":       "1.0",
   100  					"Implementation-Version": "1.3 2244 October 5 2005",
   101  				},
   102  			},
   103  		},
   104  		{
   105  			// regression test, we should not trim space and choke of empty space
   106  			// https://github.com/anchore/syft/issues/2179
   107  			fixture: "test-fixtures/manifest/leading-space",
   108  			expected: pkg.JavaManifest{
   109  				Main: map[string]string{
   110  					"Key-keykeykey": "initialconfig:com$    # aka not empty line",
   111  					"should":        "parse",
   112  				},
   113  			},
   114  		},
   115  	}
   116  
   117  	for _, test := range tests {
   118  		t.Run(test.fixture, func(t *testing.T) {
   119  			fixture, err := os.Open(test.fixture)
   120  			if err != nil {
   121  				t.Fatalf("could not open fixture: %+v", err)
   122  			}
   123  
   124  			actual, err := parseJavaManifest(test.fixture, fixture)
   125  			if err != nil {
   126  				t.Fatalf("failed to parse manifest: %+v", err)
   127  			}
   128  
   129  			diffs := deep.Equal(actual, &test.expected)
   130  			if len(diffs) > 0 {
   131  				for _, d := range diffs {
   132  					t.Errorf("diff: %+v", d)
   133  				}
   134  
   135  				b, err := json.MarshalIndent(actual, "", "  ")
   136  				if err != nil {
   137  					t.Fatalf("can't show results: %+v", err)
   138  				}
   139  
   140  				t.Errorf("full result: %s", string(b))
   141  			}
   142  		})
   143  	}
   144  }
   145  
   146  func TestSelectName(t *testing.T) {
   147  	tests := []struct {
   148  		desc     string
   149  		manifest pkg.JavaManifest
   150  		archive  archiveFilename
   151  		expected string
   152  	}{
   153  		{
   154  			desc:    "Get name from Implementation-Title",
   155  			archive: archiveFilename{},
   156  			manifest: pkg.JavaManifest{
   157  				Main: map[string]string{
   158  					"Implementation-Title": "maven-wrapper",
   159  				},
   160  			},
   161  			expected: "maven-wrapper",
   162  		},
   163  		{
   164  			desc: "Implementation-Title does not override name from filename",
   165  			manifest: pkg.JavaManifest{
   166  				Main: map[string]string{
   167  					"Name":                 "foo",
   168  					"Implementation-Title": "maven-wrapper",
   169  				},
   170  			},
   171  			archive:  newJavaArchiveFilename("/something/omg.jar"),
   172  			expected: "omg",
   173  		},
   174  		{
   175  			desc: "Use the artifact ID baked by the Apache Maven Bundle Plugin",
   176  			manifest: pkg.JavaManifest{
   177  				Main: map[string]string{
   178  					"Created-By":           "Apache Maven Bundle Plugin",
   179  					"Bundle-SymbolicName":  "com.atlassian.gadgets.atlassian-gadgets-api",
   180  					"Name":                 "foo",
   181  					"Implementation-Title": "maven-wrapper",
   182  				},
   183  			},
   184  			archive:  newJavaArchiveFilename("/something/omg.jar"),
   185  			expected: "atlassian-gadgets-api",
   186  		},
   187  		{
   188  			// example: pkg:maven/org.apache.servicemix.bundles/org.apache.servicemix.bundles.spring-beans@5.3.26_1
   189  			desc: "Apache Maven Bundle Plugin might bake a version in the created-by field",
   190  			manifest: pkg.JavaManifest{
   191  				Main: map[string]string{
   192  					"Created-By":           "Apache Maven Bundle Plugin 5.1.6",
   193  					"Bundle-SymbolicName":  "com.atlassian.gadgets.atlassian-gadgets-api",
   194  					"Name":                 "foo",
   195  					"Implementation-Title": "maven-wrapper",
   196  				},
   197  			},
   198  			archive:  newJavaArchiveFilename("/something/omg.jar"),
   199  			expected: "atlassian-gadgets-api",
   200  		},
   201  		{
   202  			desc: "Filename looks like a groupid + artifact id",
   203  			manifest: pkg.JavaManifest{
   204  				Main: map[string]string{
   205  					"Name":                 "foo",
   206  					"Implementation-Title": "maven-wrapper",
   207  				},
   208  			},
   209  			archive:  newJavaArchiveFilename("/something/com.atlassian.gadgets.atlassian-gadgets-api.jar"),
   210  			expected: "atlassian-gadgets-api",
   211  		},
   212  		{
   213  			// example: pkg:maven/com.google.oauth-client/google-oauth-client@1.25.0
   214  			desc: "skip Apache Maven Bundle Plugin logic if symbolic name is same as vendor id",
   215  			manifest: pkg.JavaManifest{
   216  				Main: map[string]string{
   217  					"Bundle-DocURL":                       "http://www.google.com/",
   218  					"Bundle-License":                      "http://www.apache.org/licenses/LICENSE-2.0.txt",
   219  					"Bundle-ManifestVersion":              "2",
   220  					"Bundle-Name":                         "Google OAuth Client Library for Java",
   221  					"Bundle-RequiredExecutionEnvironment": "JavaSE-1.6",
   222  					"Bundle-SymbolicName":                 "com.google.oauth-client",
   223  					"Bundle-Vendor":                       "Google",
   224  					"Bundle-Version":                      "1.25.0",
   225  					"Created-By":                          "Apache Maven Bundle Plugin",
   226  					"Export-Package":                      "com.google.api.client.auth.openidconnect;uses:=\"com.google.api.client.auth.oauth2,com.google.api.client.json,com.google.api.client.json.webtoken,com.google.api.client.util\";version=\"1.25.0\",com.google.api.client.auth.oauth;uses:=\"com.google.api.client.http,com.google.api.client.util\";version=\"1.25.0\",com.google.api.client.auth.oauth2;uses:=\"com.google.api.client.http,com.google.api.client.json,com.google.api.client.util,com.google.api.client.util.store\";version=\"1.25.0\"",
   227  					"Implementation-Title":                "Google OAuth Client Library for Java",
   228  					"Implementation-Vendor":               "Google",
   229  					"Implementation-Vendor-Id":            "com.google.oauth-client",
   230  					"Implementation-Version":              "1.25.0",
   231  				},
   232  			},
   233  			archive:  newJavaArchiveFilename("/something/google-oauth-client-1.25.0.jar"),
   234  			expected: "google-oauth-client",
   235  		},
   236  	}
   237  
   238  	for _, test := range tests {
   239  		t.Run(test.desc, func(t *testing.T) {
   240  			result := selectName(&test.manifest, test.archive)
   241  
   242  			if result != test.expected {
   243  				t.Errorf("mismatch in names: '%s' != '%s'", result, test.expected)
   244  			}
   245  		})
   246  	}
   247  }
   248  
   249  func TestSelectVersion(t *testing.T) {
   250  	tests := []struct {
   251  		name     string
   252  		manifest pkg.JavaManifest
   253  		archive  archiveFilename
   254  		expected string
   255  	}{
   256  		{
   257  			name:    "Get name from Implementation-Version",
   258  			archive: archiveFilename{},
   259  			manifest: pkg.JavaManifest{
   260  				Main: map[string]string{
   261  					"Implementation-Version": "1.8.2",
   262  				},
   263  			},
   264  			expected: "1.8.2",
   265  		},
   266  		{
   267  			name: "Implementation-Version takes precedence over Specification-Version",
   268  			manifest: pkg.JavaManifest{
   269  				Main: map[string]string{
   270  					"Implementation-Version": "1.8.2",
   271  					"Specification-Version":  "1.0",
   272  				},
   273  			},
   274  			expected: "1.8.2",
   275  		},
   276  		{
   277  			name: "Implementation-Version found outside the main section",
   278  			manifest: pkg.JavaManifest{
   279  				Main: map[string]string{
   280  					"Manifest-Version": "1.0",
   281  					"Ant-Version":      "Apache Ant 1.8.2",
   282  					"Created-By":       "1.5.0_22-b03 (Sun Microsystems Inc.)",
   283  				},
   284  				NamedSections: map[string]map[string]string{
   285  					"org/apache/tools/ant/taskdefs/optional/": {
   286  						"Implementation-Version": "1.8.2",
   287  					},
   288  				},
   289  			},
   290  			expected: "1.8.2",
   291  		},
   292  		{
   293  			name: "Implementation-Version takes precedence over Specification-Version in subsequent section",
   294  			manifest: pkg.JavaManifest{
   295  				Main: map[string]string{
   296  					"Manifest-Version":      "1.0",
   297  					"Ant-Version":           "Apache Ant 1.8.2",
   298  					"Created-By":            "1.5.0_22-b03 (Sun Microsystems Inc.)",
   299  					"Specification-Version": "2.0",
   300  				},
   301  				NamedSections: map[string]map[string]string{
   302  					"org/apache/tools/ant/taskdefs/optional/": {
   303  						"Specification-Version": "1.8",
   304  					},
   305  					"some-other-section": {
   306  						"Implementation-Version": "1.8.2",
   307  					},
   308  				},
   309  			},
   310  			expected: "1.8.2",
   311  		},
   312  		{
   313  			name: "Implementation-Version takes precedence over Specification-Version in subsequent section",
   314  			manifest: pkg.JavaManifest{
   315  				Main: map[string]string{
   316  					"Manifest-Version": "1.0",
   317  					"Ant-Version":      "Apache Ant 1.8.2",
   318  					"Created-By":       "1.5.0_22-b03 (Sun Microsystems Inc.)",
   319  				},
   320  				NamedSections: map[string]map[string]string{
   321  					"some-other-section": {
   322  						"Bundle-Version": "1.11.28",
   323  					},
   324  				},
   325  			},
   326  			expected: "1.11.28",
   327  		},
   328  	}
   329  
   330  	for _, test := range tests {
   331  		t.Run(test.name, func(t *testing.T) {
   332  			result := selectVersion(&test.manifest, test.archive)
   333  
   334  			assert.Equal(t, test.expected, result)
   335  		})
   336  	}
   337  }