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 -}