diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 745da7c..fd8db1a 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -16,3 +16,8 @@ jobs: java-version: '11' - name: Build with Maven run: mvn package --file pom.xml + - name: 'Upload Artifact' + uses: actions/upload-artifact@v3 + with: + name: JAR + path: target/linux-sbom-generator-*.jar diff --git a/pom.xml b/pom.xml index b53f7cb..2ee808d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,13 +5,13 @@ org.cyclonedx.contrib.com.lmco.efoss.unix.sbom linux-sbom-generator - 3.1.0-SNAPSHOT + 3.2.0-SNAPSHOT linux-sbom-generator Unix SBOM Generator https://github.com/CycloneDX/cyclonedx-linux-generator - 5.0.4 + 7.2.0 2.5.3 1.3 diff --git a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/AlpineSBomGenerator.java b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/AlpineSBomGenerator.java index ac375f8..47c1168 100644 --- a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/AlpineSBomGenerator.java +++ b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/AlpineSBomGenerator.java @@ -66,7 +66,7 @@ public Bom generateSBom() detailMap = produceDetailMap(software); version = getVersion(software); component = createComponents(software, detailMap, null, null, - version, null, null); + version, null, null, null); bom.addComponent(addPackageManager(component, PACKAGE_MANAGER)); } return bom; diff --git a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGenerator.java b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGenerator.java index 7327de3..397b4d8 100644 --- a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGenerator.java +++ b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGenerator.java @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2018,2019 Lockheed Martin Corporation. * * This work is owned by Lockheed Martin Corporation. Lockheed Martin personnel are permitted to use and @@ -18,7 +18,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.TreeMap; +import com.github.packageurl.MalformedPackageURLException; +import com.github.packageurl.PackageURL; +import com.google.common.base.Splitter; import org.cyclonedx.model.Bom; import org.cyclonedx.model.Component; import org.cyclonedx.model.LicenseChoice; @@ -28,7 +32,7 @@ /** * * (U) This class is responsible for generating the Software Bill Of Materials (SBOM) for all * Oracle Red Hat Linux Operating Systems. - * + * * @author wrgoff * @since 24 April 2020 */ @@ -37,58 +41,84 @@ public class RedHatSBomGenerator extends UnixSBomGenerator private static final List POSSIBLE_LICENSE_FILES = new ArrayList<>( List.of("LICENSE.txt", "LICENSE", "COPYING", "COPYING.LGPL", "PORTING", "Copying")); - + private static final String PACKAGE_MANAGER = "yum"; - + // Unix Commands. private static final String PURL_CMD = "yumdownloader --urls"; private static final String SOFTWARE_DETAIL_CMD = "yum info"; private static final String SOFTWARE_LIST_CMD = "yum list installed"; - + + private String purlNamespace = null; + private ProcessBuilder processBuilder = new ProcessBuilder(); - + + public void setPurlNamespace(String ns){ + this.purlNamespace = ns; + } + /** * (U) This method is used to generate the Software Bill Of Materials (SBOM) for all RedHat * Linux Operating systems. - * + * * @return Bom The Software Bill Of Materials for this RedHat Linux Operating System. * @throws SBomException if we are unable to build the SBOM. */ - public Bom generateSBom() - { - List softwareList = generateListOfSoftware(SOFTWARE_LIST_CMD, ' ', - "Installed Packages"); - - Bom bom = new Bom(); - - if (logger.isDebugEnabled()) - logger.debug("Processing " + softwareList.size() + " software programs."); - - Map detailMap = null; - String version = null; - String group = null; - LicenseChoice license = null; - String purl = null; - Component component = null; - for (String software : softwareList) - { - if (logger.isDebugEnabled()) - logger.debug("Generating Component (" + software + ")"); - detailMap = produceDetailMap(software); - version = detailMap.get("Version"); - group = detailMap.get("Release"); - license = processLicense(software, version); - purl = getPurl(software); - component = createComponents(software, detailMap, license, group, - version, purl, detailMap.get("Priority")); - bom.addComponent(addPackageManager(component, PACKAGE_MANAGER)); - } - return bom; + public Bom generateSBom() { + List softwareList = generateListOfSoftware(SOFTWARE_LIST_CMD, ' ', + "Installed Packages"); + + Bom bom = new Bom(); + + if (logger.isDebugEnabled()) + logger.debug("Processing " + softwareList.size() + " software programs."); + + Map detailMap = null; + String version = null; + String group = null; + LicenseChoice license = null; + PackageURL purl = null; + Component component = null; + String cpe = null; + for (String software : softwareList) { + if (logger.isDebugEnabled()) + logger.debug("Generating Component (" + software + ")"); + detailMap = produceDetailMap(software); + version = detailMap.get("Version"); + group = detailMap.get("Release"); + license = processLicense(software, version); + try { + purl = getPurl(software, version); + + // TODO: get arch and distro + //purl = getPurl(software,version, arch, distro); + } catch (MalformedPackageURLException e) { + logger.debug("Can't get purl", e); + } + cpe = getCpe(detailMap.get("Name"), version); + + try { + String downloadUrl = getPackageDownloadUrl(software); + if (downloadUrl != null) { + detailMap.put("Download-Url", downloadUrl); + } + } catch(SBomException e){ + logger.debug("Error getting Download-Url", e); + } + component = createComponents(software, detailMap, license, group, + version, purl, detailMap.get("Priority"), cpe); + bom.addComponent(addPackageManager(component, PACKAGE_MANAGER)); + } + return bom; + } + + private String getCpe(String pack, String version) { + return String.format("cpe:2.3:a:%s:%s:%s:*:*:*:*:*:*:*", pack, pack, version); } - + /** * (U) This method is used to attempt to figure out which file is the license file. If any. - * + * * @param software String value of the software we are attempting to get the license file for. * @param version String value of the version of the software we are attempting to get the * license for. @@ -97,19 +127,19 @@ public Bom generateSBom() public String getLicenseFileName(String software, String version) { File tempFile = null; - + software = software.trim(); - + if (software.endsWith(".x86_64")) software = software.replace(".x86_64", ""); - + if (version != null) software = software + "-" + version.trim(); - + if (logger.isDebugEnabled()) logger.debug("Attempting to get license file from " + SOFTWARE_LICENSE_DIR + software + "."); - + for (String fileToTry : POSSIBLE_LICENSE_FILES) { tempFile = new File(SOFTWARE_LICENSE_DIR + software, fileToTry); @@ -118,26 +148,52 @@ public String getLicenseFileName(String software, String version) } return null; } - + + + public PackageURL getPurl(String software, String version) throws MalformedPackageURLException { + List parts = Splitter.on('.').splitToList(software); + + if (parts.size() == 0) { + return this.getPurl(software, version, null, null); + } + if (parts.size() == 1) { + return this.getPurl(parts.get(0), version, null, null); + } else { + return this.getPurl(parts.get(0), version, parts.get(1), null); + } + } + + public PackageURL getPurl(String software, String version, String arch, String distro) throws MalformedPackageURLException { + + TreeMap qualifiers = new TreeMap<>(); + if (arch != null) { + qualifiers.put("arch", arch); + } + if (distro != null) { + qualifiers.put("distro", distro); + } + return new PackageURL( + PackageURL.StandardTypes.RPM, purlNamespace, software, version, qualifiers, null); + } + + /** * (U) This method is used to get the Product Uniform Resource Locator (PURL) or as we know it * the download Uniform Resource Locator (URL). - * + * * @param software String value of the software to get the PURL for. * @return String the URL that will be used to download this software product. * @throws SBomException in the event we are unable to get the PURL from the server. */ - public String getPurl(String software) - { + public String getPackageDownloadUrl(String software){ String purl = null; - String cmd = PURL_CMD + " " + software; - + processBuilder.command("bash", "-c", cmd); - + if (logger.isDebugEnabled()) logger.debug("Attempting to get PURL for " + software + "."); - + try { Process process = processBuilder.start(); @@ -152,10 +208,10 @@ public String getPurl(String software) } return purl; } - + /** * (U) This method is used to parse the PURL from the output of the command process. - * + * * @param process Process that is running the YUM command. * @param software String value of the software, used for debugging purposes. * @return String the PURL if available. @@ -164,15 +220,15 @@ public String getPurl(String software) public String parsePurl(Process process, String software) { String purl = null; - + try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { purl = parsePurlCmdOutput(reader); - + if (purl == null) logger.warn("No PURL found for software package (" + software + ")."); - + int exitVal = process.waitFor(); if (exitVal != 0) { @@ -194,11 +250,11 @@ public String parsePurl(Process process, String software) } return purl; } - + /** * (U) This method is used to parse the Unix Command Output to get the download Uniform Resource * Locator (URL) also known as the Product Uniform Resource Locator (PURL) - * + * * @param reader BufferedReader to parse the output form. * @return String the PURL or download URL that can be used to download the software package. * @throws SBomException in the event we are unable to parse the command's output. @@ -232,11 +288,11 @@ else if (line.startsWith("No Match for argument")) // No PURL found. } return purl; } - + /** * (U) This method is responsible for getting the license (if present) and placing it in the * LicenseChoice Object passed back. - * + * * @param software String value of the software we are attempting to get the license for. * @param version String value of the version of the software we are attempting to get the * license for. @@ -246,18 +302,18 @@ private LicenseChoice processLicense(String software, String version) { if (logger.isDebugEnabled()) logger.debug("Attempting to get license file for " + software + "."); - + LicenseChoice licenseChoice = null; - + String licenseFile = getLicenseFileName(software, version); - + try { if (licenseFile != null) { if (logger.isDebugEnabled()) logger.debug("Attempting to process license (" + licenseFile + ")"); - + String licenseTxt = new String(Files.readAllBytes(Paths.get(licenseFile))); licenseChoice = parseLicenseText(licenseTxt, AVAILABLE_LINUX_FLAVORS.REDHAT); } @@ -270,11 +326,11 @@ private LicenseChoice processLicense(String software, String version) } return licenseChoice; } - + /** * (U) This method is used to produce a Detail Map of the Software in question. This will be * used to create a CycloneDx Component. - * + * * @param software String value of the component to build the detail map for. * @return Map containing the key value pairs about the software. * @throws SBomException in the event we can NOT produce the detail map of the software. @@ -282,7 +338,7 @@ private LicenseChoice processLicense(String software, String version) private Map produceDetailMap(String software) { String cmd = SOFTWARE_DETAIL_CMD + " " + software; - + return (produceDetailMap(cmd, AVAILABLE_LINUX_FLAVORS.REDHAT)); } } diff --git a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/SBomGenerator.java b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/SBomGenerator.java index eb82c80..ec35061 100644 --- a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/SBomGenerator.java +++ b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/SBomGenerator.java @@ -145,13 +145,13 @@ public static void createBomFile(Bom bom, SBomCommons.AVAILABLE_FORMATS format) * imageUrl provided. */ public static Component createMasterComponent(String imageUrl, String name, - String group, String version) throws SBomException + String group, String version, String cpe) throws SBomException { Component master = null; if ((StringUtils.isValid(imageUrl)) || (StringUtils.isValid(name) && ((StringUtils.isValid(version))))) { - master = createMasterComponent(imageUrl); + master = createMasterComponent(imageUrl,name,group,version, imageUrl ,cpe); if (StringUtils.isValid(imageUrl)) { @@ -173,6 +173,11 @@ public static Component createMasterComponent(String imageUrl, String name, logger.error(error, e); throw new SBomException(error); } + }else{ + master.setType(Component.Type.OPERATING_SYSTEM); + if(StringUtils.isValid(cpe)){ + master.setCpe(cpe); + } } if (StringUtils.isValid(name)) master.setName(name.toLowerCase()); @@ -292,6 +297,7 @@ private static Component createMasterComponent(CommandLine cli) throws SBomExcep String name = cli.getOptionValue("name"); String group = cli.getOptionValue("group"); String version = cli.getOptionValue("version"); + String cpe = cli.getOptionValue("cpe"); if ((!StringUtils.isValid(name)) || (!StringUtils.isValid(version))) { @@ -301,9 +307,11 @@ private static Component createMasterComponent(CommandLine cli) throws SBomExcep name = osUtils.getOsVendor(); if (!StringUtils.isValid(version)) version = osUtils.getOsVersion(); + if (!StringUtils.isValid(cpe)) + cpe = osUtils.getOsCpe(); } - master = createMasterComponent(imageUrl, name, group, version); + master = createMasterComponent(imageUrl, name, group, version, cpe); return master; } @@ -312,12 +320,17 @@ private static Component createMasterComponent(CommandLine cli) throws SBomExcep * (U) This method is used to create the master component. It will then fill in * the image information (if provided). * + * + * @param url + * @param name + * @param group + * @param version * @param imageUrl String value of where to get the docker image from. * @return Component created, and filled in if the imageUrl is provided. * @throws SBomException in the event we are unable to pull the image via the * image URL provided. */ - private static Component createMasterComponent(String imageUrl) throws SBomException + private static Component createMasterComponent(String url, String name, String group, String version, String imageUrl, String cpe) throws SBomException { Component master = new Component(); master.setType(org.cyclonedx.model.Component.Type.CONTAINER); diff --git a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UbuntuSBomGenerator.java b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UbuntuSBomGenerator.java index ba935ae..373db80 100644 --- a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UbuntuSBomGenerator.java +++ b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UbuntuSBomGenerator.java @@ -71,7 +71,7 @@ public Bom generateSBom() group = detailMap.get("Release"); license = processLicense(software); component = createComponents(software, detailMap, license, group, - version, null, detailMap.get("Priority")); + version, null, detailMap.get("Priority"), null); bom.addComponent(addPackageManager(component, PACKAGE_MANAGER)); } diff --git a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGenerator.java b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGenerator.java index 6986c41..b6434ab 100644 --- a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGenerator.java +++ b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGenerator.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; +import com.github.packageurl.PackageURL; import org.apache.log4j.Logger; import org.cyclonedx.model.AttachmentText; import org.cyclonedx.model.Component; @@ -41,7 +42,7 @@ public class UnixSBomGenerator { protected enum AVAILABLE_LINUX_FLAVORS { - ALPINE, REDHAT, UBUNTU + ALPINE, REDHAT, UBUNTU, AL2 } protected static final Logger logger = Logger.getLogger(UnixSBomGenerator.class.getName()); @@ -90,6 +91,12 @@ public List buildExternalReferences(Map detai bugs.setType(ExternalReference.Type.ISSUE_TRACKER); refs.add(bugs); } + if (detailMap.containsKey("Download-Url")){ + ExternalReference docs = new ExternalReference(); + docs.setUrl(detailMap.get("Download-Url")); + docs.setType(ExternalReference.Type.DISTRIBUTION); + refs.add(docs); + } return refs; } @@ -122,10 +129,11 @@ else if (priority.equalsIgnoreCase("optional")) * @param version String value to set the version to. * @param purl String to set the URL used to pull the software from. * @param scope String value to help us set the scope of the software. + * @param cpe String value for cpe, if relevant * @return Component Sbom Component created from the supplied inputs. */ public Component createComponents(String software, Map detailMap, - LicenseChoice license, String group, String version, String purl, String scope) + LicenseChoice license, String group, String version, PackageURL purl, String scope, String cpe) { Component component = new Component(); component.setType(Type.OPERATING_SYSTEM); @@ -136,7 +144,12 @@ public Component createComponents(String software, Map detailMap component.setGroup(group); component.setLicenseChoice(license); component.setPublisher(detailMap.get("From repo")); - component.setPurl(purl); + if(purl!=null) { + component.setPurl(purl); + } + if(cpe!=null){ + component.setCpe(cpe); + } component.setScope(buildScope(scope)); component.setVersion(version); diff --git a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtils.java b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtils.java index db11a16..0fd98d0 100644 --- a/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtils.java +++ b/src/main/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtils.java @@ -30,9 +30,9 @@ public class OperatingSystemUtils { private static final Logger logger = Logger.getLogger(OperatingSystemUtils.class.getName()); - + private static final String OS_RELEASE_FILE = "/etc/os-release"; - + private Map osMap = null; /** @@ -113,6 +113,25 @@ public String getOsVersion() return version; } + /** + * (U) This method is used to get the operating system's CPE, if it has one defined. + * + * @return String the operating system CPE. + */ + public String getOsCpe() + { + String cpe = null; + + if (osMap.containsKey("CPE_NAME")) + cpe = osMap.get("CPE_NAME"); + if (StringUtils.isValid(cpe)) + cpe = CharMatcher.is('\"').trimFrom(cpe); + + return cpe; + } + + + /** * (U) This method is used to get the operating system. From the /etc/os-release * file. @@ -133,12 +152,14 @@ public Map getOs() catch (IOException ioe) { String error = "Unable to read file(" + OS_RELEASE_FILE + ") to get the " + - "operating sytem!"; + "operating system!"; logger.error(error, ioe); throw new SBomException(error, ioe); } return detailMap; } + + /** * (U) This method is used to read the contents of the OS file. @@ -146,7 +167,7 @@ public Map getOs() * @param content String value read from the file. * @return Map containing the information about the Operating system. */ - public Map readOs(String content) + public static Map readOs(String content) { Map detailMap = new HashMap<>(); diff --git a/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGeneratorTest.java b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGeneratorTest.java index a002a53..2bf8c94 100644 --- a/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGeneratorTest.java +++ b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/RedHatSBomGeneratorTest.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; +import com.github.packageurl.PackageURL; import org.cyclonedx.model.ExternalReference; import org.junit.Assert; import org.junit.jupiter.api.Test; @@ -116,9 +117,10 @@ void getPurlTest() Date startDate = DateUtils.rightNowDate(); TestUtils.logTestStart(methodName, watcher.getLogger()); - - String expected = "https://rhui3.us-west-2.aws.ce.redhat.com/pulp/repos/content/dist/rhel/rhui/server/7/7Server/x86_64/os/Packages/z/zip-3.0-11.el7.x86_64.rpm"; - + + String expectedUrl = "https://rhui3.us-west-2.aws.ce.redhat.com/pulp/repos/content/dist/rhel/rhui/server/7/7Server/x86_64/os/Packages/z/zip-3.0-11.el7.x86_64.rpm"; + String expected = "pkg:rpm/zip@3.0-11.el7?arch=x64_86"; + AutoCloseable openMocks = null; String fileName = "/purl/redhatPurl.txt"; @@ -131,12 +133,12 @@ void getPurlTest() Mockito.when(pbMock.start()).thenReturn(process); - String version = generator.getPurl("zip"); + PackageURL purl = generator.getPurl("zip.x64_86", "3.0-11.el7"); - if (expected.equalsIgnoreCase(version)) - watcher.getLogger().debug("Got expected version (" + version + ")"); + if (expected.equalsIgnoreCase(purl.toString())) + watcher.getLogger().debug("Got expected version (" + purl + ")"); else - Assert.assertEquals(expected, version); + Assert.assertEquals(expected, purl.toString()); } catch (Exception e) { diff --git a/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGeneratorTest.java b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGeneratorTest.java index 73a0914..09688d1 100644 --- a/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGeneratorTest.java +++ b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/generator/UnixSBomGeneratorTest.java @@ -137,7 +137,7 @@ void createComponentsTest() String version = detailMap.get("Version"); String group = detailMap.get("Release"); Component component = generator.createComponents("zip", - detailMap, null, group, version, null, detailMap.get("Priority")); + detailMap, null, group, version, null, detailMap.get("Priority"), null); String actualComponentName = component.getName(); String actualComponentVerison = component.getVersion(); diff --git a/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtilsTest.java b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtilsTest.java index a7b5fd8..8385cc3 100644 --- a/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtilsTest.java +++ b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/OperatingSystemUtilsTest.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Date; +import java.util.HashMap; import java.util.Map; import org.apache.commons.io.IOUtils; @@ -569,6 +570,49 @@ void testOsVersion(String file, String expectedOsVersion) } } + /** + * (U) Convenience method used to test the reading of the CPE from the + * "/etc/os-release" file. + * + * @param file String value of the contents of the + * "/etc/os-release" file. + * @param expectedCpe String value of the expected CPE. + */ + void testCpe(String file, String expectedCpe) + { + try (InputStream inputStream = OperatingSystemUtilsTest.class.getResourceAsStream(file)) + { + String osReleaseFileContents = IOUtils.toString(inputStream); + + OperatingSystemUtils osUtils = new OperatingSystemUtils(osReleaseFileContents); + + String actualCpe = osUtils.getOsCpe(); + + if (expectedCpe.equalsIgnoreCase(actualCpe)) + watcher.getLogger().debug("Got expected CPE (" + + expectedCpe + ")."); + else + watcher.getLogger().debug("Did NOT get expected CPE!\n" + + " Expected: " + expectedCpe + "\n" + + " Acutal: " + actualCpe); + + Assert.assertEquals(expectedCpe, actualCpe); + } + catch (IOException ioe) + { + String error = "Our test case failed to read the operating system " + + "etc/os-release file(" + file + ")."; + watcher.getLogger().error(error, ioe); + Assert.fail("Unable to read /etc/os-release File (" + file + "). "); + } + catch (Exception e) + { + String error = "Our test case failed unexpectedly."; + watcher.getLogger().error(error, e); + Assert.fail(error); + } + } + /** * (U) This method is used to test the parsing of the Ubuntu Os File. */ @@ -589,12 +633,12 @@ void testReadOsUbuntu() { try (InputStream inputStream = OperatingSystemUtilsTest.class.getResourceAsStream(file)) { - String theString = IOUtils.toString(inputStream); - - OperatingSystemUtils osUtils = new OperatingSystemUtils(); - Map osMap = osUtils.readOs(theString); + String theString = IOUtils.toString(inputStream); + + var opts = readOs(theString); + var osUtils = new TestOperatingSystemUtil("Ubuntu", opts); - String osVendor = osMap.get("NAME"); + String osVendor = opts.get("NAME"); osVendor = CharMatcher.is('\"').trimFrom(osVendor); if (expected.equalsIgnoreCase(osVendor)) @@ -623,4 +667,66 @@ void testReadOsUbuntu() TestUtils.logTestFinish(methodName, startDate, watcher.getLogger()); } } + + /** + * (U) This method is used to test the getting of the OS name from the + * os-release file. For Redhat. + */ + @Test + void getOsCpeRedhat() + { + // @formatter:off + String methodName = new Object(){}.getClass().getEnclosingMethod().getName(); + // @formatter:on + + Date startDate = DateUtils.rightNowDate(); + + TestUtils.logTestStart(methodName, watcher.getLogger()); + + String file = "/osReleaseFiles/redhat-os-release.txt"; + String expectedCpe = "cpe:/o:redhat:enterprise_linux:8.1:GA"; + try + { + testCpe(file, expectedCpe); + } + finally + { + TestUtils.logTestFinish(methodName, startDate, watcher.getLogger()); + } + } + + private Map readOs(String content) + { + Map detailMap = new HashMap<>(); + + String[] contents = content.split("\n"); + String componentDetailName = null; + StringBuilder componentDetailValue = new StringBuilder(); + int index = 0; + + for (String line : contents) + { + if (line.startsWith(" ")) + { + componentDetailValue.append(line); + } + else + { + if (componentDetailName != null) + { + detailMap.put(componentDetailName, componentDetailValue.toString()); + componentDetailName = null; + componentDetailValue = new StringBuilder(); + } + + if (line.contains("=")) + { + index = line.indexOf('='); + componentDetailName = line.substring(0, index); + componentDetailValue.append(line.substring(index + 1).trim()); + } + } + } + return detailMap; + } } diff --git a/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/TestOperatingSystemUtil.java b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/TestOperatingSystemUtil.java new file mode 100644 index 0000000..0ebfbdd --- /dev/null +++ b/src/test/java/org/cyclonedx/contrib/com/lmco/efoss/unix/sbom/utils/TestOperatingSystemUtil.java @@ -0,0 +1,12 @@ +package org.cyclonedx.contrib.com.lmco.efoss.unix.sbom.utils; + +import java.util.Map; + +public class TestOperatingSystemUtil { + private Map osMap = null; + + public TestOperatingSystemUtil(String name, Map testMap) + { + osMap = testMap; + } +}