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