Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 1 addition & 68 deletions central/graphql/resolvers/image_components.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/stackrox/rox/pkg/features"
pkgMetrics "github.com/stackrox/rox/pkg/metrics"
"github.com/stackrox/rox/pkg/protocompat"
"github.com/stackrox/rox/pkg/scancomponent"
"github.com/stackrox/rox/pkg/search"
"github.com/stackrox/rox/pkg/search/scoped"
"github.com/stackrox/rox/pkg/utils"
Expand Down Expand Up @@ -413,39 +412,6 @@ func getImageCVEResolvers(ctx context.Context, root *Resolver, os string, vulns
return paginate(query.GetPagination(), resolverI, nil)
}

func getImageCVEV2Resolvers(ctx context.Context, root *Resolver, imageID string, component *storage.EmbeddedImageScanComponent, query *v1.Query) ([]ImageVulnerabilityResolver, error) {
query, _ = search.FilterQueryWithMap(query, mappings.VulnerabilityOptionsMap)
predicate, err := vulnPredicateFactory.GeneratePredicate(query)
if err != nil {
return nil, err
}

componentID, err := scancomponent.ComponentIDV2(component, imageID)
if err != nil {
return nil, err
}
resolvers := make([]ImageVulnerabilityResolver, 0, len(component.GetVulns()))
for _, vuln := range component.GetVulns() {
if !predicate.Matches(vuln) {
continue
}
converted, err := cveConverter.EmbeddedVulnerabilityToImageCVEV2(imageID, componentID, vuln)
if err != nil {
return nil, err
}

resolver, err := root.wrapImageCVEV2(converted, true, nil)
if err != nil {
return nil, err
}
resolver.ctx = embeddedobjs.VulnContext(ctx, vuln)

resolvers = append(resolvers, resolver)
}

return paginate(query.GetPagination(), resolvers, nil)
}

/*
Sub Resolver Functions
*/
Expand Down Expand Up @@ -779,17 +745,7 @@ func (resolver *imageComponentV2Resolver) ImageVulnerabilities(ctx context.Conte
resolver.ctx = ctx
}

// Short path. Full image is embedded when image scan resolver is called.
embeddedComponent := embeddedobjs.ComponentFromContext(resolver.ctx)
if embeddedComponent == nil {
return resolver.root.ImageVulnerabilities(resolver.imageComponentScopeContext(ctx), args)
}

query, err := args.AsV1QueryOrEmpty()
if err != nil {
return nil, err
}
return getImageCVEV2Resolvers(resolver.ctx, resolver.root, resolver.ImageId(resolver.ctx), embeddedComponent, query)
return resolver.root.ImageVulnerabilities(resolver.imageComponentScopeContext(ctx), args)
}

func (resolver *imageComponentV2Resolver) LastScanned(ctx context.Context) (*graphql.Time, error) {
Expand Down Expand Up @@ -848,29 +804,6 @@ func (resolver *imageComponentV2Resolver) TopImageVulnerability(ctx context.Cont
resolver.ctx = ctx
}

// Short path. Full image is embedded when image scan resolver is called.
if embeddedComponent := embeddedobjs.ComponentFromContext(resolver.ctx); embeddedComponent != nil {
var topVuln *storage.EmbeddedVulnerability
for _, vuln := range embeddedComponent.GetVulns() {
if topVuln == nil || vuln.GetCvss() > topVuln.GetCvss() {
topVuln = vuln
}
}
if topVuln == nil {
return nil, nil
}
componentID, err := scancomponent.ComponentIDV2(embeddedComponent, resolver.ImageId(resolver.ctx))
if err != nil {
return nil, err
}

convertedTopVuln, err := cveConverter.EmbeddedVulnerabilityToImageCVEV2(resolver.ImageId(resolver.ctx), componentID, topVuln)
if err != nil {
return nil, err
}
return resolver.root.wrapImageCVEV2WithContext(resolver.ctx, convertedTopVuln, true, nil)
}

return resolver.root.TopImageVulnerability(resolver.imageComponentScopeContext(ctx), RawQuery{})
}

