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