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