Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Istio request handler and business logics for fetching Istio CVEs #984

Merged
merged 13 commits into from
Nov 14, 2022
49 changes: 49 additions & 0 deletions api/v1/convert/istio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package convert

import (
"github.com/hashicorp/go-version"
log "github.com/sirupsen/logrus"
"github.com/stackrox/istio-cves/types"
"github.com/stackrox/rox/pkg/stringutils"
v1 "github.com/stackrox/scanner/generated/scanner/api/v1"
"github.com/stackrox/scanner/pkg/istioutil"
pkgtypes "github.com/stackrox/scanner/pkg/types"
)

// IstioVulnerabilities converts istio cve schema to vulnerability.
func IstioVulnerabilities(vStr string, istioVulns []types.Vuln) []*v1.Vulnerability {
res := make([]*v1.Vulnerability, 0, len(istioVulns))
v, err := version.NewVersion(vStr)
if err != nil {
log.Infof("Failed to get version: %s", vStr)
return nil
}
for _, istioVuln := range istioVulns {
m, err := pkgtypes.ConvertMetadataFromIstio(istioVuln)
if err != nil {
log.Errorf("unable to convert metadata for %s: %istioVuln", istioVuln.Name, err)
continue
}
if m.IsNilOrEmpty() {
log.Warnf("nil or empty metadata for %s", istioVuln.Name)
continue
}

link := stringutils.OrDefault(istioVuln.Link, "https://istio.io/latest/news/security/")
_, fixedBy, err := istioutil.IsAffected(v, istioVuln)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we leave a comment saying we know this version is affected, which is why we can ignore that return value?

if err != nil {
log.Errorf("unable to get fixedBy for %s: %istioVuln", istioVuln.Name, err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant %v at the end?

continue
}

res = append(res, &v1.Vulnerability{
Name: istioVuln.Name,
Description: istioVuln.Description,
Link: link,
MetadataV2: Metadata(m),
FixedBy: fixedBy,
Severity: string(DatabaseSeverityToSeverity(m.GetDatabaseSeverity())),
})
}
return res
}
2 changes: 1 addition & 1 deletion api/v1/orchestratorscan/openshift_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestOpenShiftVulnVersion(t *testing.T) {
resp[record.Pkg.ID] = vulns
return resp, nil
}
service := NewService(db, nil)
service := NewService(db, nil, nil)
req := &scannerV1.GetOpenShiftVulnerabilitiesRequest{
OpenShiftVersion: c.version,
}
Expand Down
40 changes: 33 additions & 7 deletions api/v1/orchestratorscan/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/stackrox/scanner/api/v1/convert"
"github.com/stackrox/scanner/database"
v1 "github.com/stackrox/scanner/generated/scanner/api/v1"
istioCache "github.com/stackrox/scanner/istio/cache"
k8scache "github.com/stackrox/scanner/k8s/cache"
"github.com/stackrox/scanner/pkg/version"
"google.golang.org/grpc"
Expand All @@ -27,20 +28,22 @@ type Service interface {
}

// NewService returns the service for scanning
func NewService(db database.Datastore, k8sCache k8scache.Cache) Service {
func NewService(db database.Datastore, k8sCache k8scache.Cache, istioCache istioCache.Cache) Service {
return &serviceImpl{
version: version.Version,
db: db,
k8sCache: k8sCache,
version: version.Version,
db: db,
k8sCache: k8sCache,
istioCache: istioCache,
}
}

type serviceImpl struct {
v1.UnimplementedOrchestratorScanServiceServer

version string
db database.Datastore
k8sCache k8scache.Cache
version string
db database.Datastore
k8sCache k8scache.Cache
istioCache istioCache.Cache
}

func filterInvalidVulns(vulns []*v1.Vulnerability) []*v1.Vulnerability {
Expand Down Expand Up @@ -107,6 +110,29 @@ func (s *serviceImpl) GetKubeVulnerabilities(_ context.Context, req *v1.GetKubeV
return resp, nil
}

// GetIstioVulnerabilities returns Istio vulnerabilities for requested Kubernetes version.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Istio version

func (s *serviceImpl) GetIstioVulnerabilities(_ context.Context, req *v1.GetIstioVulnerabilitiesRequest) (*v1.GetIstioVulnerabilitiesResponse, error) {
resp := &v1.GetIstioVulnerabilitiesResponse{
ScannerVersion: s.version,
}

if req.GetIstioVersion() == "" {
return nil, errors.New("Can't get vulnerabilities for empty version.")
}
version, err := convert.TruncateVersion(req.GetIstioVersion())
if err != nil {
log.Warnf("Unable to convert Istio version of %s - %v. Skipping...", version, err)
return nil, nil
}

vulns := s.istioCache.GetVulnsByVersion(version)
converted := convert.IstioVulnerabilities(version, vulns)

resp.Vulnerabilities = filterInvalidVulns(converted)

return resp, nil
}

func (s *serviceImpl) getOpenShiftVulns(version *openShiftVersion) ([]*database.RHELv2Vulnerability, error) {
pkg := &database.RHELv2Package{
Name: version.CreatePkgName(),
Expand Down
10 changes: 9 additions & 1 deletion cmd/clair/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/stackrox/scanner/api/v1/vulndefs"
"github.com/stackrox/scanner/cpe/nvdtoolscache"
"github.com/stackrox/scanner/database"
istiocache "github.com/stackrox/scanner/istio/cache"
k8scache "github.com/stackrox/scanner/k8s/cache"
"github.com/stackrox/scanner/pkg/analyzer"
"github.com/stackrox/scanner/pkg/clairify/metrics"
Expand Down Expand Up @@ -119,6 +120,7 @@ func Boot(config *Config, slimMode bool) {

var nvdVulnCache nvdtoolscache.Cache
var k8sVulnCache k8scache.Cache
var istioVulnCache istiocache.Cache

if !slimMode {
wg.Add(1)
Expand All @@ -132,6 +134,12 @@ func Boot(config *Config, slimMode bool) {
defer wg.Add(-1)
k8sVulnCache = k8scache.Singleton()
}()

wg.Add(1)
go func() {
defer wg.Add(-1)
istioVulnCache = istiocache.Singleton()
}()
}

var repoToCPE *repo2cpe.Mapping
Expand Down Expand Up @@ -180,7 +188,7 @@ func Boot(config *Config, slimMode bool) {
grpcAPI.Register(
ping.NewService(),
imagescan.NewService(db, nvdVulnCache),
orchestratorscan.NewService(db, k8sVulnCache),
orchestratorscan.NewService(db, k8sVulnCache, istioVulnCache),
nodescan.NewService(db, nvdVulnCache, k8sVulnCache),
vulndefs.NewService(db),
)
Expand Down
54 changes: 54 additions & 0 deletions e2etests/orchestrator_scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,60 @@ var (
patchRegex = regexp.MustCompile(`^[0-9]+\.[0-9]+\.([0-9]+)$`)
)

func TestGRPCGetIstioVulnerabilities(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand what this is testing. What do you ultimately want to test here?

Copy link
Contributor Author

@daynewlee daynewlee Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to make sure GetIstioVulnerabilities() is accepting requests and returning correct responses. @RTann

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it may be more worthwhile to give an Istio version and ensure we find specific vulnerabilities. For example, we know Istio verison 1.13.6 is affected by ISTIO-SECURITY-2022-007, which has a specific description and CVSS and is fixed by 1.13.9

conn := connectToScanner(t)
client := v1.NewOrchestratorScanServiceClient(conn)

type istionVulnStruct struct {
version string
name string
fixedBy string
severity string
score float32
}

testCases := []istionVulnStruct{
{
version: "1.13.6",
name: "ISTIO-SECURITY-2022-006",
fixedBy: "1.13.7",
severity: "Moderate",
score: 5.9,
},
{
version: "1.14.0",
name: "ISTIO-SECURITY-2022-007",
fixedBy: "1.14.5",
severity: "Important",
score: 7.5,
},
}

testSet := make(map[string]istionVulnStruct)

for _, c := range testCases {
t.Run(fmt.Sprintf("case-%s", c.version), func(t *testing.T) {
req := &v1.GetIstioVulnerabilitiesRequest{IstioVersion: c.version}
resp, err := client.GetIstioVulnerabilities(context.Background(), req)
assert.NoError(t, err)

for _, vuln := range resp.GetVulnerabilities() {
assert.NotNil(t, vuln.GetMetadataV2().GetCvssV3())
assert.NotEmpty(t, vuln.Name)
assert.NotEmpty(t, vuln.FixedBy)
assert.NotEmpty(t, vuln.Severity)
sample := istionVulnStruct{version: c.version, name: vuln.Name, fixedBy: vuln.FixedBy, severity: vuln.Severity, score: vuln.GetMetadataV2().GetCvssV3().Score}
testSet[vuln.Name] = sample
}

assert.NotEmpty(t, testSet[c.name])
assert.Equal(t, c.fixedBy, testSet[c.name].fixedBy)
assert.Equal(t, c.severity, testSet[c.name].severity)
assert.Equal(t, c.score, testSet[c.name].score)
})
}
}

func TestGRPCGetOpenShiftVulnerabilities(t *testing.T) {
conn := connectToScanner(t)
client := v1.NewOrchestratorScanServiceClient(conn)
Expand Down
Loading