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 }