github.com/google/osv-scalibr@v0.4.1/enricher/packagedeprecation/packagedeprecation.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package packagedeprecation enriches inventory details with package version deprecation status from deps.dev 16 package packagedeprecation 17 18 import ( 19 "context" 20 "maps" 21 "slices" 22 23 "github.com/google/osv-scalibr/clients/depsdev/v1alpha1/grpcclient" 24 "github.com/google/osv-scalibr/depsdev/depsdevalpha" 25 "github.com/google/osv-scalibr/enricher" 26 "github.com/google/osv-scalibr/extractor" 27 "github.com/google/osv-scalibr/inventory" 28 "github.com/google/osv-scalibr/log" 29 "github.com/google/osv-scalibr/plugin" 30 "github.com/google/osv-scalibr/purl" 31 ) 32 33 const ( 34 // Name is the name of the package version deprecation enricher. 35 Name = "packagedeprecation/depsdev" 36 ) 37 38 // Enricher is the package version deprecation enricher. 39 type Enricher struct { 40 // client is the deps.dev GRPC client. 41 client Client 42 } 43 44 // Name of the package version deprecation enricher. 45 func (*Enricher) Name() string { return Name } 46 47 // Version of the package version deprecation enricher. 48 func (*Enricher) Version() int { return 0 } 49 50 // Requirements of the package version deprecation enricher. 51 func (*Enricher) Requirements() *plugin.Capabilities { 52 return &plugin.Capabilities{Network: plugin.NetworkOnline} 53 } 54 55 // RequiredPlugins returns a list of Plugins that need to be enabled for this Enricher to work. 56 // While this enricher can run independently, 57 // it is intended to be used with extractors to provide the inventory data to enrich. 58 func (*Enricher) RequiredPlugins() []string { 59 return []string{} 60 } 61 62 // SetClient sets the deps.dev GRPC client. 63 // This is used for testing. 64 func (e *Enricher) SetClient(client Client) { 65 e.client = client 66 } 67 68 // New returns a new package deprecation enricher. 69 func New() enricher.Enricher { 70 grpcConfig := grpcclient.DefaultConfig() 71 grpcclient, err := grpcclient.New(grpcConfig) 72 if err != nil { 73 log.Errorf("Failed to create deps.dev gRPC client: %v", err) 74 } 75 76 c := NewClient(grpcclient) 77 78 return &Enricher{client: c} 79 } 80 81 // Enrich enriches the inventory with package version deprecation status from deps.dev. 82 func (e *Enricher) Enrich(ctx context.Context, input *enricher.ScanInput, inv *inventory.Inventory) error { 83 log.Debugf("Package deprecation enricher starting, %d packages to enrich.", len(inv.Packages)) 84 85 verToPkg := make(map[VersionKey][]*extractor.Package, len(inv.Packages)) 86 87 for _, pkg := range inv.Packages { 88 verKey, ok := makeVersionKey(pkg) 89 if !ok { 90 // System is not supported by deps.dev. Default deprecated to false. 91 pkg.Deprecated = false 92 continue 93 } 94 verToPkg[verKey] = append(verToPkg[verKey], pkg) 95 } 96 97 if len(verToPkg) == 0 { 98 return nil 99 } 100 101 query := slices.Collect(maps.Keys(verToPkg)) 102 103 resp, err := e.client.GetVersionBatch(ctx, Request{VersionKeys: query}) 104 if err != nil { 105 return err 106 } 107 108 results := resp.Results 109 for verKey, pkgs := range verToPkg { 110 deprecated, ok := results[verKey] 111 if !ok { 112 // Version key not found in deps.dev. Default deprecated to false. 113 for _, pkg := range pkgs { 114 pkg.Deprecated = false 115 } 116 continue 117 } 118 for _, pkg := range pkgs { 119 pkg.Deprecated = deprecated 120 } 121 } 122 123 log.Debugf("Package deprecation enricher finished.") 124 return nil 125 } 126 127 // makeVersionKey translates system from PURL type to deps.dev system, and returns a version key. 128 // Returns false if the system is not supported by deps.dev. 129 func makeVersionKey(pkg *extractor.Package) (VersionKey, bool) { 130 system, ok := depsdevalpha.System[pkg.PURLType] 131 if !ok { 132 return VersionKey{}, false 133 } 134 name, ver := pkg.Name, pkg.Version 135 136 // Matching deps.dev naming convention. 137 if pkg.PURLType == purl.TypeGolang { 138 if name == "stdlib" { 139 ver = "go" + ver 140 } else { 141 ver = "v" + ver 142 } 143 } 144 145 return VersionKey{ 146 System: system, 147 Name: name, 148 Version: ver, 149 }, true 150 }