github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/formats/common/cyclonedxhelpers/external_references.go (about)

     1  package cyclonedxhelpers
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/CycloneDX/cyclonedx-go"
     8  	gosbomFile "github.com/nextlinux/gosbom/gosbom/file"
     9  	"github.com/nextlinux/gosbom/gosbom/pkg"
    10  )
    11  
    12  //nolint:gocognit
    13  func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference {
    14  	var refs []cyclonedx.ExternalReference
    15  	if hasMetadata(p) {
    16  		switch metadata := p.Metadata.(type) {
    17  		case pkg.ApkMetadata:
    18  			if metadata.URL != "" {
    19  				refs = append(refs, cyclonedx.ExternalReference{
    20  					URL:  metadata.URL,
    21  					Type: cyclonedx.ERTypeDistribution,
    22  				})
    23  			}
    24  		case pkg.CargoPackageMetadata:
    25  			if metadata.Source != "" {
    26  				refs = append(refs, cyclonedx.ExternalReference{
    27  					URL:  metadata.Source,
    28  					Type: cyclonedx.ERTypeDistribution,
    29  				})
    30  			}
    31  		case pkg.NpmPackageJSONMetadata:
    32  			if metadata.URL != "" {
    33  				refs = append(refs, cyclonedx.ExternalReference{
    34  					URL:  metadata.URL,
    35  					Type: cyclonedx.ERTypeDistribution,
    36  				})
    37  			}
    38  			if metadata.Homepage != "" {
    39  				refs = append(refs, cyclonedx.ExternalReference{
    40  					URL:  metadata.Homepage,
    41  					Type: cyclonedx.ERTypeWebsite,
    42  				})
    43  			}
    44  		case pkg.GemMetadata:
    45  			if metadata.Homepage != "" {
    46  				refs = append(refs, cyclonedx.ExternalReference{
    47  					URL:  metadata.Homepage,
    48  					Type: cyclonedx.ERTypeWebsite,
    49  				})
    50  			}
    51  		case pkg.JavaMetadata:
    52  			if len(metadata.ArchiveDigests) > 0 {
    53  				for _, digest := range metadata.ArchiveDigests {
    54  					refs = append(refs, cyclonedx.ExternalReference{
    55  						URL:  "",
    56  						Type: cyclonedx.ERTypeBuildMeta,
    57  						Hashes: &[]cyclonedx.Hash{{
    58  							Algorithm: toCycloneDXAlgorithm(digest.Algorithm),
    59  							Value:     digest.Value,
    60  						}},
    61  					})
    62  				}
    63  			}
    64  		case pkg.PythonPackageMetadata:
    65  			if metadata.DirectURLOrigin != nil && metadata.DirectURLOrigin.URL != "" {
    66  				ref := cyclonedx.ExternalReference{
    67  					URL:  metadata.DirectURLOrigin.URL,
    68  					Type: cyclonedx.ERTypeVCS,
    69  				}
    70  				if metadata.DirectURLOrigin.CommitID != "" {
    71  					ref.Comment = fmt.Sprintf("commit: %s", metadata.DirectURLOrigin.CommitID)
    72  				}
    73  				refs = append(refs, ref)
    74  			}
    75  		}
    76  	}
    77  	if len(refs) > 0 {
    78  		return &refs
    79  	}
    80  	return nil
    81  }
    82  
    83  // supported algorithm in cycloneDX as of 1.4
    84  // "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512",
    85  // "SHA3-256", "SHA3-384", "SHA3-512", "BLAKE2b-256", "BLAKE2b-384", "BLAKE2b-512", "BLAKE3"
    86  // gosbom supported digests: cmd/gosbom/cli/eventloop/tasks.go
    87  // MD5, SHA1, SHA256
    88  func toCycloneDXAlgorithm(algorithm string) cyclonedx.HashAlgorithm {
    89  	validMap := map[string]cyclonedx.HashAlgorithm{
    90  		"sha1":   cyclonedx.HashAlgorithm("SHA-1"),
    91  		"md5":    cyclonedx.HashAlgorithm("MD5"),
    92  		"sha256": cyclonedx.HashAlgorithm("SHA-256"),
    93  	}
    94  
    95  	return validMap[algorithm]
    96  }
    97  
    98  func decodeExternalReferences(c *cyclonedx.Component, metadata interface{}) {
    99  	if c.ExternalReferences == nil {
   100  		return
   101  	}
   102  	switch meta := metadata.(type) {
   103  	case *pkg.ApkMetadata:
   104  		meta.URL = refURL(c, cyclonedx.ERTypeDistribution)
   105  	case *pkg.CargoPackageMetadata:
   106  		meta.Source = refURL(c, cyclonedx.ERTypeDistribution)
   107  	case *pkg.NpmPackageJSONMetadata:
   108  		meta.URL = refURL(c, cyclonedx.ERTypeDistribution)
   109  		meta.Homepage = refURL(c, cyclonedx.ERTypeWebsite)
   110  	case *pkg.GemMetadata:
   111  		meta.Homepage = refURL(c, cyclonedx.ERTypeWebsite)
   112  	case *pkg.JavaMetadata:
   113  		var digests []gosbomFile.Digest
   114  		if ref := findExternalRef(c, cyclonedx.ERTypeBuildMeta); ref != nil {
   115  			if ref.Hashes != nil {
   116  				for _, hash := range *ref.Hashes {
   117  					digests = append(digests, gosbomFile.Digest{
   118  						Algorithm: gosbomFile.CleanDigestAlgorithmName(string(hash.Algorithm)),
   119  						Value:     hash.Value,
   120  					})
   121  				}
   122  			}
   123  		}
   124  
   125  		meta.ArchiveDigests = digests
   126  	case *pkg.PythonPackageMetadata:
   127  		if meta.DirectURLOrigin == nil {
   128  			meta.DirectURLOrigin = &pkg.PythonDirectURLOriginInfo{}
   129  		}
   130  		meta.DirectURLOrigin.URL = refURL(c, cyclonedx.ERTypeVCS)
   131  		meta.DirectURLOrigin.CommitID = strings.TrimPrefix(refComment(c, cyclonedx.ERTypeVCS), "commit: ")
   132  	}
   133  }
   134  
   135  func findExternalRef(c *cyclonedx.Component, typ cyclonedx.ExternalReferenceType) *cyclonedx.ExternalReference {
   136  	if c.ExternalReferences != nil {
   137  		for _, r := range *c.ExternalReferences {
   138  			if r.Type == typ {
   139  				return &r
   140  			}
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  func refURL(c *cyclonedx.Component, typ cyclonedx.ExternalReferenceType) string {
   147  	if r := findExternalRef(c, typ); r != nil {
   148  		return r.URL
   149  	}
   150  	return ""
   151  }
   152  
   153  func refComment(c *cyclonedx.Component, typ cyclonedx.ExternalReferenceType) string {
   154  	if r := findExternalRef(c, typ); r != nil {
   155  		return r.Comment
   156  	}
   157  	return ""
   158  }