Skip to content

Commit f85ba94

Browse files
committed
Add StandardBomParserTest.testCreateSbomFromScratch() to check for differences in format when updating from upstream
1 parent 2f8cad4 commit f85ba94

2 files changed

Lines changed: 307 additions & 0 deletions

File tree

src/test/java/com/siemens/sbom/standardbom/StandardBomParserTest.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,26 @@
1212
import java.nio.charset.StandardCharsets;
1313
import java.nio.file.Files;
1414
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;
1521
import java.util.Date;
1622
import java.util.List;
1723
import java.util.regex.Matcher;
1824
import java.util.regex.Pattern;
1925
import javax.annotation.Nonnull;
2026

27+
import com.github.packageurl.MalformedPackageURLException;
28+
import com.github.packageurl.PackageURL;
29+
import com.github.packageurl.PackageURLBuilder;
2130
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;
2235
import org.junit.Assert;
2336
import org.junit.Assume;
2437
import org.junit.Rule;
@@ -27,6 +40,9 @@
2740

2841
import com.siemens.sbom.standardbom.internal.VersionUtil;
2942
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;
3046
import com.siemens.sbom.standardbom.model.StandardBom;
3147

3248

@@ -202,4 +218,112 @@ private StandardBom parseFile(final String pFilename)
202218
}
203219
return null;
204220
}
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+
}
205329
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
{
2+
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
3+
"bomFormat" : "CycloneDX",
4+
"specVersion" : "1.6",
5+
"serialNumber" : "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
6+
"version" : 1,
7+
"metadata" : {
8+
"timestamp" : "2026-01-02T12:00:00Z",
9+
"tools" : {
10+
"components" : [
11+
{
12+
"type" : "library",
13+
"supplier" : {
14+
"name" : "Siemens AG"
15+
},
16+
"group" : "com.siemens.sbom.standardbom",
17+
"name" : "standard-bom-java",
18+
"version" : "${libraryVersion}",
19+
"description" : "Standard BOM helper library for Java",
20+
"externalReferences" : [
21+
{
22+
"type" : "website",
23+
"url" : "https://github.com/siemens/standard-bom-java"
24+
}
25+
]
26+
}
27+
]
28+
},
29+
"component" : {
30+
"group" : "com.example",
31+
"name" : "primary",
32+
"version" : "2.0.0",
33+
"description" : "Description of the primary component"
34+
},
35+
"supplier" : {
36+
"name" : "ACME"
37+
},
38+
"properties" : [
39+
{
40+
"name" : "siemens:sbomNature",
41+
"value" : "binary"
42+
},
43+
{
44+
"name" : "siemens:profile",
45+
"value" : "clearing"
46+
}
47+
]
48+
},
49+
"components" : [
50+
{
51+
"type" : "library",
52+
"bom-ref" : "pkg:maven/com.siemens.sbom/component1@1.0.0",
53+
"authors" : [
54+
{
55+
"name" : "author1",
56+
"email" : "author1@example.com"
57+
},
58+
{
59+
"name" : "author2",
60+
"email" : "author2@example.com"
61+
}
62+
],
63+
"group" : "com.siemens.sbom",
64+
"name" : "component1",
65+
"version" : "1.0.0",
66+
"description" : "This is the description of component1",
67+
"hashes" : [
68+
{
69+
"alg" : "MD5",
70+
"content" : "15f41a7cddbf60f741bc49966c38f75b"
71+
},
72+
{
73+
"alg" : "SHA-1",
74+
"content" : "d771af8e336e372fb5399c99edabe0919aeaf5b2"
75+
}
76+
],
77+
"licenses" : [
78+
{
79+
"license" : {
80+
"name" : "The Apache Software License, version 2.0",
81+
"url" : "https://www.apache.org/licenses/LICENSE-2.0.txt"
82+
}
83+
},
84+
{
85+
"license" : {
86+
"id" : "MIT",
87+
"url" : "https://www.opensource.org/licenses/mit-license.php"
88+
}
89+
}
90+
],
91+
"copyright" : "(c) the copyright holders",
92+
"cpe" : "cpe:2.3:a:com.siemens.sbom:component1:-:*:*:*:*:*:*:*",
93+
"purl" : "pkg:maven/com.siemens.sbom/component1@1.0.0",
94+
"externalReferences" : [
95+
{
96+
"type" : "website",
97+
"url" : "https://example.com"
98+
},
99+
{
100+
"type" : "vcs",
101+
"url" : "https://example.com/component1.git"
102+
},
103+
{
104+
"type" : "distribution",
105+
"url" : "file:///binaries/d771af8e336e372fb5399c99edabe0919aeaf5b2/component1.jar",
106+
"comment" : "relativePath"
107+
},
108+
{
109+
"type" : "source-distribution",
110+
"url" : "https://example.com/sources/component1-sources.jar",
111+
"hashes" : [
112+
{
113+
"alg" : "MD5",
114+
"content" : "11141a7cddbf60f741bc49966c38f111"
115+
},
116+
{
117+
"alg" : "SHA-1",
118+
"content" : "7771af8e336e372fb5399c99edabe0919aeaf777"
119+
}
120+
]
121+
},
122+
{
123+
"type" : "distribution",
124+
"url" : "file:///sources/7771af8e336e372fb5399c99edabe0919aeaf777/component1-sources.jar",
125+
"comment" : "source archive (local copy)",
126+
"hashes" : [
127+
{
128+
"alg" : "MD5",
129+
"content" : "11141a7cddbf60f741bc49966c38f111"
130+
},
131+
{
132+
"alg" : "SHA-1",
133+
"content" : "7771af8e336e372fb5399c99edabe0919aeaf777"
134+
}
135+
]
136+
}
137+
],
138+
"properties" : [
139+
{
140+
"name" : "siemens:direct",
141+
"value" : "true"
142+
},
143+
{
144+
"name" : "siemens:filename",
145+
"value" : "component1.jar"
146+
},
147+
{
148+
"name" : "siemens:internal",
149+
"value" : "false"
150+
},
151+
{
152+
"name" : "siemens:legalRemark",
153+
"value" : "a legal remark"
154+
},
155+
{
156+
"name" : "siemens:primaryLanguage",
157+
"value" : "Java"
158+
},
159+
{
160+
"name" : "siemens:thirdPartyNotices",
161+
"value" : "line1\nline2\nline3"
162+
}
163+
]
164+
}
165+
],
166+
"definitions" : {
167+
"standards" : [
168+
{
169+
"name" : "Standard BOM",
170+
"version" : "${formatVersion}",
171+
"description" : "The Standard for Software Bills of Materials in Siemens",
172+
"owner" : "Siemens AG",
173+
"externalReferences" : [
174+
{
175+
"type" : "website",
176+
"url" : "https://sbom.siemens.io/"
177+
}
178+
],
179+
"bom-ref" : "standard-bom"
180+
}
181+
]
182+
}
183+
}

0 commit comments

Comments
 (0)