Skip to content

Commit

Permalink
Capture CycloneDX JAR SHAs in SBoM (#3538)
Browse files Browse the repository at this point in the history
* sbom: Updates to include CycloneDX jar SHA

Signed-off-by: Stewart X Addison <sxa@redhat.com>
  • Loading branch information
sxa authored Dec 11, 2023
1 parent b2ca4dd commit 380140f
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 2 deletions.
33 changes: 33 additions & 0 deletions cyclonedx-lib/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,41 @@
<arg value="--metadataName"/>
<arg value="Eclipse Adoptium"/>
<arg value="--jsonFile"/>
<arg value="${testSBOMFile}"/>
</java>
<java classpath="${classpath}" classname="temurin.sbom.TemurinGenSBOM">
<arg value="--verbose"/>
<arg value="--addFormulation"/>
<arg value="--formulaName"/>
<arg value="MyFormula"/>
<arg value="--jsonFile"/>
<arg value="${testSBOMFile}"/>
</java>
<java classpath="${classpath}" classname="temurin.sbom.TemurinGenSBOM">
<arg value="--verbose"/>
<arg value="--addFormulationComp"/>
<arg value="--formulaName"/>
<arg value="MyFormula"/>
<arg value="--name"/>
<arg value="CycloneDX SHAs"/>
<arg value="--jsonFile"/>
<arg value="${testSBOMFile}"/>
</java>
<java classpath="${classpath}" classname="temurin.sbom.TemurinGenSBOM">
<arg value="--verbose"/>
<arg value="--addFormulationCompProp"/>
<arg value="--formulaName"/>
<arg value="MyFormula"/>
<arg value="--compName"/>
<arg value="CycloneDX SHAs"/>
<arg value="--name"/>
<arg value="CycloneDX core lib"/>
<arg value="--value"/>
<arg value="sha123"/>
<arg value="--jsonFile"/>
<arg value="${testSBOMFile}"/>
</java>

</target>

<macrodef name="download-file" description="Use curl to download a file">
Expand Down
111 changes: 109 additions & 2 deletions cyclonedx-lib/src/temurin/sbom/TemurinGenSBOM.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
import org.cyclonedx.model.OrganizationalContact;
Expand All @@ -31,6 +32,7 @@
import java.io.FileWriter;
import java.util.Collections;
import java.util.List;
import java.util.LinkedList;

/**
* Command line tool to construct a CycloneDX SBOM.
Expand All @@ -50,6 +52,7 @@ public static void main(final String[] args) {
String cmd = null;
String comment = null;
String compName = null;
String formulaName = null;
String description = null;
String fileName = null;
String hash = null;
Expand Down Expand Up @@ -77,6 +80,8 @@ public static void main(final String[] args) {
hash = args[++i];
} else if (args[i].equals("--compName")) {
compName = args[++i];
} else if (args[i].equals("--formulaName")) {
formulaName = args[++i];
} else if (args[i].equals("--description")) {
description = args[++i];
} else if (args[i].equals("--type")) {
Expand All @@ -103,6 +108,12 @@ public static void main(final String[] args) {
cmd = "addComponentExternalReference";
} else if (args[i].equals("--addMetadataTools")) {
cmd = "addMetadataTools";
} else if (args[i].equals("--addFormulation")) { // Formulation Component. We can set "name" for Formulation.
cmd = "addFormulation";
} else if (args[i].equals("--addFormulationComp")) { // Formulation Component. We can set "name" for Formulation.
cmd = "addFormulationComp";
} else if (args[i].equals("--addFormulationCompProp")) { // Formulation --> Component --> Property --> name-value
cmd = "addFormulationCompProp";
} else if (args[i].equals("--verbose")) {
verbose = true;
}
Expand All @@ -128,6 +139,20 @@ public static void main(final String[] args) {
writeJSONfile(bom, fileName);
break;

case "addFormulation": // Adds Formulation --> name
bom = addFormulation(fileName, formulaName);
writeJSONfile(bom, fileName);
break;

case "addFormulationComp": // Adds Formulation --> Component--> name
bom = addFormulationComp(fileName, formulaName, name, type);
writeJSONfile(bom, fileName);
break;
case "addFormulationCompProp": // Adds Formulation --> Component -> name-value:
bom = addFormulationCompProp(fileName, formulaName, compName, name, value);
writeJSONfile(bom, fileName);
break;

case "addMetadataTools":
bom = addMetadataTools(fileName, tool, version);
writeJSONfile(bom, fileName);
Expand Down Expand Up @@ -302,9 +327,91 @@ static Bom addComponentExternalReference(final String fileName, final String has
return bom;
}

static Bom addFormulation(final String fileName, final String name) {
Bom bom = readJSONfile(fileName);
List<Formula> formulation = bom.getFormulation();
if (formulation == null) {
formulation = new LinkedList<Formula>();
Formula formula = new Formula();
System.err.println("SXAECW: " + name);
formula.setBomRef(name);
formulation.add(formula);
bom.setFormulation(formulation);
}
return bom;
}

static Bom addFormulationComp(final String fileName, final String formulaName, final String name, final String type) {
Bom bom = readJSONfile(fileName);
if (formulaName == null) {
System.out.println("addFormulationComp: formulaName is null");
return bom;
} else if (name == null) {
System.out.println("addFormulationComp: name is null");
return bom;
}
List<Formula> formulation = bom.getFormulation();
// Look for the formula, and add the new component to it
boolean found = false;
for (Formula item : formulation) {
if (item.getBomRef().equals(formulaName)) {
found = true;
Component comp = new Component();
Component.Type compType = Component.Type.FRAMEWORK;
comp.setType(Component.Type.FRAMEWORK);
comp.setName(name);
List<Component> components = item.getComponents();
if (components == null) {
components = new LinkedList<Component>();
}
components.add(comp);
item.setComponents(components);
}
}
if (!found) {
System.out.println("addFormulationComp could not add component as it couldn't find an entry for formula " + formulaName);
}
return bom;
}

static Bom addFormulationCompProp(final String fileName, final String formulaName, final String componentName, final String name, final String value) {
Bom bom = readJSONfile(fileName);
boolean foundFormula = false;
boolean foundComponent = false;
List<Formula> formulation = bom.getFormulation();
// Look for the formula, and add the new component to it
for (Formula item : formulation) {
if (item.getBomRef().equals(formulaName)) {
foundFormula = true;
// Search for the component in the formula and add new component to it
List<Component> components = item.getComponents();
if (components == null) {
System.out.println("addFormulationCompProp: Components is null - has addFormulationComp been called?");
} else {
for (Component comp : components) {
if (comp.getName().equals(componentName)) {
foundComponent = true;
Property prop1 = new Property();
prop1.setName(name);
prop1.setValue(value);
comp.addProperty(prop1);
item.setComponents(components);
}
}
}
}
}
if (!foundFormula) {
System.out.println("addFormulationCompProp could not add add property as it couldn't find an entry for formula " + formulaName);
} else if (!foundComponent) {
System.out.println("addFormulationCompProp could not add add property as it couldn't find an entry for component " + componentName);
}
return bom;
}

static String generateBomJson(final Bom bom) {
// Use schema v14: https://cyclonedx.org/schema/bom-1.4.schema.json
BomJsonGenerator bomGen = BomGeneratorFactory.createJson(CycloneDxSchema.Version.VERSION_14, bom);
// Use schema v15: https://cyclonedx.org/schema/bom-1.5.schema.json
BomJsonGenerator bomGen = BomGeneratorFactory.createJson(CycloneDxSchema.Version.VERSION_15, bom);
String json = bomGen.toJsonString();
return json;
}
Expand Down
24 changes: 24 additions & 0 deletions sbin/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,10 @@ generateSBoM() {
addSBOMMetadataProperty "${javaHome}" "${classpath}" "${sbomJson}" "OS version" "${BUILD_CONFIG[OS_FULL_VERSION]^}"
addSBOMMetadataProperty "${javaHome}" "${classpath}" "${sbomJson}" "OS architecture" "${BUILD_CONFIG[OS_ARCHITECTURE]^}"

# Set default SBOM formulation
addSBOMFormulation "${javaHome}" "${classpath}" "${sbomJson}" "CycloneDX"
addSBOMFormulationComp "${javaHome}" "${classpath}" "${sbomJson}" "CycloneDX" "CycloneDX jar SHAs"

# Below add build tools into metadata tools
if [ "${BUILD_CONFIG[OS_KERNEL_NAME]}" == "linux" ]; then
addGLIBCforLinux
Expand All @@ -894,6 +898,8 @@ generateSBoM() {
if [ -f "${freemarker_version}" ]; then
addSBOMMetadataTools "${javaHome}" "${classpath}" "${sbomJson}" "FreeMarker" "$(cat ${freemarker_version})"
fi
# Add CycloneDX versions
addCycloneDXVersions

# Add Build Docker image SHA1
local buildimagesha=$(cat ${BUILD_CONFIG[WORKSPACE_DIR]}/${BUILD_CONFIG[TARGET_DIR]}/metadata/docker.txt)
Expand Down Expand Up @@ -1046,6 +1052,24 @@ addFreeTypeVersionInfo() {
addSBOMMetadataTools "${javaHome}" "${classpath}" "${sbomJson}" "FreeType" "${version}"
}

# Determine and store CycloneDX SHAs that have been used to provide the SBOMs
addCycloneDXVersions() {
if [ ! -d "${CYCLONEDB_DIR}/build/jar" ]; then
echo "ERROR: CycloneDX jar directory not found at ${CYCLONEDB_DIR}/build/jar - cannot store checksums in SBOM"
else
# Should we do something special if the sha256sum fails?
for JAR in "${CYCLONEDB_DIR}/build/jar"/*.jar; do
JarName=$(basename "$JAR")
if [ "$(uname)" = "Darwin" ]; then
JarSha=$(shasum -a 256 "${CYCLONEDB_DIR}/build/jar/cyclonedx-core-java.jar" | cut -d' ' -f1)
else
JarSha=$(sha256sum "${CYCLONEDB_DIR}/build/jar/cyclonedx-core-java.jar" | cut -d' ' -f1)
fi
addSBOMFormulationComponentProperty "${javaHome}" "${classpath}" "${sbomJson}" "CycloneDX" "CycloneDX jar SHAs" "${JarName}" "${JarSha}"
done
fi
}

# Below add versions to sbom | Facilitate reproducible builds

addGLIBCforLinux() {
Expand Down
33 changes: 33 additions & 0 deletions sbin/common/sbom.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,39 @@ addSBOMMetadataProperty() {
fi
"${javaHome}"/bin/java -cp "${classpath}" temurin.sbom.TemurinGenSBOM --addMetadataProp --jsonFile "${jsonFile}" --name "${name}" --value "${value}"
}

# Set basic SBoM formulation
addSBOMFormulation() {
local javaHome="${1}"
local classpath="${2}"
local jsonFile="${3}"
local formulaName="${4}"
"${javaHome}"/bin/java -cp "${classpath}" temurin.sbom.TemurinGenSBOM --addFormulation --formulaName "${formulaName}" --jsonFile "${jsonFile}"
}

addSBOMFormulationComp() {
local javaHome="${1}"
local classpath="${2}"
local jsonFile="${3}"
local formulaName="${4}"
local name="${5}"
"${javaHome}"/bin/java -cp "${classpath}" temurin.sbom.TemurinGenSBOM --addFormulationComp --jsonFile "${jsonFile}" --formulaName "${formulaName}" --name "${name}"
}

# Ref: https://cyclonedx.org/docs/1.4/json/#formulation
# Add the given Property name & value to the SBOM Formulation
addSBOMFormulationComponentProperty() {
local javaHome="${1}"
local classpath="${2}"
local jsonFile="${3}"
local formulaName="${4}"
local compName="${5}"
local name="${6}"
local value="${7}"
"${javaHome}"/bin/java -cp "${classpath}" temurin.sbom.TemurinGenSBOM --addFormulationCompProp --jsonFile "${jsonFile}" --formulaName "${formulaName}" --compName "${compName}" --name "${name}" --value "${value}"
}


# Ref: https://cyclonedx.org/docs/1.4/json/#metadata
# If the given property file exists and size over 2bytes, then add the given Property name with the given file contents value to the SBOM Metadata
addSBOMMetadataPropertyFromFile() {
Expand Down

0 comments on commit 380140f

Please sign in to comment.