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