|
12 | 12 | import java.nio.charset.StandardCharsets; |
13 | 13 | import java.nio.file.Files; |
14 | 14 | import java.nio.file.Paths; |
| 15 | +import java.time.LocalDate; |
| 16 | +import java.time.LocalDateTime; |
| 17 | +import java.time.LocalTime; |
| 18 | +import java.time.Month; |
| 19 | +import java.time.ZoneOffset; |
| 20 | +import java.util.Arrays; |
15 | 21 | import java.util.Date; |
16 | 22 | import java.util.List; |
17 | 23 | import java.util.regex.Matcher; |
18 | 24 | import java.util.regex.Pattern; |
19 | 25 | import javax.annotation.Nonnull; |
20 | 26 |
|
| 27 | +import com.github.packageurl.MalformedPackageURLException; |
| 28 | +import com.github.packageurl.PackageURL; |
| 29 | +import com.github.packageurl.PackageURLBuilder; |
21 | 30 | import org.cyclonedx.exception.ParseException; |
| 31 | +import org.cyclonedx.model.Component; |
| 32 | +import org.cyclonedx.model.License; |
| 33 | +import org.cyclonedx.model.OrganizationalContact; |
| 34 | +import org.cyclonedx.model.OrganizationalEntity; |
22 | 35 | import org.junit.Assert; |
23 | 36 | import org.junit.Assume; |
24 | 37 | import org.junit.Rule; |
|
27 | 40 |
|
28 | 41 | import com.siemens.sbom.standardbom.internal.VersionUtil; |
29 | 42 | import com.siemens.sbom.standardbom.model.BomEntry; |
| 43 | +import com.siemens.sbom.standardbom.model.SbomNature; |
| 44 | +import com.siemens.sbom.standardbom.model.SourceArtifactRefLocal; |
| 45 | +import com.siemens.sbom.standardbom.model.SourceArtifactRefUrl; |
30 | 46 | import com.siemens.sbom.standardbom.model.StandardBom; |
31 | 47 |
|
32 | 48 |
|
@@ -202,4 +218,112 @@ private StandardBom parseFile(final String pFilename) |
202 | 218 | } |
203 | 219 | return null; |
204 | 220 | } |
| 221 | + |
| 222 | + |
| 223 | + |
| 224 | + @Test |
| 225 | + public void testCreateSbomFromScratch() |
| 226 | + throws MalformedPackageURLException, IOException, URISyntaxException |
| 227 | + { |
| 228 | + final StandardBom sbom = new StandardBom(); |
| 229 | + sbom.setSbomNature(SbomNature.Binary); |
| 230 | + sbom.setProfile("clearing"); |
| 231 | + LocalDateTime sometime = LocalDate.of(2026, Month.JANUARY, 2).atTime(LocalTime.of(12, 0, 0)); |
| 232 | + sbom.setTimestamp(Date.from(sometime.toInstant(ZoneOffset.UTC))); |
| 233 | + sbom.setSerialNumber("urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79"); |
| 234 | + |
| 235 | + OrganizationalEntity supplier = new OrganizationalEntity(); |
| 236 | + supplier.setName("ACME"); |
| 237 | + sbom.getMetadata().setSupplier(supplier); |
| 238 | + |
| 239 | + Component primaryComponent = new Component(); |
| 240 | + primaryComponent.setName("primary"); |
| 241 | + primaryComponent.setDescription("Description of the primary component"); |
| 242 | + primaryComponent.setVersion("2.0.0"); |
| 243 | + primaryComponent.setGroup("com.example"); |
| 244 | + sbom.getMetadata().setComponent(primaryComponent); |
| 245 | + |
| 246 | + // actual content: |
| 247 | + sbom.addComponent(buildComponentEntry()); |
| 248 | + |
| 249 | + final File outputFile = tempDir.newFile("scratch-actual.cdx.json"); |
| 250 | + new StandardBomParser().save(sbom, outputFile); |
| 251 | + Assert.assertTrue(outputFile.canRead()); |
| 252 | + |
| 253 | + @SuppressWarnings("ConstantConditions") |
| 254 | + byte[] expBytes = Files.readAllBytes(Paths.get(getClass().getResource("scratch-expected.cdx.json").toURI())); |
| 255 | + String expected = new String(expBytes, StandardCharsets.UTF_8); |
| 256 | + expected = expected.replaceAll(Pattern.quote("${libraryVersion}"), Matcher.quoteReplacement(LIBRARY_VERSION)); |
| 257 | + expected = expected.replaceAll(Pattern.quote("${formatVersion}"), Matcher.quoteReplacement(FORMAT_VERSION)); |
| 258 | + final String actual = new String(Files.readAllBytes(Paths.get(outputFile.toURI())), StandardCharsets.UTF_8); |
| 259 | + |
| 260 | + Assert.assertEquals(expected, actual); |
| 261 | + } |
| 262 | + |
| 263 | + |
| 264 | + |
| 265 | + private BomEntry buildComponentEntry() |
| 266 | + throws MalformedPackageURLException |
| 267 | + { |
| 268 | + final BomEntry component = new BomEntry(); |
| 269 | + component.setType(Component.Type.LIBRARY); |
| 270 | + component.setGroup("com.siemens.sbom"); |
| 271 | + component.setName("component1"); |
| 272 | + component.setVersion("1.0.0"); |
| 273 | + |
| 274 | + component.setPurl(PackageURLBuilder.aPackageURL() |
| 275 | + .withType(PackageURL.StandardTypes.MAVEN) |
| 276 | + .withNamespace("com.siemens.sbom") |
| 277 | + .withName("component1") |
| 278 | + .withVersion("1.0.0") |
| 279 | + .build().toString()); |
| 280 | + component.setBomRef(component.getPurl()); |
| 281 | + |
| 282 | + OrganizationalContact author1 = new OrganizationalContact(); |
| 283 | + author1.setName("author1"); |
| 284 | + author1.setEmail("author1@example.com"); |
| 285 | + OrganizationalContact author2 = new OrganizationalContact(); |
| 286 | + author2.setName("author2"); |
| 287 | + author2.setEmail("author2@example.com"); |
| 288 | + component.setAuthors(Arrays.asList(author1, author2)); |
| 289 | + |
| 290 | + component.setCopyright("(c) the copyright holders"); |
| 291 | + component.setThirdPartyNotices("line1\nline2\nline3"); |
| 292 | + component.setDescription("This is the description of component1"); |
| 293 | + component.setDirectDependency(true); |
| 294 | + component.setCpe("cpe:2.3:a:com.siemens.sbom:component1:-:*:*:*:*:*:*:*"); |
| 295 | + component.setFilename("component1.jar"); |
| 296 | + component.setLegalRemark("a legal remark"); |
| 297 | + component.setPrimaryLanguage("Java"); |
| 298 | + component.setInternal(false); |
| 299 | + component.setWebsite("https://example.com"); |
| 300 | + component.setRepoUrl("https://example.com/component1.git"); |
| 301 | + component.setRelativePath("file:///binaries/d771af8e336e372fb5399c99edabe0919aeaf5b2/component1.jar"); |
| 302 | + component.setMd5("15f41a7cddbf60f741bc49966c38f75b"); |
| 303 | + component.setSha1("d771af8e336e372fb5399c99edabe0919aeaf5b2"); |
| 304 | + |
| 305 | + License license1 = new License(); |
| 306 | + license1.setName("The Apache Software License, version 2.0"); |
| 307 | + license1.setUrl("https://www.apache.org/licenses/LICENSE-2.0.txt"); |
| 308 | + component.addLicense(license1); |
| 309 | + License license2 = new License(); |
| 310 | + license2.setId("MIT"); |
| 311 | + license2.setUrl("https://www.opensource.org/licenses/mit-license.php"); |
| 312 | + component.addLicense(license2); |
| 313 | + |
| 314 | + SourceArtifactRefUrl comp1SourceUrl = new SourceArtifactRefUrl(); |
| 315 | + comp1SourceUrl.setUrl("https://example.com/sources/component1-sources.jar"); |
| 316 | + comp1SourceUrl.setMd5("11141a7cddbf60f741bc49966c38f111"); |
| 317 | + comp1SourceUrl.setSha1("7771af8e336e372fb5399c99edabe0919aeaf777"); |
| 318 | + component.addSources(comp1SourceUrl); |
| 319 | + |
| 320 | + SourceArtifactRefLocal comp1SourceArchive = new SourceArtifactRefLocal(); |
| 321 | + comp1SourceArchive.setRelativePath( |
| 322 | + "file:///sources/7771af8e336e372fb5399c99edabe0919aeaf777/component1-sources.jar"); |
| 323 | + comp1SourceArchive.setMd5("11141a7cddbf60f741bc49966c38f111"); |
| 324 | + comp1SourceArchive.setSha1("7771af8e336e372fb5399c99edabe0919aeaf777"); |
| 325 | + component.addSources(comp1SourceArchive); |
| 326 | + |
| 327 | + return component; |
| 328 | + } |
205 | 329 | } |
0 commit comments