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 }