Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture CycloneDX JAR SHAs in SBoM #3538

Merged
merged 18 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading