xref: /freebsd/contrib/pkgconf/cli/spdxtool/generate.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  *​ Copyright (c) 2025 The FreeBSD Foundation
5  *​
6  *​ Portions of this software were developed by
7  * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
8  * the FreeBSD Foundation
9  */
10 
11 #include <libpkgconf/stdinc.h>
12 #include <libpkgconf/libpkgconf.h>
13 #include "util.h"
14 #include "core.h"
15 #include "software.h"
16 #include "serialize.h"
17 #include "simplelicensing.h"
18 #include "generate.h"
19 
20 // NOTE: this function is passed to pkgconf_pkg_traverse
21 static void
generate_spdx_package(pkgconf_client_t * client,pkgconf_pkg_t * pkg,void * ptr)22 generate_spdx_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *ptr)
23 {
24 	spdxtool_core_spdx_document_t *document = (spdxtool_core_spdx_document_t *)ptr;
25 	pkgconf_node_t *node = NULL;
26 	spdxtool_software_sbom_t *sbom = NULL;
27 	char *package_spdx = NULL;
28 	char *spdx_id_string = NULL;
29 	char sep = spdxtool_util_get_uri_separator(client);
30 
31 	if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
32 		return;
33 
34 	spdx_id_string = spdxtool_util_get_spdx_id_string(client, "software_Sbom", pkg->id);
35 	if (!spdx_id_string)
36 		goto err;
37 
38 	sbom = spdxtool_software_sbom_new(client, spdx_id_string, document->creation_info, "build");
39 	free(spdx_id_string);
40 	spdx_id_string = NULL;
41 	if (!sbom)
42 		goto err;
43 
44 	sbom->spdx_document = document;
45 	sbom->rootElement = pkg;
46 
47 	package_spdx = spdxtool_util_get_spdx_id_string(client, "Package", pkg->id);
48 	if (!package_spdx)
49 		goto err;
50 
51 	pkgconf_tuple_add(client, &pkg->vars, "spdxId", package_spdx, false, 0);
52 	free(package_spdx);
53 	package_spdx = NULL;
54 
55 	pkgconf_tuple_add(client, &pkg->vars, "creationInfo", document->creation_info, false, 0);
56 	pkgconf_tuple_add(client, &pkg->vars, "agent", document->agent, false, 0);
57 
58 	if (pkg->maintainer != NULL)
59 	{
60 		const char *supplier = spdxtool_core_spdx_document_add_maintainer(client, document, pkg->maintainer);
61 		if (!supplier)
62 			goto err;
63 
64 		pkgconf_tuple_add(client, &pkg->vars, "suppliedBy", supplier, false, 0);
65 	}
66 
67 	if (pkg->license.head != NULL)
68 	{
69 		pkgconf_buffer_t spdx_id_buf = PKGCONF_BUFFER_INITIALIZER;
70 
71 		pkgconf_buffer_append_fmt(&spdx_id_buf, "%s%chasDeclaredLicense", pkg->id, sep);
72 		char *spdx_id_name = pkgconf_buffer_freeze(&spdx_id_buf);
73 		if (!spdx_id_name)
74 			goto err;
75 
76 		package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
77 		free(spdx_id_name);
78 		if (!package_spdx)
79 			goto err;
80 
81 		pkgconf_tuple_add(client, &pkg->vars, "hasDeclaredLicense", package_spdx, false, 0);
82 		free(package_spdx);
83 		package_spdx = NULL;
84 
85 		pkgconf_buffer_t concluded_buf = PKGCONF_BUFFER_INITIALIZER;
86 		pkgconf_buffer_append_fmt(&concluded_buf, "%s%chasConcludedLicense", pkg->id, sep);
87 		spdx_id_name = pkgconf_buffer_freeze(&concluded_buf);
88 		if (!spdx_id_name)
89 			goto err;
90 
91 		package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
92 		free(spdx_id_name);
93 		if (!package_spdx)
94 			goto err;
95 
96 		pkgconf_tuple_add(client, &pkg->vars, "hasConcludedLicense", package_spdx, false, 0);
97 		free(package_spdx);
98 		package_spdx = NULL;
99 
100 		PKGCONF_FOREACH_LIST_ENTRY(pkg->license.head, node)
101 		{
102 			const pkgconf_license_t *license = node->data;
103 			if (license->type == PKGCONF_LICENSE_EXPRESSION)
104 			{
105 				if (!spdxtool_core_spdx_document_add_license(client, document, license->data))
106 					goto err;
107 			}
108 		}
109 	}
110 
111 	node = calloc(1, sizeof(pkgconf_node_t));
112 	if (!node)
113 		goto err;
114 
115 	pkgconf_node_insert_tail(node, sbom, &document->rootElement);
116 	return;
117 
118 err:
119 	pkgconf_error(client, "generate_spdx_package: failed for %s", pkg->id);
120 	free(package_spdx);
121 	free(spdx_id_string);
122 	spdxtool_software_sbom_free(sbom);
123 }
124 
125 bool
spdxtool_generate(pkgconf_client_t * client,pkgconf_pkg_t * world,FILE * out,int maxdepth,const char * creation_time,const char * creation_id,const char * agent_name)126 spdxtool_generate(pkgconf_client_t *client, pkgconf_pkg_t *world, FILE *out, int maxdepth,
127 	const char *creation_time, const char *creation_id, const char *agent_name)
128 {
129 	const char *agent_name_string = agent_name ? agent_name : "Default";
130 	const char *creation_id_string = creation_id ? creation_id : "_:creationinfo_1";
131 
132 	spdxtool_core_agent_t *agent = spdxtool_core_agent_new(client, creation_id_string, agent_name_string);
133 	if (!agent)
134 	{
135 		pkgconf_error(client, "Could not create agent struct");
136 		return false;
137 	}
138 
139 	spdxtool_core_creation_info_t *creation = spdxtool_core_creation_info_new(client, agent->spdx_id, creation_id_string, creation_time);
140 	if (!creation)
141 	{
142 		pkgconf_error(client, "Could not create creation info struct");
143 		spdxtool_core_agent_free(agent);
144 		return false;
145 	}
146 
147 	char *spdx_id_int = spdxtool_util_get_spdx_id_int(client, "spdxDocument");
148 	spdxtool_core_spdx_document_t *document = spdxtool_core_spdx_document_new(client, spdx_id_int, creation_id_string, agent->spdx_id);
149 	free(spdx_id_int);
150 	if (!document)
151 	{
152 		pkgconf_error(client, "Could not create document");
153 		spdxtool_core_creation_info_free(creation);
154 		spdxtool_core_agent_free(agent);
155 		return false;
156 	}
157 
158 	int eflag = pkgconf_pkg_traverse(client, world, generate_spdx_package, document, maxdepth, 0);
159 	if (eflag != PKGCONF_PKG_ERRF_OK)
160 	{
161 		spdxtool_core_spdx_document_free(document);
162 		spdxtool_core_creation_info_free(creation);
163 		spdxtool_core_agent_free(agent);
164 		return false;
165 	}
166 
167 	spdxtool_serialize_value_t *root = spdxtool_serialize_sbom(client, agent, creation, document);
168 	if (!root)
169 	{
170 		spdxtool_core_spdx_document_free(document);
171 		spdxtool_core_creation_info_free(creation);
172 		spdxtool_core_agent_free(agent);
173 		return false;
174 	}
175 
176 	pkgconf_buffer_t buffer = PKGCONF_BUFFER_INITIALIZER;
177 	spdxtool_serialize_value_to_buf(&buffer, root, 0);
178 	spdxtool_serialize_value_free(root);
179 
180 	bool ret = pkgconf_output_file_fmt(out, "%s\n", pkgconf_buffer_str(&buffer));
181 	pkgconf_buffer_finalize(&buffer);
182 
183 	spdxtool_core_spdx_document_free(document);
184 	spdxtool_core_creation_info_free(creation);
185 	spdxtool_core_agent_free(agent);
186 
187 	if (!ret)
188 		pkgconf_error(client, "spdxtool: Could not output to file: %s", strerror(errno));
189 	return ret;
190 }
191