1// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 2 3package parser2v3 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_3" 12) 13 14func (parser *rdfParser2_3) getPackageFromNode(packageNode *gordfParser.Node) (pkg *v2_3.Package, err error) { 15 pkg = &v2_3.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_3.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: // 7.1 61 // cardinality: exactly 1 62 pkg.PackageName = subTriple.Object.ID 63 case SPDX_VERSION_INFO: // 7.3 64 // cardinality: max 1 65 pkg.PackageVersion = subTriple.Object.ID 66 case SPDX_PACKAGE_FILE_NAME: // 7.4 67 // cardinality: max 1 68 pkg.PackageFileName = subTriple.Object.ID 69 case SPDX_SUPPLIER: // 7.5 70 // cardinality: max 1 71 err = setPackageSupplier(pkg, subTriple.Object.ID) 72 case SPDX_ORIGINATOR: // 7.6 73 // cardinality: max 1 74 err = setPackageOriginator(pkg, subTriple.Object.ID) 75 case SPDX_DOWNLOAD_LOCATION: // 7.7 76 // cardinality: exactly 1 77 err = setDocumentLocationFromURI(pkg, subTriple.Object.ID) 78 case SPDX_FILES_ANALYZED: // 7.8 79 // cardinality: max 1 80 err = setFilesAnalyzed(pkg, subTriple.Object.ID) 81 case SPDX_PACKAGE_VERIFICATION_CODE: // 7.9 82 // cardinality: max 1 83 err = parser.setPackageVerificationCode(pkg, subTriple.Object) 84 case SPDX_CHECKSUM: // 7.10 85 // cardinality: min 0 86 err = parser.setPackageChecksum(pkg, subTriple.Object) 87 case DOAP_HOMEPAGE: // 7.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: // 7.12 95 // cardinality: max 1 96 pkg.PackageSourceInfo = subTriple.Object.ID 97 case SPDX_LICENSE_CONCLUDED: // 7.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: // 7.14 105 // cardinality: min 0 106 pkg.PackageLicenseInfoFromFiles = append(pkg.PackageLicenseInfoFromFiles, getLicenseStringFromURI(subTriple.Object.ID)) 107 case SPDX_LICENSE_DECLARED: // 7.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: // 7.16 115 // cardinality: max 1 116 pkg.PackageLicenseComments = subTriple.Object.ID 117 case SPDX_COPYRIGHT_TEXT: // 7.17 118 // cardinality: exactly 1 119 pkg.PackageCopyrightText = subTriple.Object.ID 120 case SPDX_SUMMARY: // 7.18 121 // cardinality: max 1 122 pkg.PackageSummary = subTriple.Object.ID 123 case SPDX_DESCRIPTION: // 7.19 124 // cardinality: max 1 125 pkg.PackageDescription = subTriple.Object.ID 126 case RDFS_COMMENT: // 7.20 127 // cardinality: max 1 128 pkg.PackageComment = subTriple.Object.ID 129 case SPDX_EXTERNAL_REF: // 7.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: // 7.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_PRIMARY_PACKAGE_PURPOSE: // 7.24 144 // cardinality: exactly 1 145 pkg.PrimaryPackagePurpose = getPrimaryPackagePurpose(subTriple.Object.ID) 146 case SPDX_RELEASE_DATE: // 7.25 147 // cardinality: exactly 1 148 pkg.ReleaseDate = subTriple.Object.ID 149 case SPDX_BUILT_DATE: // 7.26 150 // cardinality: exactly 1 151 pkg.BuiltDate = subTriple.Object.ID 152 case SPDX_VALID_UNTIL_DATE: // 7.27 153 // cardinality: exactly 1 154 pkg.ValidUntilDate = subTriple.Object.ID 155 case SPDX_RELATIONSHIP: 156 // cardinality: min 0 157 err = parser.parseRelationship(subTriple) 158 case SPDX_ATTRIBUTION_TEXT: 159 // cardinality: min 0 160 pkg.PackageAttributionTexts = append(pkg.PackageAttributionTexts, subTriple.Object.ID) 161 case SPDX_ANNOTATION: 162 // cardinality: min 0 163 err = parser.parseAnnotationFromNode(subTriple.Object) 164 default: 165 return nil, fmt.Errorf("unknown predicate id %s while parsing a package", subTriple.Predicate.ID) 166 } 167 if err != nil { 168 return nil, err 169 } 170 } 171 172 if existingPackageIndex != -1 { 173 parser.doc.Packages[existingPackageIndex] = pkg 174 } else { 175 parser.doc.Packages = append(parser.doc.Packages, pkg) 176 } 177 178 return pkg, nil 179} 180 181// parses externalReference found in the package by the associated triple. 182func (parser *rdfParser2_3) getPackageExternalRef(node *gordfParser.Node) (externalDocRef *v2_3.PackageExternalReference, err error) { 183 externalDocRef = &v2_3.PackageExternalReference{} 184 for _, triple := range parser.nodeToTriples(node) { 185 switch triple.Predicate.ID { 186 case SPDX_REFERENCE_CATEGORY: 187 // cardinality: exactly 1 188 switch triple.Object.ID { 189 case SPDX_REFERENCE_CATEGORY_SECURITY: 190 externalDocRef.Category = "SECURITY" 191 case SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER: 192 externalDocRef.Category = "PACKAGE-MANAGER" 193 case SPDX_REFERENCE_CATEGORY_OTHER: 194 externalDocRef.Category = "OTHER" 195 default: 196 return nil, fmt.Errorf("unknown packageManager uri %s", triple.Predicate.ID) 197 } 198 case RDF_TYPE: 199 continue 200 case SPDX_REFERENCE_TYPE: 201 // assumes: the reference type is associated with just the uri and 202 // other associated fields are ignored. 203 // other fields include: 204 // 1. contextualExample, 205 // 2. documentation and, 206 // 3. externalReferenceSite 207 externalDocRef.RefType = triple.Object.ID 208 case SPDX_REFERENCE_LOCATOR: 209 // cardinality: exactly 1 210 externalDocRef.Locator = triple.Object.ID 211 case RDFS_COMMENT: 212 // cardinality: max 1 213 externalDocRef.ExternalRefComment = triple.Object.ID 214 default: 215 return nil, fmt.Errorf("unknown package external reference predicate id %s", triple.Predicate.ID) 216 } 217 } 218 return 219} 220 221func getPrimaryPackagePurpose(purpose string) string { 222 value := strings.ReplaceAll(purpose, "packagePurpose_", "") 223 value = strings.ReplaceAll(value, "_", "-") 224 value = strings.ToUpper(value) 225 switch value { 226 case "APPLICATION", "FRAMEWORK", "LIBRARY", "CONTAINER", "OPERATING-SYSTEM", "DEVICE", "FIRMWARE", "SOURCE", "ARCHIVE", "FILE", "INSTALL", "OTHER": 227 return value 228 } 229 // invalid value 230 return "" 231} 232 233func (parser *rdfParser2_3) setPackageVerificationCode(pkg *v2_3.Package, node *gordfParser.Node) error { 234 if pkg.PackageVerificationCode == nil { 235 pkg.PackageVerificationCode = &common.PackageVerificationCode{} 236 } 237 for _, subTriple := range parser.nodeToTriples(node) { 238 switch subTriple.Predicate.ID { 239 case SPDX_PACKAGE_VERIFICATION_CODE_VALUE: 240 // cardinality: exactly 1 241 pkg.PackageVerificationCode.Value = subTriple.Object.ID 242 case SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE: 243 // cardinality: min 0 244 pkg.PackageVerificationCode.ExcludedFiles = append(pkg.PackageVerificationCode.ExcludedFiles, subTriple.Object.ID) 245 case RDF_TYPE: 246 // cardinality: exactly 1 247 continue 248 default: 249 return fmt.Errorf("unparsed predicate %s", subTriple.Predicate.ID) 250 } 251 } 252 return nil 253} 254 255// appends the file to the package and also sets the assocWithPackage for the 256// file to indicate the file is associated with a package 257func (parser *rdfParser2_3) setFileToPackage(pkg *v2_3.Package, file *v2_3.File) { 258 if pkg.Files == nil { 259 pkg.Files = []*v2_3.File{} 260 } 261 pkg.Files = append(pkg.Files, file) 262 parser.assocWithPackage[file.FileSPDXIdentifier] = true 263} 264 265// given a supplierObject, sets the PackageSupplier attribute of the pkg. 266// Args: 267// 268// value: [NOASSERTION | [Person | Organization]: string] 269func setPackageSupplier(pkg *v2_3.Package, value string) error { 270 value = strings.TrimSpace(value) 271 supplier := &common.Supplier{} 272 if strings.ToUpper(value) == "NOASSERTION" { 273 supplier.Supplier = "NOASSERTION" 274 pkg.PackageSupplier = supplier 275 return nil 276 } 277 278 subKey, subValue, err := ExtractSubs(value, ":") 279 if err != nil { 280 return fmt.Errorf("package supplier must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value) 281 } 282 switch subKey { 283 case "Person", "Organization": 284 supplier.Supplier = subValue 285 supplier.SupplierType = subKey 286 default: 287 return fmt.Errorf("unknown supplier %s", subKey) 288 } 289 290 pkg.PackageSupplier = supplier 291 292 return nil 293} 294 295// given a OriginatorObject, sets the PackageOriginator attribute of the pkg. 296// Args: 297// 298// value: [NOASSERTION | [Person | Organization]: string] 299func setPackageOriginator(pkg *v2_3.Package, value string) error { 300 value = strings.TrimSpace(value) 301 originator := &common.Originator{} 302 if strings.ToUpper(value) == "NOASSERTION" { 303 originator.Originator = "NOASSERTION" 304 pkg.PackageOriginator = originator 305 return nil 306 } 307 308 subKey, subValue, err := ExtractSubs(value, ":") 309 if err != nil { 310 return fmt.Errorf("package Originator must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value) 311 } 312 switch subKey { 313 case "Person", "Organization": 314 originator.Originator = subValue 315 originator.OriginatorType = subKey 316 default: 317 return fmt.Errorf("unknown Originator %s", subKey) 318 } 319 320 pkg.PackageOriginator = originator 321 322 return nil 323} 324 325// validates the uri and sets the location if it is valid 326func setDocumentLocationFromURI(pkg *v2_3.Package, locationURI string) error { 327 switch locationURI { 328 case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL: 329 pkg.PackageDownloadLocation = "NOASSERTION" 330 case SPDX_NONE_CAPS, SPDX_NONE_SMALL: 331 pkg.PackageDownloadLocation = "NONE" 332 default: 333 if !isUriValid(locationURI) { 334 return fmt.Errorf("%s is not a valid uri", locationURI) 335 } 336 pkg.PackageDownloadLocation = locationURI 337 } 338 return nil 339} 340 341// sets the FilesAnalyzed attribute to the given package 342// boolValue is a string of type "true" or "false" 343func setFilesAnalyzed(pkg *v2_3.Package, boolValue string) (err error) { 344 pkg.IsFilesAnalyzedTagPresent = true 345 pkg.FilesAnalyzed, err = boolFromString(boolValue) 346 return err 347} 348 349func (parser *rdfParser2_3) setPackageChecksum(pkg *v2_3.Package, node *gordfParser.Node) error { 350 checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(node) 351 if err != nil { 352 return fmt.Errorf("error getting checksum algorithm and value from %v", node) 353 } 354 if pkg.PackageChecksums == nil { 355 pkg.PackageChecksums = make([]common.Checksum, 0, 1) 356 } 357 switch checksumAlgorithm { 358 case common.SHA1, 359 common.SHA224, 360 common.SHA256, 361 common.SHA384, 362 common.SHA512, 363 common.MD2, 364 common.MD4, 365 common.MD5, 366 common.MD6, 367 common.SHA3_256, 368 common.SHA3_384, 369 common.SHA3_512, 370 common.BLAKE2b_256, 371 common.BLAKE2b_384, 372 common.BLAKE2b_512, 373 common.BLAKE3, 374 common.ADLER32: 375 pkg.PackageChecksums = append(pkg.PackageChecksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue}) 376 default: 377 return fmt.Errorf("unknown checksumAlgorithm %s while parsing a package", checksumAlgorithm) 378 } 379 return nil 380} 381