github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/common/spdxhelpers/license.go (about)

     1  package spdxhelpers
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/anchore/syft/syft/license"
     9  	"github.com/anchore/syft/syft/pkg"
    10  	"github.com/lineaje-labs/syft/internal/spdxlicense"
    11  )
    12  
    13  func License(p pkg.Package) (concluded, declared string) {
    14  	// source: https://spdx.github.io/spdx-spec/3-package-information/#313-concluded-license
    15  	// The options to populate this field are limited to:
    16  	// A valid SPDX License Expression as defined in Appendix IV;
    17  	// NONE, if the SPDX file creator concludes there is no license available for this package; or
    18  	// NOASSERTION if:
    19  	//   (i) the SPDX file creator has attempted to but cannot reach a reasonable objective determination;
    20  	//   (ii) the SPDX file creator has made no attempt to determine this field; or
    21  	//   (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so).
    22  
    23  	if p.Licenses.Empty() {
    24  		return NOASSERTION, NOASSERTION
    25  	}
    26  
    27  	// take all licenses and assume an AND expression;
    28  	// for information about license expressions see:
    29  	// https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
    30  	pc, pd := parseLicenses(p.Licenses.ToSlice())
    31  
    32  	return joinLicenses(pc), joinLicenses(pd)
    33  }
    34  
    35  func joinLicenses(licenses []spdxLicense) string {
    36  	if len(licenses) == 0 {
    37  		return NOASSERTION
    38  	}
    39  
    40  	var newLicenses []string
    41  
    42  	for _, l := range licenses {
    43  		v := l.id
    44  		// check if license does not start or end with parens
    45  		if !strings.HasPrefix(v, "(") && !strings.HasSuffix(v, ")") {
    46  			// if license contains AND, OR, or WITH, then wrap in parens
    47  			if strings.Contains(v, " AND ") ||
    48  				strings.Contains(v, " OR ") ||
    49  				strings.Contains(v, " WITH ") {
    50  				newLicenses = append(newLicenses, "("+v+")")
    51  				continue
    52  			}
    53  		}
    54  		newLicenses = append(newLicenses, v)
    55  	}
    56  
    57  	return strings.Join(newLicenses, " AND ")
    58  }
    59  
    60  type spdxLicense struct {
    61  	id    string
    62  	value string
    63  }
    64  
    65  func parseLicenses(raw []pkg.License) (concluded, declared []spdxLicense) {
    66  	for _, l := range raw {
    67  		if l.Value == "" {
    68  			continue
    69  		}
    70  
    71  		candidate := spdxLicense{}
    72  		if l.SPDXExpression != "" {
    73  			candidate.id = l.SPDXExpression
    74  		} else {
    75  			// we did not find a valid SPDX license ID so treat as separate license
    76  			if len(l.Value) <= 64 {
    77  				// if the license text is less than the size of the hash,
    78  				// just use it directly so the id is more readable
    79  				candidate.id = spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value)
    80  			} else {
    81  				hash := sha256.Sum256([]byte(l.Value))
    82  				candidate.id = fmt.Sprintf("%s%x", spdxlicense.LicenseRefPrefix, hash)
    83  			}
    84  			candidate.value = l.Value
    85  		}
    86  
    87  		switch l.Type {
    88  		case license.Concluded:
    89  			concluded = append(concluded, candidate)
    90  		case license.Declared:
    91  			declared = append(declared, candidate)
    92  		}
    93  	}
    94  
    95  	return concluded, declared
    96  }