diff --git a/.github/linters/.gitleaks.toml b/.github/linters/.gitleaks.toml
new file mode 100644
index 000000000..6012aaeb6
--- /dev/null
+++ b/.github/linters/.gitleaks.toml
@@ -0,0 +1,5 @@
+title = "gitleaks config"
+[allowlist]
+files = [
+ "cyclonedx-lib/dependency_data/dependency_data.properties"
+]
diff --git a/.github/linters/suppressed-java.xml b/.github/linters/suppressed-java.xml
index b9c1a256e..0acb11e80 100644
--- a/.github/linters/suppressed-java.xml
+++ b/.github/linters/suppressed-java.xml
@@ -28,4 +28,5 @@
-
\ No newline at end of file
+
+
diff --git a/.github/workflows/testsbom.yml b/.github/workflows/testcyclonedx.yml
similarity index 52%
rename from .github/workflows/testsbom.yml
rename to .github/workflows/testcyclonedx.yml
index 073411098..f4f5703d9 100644
--- a/.github/workflows/testsbom.yml
+++ b/.github/workflows/testcyclonedx.yml
@@ -1,5 +1,5 @@
# ********************************************************************************
-# Copyright (c) 2023 Contributors to the Eclipse Foundation
+# Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) with this work for additional
# information regarding copyright ownership.
@@ -12,7 +12,7 @@
# ********************************************************************************
---
-name: TestSBOM
+name: TestCycloneDX
on:
pull_request:
@@ -30,21 +30,21 @@ permissions:
contents: read
jobs:
- test_sbom_gen:
- name: gen_sbom
+ test_cyclonedx_gen:
+ name: gen_cyclonedx
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- # Build with jdk8 to ensure TemurinGenSBOM meets min compatibility
+ # Build with jdk8 to ensure TemurinGen* meets min compatibility
- uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
id: setup-java
with:
java-version: 8
distribution: 'temurin'
- - name: Build TemurinGenSBOM.java
+ - name: Build TemurinGenSBOM.java and TemurinGenCDXA.java
run: |
ant -noinput -buildfile cyclonedx-lib/build.xml clean
ant -noinput -buildfile cyclonedx-lib/build.xml build
@@ -52,8 +52,27 @@ jobs:
- name: Run TemurinGenSBOM Unit test
run: ant -noinput -buildfile cyclonedx-lib/build.xml run
+ - name: Run TemurinGenCDXA Unit test
+ run: ant -noinput -buildfile cyclonedx-lib/build.xml runCDXA
+
+ - name: Validate generated SBOM and CDXA documents using cyclonedx-cli validate
+ run: |
+ curl -L -O https://github.com/CycloneDX/cyclonedx-cli/releases/latest/download/cyclonedx-linux-x64
+ chmod +x cyclonedx-linux-x64
+ ./cyclonedx-linux-x64 validate --input-file cyclonedx-lib/build/testSBOM.json --fail-on-errors --input-version v1_6
+ ./cyclonedx-linux-x64 validate --input-file cyclonedx-lib/build/testSBOM.xml --fail-on-errors --input-version v1_6
+ ./cyclonedx-linux-x64 validate --input-file cyclonedx-lib/build/testCDXA.json --fail-on-errors --input-version v1_6
+ ./cyclonedx-linux-x64 validate --input-file cyclonedx-lib/build/testCDXA.xml --fail-on-errors --input-version v1_6
+
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
name: Collect and Archive TemurinGenSBOM Artifacts
with:
name: testSBOM
- path: cyclonedx-lib/build/testSBOM.json
+ path: cyclonedx-lib/build/testSBOM.*
+
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
+ name: Collect and Archive TemurinGenCDXA Artifacts
+ with:
+ name: testCDXA
+ path: cyclonedx-lib/build/testCDXA.*
+
diff --git a/cyclonedx-lib/build.xml b/cyclonedx-lib/build.xml
index f026a2611..f4e11f65c 100644
--- a/cyclonedx-lib/build.xml
+++ b/cyclonedx-lib/build.xml
@@ -30,7 +30,7 @@
-
+
@@ -42,8 +42,11 @@
+
+
+
@@ -67,7 +70,7 @@
-
+
@@ -114,11 +117,23 @@
+
+
+
+
-
+
+
+
+
+
+
+
+
+
@@ -134,11 +149,16 @@
-
+
+
+
+
+
+
@@ -179,9 +199,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -193,6 +274,7 @@
+
@@ -333,7 +415,7 @@
-
+
@@ -353,7 +435,7 @@
-
+
@@ -417,7 +499,7 @@
-
+
@@ -429,7 +511,7 @@
-
+
@@ -476,6 +558,289 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cyclonedx-lib/dependency_data/dependency_data.properties b/cyclonedx-lib/dependency_data/dependency_data.properties
index 4b5607d50..cb2d15487 100644
--- a/cyclonedx-lib/dependency_data/dependency_data.properties
+++ b/cyclonedx-lib/dependency_data/dependency_data.properties
@@ -21,11 +21,14 @@ commons-codec.jar=commons-codec-${commons-codec.version}.jar
commons-collections4.version=4.4
commons-collections4.sha256=1df8b9430b5c8ed143d7815e403e33ef5371b2400aadbe9bda0883762e0846d1
commons-collections4.jar=commons-collections4-${commons-collections4.version}.jar
+commons-lang3.version=3.17.0
+commons-lang3.sha256=6ee731df5c8e5a2976a1ca023b6bb320ea8d3539fbe64c8a1d5cb765127c33b4
+commons-lang3.jar=commons-lang3-${commons-lang3.version}.jar
commons-io.version=2.16.1
commons-io.sha256=f41f7baacd716896447ace9758621f62c1c6b0a91d89acee488da26fc477c84f
commons-io.jar=commons-io-${commons-io.version}.jar
-cyclonedx-core-java.version=9.0.5
-cyclonedx-core-java.sha256=9474c73a81d9be6206367d357a3449e03e70c69bc672d82be04f15806ef170fa
+cyclonedx-core-java.version=9.1.0
+cyclonedx-core-java.sha256=a911ee5e5ebdabc2b2c08d08f9c92c673c21965ee1b982f40fc166d80f1eb088
cyclonedx-core-java.jar=cyclonedx-core-java-${cyclonedx-core-java.version}.jar
github-package-url.version=1.5.0
github-package-url.sha256=e45551727707acc0c56ac62d56964332ea0f138d6cc3656d988b9369150f5247
@@ -45,10 +48,17 @@ jackson-dataformat-xml.jar=jackson-dataformat-xml-${jackson-dataformat-xml.versi
json-schema-validator.version=1.5.1
json-schema-validator.sha256=de015f79d4a63d22c002bad76bb30c039cafa205465eef8770e2c6b85880ded7
json-schema-validator.jar=json-schema-validator-${json-schema-validator.version}.jar
+stax2-api.version=4.2.2
+stax2-api.sha256=a61c48d553efad78bc01fffc4ac528bebbae64cbaec170b2a5e39cf61eb51abe
+stax2-api.jar=stax2-api-${stax2-api.version}.jar
+woodstox-core.version=7.1.0
+woodstox-core.sha256=81266920a1cdc47306a8a2b4726c99ec89b3fbf31c2470e4f5e477d9d857ca9f
+woodstox-core.jar=woodstox-core-${woodstox-core.version}.jar
# Download URLs
commons-codec.url=${maven.central.repo}/commons-codec/commons-codec/${commons-codec.version}/${commons-codec.jar}
commons-collections4.url=${maven.central.repo}/org/apache/commons/commons-collections4/${commons-collections4.version}/${commons-collections4.jar}
+commons-lang3.url=${maven.central.repo}/org/apache/commons/commons-lang3/${commons-lang3.version}/${commons-lang3.jar}
commons-io.url=${maven.central.repo}/commons-io/commons-io/${commons-io.version}/${commons-io.jar}
cyclonedx-core-java.url=${maven.central.repo}/org/cyclonedx/cyclonedx-core-java/${cyclonedx-core-java.version}/${cyclonedx-core-java.jar}
github-package-url.url=${maven.central.repo}/com/github/package-url/packageurl-java/${github-package-url.version}/${github-package-url.jar}
@@ -57,4 +67,6 @@ jackson-core.url=${maven.central.repo}/com/fasterxml/jackson/core/jackson-core/$
jackson-databind.url=${maven.central.repo}/com/fasterxml/jackson/core/jackson-databind/${jackson-databind.version}/${jackson-databind.jar}
jackson-dataformat-xml.url=${maven.central.repo}/com/fasterxml/jackson/dataformat/jackson-dataformat-xml/${jackson-dataformat-xml.version}/${jackson-dataformat-xml.jar}
json-schema-validator.url=${maven.central.repo}/com/networknt/json-schema-validator/${json-schema-validator.version}/${json-schema-validator.jar}
+stax2-api.url=${maven.central.repo}/org/codehaus/woodstox/stax2-api/${stax2-api.version}/${stax2-api.jar}
+woodstox-core.url=${maven.central.repo}/com/fasterxml/woodstox/woodstox-core/${woodstox-core.version}/${woodstox-core.jar}
diff --git a/cyclonedx-lib/sign_src/TemurinSignSBOM.java b/cyclonedx-lib/sign_src/TemurinSignSBOM.java
index 14784fc76..d9d35f105 100644
--- a/cyclonedx-lib/sign_src/TemurinSignSBOM.java
+++ b/cyclonedx-lib/sign_src/TemurinSignSBOM.java
@@ -60,7 +60,7 @@ private TemurinSignSBOM() {
* @param args Arguments for sbom operation.
*/
public static void main(final String[] args) {
- String cmd = null;
+ String cmd = "";
String privateKeyFile = null;
String publicKeyFile = null;
String fileName = null;
@@ -95,6 +95,8 @@ public static void main(final String[] args) {
} else if (cmd.equals("verifySignature")) {
success = verifySignature(fileName, publicKeyFile); // set success to the result of verifySignature
System.out.println("Signature verification result: " + (success ? "Valid" : "Invalid"));
+ } else {
+ System.out.println("Please enter a command.");
}
// Set success to true only when the operation is completed successfully.
diff --git a/cyclonedx-lib/src/temurin/sbom/TemurinGenCDXA.java b/cyclonedx-lib/src/temurin/sbom/TemurinGenCDXA.java
new file mode 100644
index 000000000..a1f02c0f1
--- /dev/null
+++ b/cyclonedx-lib/src/temurin/sbom/TemurinGenCDXA.java
@@ -0,0 +1,336 @@
+/*
+ * ********************************************************************************
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Apache Software License 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ********************************************************************************
+ */
+
+package temurin.sbom;
+
+import org.cyclonedx.exception.GeneratorException;
+import org.cyclonedx.generators.json.BomJsonGenerator;
+import org.cyclonedx.generators.xml.BomXmlGenerator;
+import org.cyclonedx.model.Bom;
+import org.cyclonedx.model.Component;
+import org.cyclonedx.model.ExternalReference;
+import org.cyclonedx.model.Hash;
+import org.cyclonedx.model.OrganizationalEntity;
+import org.cyclonedx.model.attestation.Declarations;
+import org.cyclonedx.model.attestation.Assessor;
+import org.cyclonedx.model.attestation.Attestation;
+import org.cyclonedx.model.attestation.AttestationMap;
+import org.cyclonedx.model.attestation.Claim;
+import org.cyclonedx.model.attestation.affirmation.Affirmation;
+import org.cyclonedx.model.attestation.affirmation.Signatory;
+import org.cyclonedx.model.attestation.Targets;
+import org.cyclonedx.parsers.JsonParser;
+import org.cyclonedx.parsers.XmlParser;
+import org.cyclonedx.Version;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.UUID;
+
+/**
+ * Command line tool to construct a CycloneDX CDXA.
+ */
+public final class TemurinGenCDXA {
+
+ private static boolean verbose = false;
+ private static boolean useJson = false;
+
+ // Valid predicates
+ enum CDXAPredicate {
+ VERIFIED_REPRODUCIBLE_BUILD
+ };
+
+ private TemurinGenCDXA() {
+ }
+
+ /**
+ * Main entry.
+ * @param args Arguments for operation.
+ */
+ public static void main(final String[] args) {
+ String cmd = "";
+ String fileName = null;
+ String attestingOrgName = null;
+ String predicate = null;
+ String targetName = null;
+ String targetUrl = null;
+ String targetHash = null;
+ String affirmationStmt = null;
+ String affirmationWebsite = null;
+ boolean thirdParty = true;
+
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("--jsonFile")) {
+ fileName = args[++i];
+ useJson = true;
+ } else if (args[i].equals("--xmlFile")) {
+ fileName = args[++i];
+ useJson = false;
+ } else if (args[i].equals("--attesting-org-name")) {
+ attestingOrgName = args[++i];
+ } else if (args[i].equals("--predicate")) {
+ predicate = args[++i];
+ } else if (args[i].equals("--target-name")) {
+ targetName = args[++i];
+ } else if (args[i].equals("--target-url")) {
+ targetUrl = args[++i];
+ } else if (args[i].equals("--target-sha256-hash")) {
+ targetHash = args[++i];
+ } else if (args[i].equals("--affirmation-stmt")) {
+ affirmationStmt = args[++i];
+ } else if (args[i].equals("--affirmation-website")) {
+ affirmationWebsite = args[++i];
+ } else if (args[i].equals("--not-third-party")) {
+ thirdParty = false;
+ } else if (args[i].equals("--createNewCDXA")) {
+ cmd = "createCDXA";
+ } else if (args[i].equals("--verbose")) {
+ verbose = true;
+ }
+ }
+
+ switch (cmd) {
+ case "createCDXA": // Create a new CDXA json file
+ Bom bom = createCdxa(fileName, attestingOrgName, predicate, targetName, targetUrl, targetHash, affirmationStmt, affirmationWebsite, thirdParty);
+ if (bom != null) {
+ writeFile(bom, fileName);
+ } else {
+ System.exit(1);
+ }
+ break;
+
+ default:
+ System.out.println("Please enter a command.");
+ System.exit(1);
+ }
+ }
+
+ static Bom createCdxa(final String fileName, final String attestingOrgName, final String predicate,
+ final String targetName, final String targetUrl, final String targetHash,
+ final String affirmationStmt, final String affirmationWebsite, final boolean thirdParty) {
+ // Validate inputs
+ boolean validInput = true;
+ if (fileName == null) {
+ System.out.println("--xmlFile|--jsonFile not specified");
+ validInput = false;
+ }
+ if (attestingOrgName == null) {
+ System.out.println("--attesting-org-name not specified");
+ validInput = false;
+ }
+ if (predicate == null) {
+ System.out.println("--predicate not specified"); validInput = false;
+ } else {
+ boolean validPred = false;
+ for (CDXAPredicate validPredicate : CDXAPredicate.values()) {
+ if (validPredicate.name().equals(predicate)) {
+ validPred = true;
+ break;
+ }
+ }
+ if (!validPred) {
+ System.out.println("--predicate " + predicate + " not a valid value");
+ validInput = false;
+ }
+ }
+ if (targetName == null) {
+ System.out.println("--target-name not specified");
+ validInput = false;
+ }
+ if (targetUrl == null) {
+ System.out.println("--target-url not specified");
+ validInput = false;
+ }
+ if (targetHash == null) {
+ System.out.println("--target-sha256-hash not specified");
+ validInput = false;
+ }
+ if (affirmationStmt == null) {
+ System.out.println("--affirmation-stmt not specified");
+ validInput = false;
+ }
+ if (affirmationWebsite == null) {
+ System.out.println("--affirmation-website not specified");
+ validInput = false;
+ }
+ if (!validInput) {
+ return null;
+ }
+
+ Declarations declarations = new Declarations();
+ Assessor assessor = new Assessor();
+ Claim claim = new Claim();
+ Targets targets = new Targets();
+ Affirmation affirmation = new Affirmation();
+ Signatory signatory = new Signatory();
+ Attestation attestation = new Attestation();
+ AttestationMap attestationMap = new AttestationMap();
+
+ final String targetJdkBomRef = "target-jdk-1";
+ final String assessorBomRef = "assessor-1";
+ final String claimBomRef = "claim-1";
+
+ // External reference to the target JDK
+ ExternalReference extRef = new ExternalReference();
+ Hash hash1 = new Hash(Hash.Algorithm.SHA_256, targetHash);
+ extRef.addHash(hash1);
+ extRef.setUrl(targetUrl);
+ extRef.setType(ExternalReference.Type.DISTRIBUTION);
+
+ // Target JDK Component
+ Component targetJDK = new Component();
+ targetJDK.setType(Component.Type.APPLICATION);
+ targetJDK.setName(targetName);
+ targetJDK.addExternalReference(extRef);
+ targetJDK.setBomRef(targetJdkBomRef);
+ List components = new LinkedList();
+ components.add(targetJDK);
+ targets.setComponents(components);
+ declarations.setTargets(targets);
+
+ // Assessor
+ assessor.setThirdParty(thirdParty);
+ OrganizationalEntity org = new OrganizationalEntity();
+ org.setName(attestingOrgName);
+ assessor.setOrganization(org);
+ assessor.setBomRef(assessorBomRef);
+ List assessors = new LinkedList();
+ assessors.add(assessor);
+ declarations.setAssessors(assessors);
+
+ // Claim
+ claim.setPredicate(predicate);
+ claim.setTarget(targetJDK.getBomRef());
+ claim.setBomRef(claimBomRef);
+ List claims = new LinkedList();
+ claims.add(claim);
+ declarations.setClaims(claims);
+
+ // Affirmation
+ affirmation.setStatement(affirmationStmt);
+ signatory.setOrganization(org);
+ ExternalReference orgExtRef = new ExternalReference();
+ orgExtRef.setUrl(affirmationWebsite);
+ orgExtRef.setType(ExternalReference.Type.WEBSITE);
+ signatory.setExternalReference(orgExtRef);
+ List signatories = new LinkedList();
+ signatories.add(signatory);
+ affirmation.setSignatories(signatories);
+ declarations.setAffirmation(affirmation);
+
+ // Construct the Attestation
+ attestation.setSummary("Eclipse Temurin Attestation");
+ attestation.setAssessor(assessor.getBomRef());
+ List claimsList = new LinkedList();
+ claimsList.add(claim.getBomRef());
+ attestationMap.setClaims(claimsList);
+ List attestationMaps = new LinkedList();
+ attestationMaps.add(attestationMap);
+ attestation.setMap(attestationMaps);
+ List attestations = new LinkedList();
+ attestations.add(attestation);
+ declarations.setAttestations(attestations);
+
+ // Create CDXA Bom
+ Bom cdxa = new Bom();
+ cdxa.setSerialNumber("urn:uuid:" + UUID.randomUUID());
+ cdxa.setDeclarations(declarations);
+
+ return cdxa;
+ }
+
+ static String generateBomJson(final Bom bom) throws GeneratorException {
+ // Use schema v16: https://cyclonedx.org/schema/bom-1.6.schema.json
+ BomJsonGenerator bomGen = new BomJsonGenerator(bom, Version.VERSION_16);
+ String json = bomGen.toJsonString();
+ return json;
+ }
+
+ static String generateBomXml(final Bom bom) throws GeneratorException {
+ BomXmlGenerator bomGen = new BomXmlGenerator(bom, Version.VERSION_16);
+ String xml = bomGen.toXmlString();
+ return xml;
+ }
+
+ // Writes the BOM object to the specified type of file
+ static void writeFile(final Bom bom, final String fileName) {
+ if (useJson) {
+ writeJSONfile(bom, fileName);
+ } else {
+ writeXMLfile(bom, fileName);
+ }
+ }
+
+ // Writes the BOM object to the specified JSON file.
+ static void writeJSONfile(final Bom bom, final String fileName) {
+ FileWriter file;
+ try {
+ String json = generateBomJson(bom);
+
+ file = new FileWriter(fileName);
+ file.write(json);
+ file.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ // Writes the BOM object to the specified XML file.
+ static void writeXMLfile(final Bom bom, final String fileName) {
+ FileWriter file;
+ try {
+ String xml = generateBomXml(bom);
+
+ file = new FileWriter(fileName);
+ file.write(xml);
+ file.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ // Returns a parsed BOM object from the specified file.
+ static Bom readJSONfile(final String fileName) {
+ Bom bom = null;
+ try {
+ FileReader reader = new FileReader(fileName);
+ JsonParser parser = new JsonParser();
+ bom = parser.parse(reader);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ } finally {
+ return bom;
+ }
+ }
+
+ // Returns a parsed BOM object from the specified file.
+ static Bom readXMLfile(final String fileName) {
+ Bom bom = null;
+ try {
+ FileReader reader = new FileReader(fileName);
+ XmlParser parser = new XmlParser();
+ bom = parser.parse(reader);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ } finally {
+ return bom;
+ }
+ }
+}
diff --git a/cyclonedx-lib/src/temurin/sbom/TemurinGenSBOM.java b/cyclonedx-lib/src/temurin/sbom/TemurinGenSBOM.java
index 639ed6737..4533a9c37 100644
--- a/cyclonedx-lib/src/temurin/sbom/TemurinGenSBOM.java
+++ b/cyclonedx-lib/src/temurin/sbom/TemurinGenSBOM.java
@@ -17,9 +17,9 @@
import org.cyclonedx.exception.GeneratorException;
import org.cyclonedx.generators.json.BomJsonGenerator;
+import org.cyclonedx.generators.xml.BomXmlGenerator;
import org.cyclonedx.model.Bom;
import org.cyclonedx.model.Component;
-import org.cyclonedx.model.ExternalReference;
import org.cyclonedx.model.formulation.Formula;
import org.cyclonedx.model.Hash;
import org.cyclonedx.model.Metadata;
@@ -28,12 +28,14 @@
import org.cyclonedx.model.Property;
import org.cyclonedx.model.Tool;
import org.cyclonedx.parsers.JsonParser;
+import org.cyclonedx.parsers.XmlParser;
import org.cyclonedx.Version;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Collections;
import java.util.List;
import java.util.LinkedList;
+import java.util.UUID;
/**
* Command line tool to construct a CycloneDX SBOM.
@@ -41,6 +43,7 @@
public final class TemurinGenSBOM {
private static boolean verbose = false;
+ private static boolean useJson = false;
private TemurinGenSBOM() {
}
@@ -50,7 +53,7 @@ private TemurinGenSBOM() {
* @param args Arguments for sbom operation.
*/
public static void main(final String[] args) {
- String cmd = null;
+ String cmd = "";
String comment = null;
String compName = null;
String formulaName = null;
@@ -67,6 +70,10 @@ public static void main(final String[] args) {
for (int i = 0; i < args.length; i++) {
if (args[i].equals("--jsonFile")) {
fileName = args[++i];
+ useJson = true;
+ } else if (args[i].equals("--xmlFile")) {
+ fileName = args[++i];
+ useJson = false;
} else if (args[i].equals("--version")) {
version = args[++i];
} else if (args[i].equals("--name")) {
@@ -120,69 +127,60 @@ public static void main(final String[] args) {
}
}
switch (cmd) {
- case "createNewSBOM": // Creates JSON file
+ case "createNewSBOM": // Creates new SBOM
Bom bom = createBom();
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addMetadata": // Adds Metadata --> name
bom = addMetadata(fileName);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addMetadataComponent": // Adds Metadata --> Component --> name
bom = addMetadataComponent(fileName, name, type, version, description);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addMetadataProperty": // Adds MetaData --> Property --> name-value:
bom = addMetadataProperty(fileName, name, value);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addFormulation": // Adds Formulation --> name
bom = addFormulation(fileName, formulaName);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addFormulationComp": // Adds Formulation --> Component--> name
bom = addFormulationComp(fileName, formulaName, name, type);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addFormulationCompProp": // Adds Formulation --> Component -> name-value:
bom = addFormulationCompProp(fileName, formulaName, compName, name, value);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addMetadataTools":
bom = addMetadataTools(fileName, tool, version);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addComponent": // Adds Components --> Component --> name
bom = addComponent(fileName, compName, version, description);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addComponentHash": // Adds Components --> Component --> hash
bom = addComponentHash(fileName, compName, hash);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
case "addComponentProp": // Adds Components --> Component --> name-value pairs
bom = addComponentProperty(fileName, compName, name, value);
- writeJSONfile(bom, fileName);
- break;
-
- case "addExternalReference": // Adds external Reference
- bom = addExternalReference(fileName, hash, url, comment);
- writeJSONfile(bom, fileName);
+ writeFile(bom, fileName);
break;
- case "addComponentExternalReference": // Adds external Reference to component
- bom = addComponentExternalReference(fileName, hash, url, comment);
- writeJSONfile(bom, fileName);
- break;
default:
System.out.println("Please enter a command.");
}
@@ -194,12 +192,13 @@ public static void main(final String[] args) {
*/
static Bom createBom() {
Bom bom = new Bom();
+ bom.setSerialNumber("urn:uuid:" + UUID.randomUUID());
return bom;
}
// Method to store Metadata --> name.
static Bom addMetadata(final String fileName) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
Metadata meta = new Metadata();
OrganizationalEntity org = new OrganizationalEntity();
org.setName("Eclipse Foundation");
@@ -213,7 +212,7 @@ static Bom addMetadata(final String fileName) {
}
static Bom addMetadataComponent(final String fileName, final String name, final String type, final String version, final String description) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
Metadata meta = new Metadata();
Component comp = new Component();
Component.Type compType = Component.Type.FRAMEWORK;
@@ -235,7 +234,7 @@ static Bom addMetadataComponent(final String fileName, final String name, final
// Method to store Metadata --> Properties List --> name-values.
static Bom addMetadataProperty(final String fileName, final String name, final String value) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
Metadata meta = new Metadata();
Property prop1 = new Property();
meta = bom.getMetadata();
@@ -247,7 +246,7 @@ static Bom addMetadataProperty(final String fileName, final String name, final S
}
static Bom addMetadataTools(final String fileName, final String toolName, final String version) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
Metadata meta = new Metadata();
Tool tool = new Tool();
meta = bom.getMetadata();
@@ -260,7 +259,7 @@ static Bom addMetadataTools(final String fileName, final String toolName, final
// Method to store Component --> name & single name-value pair.
static Bom addComponent(final String fileName, final String compName, final String version, final String description) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
Component comp = new Component();
comp.setName(compName);
comp.setVersion(version);
@@ -274,7 +273,7 @@ static Bom addComponent(final String fileName, final String compName, final Stri
}
static Bom addComponentHash(final String fileName, final String compName, final String hash) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
List componentArrayList = bom.getComponents();
for (Component item : componentArrayList) {
if (item.getName().equals(compName)) {
@@ -287,7 +286,7 @@ static Bom addComponentHash(final String fileName, final String compName, final
// Method to add Component --> Property --> name-value pairs.
static Bom addComponentProperty(final String fileName, final String compName, final String name, final String value) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
List componentArrayList = bom.getComponents();
for (Component item : componentArrayList) {
if (item.getName().equals(compName)) {
@@ -300,36 +299,8 @@ static Bom addComponentProperty(final String fileName, final String compName, fi
return bom;
}
- // Method to store externalReferences: dependency_version_alsa.
- static Bom addExternalReference(final String fileName, final String hash, final String url, final String comment) {
- Bom bom = readJSONfile(fileName);
- ExternalReference extRef = new ExternalReference();
- Hash hash1 = new Hash(Hash.Algorithm.SHA3_256, hash);
- extRef.setType(ExternalReference.Type.BUILD_SYSTEM); //required
- extRef.setUrl(url); // required must be a valid URL with protocol
- extRef.setComment(comment);
- extRef.addHash(hash1);
- bom.addExternalReference(extRef);
- return bom;
- }
-
- // Method to store externalReferences to store: openjdk_source.
- static Bom addComponentExternalReference(final String fileName, final String hash, final String url, final String comment) {
- Bom bom = readJSONfile(fileName);
- ExternalReference extRef = new ExternalReference();
- Hash hash1 = new Hash(Hash.Algorithm.SHA3_256, hash);
- Component comp = new Component();
- extRef.addHash(hash1);
- extRef.setUrl(url);
- extRef.setComment(comment); //"openjdk_source"
- extRef.setType(ExternalReference.Type.BUILD_SYSTEM);
- comp.addExternalReference(extRef);
- bom.addComponent(comp);
- return bom;
- }
-
static Bom addFormulation(final String fileName, final String name) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
List formulation = bom.getFormulation();
if (formulation == null) {
formulation = new LinkedList();
@@ -343,7 +314,7 @@ static Bom addFormulation(final String fileName, final String name) {
}
static Bom addFormulationComp(final String fileName, final String formulaName, final String name, final String type) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
if (formulaName == null) {
System.out.println("addFormulationComp: formulaName is null");
return bom;
@@ -376,7 +347,7 @@ static Bom addFormulationComp(final String fileName, final String formulaName, f
}
static Bom addFormulationCompProp(final String fileName, final String formulaName, final String componentName, final String name, final String value) {
- Bom bom = readJSONfile(fileName);
+ Bom bom = readFile(fileName);
boolean foundFormula = false;
boolean foundComponent = false;
List formulation = bom.getFormulation();
@@ -417,6 +388,32 @@ static String generateBomJson(final Bom bom) throws GeneratorException {
return json;
}
+ static String generateBomXml(final Bom bom) throws GeneratorException {
+ BomXmlGenerator bomGen = new BomXmlGenerator(bom, Version.VERSION_16);
+ String xml = bomGen.toXmlString();
+ return xml;
+ }
+
+ // Writes the BOM object to the specified type of file
+ static void writeFile(final Bom bom, final String fileName) {
+ if (useJson) {
+ writeJSONfile(bom, fileName);
+ } else {
+ writeXMLfile(bom, fileName);
+ }
+ }
+
+ // Read the BOM object from the specified type of file
+ static Bom readFile(final String fileName) {
+ Bom bom;
+ if (useJson) {
+ bom = readJSONfile(fileName);
+ } else {
+ bom = readXMLfile(fileName);
+ }
+ return bom;
+ }
+
// Writes the BOM object to the specified file.
static void writeJSONfile(final Bom bom, final String fileName) {
FileWriter file;
@@ -432,6 +429,21 @@ static void writeJSONfile(final Bom bom, final String fileName) {
}
}
+ // Writes the BOM object to the specified XML file.
+ static void writeXMLfile(final Bom bom, final String fileName) {
+ FileWriter file;
+ try {
+ String xml = generateBomXml(bom);
+
+ file = new FileWriter(fileName);
+ file.write(xml);
+ file.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
// Returns a parsed BOM object from the specified file.
static Bom readJSONfile(final String fileName) {
Bom bom = null;
@@ -446,4 +458,19 @@ static Bom readJSONfile(final String fileName) {
return bom;
}
}
+
+ // Returns a parsed BOM object from the specified file.
+ static Bom readXMLfile(final String fileName) {
+ Bom bom = null;
+ try {
+ FileReader reader = new FileReader(fileName);
+ XmlParser parser = new XmlParser();
+ bom = parser.parse(reader);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ } finally {
+ return bom;
+ }
+ }
}
diff --git a/sbin/common/sbom.sh b/sbin/common/sbom.sh
index 945389b68..7fd7869aa 100755
--- a/sbin/common/sbom.sh
+++ b/sbin/common/sbom.sh
@@ -205,18 +205,3 @@ addSBOMComponentPropertyFromFile() {
fi
}
-# Function not in use
-# Ref: https://cyclonedx.org/docs/1.4/json/#externalReferences
-addExternalReference() {
- local javaHome="${1}"
- local classpath="${2}"
- local jsonFile="${3}"
- local url="${4}" # required
- local comment="${5}"
- local hash="${6}"
- if [ -z "${hash}" ]; then
- "${javaHome}"/bin/java -cp "${classpath}" temurin.sbom.TemurinGenSBOM --addExternalReference --jsonFile "${jsonFile}" --url "${url}" --comment "${comment}" --hash "${hash}"
- else
- "${javaHome}"/bin/java -cp "${classpath}" temurin.sbom.TemurinGenSBOM --addExternalReference --jsonFile "${jsonFile}" --url "${url}" --comment "${comment}"
- fi
-}