예제 #1
0
        public static Bom ToCycloneDX(this SpdxDocument doc)
        {
            var bom = new Bom()
            {
                Metadata = new Metadata
                {
                    Properties = new List <Property>(),
                }
            };

            // document
            bom.Metadata.Properties.AddSpdxElement(PropertyTaxonomy.SPDXID, doc.SPDXID);
            bom.Metadata.Properties.AddSpdxElement(PropertyTaxonomy.DOCUMENT_SPDX_VERSION, doc.SpdxVersion);
            bom.Metadata.Properties.AddSpdxElement(PropertyTaxonomy.COMMENT, doc.Comment);
            bom.Metadata.Properties.AddSpdxElement(PropertyTaxonomy.DOCUMENT_NAME, doc.Name);
            bom.Metadata.Properties.AddSpdxElement(PropertyTaxonomy.DOCUMENT_NAMESPACE, doc.DocumentNamespace);

            // creation info
            if (doc.CreationInfo != null)
            {
                bom.Metadata.Properties.AddSpdxElement(PropertyTaxonomy.CREATION_INFO_COMMENT, doc.CreationInfo.Comment);
                bom.Metadata.Timestamp = doc.CreationInfo.Created;
                bom.AddSpdxCreators(doc.CreationInfo.Creators);
                bom.Metadata.Properties.AddSpdxElement(PropertyTaxonomy.CREATION_INFO_LICENSE_LIST_VERSION, doc.CreationInfo.LicenseListVersion);
            }

            bom.Metadata.Properties.AddSpdxElements <ExternalDocumentRef>(PropertyTaxonomy.DOCUMENT_EXTERNAL_DOCUMENT_REF, doc.ExternalDocumentRefs);
            bom.Metadata.Properties.AddSpdxElements <Annotation>(PropertyTaxonomy.ANNOTATION, doc.Annotations);
            bom.Metadata.Properties.AddSpdxElements(PropertyTaxonomy.DOCUMENT_DESCRIBES, doc.DocumentDescribes);

            bom.AddSpdxPackages(doc);
            bom.AddSpdxFiles(doc.Files);

            return(bom);
        }