Expand Down
137 changes: 1 addition & 136 deletions central/graphql/resolvers/image_scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import (
"sort"

"github.com/stackrox/rox/central/graphql/resolvers/embeddedobjs"
"github.com/stackrox/rox/central/graphql/resolvers/loaders"
"github.com/stackrox/rox/central/image/datastore/store/common/v2"
"github.com/stackrox/rox/central/image/mappings"
commonv2 "github.com/stackrox/rox/central/imagev2/datastore/store/common"
v1 "github.com/stackrox/rox/generated/api/v1"
"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/pkg/features"
Expand All @@ -26,13 +24,6 @@ func init() {
"imageComponentCount(query: String): Int!",
"imageComponents(query: String, pagination: Pagination): [ImageComponent!]!",
}),
// deprecated fields
schema.AddExtraResolvers("ImageScan", []string{
"componentCount(query: String): Int! " +
"@deprecated(reason: \"use 'imageComponentCount'\")",
"components(query: String, pagination: Pagination): [EmbeddedImageScanComponent!]! " +
"@deprecated(reason: \"use 'imageComponents'\")",
}),
)
}

Expand All @@ -42,7 +33,7 @@ func (resolver *imageScanResolver) ImageComponents(_ context.Context, args Pagin
return nil, err
}
if features.FlattenCVEData.Enabled() {
return getImageComponentV2Resolvers(resolver.ctx, resolver.root, resolver.data, query)
return resolver.root.ImageComponents(resolver.ctx, args)
}
return getImageComponentResolvers(resolver.ctx, resolver.root, resolver.data, query)
}
Expand Down Expand Up @@ -94,129 +85,3 @@ func getImageComponentResolvers(ctx context.Context, root *Resolver, imageScan *
}
return paginate(query.GetPagination(), resolverI, nil)
}

