github.com/kastenhq/syft@v0.0.0-20230821225854-0710af25cdbe/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/kastenhq/syft/internal/log" 11 "github.com/kastenhq/syft/syft/file" 12 "github.com/kastenhq/syft/syft/pkg" 13 ) 14 15 func newPackageJSONPackage(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), 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 newPackageLockV1Package(resolver file.Resolver, location file.Location, name string, u lockDependency) pkg.Package { 48 version := u.Version 49 50 const aliasPrefixPackageLockV1 = "npm:" 51 52 // Handles type aliases https://github.com/npm/rfcs/blob/main/implemented/0001-package-aliases.md 53 if strings.HasPrefix(version, aliasPrefixPackageLockV1) { 54 // this is an alias. 55 // `"version": "npm:canonical-name@X.Y.Z"` 56 canonicalPackageAndVersion := version[len(aliasPrefixPackageLockV1):] 57 versionSeparator := strings.LastIndex(canonicalPackageAndVersion, "@") 58 59 name = canonicalPackageAndVersion[:versionSeparator] 60 version = canonicalPackageAndVersion[versionSeparator+1:] 61 } 62 63 return finalizeLockPkg( 64 resolver, 65 location, 66 pkg.Package{ 67 Name: name, 68 Version: version, 69 Locations: file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), 70 PURL: packageURL(name, version), 71 Language: pkg.JavaScript, 72 Type: pkg.NpmPkg, 73 MetadataType: pkg.NpmPackageLockJSONMetadataType, 74 Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: u.Resolved, Integrity: u.Integrity}, 75 }, 76 ) 77 } 78 79 func newPackageLockV2Package(resolver file.Resolver, location file.Location, name string, u lockPackage) pkg.Package { 80 return finalizeLockPkg( 81 resolver, 82 location, 83 pkg.Package{ 84 Name: name, 85 Version: u.Version, 86 Locations: file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), 87 Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(location, u.License...)...), 88 PURL: packageURL(name, u.Version), 89 Language: pkg.JavaScript, 90 Type: pkg.NpmPkg, 91 MetadataType: pkg.NpmPackageLockJSONMetadataType, 92 Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: u.Resolved, Integrity: u.Integrity}, 93 }, 94 ) 95 } 96 97 func newPnpmPackage(resolver file.Resolver, location file.Location, name, version string) pkg.Package { 98 return finalizeLockPkg( 99 resolver, 100 location, 101 pkg.Package{ 102 Name: name, 103 Version: version, 104 Locations: file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), 105 PURL: packageURL(name, version), 106 Language: pkg.JavaScript, 107 Type: pkg.NpmPkg, 108 }, 109 ) 110 } 111 112 func newYarnLockPackage(resolver file.Resolver, location file.Location, name, version string) pkg.Package { 113 return finalizeLockPkg( 114 resolver, 115 location, 116 pkg.Package{ 117 Name: name, 118 Version: version, 119 Locations: file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), 120 PURL: packageURL(name, version), 121 Language: pkg.JavaScript, 122 Type: pkg.NpmPkg, 123 }, 124 ) 125 } 126 127 func finalizeLockPkg(resolver file.Resolver, location file.Location, p pkg.Package) pkg.Package { 128 licenseCandidate := addLicenses(p.Name, resolver, location) 129 p.Licenses.Add(pkg.NewLicensesFromLocation(location, licenseCandidate...)...) 130 p.SetID() 131 return p 132 } 133 134 func addLicenses(name string, resolver file.Resolver, location file.Location) (allLicenses []string) { 135 if resolver == nil { 136 return allLicenses 137 } 138 139 dir := path.Dir(location.RealPath) 140 pkgPath := []string{dir, "node_modules"} 141 pkgPath = append(pkgPath, strings.Split(name, "/")...) 142 pkgPath = append(pkgPath, "package.json") 143 pkgFile := path.Join(pkgPath...) 144 locations, err := resolver.FilesByPath(pkgFile) 145 if err != nil { 146 log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err) 147 return allLicenses 148 } 149 150 if len(locations) == 0 { 151 return allLicenses 152 } 153 154 for _, l := range locations { 155 contentReader, err := resolver.FileContentsByLocation(l) 156 if err != nil { 157 log.Debugf("error getting file content reader for %s: %v", pkgFile, err) 158 return allLicenses 159 } 160 161 contents, err := io.ReadAll(contentReader) 162 if err != nil { 163 log.Debugf("error reading file contents for %s: %v", pkgFile, err) 164 return allLicenses 165 } 166 167 var pkgJSON packageJSON 168 err = json.Unmarshal(contents, &pkgJSON) 169 if err != nil { 170 log.Debugf("error parsing %s: %v", pkgFile, err) 171 return allLicenses 172 } 173 174 licenses, err := pkgJSON.licensesFromJSON() 175 if err != nil { 176 log.Debugf("error getting licenses from %s: %v", pkgFile, err) 177 return allLicenses 178 } 179 180 allLicenses = append(allLicenses, licenses...) 181 } 182 183 return allLicenses 184 } 185 186 // packageURL returns the PURL for the specific NPM package (see https://github.com/package-url/purl-spec) 187 func packageURL(name, version string) string { 188 var namespace string 189 190 fields := strings.SplitN(name, "/", 2) 191 if len(fields) > 1 { 192 namespace = fields[0] 193 name = fields[1] 194 } 195 196 return packageurl.NewPackageURL( 197 packageurl.TypeNPM, 198 namespace, 199 name, 200 version, 201 nil, 202 "", 203 ).ToString() 204 }