예제 #2
0
        public static void AddSpdxPackages(this Bom bom, SpdxDocument doc)
        {
            if (doc.Packages == null || doc.Packages.Count == 0)
            {
                return;
            }
            if (bom.Components == null)
            {
                bom.Components = new List <Component>();
            }
            foreach (var package in doc.Packages)
            {
                var component = new Component
                {
                    Type        = Component.Classification.Library,
                    Name        = package.Name,
                    Version     = package.VersionInfo,
                    Copyright   = package.CopyrightText,
                    Description = package.Description,
                    Properties  = new List <Property>(),
                };
                component.Properties.AddSpdxElement(PropertyTaxonomy.SPDXID, package.SPDXID);
                component.Properties.AddSpdxElements <Annotation>(PropertyTaxonomy.ANNOTATION, package.Annotations);
                component.Properties.AddSpdxElement(PropertyTaxonomy.FILES_ANALYZED, package.FilesAnalyzed);
                component.Properties.AddSpdxElement(PropertyTaxonomy.LICENSE_COMMENTS, package.LicenseComments);
                component.Properties.AddSpdxElement(PropertyTaxonomy.LICENSE_CONCLUDED, package.LicenseConcluded);
                component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_FILENAME, package.PackageFileName);
                component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_VERIFICATION_CODE_VALUE, package.PackageVerificationCode?.PackageVerificationCodeValue);
                component.Properties.AddSpdxElements(PropertyTaxonomy.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE, package.PackageVerificationCode?.PackageVerificationCodeExcludedFiles);
                component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_SOURCE_INFO, package.SourceInfo);
                component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_SUMMARY, package.Summary);
                component.Properties.AddSpdxElement(PropertyTaxonomy.COMMENT, package.Comment);

                if (package.LicenseInfoFromFiles != null && package.LicenseInfoFromFiles.Count > 0)
                {
                    if (component.Evidence == null)
                    {
                        component.Evidence = new Evidence();
                    }
                    if (component.Evidence.Licenses == null)
                    {
                        component.Evidence.Licenses = new List <LicenseChoice>();
                    }
                    foreach (var licenseInfo in package.LicenseInfoFromFiles)
                    {
                        if (licenseInfo.StartsWith("LicenseRef-") &&
                            doc.HasExtractedLicensingInfos != null &&
                            doc.HasExtractedLicensingInfos.Exists(l => l.LicenseId == licenseInfo))
                        {
                            var license = doc.HasExtractedLicensingInfos.First(l => l.LicenseId == licenseInfo);
                            component.Evidence.Licenses.Add(new LicenseChoice
                            {
                                License = new License
                                {
                                    Name = license.Name,
                                    Text = new AttachedText
                                    {
                                        ContentType = "text/plain",
                                        Encoding    = "base64",
                                        Content     = license.ExtractedText.Base64Encode(),
                                    },
                                    Url = license.SeeAlsos?.FirstOrDefault(),
                                }
                            });
                        }
                        else if (licenseInfo.StartsWith("LicenseRef-") || licenseInfo.StartsWith("DocumentRef-"))
                        {
                            component.Evidence.Licenses.Add(new LicenseChoice
                            {
                                License = new License
                                {
                                    Name = licenseInfo,
                                }
                            });
                        }
                        else if (licenseInfo == "NONE" || licenseInfo == "NOASSERTION")
                        {
                            // don't do anything for this case
                        }
                        else
                        {
                            component.Evidence.Licenses.Add(new LicenseChoice
                            {
                                License = new License
                                {
                                    Id = licenseInfo,
                                }
                            });
                        }
                    }
                }

                if (package.LicenseDeclared == "NOASSERTION")
                {
                    component.Properties.AddSpdxElement(PropertyTaxonomy.LICENSE_DECLARED, package.LicenseDeclared);
                }
                else if (package.LicenseDeclared == "NONE")
                {
                    component.Licenses = new List <LicenseChoice>();
                }
                else
                {
                    component.Licenses = new List <LicenseChoice> {
                        new LicenseChoice {
                            Expression = package.LicenseDeclared
                        }
                    };
                }

                if (package.Originator != null)
                {
                    if (package.Originator == "NOASSERTION")
                    {
                        component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_ORIGINATOR, package.Originator);
                    }
                    else
                    {
                        var originatorRegex = new Regex(@"(Person|Organization): (?<name>.*) \((?<email>.*)\)");
                        var originatorMatch = originatorRegex.Match(package.Originator);
                        if (originatorMatch.Success)
                        {
                            component.Author = originatorMatch.Groups["name"].ToString();
                            if (package.Originator.ToLowerInvariant().StartsWith("organization:"))
                            {
                                component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_ORIGINATOR_ORGANIZATION, component.Author);
                            }
                            component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_ORIGINATOR_EMAIL, originatorMatch.Groups["email"].ToString());
                        }
                    }
                }

                if (package.Supplier != null)
                {
                    if (package.Supplier == "NOASSERTION")
                    {
                        component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_SUPPLIER, package.Supplier);
                    }
                    else
                    {
                        var supplierRegex = new Regex(@"(Person|Organization): (?<name>.*) \((?<email>.*)\)");
                        var supplierMatch = supplierRegex.Match(package.Supplier);
                        if (supplierMatch.Success)
                        {
                            component.Supplier = new OrganizationalEntity
                            {
                                Name    = supplierMatch.Groups["name"].ToString(),
                                Contact = new List <OrganizationalContact>
                                {
                                    new OrganizationalContact
                                    {
                                        Email = supplierMatch.Groups["email"].ToString()
                                    }
                                },
                            };
                            if (package.Supplier.ToLowerInvariant().StartsWith("organization:"))
                            {
                                component.Properties.AddSpdxElement(PropertyTaxonomy.PACKAGE_SUPPLIER_ORGANIZATION, component.Supplier.Name);
                            }
                        }
                    }
                }

                component.AddSpdxAttributionTexts(package.AttributionTexts);
                component.AddSpdxChecksums(package.Checksums);
                component.AddSpdxExternalRefs(package.ExternalRefs);

                if (package.DownloadLocation != null)
                {
                    if (component.ExternalReferences == null)
                    {
                        component.ExternalReferences = new List <ExternalReference>();
                    }
                    component.ExternalReferences.Add(new ExternalReference
                    {
                        Type = ExternalReference.ExternalReferenceType.Distribution,
                        Url  = package.DownloadLocation,
                    });
                    component.Properties.AddSpdxElement(PropertyTaxonomy.DOWNLOAD_LOCATION, package.DownloadLocation);
                }

                if (package.Homepage != null)
                {
                    if (component.ExternalReferences == null)
                    {
                        component.ExternalReferences = new List <ExternalReference>();
                    }
                    component.ExternalReferences.Add(new ExternalReference
                    {
                        Type = ExternalReference.ExternalReferenceType.Website,
                        Url  = package.Homepage,
                    });
                    component.Properties.AddSpdxElement(PropertyTaxonomy.HOMEPAGE, package.Homepage);
                }

                //TODO HasFile

                bom.Components.Add(component);
            }
        }