func getImageComponentV2Resolvers(ctx context.Context, root *Resolver, imageScan *storage.ImageScan, query *v1.Query) ([]ImageComponentResolver, error) {
imageID := getImageIDFromScope(ctx)
if imageID == "" {
return nil, nil
}

if features.FlattenImageData.Enabled() {
imageLoader, err := loaders.GetImageV2Loader(ctx)
if err != nil {
return nil, err
}

image, err := imageLoader.FullImageWithID(ctx, imageID)
if err != nil {
return nil, err
}

query, _ = search.FilterQueryWithMap(query, mappings.ComponentV2OptionsMap)
predicate, err := componentPredicateFactory.GeneratePredicate(query)
if err != nil {
return nil, err
}

idToComponent := make(map[string]*imageComponentV2Resolver)
for _, embeddedComponent := range imageScan.GetComponents() {
if !predicate.Matches(embeddedComponent) {
continue
}

os := imageScan.GetOperatingSystem()
id, err := scancomponent.ComponentIDV2(embeddedComponent, imageID)
if err != nil {
return nil, err
}
if _, exists := idToComponent[id]; !exists {
component, err := commonv2.GenerateImageComponentV2(os, image, embeddedComponent)
if err != nil {
return nil, err
}

resolver, err := root.wrapImageComponentV2(component, true, nil)
if err != nil {
return nil, err
}
imageScanTime := protocompat.ConvertTimestampToTimeOrNil(imageScan.GetScanTime())
resolver.ctx = embeddedobjs.ComponentContext(ctx, os, imageScanTime, embeddedComponent)
idToComponent[id] = resolver
}
}

// For now, sort by IDs.
resolvers := make([]*imageComponentV2Resolver, 0, len(idToComponent))
for _, component := range idToComponent {
resolvers = append(resolvers, component)
}
if len(query.GetPagination().GetSortOptions()) == 0 {
sort.SliceStable(resolvers, func(i, j int) bool {
return resolvers[i].data.GetId() < resolvers[j].data.GetId()
})
}
resolverI := make([]ImageComponentResolver, 0, len(resolvers))
for _, resolver := range resolvers {
resolverI = append(resolverI, resolver)
}
return paginate(query.GetPagination(), resolverI, nil)
}
imageLoader, err := loaders.GetImageLoader(ctx)
if err != nil {
return nil, err
}

image, err := imageLoader.FullImageWithID(ctx, imageID)
if err != nil {
return nil, err
}

query, _ = search.FilterQueryWithMap(query, mappings.ComponentV2OptionsMap)
predicate, err := componentPredicateFactory.GeneratePredicate(query)
if err != nil {
return nil, err
}

idToComponent := make(map[string]*imageComponentV2Resolver)
for _, embeddedComponent := range imageScan.GetComponents() {
if !predicate.Matches(embeddedComponent) {
continue
}

os := imageScan.GetOperatingSystem()
id, err := scancomponent.ComponentIDV2(embeddedComponent, imageID)
if err != nil {
return nil, err
}
if _, exists := idToComponent[id]; !exists {
component, err := common.GenerateImageComponentV2(os, image, embeddedComponent)
if err != nil {
return nil, err
}

resolver, err := root.wrapImageComponentV2(component, true, nil)
if err != nil {
return nil, err
}
imageScanTime := protocompat.ConvertTimestampToTimeOrNil(imageScan.GetScanTime())
resolver.ctx = embeddedobjs.ComponentContext(ctx, os, imageScanTime, embeddedComponent)
idToComponent[id] = resolver
}
}

// For now, sort by IDs.
resolvers := make([]*imageComponentV2Resolver, 0, len(idToComponent))
for _, component := range idToComponent {
resolvers = append(resolvers, component)
}
if len(query.GetPagination().GetSortOptions()) == 0 {
sort.SliceStable(resolvers, func(i, j int) bool {
return resolvers[i].data.GetId() < resolvers[j].data.GetId()
})
}
resolverI := make([]ImageComponentResolver, 0, len(resolvers))
for _, resolver := range resolvers {
resolverI = append(resolverI, resolver)
}
return paginate(query.GetPagination(), resolverI, nil)
}
5 changes: 5 additions & 0 deletions central/graphql/resolvers/image_scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ func (s *ImageScanResolverTestSuite) TestGetImagesWithScan() {
Return([]*storage.Image{cloned}, nil)
s.imageDataStore.EXPECT().GetImagesBatch(gomock.Any(), gomock.Any()).
Return([]*storage.Image{img}, nil)
if features.FlattenCVEData.Enabled() {
s.imageComponentFlatView.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, nil)
s.imageComponentDataStoreV2.EXPECT().SearchRawImageComponents(gomock.Any(), gomock.Any()).
Return(nil, nil)
}
response := s.schema.Exec(s.ctx, imageWithScanQuery, "getImages", nil)
s.Len(response.Errors, 0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class VulnScanWithGraphQLTest extends BaseSpecification {
tag
}
scan {
components {
imageComponents {
name
layerIndex
version
Expand All @@ -49,7 +49,7 @@ class VulnScanWithGraphQLTest extends BaseSpecification {
type
url
}
vulns {
imageVulnerabilities {
cve
cvss
link
Expand Down Expand Up @@ -136,8 +136,8 @@ class VulnScanWithGraphQLTest extends BaseSpecification {
then:
assert resultRet.getValue() != null
def image = resultRet.getValue().image
assert image?.scan?.components?.vulns != null
int cve = getCVEs(image.scan.components.vulns)
assert image?.scan?.imageComponents?.imageVulnerabilities != null
int cve = getCVEs(image.scan.imageComponents.imageVulnerabilities)
assert cve >= vuln_cve
where:
"Data inputs are :"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ const ConfigManagementEntityImage = ({
tag
}
scan {
components {
imageComponents {
name
layerIndex
version
vulns {
imageVulnerabilities {
cve
cvss
link
Expand Down Expand Up @@ -162,9 +162,14 @@ const ConfigManagementEntityImage = ({
layers.forEach((layer, i) => {
layers[i].components = [];
});
scan.components.forEach((component) => {
scan.imageComponents.forEach((component) => {
if (component.layerIndex !== undefined && layers[component.layerIndex]) {
layers[component.layerIndex].components.push(component);
// Transform imageVulnerabilities to vulns for CVETable compatibility
const transformedComponent = {
...component,
vulns: component.imageVulnerabilities || [],
};
layers[component.layerIndex].components.push(transformedComponent);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ const VulnMgmtEntityImage = ({
name
}
notes
components: imageComponents {
imageComponents {
id
priority
name
layerIndex
version
source
location
vulns: imageVulnerabilities {
imageVulnerabilities {
...cveFields
}
}
Expand Down
Loading
Loading