xref: /aosp_15_r20/external/spdx-tools/rdfloader/parser2v2/parse_package.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2
3package parser2v2
4
5import (
6	"fmt"
7	"strings"
8
9	gordfParser "github.com/spdx/gordf/rdfloader/parser"
10	"github.com/spdx/tools-golang/spdx/common"
11	"github.com/spdx/tools-golang/spdx/v2_2"
12)
13
14func (parser *rdfParser2_2) getPackageFromNode(packageNode *gordfParser.Node) (pkg *v2_2.Package, err error) {
15	pkg = &v2_2.Package{} // new package which will be returned
16
17	currState := parser.cache[packageNode.ID]
18	if currState == nil {
19		// there is no entry about the state of current package node.
20		// this is the first time we're seeing this node.
21		parser.cache[packageNode.ID] = &nodeState{
22			object: pkg,
23			Color:  WHITE,
24		}
25	} else if currState.Color == GREY {
26		// we have already started parsing this package node and we needn't parse it again.
27		return currState.object.(*v2_2.Package), nil
28	}
29
30	// setting color of the state to grey to indicate that we've started to
31	// parse this node once.
32	parser.cache[packageNode.ID].Color = GREY
33
34	// setting state color to black to indicate when we're done parsing this node.
35	defer func() { parser.cache[packageNode.ID].Color = BLACK }()
36
37	// setting the SPDXIdentifier for the package.
38	eId, err := ExtractElementID(getLastPartOfURI(packageNode.ID))
39	if err != nil {
40		return nil, fmt.Errorf("error extracting elementID of a package identifier: %v", err)
41	}
42	pkg.PackageSPDXIdentifier = eId // 3.2
43
44	// check if we already have a package initialized for this ID
45	existingPackageIndex := -1
46	for ii, existingPkg := range parser.doc.Packages {
47		if existingPkg != nil && existingPkg.PackageSPDXIdentifier == eId {
48			existingPackageIndex = ii
49			pkg = existingPkg
50			break
51		}
52	}
53
54	// iterate over all the triples associated with the provided package packageNode.
55	for _, subTriple := range parser.nodeToTriples(packageNode) {
56		switch subTriple.Predicate.ID {
57		case RDF_TYPE:
58			// cardinality: exactly 1
59			continue
60		case SPDX_NAME: // 3.1
61			// cardinality: exactly 1
62			pkg.PackageName = subTriple.Object.ID
63		case SPDX_VERSION_INFO: // 3.3
64			// cardinality: max 1
65			pkg.PackageVersion = subTriple.Object.ID
66		case SPDX_PACKAGE_FILE_NAME: // 3.4
67			// cardinality: max 1
68			pkg.PackageFileName = subTriple.Object.ID
69		case SPDX_SUPPLIER: // 3.5
70			// cardinality: max 1
71			err = setPackageSupplier(pkg, subTriple.Object.ID)
72		case SPDX_ORIGINATOR: // 3.6
73			// cardinality: max 1
74			err = setPackageOriginator(pkg, subTriple.Object.ID)
75		case SPDX_DOWNLOAD_LOCATION: // 3.7
76			// cardinality: exactly 1
77			err = setDocumentLocationFromURI(pkg, subTriple.Object.ID)
78		case SPDX_FILES_ANALYZED: // 3.8
79			// cardinality: max 1
80			err = setFilesAnalyzed(pkg, subTriple.Object.ID)
81		case SPDX_PACKAGE_VERIFICATION_CODE: // 3.9
82			// cardinality: max 1
83			err = parser.setPackageVerificationCode(pkg, subTriple.Object)
84		case SPDX_CHECKSUM: // 3.10
85			// cardinality: min 0
86			err = parser.setPackageChecksum(pkg, subTriple.Object)
87		case DOAP_HOMEPAGE: // 3.11
88			// cardinality: max 1
89			// homepage must be a valid Uri
90			if !isUriValid(subTriple.Object.ID) {
91				return nil, fmt.Errorf("invalid uri %s while parsing doap_homepage in a package", subTriple.Object.ID)
92			}
93			pkg.PackageHomePage = subTriple.Object.ID
94		case SPDX_SOURCE_INFO: // 3.12
95			// cardinality: max 1
96			pkg.PackageSourceInfo = subTriple.Object.ID
97		case SPDX_LICENSE_CONCLUDED: // 3.13
98			// cardinality: exactly 1
99			anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object)
100			if err != nil {
101				return nil, err
102			}
103			pkg.PackageLicenseConcluded = anyLicenseInfo.ToLicenseString()
104		case SPDX_LICENSE_INFO_FROM_FILES: // 3.14
105			// cardinality: min 0
106			pkg.PackageLicenseInfoFromFiles = append(pkg.PackageLicenseInfoFromFiles, getLicenseStringFromURI(subTriple.Object.ID))
107		case SPDX_LICENSE_DECLARED: // 3.15
108			// cardinality: exactly 1
109			anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object)
110			if err != nil {
111				return nil, err
112			}
113			pkg.PackageLicenseDeclared = anyLicenseInfo.ToLicenseString()
114		case SPDX_LICENSE_COMMENTS: // 3.16
115			// cardinality: max 1
116			pkg.PackageLicenseComments = subTriple.Object.ID
117		case SPDX_COPYRIGHT_TEXT: // 3.17
118			// cardinality: exactly 1
119			pkg.PackageCopyrightText = subTriple.Object.ID
120		case SPDX_SUMMARY: // 3.18
121			// cardinality: max 1
122			pkg.PackageSummary = subTriple.Object.ID
123		case SPDX_DESCRIPTION: // 3.19
124			// cardinality: max 1
125			pkg.PackageDescription = subTriple.Object.ID
126		case RDFS_COMMENT: // 3.20
127			// cardinality: max 1
128			pkg.PackageComment = subTriple.Object.ID
129		case SPDX_EXTERNAL_REF: // 3.21
130			// cardinality: min 0
131			externalDocRef, err := parser.getPackageExternalRef(subTriple.Object)
132			if err != nil {
133				return nil, fmt.Errorf("error parsing externalRef of a package: %v", err)
134			}
135			pkg.PackageExternalReferences = append(pkg.PackageExternalReferences, externalDocRef)
136		case SPDX_HAS_FILE: // 3.22
137			// cardinality: min 0
138			file, err := parser.getFileFromNode(subTriple.Object)
139			if err != nil {
140				return nil, fmt.Errorf("error setting file inside a package: %v", err)
141			}
142			parser.setFileToPackage(pkg, file)
143		case SPDX_RELATIONSHIP:
144			// cardinality: min 0
145			err = parser.parseRelationship(subTriple)
146		case SPDX_ATTRIBUTION_TEXT:
147			// cardinality: min 0
148			pkg.PackageAttributionTexts = append(pkg.PackageAttributionTexts, subTriple.Object.ID)
149		case SPDX_ANNOTATION:
150			// cardinality: min 0
151			err = parser.parseAnnotationFromNode(subTriple.Object)
152		default:
153			return nil, fmt.Errorf("unknown predicate id %s while parsing a package", subTriple.Predicate.ID)
154		}
155		if err != nil {
156			return nil, err
157		}
158	}
159
160	if existingPackageIndex != -1 {
161		parser.doc.Packages[existingPackageIndex] = pkg
162	} else {
163		parser.doc.Packages = append(parser.doc.Packages, pkg)
164	}
165
166	return pkg, nil
167}
168
169// parses externalReference found in the package by the associated triple.
170func (parser *rdfParser2_2) getPackageExternalRef(node *gordfParser.Node) (externalDocRef *v2_2.PackageExternalReference, err error) {
171	externalDocRef = &v2_2.PackageExternalReference{}
172	for _, triple := range parser.nodeToTriples(node) {
173		switch triple.Predicate.ID {
174		case SPDX_REFERENCE_CATEGORY:
175			// cardinality: exactly 1
176			switch triple.Object.ID {
177			case SPDX_REFERENCE_CATEGORY_SECURITY:
178				externalDocRef.Category = "SECURITY"
179			case SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER:
180				externalDocRef.Category = "PACKAGE-MANAGER"
181			case SPDX_REFERENCE_CATEGORY_OTHER:
182				externalDocRef.Category = "OTHER"
183			default:
184				return nil, fmt.Errorf("unknown packageManager uri %s", triple.Predicate.ID)
185			}
186		case RDF_TYPE:
187			continue
188		case SPDX_REFERENCE_TYPE:
189			// assumes: the reference type is associated with just the uri and
190			// 			other associated fields are ignored.
191			// other fields include:
192			//		1. contextualExample,
193			//		2. documentation and,
194			//		3. externalReferenceSite
195			externalDocRef.RefType = triple.Object.ID
196		case SPDX_REFERENCE_LOCATOR:
197			// cardinality: exactly 1
198			externalDocRef.Locator = triple.Object.ID
199		case RDFS_COMMENT:
200			// cardinality: max 1
201			externalDocRef.ExternalRefComment = triple.Object.ID
202		default:
203			return nil, fmt.Errorf("unknown package external reference predicate id %s", triple.Predicate.ID)
204		}
205	}
206	return
207}
208
209func (parser *rdfParser2_2) setPackageVerificationCode(pkg *v2_2.Package, node *gordfParser.Node) error {
210	for _, subTriple := range parser.nodeToTriples(node) {
211		switch subTriple.Predicate.ID {
212		case SPDX_PACKAGE_VERIFICATION_CODE_VALUE:
213			// cardinality: exactly 1
214			pkg.PackageVerificationCode.Value = subTriple.Object.ID
215		case SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE:
216			// cardinality: min 0
217			pkg.PackageVerificationCode.ExcludedFiles = append(pkg.PackageVerificationCode.ExcludedFiles, subTriple.Object.ID)
218		case RDF_TYPE:
219			// cardinality: exactly 1
220			continue
221		default:
222			return fmt.Errorf("unparsed predicate %s", subTriple.Predicate.ID)
223		}
224	}
225	return nil
226}
227
228// appends the file to the package and also sets the assocWithPackage for the
229// file to indicate the file is associated with a package
230func (parser *rdfParser2_2) setFileToPackage(pkg *v2_2.Package, file *v2_2.File) {
231	if pkg.Files == nil {
232		pkg.Files = []*v2_2.File{}
233	}
234	pkg.Files = append(pkg.Files, file)
235	parser.assocWithPackage[file.FileSPDXIdentifier] = true
236}
237
238// given a supplierObject, sets the PackageSupplier attribute of the pkg.
239// Args:
240//
241//	value: [NOASSERTION | [Person | Organization]: string]
242func setPackageSupplier(pkg *v2_2.Package, value string) error {
243	value = strings.TrimSpace(value)
244	supplier := &common.Supplier{}
245	if strings.ToUpper(value) == "NOASSERTION" {
246		supplier.Supplier = "NOASSERTION"
247		pkg.PackageSupplier = supplier
248		return nil
249	}
250
251	subKey, subValue, err := ExtractSubs(value, ":")
252	if err != nil {
253		return fmt.Errorf("package supplier must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value)
254	}
255	switch subKey {
256	case "Person", "Organization":
257		supplier.Supplier = subValue
258		supplier.SupplierType = subKey
259	default:
260		return fmt.Errorf("unknown supplier %s", subKey)
261	}
262
263	pkg.PackageSupplier = supplier
264
265	return nil
266}
267
268// given a OriginatorObject, sets the PackageOriginator attribute of the pkg.
269// Args:
270//
271//	value: [NOASSERTION | [Person | Organization]: string]
272func setPackageOriginator(pkg *v2_2.Package, value string) error {
273	value = strings.TrimSpace(value)
274	originator := &common.Originator{}
275	if strings.ToUpper(value) == "NOASSERTION" {
276		originator.Originator = "NOASSERTION"
277		pkg.PackageOriginator = originator
278		return nil
279	}
280
281	subKey, subValue, err := ExtractSubs(value, ":")
282	if err != nil {
283		return fmt.Errorf("package Originator must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value)
284	}
285	switch subKey {
286	case "Person", "Organization":
287		originator.Originator = subValue
288		originator.OriginatorType = subKey
289	default:
290		return fmt.Errorf("unknown Originator %s", subKey)
291	}
292
293	pkg.PackageOriginator = originator
294
295	return nil
296}
297
298// validates the uri and sets the location if it is valid
299func setDocumentLocationFromURI(pkg *v2_2.Package, locationURI string) error {
300	switch locationURI {
301	case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL:
302		pkg.PackageDownloadLocation = "NOASSERTION"
303	case SPDX_NONE_CAPS, SPDX_NONE_SMALL:
304		pkg.PackageDownloadLocation = "NONE"
305	default:
306		if !isUriValid(locationURI) {
307			return fmt.Errorf("%s is not a valid uri", locationURI)
308		}
309		pkg.PackageDownloadLocation = locationURI
310	}
311	return nil
312}
313
314// sets the FilesAnalyzed attribute to the given package
315// boolValue is a string of type "true" or "false"
316func setFilesAnalyzed(pkg *v2_2.Package, boolValue string) (err error) {
317	pkg.IsFilesAnalyzedTagPresent = true
318	pkg.FilesAnalyzed, err = boolFromString(boolValue)
319	return err
320}
321
322func (parser *rdfParser2_2) setPackageChecksum(pkg *v2_2.Package, node *gordfParser.Node) error {
323	checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(node)
324	if err != nil {
325		return fmt.Errorf("error getting checksum algorithm and value from %v", node)
326	}
327	if pkg.PackageChecksums == nil {
328		pkg.PackageChecksums = make([]common.Checksum, 0, 1)
329	}
330	switch checksumAlgorithm {
331	case common.SHA1,
332		common.SHA224,
333		common.SHA256,
334		common.SHA384,
335		common.SHA512,
336		common.MD2,
337		common.MD4,
338		common.MD5,
339		common.MD6:
340		pkg.PackageChecksums = append(pkg.PackageChecksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue})
341	default:
342		return fmt.Errorf("unknown checksumAlgorithm %s while parsing a package", checksumAlgorithm)
343	}
344	return nil
345}
346