예제 #3
0
        public static SpdxDocument ToSpdx(this Bom bom)
        {
            var doc = new SpdxDocument()
            {
                CreationInfo = new CreationInfo(),
            };

            // document
            doc.SPDXID  = bom?.Metadata?.Properties?.GetSpdxElement(PropertyTaxonomy.SPDXID) ?? "SPDXRef-DOCUMENT";
            doc.Comment = bom?.Metadata?.Properties?.GetSpdxElement(PropertyTaxonomy.COMMENT);

            doc.Name = bom?.Metadata?.Properties?.GetSpdxElement(PropertyTaxonomy.DOCUMENT_NAME);
            if (doc.Name == null)
            {
                if (bom.Metadata?.Component?.Name != null)
                {
                    doc.Name = bom.Metadata.Component.Name;
                    if (bom.Metadata.Component.Version != null)
                    {
                        doc.Name += $"-{bom.Metadata.Component.Version}";
                    }
                    if (bom.Metadata.Component.Group != null)
                    {
                        doc.Name = $"{bom.Metadata.Component.Group} {doc.Name}";
                    }
                }
                else
                {
                    doc.Name = "CycloneDX BOM";
                }
            }

            doc.DocumentNamespace = bom?.Metadata?.Properties?.GetSpdxElement(PropertyTaxonomy.DOCUMENT_NAMESPACE);
            if (doc.DocumentNamespace == null)
            {
                string docId;
                if (string.IsNullOrEmpty(bom.SerialNumber))
                {
                    docId = Guid.NewGuid().ToString();
                }
                else if (bom.SerialNumber.StartsWith("urn:uuid:", StringComparison.InvariantCulture))
                {
                    docId = bom.SerialNumber.Remove(0, 9);
                }
                else
                {
                    docId = bom.SerialNumber;
                }
                doc.DocumentNamespace = $"http://spdx.org/spdxdocs/{doc.Name}-{docId}";
            }

            // creation info
            doc.CreationInfo.Comment            = bom.Metadata?.Properties?.GetSpdxElement(PropertyTaxonomy.CREATION_INFO_COMMENT) ?? "This SPDX document has been converted from CycloneDX format.";
            doc.CreationInfo.Created            = bom.Metadata?.Timestamp != null ? bom.Metadata.Timestamp.Value : DateTime.UtcNow;
            doc.CreationInfo.Creators           = bom.GetSpdxCreators();
            doc.CreationInfo.LicenseListVersion = bom.Metadata?.Properties?.GetSpdxElement(PropertyTaxonomy.CREATION_INFO_LICENSE_LIST_VERSION);

            doc.ExternalDocumentRefs = bom.Metadata?.Properties?.GetSpdxElements <ExternalDocumentRef>(PropertyTaxonomy.DOCUMENT_EXTERNAL_DOCUMENT_REF);
            doc.Annotations          = bom.Metadata?.Properties?.GetSpdxElements <Annotation>(PropertyTaxonomy.ANNOTATION);
            doc.DocumentDescribes    = bom.Metadata?.Properties?.GetSpdxElements(PropertyTaxonomy.DOCUMENT_DESCRIBES);

            doc.AddCycloneDXComponents(bom);
            doc.Files = bom.GetSpdxFiles();
            //TODO HasExtractedLicensingInfos
            //TODO relationships, assemblies, dependency graph, etc

            return(doc);
        }
