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); }
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); } }
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); }
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); } }