summaryrefslogtreecommitdiff
path: root/cli/spdxtool
diff options
context:
space:
mode:
authorPierre Pronchery <khorben@FreeBSD.org>2026-06-25 22:30:05 +0000
committerPierre Pronchery <khorben@FreeBSD.org>2026-06-29 05:21:48 +0000
commitb0fc1b7a2fe8fcb18ee407227591bf25a86ffbaf (patch)
tree0cc32f86bf808bcf9d79da95db969cd76f0feadb /cli/spdxtool
parenteaa637963bcfde283b411c14968b7903b02d6aa1 (diff)
Diffstat (limited to 'cli/spdxtool')
-rw-r--r--cli/spdxtool/core.c113
-rw-r--r--cli/spdxtool/core.h6
-rw-r--r--cli/spdxtool/main.c16
-rw-r--r--cli/spdxtool/serialize.c15
-rw-r--r--cli/spdxtool/serialize.h6
-rw-r--r--cli/spdxtool/software.c100
-rw-r--r--cli/spdxtool/util.h2
7 files changed, 248 insertions, 10 deletions
diff --git a/cli/spdxtool/core.c b/cli/spdxtool/core.c
index 5cf7f10e81e1..7dc7a8b41cce 100644
--- a/cli/spdxtool/core.c
+++ b/cli/spdxtool/core.c
@@ -222,8 +222,15 @@ spdxtool_core_creation_info_to_object(pkgconf_client_t *client, const spdxtool_c
if (!(spdxtool_serialize_object_add_string(object_list, "type", creation->type) &&
spdxtool_serialize_object_add_string(object_list, "@id", creation->id) &&
- spdxtool_serialize_object_add_string(object_list, "created", creation->created) &&
- spdxtool_serialize_object_add_array(object_list, "createdBy", created_by) &&
+ spdxtool_serialize_object_add_string(object_list, "created", creation->created)))
+ {
+ /* created_by has not been handed to the object list yet */
+ spdxtool_serialize_array_free(created_by);
+ goto err;
+ }
+
+ /* object_add_array takes ownership of created_by, freeing it on failure */
+ if (!(spdxtool_serialize_object_add_array(object_list, "createdBy", created_by) &&
spdxtool_serialize_object_add_string(object_list, "specVersion", creation->spec_version)))
{
goto err;
@@ -329,6 +336,13 @@ spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t *spdx)
free(iter);
}
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->maintainers.head, iter_next, iter)
+ {
+ spdxtool_core_agent_t *maintainer = iter->data;
+ spdxtool_core_agent_free(maintainer);
+ free(iter);
+ }
+
PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->packages.head, iter_next, iter)
{
free(iter);
@@ -547,6 +561,60 @@ spdxtool_core_spdx_document_add_element(pkgconf_client_t *client, spdxtool_core_
/*
* !doc
*
+ * .. c:function:: const char *spdxtool_core_spdx_document_add_maintainer(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *name)
+ *
+ * Register a package maintainer as an Agent and add it to the SpdxDocument so that
+ * packages may reference it via their ``suppliedBy`` field. Maintainers are
+ * deduplicated by their spdxId, so a maintainer shared between packages is only
+ * emitted once. The first time a maintainer is seen, its spdxId is also added to
+ * the document element list.
+ *
+ * :param pkgconf_client_t *client: The pkgconf client being accessed.
+ * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct being used.
+ * :param const char *name: Maintainer name as declared in the package.
+ * :return: the maintainer Agent spdxId (owned by the document) on success, NULL on failure
+ */
+const char *
+spdxtool_core_spdx_document_add_maintainer(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *name)
+{
+ pkgconf_node_t *iter = NULL;
+
+ if (!client || !spdx || !name)
+ return NULL;
+
+ spdxtool_core_agent_t *agent = spdxtool_core_agent_new(client, spdx->creation_info, name);
+ if (!agent)
+ return NULL;
+
+ PKGCONF_FOREACH_LIST_ENTRY(spdx->maintainers.head, iter)
+ {
+ spdxtool_core_agent_t *existing = iter->data;
+ if (!strcmp(existing->spdx_id, agent->spdx_id))
+ {
+ spdxtool_core_agent_free(agent);
+ return existing->spdx_id;
+ }
+ }
+
+ pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
+ if (!node)
+ {
+ pkgconf_error(client, "spdxtool_core_spdx_document_add_maintainer: out of memory");
+ spdxtool_core_agent_free(agent);
+ return NULL;
+ }
+
+ pkgconf_node_insert_tail(node, agent, &spdx->maintainers);
+
+ if (!spdxtool_core_spdx_document_add_element(client, spdx, agent->spdx_id))
+ return NULL;
+
+ return agent->spdx_id;
+}
+
+/*
+ * !doc
+ *
* .. c:function:: bool spdxtool_core_spdx_document_add_relationship(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, spdxtool_core_relationship_t *relationship)
*
* Add relationship rel to SpdxDocument
@@ -650,6 +718,40 @@ err:
/*
* !doc
*
+ * .. c:function:: bool spdxtool_core_relationship_set_scope(pkgconf_client_t *client, spdxtool_core_relationship_t *relationship, const char *scope)
+ *
+ * Promote a relationship to a /Core/LifecycleScopedRelationship by attaching a lifecycle
+ * scope (for example, ``development`` for a private build dependency). This is the SPDX 3.0
+ * representation of SPDX 2.x's ``*_DEPENDENCY_OF`` relationship variants.
+ *
+ * :param pkgconf_client_t *client: The pkgconf client being accessed.
+ * :param spdxtool_core_relationship_t *relationship: Relationship struct to scope.
+ * :param const char *scope: LifecycleScopeType value, e.g. "development".
+ * :return: true on success, false on failure
+ */
+bool
+spdxtool_core_relationship_set_scope(pkgconf_client_t *client, spdxtool_core_relationship_t *relationship, const char *scope)
+{
+ if (!relationship || !scope)
+ return false;
+
+ char *scope_copy = strdup(scope);
+ if (!scope_copy)
+ {
+ pkgconf_error(client, "spdxtool_core_relationship_set_scope: out of memory");
+ return false;
+ }
+
+ free(relationship->scope);
+ relationship->scope = scope_copy;
+ relationship->type = "LifecycleScopedRelationship";
+
+ return true;
+}
+
+/*
+ * !doc
+ *
* .. c:function:: void spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship)
*
* Free /Core/Relationship struct
@@ -669,6 +771,7 @@ spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship)
pkgconf_license_free(relationship->to);
free(relationship->to);
free(relationship->relationship_type);
+ free(relationship->scope);
free(relationship);
}
@@ -717,6 +820,12 @@ spdxtool_core_relationship_to_object(pkgconf_client_t *client, const spdxtool_co
goto err;
}
+ if (relationship->scope != NULL &&
+ !spdxtool_serialize_object_add_string(object_list, "scope", relationship->scope))
+ {
+ goto err;
+ }
+
ret = spdxtool_serialize_value_object(object_list);
object_list = NULL;
diff --git a/cli/spdxtool/core.h b/cli/spdxtool/core.h
index 0cbb1f4b3d51..9575310ebd1f 100644
--- a/cli/spdxtool/core.h
+++ b/cli/spdxtool/core.h
@@ -55,6 +55,9 @@ spdxtool_core_spdx_document_add_package(pkgconf_client_t *client, spdxtool_core_
bool
spdxtool_core_spdx_document_add_element(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *element);
+const char *
+spdxtool_core_spdx_document_add_maintainer(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *name);
+
void
spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t *spdx);
@@ -64,6 +67,9 @@ spdxtool_core_spdx_document_to_object(pkgconf_client_t *client, spdxtool_core_sp
spdxtool_core_relationship_t *
spdxtool_core_relationship_new(pkgconf_client_t *client, const char *creation_info_id, const char *spdx_id, const char *from, pkgconf_list_t *to, const char *relationship_type);
+bool
+spdxtool_core_relationship_set_scope(pkgconf_client_t *client, spdxtool_core_relationship_t *relationship, const char *scope);
+
void
spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship);
diff --git a/cli/spdxtool/main.c b/cli/spdxtool/main.c
index 70cdce2dd891..7d52abe56670 100644
--- a/cli/spdxtool/main.c
+++ b/cli/spdxtool/main.c
@@ -30,7 +30,6 @@ static int maximum_traverse_depth = 2000;
static pkgconf_client_t pkg_client;
static uint64_t want_flags;
-static size_t maximum_package_count = 0;
// static int maximum_traverse_depth = 2000;
static FILE *error_msgout = NULL;
static FILE *sbom_out = NULL;
@@ -94,6 +93,15 @@ generate_spdx_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *ptr)
pkgconf_tuple_add(client, &pkg->vars, "creationInfo", document->creation_info, false, 0);
pkgconf_tuple_add(client, &pkg->vars, "agent", document->agent, false, 0);
+ if (pkg->maintainer != NULL)
+ {
+ const char *supplier = spdxtool_core_spdx_document_add_maintainer(client, document, pkg->maintainer);
+ if (!supplier)
+ goto err;
+
+ pkgconf_tuple_add(client, &pkg->vars, "suppliedBy", supplier, false, 0);
+ }
+
if (pkg->license.head != NULL)
{
pkgconf_buffer_t spdx_id_buf = PKGCONF_BUFFER_INITIALIZER;
@@ -364,12 +372,6 @@ main(int argc, char *argv[])
if (package == NULL)
break;
- /* check if there is a limit to the number of packages allowed to be included, if so and we have hit
- * the limit, stop adding packages to the queue.
- */
- if (maximum_package_count > 0 && pkgq.length > maximum_package_count)
- break;
-
while (isspace((unsigned char)package[0]))
package++;
diff --git a/cli/spdxtool/serialize.c b/cli/spdxtool/serialize.c
index 69ef3a70a3b1..fc5c460d77cb 100644
--- a/cli/spdxtool/serialize.c
+++ b/cli/spdxtool/serialize.c
@@ -169,7 +169,8 @@ spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t *object_list
{
free(node);
free(keycopy);
- spdxtool_serialize_object_free(object);
+ /* object->key/value are not assigned yet; free the struct itself */
+ free(object);
spdxtool_serialize_value_free(value);
return NULL;
}
@@ -397,6 +398,18 @@ spdxtool_serialize_sbom(pkgconf_client_t *client, spdxtool_core_agent_t *agent,
goto err;
pkgconf_node_t *iter = NULL;
+ PKGCONF_FOREACH_LIST_ENTRY(spdx->maintainers.head, iter)
+ {
+ spdxtool_core_agent_t *maintainer = iter->data;
+ if (!maintainer)
+ {
+ errstr = "maintainers list corrupted";
+ goto err;
+ }
+ if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_agent_to_object(client, maintainer)))
+ goto err;
+ }
+
PKGCONF_FOREACH_LIST_ENTRY(spdx->licenses.head, iter)
{
spdxtool_simplelicensing_license_expression_t *expression = iter->data;
diff --git a/cli/spdxtool/serialize.h b/cli/spdxtool/serialize.h
index e04b7f38b741..a1eb99403406 100644
--- a/cli/spdxtool/serialize.h
+++ b/cli/spdxtool/serialize.h
@@ -203,7 +203,10 @@ spdxtool_serialize_value_object(spdxtool_serialize_object_list_t *object_list)
{
spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
if (!value)
+ {
+ spdxtool_serialize_object_list_free(object_list);
return NULL;
+ }
value->type = SPDXTOOL_SERIALIZE_TYPE_OBJECT;
value->value.o = object_list;
@@ -227,7 +230,10 @@ spdxtool_serialize_value_array(spdxtool_serialize_array_t *array)
{
spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
if (!value)
+ {
+ spdxtool_serialize_array_free(array);
return NULL;
+ }
value->type = SPDXTOOL_SERIALIZE_TYPE_ARRAY;
value->value.a = array;
diff --git a/cli/spdxtool/software.c b/cli/spdxtool/software.c
index 4e9712ba6942..ac76cf73e7de 100644
--- a/cli/spdxtool/software.c
+++ b/cli/spdxtool/software.c
@@ -162,6 +162,37 @@ spdxtool_software_sbom_to_object(pkgconf_client_t *client, spdxtool_software_sbo
free(spdx_id_relation);
}
+ PKGCONF_FOREACH_LIST_ENTRY(sbom->rootElement->requires_private.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ pkgconf_pkg_t *match = dep->match;
+ pkgconf_buffer_t relationship_buf = PKGCONF_BUFFER_INITIALIZER;
+
+ pkgconf_buffer_append_fmt(&relationship_buf, "%s%cdependsOn%c%s", sbom->rootElement->id, sep, sep, match->id);
+ char *relationship_str = pkgconf_buffer_freeze(&relationship_buf);
+ if (!relationship_str)
+ goto err;
+
+ char *spdx_id_relation = spdxtool_util_get_spdx_id_string(client, "Relationship", relationship_str);
+ free(relationship_str);
+ if (!spdx_id_relation)
+ goto err;
+
+ if (!spdxtool_serialize_array_add_string(element_array, spdx_id_relation))
+ {
+ free(spdx_id_relation);
+ goto err;
+ }
+
+ if (!spdxtool_core_spdx_document_add_element(client, sbom->spdx_document, spdx_id_relation))
+ {
+ free(spdx_id_relation);
+ goto err;
+ }
+
+ free(spdx_id_relation);
+ }
+
char *value = spdxtool_util_tuple_lookup(client, &sbom->rootElement->vars, "hasDeclaredLicense");
if (value)
{
@@ -275,9 +306,11 @@ spdxtool_software_package_to_object(pkgconf_client_t *client, pkgconf_pkg_t *pkg
spdxtool_serialize_value_t *ret = NULL;
spdxtool_serialize_object_list_t *object_list = NULL;
spdxtool_serialize_array_t *originated_by = NULL;
+ spdxtool_serialize_array_t *supplied_by = NULL;
char *creation_info = NULL;
char *spdx_id = NULL;
char *agent = NULL;
+ char *supplier = NULL;
char *spdx_id_license = NULL;
pkgconf_list_t relations = PKGCONF_LIST_INITIALIZER;
pkgconf_list_t *cpy_relations = NULL;
@@ -314,6 +347,21 @@ spdxtool_software_package_to_object(pkgconf_client_t *client, pkgconf_pkg_t *pkg
goto err;
originated_by = NULL;
+ supplier = spdxtool_util_tuple_lookup(client, &pkg->vars, "suppliedBy");
+ if (supplier)
+ {
+ supplied_by = spdxtool_serialize_array_new();
+ if (!supplied_by)
+ goto err;
+
+ if (!spdxtool_serialize_array_add_string(supplied_by, supplier))
+ goto err;
+
+ if (!spdxtool_serialize_object_add_array(object_list, "suppliedBy", supplied_by))
+ goto err;
+ supplied_by = NULL;
+ }
+
if (!serialize_copyright_lines_to_object(object_list, &pkg->copyright))
goto err;
@@ -430,6 +478,56 @@ spdxtool_software_package_to_object(pkgconf_client_t *client, pkgconf_pkg_t *pkg
cpy_relations = NULL;
}
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ pkgconf_pkg_t *match = dep->match;
+ pkgconf_buffer_t relationship_buf = PKGCONF_BUFFER_INITIALIZER;
+
+ pkgconf_buffer_append_fmt(&relationship_buf, "%s%cdependsOn%c%s", pkg->id, sep, sep, match->id);
+ char *relationship_str = pkgconf_buffer_freeze(&relationship_buf);
+ if (!relationship_str)
+ goto err;
+
+ char *spdx_id_relation = spdxtool_util_get_spdx_id_string(client, "Relationship", relationship_str);
+ free(relationship_str);
+ if (!spdx_id_relation)
+ goto err;
+
+ char *spdx_id_package = spdxtool_util_get_spdx_id_string(client, "Package", match->id);
+ if (!spdx_id_package)
+ {
+ free(spdx_id_relation);
+ goto err;
+ }
+
+ cpy_relations = calloc(1, sizeof(pkgconf_list_t));
+ if (!cpy_relations)
+ {
+ free(spdx_id_relation);
+ free(spdx_id_package);
+ goto err;
+ }
+
+ pkgconf_license_insert(client, cpy_relations, PKGCONF_LICENSE_UNKNOWN, spdx_id_package);
+ spdxtool_core_relationship_t *relationship = spdxtool_core_relationship_new(client, creation_info, spdx_id_relation, spdx_id, cpy_relations, "dependsOn");
+ free(spdx_id_relation);
+ free(spdx_id_package);
+ if (!relationship)
+ goto err;
+ cpy_relations = NULL;
+ if (!spdxtool_core_relationship_set_scope(client, relationship, "development"))
+ {
+ spdxtool_core_relationship_free(relationship);
+ goto err;
+ }
+ if (!spdxtool_core_spdx_document_add_relationship(client, spdx, relationship))
+ {
+ spdxtool_core_relationship_free(relationship);
+ goto err;
+ }
+ }
+
ret = spdxtool_serialize_value_object(object_list);
object_list = NULL;
@@ -440,8 +538,10 @@ err:
free(creation_info);
free(spdx_id);
free(agent);
+ free(supplier);
free(spdx_id_license);
spdxtool_serialize_object_list_free(object_list);
spdxtool_serialize_array_free(originated_by);
+ spdxtool_serialize_array_free(supplied_by);
return ret;
}
diff --git a/cli/spdxtool/util.h b/cli/spdxtool/util.h
index e39c173e64c5..645d93a5a8e9 100644
--- a/cli/spdxtool/util.h
+++ b/cli/spdxtool/util.h
@@ -49,6 +49,7 @@ typedef struct spdxtool_core_spdx_document
pkgconf_list_t rootElement;
pkgconf_list_t relationships;
pkgconf_list_t packages;
+ pkgconf_list_t maintainers;
} spdxtool_core_spdx_document_t;
typedef struct spdxtool_software_sbom_
@@ -76,6 +77,7 @@ typedef struct spdxtool_core_relationship_
char *from;
pkgconf_list_t *to;
char *relationship_type;
+ char *scope;
} spdxtool_core_relationship_t;
void