Skip to content

Commit

Permalink
Handle package not found in package registries
Browse files Browse the repository at this point in the history
If we don't find a package in the configured package registry, we would
have errored out. Let's instead reply inline that there is a
vulnerability but that we can't find information about the package.
  • Loading branch information
jhrozek committed May 18, 2024
1 parent ad49d69 commit 9b7cba6
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 2 deletions.
66 changes: 65 additions & 1 deletion internal/engine/eval/vulncheck/pkgdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import (
pb "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1"
)

// ErrPkgNotFound is returned when the package is not found in the package repository
var ErrPkgNotFound = fmt.Errorf("package not found")

func urlFromEndpointAndPaths(
endpoint string,
pathComponents ...string,
Expand All @@ -44,6 +47,10 @@ func urlFromEndpointAndPaths(
return u, nil
}

type formatterMeta struct {
missingInRegistry bool
}

// The patchLocatorFormatter interface is used to format the patch suggestion
// for the particular package manager. The interface should probably be refactored
// and each type implementing its own interface should handle the indenting rather
Expand All @@ -52,13 +59,15 @@ type patchLocatorFormatter interface {
LineHasDependency(line string) bool
IndentedString(indent int, oldDepLine string, oldDep *pb.Dependency) string
HasPatchedVersion() bool
IsNotFound() bool
GetPatchedVersion() string
}

// RepoQuerier is the interface for querying a repository
type RepoQuerier interface {
SendRecvRequest(ctx context.Context, dep *pb.Dependency, patched string, latest bool) (patchLocatorFormatter, error)
NoPatchAvailableFormatter(dep *pb.Dependency) patchLocatorFormatter
NotFoundFormatter(dep *pb.Dependency) patchLocatorFormatter
}

type repoCache struct {
Expand Down Expand Up @@ -93,6 +102,8 @@ func (rc *repoCache) newRepository(ecoConfig *ecosystemConfig) (RepoQuerier, err
}

type packageJson struct {
formatterMeta

Name string `json:"name"`
Version string `json:"version"`
Dist struct {
Expand Down Expand Up @@ -133,6 +144,11 @@ func (pj *packageJson) HasPatchedVersion() bool {
return pj.Version != ""
}

// IsNotFound returns true if the package is not found in the npm registry
func (pj *packageJson) IsNotFound() bool {
return pj.missingInRegistry
}

func (pj *packageJson) GetPatchedVersion() string {
return pj.Version
}
Expand All @@ -147,6 +163,8 @@ type pypiRepository struct {

// PyPiReply is the reply from the PyPi API
type PyPiReply struct {
formatterMeta

Info struct {
Name string `json:"name"`
Version string `json:"version"`
Expand Down Expand Up @@ -179,6 +197,11 @@ func (p *PyPiReply) HasPatchedVersion() bool {
return p.Info.Version != ""
}

// IsNotFound returns true if the package is not found in the PyPi registry
func (p *PyPiReply) IsNotFound() bool {
return p.missingInRegistry
}

// GetPatchedVersion returns the suggested patch version for a vulnerable package
func (p *PyPiReply) GetPatchedVersion() string {
return p.Info.Version
Expand Down Expand Up @@ -219,6 +242,18 @@ func (_ *pypiRepository) NoPatchAvailableFormatter(dep *pb.Dependency) patchLoca
}
}

func (_ *pypiRepository) NotFoundFormatter(dep *pb.Dependency) patchLocatorFormatter {
return &PyPiReply{
formatterMeta: formatterMeta{
missingInRegistry: true,
},
Info: struct {
Name string `json:"name"`
Version string `json:"version"`
}{Name: dep.Name, Version: ""},
}
}

func newPyPIRepository(endpoint string) *pypiRepository {
return &pypiRepository{
client: &http.Client{},
Expand Down Expand Up @@ -296,7 +331,9 @@ func (n *npmRepository) SendRecvRequest(ctx context.Context, dep *pb.Dependency,
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
if resp.StatusCode == http.StatusNotFound {
return nil, ErrPkgNotFound
} else if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("received non-200 response: %d", resp.StatusCode)
}

Expand All @@ -316,7 +353,19 @@ func (_ *npmRepository) NoPatchAvailableFormatter(dep *pb.Dependency) patchLocat
}
}

func (_ *npmRepository) NotFoundFormatter(dep *pb.Dependency) patchLocatorFormatter {
return &packageJson{
formatterMeta: formatterMeta{
missingInRegistry: true,
},
Name: dep.Name,
Version: "",
}
}

type goModPackage struct {
formatterMeta

// just for locating in the patch
oldVersion string

Expand All @@ -338,6 +387,11 @@ func (gmp *goModPackage) HasPatchedVersion() bool {
return gmp.Version != ""
}

// IsNotFound returns true if the package is not found in the Go registry
func (gmp *goModPackage) IsNotFound() bool {
return gmp.missingInRegistry
}

func (gmp *goModPackage) GetPatchedVersion() string {
return gmp.Version
}
Expand Down Expand Up @@ -500,3 +554,13 @@ func (_ *goProxyRepository) NoPatchAvailableFormatter(dep *pb.Dependency) patchL
oldVersion: dep.Version,
}
}

func (_ *goProxyRepository) NotFoundFormatter(dep *pb.Dependency) patchLocatorFormatter {
return &goModPackage{
formatterMeta: formatterMeta{
missingInRegistry: true,
},
Name: dep.Name,
oldVersion: dep.Version,
}
}
1 change: 1 addition & 0 deletions internal/engine/eval/vulncheck/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const (
`
reviewBodyDismissCommentText = "Previous Minder review was dismissed because the PR was updated"
vulnFoundWithNoPatch = "Vulnerability found, but no patched version exists yet."
vulnPkgNotFound = "Vulnerability found, but package not found in the package database."
)

const (
Expand Down
2 changes: 2 additions & 0 deletions internal/engine/eval/vulncheck/review.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ func (ra *reviewPrHandler) trackVulnerableDep(
comment := patch.IndentedString(location.leadingWhitespace, location.line, dep.Dep)
body = reviewBodyWithSuggestion(comment)
lineTo = len(strings.Split(comment, "\n")) - 1
} else if patch.IsNotFound() {
body = vulnPkgNotFound
} else {
body = vulnFoundWithNoPatch
}
Expand Down
7 changes: 6 additions & 1 deletion internal/engine/eval/vulncheck/vulncheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package vulncheck

import (
"context"
"errors"
"fmt"
"sort"
"strings"
Expand Down Expand Up @@ -206,7 +207,11 @@ func (e *Evaluator) checkVulnerabilities(
if patched, latest, noFix := getPatchedVersion(response.Vulns); noFix {
patchFormatter = pkgRepo.NoPatchAvailableFormatter(dep.Dep)
} else if patchFormatter, err = pkgRepo.SendRecvRequest(ctx, dep.Dep, patched, latest); err != nil {
return false, fmt.Errorf("failed to send package request: %w", err)
if errors.Is(err, ErrPkgNotFound) {
patchFormatter = pkgRepo.NotFoundFormatter(dep.Dep)
} else {
return false, fmt.Errorf("failed to send package request: %w", err)
}
}

if err := prHandler.trackVulnerableDep(ctx, dep, response, patchFormatter); err != nil {
Expand Down

0 comments on commit 9b7cba6

Please sign in to comment.