diff --git a/src/main/java/org/cyclonedx/model/Dependency.java b/src/main/java/org/cyclonedx/model/Dependency.java
index 125d18b4e..eb3e4343a 100644
--- a/src/main/java/org/cyclonedx/model/Dependency.java
+++ b/src/main/java/org/cyclonedx/model/Dependency.java
@@ -18,6 +18,7 @@
*/
package org.cyclonedx.model;
+import org.cyclonedx.Version;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -36,6 +37,12 @@ public class Dependency extends BomReference {
@JacksonXmlProperty(localName = "dependency")
private List dependencies;
+ @VersionFilter(Version.VERSION_16)
+ @JsonProperty("provides")
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "provides")
+ private List provides;
+
public Dependency(final String ref) {
super(ref);
}
@@ -60,6 +67,23 @@ public void addDependency(final Dependency dependency) {
}
}
+ public List getProvides() {
+ return provides;
+ }
+
+ public void setProvides(final List provides) {
+ this.provides = provides;
+ }
+
+ public void addProvides(final BomReference dependency) {
+ if (provides == null) {
+ provides = new ArrayList<>();
+ }
+ boolean found = provides.stream().anyMatch(d -> d.getRef().equals(dependency.getRef()));
+ if (!found) {
+ provides.add(dependency);
+ }
+ }
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/src/main/java/org/cyclonedx/util/serializer/DependencySerializer.java b/src/main/java/org/cyclonedx/util/serializer/DependencySerializer.java
index c4af1de98..06c6c907a 100644
--- a/src/main/java/org/cyclonedx/util/serializer/DependencySerializer.java
+++ b/src/main/java/org/cyclonedx/util/serializer/DependencySerializer.java
@@ -34,6 +34,7 @@
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.cyclonedx.CycloneDxSchema;
+import org.cyclonedx.model.BomReference;
import org.cyclonedx.model.Dependency;
import org.cyclonedx.model.DependencyList;
@@ -97,6 +98,13 @@ private void writeJSONDependenciesWithGenerator(final JsonGenerator generator, f
}
}
generator.writeEndArray();
+ if (CollectionUtils.isNotEmpty(dependency.getProvides())) {
+ generator.writeArrayFieldStart("provides");
+ for (BomReference subDependency : dependency.getProvides()) {
+ generator.writeString(subDependency.getRef());
+ }
+ generator.writeEndArray();
+ }
generator.writeEndObject();
}
}
@@ -134,6 +142,11 @@ private void writeXMLDependency(final Dependency dependency, final ToXmlGenerato
generator.writeString(dependency.getRef());
generator.setNextIsAttribute(false);
+ // Write provides
+ if (CollectionUtils.isNotEmpty(dependency.getProvides())) {
+ writeXMLProvides(dependency, generator);
+ }
+
if (CollectionUtils.isNotEmpty(dependency.getDependencies())) {
for (Dependency subDependency : dependency.getDependencies()) {
// You got Shay'd
@@ -142,12 +155,31 @@ private void writeXMLDependency(final Dependency dependency, final ToXmlGenerato
}
if (CollectionUtils.isNotEmpty(dependency.getDependencies())) {
- generator.writeEndArray();
- }
+ generator.writeEndArray();
+ }
generator.writeEndObject();
}
+ private void writeXMLProvides(final Dependency dependency, final ToXmlGenerator generator)
+ throws IOException, XMLStreamException
+ {
+ QName qName = new QName("provides");
+ generator.setNextName(qName);
+ generator.writeFieldName(qName.getLocalPart());
+ generator.writeStartArray();
+
+ for (BomReference ref : dependency.getProvides()) {
+ generator.writeStartObject();
+ generator.setNextIsAttribute(true);
+ generator.writeFieldName("ref");
+ generator.writeString(ref.getRef());
+ generator.setNextIsAttribute(false);
+ generator.writeEndObject();
+ }
+ generator.writeEndArray();
+ }
+
private void processNamespace(final ToXmlGenerator toXmlGenerator, final String dependencies)
throws XMLStreamException, IOException
{
diff --git a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java
index 6a162c9f6..dddb9c91c 100644
--- a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java
+++ b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java
@@ -20,23 +20,20 @@
import com.fasterxml.jackson.databind.JsonNode;
-import java.io.FileReader;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
-import org.cyclonedx.exception.GeneratorException;
import org.cyclonedx.generators.BomGeneratorFactory;
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.Dependency;
import org.cyclonedx.model.Component.Type;
import org.cyclonedx.model.License;
import org.cyclonedx.model.LicenseChoice;
import org.cyclonedx.model.Metadata;
-import org.cyclonedx.model.OrganizationalContact;
import org.cyclonedx.model.Service;
import org.cyclonedx.model.license.Expression;
-import org.cyclonedx.model.metadata.ToolInformation;
import org.cyclonedx.parsers.JsonParser;
import org.cyclonedx.parsers.XmlParser;
import org.junit.jupiter.api.AfterEach;
@@ -52,10 +49,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.UUID;
import java.util.stream.Stream;
import java.util.Objects;
@@ -336,6 +329,72 @@ public void schema16_testAttestations_json() throws Exception {
assertTrue(parser.isValid(loadedFile, version));
}
+ @Test
+ public void schema16_testDependencyProvides_json() throws Exception {
+ Version version = Version.VERSION_16;
+ Bom bom = createCommonJsonBom("/1.6/valid-dependency-provides-1.6.json");
+
+ BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom);
+ File loadedFile = writeToFile(generator.toJsonString());
+
+ JsonParser parser = new JsonParser();
+ assertTrue(parser.isValid(loadedFile, version));
+
+ Bom parsedBom = parser.parse(loadedFile);
+ assertNotNull(parsedBom.getDependencies());
+ assertEquals(3, parsedBom.getDependencies().size());
+ // Test dependency library-a
+ Dependency libA = parsedBom.getDependencies().get(0);
+ assertEquals("library-a", libA.getRef());
+ assertNotNull(libA.getDependencies());
+ assertEquals(0, libA.getDependencies().size());
+ assertNull(libA.getProvides());
+ // Test dependency library-b
+ Dependency libB = parsedBom.getDependencies().get(1);
+ assertEquals("library-b", libB.getRef());
+ assertEquals(1, libB.getDependencies().size());
+ assertEquals("library-c", libB.getDependencies().get(0).getRef());
+ // Test dependency library-c
+ Dependency libC = parsedBom.getDependencies().get(2);
+ assertEquals("library-c", libC.getRef());
+ assertNotNull(libC.getDependencies());
+ assertNotNull(libC.getProvides());
+ assertEquals("library-d", libC.getProvides().get(0).getRef());
+ }
+
+ @Test
+ public void schema16_testDependencyProvides() throws Exception {
+ Version version = Version.VERSION_16;
+ Bom bom = createCommonXmlBom("/1.6/valid-dependency-provides-1.6.xml");
+
+ BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom);
+ File loadedFile = writeToFile(generator.toJsonString());
+
+ JsonParser parser = new JsonParser();
+ assertTrue(parser.isValid(loadedFile, version));
+
+ Bom parsedBom = parser.parse(loadedFile);
+ assertNotNull(parsedBom.getDependencies());
+ assertEquals(3, parsedBom.getDependencies().size());
+ // Test dependency library-a
+ Dependency libA = parsedBom.getDependencies().get(0);
+ assertEquals("library-a", libA.getRef());
+ assertNotNull(libA.getDependencies());
+ assertEquals(0, libA.getDependencies().size());
+ assertNull(libA.getProvides());
+ // Test dependency library-b
+ Dependency libB = parsedBom.getDependencies().get(1);
+ assertEquals("library-b", libB.getRef());
+ assertEquals(1, libB.getDependencies().size());
+ assertEquals("library-c", libB.getDependencies().get(0).getRef());
+ // Test dependency library-c
+ Dependency libC = parsedBom.getDependencies().get(2);
+ assertEquals("library-c", libC.getRef());
+ assertNotNull(libC.getDependencies());
+ assertNotNull(libC.getProvides());
+ assertEquals("library-d", libC.getProvides().get(0).getRef());
+ }
+
@Test
public void schema16_testCompositions() throws Exception {
Version version = Version.VERSION_16;
diff --git a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java
index bf041845b..63ec017d1 100644
--- a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java
+++ b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java
@@ -28,6 +28,7 @@
import org.cyclonedx.model.Bom;
import org.cyclonedx.model.Component;
import org.cyclonedx.model.Component.Type;
+import org.cyclonedx.model.Dependency;
import org.cyclonedx.model.ExtensibleType;
import org.cyclonedx.model.ExternalReference;
import org.cyclonedx.model.License;
@@ -454,6 +455,70 @@ public void schema16_testAttestations_xml() throws Exception {
assertTrue(parser.isValid(loadedFile, version));
}
+ @Test
+ public void schema16_testDependencyProvides() throws Exception {
+ Version version = Version.VERSION_16;
+ Bom bom = createCommonJsonBom("/1.6/valid-dependency-provides-1.6.json");
+
+ BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom);
+ File loadedFile = writeToFile(generator.toXmlString());
+
+ XmlParser parser = new XmlParser();
+ assertTrue(parser.isValid(loadedFile, version));
+
+ Bom parsedBom = parser.parse(loadedFile);
+ assertNotNull(parsedBom.getDependencies());
+ assertEquals(3, parsedBom.getDependencies().size());
+ // Test dependency library-a
+ Dependency libA = parsedBom.getDependencies().get(0);
+ assertEquals("library-a", libA.getRef());
+ assertNull(libA.getDependencies());
+ assertNull(libA.getProvides());
+ // Test dependency library-b
+ Dependency libB = parsedBom.getDependencies().get(1);
+ assertEquals("library-b", libB.getRef());
+ assertEquals(1, libB.getDependencies().size());
+ assertEquals("library-c", libB.getDependencies().get(0).getRef());
+ // Test dependency library-c
+ Dependency libC = parsedBom.getDependencies().get(2);
+ assertEquals("library-c", libC.getRef());
+ assertNull(libC.getDependencies());
+ assertNotNull(libC.getProvides());
+ assertEquals("library-d", libC.getProvides().get(0).getRef());
+ }
+
+ @Test
+ public void schema16_testDependencyProvides_xml() throws Exception {
+ Version version = Version.VERSION_16;
+ Bom bom = createCommonBomXml("/1.6/valid-dependency-provides-1.6.xml");
+
+ BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom);
+ File loadedFile = writeToFile(generator.toXmlString());
+
+ XmlParser parser = new XmlParser();
+ assertTrue(parser.isValid(loadedFile, version));
+
+ Bom parsedBom = parser.parse(loadedFile);
+ assertNotNull(parsedBom.getDependencies());
+ assertEquals(3, parsedBom.getDependencies().size());
+ // Test dependency library-a
+ Dependency libA = parsedBom.getDependencies().get(0);
+ assertEquals("library-a", libA.getRef());
+ assertNull(libA.getDependencies());
+ assertNull(libA.getProvides());
+ // Test dependency library-b
+ Dependency libB = parsedBom.getDependencies().get(1);
+ assertEquals("library-b", libB.getRef());
+ assertEquals(1, libB.getDependencies().size());
+ assertEquals("library-c", libB.getDependencies().get(0).getRef());
+ // Test dependency library-c
+ Dependency libC = parsedBom.getDependencies().get(2);
+ assertEquals("library-c", libC.getRef());
+ assertNull(libC.getDependencies());
+ assertNotNull(libC.getProvides());
+ assertEquals("library-d", libC.getProvides().get(0).getRef());
+ }
+
private void addSignature(Bom bom) {
List attributes = new ArrayList<>();
attributes.add(new Attribute("xmlns", "http://www.w3.org/2000/09/xmldsig#"));
diff --git a/src/test/resources/1.6/valid-dependency-1.6.json b/src/test/resources/1.6/valid-dependency-1.6.json
index 1e87f38ef..6e0cf61a4 100644
--- a/src/test/resources/1.6/valid-dependency-1.6.json
+++ b/src/test/resources/1.6/valid-dependency-1.6.json
@@ -36,4 +36,4 @@
]
}
]
-}
+}
\ No newline at end of file
diff --git a/src/test/resources/1.6/valid-dependency-1.6.textproto b/src/test/resources/1.6/valid-dependency-1.6.textproto
index 363dfba93..a06cfd0f2 100644
--- a/src/test/resources/1.6/valid-dependency-1.6.textproto
+++ b/src/test/resources/1.6/valid-dependency-1.6.textproto
@@ -30,4 +30,4 @@ dependencies {
dependencies {
ref: "library-c"
}
-}
+}
\ No newline at end of file
diff --git a/src/test/resources/1.6/valid-dependency-1.6.xml b/src/test/resources/1.6/valid-dependency-1.6.xml
index 7fab83476..d49d1df0f 100644
--- a/src/test/resources/1.6/valid-dependency-1.6.xml
+++ b/src/test/resources/1.6/valid-dependency-1.6.xml
@@ -20,4 +20,4 @@
-
+
\ No newline at end of file
diff --git a/src/test/resources/1.6/valid-dependency-provides-1.6.json b/src/test/resources/1.6/valid-dependency-provides-1.6.json
new file mode 100644
index 000000000..289cf8b5b
--- /dev/null
+++ b/src/test/resources/1.6/valid-dependency-provides-1.6.json
@@ -0,0 +1,51 @@
+{
+ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
+ "bomFormat": "CycloneDX",
+ "specVersion": "1.6",
+ "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
+ "version": 1,
+ "components": [
+ {
+ "bom-ref": "library-a",
+ "type": "library",
+ "name": "library-a",
+ "version": "1.0.0"
+ },
+ {
+ "bom-ref": "library-b",
+ "type": "library",
+ "name": "library-b",
+ "version": "1.0.0"
+ },
+ {
+ "bom-ref": "library-c",
+ "type": "library",
+ "name": "library-c",
+ "version": "1.0.0"
+ },
+ {
+ "bom-ref": "library-d",
+ "type": "library",
+ "name": "library-d",
+ "version": "1.0.0"
+ }
+ ],
+ "dependencies": [
+ {
+ "ref": "library-a",
+ "dependsOn": []
+ },
+ {
+ "ref": "library-b",
+ "dependsOn": [
+ "library-c"
+ ]
+ },
+ {
+ "ref": "library-c",
+ "provides": [
+ "library-d"
+ ]
+ }
+ ]
+}
diff --git a/src/test/resources/1.6/valid-dependency-provides-1.6.textproto b/src/test/resources/1.6/valid-dependency-provides-1.6.textproto
new file mode 100644
index 000000000..a636b12af
--- /dev/null
+++ b/src/test/resources/1.6/valid-dependency-provides-1.6.textproto
@@ -0,0 +1,44 @@
+# proto-file: schema/bom-1.6.proto
+# proto-message: Bom
+
+spec_version: "1.6"
+version: 1
+serial_number: "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79"
+components {
+ type: CLASSIFICATION_LIBRARY
+ bom_ref: "library-a"
+ name: "library-a"
+ version: "1.0.0"
+}
+components {
+ type: CLASSIFICATION_LIBRARY
+ bom_ref: "library-b"
+ name: "library-b"
+ version: "1.0.0"
+}
+components {
+ type: CLASSIFICATION_LIBRARY
+ bom_ref: "library-c"
+ name: "library-c"
+ version: "1.0.0"
+}
+components {
+ type: CLASSIFICATION_LIBRARY
+ bom_ref: "library-d"
+ name: "library-d"
+ version: "1.0.0"
+}
+dependencies {
+ ref: "library-a"
+}
+dependencies {
+ ref: "library-b"
+ dependencies {
+ ref: "library-c"
+ }
+}
+dependencies {
+ ref: "library-c"
+ provides: ["library-d"]
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/1.6/valid-dependency-provides-1.6.xml b/src/test/resources/1.6/valid-dependency-provides-1.6.xml
new file mode 100644
index 000000000..9cdd44fa9
--- /dev/null
+++ b/src/test/resources/1.6/valid-dependency-provides-1.6.xml
@@ -0,0 +1,30 @@
+
+
+
+
+ library-a
+ 1.0.0
+
+
+ library-b
+ 1.0.0
+
+
+ library-c
+ 1.0.0
+
+
+ library-d
+ 1.0.0
+
+
+
+
+
+
+
+
+
+
+
+