예제 #4
0
        public static void AddCycloneDXComponents(this SpdxDocument doc, Bom bom)
        {
            if (bom.Components == null || bom.Components.Count == 0)
            {
                return;
            }
            doc.Packages = doc.Packages ?? new List <Package>();
            foreach (var component in bom.Components.Where(c => IsSpdxPackageSupportedComponentType(c)))
            {
                var package = new Package
                {
                    Name          = component.Name,
                    VersionInfo   = component.Version,
                    Description   = component.Description,
                    CopyrightText = component.Copyright ?? "NOASSERTION",
                };
                package.SPDXID = component.Properties?.GetSpdxElement(PropertyTaxonomy.SPDXID);
                if (package.SPDXID == null)
                {
                    if (component.BomRef == null)
                    {
                        package.SPDXID = "SPDXRef-Package-" + (doc.Packages.Count + 1).ToString();
                    }
                    else
                    {
                        package.SPDXID = $"SPDXRef-{component.BomRef}";
                    }
                }
                package.Annotations      = component.Properties?.GetSpdxElements <Annotation>(PropertyTaxonomy.ANNOTATION);
                package.FilesAnalyzed    = component.Properties?.GetSpdxElement <bool?>(PropertyTaxonomy.FILES_ANALYZED);
                package.LicenseComments  = component.Properties?.GetSpdxElement(PropertyTaxonomy.LICENSE_COMMENTS);
                package.LicenseConcluded = component.Properties?.GetSpdxElement(PropertyTaxonomy.LICENSE_CONCLUDED) ?? "NOASSERTION";
                package.PackageFileName  = component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_FILENAME);
                var packageVerificationCode = component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_VERIFICATION_CODE_VALUE);
                if (packageVerificationCode != null)
                {
                    package.PackageVerificationCode = new PackageVerificationCode
                    {
                        PackageVerificationCodeValue         = packageVerificationCode,
                        PackageVerificationCodeExcludedFiles = component.Properties?.GetSpdxElements(PropertyTaxonomy.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE),
                    };
                }
                package.SourceInfo = component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_SOURCE_INFO);
                package.Summary    = component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_SUMMARY);
                package.Comment    = component.Properties?.GetSpdxElement(PropertyTaxonomy.COMMENT);

                // LicenseInfoFromFiles
                if (component.Evidence?.Licenses != null && component.Evidence.Licenses.Count > 0)
                {
                    if (package.LicenseInfoFromFiles == null)
                    {
                        package.LicenseInfoFromFiles = new List <string>();
                    }
                    foreach (var license in component.Evidence.Licenses)
                    {
                        if (license.Expression != null)
                        {
                            // TODO revisit this after some sleep
                            // at first glance it doesn't look like expressions are handled in ExtractedLicensingInfo
                        }
                        else if (license.License != null)
                        {
                            if (license.License.Id != null)
                            {
                                package.LicenseInfoFromFiles.Add(license.License.Id);
                            }
                            else
                            {
                                if (doc.HasExtractedLicensingInfos == null)
                                {
                                    doc.HasExtractedLicensingInfos = new List <ExtractedLicensingInfo>();
                                }
                                var extLicInfo = new ExtractedLicensingInfo
                                {
                                    LicenseId = $"LicenseRef-{doc.HasExtractedLicensingInfos.Count + 1}",
                                    Name      = license.License.Name,
                                    SeeAlsos  = license.License.Url == null ? null : new List <string> {
                                        license.License.Url
                                    },
                                    ExtractedText = license.License.Text?.Content?.Base64Decode(),
                                };
                                doc.HasExtractedLicensingInfos.Add(extLicInfo);
                                package.LicenseInfoFromFiles.Add(extLicInfo.LicenseId);
                            }
                        }
                    }
                }
                if (package.LicenseInfoFromFiles == null || package.LicenseInfoFromFiles.Count == 0)
                {
                    package.LicenseInfoFromFiles = new List <string> {
                        "NOASSERTION"
                    };
                }

                // LicenseDeclared
                package.LicenseDeclared = component.Properties?.GetSpdxElement(PropertyTaxonomy.LICENSE_DECLARED);
                if (component.Licenses == null || component.Licenses.Count == 0)
                {
                    package.LicenseDeclared = "NOASSERTION";
                }
                else
                {
                    if (component.Licenses.Count == 1)
                    {
                        package.LicenseDeclared = component.Licenses.First().Expression ?? component.Licenses.First().License.Id;
                    }
                    else
                    {
                        package.LicenseDeclared = "NOASSERTION";
                    }
                }

                // Package Originator
                package.Originator = component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_ORIGINATOR) ?? "NOASSERTION";
                if (component.Author != null)
                {
                    if (component.Author == component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_ORIGINATOR_ORGANIZATION))
                    {
                        package.Originator = $"Organization: {component.Author} ({component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_ORIGINATOR_EMAIL) ?? ""})";
                    }
                    else
                    {
                        package.Originator = $"Person: {component.Author} ({component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_ORIGINATOR_EMAIL) ?? ""})";
                    }
                }

                package.Supplier = component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_SUPPLIER) ?? "NOASSERTION";
                if (component.Supplier != null)
                {
                    var supplierEmails = component.Supplier.Contact.Where(c => c.Email != null).ToList();
                    var supplierEmail  = supplierEmails.Count > 0 ? supplierEmails.First().Email : "";
                    if (component.Supplier.Name == component.Properties?.GetSpdxElement(PropertyTaxonomy.PACKAGE_SUPPLIER_ORGANIZATION))
                    {
                        package.Supplier = $"Organization: {component.Supplier.Name} ({supplierEmail})";
                    }
                    else
                    {
                        package.Supplier = $"Person: {component.Supplier.Name} ({supplierEmail})";
                    }
                }

                package.AttributionTexts = component.GetSpdxAttributionTexts();

                package.Checksums    = component.GetSpdxChecksums();
                package.ExternalRefs = component.GetSpdxExternalRefs();

                package.DownloadLocation = component.Properties?.GetSpdxElement(PropertyTaxonomy.DOWNLOAD_LOCATION) ?? "NOASSERTION";
                package.Homepage         = component.Properties?.GetSpdxElement(PropertyTaxonomy.HOMEPAGE) ?? "NOASSERTION";

                //TODO HasFile

                doc.Packages.Add(package);
            }
        }