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