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