This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ant-antlibs-cyclonedx.git
The following commit(s) were added to refs/heads/main by this push:
new ac4767a document the simple part of component and add some tests
ac4767a is described below
commit ac4767a2e11def1a2602c3280b03a93f6ab623f6
Author: Stefan Bodewig <[email protected]>
AuthorDate: Thu May 14 21:54:04 2026 +0200
document the simple part of component and add some tests
---
docs/component.html | 311 ++++++++++++++++++++
docs/externalreferenceset.html | 2 +-
docs/organization.html | 2 +-
src/main/org/apache/ant/cyclonedx/Component.java | 317 +++++++++++++++------
.../org/apache/ant/cyclonedx/ComponentBomTask.java | 2 +-
.../org/apache/ant/cyclonedx/Organization.java | 4 +-
src/tests/antunit/component-test.xml | 273 ++++++++++++++++++
src/tests/antunit/componentbom-test.xml | 97 -------
8 files changed, 823 insertions(+), 185 deletions(-)
diff --git a/docs/component.html b/docs/component.html
new file mode 100644
index 0000000..e64e743
--- /dev/null
+++ b/docs/component.html
@@ -0,0 +1,311 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+ <head>
+ <meta http-equiv="Content-Language" content="en-us"></meta>
+ <link rel="stylesheet" type="text/css" href="style.css">
+ <title>Apache CycloneDX Ant Library - component</title>
+ </head>
+
+ <body>
+ <h2 id="component">component</h2>
+
+ <p>Components are central to CycloneDX SBOMs. There is <b>the</b>
+ component the whole SBOM is about - and there are more
+ components specified inside the SBOM as (transitive)
+ dependencies or parts of the other components.</p>
+
+ <p>The component elements can be used as top-level elements and be
+ given an id so they can be later referred to via
+ the <code>refid</code> attribute -
+ see <a href="https://ant.apache.org/manual/using.html#references">the
+ Ant manual</a>. The can also be referred to by
+ a <a href="#dependency">dependency</a> of another component
+ via <code>componentRef</code>.</p>
+
+ <h3>Attributes</h3>
+
+ <table class="attr">
+ <tr>
+ <th scope="col">Attribute</th>
+ <th scope="col">Description</th>
+ <th scope="col">Required</th>
+ </tr>
+ <tr>
+ <td>name</td>
+ <td>The name of the component.</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <td>type</td>
+ <td>The type of the component. Valid types are defined by the
+ <a
href="https://cyclonedx.org/docs/1.7/json/#metadata_tools_oneOf_i0_components_items_type">CycloneDX
+ specification</a>.</td>
+ <td>No, the default is "library"</td>
+ </tr>
+ <tr>
+ <td>group</td>
+ <td>The group of the component.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>version</td>
+ <td>The version of the component.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>purl</td>
+ <td>The <a href="https://github.com/package-url/purl-spec">Package-URL
+ (purl)</a> of the component.</td>
+ <td>No - if not set but <code>name</code>, <code>group</code>
+ and <code>version</code> are present an implicit purl is
+ calculated as <code>pkg:maven</code> purl from these
+ values.</td>
+ </tr>
+ <tr>
+ <td>bomRef</td>
+ <td>The bom-ref of the component.</td>
+ <td>No - if not set, the component's - potentially calculated
+ - purl will be used.</td>
+ </tr>
+ <tr>
+ <td>scope</td>
+ <td>The scope of the component. Valid scopes are defined by the
+ <a
href="https://cyclonedx.org/docs/1.7/json/#metadata_tools_oneOf_i0_components_items_scope">CycloneDX
+ specification</a>.</td>
+ <td>No, <code>scope</code> is prohibited for the main
+ component of the SBOM.</td>
+ </tr>
+ <tr>
+ <td>isExternal</td>
+ <td>Whether the component is external. The CycloneDX
+ Specification says:
+ <cite>An external component is one that is not part of an
+ assembly, but is expected to be provided by the
+ environment, regardless of the component's
+ scope.</cite><br/>
+ This attribute doesn't have any effect as long as CycloneDX
+ Core doesn't support version 1.7 of the specification.
+ </td>
+ <td>No, <code>isExternal</code> must not be <code>true</code>
+ for the main component of the SBOM.</td>
+ </tr>
+ <tr>
+ <td>description</td>
+ <td>The description of the component.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>publisher</td>
+ <td>The publisher of the component.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>copyright</td>
+ <td>The copyright of the component.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>mimetype</td>
+ <td>The MIME-type of the component.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>manufacturerIsSupplier</td>
+ <td>Whether the
+ nested <a href="#manufacturer">manufacturer</a> shall be used
+ as supplier as well.</td>
+ <td>No - defaults to <code>false</code>. Must not
+ be <code>true</code> if a
+ nested <a href="#supplier">supplier</a> exists.</td>
+ </tr>
+ <tr>
+ <td>unknownDependencies</td>
+ <td>Whether the dependencies of this component are unknown. If
+ set to <code>false</code> then a component without any
+ nested
+ <a href="#dependency">dependency</a> elements is treated as
+ having no dependencies at all. </td>
+ <td>No - defaults to <code>false</code>.</td>
+ </tr>
+ </table>
+
+ <h3>Nested elements</h3>
+
+ <h4>any
+ file-system <a
href="https://ant.apache.org/manual/Types/resources.html">resource</a></h4>
+
+ <p>At most one nested resource specifies the file the component
+ describes. This is required if you want to include hashes for
+ the component in your SBOM.</p>
+
+ <h4 id="manufacturer">manufacturer</h4>
+
+ <p>At most one nested <a href="organization.html">organization</a>
+ specifies the manufacturer of the component.</p>
+
+ <h4 id="supplier">supplier</h4>
+
+ <p>At most one nested <a href="organization.html">organization</a>
+ specifies the supplier of the component.</p>
+
+ <h4>author</h4>
+
+ <p>Adds an author to the component.</p>
+
+ <h5>Attributes</h5>
+
+ <table class="attr">
+ <tr>
+ <th scope="col">Attribute</th>
+ <th scope="col">Description</th>
+ <th scope="col">Required</th>
+ </tr>
+ <tr>
+ <td>bomRef</td>
+ <td>The bom-ref of the author.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>name</td>
+ <td>The name of the author.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>email</td>
+ <td>The email of the author.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>phone</td>
+ <td>The phone of the author.</td>
+ <td>No</td>
+ </tr>
+ </table>
+
+ <h4>tag</h4>
+
+ <p>Adds a tag to the component. Tags haven't got any attributes or
+ nested elements, the nested text is used as tag.</p>
+
+ <h4>property</h4>
+
+ <p>Adds a property to the component.</p>
+
+ <h5>Attributes</h5>
+
+ <table class="attr">
+ <tr>
+ <th scope="col">Attribute</th>
+ <th scope="col">Description</th>
+ <th scope="col">Required</th>
+ </tr>
+ <tr>
+ <td>name</td>
+ <td>The name of the property.</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <td>value</td>
+ <td>The value of the property.</td>
+ <td>No</td>
+ </tr>
+ </table>
+
+ <h4>license</h4>
+
+ <p>A nested <a href="license.html">license</a> specifies the
+ license information of the component.</p>
+
+ <h4>externalReference</h4>
+
+ <p>A
+ nested <a
href="externalreferenceset.html#externalReference">externalReference</a>
+ specifies an external reference for the component.</p>
+
+ <h4>externalReferenceSet</h4>
+
+ <p>A
+ nested <a href="externalreferenceset.html">externalreferenceset</a>
+ specifies external references for the component.</p>
+
+ <h4 id="dependency">dependency</h4>
+
+ <p>Adds a dependency to the component.</p>
+
+ <h5>Attributes</h5>
+
+ <table class="attr">
+ <tr>
+ <th scope="col">Attribute</th>
+ <th scope="col">Description</th>
+ <th scope="col">Required</th>
+ </tr>
+ <tr>
+ <td>bomRef</td>
+ <td>References the dependency by
+ its <code>bom-ref</code>.</td>
+ <td rowspan="2">Exactly one of the two.</td>
+ </tr>
+ <tr>
+ <td>componentRef</td>
+ <td>References the dependency by its Ant <code>id</code>
+ attribute. The referenced component must have
+ a <code>bom-ref</code>.</td>
+ </tr>
+ </table>
+
+ <h3>Examples</h3>
+
+ <p>Below is a component that could describe this Antlib.</p>
+
+ <pre>
+ <cdx:component
+ name="ant"
+ group="org.apache.ant"
+ version="1.10.17"
+ isExternal="true"
+ unknownDependencies="true"
+ id="ant"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <manufacturer refid="ant-team"/>
+ <license refid="apache-2"/>
+ <externalReference
+ type="VCS"
+ url="https://github.com/apache/ant"/>
+ <externalReference
+ type="WEBSITE"
+ url="https://ant.apache.org/"/>
+ </cdx:component>
+ <cdx:component
+ name="ant-cyclonedx"
+ group="org.apache.ant"
+ version="0.1"
+ description="Apache CycloneDX Antlib"
+ publisher="The Apache Software Foundation"
+ manufacturerIsSupplier="true"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <file file="ant-antlibs-cyclonedx-0.1.jar"/>
+ <manufacturer refid="ant-team"/>
+ <license refid="apache-2"/>
+ <externalReferenceSet refid="antlib-ext-refs"/>
+ <dependency componentRef="ant"/>
+ <dependency
bomRef="pkg:maven/org.cyclonedx/[email protected]?type=jar"/>
+ </cdx:component>
+ </pre>
+ </body>
+</html>
diff --git a/docs/externalreferenceset.html b/docs/externalreferenceset.html
index ed51828..a8352dc 100644
--- a/docs/externalreferenceset.html
+++ b/docs/externalreferenceset.html
@@ -37,7 +37,7 @@ <h2 id="externalreferenceset">externalreferenceset</h2>
<h3>Nested elements</h3>
- <h4>externalReference</h4>
+ <h4 id="externalReference">externalReference</h4>
<p>Represents a single external reference to be added to the
BOM.</p>
diff --git a/docs/organization.html b/docs/organization.html
index 2355747..d3fbf2e 100644
--- a/docs/organization.html
+++ b/docs/organization.html
@@ -25,7 +25,7 @@
<h2 id="organization">organization</h2>
<p>Organizations can be attached to components as well as the SBOM
- itself using several roles (manufacturer, supplier, publisher)
+ itself using several roles (manufacturer, supplier, ...)
in CycloneDX SBOMs.</p>
<p>The organization elements can be used as top-level elements and
diff --git a/src/main/org/apache/ant/cyclonedx/Component.java
b/src/main/org/apache/ant/cyclonedx/Component.java
index e44b0c8..6b06df2 100644
--- a/src/main/org/apache/ant/cyclonedx/Component.java
+++ b/src/main/org/apache/ant/cyclonedx/Component.java
@@ -14,8 +14,8 @@ import java.util.stream.Collectors;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
-import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileProvider;
import org.apache.tools.ant.types.resources.Union;
@@ -32,6 +32,17 @@ import org.cyclonedx.parsers.BomParserFactory;
import org.cyclonedx.parsers.Parser;
import org.cyclonedx.util.BomUtils;
+/**
+ * The central concept of an SBOM which describes components and their
dependencies.
+ *
+ * <p>The CycloneDX specification supports more information for a
+ * component than this type currently exposes.</p>
+ *
+ * <p>This class is a type exposed by this Ant Library. When using the
+ * inherited {@code refid} attribute it can reference an instance
+ * defined previously - in which case no child elements or other
+ * attributes are allowed.</p>
+ */
public class Component extends DataType {
private Resource resource;
private org.cyclonedx.model.Component.Type type =
org.cyclonedx.model.Component.Type.LIBRARY;
@@ -60,6 +71,13 @@ public class Component extends DataType {
private String mimeType;
private Union sbomLink;
+ /**
+ * Sets the resource the component is about.
+ *
+ * <p>At most one resource can be set. Without a nested resource
+ * the component will not have any "hashes" when written to the
+ * SBOM.</p>
+ */
public void add(Resource resource) {
checkChildrenAllowed();
if (this.resource != null) {
@@ -68,62 +86,93 @@ public class Component extends DataType {
this.resource = resource;
}
+ /**
+ * Sets the type of the component.
+ *
+ * <p>Defaults to "library".</p>
+ */
public void setType(ComponentType type) {
checkAttributesAllowed();
this.type = type.getType();
}
+ /**
+ * Sets the name of the component.
+ */
public void setName(String name) {
checkAttributesAllowed();
this.name = name;
}
- public String getName() {
- if (isReference()) {
- return getRef().getName();
- }
- dieOnCircularReference();
- return name;
- }
-
+ /**
+ * Sets the group of the component.
+ */
public void setGroup(String group) {
checkAttributesAllowed();
this.group = group;
}
- public String getGroup() {
- if (isReference()) {
- return getRef().getGroup();
- }
- dieOnCircularReference();
- return group;
- }
-
+ /**
+ * Sets the version of the component.
+ */
public void setVersion(String version) {
checkAttributesAllowed();
this.version = version;
}
+ /**
+ * Sets the Package-URL (purl) of the component.
+ */
+ public void setPurl(String purl) {
+ checkAttributesAllowed();
+ this.purl = purl;
+ }
+
+ /**
+ * Sets the bom-ref of the component.
+ */
+ public void setBomRef(String bomRef) {
+ checkAttributesAllowed();
+ this.bomRef = bomRef;
+ }
+
+ /**
+ * Sets the decription of the component.
+ */
public void setDescription(String description) {
checkAttributesAllowed();
this.description = description;
}
+ /**
+ * Sets the publisher of the component.
+ */
public void setPublisher(String publisher) {
checkAttributesAllowed();
this.publisher = publisher;
}
+ /**
+ * Sets the copyright of the component.
+ */
public void setCopyright(String copyright) {
checkAttributesAllowed();
this.copyright = copyright;
}
+ /**
+ * Sets the mime-type of the component.
+ */
public void setMimeType(String mimeType) {
checkAttributesAllowed();
this.mimeType = mimeType;
}
+ /**
+ * Sets the manufacturer of the component.
+ *
+ * <p>At most one manufacturer can be set.</p>
+ */
public void addManufacturer(Organization manufacturer) {
checkChildrenAllowed();
if (this.manufacturer != null) {
@@ -132,6 +181,11 @@ public class Component extends DataType {
this.manufacturer = manufacturer;
}
+ /**
+ * Sets the supplier of the component.
+ *
+ * <p>At most one supplier can be set.</p>
+ */
public void addSupplier(Organization supplier) {
checkChildrenAllowed();
if (this.supplier != null) {
@@ -140,36 +194,152 @@ public class Component extends DataType {
this.supplier = supplier;
}
+ /**
+ * Adds an author to the component.
+ */
public void addAuthor(OrganizationalContact author) {
checkChildrenAllowed();
authors.add(author);
}
+ /**
+ * Adds a tag to the component.
+ */
public void addTag(Tag tag) {
checkChildrenAllowed();
tags.add(tag);
}
+ /**
+ * Adds a property to the component.
+ */
public void addProperty(Property property) {
checkChildrenAllowed();
properties.add(property);
}
+ /**
+ * If set to {@code true} the manufacturer will also be used to
+ * provide the supplier information.
+ */
public void setManufacturerIsSupplier(boolean manufacturerIsSupplier) {
checkAttributesAllowed();
this.manufacturerIsSupplier = manufacturerIsSupplier;
}
+ /**
+ * Adds a license to this component.
+ */
public void addConfiguredLicense(License l) {
checkChildrenAllowed();
licenses.add(l.toCycloneDxLicense());
}
- public void setPurl(String purl) {
+ /**
+ * Adds an external reference to the component.
+ */
+ public void addConfiguredExternalReference(ExternalReference ref) {
+ checkChildrenAllowed();
+ externalReferences.add(ref.toCycloneDxExternalReference());
+ }
+
+ /**
+ * Adds a set of external references to the component.
+ */
+ public void addConfiguredExternalReferenceSet(ExternalReferenceSet set) {
+ checkChildrenAllowed();
+ externalReferences.addAll(set.getExternalReferences());
+ }
+
+ /**
+ * Sets the scope of this component.
+ *
+ * <p>Must not be set for the main component of the SBOM.</p>
+ */
+ public void setScope(ComponentScope scope) {
checkAttributesAllowed();
- this.purl = purl;
+ this.scope = scope.getScope();
+ }
+
+ /**
+ * Sets whether the component is external.
+ *
+ * <p>The CycloneDX Specification says: An external component is
+ * one that is not part of an assembly, but is expected to be
+ * provided by the environment, regardless of the component's
+ * scope.</p>
+ *
+ * <p>Must not be set to {@code true} for the main component of
+ * the SBOM.</p>
+ *
+ * <p>Right now this attribute has no effect until the CycloneDX
+ * core library supports the specification version 1.7.</p>
+ */
+ public void setIsExternal(boolean isExternal) {
+ checkAttributesAllowed();
+ this.isExternal = isExternal;
+ }
+
+ /**
+ * Adds a dependency to this component.
+ */
+ public void addDependency(Dependency d) {
+ checkChildrenAllowed();
+ dependencies.add(d);
}
+ /**
+ * Sets whether the dependencies of this component are unknown.
+ *
+ * <p>This flag is needed to be able to tell dependencies with
+ * unknown dependencies from components without any
+ * dependencies.</p>
+ */
+ public void setUnknownDependencies(boolean unknownDependencies) {
+ checkAttributesAllowed();
+ this.unknownDependencies = unknownDependencies;
+ }
+
+ public void addComponent(Component c) {
+ checkChildrenAllowed();
+ nestedComponents.add(c);
+ setChecked(false);
+ }
+
+ public Union createSbomLink() {
+ checkChildrenAllowed();
+ return sbomLink == null ? (sbomLink = new Union()) : sbomLink;
+ }
+
+ /**
+ * Gets the name of the component.
+ */
+ public String getName() {
+ if (isReference()) {
+ return getRef().getName();
+ }
+ dieOnCircularReference();
+ return name;
+ }
+
+ /**
+ * Gets the group of the component.
+ */
+ public String getGroup() {
+ if (isReference()) {
+ return getRef().getGroup();
+ }
+ dieOnCircularReference();
+ return group;
+ }
+
+ /**
+ * Gets the Package-URL (purl) of the component.
+ *
+ * @return the value set with {@see #setPurl} or a Maven purl
+ * derived from name, group and version if all three or set - or
+ * {@code null otherwise}.
+ */
public String getPurl() {
if (isReference()) {
return getRef().getPurl();
@@ -184,11 +354,11 @@ public class Component extends DataType {
return null;
}
- public void setBomRef(String bomRef) {
- checkAttributesAllowed();
- this.bomRef = bomRef;
- }
-
+ /**
+ * Gets the bom-ref of the component.
+ *
+ * @return the value set with {@see #setBomRef} or the result of {@see
#getPurl}.
+ */
public String getBomRef() {
if (isReference()) {
return getRef().getBomRef();
@@ -200,31 +370,9 @@ public class Component extends DataType {
return bomRef;
}
- public void addConfiguredExternalReference(ExternalReference ref) {
- checkChildrenAllowed();
- externalReferences.add(ref.toCycloneDxExternalReference());
- }
-
- public void addConfiguredExternalReferenceSet(ExternalReferenceSet set) {
- checkChildrenAllowed();
- externalReferences.addAll(set.getExternalReferences());
- }
-
- public void setScope(ComponentScope scope) {
- checkAttributesAllowed();
- this.scope = scope.getScope();
- }
-
- public void setIsExternal(boolean isExternal) {
- checkAttributesAllowed();
- this.isExternal = isExternal;
- }
-
- public void addDependency(Dependency d) {
- checkChildrenAllowed();
- dependencies.add(d);
- }
-
+ /**
+ * Gets the dependencies of the component.
+ */
public Iterable<Dependency> getDependencies() {
if (isReference()) {
return getRef().getDependencies();
@@ -233,10 +381,16 @@ public class Component extends DataType {
return dependencies;
}
- public void addComponent(Component c) {
- checkChildrenAllowed();
- nestedComponents.add(c);
- setChecked(false);
+ /**
+ * @return the value set with {@link #setUnknownDependencies}
+ * or {@code false}.
+ */
+ public boolean areDependenciesUnknown() {
+ if (isReference()) {
+ return getRef().areDependenciesUnknown();
+ }
+ dieOnCircularReference();
+ return unknownDependencies;
}
public List<Component> getNestedComponents() {
@@ -253,16 +407,6 @@ public class Component extends DataType {
return result;
}
- public void setUnknownDependencies(boolean unknownDependencies) {
- checkAttributesAllowed();
- this.unknownDependencies = unknownDependencies;
- }
-
- public Union createSbomLink() {
- checkChildrenAllowed();
- return sbomLink == null ? (sbomLink = new Union()) : sbomLink;
- }
-
public boolean hasSbomLink() {
if (isReference()) {
return getRef().hasSbomLink();
@@ -271,14 +415,6 @@ public class Component extends DataType {
return sbomLink != null;
}
- public boolean areDependenciesUnknown() {
- if (isReference()) {
- return getRef().areDependenciesUnknown();
- }
- dieOnCircularReference();
- return unknownDependencies;
- }
-
public Collection<Component> resolve() throws IOException {
if (isReference()) {
return getRef().resolve();
@@ -354,7 +490,7 @@ public class Component extends DataType {
return Collections.emptyList();
}
- public org.cyclonedx.model.Component toMainCycloneDxComponent(Version
bomVersion)
+ org.cyclonedx.model.Component toMainCycloneDxComponent(Version bomVersion)
throws IOException {
if (isReference()) {
return getRef().toMainCycloneDxComponent(bomVersion);
@@ -362,10 +498,13 @@ public class Component extends DataType {
if (isExternal) {
throw new BuildException("isExternal can not be true for the main
bom component");
}
+ if (scope != null) {
+ throw new BuildException("scope must not be set for the main bom
component");
+ }
return toCycloneDxComponent(bomVersion);
}
- public org.cyclonedx.model.Component
toAdditionalCycloneDxComponent(Version bomVersion)
+ org.cyclonedx.model.Component toAdditionalCycloneDxComponent(Version
bomVersion)
throws IOException {
if (isReference()) {
return getRef().toAdditionalCycloneDxComponent(bomVersion);
@@ -377,7 +516,7 @@ public class Component extends DataType {
return component;
}
- public static Component from(org.cyclonedx.model.Component real) {
+ private static Component from(org.cyclonedx.model.Component real) {
Component c = new Component();
c.fillFrom(real);
return c;
@@ -548,15 +687,24 @@ public class Component extends DataType {
component.setHashes(BomUtils.calculateHashes(file, bomVersion));
}
- public static class Dependency extends ProjectComponent {
+ /**
+ * Represents a dependency of a component.
+ */
+ public static class Dependency {
private String bomRef;
- private String componentRef;
+ private Reference componentRef;
+ /**
+ * Identifies the dependency by its bom-ref.
+ */
public void setBomRef(String bomRef) {
this.bomRef = bomRef;
}
- public void setComponentRef(String componentRef) {
+ /**
+ * Identifies the dependency by its Ant {@code id} attribute.
+ */
+ public void setComponentRef(Reference componentRef) {
this.componentRef = componentRef;
}
@@ -571,10 +719,7 @@ public class Component extends DataType {
return bomRef;
}
- Object component = getProject().getReference(componentRef);
- if (component == null) {
- throw new BuildException("componentRef '" + componentRef + "'
is unknown");
- }
+ Object component = componentRef.getReferencedObject();
if (component instanceof Component) {
String b = ((Component) component).getBomRef();
if (b == null) {
@@ -585,21 +730,27 @@ public class Component extends DataType {
throw new BuildException("componentRef '" + componentRef + "'
doesn't refer to a component");
}
- public static Dependency from(org.cyclonedx.model.Dependency
dependency) {
+ static Dependency from(org.cyclonedx.model.Dependency dependency) {
Dependency d = new Dependency();
d.setBomRef(dependency.getRef());
return d;
}
}
+ /**
+ * Represents a tag.
+ */
public static class Tag {
private String tag;
+ /**
+ * Sets the tag value.
+ */
public void addText(String text) {
tag = text;
}
- public String getTag() {
+ String getTag() {
return tag;
}
}
diff --git a/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java
b/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java
index c0bc776..7d41bee 100644
--- a/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java
+++ b/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java
@@ -205,7 +205,7 @@ public class ComponentBomTask extends Task {
}
}
- if (component.getBomRef() != null) {
+ if (component.getBomRef() != null &&
!component.areDependenciesUnknown()) {
Dependency dep = new Dependency(component.getBomRef());
for (Component.Dependency d : component.getDependencies()) {
String br = d.getBomRef();
diff --git a/src/main/org/apache/ant/cyclonedx/Organization.java
b/src/main/org/apache/ant/cyclonedx/Organization.java
index 2ab4e4e..962ad80 100644
--- a/src/main/org/apache/ant/cyclonedx/Organization.java
+++ b/src/main/org/apache/ant/cyclonedx/Organization.java
@@ -9,8 +9,8 @@ import org.apache.tools.ant.types.resources.URLResource;
import org.cyclonedx.model.OrganizationalEntity;
/**
- * Organization appears as "manufacturer", "publisher" or "supplier"
- * of components or the SBOM itself.
+ * Organization appears as "manufacturer" or "supplier" of components
+ * or the SBOM itself.
*
* <p>The CycloneDX specification supports more information for an
* organization than this type currently exposes.</p>
diff --git a/src/tests/antunit/component-test.xml
b/src/tests/antunit/component-test.xml
new file mode 100644
index 0000000..5835cd2
--- /dev/null
+++ b/src/tests/antunit/component-test.xml
@@ -0,0 +1,273 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project name="component-test" default="antunit">
+
+ <import file="shared.xml" />
+
+ <target name="testComponentNameIsRequired">
+ <au:expectfailure expectedMessage="component name is required"
+ xmlns:au="antlib:org.apache.ant.antunit">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component/>
+ </cdx:componentbom>
+ </au:expectfailure>
+ </target>
+
+ <target name="testComponentsDetectCircularReferences">
+ <au:expectfailure expectedMessage="This data type contains a circular
reference"
+ xmlns:au="antlib:org.apache.ant.antunit">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="level1" id="top">
+ <component name="level2">
+ <component refid="top"/>
+ </component>
+ </component>
+ </cdx:componentbom>
+ </au:expectfailure>
+ </target>
+
+ <target name="testMinimalComponentData">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="testname"/>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.name"
+ value="testname"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component(type)"
+ value="library"/>
+ </target>
+
+ <target name="testComponentTypeCanBeSet">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="testname" type="APPLICATION"/>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component(type)"
+ value="application"/>
+ </target>
+
+ <target name="testComponentTypeCanBeSetInLowerCase">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="testname" type="application"/>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component(type)"
+ value="application"/>
+ </target>
+
+ <target name="testCalculatesPurlAndBomRef">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="testname" group="org.example" version="1.0"/>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.purl"
+ value="pkg:maven/org.example/[email protected]?type=jar"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component(bom-ref)"
+ value="pkg:maven/org.example/[email protected]?type=jar"/>
+ </target>
+
+ <target name="testPurlAndBomRefCanBeSet">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="testname" group="org.example" version="1.0"
+ purl="foo" bomRef="bar"/>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.purl"
+ value="foo"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component(bom-ref)"
+ value="bar"/>
+ </target>
+
+ <target name="testScopeIsProhibitedForMainComponent">
+ <au:expectfailure expectedMessage="scope must not be set for the main bom
component"
+ xmlns:au="antlib:org.apache.ant.antunit">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo" scope="optional"/>
+ </cdx:componentbom>
+ </au:expectfailure>
+ </target>
+
+ <target name="testIsExternalTrueIsProhibitedForMainComponent">
+ <au:expectfailure expectedMessage="isExternal can not be true for the main
bom component"
+ xmlns:au="antlib:org.apache.ant.antunit">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo" isExternal="true"/>
+ </cdx:componentbom>
+ </au:expectfailure>
+ </target>
+
+ <target name="testIsExternalFalseIsPermittedForMainComponent">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo" isExternal="false"/>
+ </cdx:componentbom>
+ </target>
+
+ <target name="testScopeCanBeSetForAdditionalComponent">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo"/>
+ <additionalComponent name="bar" scope="optional"/>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.components.component.scope"
+ value="optional"/>
+ </target>
+
+ <target name="testScopeCanBeSetInUpperCaseForAdditionalComponent">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo"/>
+ <additionalComponent name="bar" scope="OPTIONAL"/>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.components.component.scope"
+ value="optional"/>
+ </target>
+
+ <target name="testIsExternalCanBeSetForAdditionalComponent">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo"/>
+ <additionalComponent name="bar" isExternal="true"/>
+ </cdx:componentbom>
+ </target>
+
+ <target name="testManufacturerIsNotCopiedToSupplierByDefault">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo">
+ <manufacturer name="Example">
+ <url url="https://example.com/"/>
+ </manufacturer>
+ </component>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.manufacturer.name"
+ value="Example"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.manufacturer.url"
+ value="https://example.com/"/>
+ <au:fail message="expected bom.metadata.components.component.supplier.name
to not be set"
+ xmlns:au="antlib:org.apache.ant.antunit">
+ <not>
+ <isset property="bom.metadata.component.supplier.name"/>
+ </not>
+ </au:fail>
+ </target>
+
+ <target name="testManufacturerIsCopiedToSupplierWhenRequested">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo" manufacturerIsSupplier="true">
+ <manufacturer name="Example">
+ <url url="https://example.com/"/>
+ </manufacturer>
+ </component>
+ </cdx:componentbom>
+ <xmlproperty file="${output}/bom.xml"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.manufacturer.name"
+ value="Example"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.manufacturer.url"
+ value="https://example.com/"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.supplier.name"
+ value="Example"/>
+ <au:assertPropertyEquals
+ xmlns:au="antlib:org.apache.ant.antunit"
+ name="bom.metadata.component.supplier.url"
+ value="https://example.com/"/>
+ </target>
+
+ <target name="testManufacturerIsSupplierMustNotBeTrueWhenSupplierIsPresent">
+ <au:expectfailure expectedMessage="component with supplier can't use
manufacturer as supplier"
+ xmlns:au="antlib:org.apache.ant.antunit">
+ <cdx:componentbom outputdirectory="${output}" format="xml"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo" manufacturerIsSupplier="true">
+ <manufacturer name="Example">
+ <url url="https://example.com/"/>
+ </manufacturer>
+ <supplier name="Example 2">
+ <url url="https://example.org/"/>
+ </supplier>
+ </component>
+ </cdx:componentbom>
+ </au:expectfailure>
+ </target>
+
+ <target name="testDependenciesAreEmptyByDefault">
+ <cdx:componentbom outputdirectory="${output}" format="json"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo" bomRef="foo"/>
+ </cdx:componentbom>
+ <au:assertResourceContains
+ xmlns:au="antlib:org.apache.ant.antunit"
+ resource="${output}/bom.json"
+ value='"dependsOn" : [ ]'/>
+ </target>
+
+ <target name="testDependenciesAreNotPresentIfUnknown">
+ <cdx:componentbom outputdirectory="${output}" format="json"
+ xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+ <component name="foo" bomRef="foo" unknownDependencies="true"/>
+ </cdx:componentbom>
+ <copy todir="/tmp" file="${output}/bom.json"/>
+ <au:assertResourceContains
+ xmlns:au="antlib:org.apache.ant.antunit"
+ resource="${output}/bom.json"
+ value='"dependencies" : [ ]'/>
+ </target>
+</project>
diff --git a/src/tests/antunit/componentbom-test.xml
b/src/tests/antunit/componentbom-test.xml
index 431c578..bcc804e 100644
--- a/src/tests/antunit/componentbom-test.xml
+++ b/src/tests/antunit/componentbom-test.xml
@@ -239,30 +239,6 @@
</au:expectfailure>
</target>
- <target name="testComponentNameIsRequired">
- <au:expectfailure expectedMessage="component name is required"
- xmlns:au="antlib:org.apache.ant.antunit">
- <cdx:componentbom outputdirectory="${output}" format="xml"
- xmlns:cdx="antlib:org.apache.ant.cyclonedx">
- <component/>
- </cdx:componentbom>
- </au:expectfailure>
- </target>
-
- <target name="testComponentsDetectCircularReferences">
- <au:expectfailure expectedMessage="This data type contains a circular
reference"
- xmlns:au="antlib:org.apache.ant.antunit">
- <cdx:componentbom outputdirectory="${output}" format="xml"
- xmlns:cdx="antlib:org.apache.ant.cyclonedx">
- <component name="level1" id="top">
- <component name="level2">
- <component refid="top"/>
- </component>
- </component>
- </cdx:componentbom>
- </au:expectfailure>
- </target>
-
<target name="testSpecVersionAsVersionString">
<cdx:componentbom
outputdirectory="${output}"
@@ -291,79 +267,6 @@
value='"specVersion" : "1.6"'/>
</target>
- <target name="testMinimalComponentData">
- <cdx:componentbom outputdirectory="${output}" format="xml"
- xmlns:cdx="antlib:org.apache.ant.cyclonedx">
- <component name="testname"/>
- </cdx:componentbom>
- <xmlproperty file="${output}/bom.xml"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component.name"
- value="testname"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component(type)"
- value="library"/>
- </target>
-
- <target name="testComponentTypeCanBeSet">
- <cdx:componentbom outputdirectory="${output}" format="xml"
- xmlns:cdx="antlib:org.apache.ant.cyclonedx">
- <component name="testname" type="APPLICATION"/>
- </cdx:componentbom>
- <xmlproperty file="${output}/bom.xml"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component(type)"
- value="application"/>
- </target>
-
- <target name="testComponentTypeCanBeSetInLowerCase">
- <cdx:componentbom outputdirectory="${output}" format="xml"
- xmlns:cdx="antlib:org.apache.ant.cyclonedx">
- <component name="testname" type="application"/>
- </cdx:componentbom>
- <xmlproperty file="${output}/bom.xml"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component(type)"
- value="application"/>
- </target>
-
- <target name="testCalculatesPurlAndBomRef">
- <cdx:componentbom outputdirectory="${output}" format="xml"
- xmlns:cdx="antlib:org.apache.ant.cyclonedx">
- <component name="testname" group="org.example" version="1.0"/>
- </cdx:componentbom>
- <xmlproperty file="${output}/bom.xml"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component.purl"
- value="pkg:maven/org.example/[email protected]?type=jar"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component(bom-ref)"
- value="pkg:maven/org.example/[email protected]?type=jar"/>
- </target>
-
- <target name="testPurlAndBomRefCanBeSet">
- <cdx:componentbom outputdirectory="${output}" format="xml"
- xmlns:cdx="antlib:org.apache.ant.cyclonedx">
- <component name="testname" group="org.example" version="1.0"
- purl="foo" bomRef="bar"/>
- </cdx:componentbom>
- <xmlproperty file="${output}/bom.xml"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component.purl"
- value="foo"/>
- <au:assertPropertyEquals
- xmlns:au="antlib:org.apache.ant.antunit"
- name="bom.metadata.component(bom-ref)"
- value="bar"/>
- </target>
-
<target name="testMaximalComponentData">
<checksum property="ant.file.sha256" file="${ant.file}"
algorithm="SHA-256"/>
<cdx:componentbom outputdirectory="${output}" format="xml"