github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/javascript/package.go (about)

     1  package javascript
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"path"
     7  	"strings"
     8  
     9  	"github.com/anchore/packageurl-go"
    10  	"github.com/anchore/syft/internal/log"
    11  	"github.com/anchore/syft/syft/file"
    12  	"github.com/anchore/syft/syft/pkg"
    13  )
    14  
    15  func newPackageJSONRootPackage(u packageJSON, indexLocation file.Location) pkg.Package {
    16  	licenseCandidates, err := u.licensesFromJSON()
    17  	if err != nil {
    18  		log.Warnf("unable to extract licenses from javascript package.json: %+v", err)
    19  	}
    20  
    21  	license := pkg.NewLicensesFromLocation(indexLocation, licenseCandidates...)
    22  	p := pkg.Package{
    23  		Name:         u.Name,
    24  		Version:      u.Version,
    25  		PURL:         packageURL(u.Name, u.Version),
    26  		Locations:    file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
    27  		Language:     pkg.JavaScript,
    28  		Licenses:     pkg.NewLicenseSet(license...),
    29  		Type:         pkg.NpmPkg,
    30  		MetadataType: pkg.NpmPackageJSONMetadataType,
    31  		Metadata: pkg.NpmPackageJSONMetadata{
    32  			Name:        u.Name,
    33  			Version:     u.Version,
    34  			Description: u.Description,
    35  			Author:      u.Author.AuthorString(),
    36  			Homepage:    u.Homepage,
    37  			URL:         u.Repository.URL,
    38  			Private:     u.Private,
    39  		},
    40  	}
    41  
    42  	p.SetID()
    43  
    44  	return p
    45  }
    46  
    47  func finalizeLockPkg(resolver file.Resolver, location file.Location, p pkg.Package) pkg.Package {
    48  	licenseCandidate := addLicenses(p.Name, resolver, location)
    49  	p.Licenses.Add(pkg.NewLicensesFromLocation(location, licenseCandidate...)...)
    50  	p.SetID()
    51  	return p
    52  }
    53  
    54  func addLicenses(name string, resolver file.Resolver, location file.Location) (allLicenses []string) {
    55  	if resolver == nil {
    56  		return allLicenses
    57  	}
    58  
    59  	dir := path.Dir(location.RealPath)
    60  	pkgPath := []string{dir, "node_modules"}
    61  	pkgPath = append(pkgPath, strings.Split(name, "/")...)
    62  	pkgPath = append(pkgPath, "package.json")
    63  	pkgFile := path.Join(pkgPath...)
    64  	locations, err := resolver.FilesByPath(pkgFile)
    65  	if err != nil {
    66  		log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err)
    67  		return allLicenses
    68  	}
    69  
    70  	if len(locations) == 0 {
    71  		return allLicenses
    72  	}
    73  
    74  	for _, l := range locations {
    75  		contentReader, err := resolver.FileContentsByLocation(l)
    76  		if err != nil {
    77  			log.Debugf("error getting file content reader for %s: %v", pkgFile, err)
    78  			return allLicenses
    79  		}
    80  
    81  		contents, err := io.ReadAll(contentReader)
    82  		if err != nil {
    83  			log.Debugf("error reading file contents for %s: %v", pkgFile, err)
    84  			return allLicenses
    85  		}
    86  
    87  		var pkgJSON packageJSON
    88  		err = json.Unmarshal(contents, &pkgJSON)
    89  		if err != nil {
    90  			log.Debugf("error parsing %s: %v", pkgFile, err)
    91  			return allLicenses
    92  		}
    93  
    94  		licenses, err := pkgJSON.licensesFromJSON()
    95  		if err != nil {
    96  			log.Debugf("error getting licenses from %s: %v", pkgFile, err)
    97  			return allLicenses
    98  		}
    99  
   100  		allLicenses = append(allLicenses, licenses...)
   101  	}
   102  
   103  	return allLicenses
   104  }
   105  
   106  // packageURL returns the PURL for the specific NPM package (see https://github.com/package-url/purl-spec)
   107  func packageURL(name, version string) string {
   108  	var namespace string
   109  
   110  	fields := strings.SplitN(name, "/", 2)
   111  	if len(fields) > 1 {
   112  		namespace = fields[0]
   113  		name = fields[1]
   114  	}
   115  
   116  	return packageurl.NewPackageURL(
   117  		packageurl.TypeNPM,
   118  		namespace,
   119  		name,
   120  		version,
   121  		nil,
   122  		"",
   123  	).ToString()
   124  }
   125  
   126  func newPackageLockV1Package(resolver file.Resolver, location file.Location, name string, u packageLockDependency) pkg.Package {
   127  	version := u.Version
   128  
   129  	const aliasPrefixPackageLockV1 = "npm:"
   130  
   131  	// Handles type aliases https://github.com/npm/rfcs/blob/main/implemented/0001-package-aliases.md
   132  	if strings.HasPrefix(version, aliasPrefixPackageLockV1) {
   133  		// this is an alias.
   134  		// `"version": "npm:canonical-name@X.Y.Z"`
   135  		canonicalPackageAndVersion := version[len(aliasPrefixPackageLockV1):]
   136  		versionSeparator := strings.LastIndex(canonicalPackageAndVersion, "@")
   137  
   138  		name = canonicalPackageAndVersion[:versionSeparator]
   139  		version = canonicalPackageAndVersion[versionSeparator+1:]
   140  	}
   141  
   142  	return finalizeLockPkg(
   143  		resolver,
   144  		location,
   145  		pkg.Package{
   146  			Name:         name,
   147  			Version:      version,
   148  			Locations:    file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
   149  			PURL:         packageURL(name, version),
   150  			Language:     pkg.JavaScript,
   151  			Type:         pkg.NpmPkg,
   152  			MetadataType: pkg.NpmPackageLockJSONMetadataType,
   153  			Metadata:     pkg.NpmPackageLockJSONMetadata{Resolved: u.Resolved, Integrity: u.Integrity},
   154  		},
   155  	)
   156  }
   157  
   158  func newPackageLockV2Package(resolver file.Resolver, location file.Location, name string, u packageLockPackage) pkg.Package {
   159  	return finalizeLockPkg(
   160  		resolver,
   161  		location,
   162  		pkg.Package{
   163  			Name:         name,
   164  			Version:      u.Version,
   165  			Locations:    file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
   166  			Licenses:     pkg.NewLicenseSet(pkg.NewLicensesFromLocation(location, u.License...)...),
   167  			PURL:         packageURL(name, u.Version),
   168  			Language:     pkg.JavaScript,
   169  			Type:         pkg.NpmPkg,
   170  			MetadataType: pkg.NpmPackageLockJSONMetadataType,
   171  			Metadata:     pkg.NpmPackageLockJSONMetadata{Resolved: u.Resolved, Integrity: u.Integrity},
   172  		},
   173  	)
   174  }