Skip to content

Commit

Permalink
refactor: Licence matcher (#1491)
Browse files Browse the repository at this point in the history
Also partially removes the global user agent variable to be specificly
passed in for every client.

Majority of the code in licencematcher.go is migrated from the deleted
license.go file.
  • Loading branch information
another-rex authored Jan 15, 2025
1 parent 6cd1233 commit 2e9d96a
Show file tree
Hide file tree
Showing 15 changed files with 173 additions and 155 deletions.
3 changes: 2 additions & 1 deletion cmd/osv-scanner/fix/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/google/osv-scanner/internal/resolution/client"
"github.com/google/osv-scanner/internal/resolution/lockfile"
"github.com/google/osv-scanner/internal/resolution/manifest"
"github.com/google/osv-scanner/internal/version"
"github.com/google/osv-scanner/pkg/reporter"
"github.com/urfave/cli/v2"
"golang.org/x/term"
Expand Down Expand Up @@ -330,7 +331,7 @@ func action(ctx *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, erro

switch ctx.String("data-source") {
case "deps.dev":
cl, err := client.NewDepsDevClient(depsdev.DepsdevAPI)
cl, err := client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_fix/"+version.OSVVersion)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/osv-scanner/update/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/google/osv-scanner/internal/remediation/suggest"
"github.com/google/osv-scanner/internal/resolution/client"
"github.com/google/osv-scanner/internal/resolution/manifest"
"github.com/google/osv-scanner/internal/version"
"github.com/google/osv-scanner/pkg/lockfile"
"github.com/google/osv-scanner/pkg/reporter"
"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -74,7 +75,7 @@ func action(ctx *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, erro
}

var err error
options.Client.DependencyClient, err = client.NewDepsDevClient(depsdev.DepsdevAPI)
options.Client.DependencyClient, err = client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_update/"+version.OSVVersion)
if err != nil {
return nil, err
}
Expand Down
109 changes: 109 additions & 0 deletions internal/clients/clientimpl/licensematcher/licensematcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package licensematcher

import (
"context"

depsdevpb "deps.dev/api/v3"
"github.com/google/osv-scanner/internal/depsdev"
"github.com/google/osv-scanner/internal/imodels"
"github.com/google/osv-scanner/internal/resolution/datasource"
"github.com/google/osv-scanner/pkg/models"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

const (
maxConcurrentRequests = 1000
)

// DepsDevLicenseMatcher implements the LicenseMatcher interface with a deps.dev client.
// It sends out requests for every package version and does not perform caching.
type DepsDevLicenseMatcher struct {
Client *datasource.DepsDevAPIClient
}

func (matcher *DepsDevLicenseMatcher) MatchLicenses(ctx context.Context, packages []imodels.PackageScanResult) error {
queries := make([]*depsdevpb.GetVersionRequest, len(packages))

for i, psr := range packages {
pkg := psr.PackageInfo
system, ok := depsdev.System[psr.PackageInfo.Ecosystem().Ecosystem]
if !ok || pkg.Name() == "" || pkg.Version() == "" {
continue
}
queries[i] = versionQuery(system, pkg.Name(), pkg.Version())
}

licenses, err := matcher.makeVersionRequest(ctx, queries)
if err != nil {
return err
}

for i, license := range licenses {
packages[i].Licenses = license
}

return nil
}

// makeVersionRequest calls the deps.dev GetVersion gRPC API endpoint for each
// query. It makes these requests concurrently, sharing the single HTTP/2
// connection. The order in which the requests are specified should correspond
// to the order of licenses returned by this function.
func (matcher *DepsDevLicenseMatcher) makeVersionRequest(ctx context.Context, queries []*depsdevpb.GetVersionRequest) ([][]models.License, error) {
licenses := make([][]models.License, len(queries))
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(maxConcurrentRequests)

for i := range queries {
if queries[i] == nil {
// This may be a private package.
licenses[i] = []models.License{models.License("UNKNOWN")}
continue
}
g.Go(func() error {
resp, err := matcher.Client.GetVersion(ctx, queries[i])
if err != nil {
if status.Code(err) == codes.NotFound {
licenses[i] = append(licenses[i], "UNKNOWN")
return nil
}

return err
}
ls := make([]models.License, len(resp.GetLicenses()))
for j, license := range resp.GetLicenses() {
ls[j] = models.License(license)
}
if len(ls) == 0 {
// The deps.dev API will return an
// empty slice if the license is
// unknown.
ls = []models.License{models.License("UNKNOWN")}
}
licenses[i] = ls

return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}

return licenses, nil
}

func versionQuery(system depsdevpb.System, name string, version string) *depsdevpb.GetVersionRequest {
if system == depsdevpb.System_GO {
version = "v" + version
}

return &depsdevpb.GetVersionRequest{
VersionKey: &depsdevpb.VersionKey{
System: system,
Name: name,
Version: version,
},
}
}
11 changes: 11 additions & 0 deletions internal/clients/clientinterfaces/licensematcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package clientinterfaces

import (
"context"

"github.com/google/osv-scanner/internal/imodels"
)

type LicenseMatcher interface {
MatchLicenses(ctx context.Context, psr []imodels.PackageScanResult) error
}
21 changes: 21 additions & 0 deletions internal/depsdev/depsdev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package depsdev

import (
"github.com/ossf/osv-schema/bindings/go/osvschema"

depsdevpb "deps.dev/api/v3"
)

// DepsdevAPI is the URL to the deps.dev API. It is documented at
// docs.deps.dev/api.
const DepsdevAPI = "api.deps.dev:443"

// System maps from a lockfile system to the depsdev API system.
var System = map[osvschema.Ecosystem]depsdevpb.System{
osvschema.EcosystemNPM: depsdevpb.System_NPM,
osvschema.EcosystemNuGet: depsdevpb.System_NUGET,
osvschema.EcosystemCratesIO: depsdevpb.System_CARGO,
osvschema.EcosystemGo: depsdevpb.System_GO,
osvschema.EcosystemMaven: depsdevpb.System_MAVEN,
osvschema.EcosystemPyPI: depsdevpb.System_PYPI,
}
114 changes: 0 additions & 114 deletions internal/depsdev/license.go

This file was deleted.

2 changes: 1 addition & 1 deletion internal/osvdev/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func DefaultConfig() ClientConfig {
JitterMultiplier: 2,
BackoffDurationExponential: 2,
BackoffDurationMultiplier: 1,
UserAgent: "osv-scanner/" + version.OSVVersion,
UserAgent: "osv-scanner_scan/" + version.OSVVersion,
MaxConcurrentBatchRequests: 10,
}
}
4 changes: 3 additions & 1 deletion internal/osvdev/osvdev.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const (

// MaxQueriesPerQueryBatchRequest is a limit set in osv.dev's API, so is not configurable
MaxQueriesPerQueryBatchRequest = 1000

DefaultBaseURL = "https://api.osv.dev"
)

type OSVClient struct {
Expand All @@ -39,7 +41,7 @@ func DefaultClient() *OSVClient {
return &OSVClient{
HTTPClient: http.DefaultClient,
Config: DefaultConfig(),
BaseHostURL: "https://api.osv.dev",
BaseHostURL: DefaultBaseURL,
}
}

Expand Down
4 changes: 2 additions & 2 deletions internal/resolution/client/depsdev_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type DepsDevClient struct {
c *datasource.DepsDevAPIClient
}

func NewDepsDevClient(addr string) (*DepsDevClient, error) {
c, err := datasource.NewDepsDevAPIClient(addr)
func NewDepsDevClient(addr string, userAgent string) (*DepsDevClient, error) {
c, err := datasource.NewDepsDevAPIClient(addr, userAgent)
if err != nil {
return nil, err
}
Expand Down
7 changes: 3 additions & 4 deletions internal/resolution/datasource/depsdev_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"time"

pb "deps.dev/api/v3"
"github.com/google/osv-scanner/pkg/osv"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
Expand Down Expand Up @@ -52,16 +51,16 @@ func makeVersionKey(k *pb.VersionKey) versionKey {
}
}

func NewDepsDevAPIClient(addr string) (*DepsDevAPIClient, error) {
func NewDepsDevAPIClient(addr string, userAgent string) (*DepsDevAPIClient, error) {
certPool, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("getting system cert pool: %w", err)
}
creds := credentials.NewClientTLSFromCert(certPool, "")
dialOpts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}

if osv.RequestUserAgent != "" {
dialOpts = append(dialOpts, grpc.WithUserAgent(osv.RequestUserAgent))
if userAgent != "" {
dialOpts = append(dialOpts, grpc.WithUserAgent(userAgent))
}

conn, err := grpc.NewClient(addr, dialOpts...)
Expand Down
2 changes: 1 addition & 1 deletion internal/version/version.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package version

// OSVVersion is the current release version, you should update this variable when doing a release
var OSVVersion = "1.9.1"
const OSVVersion = "1.9.1"
Loading

0 comments on commit 2e9d96a

Please sign in to comment.