From eb42d7858990eb17cba40a9d21f07f27a41bc57c Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Mon, 25 May 2026 19:29:02 +0200 Subject: [PATCH 01/36] feat: add CVE priority levels to vulnerability GraphQL API and issue checker - Add priorityActNow, priorityHigh, priorityElevated, priorityMonitor fields to ImageVulnerabilitySummary model - Expose priority fields in vulnerability.graphqls and GraphQL resolvers - Add VULNERABILITY_PRIORITY_ACT_NOW and VULNERABILITY_PRIORITY_HIGH sort fields - Add ExternalIngressActNowVulnerabilityIssue type and issue checker - Map priority signals (EPSS, KEV, ransomware) via VulnerabilityPrioritySignals - Bump golang.org/x/net to v0.55.0 and golang.org/x/crypto to v0.52.0 to fix known vulnerabilities - Update v13s/pkg/api to v0.0.0-20260525171357-13563f32226d (priority_elevated, priority_monitor support) --- internal/graph/gengql/issues.generated.go | 322 ++++++++++++++++++++++ internal/graph/gengql/root_.generated.go | 73 +++++ internal/graph/gengql/schema.generated.go | 7 + internal/graph/issues.resolvers.go | 14 + internal/graph/schema/issues.graphqls | 13 + internal/issue/checker/workload_v13s.go | 77 +++++- internal/issue/model.go | 19 +- internal/issue/queries.go | 9 + internal/vulnerability/fake/fakedata.go | 68 +++-- internal/vulnerability/models.go | 21 +- internal/vulnerability/sortfilter.go | 9 + 11 files changed, 586 insertions(+), 46 deletions(-) diff --git a/internal/graph/gengql/issues.generated.go b/internal/graph/gengql/issues.generated.go index fb56b878a..ea237acc8 100644 --- a/internal/graph/gengql/issues.generated.go +++ b/internal/graph/gengql/issues.generated.go @@ -45,6 +45,11 @@ type DeprecatedRegistryIssueResolver interface { Workload(ctx context.Context, obj *issue.DeprecatedRegistryIssue) (workload.Workload, error) } +type ExternalIngressActNowVulnerabilityIssueResolver interface { + TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (*team.TeamEnvironment, error) + + Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) +} type ExternalIngressCriticalVulnerabilityIssueResolver interface { TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) @@ -617,6 +622,185 @@ func (ec *executionContext) fieldContext_DeprecatedRegistryIssue_workload(_ cont return fc, nil } +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_id(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ID, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { + return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type ID does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.ExternalIngressActNowVulnerabilityIssue().TeamEnvironment(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { + return ec.marshalNTeamEnvironment2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeamEnvironment(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ExternalIngressActNowVulnerabilityIssue", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_TeamEnvironment(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_severity(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_severity(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Severity, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v issue.Severity) graphql.Marshaler { + return ec.marshalNSeverity2githubᚗcomᚋnaisᚋapiᚋinternalᚋissueᚐSeverity(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_severity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type Severity does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_message(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_workload(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.ExternalIngressActNowVulnerabilityIssue().Workload(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v workload.Workload) graphql.Marshaler { + return ec.marshalNWorkload2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚐWorkload(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ExternalIngressActNowVulnerabilityIssue", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + return fc, nil +} + +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_priorityActNow(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_priorityActNow(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.PriorityActNow, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_priorityActNow(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_ingresses(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Ingresses, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []string) graphql.Marshaler { + return ec.marshalNString2ᚕstringᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_ingresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +} + func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3329,6 +3513,13 @@ func (ec *executionContext) _Issue(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) + case issue.ExternalIngressActNowVulnerabilityIssue: + return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressActNowVulnerabilityIssue: + if obj == nil { + return graphql.Null + } + return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, obj) case issue.DeprecatedRegistryIssue: return ec._DeprecatedRegistryIssue(ctx, sel, &obj) case *issue.DeprecatedRegistryIssue: @@ -3746,6 +3937,137 @@ func (ec *executionContext) _DeprecatedRegistryIssue(ctx context.Context, sel as return out } +var externalIngressActNowVulnerabilityIssueImplementors = []string{"ExternalIngressActNowVulnerabilityIssue", "Issue", "Node"} + +func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressActNowVulnerabilityIssue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, externalIngressActNowVulnerabilityIssueImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ExternalIngressActNowVulnerabilityIssue") + case "id": + out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "teamEnvironment": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "severity": + out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_severity(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "message": + out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "workload": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ExternalIngressActNowVulnerabilityIssue_workload(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "priorityActNow": + out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_priorityActNow(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "ingresses": + out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_ingresses(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var externalIngressCriticalVulnerabilityIssueImplementors = []string{"ExternalIngressCriticalVulnerabilityIssue", "Issue", "Node"} func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressCriticalVulnerabilityIssue) graphql.Marshaler { diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index b472fcc46..b4f91e7ca 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -82,6 +82,7 @@ type ResolverRoot interface { DeprecatedIngressIssue() DeprecatedIngressIssueResolver DeprecatedRegistryIssue() DeprecatedRegistryIssueResolver Environment() EnvironmentResolver + ExternalIngressActNowVulnerabilityIssue() ExternalIngressActNowVulnerabilityIssueResolver ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver Ingress() IngressResolver @@ -947,6 +948,16 @@ type ComplexityRoot struct { Node func(childComplexity int) int } + ExternalIngressActNowVulnerabilityIssue struct { + ID func(childComplexity int) int + Ingresses func(childComplexity int) int + Message func(childComplexity int) int + PriorityActNow func(childComplexity int) int + Severity func(childComplexity int) int + TeamEnvironment func(childComplexity int) int + Workload func(childComplexity int) int + } + ExternalIngressCriticalVulnerabilityIssue struct { CvssScore func(childComplexity int) int ID func(childComplexity int) int @@ -6800,6 +6811,55 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.EnvironmentEdge.Node(childComplexity), true + case "ExternalIngressActNowVulnerabilityIssue.id": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.ID == nil { + break + } + + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.ID(childComplexity), true + + case "ExternalIngressActNowVulnerabilityIssue.ingresses": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Ingresses == nil { + break + } + + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Ingresses(childComplexity), true + + case "ExternalIngressActNowVulnerabilityIssue.message": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Message == nil { + break + } + + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Message(childComplexity), true + + case "ExternalIngressActNowVulnerabilityIssue.priorityActNow": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.PriorityActNow == nil { + break + } + + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.PriorityActNow(childComplexity), true + + case "ExternalIngressActNowVulnerabilityIssue.severity": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Severity == nil { + break + } + + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Severity(childComplexity), true + + case "ExternalIngressActNowVulnerabilityIssue.teamEnvironment": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.TeamEnvironment == nil { + break + } + + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.TeamEnvironment(childComplexity), true + + case "ExternalIngressActNowVulnerabilityIssue.workload": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload == nil { + break + } + + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload(childComplexity), true + case "ExternalIngressCriticalVulnerabilityIssue.cvssScore": if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore == nil { break @@ -23508,6 +23568,7 @@ enum IssueType { MISSING_SBOM VULNERABLE_IMAGE EXTERNAL_INGRESS_CRITICAL_VULNERABILITY + EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." APPLICATION_RESTART_LOOP @@ -23535,6 +23596,18 @@ type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { ingresses: [String!]! } +"Raised when a workload with external ingresses has one or more ACT_NOW priority vulnerabilities." +type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { + id: ID! + teamEnvironment: TeamEnvironment! + severity: Severity! + message: String! + + workload: Workload! + priorityActNow: Int! + ingresses: [String!]! +} + type MissingSbomIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! diff --git a/internal/graph/gengql/schema.generated.go b/internal/graph/gengql/schema.generated.go index 98dbd2564..3876800f0 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -6614,6 +6614,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) + case issue.ExternalIngressActNowVulnerabilityIssue: + return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressActNowVulnerabilityIssue: + if obj == nil { + return graphql.Null + } + return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, obj) case issue.DeprecatedRegistryIssue: return ec._DeprecatedRegistryIssue(ctx, sel, &obj) case *issue.DeprecatedRegistryIssue: diff --git a/internal/graph/issues.resolvers.go b/internal/graph/issues.resolvers.go index 558894d1f..541685f30 100644 --- a/internal/graph/issues.resolvers.go +++ b/internal/graph/issues.resolvers.go @@ -2,6 +2,7 @@ package graph import ( "context" + "fmt" "github.com/nais/api/internal/graph/gengql" "github.com/nais/api/internal/graph/pagination" @@ -40,6 +41,14 @@ func (r *deprecatedRegistryIssueResolver) Workload(ctx context.Context, obj *iss return getWorkloadByResourceType(ctx, obj.TeamSlug, obj.EnvironmentName, obj.ResourceName, obj.ResourceType) } +func (r *externalIngressActNowVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (*team.TeamEnvironment, error) { + panic(fmt.Errorf("not implemented: TeamEnvironment - teamEnvironment")) +} + +func (r *externalIngressActNowVulnerabilityIssueResolver) Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) { + panic(fmt.Errorf("not implemented: Workload - workload")) +} + func (r *externalIngressCriticalVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) { return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) } @@ -173,6 +182,10 @@ func (r *Resolver) DeprecatedRegistryIssue() gengql.DeprecatedRegistryIssueResol return &deprecatedRegistryIssueResolver{r} } +func (r *Resolver) ExternalIngressActNowVulnerabilityIssue() gengql.ExternalIngressActNowVulnerabilityIssueResolver { + return &externalIngressActNowVulnerabilityIssueResolver{r} +} + func (r *Resolver) ExternalIngressCriticalVulnerabilityIssue() gengql.ExternalIngressCriticalVulnerabilityIssueResolver { return &externalIngressCriticalVulnerabilityIssueResolver{r} } @@ -231,6 +244,7 @@ type ( applicationRestartLoopIssueResolver struct{ *Resolver } deprecatedIngressIssueResolver struct{ *Resolver } deprecatedRegistryIssueResolver struct{ *Resolver } + externalIngressActNowVulnerabilityIssueResolver struct{ *Resolver } externalIngressCriticalVulnerabilityIssueResolver struct{ *Resolver } failedSynchronizationIssueResolver struct{ *Resolver } invalidSpecIssueResolver struct{ *Resolver } diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index 81d65dbd6..0365566f7 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -230,6 +230,7 @@ enum IssueType { MISSING_SBOM VULNERABLE_IMAGE EXTERNAL_INGRESS_CRITICAL_VULNERABILITY + EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." APPLICATION_RESTART_LOOP @@ -257,6 +258,18 @@ type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { ingresses: [String!]! } +"Raised when a workload with external ingresses has one or more ACT_NOW priority vulnerabilities." +type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { + id: ID! + teamEnvironment: TeamEnvironment! + severity: Severity! + message: String! + + workload: Workload! + priorityActNow: Int! + ingresses: [String!]! +} + type MissingSbomIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 1fc65db5d..95c925762 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -35,11 +35,13 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... Cluster: "dev-gcp", Type: "app", ImageName: "vulnerable-image", - ImageTag: "tag1", + ImageTag: "tag1", }, VulnerabilitySummary: &vulnerabilities.Summary{ - Critical: 5, - RiskScore: 250, + Critical: 5, + RiskScore: 250, + PriorityActNow: 2, + PriorityHigh: 3, }, SbomStatus: &vulnerabilities.SbomStatusInfo{ Status: vulnerabilities.SbomStatus_SBOM_STATUS_READY, @@ -67,11 +69,13 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... Cluster: "dev-gcp", Type: "app", ImageName: "vulnerable-image", - ImageTag: "tag1", + ImageTag: "tag1", }, VulnerabilitySummary: &vulnerabilities.Summary{ - Critical: 5, - RiskScore: 250, + Critical: 5, + RiskScore: 250, + PriorityActNow: 2, + PriorityHigh: 3, }, SbomStatus: &vulnerabilities.SbomStatusInfo{ Status: vulnerabilities.SbomStatus_SBOM_STATUS_READY, @@ -181,23 +185,28 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { continue } - if node.VulnerabilitySummary != nil && (node.VulnerabilitySummary.Critical > 0 || node.VulnerabilitySummary.RiskScore > 100) { + summary := node.VulnerabilitySummary + if summary != nil && (summary.PriorityActNow > 0 || summary.PriorityHigh > 0) { + severity := issue.SeverityWarning + if summary.PriorityActNow > 0 { + severity = issue.SeverityCritical + } ret = append(ret, &Issue{ IssueType: issue.IssueTypeVulnerableImage, ResourceType: workloadType, ResourceName: node.Workload.GetName(), Team: node.Workload.GetNamespace(), Env: environmentmapper.EnvironmentName(node.Workload.GetCluster()), - Severity: issue.SeverityWarning, + Severity: severity, Message: fmt.Sprintf( - "Image '%s' has %d critical vulnerabilities and a risk score of %d", + "Image '%s' has %d ACT_NOW and %d HIGH priority vulnerabilities", node.Workload.ImageName, - node.VulnerabilitySummary.Critical, - node.VulnerabilitySummary.RiskScore, + summary.PriorityActNow, + summary.PriorityHigh, ), IssueDetails: issue.VulnerableImageIssueDetails{ - Critical: int(node.VulnerabilitySummary.Critical), - RiskScore: int(node.VulnerabilitySummary.RiskScore), + Critical: int(summary.Critical), + RiskScore: int(summary.RiskScore), }, }) } @@ -280,6 +289,48 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { }) } + seenActNow := map[string]struct{}{} + for _, node := range resp.GetNodes() { + workloadType, ok := mapType(node.Workload.GetType()) + if !ok || workloadType != issue.ResourceTypeApplication { + continue + } + + if node.VulnerabilitySummary == nil || node.VulnerabilitySummary.PriorityActNow == 0 { + continue + } + + env := environmentmapper.EnvironmentName(node.Workload.GetCluster()) + key := workloadKey(env, node.Workload.GetNamespace(), node.Workload.GetName()) + if _, exists := seenActNow[key]; exists { + continue + } + + externalIngresses := externalIngressesByWorkload[key] + if len(externalIngresses) == 0 { + continue + } + seenActNow[key] = struct{}{} + + ret = append(ret, &Issue{ + IssueType: issue.IssueTypeExternalIngressActNowVulnerability, + ResourceType: workloadType, + ResourceName: node.Workload.GetName(), + Team: node.Workload.GetNamespace(), + Env: env, + Severity: issue.SeverityCritical, + Message: fmt.Sprintf( + "Workload with external ingresses %s has %d ACT_NOW priority vulnerabilities", + strings.Join(externalIngresses, ", "), + node.VulnerabilitySummary.PriorityActNow, + ), + IssueDetails: issue.ExternalIngressActNowVulnerabilityIssueDetails{ + PriorityActNow: int(node.VulnerabilitySummary.PriorityActNow), + Ingresses: externalIngresses, + }, + }) + } + return ret } diff --git a/internal/issue/model.go b/internal/issue/model.go index 9a6e9b10a..e2a52fa24 100644 --- a/internal/issue/model.go +++ b/internal/issue/model.go @@ -189,6 +189,11 @@ type ExternalIngressCriticalVulnerabilityIssueDetails struct { Ingresses []string `json:"ingresses"` } +type ExternalIngressActNowVulnerabilityIssueDetails struct { + PriorityActNow int `json:"priorityActNow"` + Ingresses []string `json:"ingresses"` +} + type IssueType string const ( @@ -208,6 +213,7 @@ const ( IssueTypeVulnerableImage IssueType = "VULNERABLE_IMAGE" IssueTypeMissingSBOM IssueType = "MISSING_SBOM" IssueTypeExternalIngressCriticalVulnerability IssueType = "EXTERNAL_INGRESS_CRITICAL_VULNERABILITY" + IssueTypeExternalIngressActNowVulnerability IssueType = "EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY" IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" ) @@ -227,6 +233,7 @@ var AllIssueType = []IssueType{ IssueTypeVulnerableImage, IssueTypeMissingSBOM, IssueTypeExternalIngressCriticalVulnerability, + IssueTypeExternalIngressActNowVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop, } @@ -238,7 +245,8 @@ func (e IssueType) IsValid() bool { IssueTypeNoRunningInstances, IssueTypeLastRunFailed, IssueTypeWorkloadProblem, IssueTypeInvalidSpec, IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, IssueTypeExternalIngressCriticalVulnerability, - IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop: + IssueTypeExternalIngressActNowVulnerability, IssueTypeUnleashReleaseChannel, + IssueTypeApplicationRestartLoop: return true } return false @@ -504,6 +512,15 @@ func (ExternalIngressCriticalVulnerabilityIssue) IsIssue() {} func (ExternalIngressCriticalVulnerabilityIssue) IsNode() {} +type ExternalIngressActNowVulnerabilityIssue struct { + Base + ExternalIngressActNowVulnerabilityIssueDetails +} + +func (ExternalIngressActNowVulnerabilityIssue) IsIssue() {} + +func (ExternalIngressActNowVulnerabilityIssue) IsNode() {} + type UnleashReleaseChannelIssueDetails struct { ChannelName string `json:"channelName"` MajorVersion int `json:"majorVersion"` diff --git a/internal/issue/queries.go b/internal/issue/queries.go index ad1ab7401..d816b3f61 100644 --- a/internal/issue/queries.go +++ b/internal/issue/queries.go @@ -215,6 +215,15 @@ func convert(issue *issuesql.Issue) (Issue, error) { Base: base, ExternalIngressCriticalVulnerabilityIssueDetails: *d, }, nil + case IssueTypeExternalIngressActNowVulnerability: + d, err := unmarshal[ExternalIngressActNowVulnerabilityIssueDetails](issue.IssueDetails) + if err != nil { + return nil, err + } + return &ExternalIngressActNowVulnerabilityIssue{ + Base: base, + ExternalIngressActNowVulnerabilityIssueDetails: *d, + }, nil case IssueTypeUnleashReleaseChannel: d, err := unmarshal[UnleashReleaseChannelIssueDetails](issue.IssueDetails) if err != nil { diff --git a/internal/vulnerability/fake/fakedata.go b/internal/vulnerability/fake/fakedata.go index 7ac12556b..cc65b2e68 100644 --- a/internal/vulnerability/fake/fakedata.go +++ b/internal/vulnerability/fake/fakedata.go @@ -77,15 +77,19 @@ func createFakeData(ctx context.Context) *fakeData { func createWorkloadSummary(env, team, workloadType, name, image string, vulnFactor int32) *vulnerabilities.WorkloadSummary { imageName, imageTag := workload.FormatImageReferenceForLookup(image) summary := &vulnerabilities.Summary{ - Critical: vulnFactor, - High: vulnFactor * 2, - Medium: vulnFactor + 2, - Low: vulnFactor + 1, - Unassigned: vulnFactor, - Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, - RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, - HasSbom: true, - LastUpdated: timestamppb.New(time.Now()), + Critical: vulnFactor, + High: vulnFactor * 2, + Medium: vulnFactor + 2, + Low: vulnFactor + 1, + Unassigned: vulnFactor, + Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, + RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, + HasSbom: true, + LastUpdated: timestamppb.New(time.Now()), + PriorityActNow: vulnFactor, + PriorityHigh: vulnFactor * 2, + PriorityElevated: vulnFactor * 3, + PriorityMonitor: vulnFactor * 4, } if name == "no-errors" { @@ -118,38 +122,56 @@ func createWorkloadSummary(env, team, workloadType, name, image string, vulnFact func createVulnerabilities(w *vulnerabilities.WorkloadSummary) []*vulnerabilities.Vulnerability { findings := make([]*vulnerabilities.Vulnerability, 0) + priorities := []vulnerabilities.Priority{ + vulnerabilities.Priority_PRIORITY_ACT_NOW, + vulnerabilities.Priority_PRIORITY_HIGH, + vulnerabilities.Priority_PRIORITY_ELEVATED, + vulnerabilities.Priority_PRIORITY_MONITOR, + } + idx := 0 + nextPriority := func() vulnerabilities.Priority { + p := priorities[idx%len(priorities)] + idx++ + return p + } + epssScore := 0.85 + epssPercentile := 97.3 for i := range w.VulnerabilitySummary.Critical { - findings = append(findings, createVulnerability(vulnerabilities.Severity_CRITICAL, fmt.Sprintf("some-component-%d", i))) + findings = append(findings, createVulnerability(vulnerabilities.Severity_CRITICAL, fmt.Sprintf("some-component-%d", i), nextPriority(), &epssScore, &epssPercentile, true, false)) } for i := range w.VulnerabilitySummary.High { - findings = append(findings, createVulnerability(vulnerabilities.Severity_HIGH, fmt.Sprintf("some-component-%d", i))) + findings = append(findings, createVulnerability(vulnerabilities.Severity_HIGH, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Medium { - findings = append(findings, createVulnerability(vulnerabilities.Severity_MEDIUM, fmt.Sprintf("some-component-%d", i))) + findings = append(findings, createVulnerability(vulnerabilities.Severity_MEDIUM, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Low { - findings = append(findings, createVulnerability(vulnerabilities.Severity_LOW, fmt.Sprintf("some-component-%d", i))) + findings = append(findings, createVulnerability(vulnerabilities.Severity_LOW, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Unassigned { - findings = append(findings, createVulnerability(vulnerabilities.Severity_UNASSIGNED, fmt.Sprintf("some-component-%d", i))) + findings = append(findings, createVulnerability(vulnerabilities.Severity_UNASSIGNED, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) } return findings } -func createVulnerability(severity vulnerabilities.Severity, componentName string) *vulnerabilities.Vulnerability { +func createVulnerability(severity vulnerabilities.Severity, componentName string, priority vulnerabilities.Priority, epssScore, epssPercentile *float64, hasKevEntry, knownRansomwareUse bool) *vulnerabilities.Vulnerability { return &vulnerabilities.Vulnerability{ Id: uuid.New().String(), Package: fmt.Sprintf("pkg:golang/%s@v2.0.8?type=module", componentName), Cve: &vulnerabilities.Cve{ - Id: fmt.Sprintf("CVE-2024-%d", rand.IntN(100000)), - Title: "title for " + componentName, - Description: "desc for " + componentName, - Link: "", - Severity: severity, - References: nil, + Id: fmt.Sprintf("CVE-2024-%d", rand.IntN(100000)), + Title: "title for " + componentName, + Description: "desc for " + componentName, + Link: "", + Severity: severity, + References: nil, + Priority: priority, + EpssScore: epssScore, + EpssPercentile: epssPercentile, + HasKevEntry: hasKevEntry, + KnownRansomwareUse: knownRansomwareUse, }, LatestVersion: "", - // TODO: check if suppression is ever nil in protobuf - Suppression: nil, + Suppression: nil, } } diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index c1ead8f2c..94e9f2f21 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -71,15 +71,18 @@ type ImageVulnerabilitySuppression struct { } type ImageVulnerabilitySummary struct { - Total int `json:"total"` - RiskScore int `json:"riskScore"` - Low int `json:"low"` - Medium int `json:"medium"` - High int `json:"high"` - Critical int `json:"critical"` - Unassigned int `json:"unassigned"` - LastUpdated *time.Time `json:"lastUpdated"` - StaleImageTag *string `json:"staleImageTag"` + Total int `json:"total"` + RiskScore int `json:"riskScore"` + Low int `json:"low"` + Medium int `json:"medium"` + High int `json:"high"` + Critical int `json:"critical"` + Unassigned int `json:"unassigned"` + LastUpdated *time.Time `json:"lastUpdated"` + PriorityActNow int `json:"priorityActNow"` + PriorityHigh int `json:"priorityHigh"` + PriorityElevated int `json:"priorityElevated"` + PriorityMonitor int `json:"priorityMonitor"` } type ImageVulnerabilityOrderField string diff --git a/internal/vulnerability/sortfilter.go b/internal/vulnerability/sortfilter.go index cad3014d3..49625cb64 100644 --- a/internal/vulnerability/sortfilter.go +++ b/internal/vulnerability/sortfilter.go @@ -16,6 +16,7 @@ var SortFilterImageVulnerabilities = map[ImageVulnerabilityOrderField]vulnerabil "STATE": vulnerabilities.OrderByReason, "SUPPRESSED": vulnerabilities.OrderBySuppressed, "SEVERITY_SINCE": vulnerabilities.OrderBySeveritySince, + "PRIORITY": vulnerabilities.OrderByPriority, } var SortFilterWorkloadSummaries = map[VulnerabilitySummaryOrderByField]vulnerabilities.OrderByField{ @@ -27,6 +28,8 @@ var SortFilterWorkloadSummaries = map[VulnerabilitySummaryOrderByField]vulnerabi "VULNERABILITY_SEVERITY_MEDIUM": vulnerabilities.OrderByMedium, "VULNERABILITY_SEVERITY_LOW": vulnerabilities.OrderByLow, "VULNERABILITY_SEVERITY_UNASSIGNED": vulnerabilities.OrderByUnassigned, + "VULNERABILITY_PRIORITY_ACT_NOW": vulnerabilities.OrderByPriorityActNow, + "VULNERABILITY_PRIORITY_HIGH": vulnerabilities.OrderByPriorityHigh, } const ( @@ -78,6 +81,12 @@ func workloadInit() { workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_SEVERITY_UNASSIGNED", summarySorter(func(sum *ImageVulnerabilitySummary) int { return sum.Unassigned }), "NAME", "ENVIRONMENT") + workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_ACT_NOW", summarySorter(func(sum *ImageVulnerabilitySummary) int { + return sum.PriorityActNow + }), "NAME", "ENVIRONMENT") + workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_HIGH", summarySorter(func(sum *ImageVulnerabilitySummary) int { + return sum.PriorityHigh + }), "NAME", "ENVIRONMENT") workload.SortFilter.RegisterConcurrentSort("HAS_SBOM", func(ctx context.Context, a workload.Workload) int { hasSBOM, err := GetImageHasSBOM(ctx, a.GetImageString()) if err != nil { From de05bd40216ab9bc7725a89967e1275487ba1965 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Thu, 28 May 2026 13:53:39 +0200 Subject: [PATCH 02/36] feat(vulnerability): expose CVE priority, EPSS, KEV fields in GraphQL; fix ExternalIngressActNow resolver stubs; add priority sort fields --- internal/graph/gengql/root_.generated.go | 173 +++++++++- .../graph/gengql/vulnerability.generated.go | 310 +++++++++++++++++- internal/graph/issues.resolvers.go | 5 +- internal/graph/schema/vulnerability.graphqls | 48 +++ internal/graph/vulnerability.resolvers.go | 10 + internal/issue/checker/workload_v13s.go | 4 +- internal/vulnerability/models.go | 67 +++- internal/vulnerability/queries.go | 2 + internal/vulnerability/transform.go | 30 +- 9 files changed, 602 insertions(+), 47 deletions(-) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index b4f91e7ca..33432146b 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -85,6 +85,7 @@ type ResolverRoot interface { ExternalIngressActNowVulnerabilityIssue() ExternalIngressActNowVulnerabilityIssueResolver ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver + ImageVulnerabilitySummary() ImageVulnerabilitySummaryResolver Ingress() IngressResolver IngressMetrics() IngressMetricsResolver InstanceGroup() InstanceGroupResolver @@ -535,14 +536,19 @@ type ComplexityRoot struct { } CVE struct { - CVSSScore func(childComplexity int) int - Description func(childComplexity int) int - DetailsLink func(childComplexity int) int - ID func(childComplexity int) int - Identifier func(childComplexity int) int - Severity func(childComplexity int) int - Title func(childComplexity int) int - Workloads func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, filter *vulnerability.CVEWorkloadsFilter) int + CVSSScore func(childComplexity int) int + Description func(childComplexity int) int + DetailsLink func(childComplexity int) int + EpssPercentile func(childComplexity int) int + EpssScore func(childComplexity int) int + HasKevEntry func(childComplexity int) int + ID func(childComplexity int) int + Identifier func(childComplexity int) int + KnownRansomwareUse func(childComplexity int) int + Priority func(childComplexity int) int + Severity func(childComplexity int) int + Title func(childComplexity int) int + Workloads func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, filter *vulnerability.CVEWorkloadsFilter) int } CVEConnection struct { @@ -1087,15 +1093,19 @@ type ComplexityRoot struct { } ImageVulnerabilitySummary struct { - Critical func(childComplexity int) int - High func(childComplexity int) int - LastUpdated func(childComplexity int) int - Low func(childComplexity int) int - Medium func(childComplexity int) int - RiskScore func(childComplexity int) int - StaleImageTag func(childComplexity int) int - Total func(childComplexity int) int - Unassigned func(childComplexity int) int + Critical func(childComplexity int) int + High func(childComplexity int) int + LastUpdated func(childComplexity int) int + Low func(childComplexity int) int + Medium func(childComplexity int) int + PriorityActNow func(childComplexity int) int + PriorityElevated func(childComplexity int) int + PriorityHigh func(childComplexity int) int + PriorityMonitor func(childComplexity int) int + RiskScore func(childComplexity int) int + StaleImageTag func(childComplexity int) int + Total func(childComplexity int) int + Unassigned func(childComplexity int) int } ImageVulnerabilitySuppression struct { @@ -5302,6 +5312,27 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.CVE.DetailsLink(childComplexity), true + case "CVE.epssPercentile": + if e.ComplexityRoot.CVE.EpssPercentile == nil { + break + } + + return e.ComplexityRoot.CVE.EpssPercentile(childComplexity), true + + case "CVE.epssScore": + if e.ComplexityRoot.CVE.EpssScore == nil { + break + } + + return e.ComplexityRoot.CVE.EpssScore(childComplexity), true + + case "CVE.hasKevEntry": + if e.ComplexityRoot.CVE.HasKevEntry == nil { + break + } + + return e.ComplexityRoot.CVE.HasKevEntry(childComplexity), true + case "CVE.id": if e.ComplexityRoot.CVE.ID == nil { break @@ -5316,6 +5347,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.CVE.Identifier(childComplexity), true + case "CVE.knownRansomwareUse": + if e.ComplexityRoot.CVE.KnownRansomwareUse == nil { + break + } + + return e.ComplexityRoot.CVE.KnownRansomwareUse(childComplexity), true + + case "CVE.priority": + if e.ComplexityRoot.CVE.Priority == nil { + break + } + + return e.ComplexityRoot.CVE.Priority(childComplexity), true + case "CVE.severity": if e.ComplexityRoot.CVE.Severity == nil { break @@ -7392,6 +7437,34 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.Medium(childComplexity), true + case "ImageVulnerabilitySummary.priorityActNow": + if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityActNow == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityActNow(childComplexity), true + + case "ImageVulnerabilitySummary.priorityElevated": + if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityElevated == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityElevated(childComplexity), true + + case "ImageVulnerabilitySummary.priorityHigh": + if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityHigh == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityHigh(childComplexity), true + + case "ImageVulnerabilitySummary.priorityMonitor": + if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityMonitor == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityMonitor(childComplexity), true + case "ImageVulnerabilitySummary.riskScore": if e.ComplexityRoot.ImageVulnerabilitySummary.RiskScore == nil { break @@ -31607,6 +31680,7 @@ enum CVEOrderField { SEVERITY CVSS_SCORE AFFECTED_WORKLOADS_COUNT + PRIORITY } extend interface Workload { @@ -31835,6 +31909,18 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! + "Number of vulnerabilities with priority ACT_NOW." + priorityActNow: Int! + + "Number of vulnerabilities with priority HIGH." + priorityHigh: Int! + + "Number of vulnerabilities with priority ELEVATED." + priorityElevated: Int! + + "Number of vulnerabilities with priority MONITOR." + priorityMonitor: Int! + "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -31925,6 +32011,17 @@ type ImageVulnerability implements Node { cvssScore: Float } +enum CVEPriority { + "Vulnerability is known to be actively exploited and requires immediate action." + ACT_NOW + "Vulnerability is associated with ransomware or has a high EPSS percentile." + HIGH + "Vulnerability has a critical or high severity and elevated EPSS percentile." + ELEVATED + "Vulnerability requires monitoring but no immediate action." + MONITOR +} + type CVE implements Node { "The globally unique ID of the CVE." id: ID! @@ -31947,6 +32044,21 @@ type CVE implements Node { "CVSS score of the CVE." cvssScore: Float + "Priority of the CVE based on threat intelligence signals." + priority: CVEPriority! + + "EPSS score of the CVE (probability of exploitation)." + epssScore: Float + + "EPSS percentile of the CVE." + epssPercentile: Float + + "Whether the CVE has a Known Exploited Vulnerability (KEV) entry." + hasKevEntry: Boolean! + + "Whether the CVE is known to be used in ransomware attacks." + knownRansomwareUse: Boolean! + "Affected workloads" workloads( "Get the first n items in the connection. This can be used in combination with the after parameter." @@ -32057,6 +32169,7 @@ enum ImageVulnerabilityOrderField { PACKAGE STATE SUPPRESSED + PRIORITY } type WorkloadVulnerabilitySummary implements Node { @@ -32122,6 +32235,14 @@ enum VulnerabilitySummaryOrderByField { Order by vulnerability severity unassigned" """ VULNERABILITY_SEVERITY_UNASSIGNED + """ + Order by priority ACT_NOW count" + """ + VULNERABILITY_PRIORITY_ACT_NOW + """ + Order by priority HIGH count" + """ + VULNERABILITY_PRIORITY_HIGH } type TenantVulnerabilitySummary { @@ -33430,6 +33551,16 @@ func (ec *executionContext) childFields_CVE(ctx context.Context, field graphql.C return ec.fieldContext_CVE_detailsLink(ctx, field) case "cvssScore": return ec.fieldContext_CVE_cvssScore(ctx, field) + case "priority": + return ec.fieldContext_CVE_priority(ctx, field) + case "epssScore": + return ec.fieldContext_CVE_epssScore(ctx, field) + case "epssPercentile": + return ec.fieldContext_CVE_epssPercentile(ctx, field) + case "hasKevEntry": + return ec.fieldContext_CVE_hasKevEntry(ctx, field) + case "knownRansomwareUse": + return ec.fieldContext_CVE_knownRansomwareUse(ctx, field) case "workloads": return ec.fieldContext_CVE_workloads(ctx, field) } @@ -34244,6 +34375,14 @@ func (ec *executionContext) childFields_ImageVulnerabilitySummary(ctx context.Co return ec.fieldContext_ImageVulnerabilitySummary_critical(ctx, field) case "unassigned": return ec.fieldContext_ImageVulnerabilitySummary_unassigned(ctx, field) + case "priorityActNow": + return ec.fieldContext_ImageVulnerabilitySummary_priorityActNow(ctx, field) + case "priorityHigh": + return ec.fieldContext_ImageVulnerabilitySummary_priorityHigh(ctx, field) + case "priorityElevated": + return ec.fieldContext_ImageVulnerabilitySummary_priorityElevated(ctx, field) + case "priorityMonitor": + return ec.fieldContext_ImageVulnerabilitySummary_priorityMonitor(ctx, field) case "lastUpdated": return ec.fieldContext_ImageVulnerabilitySummary_lastUpdated(ctx, field) case "staleImageTag": diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index f0d454212..b4dd812f7 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -32,6 +32,9 @@ type ContainerImageSBOMResolver interface { type ContainerImageWorkloadReferenceResolver interface { Workload(ctx context.Context, obj *vulnerability.ContainerImageWorkloadReference) (workload.Workload, error) } +type ImageVulnerabilitySummaryResolver interface { + StaleImageTag(ctx context.Context, obj *vulnerability.ImageVulnerabilitySummary) (*string, error) +} type TeamVulnerabilitySummaryResolver interface { RiskScoreTrend(ctx context.Context, obj *vulnerability.TeamVulnerabilitySummary) (vulnerability.TeamVulnerabilityRiskScoreTrend, error) } @@ -258,6 +261,121 @@ func (ec *executionContext) fieldContext_CVE_cvssScore(_ context.Context, field return graphql.NewScalarFieldContext("CVE", field, false, false, errors.New("field of type Float does not have child fields")) } +func (ec *executionContext) _CVE_priority(ctx context.Context, field graphql.CollectedField, obj *vulnerability.CVE) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CVE_priority(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Priority, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v vulnerability.CVEPriority) graphql.Marshaler { + return ec.marshalNCVEPriority2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CVE_priority(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CVE", field, false, false, errors.New("field of type CVEPriority does not have child fields")) +} + +func (ec *executionContext) _CVE_epssScore(ctx context.Context, field graphql.CollectedField, obj *vulnerability.CVE) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CVE_epssScore(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.EpssScore, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *float64) graphql.Marshaler { + return ec.marshalOFloat2ᚖfloat64(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_CVE_epssScore(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CVE", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _CVE_epssPercentile(ctx context.Context, field graphql.CollectedField, obj *vulnerability.CVE) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CVE_epssPercentile(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.EpssPercentile, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *float64) graphql.Marshaler { + return ec.marshalOFloat2ᚖfloat64(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_CVE_epssPercentile(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CVE", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _CVE_hasKevEntry(ctx context.Context, field graphql.CollectedField, obj *vulnerability.CVE) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CVE_hasKevEntry(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.HasKevEntry, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CVE_hasKevEntry(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CVE", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _CVE_knownRansomwareUse(ctx context.Context, field graphql.CollectedField, obj *vulnerability.CVE) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CVE_knownRansomwareUse(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.KnownRansomwareUse, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CVE_knownRansomwareUse(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CVE", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + func (ec *executionContext) _CVE_workloads(ctx context.Context, field graphql.CollectedField, obj *vulnerability.CVE) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -1320,6 +1438,98 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_unassigned(_ return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) } +func (ec *executionContext) _ImageVulnerabilitySummary_priorityActNow(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_priorityActNow(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.PriorityActNow, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityActNow(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummary_priorityHigh(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_priorityHigh(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.PriorityHigh, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityHigh(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummary_priorityElevated(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_priorityElevated(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.PriorityElevated, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityElevated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummary_priorityMonitor(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_priorityMonitor(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.PriorityMonitor, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityMonitor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +} + func (ec *executionContext) _ImageVulnerabilitySummary_lastUpdated(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -1352,7 +1562,7 @@ func (ec *executionContext) _ImageVulnerabilitySummary_staleImageTag(ctx context return ec.fieldContext_ImageVulnerabilitySummary_staleImageTag(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.StaleImageTag, nil + return ec.Resolvers.ImageVulnerabilitySummary().StaleImageTag(ctx, obj) }, nil, func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { @@ -1363,7 +1573,7 @@ func (ec *executionContext) _ImageVulnerabilitySummary_staleImageTag(ctx context ) } func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_staleImageTag(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type String does not have child fields")) + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, true, true, errors.New("field of type String does not have child fields")) } func (ec *executionContext) _ImageVulnerabilitySuppression_state(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySuppression) (ret graphql.Marshaler) { @@ -3236,6 +3446,25 @@ func (ec *executionContext) _CVE(ctx context.Context, sel ast.SelectionSet, obj } case "cvssScore": out.Values[i] = ec._CVE_cvssScore(ctx, field, obj) + case "priority": + out.Values[i] = ec._CVE_priority(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "epssScore": + out.Values[i] = ec._CVE_epssScore(ctx, field, obj) + case "epssPercentile": + out.Values[i] = ec._CVE_epssPercentile(ctx, field, obj) + case "hasKevEntry": + out.Values[i] = ec._CVE_hasKevEntry(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "knownRansomwareUse": + out.Values[i] = ec._CVE_knownRansomwareUse(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } case "workloads": field := field @@ -3919,42 +4148,93 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel case "total": out.Values[i] = ec._ImageVulnerabilitySummary_total(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "riskScore": out.Values[i] = ec._ImageVulnerabilitySummary_riskScore(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "low": out.Values[i] = ec._ImageVulnerabilitySummary_low(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "medium": out.Values[i] = ec._ImageVulnerabilitySummary_medium(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "high": out.Values[i] = ec._ImageVulnerabilitySummary_high(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "critical": out.Values[i] = ec._ImageVulnerabilitySummary_critical(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "unassigned": out.Values[i] = ec._ImageVulnerabilitySummary_unassigned(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) + } + case "priorityActNow": + out.Values[i] = ec._ImageVulnerabilitySummary_priorityActNow(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "priorityHigh": + out.Values[i] = ec._ImageVulnerabilitySummary_priorityHigh(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "priorityElevated": + out.Values[i] = ec._ImageVulnerabilitySummary_priorityElevated(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "priorityMonitor": + out.Values[i] = ec._ImageVulnerabilitySummary_priorityMonitor(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) } case "lastUpdated": out.Values[i] = ec._ImageVulnerabilitySummary_lastUpdated(ctx, field, obj) case "staleImageTag": - out.Values[i] = ec._ImageVulnerabilitySummary_staleImageTag(ctx, field, obj) + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ImageVulnerabilitySummary_staleImageTag(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -4872,6 +5152,16 @@ func (ec *executionContext) marshalNCVEOrderField2githubᚗcomᚋnaisᚋapiᚋin return v } +func (ec *executionContext) unmarshalNCVEPriority2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx context.Context, v any) (vulnerability.CVEPriority, error) { + var res vulnerability.CVEPriority + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNCVEPriority2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx context.Context, sel ast.SelectionSet, v vulnerability.CVEPriority) graphql.Marshaler { + return v +} + func (ec *executionContext) marshalNContainerImageSBOM2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐContainerImageSBOM(ctx context.Context, sel ast.SelectionSet, v vulnerability.ContainerImageSBOM) graphql.Marshaler { return ec._ContainerImageSBOM(ctx, sel, &v) } diff --git a/internal/graph/issues.resolvers.go b/internal/graph/issues.resolvers.go index 541685f30..80c46b961 100644 --- a/internal/graph/issues.resolvers.go +++ b/internal/graph/issues.resolvers.go @@ -2,7 +2,6 @@ package graph import ( "context" - "fmt" "github.com/nais/api/internal/graph/gengql" "github.com/nais/api/internal/graph/pagination" @@ -42,11 +41,11 @@ func (r *deprecatedRegistryIssueResolver) Workload(ctx context.Context, obj *iss } func (r *externalIngressActNowVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (*team.TeamEnvironment, error) { - panic(fmt.Errorf("not implemented: TeamEnvironment - teamEnvironment")) + return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) } func (r *externalIngressActNowVulnerabilityIssueResolver) Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) { - panic(fmt.Errorf("not implemented: Workload - workload")) + return getWorkloadByResourceType(ctx, obj.TeamSlug, obj.EnvironmentName, obj.ResourceName, obj.ResourceType) } func (r *externalIngressCriticalVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) { diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 4359548fd..3d5b2cfcb 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -55,6 +55,7 @@ enum CVEOrderField { SEVERITY CVSS_SCORE AFFECTED_WORKLOADS_COUNT + PRIORITY } extend interface Workload { @@ -283,6 +284,18 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! + "Number of vulnerabilities with priority ACT_NOW." + priorityActNow: Int! + + "Number of vulnerabilities with priority HIGH." + priorityHigh: Int! + + "Number of vulnerabilities with priority ELEVATED." + priorityElevated: Int! + + "Number of vulnerabilities with priority MONITOR." + priorityMonitor: Int! + "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -373,6 +386,17 @@ type ImageVulnerability implements Node { cvssScore: Float } +enum CVEPriority { + "Vulnerability is known to be actively exploited and requires immediate action." + ACT_NOW + "Vulnerability is associated with ransomware or has a high EPSS percentile." + HIGH + "Vulnerability has a critical or high severity and elevated EPSS percentile." + ELEVATED + "Vulnerability requires monitoring but no immediate action." + MONITOR +} + type CVE implements Node { "The globally unique ID of the CVE." id: ID! @@ -395,6 +419,21 @@ type CVE implements Node { "CVSS score of the CVE." cvssScore: Float + "Priority of the CVE based on threat intelligence signals." + priority: CVEPriority! + + "EPSS score of the CVE (probability of exploitation)." + epssScore: Float + + "EPSS percentile of the CVE." + epssPercentile: Float + + "Whether the CVE has a Known Exploited Vulnerability (KEV) entry." + hasKevEntry: Boolean! + + "Whether the CVE is known to be used in ransomware attacks." + knownRansomwareUse: Boolean! + "Affected workloads" workloads( "Get the first n items in the connection. This can be used in combination with the after parameter." @@ -505,6 +544,7 @@ enum ImageVulnerabilityOrderField { PACKAGE STATE SUPPRESSED + PRIORITY } type WorkloadVulnerabilitySummary implements Node { @@ -570,6 +610,14 @@ enum VulnerabilitySummaryOrderByField { Order by vulnerability severity unassigned" """ VULNERABILITY_SEVERITY_UNASSIGNED + """ + Order by priority ACT_NOW count" + """ + VULNERABILITY_PRIORITY_ACT_NOW + """ + Order by priority HIGH count" + """ + VULNERABILITY_PRIORITY_HIGH } type TenantVulnerabilitySummary { diff --git a/internal/graph/vulnerability.resolvers.go b/internal/graph/vulnerability.resolvers.go index fbc3c706e..7529d8d23 100644 --- a/internal/graph/vulnerability.resolvers.go +++ b/internal/graph/vulnerability.resolvers.go @@ -2,6 +2,7 @@ package graph import ( "context" + "fmt" "time" "github.com/nais/api/internal/auth/authz" @@ -80,6 +81,10 @@ func (r *containerImageWorkloadReferenceResolver) Workload(ctx context.Context, return getWorkload(ctx, obj.Reference, obj.TeamSlug, environmentmapper.EnvironmentName(obj.EnvironmentName)) } +func (r *imageVulnerabilitySummaryResolver) StaleImageTag(ctx context.Context, obj *vulnerability.ImageVulnerabilitySummary) (*string, error) { + panic(fmt.Errorf("not implemented: StaleImageTag - staleImageTag")) +} + func (r *jobResolver) ImageVulnerabilityHistory(ctx context.Context, obj *job.Job, from scalar.Date) (*vulnerability.ImageVulnerabilityHistory, error) { return vulnerability.GetWorkloadVulnerabilityHistoryForWorkload(ctx, obj, from.Time()) } @@ -173,6 +178,10 @@ func (r *Resolver) ContainerImageWorkloadReference() gengql.ContainerImageWorklo return &containerImageWorkloadReferenceResolver{r} } +func (r *Resolver) ImageVulnerabilitySummary() gengql.ImageVulnerabilitySummaryResolver { + return &imageVulnerabilitySummaryResolver{r} +} + func (r *Resolver) TeamVulnerabilitySummary() gengql.TeamVulnerabilitySummaryResolver { return &teamVulnerabilitySummaryResolver{r} } @@ -185,6 +194,7 @@ type ( cVEResolver struct{ *Resolver } containerImageSBOMResolver struct{ *Resolver } containerImageWorkloadReferenceResolver struct{ *Resolver } + imageVulnerabilitySummaryResolver struct{ *Resolver } teamVulnerabilitySummaryResolver struct{ *Resolver } workloadVulnerabilitySummaryResolver struct{ *Resolver } ) diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 95c925762..827a8d994 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -35,7 +35,7 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... Cluster: "dev-gcp", Type: "app", ImageName: "vulnerable-image", - ImageTag: "tag1", + ImageTag: "tag1", }, VulnerabilitySummary: &vulnerabilities.Summary{ Critical: 5, @@ -69,7 +69,7 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... Cluster: "dev-gcp", Type: "app", ImageName: "vulnerable-image", - ImageTag: "tag1", + ImageTag: "tag1", }, VulnerabilitySummary: &vulnerabilities.Summary{ Critical: 5, diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index 94e9f2f21..d73c5153a 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -225,6 +225,8 @@ const ( VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_LOW" VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_UNASSIGNED" VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned VulnerabilitySummaryOrderByField = "VULNERABILITY_LAST_SCANNED" + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityActNow VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_ACT_NOW" + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHigh VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_HIGH" ) var AllVulnerabilitySummaryOrderByField = []VulnerabilitySummaryOrderByField{ @@ -237,11 +239,13 @@ var AllVulnerabilitySummaryOrderByField = []VulnerabilitySummaryOrderByField{ VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned, VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned, + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityActNow, + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHigh, } func (e VulnerabilitySummaryOrderByField) IsValid() bool { switch e { - case VulnerabilitySummaryOrderByFieldName, VulnerabilitySummaryOrderByFieldEnvironment, VulnerabilitySummaryOrderByFieldVulnerabilityRiskScore, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityCritical, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityHigh, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityMedium, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned, VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned: + case VulnerabilitySummaryOrderByFieldName, VulnerabilitySummaryOrderByFieldEnvironment, VulnerabilitySummaryOrderByFieldVulnerabilityRiskScore, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityCritical, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityHigh, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityMedium, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned, VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityActNow, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHigh: return true } return false @@ -415,14 +419,57 @@ type VulnerabilityFixSample struct { TotalWorkloads int `json:"totalWorkloads"` } +type CVEPriority string + +const ( + CVEPriorityActNow CVEPriority = "ACT_NOW" + CVEPriorityHigh CVEPriority = "HIGH" + CVEPriorityElevated CVEPriority = "ELEVATED" + CVEPriorityMonitor CVEPriority = "MONITOR" +) + +func (e CVEPriority) IsValid() bool { + switch e { + case CVEPriorityActNow, CVEPriorityHigh, CVEPriorityElevated, CVEPriorityMonitor: + return true + } + return false +} + +func (e CVEPriority) String() string { + return string(e) +} + +func (e *CVEPriority) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = CVEPriority(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid CVEPriority", str) + } + return nil +} + +func (e CVEPriority) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + type CVE struct { - Identifier string `json:"identifier"` - Severity ImageVulnerabilitySeverity `json:"severity"` - Title string `json:"title"` - Description string `json:"description"` - SeveritySince *time.Time `json:"severitySince,omitempty"` - DetailsLink string `json:"detailsLink"` - CVSSScore *float64 `json:"cvssScore,omitempty"` + Identifier string `json:"identifier"` + Severity ImageVulnerabilitySeverity `json:"severity"` + Title string `json:"title"` + Description string `json:"description"` + SeveritySince *time.Time `json:"severitySince,omitempty"` + DetailsLink string `json:"detailsLink"` + CVSSScore *float64 `json:"cvssScore,omitempty"` + Priority CVEPriority `json:"priority"` + EpssScore *float64 `json:"epssScore,omitempty"` + EpssPercentile *float64 `json:"epssPercentile,omitempty"` + HasKevEntry bool `json:"hasKevEntry"` + KnownRansomwareUse bool `json:"knownRansomwareUse"` // AffectedWorkloads is used to short circuit counting affected workloads in resolvers, // if the only field requested of the workloads field is the total count. @@ -479,6 +526,7 @@ const ( CVEOrderFieldSeverity CVEOrderField = "SEVERITY" CVEOrderFieldCVSSScore CVEOrderField = "CVSS_SCORE" CVEOrderFieldAffectedWorkloadsCount CVEOrderField = "AFFECTED_WORKLOADS_COUNT" + CVEOrderFieldPriority CVEOrderField = "PRIORITY" ) var AllCVEOrderField = []CVEOrderField{ @@ -486,11 +534,12 @@ var AllCVEOrderField = []CVEOrderField{ CVEOrderFieldSeverity, CVEOrderFieldCVSSScore, CVEOrderFieldAffectedWorkloadsCount, + CVEOrderFieldPriority, } func (e CVEOrderField) IsValid() bool { switch e { - case CVEOrderFieldIdentifier, CVEOrderFieldSeverity, CVEOrderFieldCVSSScore, CVEOrderFieldAffectedWorkloadsCount: + case CVEOrderFieldIdentifier, CVEOrderFieldSeverity, CVEOrderFieldCVSSScore, CVEOrderFieldAffectedWorkloadsCount, CVEOrderFieldPriority: return true } return false diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index 79b8b493a..d8b1339b3 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -627,6 +627,8 @@ func ListCVEs(ctx context.Context, page *pagination.Pagination, orderBy *CVEOrde field = vulnerabilities.OrderByCvssScore case CVEOrderFieldAffectedWorkloadsCount: field = vulnerabilities.OrderByAffectedWorkloads + case CVEOrderFieldPriority: + field = vulnerabilities.OrderByPriority default: field = vulnerabilities.OrderByCvssScore } diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 000f50ca4..a9a318d42 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -92,12 +92,30 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa func toCVE(cve *vulnerabilities.Cve) *CVE { return &CVE{ - Identifier: cve.Id, - Title: cve.Title, - Description: cve.Description, - DetailsLink: cve.Link, - CVSSScore: cve.CvssScore, - Severity: ImageVulnerabilitySeverity(cve.Severity.String()), + Identifier: cve.Id, + Title: cve.Title, + Description: cve.Description, + DetailsLink: cve.Link, + CVSSScore: cve.CvssScore, + Severity: ImageVulnerabilitySeverity(cve.Severity.String()), + Priority: parseCVEPriority(cve.Priority), + EpssScore: cve.EpssScore, + EpssPercentile: cve.EpssPercentile, + HasKevEntry: cve.HasKevEntry, + KnownRansomwareUse: cve.KnownRansomwareUse, + } +} + +func parseCVEPriority(p vulnerabilities.Priority) CVEPriority { + switch p { + case vulnerabilities.Priority_PRIORITY_ACT_NOW: + return CVEPriorityActNow + case vulnerabilities.Priority_PRIORITY_HIGH: + return CVEPriorityHigh + case vulnerabilities.Priority_PRIORITY_ELEVATED: + return CVEPriorityElevated + default: + return CVEPriorityMonitor } } From fb8abd09ae1f3767976cf452750ebddd81812a79 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Thu, 28 May 2026 14:19:23 +0200 Subject: [PATCH 03/36] chore: bump v13s to v0.0.0-20260528121134-739c7136ac8e (cve-priority rebase onto main) --- go.mod | 2 +- go.sum | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b48044248..57921c067 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260528080657-d4f49e5737da + github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index 072ae6985..4c53b416d 100644 --- a/go.sum +++ b/go.sum @@ -813,8 +813,8 @@ github.com/nais/tester v0.1.1 h1:tpJ5HKpu3mEIWX/mec0Yj0xLHEpt+MwTAsj282n0Py0= github.com/nais/tester v0.1.1/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= -github.com/nais/v13s/pkg/api v0.0.0-20260528080657-d4f49e5737da h1:59leNz7qKRctGQS6xUnPzVUqa2NnEzVlwMDAWyhUwJs= -github.com/nais/v13s/pkg/api v0.0.0-20260528080657-d4f49e5737da/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= +github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e h1:7fut4nxp6NlX7xS5SnwLkdyAhWanqmcpuClssn/cT8Q= +github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -1403,6 +1403,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1: google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= From 6b10eb6522ab2bf9461d0e416c9b453a22690f69 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Thu, 28 May 2026 16:21:18 +0200 Subject: [PATCH 04/36] =?UTF-8?q?refactor:=20simplify=20toWorkloadVulnerab?= =?UTF-8?q?ilitySummary=20=E2=80=94=20v13s=20now=20zeroes=20counts=20for?= =?UTF-8?q?=20terminal=20states?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/vulnerability/transform.go | 41 ++++++++++++++++++----------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index a9a318d42..6b60cb8e6 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -61,26 +61,35 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa wType = workload.TypeJob } - summary := &ImageVulnerabilitySummary{} - if s := w.GetVulnerabilitySummary(); s != nil { - var lastUpdated *time.Time - if ts := s.GetLastUpdated(); ts != nil { - t := ts.AsTime() - lastUpdated = &t - } - summary.Critical = int(s.Critical) - summary.High = int(s.High) - summary.Medium = int(s.Medium) - summary.Low = int(s.Low) - summary.Unassigned = int(s.Unassigned) - summary.Total = int(s.Total) - summary.RiskScore = int(s.RiskScore) - summary.LastUpdated = lastUpdated + v13sSummary := w.GetVulnerabilitySummary() + if v13sSummary == nil { + v13sSummary = &vulnerabilities.Summary{} + } + + var lastUpdated *time.Time + if ts := v13sSummary.GetLastUpdated(); ts != nil { + t := ts.AsTime() + lastUpdated = &t + } + + summary := &ImageVulnerabilitySummary{ + Critical: int(v13sSummary.Critical), + High: int(v13sSummary.High), + Medium: int(v13sSummary.Medium), + Low: int(v13sSummary.Low), + Unassigned: int(v13sSummary.Unassigned), + Total: int(v13sSummary.Total), + RiskScore: int(v13sSummary.RiskScore), + LastUpdated: lastUpdated, + PriorityActNow: int(v13sSummary.PriorityActNow), + PriorityHigh: int(v13sSummary.PriorityHigh), + PriorityElevated: int(v13sSummary.PriorityElevated), + PriorityMonitor: int(v13sSummary.PriorityMonitor), } return &WorkloadVulnerabilitySummary{ Summary: summary, - HasSbom: w.GetSbomStatus().GetStatus() == vulnerabilities.SbomStatus_SBOM_STATUS_READY, + HasSbom: v13sSummary.GetHasSbom(), TeamSlug: slug.Slug(w.GetWorkload().GetNamespace()), EnvironmentName: environmentmapper.EnvironmentName(w.GetWorkload().GetCluster()), WorkloadReference: &workload.Reference{ From c44e4cf847ba81146cbaeed82dafbe2ffb7c1549 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 2 Jun 2026 09:58:49 +0200 Subject: [PATCH 05/36] fix: address gosec int32 cast and update issue expectation --- integration_tests/issues_for_team.lua | 4 ++-- internal/vulnerability/models.go | 1 + internal/vulnerability/queries.go | 14 ++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index a90c26a46..e84452a71 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -555,8 +555,8 @@ Test.gql("VulnerableImageIssue", function(t) nodes = { { __typename = "VulnerableImageIssue", - message = "Image 'vulnerable-image' has 5 critical vulnerabilities and a risk score of 250", - severity = "WARNING", + message = "Image 'vulnerable-image' has 2 ACT_NOW and 3 HIGH priority vulnerabilities", + severity = "CRITICAL", critical = 5, riskScore = 250, workload = { diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index d73c5153a..8599deda9 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -79,6 +79,7 @@ type ImageVulnerabilitySummary struct { Critical int `json:"critical"` Unassigned int `json:"unassigned"` LastUpdated *time.Time `json:"lastUpdated"` + StaleImageTag *string `json:"staleImageTag"` PriorityActNow int `json:"priorityActNow"` PriorityHigh int `json:"priorityHigh"` PriorityElevated int `json:"priorityElevated"` diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index d8b1339b3..7874d50d1 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -673,7 +673,7 @@ func GetWorkloadsByCVE(ctx context.Context, cve string, page *pagination.Paginat if err != nil { return nil, apierror.Errorf("list workloads for vulnerability by CVE: %v", err) } - return convertWorkloadNodes(ctx, resp.GetNodes(), page, int32(min(resp.GetPageInfo().GetTotalCount(), math.MaxInt32))) //nolint:gosec + return convertWorkloadNodes(ctx, resp.GetNodes(), page, safeInt32TotalCount(resp.GetPageInfo().GetTotalCount())) } return getWorkloadsByCVE(ctx, cve, page) } @@ -695,7 +695,17 @@ func getWorkloadsByCVE(ctx context.Context, cve string, page *pagination.Paginat if err != nil { return nil, apierror.Errorf("list workloads for vulnerability by CVE: %v", err) } - return convertWorkloadNodes(ctx, resp.GetNodes(), page, int32(min(resp.GetPageInfo().GetTotalCount(), math.MaxInt32))) //nolint:gosec + return convertWorkloadNodes(ctx, resp.GetNodes(), page, safeInt32TotalCount(resp.GetPageInfo().GetTotalCount())) +} + +func safeInt32TotalCount(totalCount int64) int32 { + if totalCount <= 0 { + return 0 + } + if totalCount > int64(math.MaxInt32) { + return math.MaxInt32 + } + return int32(totalCount) } func convertWorkloadNodes(ctx context.Context, nodes []*vulnerabilities.WorkloadForVulnerability, page *pagination.Pagination, totalCount int32) (*WorkloadWithVulnerabilityConnection, error) { From 2837b5d5abc2c27a130b24f01012001e87603f22 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:09:21 +0200 Subject: [PATCH 06/36] chore(deps): bump v13s api to af9d5e6 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 57921c067..46980802f 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e + github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index 4c53b416d..578f1edf7 100644 --- a/go.sum +++ b/go.sum @@ -815,6 +815,8 @@ github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e h1:7fut4nxp6NlX7xS5SnwLkdyAhWanqmcpuClssn/cT8Q= github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= +github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3 h1:ei4Q9fAPa0dtBo7aBwCUuVrDRXA9jtipqsU6PhOcPYw= +github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= From 871577259684b28b044b1551d1724d3c879959b2 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:09:24 +0200 Subject: [PATCH 07/36] fix(vulnerability): adapt API to v13s risk-tier model --- integration_tests/issues_for_team.lua | 2 +- internal/graph/gengql/root_.generated.go | 15 +++-- .../graph/gengql/vulnerability.generated.go | 62 +++++-------------- internal/graph/schema/vulnerability.graphqls | 14 ++--- internal/graph/vulnerability.resolvers.go | 10 --- internal/issue/checker/workload_v13s.go | 34 +++++----- internal/vulnerability/fake/fakedata.go | 58 +++++++++-------- internal/vulnerability/models.go | 10 +-- internal/vulnerability/queries.go | 2 +- internal/vulnerability/sortfilter.go | 6 +- internal/vulnerability/transform.go | 33 ++++++---- 11 files changed, 106 insertions(+), 140 deletions(-) diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index e84452a71..24afaebe9 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -555,7 +555,7 @@ Test.gql("VulnerableImageIssue", function(t) nodes = { { __typename = "VulnerableImageIssue", - message = "Image 'vulnerable-image' has 2 ACT_NOW and 3 HIGH priority vulnerabilities", + message = "Image 'vulnerable-image' has 2 IMMEDIATE and 3 HIGH risk-tier vulnerabilities", severity = "CRITICAL", critical = 5, riskScore = 250, diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 33432146b..043516866 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -85,7 +85,6 @@ type ResolverRoot interface { ExternalIngressActNowVulnerabilityIssue() ExternalIngressActNowVulnerabilityIssueResolver ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver - ImageVulnerabilitySummary() ImageVulnerabilitySummaryResolver Ingress() IngressResolver IngressMetrics() IngressMetricsResolver InstanceGroup() InstanceGroupResolver @@ -31909,16 +31908,16 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - "Number of vulnerabilities with priority ACT_NOW." + "Number of vulnerabilities with risk tier IMMEDIATE." priorityActNow: Int! - "Number of vulnerabilities with priority HIGH." + "Number of vulnerabilities with risk tier HIGH." priorityHigh: Int! - "Number of vulnerabilities with priority ELEVATED." + "Number of vulnerabilities with risk tier ELEVATED." priorityElevated: Int! - "Number of vulnerabilities with priority MONITOR." + "Number of vulnerabilities with risk tier MONITOR." priorityMonitor: Int! "Timestamp of the last update of the vulnerability summary." @@ -32013,7 +32012,7 @@ type ImageVulnerability implements Node { enum CVEPriority { "Vulnerability is known to be actively exploited and requires immediate action." - ACT_NOW + IMMEDIATE "Vulnerability is associated with ransomware or has a high EPSS percentile." HIGH "Vulnerability has a critical or high severity and elevated EPSS percentile." @@ -32236,11 +32235,11 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by priority ACT_NOW count" + Order by IMMEDIATE risk-tier count" """ VULNERABILITY_PRIORITY_ACT_NOW """ - Order by priority HIGH count" + Order by HIGH risk-tier count" """ VULNERABILITY_PRIORITY_HIGH } diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index b4dd812f7..47108d2df 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -32,9 +32,6 @@ type ContainerImageSBOMResolver interface { type ContainerImageWorkloadReferenceResolver interface { Workload(ctx context.Context, obj *vulnerability.ContainerImageWorkloadReference) (workload.Workload, error) } -type ImageVulnerabilitySummaryResolver interface { - StaleImageTag(ctx context.Context, obj *vulnerability.ImageVulnerabilitySummary) (*string, error) -} type TeamVulnerabilitySummaryResolver interface { RiskScoreTrend(ctx context.Context, obj *vulnerability.TeamVulnerabilitySummary) (vulnerability.TeamVulnerabilityRiskScoreTrend, error) } @@ -1562,7 +1559,7 @@ func (ec *executionContext) _ImageVulnerabilitySummary_staleImageTag(ctx context return ec.fieldContext_ImageVulnerabilitySummary_staleImageTag(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.ImageVulnerabilitySummary().StaleImageTag(ctx, obj) + return obj.StaleImageTag, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { @@ -1573,7 +1570,7 @@ func (ec *executionContext) _ImageVulnerabilitySummary_staleImageTag(ctx context ) } func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_staleImageTag(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, true, true, errors.New("field of type String does not have child fields")) + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type String does not have child fields")) } func (ec *executionContext) _ImageVulnerabilitySuppression_state(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySuppression) (ret graphql.Marshaler) { @@ -4148,93 +4145,62 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel case "total": out.Values[i] = ec._ImageVulnerabilitySummary_total(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "riskScore": out.Values[i] = ec._ImageVulnerabilitySummary_riskScore(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "low": out.Values[i] = ec._ImageVulnerabilitySummary_low(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "medium": out.Values[i] = ec._ImageVulnerabilitySummary_medium(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "high": out.Values[i] = ec._ImageVulnerabilitySummary_high(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "critical": out.Values[i] = ec._ImageVulnerabilitySummary_critical(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "unassigned": out.Values[i] = ec._ImageVulnerabilitySummary_unassigned(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "priorityActNow": out.Values[i] = ec._ImageVulnerabilitySummary_priorityActNow(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "priorityHigh": out.Values[i] = ec._ImageVulnerabilitySummary_priorityHigh(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "priorityElevated": out.Values[i] = ec._ImageVulnerabilitySummary_priorityElevated(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "priorityMonitor": out.Values[i] = ec._ImageVulnerabilitySummary_priorityMonitor(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } case "lastUpdated": out.Values[i] = ec._ImageVulnerabilitySummary_lastUpdated(ctx, field, obj) case "staleImageTag": - field := field - - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ImageVulnerabilitySummary_staleImageTag(ctx, field, obj) - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + out.Values[i] = ec._ImageVulnerabilitySummary_staleImageTag(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 3d5b2cfcb..2f5878ff1 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -284,16 +284,16 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - "Number of vulnerabilities with priority ACT_NOW." + "Number of vulnerabilities with risk tier IMMEDIATE." priorityActNow: Int! - "Number of vulnerabilities with priority HIGH." + "Number of vulnerabilities with risk tier HIGH." priorityHigh: Int! - "Number of vulnerabilities with priority ELEVATED." + "Number of vulnerabilities with risk tier ELEVATED." priorityElevated: Int! - "Number of vulnerabilities with priority MONITOR." + "Number of vulnerabilities with risk tier MONITOR." priorityMonitor: Int! "Timestamp of the last update of the vulnerability summary." @@ -388,7 +388,7 @@ type ImageVulnerability implements Node { enum CVEPriority { "Vulnerability is known to be actively exploited and requires immediate action." - ACT_NOW + IMMEDIATE "Vulnerability is associated with ransomware or has a high EPSS percentile." HIGH "Vulnerability has a critical or high severity and elevated EPSS percentile." @@ -611,11 +611,11 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by priority ACT_NOW count" + Order by IMMEDIATE risk-tier count" """ VULNERABILITY_PRIORITY_ACT_NOW """ - Order by priority HIGH count" + Order by HIGH risk-tier count" """ VULNERABILITY_PRIORITY_HIGH } diff --git a/internal/graph/vulnerability.resolvers.go b/internal/graph/vulnerability.resolvers.go index 7529d8d23..fbc3c706e 100644 --- a/internal/graph/vulnerability.resolvers.go +++ b/internal/graph/vulnerability.resolvers.go @@ -2,7 +2,6 @@ package graph import ( "context" - "fmt" "time" "github.com/nais/api/internal/auth/authz" @@ -81,10 +80,6 @@ func (r *containerImageWorkloadReferenceResolver) Workload(ctx context.Context, return getWorkload(ctx, obj.Reference, obj.TeamSlug, environmentmapper.EnvironmentName(obj.EnvironmentName)) } -func (r *imageVulnerabilitySummaryResolver) StaleImageTag(ctx context.Context, obj *vulnerability.ImageVulnerabilitySummary) (*string, error) { - panic(fmt.Errorf("not implemented: StaleImageTag - staleImageTag")) -} - func (r *jobResolver) ImageVulnerabilityHistory(ctx context.Context, obj *job.Job, from scalar.Date) (*vulnerability.ImageVulnerabilityHistory, error) { return vulnerability.GetWorkloadVulnerabilityHistoryForWorkload(ctx, obj, from.Time()) } @@ -178,10 +173,6 @@ func (r *Resolver) ContainerImageWorkloadReference() gengql.ContainerImageWorklo return &containerImageWorkloadReferenceResolver{r} } -func (r *Resolver) ImageVulnerabilitySummary() gengql.ImageVulnerabilitySummaryResolver { - return &imageVulnerabilitySummaryResolver{r} -} - func (r *Resolver) TeamVulnerabilitySummary() gengql.TeamVulnerabilitySummaryResolver { return &teamVulnerabilitySummaryResolver{r} } @@ -194,7 +185,6 @@ type ( cVEResolver struct{ *Resolver } containerImageSBOMResolver struct{ *Resolver } containerImageWorkloadReferenceResolver struct{ *Resolver } - imageVulnerabilitySummaryResolver struct{ *Resolver } teamVulnerabilitySummaryResolver struct{ *Resolver } workloadVulnerabilitySummaryResolver struct{ *Resolver } ) diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 827a8d994..5fc5f30ff 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -38,10 +38,10 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... ImageTag: "tag1", }, VulnerabilitySummary: &vulnerabilities.Summary{ - Critical: 5, - RiskScore: 250, - PriorityActNow: 2, - PriorityHigh: 3, + Critical: 5, + RiskScore: 250, + ActNow: 2, + HighRisk: 3, }, SbomStatus: &vulnerabilities.SbomStatusInfo{ Status: vulnerabilities.SbomStatus_SBOM_STATUS_READY, @@ -72,10 +72,10 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... ImageTag: "tag1", }, VulnerabilitySummary: &vulnerabilities.Summary{ - Critical: 5, - RiskScore: 250, - PriorityActNow: 2, - PriorityHigh: 3, + Critical: 5, + RiskScore: 250, + ActNow: 2, + HighRisk: 3, }, SbomStatus: &vulnerabilities.SbomStatusInfo{ Status: vulnerabilities.SbomStatus_SBOM_STATUS_READY, @@ -186,9 +186,9 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { } summary := node.VulnerabilitySummary - if summary != nil && (summary.PriorityActNow > 0 || summary.PriorityHigh > 0) { + if summary != nil && (summary.ActNow > 0 || summary.HighRisk > 0) { severity := issue.SeverityWarning - if summary.PriorityActNow > 0 { + if summary.ActNow > 0 { severity = issue.SeverityCritical } ret = append(ret, &Issue{ @@ -199,10 +199,10 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: environmentmapper.EnvironmentName(node.Workload.GetCluster()), Severity: severity, Message: fmt.Sprintf( - "Image '%s' has %d ACT_NOW and %d HIGH priority vulnerabilities", + "Image '%s' has %d IMMEDIATE and %d HIGH risk-tier vulnerabilities", node.Workload.ImageName, - summary.PriorityActNow, - summary.PriorityHigh, + summary.ActNow, + summary.HighRisk, ), IssueDetails: issue.VulnerableImageIssueDetails{ Critical: int(summary.Critical), @@ -296,7 +296,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { continue } - if node.VulnerabilitySummary == nil || node.VulnerabilitySummary.PriorityActNow == 0 { + if node.VulnerabilitySummary == nil || node.VulnerabilitySummary.ActNow == 0 { continue } @@ -320,12 +320,12 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload with external ingresses %s has %d ACT_NOW priority vulnerabilities", + "Workload with external ingresses %s has %d IMMEDIATE risk-tier vulnerabilities", strings.Join(externalIngresses, ", "), - node.VulnerabilitySummary.PriorityActNow, + node.VulnerabilitySummary.ActNow, ), IssueDetails: issue.ExternalIngressActNowVulnerabilityIssueDetails{ - PriorityActNow: int(node.VulnerabilitySummary.PriorityActNow), + PriorityActNow: int(node.VulnerabilitySummary.ActNow), Ingresses: externalIngresses, }, }) diff --git a/internal/vulnerability/fake/fakedata.go b/internal/vulnerability/fake/fakedata.go index cc65b2e68..ee5280c61 100644 --- a/internal/vulnerability/fake/fakedata.go +++ b/internal/vulnerability/fake/fakedata.go @@ -77,19 +77,19 @@ func createFakeData(ctx context.Context) *fakeData { func createWorkloadSummary(env, team, workloadType, name, image string, vulnFactor int32) *vulnerabilities.WorkloadSummary { imageName, imageTag := workload.FormatImageReferenceForLookup(image) summary := &vulnerabilities.Summary{ - Critical: vulnFactor, - High: vulnFactor * 2, - Medium: vulnFactor + 2, - Low: vulnFactor + 1, - Unassigned: vulnFactor, - Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, - RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, - HasSbom: true, - LastUpdated: timestamppb.New(time.Now()), - PriorityActNow: vulnFactor, - PriorityHigh: vulnFactor * 2, - PriorityElevated: vulnFactor * 3, - PriorityMonitor: vulnFactor * 4, + Critical: vulnFactor, + High: vulnFactor * 2, + Medium: vulnFactor + 2, + Low: vulnFactor + 1, + Unassigned: vulnFactor, + Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, + RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, + HasSbom: true, + LastUpdated: timestamppb.New(time.Now()), + ActNow: vulnFactor, + HighRisk: vulnFactor * 2, + ElevatedRisk: vulnFactor * 3, + Monitor: vulnFactor * 4, } if name == "no-errors" { @@ -122,39 +122,44 @@ func createWorkloadSummary(env, team, workloadType, name, image string, vulnFact func createVulnerabilities(w *vulnerabilities.WorkloadSummary) []*vulnerabilities.Vulnerability { findings := make([]*vulnerabilities.Vulnerability, 0) - priorities := []vulnerabilities.Priority{ - vulnerabilities.Priority_PRIORITY_ACT_NOW, - vulnerabilities.Priority_PRIORITY_HIGH, - vulnerabilities.Priority_PRIORITY_ELEVATED, - vulnerabilities.Priority_PRIORITY_MONITOR, + tiers := []vulnerabilities.RiskTier{ + vulnerabilities.RiskTier_ACT_NOW, + vulnerabilities.RiskTier_HIGH_RISK, + vulnerabilities.RiskTier_ELEVATED_RISK, + vulnerabilities.RiskTier_MONITOR, } idx := 0 - nextPriority := func() vulnerabilities.Priority { - p := priorities[idx%len(priorities)] + nextRiskTier := func() vulnerabilities.RiskTier { + p := tiers[idx%len(tiers)] idx++ return p } epssScore := 0.85 epssPercentile := 97.3 for i := range w.VulnerabilitySummary.Critical { - findings = append(findings, createVulnerability(vulnerabilities.Severity_CRITICAL, fmt.Sprintf("some-component-%d", i), nextPriority(), &epssScore, &epssPercentile, true, false)) + _ = nextRiskTier() + findings = append(findings, createVulnerability(vulnerabilities.Severity_CRITICAL, fmt.Sprintf("some-component-%d", i), &epssScore, &epssPercentile, true, false)) } for i := range w.VulnerabilitySummary.High { - findings = append(findings, createVulnerability(vulnerabilities.Severity_HIGH, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) + _ = nextRiskTier() + findings = append(findings, createVulnerability(vulnerabilities.Severity_HIGH, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Medium { - findings = append(findings, createVulnerability(vulnerabilities.Severity_MEDIUM, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) + _ = nextRiskTier() + findings = append(findings, createVulnerability(vulnerabilities.Severity_MEDIUM, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Low { - findings = append(findings, createVulnerability(vulnerabilities.Severity_LOW, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) + _ = nextRiskTier() + findings = append(findings, createVulnerability(vulnerabilities.Severity_LOW, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Unassigned { - findings = append(findings, createVulnerability(vulnerabilities.Severity_UNASSIGNED, fmt.Sprintf("some-component-%d", i), nextPriority(), nil, nil, false, false)) + _ = nextRiskTier() + findings = append(findings, createVulnerability(vulnerabilities.Severity_UNASSIGNED, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } return findings } -func createVulnerability(severity vulnerabilities.Severity, componentName string, priority vulnerabilities.Priority, epssScore, epssPercentile *float64, hasKevEntry, knownRansomwareUse bool) *vulnerabilities.Vulnerability { +func createVulnerability(severity vulnerabilities.Severity, componentName string, epssScore, epssPercentile *float64, hasKevEntry, knownRansomwareUse bool) *vulnerabilities.Vulnerability { return &vulnerabilities.Vulnerability{ Id: uuid.New().String(), Package: fmt.Sprintf("pkg:golang/%s@v2.0.8?type=module", componentName), @@ -165,7 +170,6 @@ func createVulnerability(severity vulnerabilities.Severity, componentName string Link: "", Severity: severity, References: nil, - Priority: priority, EpssScore: epssScore, EpssPercentile: epssPercentile, HasKevEntry: hasKevEntry, diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index 8599deda9..5a4d25986 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -423,15 +423,15 @@ type VulnerabilityFixSample struct { type CVEPriority string const ( - CVEPriorityActNow CVEPriority = "ACT_NOW" - CVEPriorityHigh CVEPriority = "HIGH" - CVEPriorityElevated CVEPriority = "ELEVATED" - CVEPriorityMonitor CVEPriority = "MONITOR" + CVEPriorityImmediate CVEPriority = "IMMEDIATE" + CVEPriorityHigh CVEPriority = "HIGH" + CVEPriorityElevated CVEPriority = "ELEVATED" + CVEPriorityMonitor CVEPriority = "MONITOR" ) func (e CVEPriority) IsValid() bool { switch e { - case CVEPriorityActNow, CVEPriorityHigh, CVEPriorityElevated, CVEPriorityMonitor: + case CVEPriorityImmediate, CVEPriorityHigh, CVEPriorityElevated, CVEPriorityMonitor: return true } return false diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index 7874d50d1..e6a758b3c 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -628,7 +628,7 @@ func ListCVEs(ctx context.Context, page *pagination.Pagination, orderBy *CVEOrde case CVEOrderFieldAffectedWorkloadsCount: field = vulnerabilities.OrderByAffectedWorkloads case CVEOrderFieldPriority: - field = vulnerabilities.OrderByPriority + field = vulnerabilities.OrderByTopRiskTier default: field = vulnerabilities.OrderByCvssScore } diff --git a/internal/vulnerability/sortfilter.go b/internal/vulnerability/sortfilter.go index 49625cb64..2ff475293 100644 --- a/internal/vulnerability/sortfilter.go +++ b/internal/vulnerability/sortfilter.go @@ -16,7 +16,7 @@ var SortFilterImageVulnerabilities = map[ImageVulnerabilityOrderField]vulnerabil "STATE": vulnerabilities.OrderByReason, "SUPPRESSED": vulnerabilities.OrderBySuppressed, "SEVERITY_SINCE": vulnerabilities.OrderBySeveritySince, - "PRIORITY": vulnerabilities.OrderByPriority, + "PRIORITY": vulnerabilities.OrderByTopRiskTier, } var SortFilterWorkloadSummaries = map[VulnerabilitySummaryOrderByField]vulnerabilities.OrderByField{ @@ -28,8 +28,8 @@ var SortFilterWorkloadSummaries = map[VulnerabilitySummaryOrderByField]vulnerabi "VULNERABILITY_SEVERITY_MEDIUM": vulnerabilities.OrderByMedium, "VULNERABILITY_SEVERITY_LOW": vulnerabilities.OrderByLow, "VULNERABILITY_SEVERITY_UNASSIGNED": vulnerabilities.OrderByUnassigned, - "VULNERABILITY_PRIORITY_ACT_NOW": vulnerabilities.OrderByPriorityActNow, - "VULNERABILITY_PRIORITY_HIGH": vulnerabilities.OrderByPriorityHigh, + "VULNERABILITY_PRIORITY_ACT_NOW": vulnerabilities.OrderByActNow, + "VULNERABILITY_PRIORITY_HIGH": vulnerabilities.OrderByHighRisk, } const ( diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 6b60cb8e6..b98f59118 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -81,10 +81,10 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa Total: int(v13sSummary.Total), RiskScore: int(v13sSummary.RiskScore), LastUpdated: lastUpdated, - PriorityActNow: int(v13sSummary.PriorityActNow), - PriorityHigh: int(v13sSummary.PriorityHigh), - PriorityElevated: int(v13sSummary.PriorityElevated), - PriorityMonitor: int(v13sSummary.PriorityMonitor), + PriorityActNow: int(v13sSummary.ActNow), + PriorityHigh: int(v13sSummary.HighRisk), + PriorityElevated: int(v13sSummary.ElevatedRisk), + PriorityMonitor: int(v13sSummary.Monitor), } return &WorkloadVulnerabilitySummary{ @@ -107,7 +107,7 @@ func toCVE(cve *vulnerabilities.Cve) *CVE { DetailsLink: cve.Link, CVSSScore: cve.CvssScore, Severity: ImageVulnerabilitySeverity(cve.Severity.String()), - Priority: parseCVEPriority(cve.Priority), + Priority: parseCVEPriority(cve), EpssScore: cve.EpssScore, EpssPercentile: cve.EpssPercentile, HasKevEntry: cve.HasKevEntry, @@ -115,17 +115,24 @@ func toCVE(cve *vulnerabilities.Cve) *CVE { } } -func parseCVEPriority(p vulnerabilities.Priority) CVEPriority { - switch p { - case vulnerabilities.Priority_PRIORITY_ACT_NOW: - return CVEPriorityActNow - case vulnerabilities.Priority_PRIORITY_HIGH: +func parseCVEPriority(cve *vulnerabilities.Cve) CVEPriority { + if cve == nil { + return CVEPriorityMonitor + } + + if cve.GetHasKevEntry() { + return CVEPriorityImmediate + } + + if cve.GetKnownRansomwareUse() || cve.GetEpssPercentile() >= 0.90 { return CVEPriorityHigh - case vulnerabilities.Priority_PRIORITY_ELEVATED: + } + + if (cve.GetSeverity() == vulnerabilities.Severity_CRITICAL || cve.GetSeverity() == vulnerabilities.Severity_HIGH) && cve.GetEpssPercentile() >= 0.50 { return CVEPriorityElevated - default: - return CVEPriorityMonitor } + + return CVEPriorityMonitor } func mapVulnerabilitySeverity(severity ImageVulnerabilitySeverity) vulnerabilities.Severity { From 92438d56d0de043b12ca1d811ec409e00eb56ed2 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:23:31 +0200 Subject: [PATCH 08/36] test(vulnerability): cover CVE risk-tier derivation --- internal/graph/gengql/root_.generated.go | 2 +- internal/graph/schema/issues.graphqls | 2 +- internal/vulnerability/transform_test.go | 86 ++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 internal/vulnerability/transform_test.go diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 043516866..a968b803f 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -23668,7 +23668,7 @@ type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { ingresses: [String!]! } -"Raised when a workload with external ingresses has one or more ACT_NOW priority vulnerabilities." +"Raised when a workload with external ingresses has one or more IMMEDIATE risk-tier vulnerabilities." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index 0365566f7..6752a70af 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -258,7 +258,7 @@ type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { ingresses: [String!]! } -"Raised when a workload with external ingresses has one or more ACT_NOW priority vulnerabilities." +"Raised when a workload with external ingresses has one or more IMMEDIATE risk-tier vulnerabilities." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! diff --git a/internal/vulnerability/transform_test.go b/internal/vulnerability/transform_test.go new file mode 100644 index 000000000..3ecc35018 --- /dev/null +++ b/internal/vulnerability/transform_test.go @@ -0,0 +1,86 @@ +package vulnerability + +import ( + "testing" + + "github.com/nais/v13s/pkg/api/vulnerabilities" +) + +func TestParseCVEPriority(t *testing.T) { + tests := []struct { + name string + cve *vulnerabilities.Cve + want CVEPriority + }{ + { + name: "nil cve defaults to monitor", + cve: nil, + want: CVEPriorityMonitor, + }, + { + name: "kev is immediate", + cve: &vulnerabilities.Cve{ + HasKevEntry: true, + KnownRansomwareUse: true, + EpssPercentile: new(0.99), + Severity: vulnerabilities.Severity_CRITICAL, + }, + want: CVEPriorityImmediate, + }, + { + name: "ransomware use is high", + cve: &vulnerabilities.Cve{ + KnownRansomwareUse: true, + }, + want: CVEPriorityHigh, + }, + { + name: "epss percentile threshold is high", + cve: &vulnerabilities.Cve{ + EpssPercentile: new(0.90), + }, + want: CVEPriorityHigh, + }, + { + name: "critical severity and elevated epss is elevated", + cve: &vulnerabilities.Cve{ + Severity: vulnerabilities.Severity_CRITICAL, + EpssPercentile: new(0.50), + }, + want: CVEPriorityElevated, + }, + { + name: "high severity and elevated epss is elevated", + cve: &vulnerabilities.Cve{ + Severity: vulnerabilities.Severity_HIGH, + EpssPercentile: new(0.50), + }, + want: CVEPriorityElevated, + }, + { + name: "low severity and elevated epss stays monitor", + cve: &vulnerabilities.Cve{ + Severity: vulnerabilities.Severity_LOW, + EpssPercentile: new(0.70), + }, + want: CVEPriorityMonitor, + }, + { + name: "high severity below elevated epss stays monitor", + cve: &vulnerabilities.Cve{ + Severity: vulnerabilities.Severity_HIGH, + EpssPercentile: new(0.49), + }, + want: CVEPriorityMonitor, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := parseCVEPriority(tt.cve) + if got != tt.want { + t.Fatalf("parseCVEPriority() = %s, want %s", got, tt.want) + } + }) + } +} From 571be0874d0bee6fe018eb24545c4cd0d5eaa47b Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 2 Jun 2026 14:18:27 +0200 Subject: [PATCH 09/36] feat(vulnerability): expose fixVersion on image vulnerabilities --- internal/graph/gengql/root_.generated.go | 13 ++++++++++ .../graph/gengql/vulnerability.generated.go | 25 +++++++++++++++++++ internal/graph/schema/vulnerability.graphqls | 3 +++ internal/vulnerability/models.go | 1 + internal/vulnerability/transform.go | 1 + 5 files changed, 43 insertions(+) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index a968b803f..eab1bd70c 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -1062,6 +1062,7 @@ type ComplexityRoot struct { ImageVulnerability struct { CvssScore func(childComplexity int) int Description func(childComplexity int) int + FixVersion func(childComplexity int) int ID func(childComplexity int) int Identifier func(childComplexity int) int Package func(childComplexity int) int @@ -7296,6 +7297,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerability.Description(childComplexity), true + case "ImageVulnerability.fixVersion": + if e.ComplexityRoot.ImageVulnerability.FixVersion == nil { + break + } + + return e.ComplexityRoot.ImageVulnerability.FixVersion(childComplexity), true + case "ImageVulnerability.id": if e.ComplexityRoot.ImageVulnerability.ID == nil { break @@ -31998,6 +32006,9 @@ type ImageVulnerability implements Node { "Package name of the vulnerability." package: String! + "First known package version that contains a fix." + fixVersion: String + suppression: ImageVulnerabilitySuppression "Timestamp of when the vulnerability got its current severity." @@ -34306,6 +34317,8 @@ func (ec *executionContext) childFields_ImageVulnerability(ctx context.Context, return ec.fieldContext_ImageVulnerability_description(ctx, field) case "package": return ec.fieldContext_ImageVulnerability_package(ctx, field) + case "fixVersion": + return ec.fieldContext_ImageVulnerability_fixVersion(ctx, field) case "suppression": return ec.fieldContext_ImageVulnerability_suppression(ctx, field) case "severitySince": diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index 47108d2df..f100ef871 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -935,6 +935,29 @@ func (ec *executionContext) fieldContext_ImageVulnerability_package(_ context.Co return graphql.NewScalarFieldContext("ImageVulnerability", field, false, false, errors.New("field of type String does not have child fields")) } +func (ec *executionContext) _ImageVulnerability_fixVersion(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerability) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerability_fixVersion(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.FixVersion, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerability_fixVersion(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerability", field, false, false, errors.New("field of type String does not have child fields")) +} + func (ec *executionContext) _ImageVulnerability_suppression(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerability) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3921,6 +3944,8 @@ func (ec *executionContext) _ImageVulnerability(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { out.Invalids++ } + case "fixVersion": + out.Values[i] = ec._ImageVulnerability_fixVersion(ctx, field, obj) case "suppression": out.Values[i] = ec._ImageVulnerability_suppression(ctx, field, obj) case "severitySince": diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 2f5878ff1..c9d16bfa1 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -374,6 +374,9 @@ type ImageVulnerability implements Node { "Package name of the vulnerability." package: String! + "First known package version that contains a fix." + fixVersion: String + suppression: ImageVulnerabilitySuppression "Timestamp of when the vulnerability got its current severity." diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index 5a4d25986..c4089f859 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -48,6 +48,7 @@ type ImageVulnerability struct { CvssScore *float64 `json:"cvssScore"` Description string `json:"description"` Package string `json:"package"` + FixVersion *string `json:"fixVersion,omitempty"` SeveritySince *time.Time `json:"severitySince"` Suppression *ImageVulnerabilitySuppression `json:"suppression"` VulnerabilityDetailsLink string `json:"vulnerabilityDetailsLink"` diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index b98f59118..e2eaade37 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -28,6 +28,7 @@ func toImageVulnerability(v *vulnerabilities.Vulnerability) *ImageVulnerability CvssScore: v.GetCve().CvssScore, Description: description, Package: v.Package, + FixVersion: v.FixVersion, SeveritySince: severitySince, VulnerabilityDetailsLink: v.Cve.Link, } From 4902d262c1520620437285da5fea676aa267bc7a Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:43:27 +0200 Subject: [PATCH 10/36] fix(vulnerability): handle RISK_TIER_UNSPECIFIED in risk-tier mapping --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 46980802f..f23a8fbef 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3 + github.com/nais/v13s/pkg/api v0.0.0-20260603094115-97cc35c043d9 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index 578f1edf7..73147ea45 100644 --- a/go.sum +++ b/go.sum @@ -817,6 +817,8 @@ github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e h1:7fut4nxp6NlX7 github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3 h1:ei4Q9fAPa0dtBo7aBwCUuVrDRXA9jtipqsU6PhOcPYw= github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= +github.com/nais/v13s/pkg/api v0.0.0-20260603094115-97cc35c043d9 h1:lwdg5KcNrjpBhqhFQtD1YK/GQmVBve1inZanhEDgGco= +github.com/nais/v13s/pkg/api v0.0.0-20260603094115-97cc35c043d9/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= From 9b73b2b3d458664b8b40fa91e1134683a66754a4 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:52:25 +0200 Subject: [PATCH 11/36] feat(vulnerability): expose KEV and EPSS fields on ImageVulnerability --- internal/graph/gengql/root_.generated.go | 52 +++++++++ .../graph/gengql/vulnerability.generated.go | 106 ++++++++++++++++++ internal/graph/schema/vulnerability.graphqls | 12 ++ internal/vulnerability/models.go | 4 + internal/vulnerability/transform.go | 4 + 5 files changed, 178 insertions(+) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index eab1bd70c..95e57664f 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -1062,9 +1062,13 @@ type ComplexityRoot struct { ImageVulnerability struct { CvssScore func(childComplexity int) int Description func(childComplexity int) int + EpssPercentile func(childComplexity int) int + EpssScore func(childComplexity int) int FixVersion func(childComplexity int) int + HasKevEntry func(childComplexity int) int ID func(childComplexity int) int Identifier func(childComplexity int) int + KnownRansomwareUse func(childComplexity int) int Package func(childComplexity int) int Severity func(childComplexity int) int SeveritySince func(childComplexity int) int @@ -7297,6 +7301,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerability.Description(childComplexity), true + case "ImageVulnerability.epssPercentile": + if e.ComplexityRoot.ImageVulnerability.EpssPercentile == nil { + break + } + + return e.ComplexityRoot.ImageVulnerability.EpssPercentile(childComplexity), true + + case "ImageVulnerability.epssScore": + if e.ComplexityRoot.ImageVulnerability.EpssScore == nil { + break + } + + return e.ComplexityRoot.ImageVulnerability.EpssScore(childComplexity), true + case "ImageVulnerability.fixVersion": if e.ComplexityRoot.ImageVulnerability.FixVersion == nil { break @@ -7304,6 +7322,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerability.FixVersion(childComplexity), true + case "ImageVulnerability.hasKevEntry": + if e.ComplexityRoot.ImageVulnerability.HasKevEntry == nil { + break + } + + return e.ComplexityRoot.ImageVulnerability.HasKevEntry(childComplexity), true + case "ImageVulnerability.id": if e.ComplexityRoot.ImageVulnerability.ID == nil { break @@ -7318,6 +7343,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerability.Identifier(childComplexity), true + case "ImageVulnerability.knownRansomwareUse": + if e.ComplexityRoot.ImageVulnerability.KnownRansomwareUse == nil { + break + } + + return e.ComplexityRoot.ImageVulnerability.KnownRansomwareUse(childComplexity), true + case "ImageVulnerability.package": if e.ComplexityRoot.ImageVulnerability.Package == nil { break @@ -32019,6 +32051,18 @@ type ImageVulnerability implements Node { "CVSS score of the vulnerability." cvssScore: Float + + "EPSS score of the vulnerability." + epssScore: Float + + "EPSS percentile of the vulnerability (0-1)." + epssPercentile: Float + + "Whether the vulnerability has a CISA KEV entry." + hasKevEntry: Boolean! + + "Whether the vulnerability has known ransomware use." + knownRansomwareUse: Boolean! } enum CVEPriority { @@ -34327,6 +34371,14 @@ func (ec *executionContext) childFields_ImageVulnerability(ctx context.Context, return ec.fieldContext_ImageVulnerability_vulnerabilityDetailsLink(ctx, field) case "cvssScore": return ec.fieldContext_ImageVulnerability_cvssScore(ctx, field) + case "epssScore": + return ec.fieldContext_ImageVulnerability_epssScore(ctx, field) + case "epssPercentile": + return ec.fieldContext_ImageVulnerability_epssPercentile(ctx, field) + case "hasKevEntry": + return ec.fieldContext_ImageVulnerability_hasKevEntry(ctx, field) + case "knownRansomwareUse": + return ec.fieldContext_ImageVulnerability_knownRansomwareUse(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type ImageVulnerability", field.Name) } diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index f100ef871..2680013fd 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -1059,6 +1059,98 @@ func (ec *executionContext) fieldContext_ImageVulnerability_cvssScore(_ context. return graphql.NewScalarFieldContext("ImageVulnerability", field, false, false, errors.New("field of type Float does not have child fields")) } +func (ec *executionContext) _ImageVulnerability_epssScore(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerability) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerability_epssScore(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.EpssScore, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *float64) graphql.Marshaler { + return ec.marshalOFloat2ᚖfloat64(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerability_epssScore(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerability", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerability_epssPercentile(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerability) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerability_epssPercentile(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.EpssPercentile, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *float64) graphql.Marshaler { + return ec.marshalOFloat2ᚖfloat64(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerability_epssPercentile(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerability", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerability_hasKevEntry(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerability) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerability_hasKevEntry(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.HasKevEntry, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerability_hasKevEntry(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerability", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerability_knownRansomwareUse(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerability) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerability_knownRansomwareUse(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.KnownRansomwareUse, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerability_knownRansomwareUse(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerability", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + func (ec *executionContext) _ImageVulnerabilityConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *pagination.Connection[*vulnerability.ImageVulnerability]) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3957,6 +4049,20 @@ func (ec *executionContext) _ImageVulnerability(ctx context.Context, sel ast.Sel } case "cvssScore": out.Values[i] = ec._ImageVulnerability_cvssScore(ctx, field, obj) + case "epssScore": + out.Values[i] = ec._ImageVulnerability_epssScore(ctx, field, obj) + case "epssPercentile": + out.Values[i] = ec._ImageVulnerability_epssPercentile(ctx, field, obj) + case "hasKevEntry": + out.Values[i] = ec._ImageVulnerability_hasKevEntry(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "knownRansomwareUse": + out.Values[i] = ec._ImageVulnerability_knownRansomwareUse(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index c9d16bfa1..03a279b7a 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -387,6 +387,18 @@ type ImageVulnerability implements Node { "CVSS score of the vulnerability." cvssScore: Float + + "EPSS score of the vulnerability." + epssScore: Float + + "EPSS percentile of the vulnerability (0-1)." + epssPercentile: Float + + "Whether the vulnerability has a CISA KEV entry." + hasKevEntry: Boolean! + + "Whether the vulnerability has known ransomware use." + knownRansomwareUse: Boolean! } enum CVEPriority { diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index c4089f859..c822edc17 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -46,6 +46,10 @@ type ImageVulnerability struct { Identifier string `json:"identifier"` Severity ImageVulnerabilitySeverity `json:"severity"` CvssScore *float64 `json:"cvssScore"` + EpssScore *float64 `json:"epssScore"` + EpssPercentile *float64 `json:"epssPercentile"` + HasKevEntry bool `json:"hasKevEntry"` + KnownRansomwareUse bool `json:"knownRansomwareUse"` Description string `json:"description"` Package string `json:"package"` FixVersion *string `json:"fixVersion,omitempty"` diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index e2eaade37..6aad4e9f3 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -26,6 +26,10 @@ func toImageVulnerability(v *vulnerabilities.Vulnerability) *ImageVulnerability Identifier: v.Cve.Id, Severity: ImageVulnerabilitySeverity(v.Cve.Severity.String()), CvssScore: v.GetCve().CvssScore, + EpssScore: v.GetCve().EpssScore, + EpssPercentile: v.GetCve().EpssPercentile, + HasKevEntry: v.GetCve().HasKevEntry, + KnownRansomwareUse: v.GetCve().KnownRansomwareUse, Description: description, Package: v.Package, FixVersion: v.FixVersion, From 3c9d4f494c688de84f3284153d2baee7177a2141 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:12:06 +0200 Subject: [PATCH 12/36] fix(vulnerability): map PRIORITY sort to cve priority order --- internal/vulnerability/queries.go | 2 +- internal/vulnerability/sortfilter.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index e6a758b3c..7874d50d1 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -628,7 +628,7 @@ func ListCVEs(ctx context.Context, page *pagination.Pagination, orderBy *CVEOrde case CVEOrderFieldAffectedWorkloadsCount: field = vulnerabilities.OrderByAffectedWorkloads case CVEOrderFieldPriority: - field = vulnerabilities.OrderByTopRiskTier + field = vulnerabilities.OrderByPriority default: field = vulnerabilities.OrderByCvssScore } diff --git a/internal/vulnerability/sortfilter.go b/internal/vulnerability/sortfilter.go index 2ff475293..a682a60c7 100644 --- a/internal/vulnerability/sortfilter.go +++ b/internal/vulnerability/sortfilter.go @@ -16,7 +16,7 @@ var SortFilterImageVulnerabilities = map[ImageVulnerabilityOrderField]vulnerabil "STATE": vulnerabilities.OrderByReason, "SUPPRESSED": vulnerabilities.OrderBySuppressed, "SEVERITY_SINCE": vulnerabilities.OrderBySeveritySince, - "PRIORITY": vulnerabilities.OrderByTopRiskTier, + "PRIORITY": vulnerabilities.OrderByPriority, } var SortFilterWorkloadSummaries = map[VulnerabilitySummaryOrderByField]vulnerabilities.OrderByField{ From 19626b1524631e6d46a1747d0ad56e7dbc71166e Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:09:54 +0200 Subject: [PATCH 13/36] chore(deps): bump v13s api after exploitable removal --- go.mod | 2 +- go.sum | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index f23a8fbef..64121c936 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260603094115-97cc35c043d9 + github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index 73147ea45..02f5bc079 100644 --- a/go.sum +++ b/go.sum @@ -813,12 +813,8 @@ github.com/nais/tester v0.1.1 h1:tpJ5HKpu3mEIWX/mec0Yj0xLHEpt+MwTAsj282n0Py0= github.com/nais/tester v0.1.1/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= -github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e h1:7fut4nxp6NlX7xS5SnwLkdyAhWanqmcpuClssn/cT8Q= -github.com/nais/v13s/pkg/api v0.0.0-20260528121134-739c7136ac8e/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= -github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3 h1:ei4Q9fAPa0dtBo7aBwCUuVrDRXA9jtipqsU6PhOcPYw= -github.com/nais/v13s/pkg/api v0.0.0-20260602103534-af9d5e6d27d3/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= -github.com/nais/v13s/pkg/api v0.0.0-20260603094115-97cc35c043d9 h1:lwdg5KcNrjpBhqhFQtD1YK/GQmVBve1inZanhEDgGco= -github.com/nais/v13s/pkg/api v0.0.0-20260603094115-97cc35c043d9/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= +github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716 h1:FpEOQH7TP50xuVCkkcMk+ZaSRnxhmHZmuhDVPGL56sU= +github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= From c9a6541a28e2395ec2aa8a09bfbe1fb8bccc6dc6 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:34:57 +0200 Subject: [PATCH 14/36] fix(vulnerability): clean enum docs and normalize fake EPSS percentile --- internal/graph/gengql/root_.generated.go | 16 ++++++++-------- internal/graph/schema/vulnerability.graphqls | 16 ++++++++-------- internal/vulnerability/fake/fakedata.go | 19 +------------------ 3 files changed, 17 insertions(+), 34 deletions(-) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 95e57664f..4ebd35542 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -32266,35 +32266,35 @@ enum VulnerabilitySummaryOrderByField { """ ENVIRONMENT """ - Order by risk score" + Order by risk score. """ VULNERABILITY_RISK_SCORE """ - Order by vulnerability severity critical" + Order by vulnerability severity critical. """ VULNERABILITY_SEVERITY_CRITICAL """ - Order by vulnerability severity high" + Order by vulnerability severity high. """ VULNERABILITY_SEVERITY_HIGH """ - Order by vulnerability severity medium" + Order by vulnerability severity medium. """ VULNERABILITY_SEVERITY_MEDIUM """ - Order by vulnerability severity low" + Order by vulnerability severity low. """ VULNERABILITY_SEVERITY_LOW """ - Order by vulnerability severity unassigned" + Order by vulnerability severity unassigned. """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by IMMEDIATE risk-tier count" + Order by IMMEDIATE risk-tier count. """ VULNERABILITY_PRIORITY_ACT_NOW """ - Order by HIGH risk-tier count" + Order by HIGH risk-tier count. """ VULNERABILITY_PRIORITY_HIGH } diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 03a279b7a..054caa6ff 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -602,35 +602,35 @@ enum VulnerabilitySummaryOrderByField { """ ENVIRONMENT """ - Order by risk score" + Order by risk score. """ VULNERABILITY_RISK_SCORE """ - Order by vulnerability severity critical" + Order by vulnerability severity critical. """ VULNERABILITY_SEVERITY_CRITICAL """ - Order by vulnerability severity high" + Order by vulnerability severity high. """ VULNERABILITY_SEVERITY_HIGH """ - Order by vulnerability severity medium" + Order by vulnerability severity medium. """ VULNERABILITY_SEVERITY_MEDIUM """ - Order by vulnerability severity low" + Order by vulnerability severity low. """ VULNERABILITY_SEVERITY_LOW """ - Order by vulnerability severity unassigned" + Order by vulnerability severity unassigned. """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by IMMEDIATE risk-tier count" + Order by IMMEDIATE risk-tier count. """ VULNERABILITY_PRIORITY_ACT_NOW """ - Order by HIGH risk-tier count" + Order by HIGH risk-tier count. """ VULNERABILITY_PRIORITY_HIGH } diff --git a/internal/vulnerability/fake/fakedata.go b/internal/vulnerability/fake/fakedata.go index ee5280c61..96b711736 100644 --- a/internal/vulnerability/fake/fakedata.go +++ b/internal/vulnerability/fake/fakedata.go @@ -122,38 +122,21 @@ func createWorkloadSummary(env, team, workloadType, name, image string, vulnFact func createVulnerabilities(w *vulnerabilities.WorkloadSummary) []*vulnerabilities.Vulnerability { findings := make([]*vulnerabilities.Vulnerability, 0) - tiers := []vulnerabilities.RiskTier{ - vulnerabilities.RiskTier_ACT_NOW, - vulnerabilities.RiskTier_HIGH_RISK, - vulnerabilities.RiskTier_ELEVATED_RISK, - vulnerabilities.RiskTier_MONITOR, - } - idx := 0 - nextRiskTier := func() vulnerabilities.RiskTier { - p := tiers[idx%len(tiers)] - idx++ - return p - } epssScore := 0.85 - epssPercentile := 97.3 + epssPercentile := 0.973 for i := range w.VulnerabilitySummary.Critical { - _ = nextRiskTier() findings = append(findings, createVulnerability(vulnerabilities.Severity_CRITICAL, fmt.Sprintf("some-component-%d", i), &epssScore, &epssPercentile, true, false)) } for i := range w.VulnerabilitySummary.High { - _ = nextRiskTier() findings = append(findings, createVulnerability(vulnerabilities.Severity_HIGH, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Medium { - _ = nextRiskTier() findings = append(findings, createVulnerability(vulnerabilities.Severity_MEDIUM, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Low { - _ = nextRiskTier() findings = append(findings, createVulnerability(vulnerabilities.Severity_LOW, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } for i := range w.VulnerabilitySummary.Unassigned { - _ = nextRiskTier() findings = append(findings, createVulnerability(vulnerabilities.Severity_UNASSIGNED, fmt.Sprintf("some-component-%d", i), nil, nil, false, false)) } return findings From c66a29cefac5c8da0d927a275999f2b568aa9bee Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:42:55 +0200 Subject: [PATCH 15/36] feat(vulnerability): add ransomwareCount, highEpssCount, and topRiskTier to ImageVulnerabilitySummary --- go.mod | 4 +- go.sum | 8 ++ internal/graph/gengql/root_.generated.go | 39 ++++++++ .../graph/gengql/vulnerability.generated.go | 97 +++++++++++++++++++ internal/graph/schema/vulnerability.graphqls | 9 ++ internal/vulnerability/fake/fakedata.go | 29 +++--- internal/vulnerability/fake/v13s.go | 22 +++-- internal/vulnerability/models.go | 29 +++--- internal/vulnerability/queries.go | 46 ++++++--- internal/vulnerability/transform.go | 22 +++++ 10 files changed, 254 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index 64121c936..1584899b3 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716 + github.com/nais/v13s/pkg/api v0.0.0-20260609085403-62cd3b4768b7 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 @@ -239,7 +239,7 @@ require ( github.com/google/flatbuffers v25.12.19+incompatible // indirect github.com/google/gnostic-models v0.7.1 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.16 // indirect github.com/googleapis/gax-go/v2 v2.22.0 // indirect github.com/gookit/color v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect diff --git a/go.sum b/go.sum index 02f5bc079..d11965358 100644 --- a/go.sum +++ b/go.sum @@ -522,6 +522,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/enterprise-certificate-proxy v0.3.16 h1:F/VPrx0YPBdksZJQdCAp0WUsqnNmZpUZszzfYt0M5Dw= +github.com/googleapis/enterprise-certificate-proxy v0.3.16/go.mod h1:9Yb0eAkH/Xqhvv3zbeKf/+wMJqCeocWc6KIhDvEAuYE= github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0= @@ -815,6 +817,8 @@ github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716 h1:FpEOQH7TP50xuVCkkcMk+ZaSRnxhmHZmuhDVPGL56sU= github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= +github.com/nais/v13s/pkg/api v0.0.0-20260609085403-62cd3b4768b7 h1:mKyZ86EL7tvEaGuY6gj0IDmdYf1cKc2ujG0jer1RVpA= +github.com/nais/v13s/pkg/api v0.0.0-20260609085403-62cd3b4768b7/go.mod h1:D1ungKHGg4t242rWJXdusLiOs6clseEezpt1o1pU4aE= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -1389,6 +1393,8 @@ gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= google.golang.org/api v0.280.0 h1:F4OfEHZhZh6a7uTufJAXXVd/2TQ8EjM4vZH+jX/vFYk= google.golang.org/api v0.280.0/go.mod h1:oGKmPZRDoD3vdkf6MA7F4VNkR1rxCiuaPSkhsf3EolU= +google.golang.org/api v0.283.0 h1:0lkp8u0MPwJVHqRL+nJlMAoZVVzbmiXmFHXMOTmSPik= +google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genai v1.54.0 h1:ZQCa70WMTJDI11FdqWCzGvZ5PanpcpfoO6jl/lrSnGU= @@ -1405,6 +1411,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 h1:PvEgGJf9C/1u5CHkInMg7UFYYUoiaQmW2LbtH0pjB78= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 4ebd35542..a09263a22 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -1099,6 +1099,7 @@ type ComplexityRoot struct { ImageVulnerabilitySummary struct { Critical func(childComplexity int) int High func(childComplexity int) int + HighEpssCount func(childComplexity int) int LastUpdated func(childComplexity int) int Low func(childComplexity int) int Medium func(childComplexity int) int @@ -1106,8 +1107,10 @@ type ComplexityRoot struct { PriorityElevated func(childComplexity int) int PriorityHigh func(childComplexity int) int PriorityMonitor func(childComplexity int) int + RansomwareCount func(childComplexity int) int RiskScore func(childComplexity int) int StaleImageTag func(childComplexity int) int + TopRiskTier func(childComplexity int) int Total func(childComplexity int) int Unassigned func(childComplexity int) int } @@ -7455,6 +7458,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.High(childComplexity), true + case "ImageVulnerabilitySummary.highEpssCount": + if e.ComplexityRoot.ImageVulnerabilitySummary.HighEpssCount == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.HighEpssCount(childComplexity), true + case "ImageVulnerabilitySummary.lastUpdated": if e.ComplexityRoot.ImageVulnerabilitySummary.LastUpdated == nil { break @@ -7504,6 +7514,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityMonitor(childComplexity), true + case "ImageVulnerabilitySummary.ransomwareCount": + if e.ComplexityRoot.ImageVulnerabilitySummary.RansomwareCount == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.RansomwareCount(childComplexity), true + case "ImageVulnerabilitySummary.riskScore": if e.ComplexityRoot.ImageVulnerabilitySummary.RiskScore == nil { break @@ -7518,6 +7535,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.StaleImageTag(childComplexity), true + case "ImageVulnerabilitySummary.topRiskTier": + if e.ComplexityRoot.ImageVulnerabilitySummary.TopRiskTier == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.TopRiskTier(childComplexity), true + case "ImageVulnerabilitySummary.total": if e.ComplexityRoot.ImageVulnerabilitySummary.Total == nil { break @@ -31960,6 +31984,15 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with risk tier MONITOR." priorityMonitor: Int! + "Number of vulnerabilities associated with known ransomware campaigns." + ransomwareCount: Int! + + "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." + highEpssCount: Int! + + "The highest risk tier among vulnerabilities in this summary." + topRiskTier: CVEPriority + "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -34447,6 +34480,12 @@ func (ec *executionContext) childFields_ImageVulnerabilitySummary(ctx context.Co return ec.fieldContext_ImageVulnerabilitySummary_priorityElevated(ctx, field) case "priorityMonitor": return ec.fieldContext_ImageVulnerabilitySummary_priorityMonitor(ctx, field) + case "ransomwareCount": + return ec.fieldContext_ImageVulnerabilitySummary_ransomwareCount(ctx, field) + case "highEpssCount": + return ec.fieldContext_ImageVulnerabilitySummary_highEpssCount(ctx, field) + case "topRiskTier": + return ec.fieldContext_ImageVulnerabilitySummary_topRiskTier(ctx, field) case "lastUpdated": return ec.fieldContext_ImageVulnerabilitySummary_lastUpdated(ctx, field) case "staleImageTag": diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index 2680013fd..5072f0130 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -1642,6 +1642,75 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityMonit return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) } +func (ec *executionContext) _ImageVulnerabilitySummary_ransomwareCount(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_ransomwareCount(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.RansomwareCount, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_ransomwareCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummary_highEpssCount(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_highEpssCount(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.HighEpssCount, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_highEpssCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummary_topRiskTier(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_topRiskTier(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.TopRiskTier, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *vulnerability.CVEPriority) graphql.Marshaler { + return ec.marshalOCVEPriority2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_topRiskTier(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type CVEPriority does not have child fields")) +} + func (ec *executionContext) _ImageVulnerabilitySummary_lastUpdated(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -4328,6 +4397,18 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel if out.Values[i] == graphql.Null { out.Invalids++ } + case "ransomwareCount": + out.Values[i] = ec._ImageVulnerabilitySummary_ransomwareCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "highEpssCount": + out.Values[i] = ec._ImageVulnerabilitySummary_highEpssCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "topRiskTier": + out.Values[i] = ec._ImageVulnerabilitySummary_topRiskTier(ctx, field, obj) case "lastUpdated": out.Values[i] = ec._ImageVulnerabilitySummary_lastUpdated(ctx, field, obj) case "staleImageTag": @@ -5728,6 +5809,22 @@ func (ec *executionContext) unmarshalOCVEOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋin return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalOCVEPriority2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx context.Context, v any) (*vulnerability.CVEPriority, error) { + if v == nil { + return nil, nil + } + var res = new(vulnerability.CVEPriority) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOCVEPriority2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx context.Context, sel ast.SelectionSet, v *vulnerability.CVEPriority) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + func (ec *executionContext) unmarshalOCVEWorkloadsFilter2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEWorkloadsFilter(ctx context.Context, v any) (*vulnerability.CVEWorkloadsFilter, error) { if v == nil { return nil, nil diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 054caa6ff..d336b7b37 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -296,6 +296,15 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with risk tier MONITOR." priorityMonitor: Int! + "Number of vulnerabilities associated with known ransomware campaigns." + ransomwareCount: Int! + + "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." + highEpssCount: Int! + + "The highest risk tier among vulnerabilities in this summary." + topRiskTier: CVEPriority + "Timestamp of the last update of the vulnerability summary." lastUpdated: Time diff --git a/internal/vulnerability/fake/fakedata.go b/internal/vulnerability/fake/fakedata.go index 96b711736..b2eaae356 100644 --- a/internal/vulnerability/fake/fakedata.go +++ b/internal/vulnerability/fake/fakedata.go @@ -77,19 +77,22 @@ func createFakeData(ctx context.Context) *fakeData { func createWorkloadSummary(env, team, workloadType, name, image string, vulnFactor int32) *vulnerabilities.WorkloadSummary { imageName, imageTag := workload.FormatImageReferenceForLookup(image) summary := &vulnerabilities.Summary{ - Critical: vulnFactor, - High: vulnFactor * 2, - Medium: vulnFactor + 2, - Low: vulnFactor + 1, - Unassigned: vulnFactor, - Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, - RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, - HasSbom: true, - LastUpdated: timestamppb.New(time.Now()), - ActNow: vulnFactor, - HighRisk: vulnFactor * 2, - ElevatedRisk: vulnFactor * 3, - Monitor: vulnFactor * 4, + Critical: vulnFactor, + High: vulnFactor * 2, + Medium: vulnFactor + 2, + Low: vulnFactor + 1, + Unassigned: vulnFactor, + Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, + RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, + HasSbom: true, + LastUpdated: timestamppb.New(time.Now()), + ActNow: vulnFactor, + HighRisk: vulnFactor * 2, + ElevatedRisk: vulnFactor * 3, + Monitor: vulnFactor * 4, + RansomwareCount: vulnFactor, + HighEpssCount: vulnFactor * 2, + TopRiskTier: vulnerabilities.RiskTier_ACT_NOW, } if name == "no-errors" { diff --git a/internal/vulnerability/fake/v13s.go b/internal/vulnerability/fake/v13s.go index f623006bc..9939b1a0c 100644 --- a/internal/vulnerability/fake/v13s.go +++ b/internal/vulnerability/fake/v13s.go @@ -189,13 +189,21 @@ func (f *fakeVulnerabilitiesClient) GetVulnerabilitySummaryTimeSeries(ctx contex resp := &vulnerabilities.GetVulnerabilitySummaryTimeSeriesResponse{ Points: []*vulnerabilities.VulnerabilitySummaryPoint{ { - Total: 1, - Critical: 1, - High: 1, - Medium: 1, - Low: 1, - Unassigned: 1, - BucketTime: timestamppb.New(time.Now()), + Total: 1, + Critical: 1, + High: 1, + Medium: 1, + Low: 1, + Unassigned: 1, + RiskScore: 10, + ActNow: 1, + HighRisk: 2, + ElevatedRisk: 3, + Monitor: 4, + RansomwareCount: 1, + HighEpssCount: 2, + TopRiskTier: vulnerabilities.RiskTier_ACT_NOW, + BucketTime: timestamppb.New(time.Now()), }, }, } diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index c822edc17..aa7797eff 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -76,19 +76,22 @@ type ImageVulnerabilitySuppression struct { } type ImageVulnerabilitySummary struct { - Total int `json:"total"` - RiskScore int `json:"riskScore"` - Low int `json:"low"` - Medium int `json:"medium"` - High int `json:"high"` - Critical int `json:"critical"` - Unassigned int `json:"unassigned"` - LastUpdated *time.Time `json:"lastUpdated"` - StaleImageTag *string `json:"staleImageTag"` - PriorityActNow int `json:"priorityActNow"` - PriorityHigh int `json:"priorityHigh"` - PriorityElevated int `json:"priorityElevated"` - PriorityMonitor int `json:"priorityMonitor"` + Total int `json:"total"` + RiskScore int `json:"riskScore"` + Low int `json:"low"` + Medium int `json:"medium"` + High int `json:"high"` + Critical int `json:"critical"` + Unassigned int `json:"unassigned"` + LastUpdated *time.Time `json:"lastUpdated"` + StaleImageTag *string `json:"staleImageTag"` + PriorityActNow int `json:"priorityActNow"` + PriorityHigh int `json:"priorityHigh"` + PriorityElevated int `json:"priorityElevated"` + PriorityMonitor int `json:"priorityMonitor"` + RansomwareCount int `json:"ransomwareCount"` + HighEpssCount int `json:"highEpssCount"` + TopRiskTier *CVEPriority `json:"topRiskTier"` } type ImageVulnerabilityOrderField string diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index 7874d50d1..2851e9b58 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -365,15 +365,22 @@ func GetImageVulnerabilitySummary(ctx context.Context, ref string) (*ImageVulner } return &ImageVulnerabilitySummary{ - Critical: int(sum.GetCritical()), - High: int(sum.GetHigh()), - Medium: int(sum.GetMedium()), - Low: int(sum.GetLow()), - Unassigned: int(sum.GetUnassigned()), - Total: int(sum.GetTotal()), - RiskScore: int(sum.GetRiskScore()), - LastUpdated: lastUpdated, - StaleImageTag: sum.StaleImageTag, + Critical: int(sum.GetCritical()), + High: int(sum.GetHigh()), + Medium: int(sum.GetMedium()), + Low: int(sum.GetLow()), + Unassigned: int(sum.GetUnassigned()), + Total: int(sum.GetTotal()), + RiskScore: int(sum.GetRiskScore()), + LastUpdated: lastUpdated, + StaleImageTag: sum.StaleImageTag, + PriorityActNow: int(sum.GetActNow()), + PriorityHigh: int(sum.GetHighRisk()), + PriorityElevated: int(sum.GetElevatedRisk()), + PriorityMonitor: int(sum.GetMonitor()), + RansomwareCount: int(sum.GetRansomwareCount()), + HighEpssCount: int(sum.GetHighEpssCount()), + TopRiskTier: mapRiskTier(sum.GetTopRiskTier()), }, nil } @@ -486,13 +493,20 @@ func getVulnerabilityHistory(ctx context.Context, opts []vulnerabilities.Option) for _, point := range resp.GetPoints() { samples = append(samples, &ImageVulnerabilitySample{ Summary: &ImageVulnerabilitySummary{ - Critical: int(point.GetCritical()), - High: int(point.GetHigh()), - Medium: int(point.GetMedium()), - Low: int(point.GetLow()), - Unassigned: int(point.GetUnassigned()), - Total: int(point.GetTotal()), - RiskScore: int(point.GetRiskScore()), + Critical: int(point.GetCritical()), + High: int(point.GetHigh()), + Medium: int(point.GetMedium()), + Low: int(point.GetLow()), + Unassigned: int(point.GetUnassigned()), + Total: int(point.GetTotal()), + RiskScore: int(point.GetRiskScore()), + PriorityActNow: int(point.GetActNow()), + PriorityHigh: int(point.GetHighRisk()), + PriorityElevated: int(point.GetElevatedRisk()), + PriorityMonitor: int(point.GetMonitor()), + RansomwareCount: int(point.GetRansomwareCount()), + HighEpssCount: int(point.GetHighEpssCount()), + TopRiskTier: mapRiskTier(point.GetTopRiskTier()), }, Date: point.GetBucketTime().AsTime(), }) diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 6aad4e9f3..8d1dac97d 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -90,6 +90,9 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa PriorityHigh: int(v13sSummary.HighRisk), PriorityElevated: int(v13sSummary.ElevatedRisk), PriorityMonitor: int(v13sSummary.Monitor), + RansomwareCount: int(v13sSummary.GetRansomwareCount()), + HighEpssCount: int(v13sSummary.GetHighEpssCount()), + TopRiskTier: mapRiskTier(v13sSummary.GetTopRiskTier()), } return &WorkloadVulnerabilitySummary{ @@ -156,3 +159,22 @@ func mapVulnerabilitySeverity(severity ImageVulnerabilitySeverity) vulnerabiliti return vulnerabilities.Severity_UNASSIGNED } } + +func mapRiskTier(tier vulnerabilities.RiskTier) *CVEPriority { + switch tier { + case vulnerabilities.RiskTier_ACT_NOW: + p := CVEPriorityImmediate + return &p + case vulnerabilities.RiskTier_HIGH_RISK: + p := CVEPriorityHigh + return &p + case vulnerabilities.RiskTier_ELEVATED_RISK: + p := CVEPriorityElevated + return &p + case vulnerabilities.RiskTier_MONITOR: + p := CVEPriorityMonitor + return &p + default: + return nil + } +} From b664cbedb7db8042175aafec622b9cdcecfb2c2b Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Wed, 10 Jun 2026 10:30:33 +0200 Subject: [PATCH 16/36] feat(vulnerability): add riskTier field to vulnerability summaries --- internal/graph/gengql/root_.generated.go | 12 ++++++++++++ internal/graph/gengql/vulnerability.generated.go | 16 +++++++++++++++- internal/graph/schema/vulnerability.graphqls | 12 ++++++++++++ internal/vulnerability/models.go | 4 +++- internal/vulnerability/queries.go | 4 ++++ internal/vulnerability/transform.go | 15 +++++++++++++++ 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index a09263a22..cda708020 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -31937,6 +31937,18 @@ input TeamVulnerabilitySummaryFilter { Only return vulnerability summaries for the given environment. """ environmentName: String + + """ + Deprecated: use environmentName instead. + Only one environment is supported if this list is used. + """ + environments: [String!] + @deprecated(reason: "Use environmentName instead. Only one value is supported.") + + """ + Only return vulnerability summaries at or above the given risk tier. + """ + riskTier: CVEPriority } """ diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index 5072f0130..494f3012e 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -3469,7 +3469,7 @@ func (ec *executionContext) unmarshalInputTeamVulnerabilitySummaryFilter(ctx con asMap[k] = v } - fieldsInOrder := [...]string{"environmentName"} + fieldsInOrder := [...]string{"environmentName", "environments", "riskTier"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -3483,6 +3483,20 @@ func (ec *executionContext) unmarshalInputTeamVulnerabilitySummaryFilter(ctx con return it, err } it.EnvironmentName = data + case "environments": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("environments")) + data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.Environments = data + case "riskTier": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("riskTier")) + data, err := ec.unmarshalOCVEPriority2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx, v) + if err != nil { + return it, err + } + it.RiskTier = data } } return it, nil diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index d336b7b37..71a01101f 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -249,6 +249,18 @@ input TeamVulnerabilitySummaryFilter { Only return vulnerability summaries for the given environment. """ environmentName: String + + """ + Deprecated: use environmentName instead. + Only one environment is supported if this list is used. + """ + environments: [String!] + @deprecated(reason: "Use environmentName instead. Only one value is supported.") + + """ + Only return vulnerability summaries at or above the given risk tier. + """ + riskTier: CVEPriority } """ diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index aa7797eff..960b677ea 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -390,7 +390,9 @@ func (e ImageVulnerabilitySuppressionState) MarshalGQL(w io.Writer) { } type TeamVulnerabilitySummaryFilter struct { - EnvironmentName *string `json:"environmentName,omitempty"` + EnvironmentName *string `json:"environmentName,omitempty"` + Environments []string `json:"environments,omitempty"` + RiskTier *CVEPriority `json:"riskTier,omitempty"` } type ImageVulnerabilitySample struct { diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index 2851e9b58..80ef594fc 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -70,6 +70,10 @@ func ListVulnerabilitySummaries(ctx context.Context, s slug.Slug, filter *TeamVu opts = append(opts, vulnerabilities.Offset(page.Offset())) opts = append(opts, vulnerabilities.Limit(page.Limit())) + if filter != nil && filter.RiskTier != nil { + opts = append(opts, vulnerabilities.RiskTierFilter(mapCVEPriorityToRiskTier(*filter.RiskTier))) + } + if orderBy != nil { direction := vulnerabilities.Direction_ASC if orderBy.Direction == model.OrderDirectionDesc { diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 8d1dac97d..084e10877 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -178,3 +178,18 @@ func mapRiskTier(tier vulnerabilities.RiskTier) *CVEPriority { return nil } } + +func mapCVEPriorityToRiskTier(p CVEPriority) vulnerabilities.RiskTier { + switch p { + case CVEPriorityImmediate: + return vulnerabilities.RiskTier_ACT_NOW + case CVEPriorityHigh: + return vulnerabilities.RiskTier_HIGH_RISK + case CVEPriorityElevated: + return vulnerabilities.RiskTier_ELEVATED_RISK + case CVEPriorityMonitor: + return vulnerabilities.RiskTier_MONITOR + default: + return vulnerabilities.RiskTier_RISK_TIER_UNSPECIFIED + } +} From 1e5d87444c41daa8c908d9c2a979fa6f9eebd8bc Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:17:39 +0200 Subject: [PATCH 17/36] chore(deps): bump v13s api --- go.mod | 4 ++-- go.sum | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 1584899b3..1ed54d596 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260609085403-62cd3b4768b7 + github.com/nais/v13s/pkg/api v0.0.0-20260616093957-c9e73f2e0468 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 @@ -76,7 +76,7 @@ require ( go.opentelemetry.io/otel/trace v1.44.0 golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa golang.org/x/oauth2 v0.36.0 - golang.org/x/sync v0.20.0 + golang.org/x/sync v0.21.0 golang.org/x/text v0.37.0 golang.org/x/tools v0.45.0 google.golang.org/api v0.280.0 diff --git a/go.sum b/go.sum index d11965358..156fdee2a 100644 --- a/go.sum +++ b/go.sum @@ -520,8 +520,6 @@ github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= -github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/enterprise-certificate-proxy v0.3.16 h1:F/VPrx0YPBdksZJQdCAp0WUsqnNmZpUZszzfYt0M5Dw= github.com/googleapis/enterprise-certificate-proxy v0.3.16/go.mod h1:9Yb0eAkH/Xqhvv3zbeKf/+wMJqCeocWc6KIhDvEAuYE= github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= @@ -815,10 +813,10 @@ github.com/nais/tester v0.1.1 h1:tpJ5HKpu3mEIWX/mec0Yj0xLHEpt+MwTAsj282n0Py0= github.com/nais/tester v0.1.1/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= -github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716 h1:FpEOQH7TP50xuVCkkcMk+ZaSRnxhmHZmuhDVPGL56sU= -github.com/nais/v13s/pkg/api v0.0.0-20260604080807-5ff2f400c716/go.mod h1:KBuEYLBJOFM36G7D5RAZ5oRyUv0/IOK9JCgkUS1eqqY= -github.com/nais/v13s/pkg/api v0.0.0-20260609085403-62cd3b4768b7 h1:mKyZ86EL7tvEaGuY6gj0IDmdYf1cKc2ujG0jer1RVpA= -github.com/nais/v13s/pkg/api v0.0.0-20260609085403-62cd3b4768b7/go.mod h1:D1ungKHGg4t242rWJXdusLiOs6clseEezpt1o1pU4aE= +github.com/nais/v13s/pkg/api v0.0.0-20260616070346-d983f8b497ce h1:RyffeqIrkGC3r5eklstBEMtrqVzMqLwao059X6ojEmk= +github.com/nais/v13s/pkg/api v0.0.0-20260616070346-d983f8b497ce/go.mod h1:D1ungKHGg4t242rWJXdusLiOs6clseEezpt1o1pU4aE= +github.com/nais/v13s/pkg/api v0.0.0-20260616093957-c9e73f2e0468 h1:tQR2pmEtny96qrf/vi7s7vvmb0FtjYSKpNwNy7mLyXo= +github.com/nais/v13s/pkg/api v0.0.0-20260616093957-c9e73f2e0468/go.mod h1:Ct3ihc4Qjjxt2h92Z+qttn0kkgtR8JQ7pmTF7PExH3s= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -1317,6 +1315,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1391,10 +1391,10 @@ gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -google.golang.org/api v0.280.0 h1:F4OfEHZhZh6a7uTufJAXXVd/2TQ8EjM4vZH+jX/vFYk= -google.golang.org/api v0.280.0/go.mod h1:oGKmPZRDoD3vdkf6MA7F4VNkR1rxCiuaPSkhsf3EolU= google.golang.org/api v0.283.0 h1:0lkp8u0MPwJVHqRL+nJlMAoZVVzbmiXmFHXMOTmSPik= google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM= +google.golang.org/api v0.284.0 h1:i+cKTgeQRcRySkP7QTl5PDO7/pAm8EcMFIUMlNbk4Vc= +google.golang.org/api v0.284.0/go.mod h1:AU44fU+XVZOCcd8uLaBIa/ZgzgPf/0qqY3+m7lQaado= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genai v1.54.0 h1:ZQCa70WMTJDI11FdqWCzGvZ5PanpcpfoO6jl/lrSnGU= @@ -1413,6 +1413,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 h1:PvEgGJf9C/1u5CHkInMg7UFYYUoiaQmW2LbtH0pjB78= google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= From ba67dd5341c9e483f9fcfac634434ef3bc229e94 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:18:04 +0200 Subject: [PATCH 18/36] feat(vulnerability): align CVE priority with v13s priority --- internal/vulnerability/fake/fakedata.go | 2 +- internal/vulnerability/fake/v13s.go | 2 +- internal/vulnerability/queries.go | 6 +- internal/vulnerability/transform.go | 81 ++++++++++++++++--------- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/internal/vulnerability/fake/fakedata.go b/internal/vulnerability/fake/fakedata.go index b2eaae356..9a219c019 100644 --- a/internal/vulnerability/fake/fakedata.go +++ b/internal/vulnerability/fake/fakedata.go @@ -92,7 +92,7 @@ func createWorkloadSummary(env, team, workloadType, name, image string, vulnFact Monitor: vulnFactor * 4, RansomwareCount: vulnFactor, HighEpssCount: vulnFactor * 2, - TopRiskTier: vulnerabilities.RiskTier_ACT_NOW, + TopPriority: vulnerabilities.Priority_PRIORITY_ACT_NOW, } if name == "no-errors" { diff --git a/internal/vulnerability/fake/v13s.go b/internal/vulnerability/fake/v13s.go index 9939b1a0c..eba200710 100644 --- a/internal/vulnerability/fake/v13s.go +++ b/internal/vulnerability/fake/v13s.go @@ -202,7 +202,7 @@ func (f *fakeVulnerabilitiesClient) GetVulnerabilitySummaryTimeSeries(ctx contex Monitor: 4, RansomwareCount: 1, HighEpssCount: 2, - TopRiskTier: vulnerabilities.RiskTier_ACT_NOW, + TopPriority: vulnerabilities.Priority_PRIORITY_ACT_NOW, BucketTime: timestamppb.New(time.Now()), }, }, diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index 80ef594fc..c64c8b255 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -71,7 +71,7 @@ func ListVulnerabilitySummaries(ctx context.Context, s slug.Slug, filter *TeamVu opts = append(opts, vulnerabilities.Limit(page.Limit())) if filter != nil && filter.RiskTier != nil { - opts = append(opts, vulnerabilities.RiskTierFilter(mapCVEPriorityToRiskTier(*filter.RiskTier))) + opts = append(opts, vulnerabilities.PriorityFilter(mapCVEPriorityToPriority(*filter.RiskTier))) } if orderBy != nil { @@ -384,7 +384,7 @@ func GetImageVulnerabilitySummary(ctx context.Context, ref string) (*ImageVulner PriorityMonitor: int(sum.GetMonitor()), RansomwareCount: int(sum.GetRansomwareCount()), HighEpssCount: int(sum.GetHighEpssCount()), - TopRiskTier: mapRiskTier(sum.GetTopRiskTier()), + TopRiskTier: mapPriority(sum.GetTopPriority()), }, nil } @@ -510,7 +510,7 @@ func getVulnerabilityHistory(ctx context.Context, opts []vulnerabilities.Option) PriorityMonitor: int(point.GetMonitor()), RansomwareCount: int(point.GetRansomwareCount()), HighEpssCount: int(point.GetHighEpssCount()), - TopRiskTier: mapRiskTier(point.GetTopRiskTier()), + TopRiskTier: mapPriority(point.GetTopPriority()), }, Date: point.GetBucketTime().AsTime(), }) diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 084e10877..70474d1d2 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -1,12 +1,14 @@ package vulnerability import ( + "strings" "time" "github.com/nais/api/internal/environmentmapper" "github.com/nais/api/internal/slug" "github.com/nais/api/internal/workload" "github.com/nais/v13s/pkg/api/vulnerabilities" + "google.golang.org/protobuf/reflect/protoreflect" ) func toImageVulnerability(v *vulnerabilities.Vulnerability) *ImageVulnerability { @@ -92,7 +94,7 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa PriorityMonitor: int(v13sSummary.Monitor), RansomwareCount: int(v13sSummary.GetRansomwareCount()), HighEpssCount: int(v13sSummary.GetHighEpssCount()), - TopRiskTier: mapRiskTier(v13sSummary.GetTopRiskTier()), + TopRiskTier: mapPriority(v13sSummary.GetTopPriority()), } return &WorkloadVulnerabilitySummary{ @@ -115,7 +117,7 @@ func toCVE(cve *vulnerabilities.Cve) *CVE { DetailsLink: cve.Link, CVSSScore: cve.CvssScore, Severity: ImageVulnerabilitySeverity(cve.Severity.String()), - Priority: parseCVEPriority(cve), + Priority: cvePriorityFromV13s(cve), EpssScore: cve.EpssScore, EpssPercentile: cve.EpssPercentile, HasKevEntry: cve.HasKevEntry, @@ -123,24 +125,47 @@ func toCVE(cve *vulnerabilities.Cve) *CVE { } } -func parseCVEPriority(cve *vulnerabilities.Cve) CVEPriority { +func cvePriorityFromV13s(cve *vulnerabilities.Cve) CVEPriority { if cve == nil { return CVEPriorityMonitor } - if cve.GetHasKevEntry() { - return CVEPriorityImmediate + if p, ok := cvePriorityFromEnumField(cve.ProtoReflect(), "risk_tier"); ok { + return p } - if cve.GetKnownRansomwareUse() || cve.GetEpssPercentile() >= 0.90 { - return CVEPriorityHigh + if p, ok := cvePriorityFromEnumField(cve.ProtoReflect(), "priority"); ok { + return p } - if (cve.GetSeverity() == vulnerabilities.Severity_CRITICAL || cve.GetSeverity() == vulnerabilities.Severity_HIGH) && cve.GetEpssPercentile() >= 0.50 { - return CVEPriorityElevated + return CVEPriorityMonitor +} + +func cvePriorityFromEnumField(msg protoreflect.Message, fieldName protoreflect.Name) (CVEPriority, bool) { + fd := msg.Descriptor().Fields().ByName(fieldName) + if fd == nil || fd.Kind() != protoreflect.EnumKind { + return CVEPriorityMonitor, false } - return CVEPriorityMonitor + enumValue := msg.Get(fd).Enum() + enumDescriptor := fd.Enum().Values().ByNumber(enumValue) + if enumDescriptor == nil { + return CVEPriorityMonitor, false + } + + enumName := strings.ToUpper(string(enumDescriptor.Name())) + switch { + case strings.Contains(enumName, "ACT_NOW"), strings.Contains(enumName, "IMMEDIATE"): + return CVEPriorityImmediate, true + case strings.Contains(enumName, "HIGH_RISK"), strings.Contains(enumName, "HIGH"): + return CVEPriorityHigh, true + case strings.Contains(enumName, "ELEVATED_RISK"), strings.Contains(enumName, "ELEVATED"): + return CVEPriorityElevated, true + case strings.Contains(enumName, "MONITOR"): + return CVEPriorityMonitor, true + default: + return CVEPriorityMonitor, false + } } func mapVulnerabilitySeverity(severity ImageVulnerabilitySeverity) vulnerabilities.Severity { @@ -160,36 +185,32 @@ func mapVulnerabilitySeverity(severity ImageVulnerabilitySeverity) vulnerabiliti } } -func mapRiskTier(tier vulnerabilities.RiskTier) *CVEPriority { - switch tier { - case vulnerabilities.RiskTier_ACT_NOW: - p := CVEPriorityImmediate - return &p - case vulnerabilities.RiskTier_HIGH_RISK: - p := CVEPriorityHigh - return &p - case vulnerabilities.RiskTier_ELEVATED_RISK: - p := CVEPriorityElevated - return &p - case vulnerabilities.RiskTier_MONITOR: - p := CVEPriorityMonitor - return &p +func mapPriority(priority vulnerabilities.Priority) *CVEPriority { + switch priority { + case vulnerabilities.Priority_PRIORITY_ACT_NOW: + return new(CVEPriorityImmediate) + case vulnerabilities.Priority_PRIORITY_HIGH: + return new(CVEPriorityHigh) + case vulnerabilities.Priority_PRIORITY_ELEVATED: + return new(CVEPriorityElevated) + case vulnerabilities.Priority_PRIORITY_MONITOR: + return new(CVEPriorityMonitor) default: return nil } } -func mapCVEPriorityToRiskTier(p CVEPriority) vulnerabilities.RiskTier { +func mapCVEPriorityToPriority(p CVEPriority) vulnerabilities.Priority { switch p { case CVEPriorityImmediate: - return vulnerabilities.RiskTier_ACT_NOW + return vulnerabilities.Priority_PRIORITY_ACT_NOW case CVEPriorityHigh: - return vulnerabilities.RiskTier_HIGH_RISK + return vulnerabilities.Priority_PRIORITY_HIGH case CVEPriorityElevated: - return vulnerabilities.RiskTier_ELEVATED_RISK + return vulnerabilities.Priority_PRIORITY_ELEVATED case CVEPriorityMonitor: - return vulnerabilities.RiskTier_MONITOR + return vulnerabilities.Priority_PRIORITY_MONITOR default: - return vulnerabilities.RiskTier_RISK_TIER_UNSPECIFIED + return vulnerabilities.Priority_PRIORITY_UNSPECIFIED } } From 1c6e0a17a05bc8e07260765e389af57531be847d Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:18:54 +0200 Subject: [PATCH 19/36] fix(vulnerability): remove legacy risk-tier fallback --- internal/vulnerability/transform.go | 46 +++++++---------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 70474d1d2..970c8d644 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -1,14 +1,12 @@ package vulnerability import ( - "strings" "time" "github.com/nais/api/internal/environmentmapper" "github.com/nais/api/internal/slug" "github.com/nais/api/internal/workload" "github.com/nais/v13s/pkg/api/vulnerabilities" - "google.golang.org/protobuf/reflect/protoreflect" ) func toImageVulnerability(v *vulnerabilities.Vulnerability) *ImageVulnerability { @@ -130,41 +128,17 @@ func cvePriorityFromV13s(cve *vulnerabilities.Cve) CVEPriority { return CVEPriorityMonitor } - if p, ok := cvePriorityFromEnumField(cve.ProtoReflect(), "risk_tier"); ok { - return p - } - - if p, ok := cvePriorityFromEnumField(cve.ProtoReflect(), "priority"); ok { - return p - } - - return CVEPriorityMonitor -} - -func cvePriorityFromEnumField(msg protoreflect.Message, fieldName protoreflect.Name) (CVEPriority, bool) { - fd := msg.Descriptor().Fields().ByName(fieldName) - if fd == nil || fd.Kind() != protoreflect.EnumKind { - return CVEPriorityMonitor, false - } - - enumValue := msg.Get(fd).Enum() - enumDescriptor := fd.Enum().Values().ByNumber(enumValue) - if enumDescriptor == nil { - return CVEPriorityMonitor, false - } - - enumName := strings.ToUpper(string(enumDescriptor.Name())) - switch { - case strings.Contains(enumName, "ACT_NOW"), strings.Contains(enumName, "IMMEDIATE"): - return CVEPriorityImmediate, true - case strings.Contains(enumName, "HIGH_RISK"), strings.Contains(enumName, "HIGH"): - return CVEPriorityHigh, true - case strings.Contains(enumName, "ELEVATED_RISK"), strings.Contains(enumName, "ELEVATED"): - return CVEPriorityElevated, true - case strings.Contains(enumName, "MONITOR"): - return CVEPriorityMonitor, true + switch cve.GetPriority() { + case vulnerabilities.Priority_PRIORITY_ACT_NOW: + return CVEPriorityImmediate + case vulnerabilities.Priority_PRIORITY_HIGH: + return CVEPriorityHigh + case vulnerabilities.Priority_PRIORITY_ELEVATED: + return CVEPriorityElevated + case vulnerabilities.Priority_PRIORITY_MONITOR: + return CVEPriorityMonitor default: - return CVEPriorityMonitor, false + return CVEPriorityMonitor } } From b698e607a2703f6500bf69f5707c6c2ac5f75438 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:18:58 +0200 Subject: [PATCH 20/36] fix(issue): only flag immediate vulnerabilities --- internal/issue/checker/workload_v13s.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 5fc5f30ff..38f7b8bdf 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -186,23 +186,18 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { } summary := node.VulnerabilitySummary - if summary != nil && (summary.ActNow > 0 || summary.HighRisk > 0) { - severity := issue.SeverityWarning - if summary.ActNow > 0 { - severity = issue.SeverityCritical - } + if summary != nil && summary.ActNow > 0 { ret = append(ret, &Issue{ IssueType: issue.IssueTypeVulnerableImage, ResourceType: workloadType, ResourceName: node.Workload.GetName(), Team: node.Workload.GetNamespace(), Env: environmentmapper.EnvironmentName(node.Workload.GetCluster()), - Severity: severity, + Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Image '%s' has %d IMMEDIATE and %d HIGH risk-tier vulnerabilities", + "Image '%s' has %d immediate vulnerabilities", node.Workload.ImageName, summary.ActNow, - summary.HighRisk, ), IssueDetails: issue.VulnerableImageIssueDetails{ Critical: int(summary.Critical), @@ -320,7 +315,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload with external ingresses %s has %d IMMEDIATE risk-tier vulnerabilities", + "Workload with external ingresses %s has %d immediate vulnerabilities", strings.Join(externalIngresses, ", "), node.VulnerabilitySummary.ActNow, ), From d036af8810a119c33b6437117547d3275f160755 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:19:01 +0200 Subject: [PATCH 21/36] test(vulnerability): remove outdated CVE priority derivation tests --- internal/vulnerability/transform_test.go | 86 ------------------------ 1 file changed, 86 deletions(-) delete mode 100644 internal/vulnerability/transform_test.go diff --git a/internal/vulnerability/transform_test.go b/internal/vulnerability/transform_test.go deleted file mode 100644 index 3ecc35018..000000000 --- a/internal/vulnerability/transform_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package vulnerability - -import ( - "testing" - - "github.com/nais/v13s/pkg/api/vulnerabilities" -) - -func TestParseCVEPriority(t *testing.T) { - tests := []struct { - name string - cve *vulnerabilities.Cve - want CVEPriority - }{ - { - name: "nil cve defaults to monitor", - cve: nil, - want: CVEPriorityMonitor, - }, - { - name: "kev is immediate", - cve: &vulnerabilities.Cve{ - HasKevEntry: true, - KnownRansomwareUse: true, - EpssPercentile: new(0.99), - Severity: vulnerabilities.Severity_CRITICAL, - }, - want: CVEPriorityImmediate, - }, - { - name: "ransomware use is high", - cve: &vulnerabilities.Cve{ - KnownRansomwareUse: true, - }, - want: CVEPriorityHigh, - }, - { - name: "epss percentile threshold is high", - cve: &vulnerabilities.Cve{ - EpssPercentile: new(0.90), - }, - want: CVEPriorityHigh, - }, - { - name: "critical severity and elevated epss is elevated", - cve: &vulnerabilities.Cve{ - Severity: vulnerabilities.Severity_CRITICAL, - EpssPercentile: new(0.50), - }, - want: CVEPriorityElevated, - }, - { - name: "high severity and elevated epss is elevated", - cve: &vulnerabilities.Cve{ - Severity: vulnerabilities.Severity_HIGH, - EpssPercentile: new(0.50), - }, - want: CVEPriorityElevated, - }, - { - name: "low severity and elevated epss stays monitor", - cve: &vulnerabilities.Cve{ - Severity: vulnerabilities.Severity_LOW, - EpssPercentile: new(0.70), - }, - want: CVEPriorityMonitor, - }, - { - name: "high severity below elevated epss stays monitor", - cve: &vulnerabilities.Cve{ - Severity: vulnerabilities.Severity_HIGH, - EpssPercentile: new(0.49), - }, - want: CVEPriorityMonitor, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parseCVEPriority(tt.cve) - if got != tt.want { - t.Fatalf("parseCVEPriority() = %s, want %s", got, tt.want) - } - }) - } -} From 9c91c28a61e2c5a359adfacb33cd655e9e1f37bf Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:28:43 +0200 Subject: [PATCH 22/36] feat(vulnerability): add riskTier field to vulnerability summaries --- integration_tests/issues_for_team.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index 24afaebe9..b35f9edce 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -555,7 +555,7 @@ Test.gql("VulnerableImageIssue", function(t) nodes = { { __typename = "VulnerableImageIssue", - message = "Image 'vulnerable-image' has 2 IMMEDIATE and 3 HIGH risk-tier vulnerabilities", + message = "Image 'vulnerable-image' has 2 immediate vulnerabilities", severity = "CRITICAL", critical = 5, riskScore = 250, From 80315d0a2c9994b5d1e7475c67bb510783bddc5f Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 16 Jun 2026 14:20:13 +0200 Subject: [PATCH 23/36] chore(deps): align v13s api with main --- go.mod | 2 +- go.sum | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 1ed54d596..a87629d87 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260616093957-c9e73f2e0468 + github.com/nais/v13s/pkg/api v0.0.0-20260616121454-101cee8c4b04 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index 156fdee2a..f7d272a58 100644 --- a/go.sum +++ b/go.sum @@ -813,10 +813,8 @@ github.com/nais/tester v0.1.1 h1:tpJ5HKpu3mEIWX/mec0Yj0xLHEpt+MwTAsj282n0Py0= github.com/nais/tester v0.1.1/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= -github.com/nais/v13s/pkg/api v0.0.0-20260616070346-d983f8b497ce h1:RyffeqIrkGC3r5eklstBEMtrqVzMqLwao059X6ojEmk= -github.com/nais/v13s/pkg/api v0.0.0-20260616070346-d983f8b497ce/go.mod h1:D1ungKHGg4t242rWJXdusLiOs6clseEezpt1o1pU4aE= -github.com/nais/v13s/pkg/api v0.0.0-20260616093957-c9e73f2e0468 h1:tQR2pmEtny96qrf/vi7s7vvmb0FtjYSKpNwNy7mLyXo= -github.com/nais/v13s/pkg/api v0.0.0-20260616093957-c9e73f2e0468/go.mod h1:Ct3ihc4Qjjxt2h92Z+qttn0kkgtR8JQ7pmTF7PExH3s= +github.com/nais/v13s/pkg/api v0.0.0-20260616121454-101cee8c4b04 h1:NSwxZsfsm8kYlaP0iqEwyUXXS5HR692oEYti3QYXQ3U= +github.com/nais/v13s/pkg/api v0.0.0-20260616121454-101cee8c4b04/go.mod h1:Ct3ihc4Qjjxt2h92Z+qttn0kkgtR8JQ7pmTF7PExH3s= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -1313,8 +1311,13 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +<<<<<<< HEAD golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +======= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +>>>>>>> f6297e83 (chore(deps): align v13s api with main) golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1391,8 +1394,6 @@ gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -google.golang.org/api v0.283.0 h1:0lkp8u0MPwJVHqRL+nJlMAoZVVzbmiXmFHXMOTmSPik= -google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM= google.golang.org/api v0.284.0 h1:i+cKTgeQRcRySkP7QTl5PDO7/pAm8EcMFIUMlNbk4Vc= google.golang.org/api v0.284.0/go.mod h1:AU44fU+XVZOCcd8uLaBIa/ZgzgPf/0qqY3+m7lQaado= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= From 1f20931e35a6e5750d4f12b0ddfdfa7e3d1f62cc Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:30:39 +0200 Subject: [PATCH 24/36] refactor(vulnerability): hard-remove priority summary extras --- internal/graph/gengql/issues.generated.go | 322 ------------------ internal/graph/gengql/root_.generated.go | 145 ++------ internal/graph/gengql/schema.generated.go | 7 - .../graph/gengql/vulnerability.generated.go | 40 +-- internal/graph/issues.resolvers.go | 22 +- internal/graph/schema/issues.graphqls | 14 +- internal/graph/schema/vulnerability.graphqls | 21 +- internal/issue/checker/workload_v13s.go | 98 ------ internal/issue/checker/workload_v13s_test.go | 97 ++++-- internal/issue/model.go | 33 +- internal/issue/queries.go | 9 - internal/vulnerability/fake/fakedata.go | 31 +- internal/vulnerability/fake/v13s.go | 29 +- internal/vulnerability/models.go | 31 +- internal/vulnerability/queries.go | 58 ++-- internal/vulnerability/transform.go | 29 +- 16 files changed, 239 insertions(+), 747 deletions(-) diff --git a/internal/graph/gengql/issues.generated.go b/internal/graph/gengql/issues.generated.go index ea237acc8..44e5af831 100644 --- a/internal/graph/gengql/issues.generated.go +++ b/internal/graph/gengql/issues.generated.go @@ -50,11 +50,6 @@ type ExternalIngressActNowVulnerabilityIssueResolver interface { Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) } -type ExternalIngressCriticalVulnerabilityIssueResolver interface { - TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) - - Workload(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (workload.Workload, error) -} type FailedSynchronizationIssueResolver interface { TeamEnvironment(ctx context.Context, obj *issue.FailedSynchronizationIssue) (*team.TeamEnvironment, error) @@ -801,185 +796,6 @@ func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.ID, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { - return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type ID does not have child fields")) -} - -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field) - }, - func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().TeamEnvironment(ctx, obj) - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { - return ec.marshalNTeamEnvironment2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeamEnvironment(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ExternalIngressCriticalVulnerabilityIssue", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_TeamEnvironment(ctx, field) - }, - } - return fc, nil -} - -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_severity(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.Severity, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v issue.Severity) graphql.Marshaler { - return ec.marshalNSeverity2githubᚗcomᚋnaisᚋapiᚋinternalᚋissueᚐSeverity(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Severity does not have child fields")) -} - -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.Message, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { - return ec.marshalNString2string(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) -} - -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field) - }, - func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().Workload(ctx, obj) - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v workload.Workload) graphql.Marshaler { - return ec.marshalNWorkload2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚐWorkload(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ExternalIngressCriticalVulnerabilityIssue", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") - }, - } - return fc, nil -} - -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.CvssScore, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { - return ec.marshalNFloat2float64(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Float does not have child fields")) -} - -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.Ingresses, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v []string) graphql.Marshaler { - return ec.marshalNString2ᚕstringᚄ(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) -} - func (ec *executionContext) _FailedSynchronizationIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.FailedSynchronizationIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3506,13 +3322,6 @@ func (ec *executionContext) _Issue(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } return ec._FailedSynchronizationIssue(ctx, sel, obj) - case issue.ExternalIngressCriticalVulnerabilityIssue: - return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, &obj) - case *issue.ExternalIngressCriticalVulnerabilityIssue: - if obj == nil { - return graphql.Null - } - return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) case issue.ExternalIngressActNowVulnerabilityIssue: return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) case *issue.ExternalIngressActNowVulnerabilityIssue: @@ -4068,137 +3877,6 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context return out } -var externalIngressCriticalVulnerabilityIssueImplementors = []string{"ExternalIngressCriticalVulnerabilityIssue", "Issue", "Node"} - -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressCriticalVulnerabilityIssue) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, externalIngressCriticalVulnerabilityIssueImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("ExternalIngressCriticalVulnerabilityIssue") - case "id": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "teamEnvironment": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "severity": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "message": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_message(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "workload": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "cvssScore": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "ingresses": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) - - for label, dfs := range deferred { - ec.ProcessDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var failedSynchronizationIssueImplementors = []string{"FailedSynchronizationIssue", "Issue", "Node"} func (ec *executionContext) _FailedSynchronizationIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.FailedSynchronizationIssue) graphql.Marshaler { diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index cda708020..da372449f 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -83,7 +83,6 @@ type ResolverRoot interface { DeprecatedRegistryIssue() DeprecatedRegistryIssueResolver Environment() EnvironmentResolver ExternalIngressActNowVulnerabilityIssue() ExternalIngressActNowVulnerabilityIssueResolver - ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver Ingress() IngressResolver IngressMetrics() IngressMetricsResolver @@ -963,16 +962,6 @@ type ComplexityRoot struct { Workload func(childComplexity int) int } - ExternalIngressCriticalVulnerabilityIssue struct { - CvssScore func(childComplexity int) int - ID func(childComplexity int) int - Ingresses func(childComplexity int) int - Message func(childComplexity int) int - Severity func(childComplexity int) int - TeamEnvironment func(childComplexity int) int - Workload func(childComplexity int) int - } - ExternalNetworkPolicyHost struct { Ports func(childComplexity int) int Target func(childComplexity int) int @@ -1097,22 +1086,21 @@ type ComplexityRoot struct { } ImageVulnerabilitySummary struct { - Critical func(childComplexity int) int - High func(childComplexity int) int - HighEpssCount func(childComplexity int) int - LastUpdated func(childComplexity int) int - Low func(childComplexity int) int - Medium func(childComplexity int) int - PriorityActNow func(childComplexity int) int - PriorityElevated func(childComplexity int) int - PriorityHigh func(childComplexity int) int - PriorityMonitor func(childComplexity int) int - RansomwareCount func(childComplexity int) int - RiskScore func(childComplexity int) int - StaleImageTag func(childComplexity int) int - TopRiskTier func(childComplexity int) int - Total func(childComplexity int) int - Unassigned func(childComplexity int) int + Critical func(childComplexity int) int + High func(childComplexity int) int + HighEpssCount func(childComplexity int) int + LastUpdated func(childComplexity int) int + Low func(childComplexity int) int + Medium func(childComplexity int) int + PriorityActNow func(childComplexity int) int + PriorityElevated func(childComplexity int) int + PriorityHigh func(childComplexity int) int + PriorityMonitor func(childComplexity int) int + RiskScore func(childComplexity int) int + StaleImageTag func(childComplexity int) int + TopVulnerabilityPriority func(childComplexity int) int + Total func(childComplexity int) int + Unassigned func(childComplexity int) int } ImageVulnerabilitySuppression struct { @@ -6912,55 +6900,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.cvssScore": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore == nil { - break - } - - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore(childComplexity), true - - case "ExternalIngressCriticalVulnerabilityIssue.id": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID == nil { - break - } - - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID(childComplexity), true - - case "ExternalIngressCriticalVulnerabilityIssue.ingresses": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses == nil { - break - } - - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses(childComplexity), true - - case "ExternalIngressCriticalVulnerabilityIssue.message": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message == nil { - break - } - - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message(childComplexity), true - - case "ExternalIngressCriticalVulnerabilityIssue.severity": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity == nil { - break - } - - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity(childComplexity), true - - case "ExternalIngressCriticalVulnerabilityIssue.teamEnvironment": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment == nil { - break - } - - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment(childComplexity), true - - case "ExternalIngressCriticalVulnerabilityIssue.workload": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload == nil { - break - } - - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload(childComplexity), true - case "ExternalNetworkPolicyHost.ports": if e.ComplexityRoot.ExternalNetworkPolicyHost.Ports == nil { break @@ -7514,13 +7453,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityMonitor(childComplexity), true - case "ImageVulnerabilitySummary.ransomwareCount": - if e.ComplexityRoot.ImageVulnerabilitySummary.RansomwareCount == nil { - break - } - - return e.ComplexityRoot.ImageVulnerabilitySummary.RansomwareCount(childComplexity), true - case "ImageVulnerabilitySummary.riskScore": if e.ComplexityRoot.ImageVulnerabilitySummary.RiskScore == nil { break @@ -7535,12 +7467,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.StaleImageTag(childComplexity), true - case "ImageVulnerabilitySummary.topRiskTier": - if e.ComplexityRoot.ImageVulnerabilitySummary.TopRiskTier == nil { + case "ImageVulnerabilitySummary.topVulnerabilityPriority": + if e.ComplexityRoot.ImageVulnerabilitySummary.TopVulnerabilityPriority == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.TopRiskTier(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummary.TopVulnerabilityPriority(childComplexity), true case "ImageVulnerabilitySummary.total": if e.ComplexityRoot.ImageVulnerabilitySummary.Total == nil { @@ -23703,7 +23635,6 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE - EXTERNAL_INGRESS_CRITICAL_VULNERABILITY EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." @@ -23721,18 +23652,7 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { - id: ID! - teamEnvironment: TeamEnvironment! - severity: Severity! - message: String! - - workload: Workload! - cvssScore: Float! - ingresses: [String!]! -} - -"Raised when a workload with external ingresses has one or more IMMEDIATE risk-tier vulnerabilities." +"Raised when a workload with external ingresses has one or more IMMEDIATE vulnerability-priority findings." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! @@ -31946,7 +31866,7 @@ input TeamVulnerabilitySummaryFilter { @deprecated(reason: "Use environmentName instead. Only one value is supported.") """ - Only return vulnerability summaries at or above the given risk tier. + Only return vulnerability summaries at or above the given vulnerability priority. """ riskTier: CVEPriority } @@ -31984,26 +31904,23 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - "Number of vulnerabilities with risk tier IMMEDIATE." + "Number of vulnerabilities with vulnerability priority IMMEDIATE." priorityActNow: Int! - "Number of vulnerabilities with risk tier HIGH." + "Number of vulnerabilities with vulnerability priority HIGH." priorityHigh: Int! - "Number of vulnerabilities with risk tier ELEVATED." + "Number of vulnerabilities with vulnerability priority ELEVATED." priorityElevated: Int! - "Number of vulnerabilities with risk tier MONITOR." + "Number of vulnerabilities with vulnerability priority MONITOR." priorityMonitor: Int! - "Number of vulnerabilities associated with known ransomware campaigns." - ransomwareCount: Int! - "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." highEpssCount: Int! - "The highest risk tier among vulnerabilities in this summary." - topRiskTier: CVEPriority + "The highest priority among vulnerabilities in this summary." + topVulnerabilityPriority: CVEPriority "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -32335,11 +32252,11 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by IMMEDIATE risk-tier count. + Order by IMMEDIATE vulnerability-priority count. """ VULNERABILITY_PRIORITY_ACT_NOW """ - Order by HIGH risk-tier count. + Order by HIGH vulnerability-priority count. """ VULNERABILITY_PRIORITY_HIGH } @@ -34492,12 +34409,10 @@ func (ec *executionContext) childFields_ImageVulnerabilitySummary(ctx context.Co return ec.fieldContext_ImageVulnerabilitySummary_priorityElevated(ctx, field) case "priorityMonitor": return ec.fieldContext_ImageVulnerabilitySummary_priorityMonitor(ctx, field) - case "ransomwareCount": - return ec.fieldContext_ImageVulnerabilitySummary_ransomwareCount(ctx, field) case "highEpssCount": return ec.fieldContext_ImageVulnerabilitySummary_highEpssCount(ctx, field) - case "topRiskTier": - return ec.fieldContext_ImageVulnerabilitySummary_topRiskTier(ctx, field) + case "topVulnerabilityPriority": + return ec.fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field) case "lastUpdated": return ec.fieldContext_ImageVulnerabilitySummary_lastUpdated(ctx, field) case "staleImageTag": diff --git a/internal/graph/gengql/schema.generated.go b/internal/graph/gengql/schema.generated.go index 3876800f0..998814ab2 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -6607,13 +6607,6 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._FailedSynchronizationIssue(ctx, sel, obj) - case issue.ExternalIngressCriticalVulnerabilityIssue: - return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, &obj) - case *issue.ExternalIngressCriticalVulnerabilityIssue: - if obj == nil { - return graphql.Null - } - return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) case issue.ExternalIngressActNowVulnerabilityIssue: return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) case *issue.ExternalIngressActNowVulnerabilityIssue: diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index 494f3012e..b8dc0a17f 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -1642,29 +1642,6 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityMonit return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_ransomwareCount(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_ransomwareCount(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.RansomwareCount, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { - return ec.marshalNInt2int(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_ransomwareCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) -} - func (ec *executionContext) _ImageVulnerabilitySummary_highEpssCount(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -1688,16 +1665,16 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_highEpssCount return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_topRiskTier(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummary_topVulnerabilityPriority(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_topRiskTier(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.TopRiskTier, nil + return obj.TopVulnerabilityPriority, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v *vulnerability.CVEPriority) graphql.Marshaler { @@ -1707,7 +1684,7 @@ func (ec *executionContext) _ImageVulnerabilitySummary_topRiskTier(ctx context.C false, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_topRiskTier(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type CVEPriority does not have child fields")) } @@ -4411,18 +4388,13 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel if out.Values[i] == graphql.Null { out.Invalids++ } - case "ransomwareCount": - out.Values[i] = ec._ImageVulnerabilitySummary_ransomwareCount(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } case "highEpssCount": out.Values[i] = ec._ImageVulnerabilitySummary_highEpssCount(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "topRiskTier": - out.Values[i] = ec._ImageVulnerabilitySummary_topRiskTier(ctx, field, obj) + case "topVulnerabilityPriority": + out.Values[i] = ec._ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field, obj) case "lastUpdated": out.Values[i] = ec._ImageVulnerabilitySummary_lastUpdated(ctx, field, obj) case "staleImageTag": diff --git a/internal/graph/issues.resolvers.go b/internal/graph/issues.resolvers.go index 80c46b961..a36c969eb 100644 --- a/internal/graph/issues.resolvers.go +++ b/internal/graph/issues.resolvers.go @@ -185,10 +185,6 @@ func (r *Resolver) ExternalIngressActNowVulnerabilityIssue() gengql.ExternalIngr return &externalIngressActNowVulnerabilityIssueResolver{r} } -func (r *Resolver) ExternalIngressCriticalVulnerabilityIssue() gengql.ExternalIngressCriticalVulnerabilityIssueResolver { - return &externalIngressCriticalVulnerabilityIssueResolver{r} -} - func (r *Resolver) FailedSynchronizationIssue() gengql.FailedSynchronizationIssueResolver { return &failedSynchronizationIssueResolver{r} } @@ -240,6 +236,7 @@ func (r *Resolver) WorkloadProblemIssue() gengql.WorkloadProblemIssueResolver { } type ( +<<<<<<< HEAD applicationRestartLoopIssueResolver struct{ *Resolver } deprecatedIngressIssueResolver struct{ *Resolver } deprecatedRegistryIssueResolver struct{ *Resolver } @@ -258,4 +255,21 @@ type ( valkeyIssueResolver struct{ *Resolver } vulnerableImageIssueResolver struct{ *Resolver } workloadProblemIssueResolver struct{ *Resolver } +======= + applicationRestartLoopIssueResolver struct{ *Resolver } + deprecatedIngressIssueResolver struct{ *Resolver } + deprecatedRegistryIssueResolver struct{ *Resolver } + externalIngressActNowVulnerabilityIssueResolver struct{ *Resolver } + failedSynchronizationIssueResolver struct{ *Resolver } + invalidSpecIssueResolver struct{ *Resolver } + lastRunFailedIssueResolver struct{ *Resolver } + missingSbomIssueResolver struct{ *Resolver } + noRunningInstancesIssueResolver struct{ *Resolver } + openSearchIssueResolver struct{ *Resolver } + sqlInstanceStateIssueResolver struct{ *Resolver } + sqlInstanceVersionIssueResolver struct{ *Resolver } + unleashReleaseChannelIssueResolver struct{ *Resolver } + valkeyIssueResolver struct{ *Resolver } + vulnerableImageIssueResolver struct{ *Resolver } +>>>>>>> 48bfc261 (refactor(vulnerability): hard-remove priority summary extras) ) diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index 6752a70af..8e5ec9bcc 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -229,7 +229,6 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE - EXTERNAL_INGRESS_CRITICAL_VULNERABILITY EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." @@ -247,18 +246,7 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { - id: ID! - teamEnvironment: TeamEnvironment! - severity: Severity! - message: String! - - workload: Workload! - cvssScore: Float! - ingresses: [String!]! -} - -"Raised when a workload with external ingresses has one or more IMMEDIATE risk-tier vulnerabilities." +"Raised when a workload with external ingresses has one or more IMMEDIATE vulnerability-priority findings." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 71a01101f..f4735e633 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -258,7 +258,7 @@ input TeamVulnerabilitySummaryFilter { @deprecated(reason: "Use environmentName instead. Only one value is supported.") """ - Only return vulnerability summaries at or above the given risk tier. + Only return vulnerability summaries at or above the given vulnerability priority. """ riskTier: CVEPriority } @@ -296,26 +296,23 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - "Number of vulnerabilities with risk tier IMMEDIATE." + "Number of vulnerabilities with vulnerability priority IMMEDIATE." priorityActNow: Int! - "Number of vulnerabilities with risk tier HIGH." + "Number of vulnerabilities with vulnerability priority HIGH." priorityHigh: Int! - "Number of vulnerabilities with risk tier ELEVATED." + "Number of vulnerabilities with vulnerability priority ELEVATED." priorityElevated: Int! - "Number of vulnerabilities with risk tier MONITOR." + "Number of vulnerabilities with vulnerability priority MONITOR." priorityMonitor: Int! - "Number of vulnerabilities associated with known ransomware campaigns." - ransomwareCount: Int! - "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." highEpssCount: Int! - "The highest risk tier among vulnerabilities in this summary." - topRiskTier: CVEPriority + "The highest priority among vulnerabilities in this summary." + topVulnerabilityPriority: CVEPriority "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -647,11 +644,11 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by IMMEDIATE risk-tier count. + Order by IMMEDIATE vulnerability-priority count. """ VULNERABILITY_PRIORITY_ACT_NOW """ - Order by HIGH risk-tier count. + Order by HIGH vulnerability-priority count. """ VULNERABILITY_PRIORITY_HIGH } diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 38f7b8bdf..0d13035de 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -19,7 +19,6 @@ const ( type V13sClient interface { ListVulnerabilitySummaries(ctx context.Context, opts ...vulnerabilities.Option) (*vulnerabilities.ListVulnerabilitySummariesResponse, error) - ListWorkloadsForVulnerability(ctx context.Context, vulnerabilityFilter vulnerabilities.VulnerabilityFilter, opts ...vulnerabilities.Option) (*vulnerabilities.ListWorkloadsForVulnerabilityResponse, error) } type fakeV13sClient struct{} @@ -113,47 +112,6 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... }, nil } -func (f fakeV13sClient) ListWorkloadsForVulnerability(ctx context.Context, vulnerabilityFilter vulnerabilities.VulnerabilityFilter, opts ...vulnerabilities.Option) (*vulnerabilities.ListWorkloadsForVulnerabilityResponse, error) { - if vulnerabilityFilter.CvssScore == nil || *vulnerabilityFilter.CvssScore != 10.0 { - return &vulnerabilities.ListWorkloadsForVulnerabilityResponse{}, nil - } - - return &vulnerabilities.ListWorkloadsForVulnerabilityResponse{ - Nodes: []*vulnerabilities.WorkloadForVulnerability{ - { - WorkloadRef: &vulnerabilities.Workload{ - Cluster: "dev-gcp", - Namespace: "devteam", - Type: "app", - Name: "vulnerable", - }, - Vulnerability: &vulnerabilities.Vulnerability{ - Cve: &vulnerabilities.Cve{ - Id: "CVE-FAKE-0001", - CvssScore: new(10.0), - }, - CvssScore: new(10.0), - }, - }, - { - WorkloadRef: &vulnerabilities.Workload{ - Cluster: "dev-gcp", - Namespace: "fake-team", - Type: "app", - Name: "fake-external-app", - }, - Vulnerability: &vulnerabilities.Vulnerability{ - Cve: &vulnerabilities.Cve{ - Id: "CVE-FAKE-0002", - CvssScore: new(10.0), - }, - CvssScore: new(10.0), - }, - }, - }, - }, nil -} - func (w Workload) vulnerabilities(ctx context.Context) []*Issue { mapType := func(s string) (issue.ResourceType, bool) { if s == "job" { @@ -226,64 +184,8 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { } } - cvss := 10.0 - workloadsForVulnerability, err := w.V13sClient.ListWorkloadsForVulnerability( - ctx, - vulnerabilities.VulnerabilityFilter{CvssScore: &cvss}, - vulnerabilities.Limit(v13sQueryLimit), - vulnerabilities.ExcludeClustersFilter("management"), - ) - if err != nil { - w.log.WithError(err).Error("fetch workloads for vulnerabilities with cvss score") - return ret - } - externalIngressesByWorkload := w.externalIngressesByWorkload() - seen := map[string]struct{}{} - for _, node := range workloadsForVulnerability.GetNodes() { - workloadRef := node.GetWorkloadRef() - vulnerability := node.GetVulnerability() - if workloadRef == nil || vulnerability == nil { - continue - } - - workloadType, ok := mapType(workloadRef.GetType()) - if !ok || workloadType != issue.ResourceTypeApplication { - continue - } - - env := environmentmapper.EnvironmentName(workloadRef.GetCluster()) - key := workloadKey(env, workloadRef.GetNamespace(), workloadRef.GetName()) - if _, exists := seen[key]; exists { - continue - } - - externalIngresses := externalIngressesByWorkload[key] - if len(externalIngresses) == 0 { - continue - } - seen[key] = struct{}{} - - ret = append(ret, &Issue{ - IssueType: issue.IssueTypeExternalIngressCriticalVulnerability, - ResourceType: workloadType, - ResourceName: workloadRef.GetName(), - Team: workloadRef.GetNamespace(), - Env: env, - Severity: issue.SeverityCritical, - Message: fmt.Sprintf( - "Workload with external ingresses %s has a vulnerability with CVSS score %.1f", - strings.Join(externalIngresses, ", "), - cvss, - ), - IssueDetails: issue.ExternalIngressCriticalVulnerabilityIssueDetails{ - CvssScore: cvss, - Ingresses: externalIngresses, - }, - }) - } - seenActNow := map[string]struct{}{} for _, node := range resp.GetNodes() { workloadType, ok := mapType(node.Workload.GetType()) diff --git a/internal/issue/checker/workload_v13s_test.go b/internal/issue/checker/workload_v13s_test.go index 7810737dd..23055a450 100644 --- a/internal/issue/checker/workload_v13s_test.go +++ b/internal/issue/checker/workload_v13s_test.go @@ -16,18 +16,36 @@ import ( ) type staticV13sClient struct { - workloads []*vulnerabilities.WorkloadForVulnerability + summaries []*vulnerabilities.WorkloadSummary } func (s staticV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ...vulnerabilities.Option) (*vulnerabilities.ListVulnerabilitySummariesResponse, error) { - return &vulnerabilities.ListVulnerabilitySummariesResponse{}, nil + return &vulnerabilities.ListVulnerabilitySummariesResponse{Nodes: s.summaries}, nil } -func (s staticV13sClient) ListWorkloadsForVulnerability(ctx context.Context, vulnerabilityFilter vulnerabilities.VulnerabilityFilter, opts ...vulnerabilities.Option) (*vulnerabilities.ListWorkloadsForVulnerabilityResponse, error) { - return &vulnerabilities.ListWorkloadsForVulnerabilityResponse{Nodes: s.workloads}, nil +func TestVulnerabilities_ExternalIngressActNowIssue(t *testing.T) { + tests := []struct { + name string + workloadName string + expectedIngress string + wantIssue bool + }{ + {name: "legacy external ingress class", workloadName: "ext-app-legacy", expectedIngress: "https://legacy.external.example.com", wantIssue: true}, + {name: "external haproxy ingress class", workloadName: "ext-app-haproxy", expectedIngress: "https://haproxy.external.example.com", wantIssue: true}, + {name: "external authenticated haproxy ingress class", workloadName: "ext-app-fa-haproxy", expectedIngress: "https://haproxy.fa.external.example.com", wantIssue: true}, + {name: "internal haproxy ingress class", workloadName: "internal-only-haproxy", wantIssue: false}, + {name: "unknown ingress class", workloadName: "unknown-class-ingress", wantIssue: false}, + {name: "missing ingress class", workloadName: "no-class-ingress", wantIssue: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testVulnerabilitiesExternalIngressActNowIssue(t, tt.workloadName, tt.expectedIngress, tt.wantIssue) + }) + } } -func TestVulnerabilities_ExternalIngressCriticalIssue(t *testing.T) { +func testVulnerabilitiesExternalIngressActNowIssue(t *testing.T, workloadName, expectedIngress string, wantIssue bool) { ctx := context.Background() scheme, err := kubernetes.NewScheme() @@ -58,52 +76,79 @@ func TestVulnerabilities_ExternalIngressCriticalIssue(t *testing.T) { workload := Workload{ AppWatcher: *appWatcher, IngressWatcher: *ingressWatcher, - V13sClient: staticV13sClient{workloads: []*vulnerabilities.WorkloadForVulnerability{ + V13sClient: staticV13sClient{summaries: []*vulnerabilities.WorkloadSummary{ { - WorkloadRef: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: "ext-app"}, - Vulnerability: &vulnerabilities.Vulnerability{CvssScore: new(10.0), Cve: &vulnerabilities.Cve{CvssScore: new(10.0)}}, + Workload: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: workloadName}, + VulnerabilitySummary: &vulnerabilities.Summary{ + Critical: 2, + RiskScore: 100, + ActNow: 2, + }, }, { - WorkloadRef: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: "ext-app"}, - Vulnerability: &vulnerabilities.Vulnerability{CvssScore: new(10.0), Cve: &vulnerabilities.Cve{CvssScore: new(10.0)}}, + Workload: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: workloadName}, + VulnerabilitySummary: &vulnerabilities.Summary{ + Critical: 2, + RiskScore: 100, + ActNow: 2, + }, }, { - WorkloadRef: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: "internal-only"}, - Vulnerability: &vulnerabilities.Vulnerability{CvssScore: new(10.0), Cve: &vulnerabilities.Cve{CvssScore: new(10.0)}}, + Workload: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: "non-existing-workload"}, + VulnerabilitySummary: &vulnerabilities.Summary{ + Critical: 2, + RiskScore: 100, + ActNow: 2, + }, }, { - WorkloadRef: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: "ext-app"}, - Vulnerability: &vulnerabilities.Vulnerability{CvssScore: new(9.9), Cve: &vulnerabilities.Cve{CvssScore: new(9.9)}}, + Workload: &vulnerabilities.Workload{Cluster: "dev-gcp", Namespace: "devteam", Type: "app", Name: workloadName}, + VulnerabilitySummary: &vulnerabilities.Summary{ + ActNow: 0, + }, }, }}, log: logrus.New(), } issues := workload.vulnerabilities(ctx) + actNowIssues := make([]*Issue, 0) + for i := range issues { + if issues[i].IssueType == issue.IssueTypeExternalIngressActNowVulnerability { + actNowIssues = append(actNowIssues, issues[i]) + } + } + + if !wantIssue { + if len(actNowIssues) != 0 { + t.Fatalf("expected 0 external ingress act-now issues, got %d", len(actNowIssues)) + } + return + } - if len(issues) != 1 { - t.Fatalf("expected 1 issue, got %d", len(issues)) + if len(actNowIssues) != 1 { + t.Fatalf("expected 1 external ingress act-now issue, got %d", len(actNowIssues)) } - got := issues[0] - if got.IssueType != issue.IssueTypeExternalIngressCriticalVulnerability { - t.Fatalf("expected issue type %s, got %s", issue.IssueTypeExternalIngressCriticalVulnerability, got.IssueType) + got := actNowIssues[0] + if got.IssueType != issue.IssueTypeExternalIngressActNowVulnerability { + t.Fatalf("expected issue type %s, got %s", issue.IssueTypeExternalIngressActNowVulnerability, got.IssueType) } - if got.ResourceName != "ext-app" { - t.Fatalf("expected resource ext-app, got %s", got.ResourceName) + if got.ResourceName != workloadName { + t.Fatalf("expected resource %s, got %s", workloadName, got.ResourceName) } - details, ok := got.IssueDetails.(issue.ExternalIngressCriticalVulnerabilityIssueDetails) + details, ok := got.IssueDetails.(issue.ExternalIngressActNowVulnerabilityIssueDetails) if !ok { - t.Fatalf("expected external ingress critical details, got %T", got.IssueDetails) + t.Fatalf("expected external ingress act-now details, got %T", got.IssueDetails) } - if details.CvssScore != 10.0 { - t.Fatalf("expected CVSS 10.0, got %v", details.CvssScore) + if details.PriorityActNow != 2 { + t.Fatalf("expected priorityActNow 2, got %v", details.PriorityActNow) } - if len(details.Ingresses) != 1 || details.Ingresses[0] != "https://ext.example.com" { + if len(details.Ingresses) != 1 || details.Ingresses[0] != expectedIngress { t.Fatalf("expected only external ingress URL, got %+v", details.Ingresses) } } diff --git a/internal/issue/model.go b/internal/issue/model.go index e2a52fa24..cd6d236a8 100644 --- a/internal/issue/model.go +++ b/internal/issue/model.go @@ -184,11 +184,6 @@ type VulnerableImageIssueDetails struct { Critical int `json:"critical"` } -type ExternalIngressCriticalVulnerabilityIssueDetails struct { - CvssScore float64 `json:"cvssScore"` - Ingresses []string `json:"ingresses"` -} - type ExternalIngressActNowVulnerabilityIssueDetails struct { PriorityActNow int `json:"priorityActNow"` Ingresses []string `json:"ingresses"` @@ -197,6 +192,7 @@ type ExternalIngressActNowVulnerabilityIssueDetails struct { type IssueType string const ( +<<<<<<< HEAD IssueTypeOpenSearch IssueType = "OPENSEARCH" IssueTypeValkey IssueType = "VALKEY" IssueTypeSqlInstanceState IssueType = "SQLINSTANCE_STATE" @@ -216,6 +212,23 @@ const ( IssueTypeExternalIngressActNowVulnerability IssueType = "EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY" IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" +======= + IssueTypeOpenSearch IssueType = "OPENSEARCH" + IssueTypeValkey IssueType = "VALKEY" + IssueTypeSqlInstanceState IssueType = "SQLINSTANCE_STATE" + IssueTypeSqlInstanceVersion IssueType = "SQLINSTANCE_VERSION" + IssueTypeDeprecatedIngress IssueType = "DEPRECATED_INGRESS" + IssueTypeDeprecatedRegistry IssueType = "DEPRECATED_REGISTRY" + IssueTypeNoRunningInstances IssueType = "NO_RUNNING_INSTANCES" + IssueTypeLastRunFailed IssueType = "LAST_RUN_FAILED" + IssueTypeFailedSynchronization IssueType = "FAILED_SYNCHRONIZATION" + IssueTypeInvalidSpec IssueType = "INVALID_SPEC" + IssueTypeVulnerableImage IssueType = "VULNERABLE_IMAGE" + IssueTypeMissingSBOM IssueType = "MISSING_SBOM" + IssueTypeExternalIngressActNowVulnerability IssueType = "EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY" + IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" + IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" +>>>>>>> 48bfc261 (refactor(vulnerability): hard-remove priority summary extras) ) var AllIssueType = []IssueType{ @@ -232,7 +245,6 @@ var AllIssueType = []IssueType{ IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, - IssueTypeExternalIngressCriticalVulnerability, IssueTypeExternalIngressActNowVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop, @@ -503,15 +515,6 @@ func (VulnerableImageIssue) IsIssue() {} func (VulnerableImageIssue) IsNode() {} -type ExternalIngressCriticalVulnerabilityIssue struct { - Base - ExternalIngressCriticalVulnerabilityIssueDetails -} - -func (ExternalIngressCriticalVulnerabilityIssue) IsIssue() {} - -func (ExternalIngressCriticalVulnerabilityIssue) IsNode() {} - type ExternalIngressActNowVulnerabilityIssue struct { Base ExternalIngressActNowVulnerabilityIssueDetails diff --git a/internal/issue/queries.go b/internal/issue/queries.go index d816b3f61..c9ea89144 100644 --- a/internal/issue/queries.go +++ b/internal/issue/queries.go @@ -206,15 +206,6 @@ func convert(issue *issuesql.Issue) (Issue, error) { return &MissingSbomIssue{ Base: base, }, nil - case IssueTypeExternalIngressCriticalVulnerability: - d, err := unmarshal[ExternalIngressCriticalVulnerabilityIssueDetails](issue.IssueDetails) - if err != nil { - return nil, err - } - return &ExternalIngressCriticalVulnerabilityIssue{ - Base: base, - ExternalIngressCriticalVulnerabilityIssueDetails: *d, - }, nil case IssueTypeExternalIngressActNowVulnerability: d, err := unmarshal[ExternalIngressActNowVulnerabilityIssueDetails](issue.IssueDetails) if err != nil { diff --git a/internal/vulnerability/fake/fakedata.go b/internal/vulnerability/fake/fakedata.go index 9a219c019..310cbd8f8 100644 --- a/internal/vulnerability/fake/fakedata.go +++ b/internal/vulnerability/fake/fakedata.go @@ -77,22 +77,21 @@ func createFakeData(ctx context.Context) *fakeData { func createWorkloadSummary(env, team, workloadType, name, image string, vulnFactor int32) *vulnerabilities.WorkloadSummary { imageName, imageTag := workload.FormatImageReferenceForLookup(image) summary := &vulnerabilities.Summary{ - Critical: vulnFactor, - High: vulnFactor * 2, - Medium: vulnFactor + 2, - Low: vulnFactor + 1, - Unassigned: vulnFactor, - Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, - RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, - HasSbom: true, - LastUpdated: timestamppb.New(time.Now()), - ActNow: vulnFactor, - HighRisk: vulnFactor * 2, - ElevatedRisk: vulnFactor * 3, - Monitor: vulnFactor * 4, - RansomwareCount: vulnFactor, - HighEpssCount: vulnFactor * 2, - TopPriority: vulnerabilities.Priority_PRIORITY_ACT_NOW, + Critical: vulnFactor, + High: vulnFactor * 2, + Medium: vulnFactor + 2, + Low: vulnFactor + 1, + Unassigned: vulnFactor, + Total: vulnFactor + (vulnFactor * 2) + (vulnFactor + 2) + (vulnFactor + 1) + vulnFactor, + RiskScore: vulnFactor*10 + (vulnFactor*2)*5 + (vulnFactor+2)*3 + (vulnFactor + 1) + vulnFactor*5, + HasSbom: true, + LastUpdated: timestamppb.New(time.Now()), + ActNow: vulnFactor, + HighRisk: vulnFactor * 2, + ElevatedRisk: vulnFactor * 3, + Monitor: vulnFactor * 4, + HighEpssCount: vulnFactor * 2, + TopPriority: vulnerabilities.Priority_PRIORITY_ACT_NOW, } if name == "no-errors" { diff --git a/internal/vulnerability/fake/v13s.go b/internal/vulnerability/fake/v13s.go index eba200710..3e5480e44 100644 --- a/internal/vulnerability/fake/v13s.go +++ b/internal/vulnerability/fake/v13s.go @@ -189,21 +189,20 @@ func (f *fakeVulnerabilitiesClient) GetVulnerabilitySummaryTimeSeries(ctx contex resp := &vulnerabilities.GetVulnerabilitySummaryTimeSeriesResponse{ Points: []*vulnerabilities.VulnerabilitySummaryPoint{ { - Total: 1, - Critical: 1, - High: 1, - Medium: 1, - Low: 1, - Unassigned: 1, - RiskScore: 10, - ActNow: 1, - HighRisk: 2, - ElevatedRisk: 3, - Monitor: 4, - RansomwareCount: 1, - HighEpssCount: 2, - TopPriority: vulnerabilities.Priority_PRIORITY_ACT_NOW, - BucketTime: timestamppb.New(time.Now()), + Total: 1, + Critical: 1, + High: 1, + Medium: 1, + Low: 1, + Unassigned: 1, + RiskScore: 10, + ActNow: 1, + HighRisk: 2, + ElevatedRisk: 3, + Monitor: 4, + HighEpssCount: 2, + TopPriority: vulnerabilities.Priority_PRIORITY_ACT_NOW, + BucketTime: timestamppb.New(time.Now()), }, }, } diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index 960b677ea..76321ab07 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -76,22 +76,21 @@ type ImageVulnerabilitySuppression struct { } type ImageVulnerabilitySummary struct { - Total int `json:"total"` - RiskScore int `json:"riskScore"` - Low int `json:"low"` - Medium int `json:"medium"` - High int `json:"high"` - Critical int `json:"critical"` - Unassigned int `json:"unassigned"` - LastUpdated *time.Time `json:"lastUpdated"` - StaleImageTag *string `json:"staleImageTag"` - PriorityActNow int `json:"priorityActNow"` - PriorityHigh int `json:"priorityHigh"` - PriorityElevated int `json:"priorityElevated"` - PriorityMonitor int `json:"priorityMonitor"` - RansomwareCount int `json:"ransomwareCount"` - HighEpssCount int `json:"highEpssCount"` - TopRiskTier *CVEPriority `json:"topRiskTier"` + Total int `json:"total"` + RiskScore int `json:"riskScore"` + Low int `json:"low"` + Medium int `json:"medium"` + High int `json:"high"` + Critical int `json:"critical"` + Unassigned int `json:"unassigned"` + LastUpdated *time.Time `json:"lastUpdated"` + StaleImageTag *string `json:"staleImageTag"` + PriorityActNow int `json:"priorityActNow"` + PriorityHigh int `json:"priorityHigh"` + PriorityElevated int `json:"priorityElevated"` + PriorityMonitor int `json:"priorityMonitor"` + HighEpssCount int `json:"highEpssCount"` + TopVulnerabilityPriority *CVEPriority `json:"topVulnerabilityPriority"` } type ImageVulnerabilityOrderField string diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index c64c8b255..884282c02 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -369,22 +369,21 @@ func GetImageVulnerabilitySummary(ctx context.Context, ref string) (*ImageVulner } return &ImageVulnerabilitySummary{ - Critical: int(sum.GetCritical()), - High: int(sum.GetHigh()), - Medium: int(sum.GetMedium()), - Low: int(sum.GetLow()), - Unassigned: int(sum.GetUnassigned()), - Total: int(sum.GetTotal()), - RiskScore: int(sum.GetRiskScore()), - LastUpdated: lastUpdated, - StaleImageTag: sum.StaleImageTag, - PriorityActNow: int(sum.GetActNow()), - PriorityHigh: int(sum.GetHighRisk()), - PriorityElevated: int(sum.GetElevatedRisk()), - PriorityMonitor: int(sum.GetMonitor()), - RansomwareCount: int(sum.GetRansomwareCount()), - HighEpssCount: int(sum.GetHighEpssCount()), - TopRiskTier: mapPriority(sum.GetTopPriority()), + Critical: int(sum.GetCritical()), + High: int(sum.GetHigh()), + Medium: int(sum.GetMedium()), + Low: int(sum.GetLow()), + Unassigned: int(sum.GetUnassigned()), + Total: int(sum.GetTotal()), + RiskScore: int(sum.GetRiskScore()), + LastUpdated: lastUpdated, + StaleImageTag: sum.StaleImageTag, + PriorityActNow: int(sum.GetActNow()), + PriorityHigh: int(sum.GetHighRisk()), + PriorityElevated: int(sum.GetElevatedRisk()), + PriorityMonitor: int(sum.GetMonitor()), + HighEpssCount: int(sum.GetHighEpssCount()), + TopVulnerabilityPriority: mapPriority(sum.GetTopPriority()), }, nil } @@ -497,20 +496,19 @@ func getVulnerabilityHistory(ctx context.Context, opts []vulnerabilities.Option) for _, point := range resp.GetPoints() { samples = append(samples, &ImageVulnerabilitySample{ Summary: &ImageVulnerabilitySummary{ - Critical: int(point.GetCritical()), - High: int(point.GetHigh()), - Medium: int(point.GetMedium()), - Low: int(point.GetLow()), - Unassigned: int(point.GetUnassigned()), - Total: int(point.GetTotal()), - RiskScore: int(point.GetRiskScore()), - PriorityActNow: int(point.GetActNow()), - PriorityHigh: int(point.GetHighRisk()), - PriorityElevated: int(point.GetElevatedRisk()), - PriorityMonitor: int(point.GetMonitor()), - RansomwareCount: int(point.GetRansomwareCount()), - HighEpssCount: int(point.GetHighEpssCount()), - TopRiskTier: mapPriority(point.GetTopPriority()), + Critical: int(point.GetCritical()), + High: int(point.GetHigh()), + Medium: int(point.GetMedium()), + Low: int(point.GetLow()), + Unassigned: int(point.GetUnassigned()), + Total: int(point.GetTotal()), + RiskScore: int(point.GetRiskScore()), + PriorityActNow: int(point.GetActNow()), + PriorityHigh: int(point.GetHighRisk()), + PriorityElevated: int(point.GetElevatedRisk()), + PriorityMonitor: int(point.GetMonitor()), + HighEpssCount: int(point.GetHighEpssCount()), + TopVulnerabilityPriority: mapPriority(point.GetTopPriority()), }, Date: point.GetBucketTime().AsTime(), }) diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 970c8d644..fb103a453 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -78,21 +78,20 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa } summary := &ImageVulnerabilitySummary{ - Critical: int(v13sSummary.Critical), - High: int(v13sSummary.High), - Medium: int(v13sSummary.Medium), - Low: int(v13sSummary.Low), - Unassigned: int(v13sSummary.Unassigned), - Total: int(v13sSummary.Total), - RiskScore: int(v13sSummary.RiskScore), - LastUpdated: lastUpdated, - PriorityActNow: int(v13sSummary.ActNow), - PriorityHigh: int(v13sSummary.HighRisk), - PriorityElevated: int(v13sSummary.ElevatedRisk), - PriorityMonitor: int(v13sSummary.Monitor), - RansomwareCount: int(v13sSummary.GetRansomwareCount()), - HighEpssCount: int(v13sSummary.GetHighEpssCount()), - TopRiskTier: mapPriority(v13sSummary.GetTopPriority()), + Critical: int(v13sSummary.Critical), + High: int(v13sSummary.High), + Medium: int(v13sSummary.Medium), + Low: int(v13sSummary.Low), + Unassigned: int(v13sSummary.Unassigned), + Total: int(v13sSummary.Total), + RiskScore: int(v13sSummary.RiskScore), + LastUpdated: lastUpdated, + PriorityActNow: int(v13sSummary.ActNow), + PriorityHigh: int(v13sSummary.HighRisk), + PriorityElevated: int(v13sSummary.ElevatedRisk), + PriorityMonitor: int(v13sSummary.Monitor), + HighEpssCount: int(v13sSummary.GetHighEpssCount()), + TopVulnerabilityPriority: mapPriority(v13sSummary.GetTopPriority()), } return &WorkloadVulnerabilitySummary{ From dbed33163c52e08556bcf38acbac37bbe5c50a26 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:39:41 +0200 Subject: [PATCH 25/36] chore(deps): bump v13s api to latest main --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a87629d87..e633c1c6a 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe - github.com/nais/v13s/pkg/api v0.0.0-20260616121454-101cee8c4b04 + github.com/nais/v13s/pkg/api v0.0.0-20260617075806-adadfda4fd8d github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index f7d272a58..15ba152ee 100644 --- a/go.sum +++ b/go.sum @@ -813,8 +813,8 @@ github.com/nais/tester v0.1.1 h1:tpJ5HKpu3mEIWX/mec0Yj0xLHEpt+MwTAsj282n0Py0= github.com/nais/tester v0.1.1/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= -github.com/nais/v13s/pkg/api v0.0.0-20260616121454-101cee8c4b04 h1:NSwxZsfsm8kYlaP0iqEwyUXXS5HR692oEYti3QYXQ3U= -github.com/nais/v13s/pkg/api v0.0.0-20260616121454-101cee8c4b04/go.mod h1:Ct3ihc4Qjjxt2h92Z+qttn0kkgtR8JQ7pmTF7PExH3s= +github.com/nais/v13s/pkg/api v0.0.0-20260617075806-adadfda4fd8d h1:jEokr0rmq9Y4jk96QXb7lxq5qL0UdU6ZYauiyzxpVMM= +github.com/nais/v13s/pkg/api v0.0.0-20260617075806-adadfda4fd8d/go.mod h1:Ct3ihc4Qjjxt2h92Z+qttn0kkgtR8JQ7pmTF7PExH3s= github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= From c66f0f3fa30e477397c448a2cb263ceb09167457 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:52:45 +0200 Subject: [PATCH 26/36] fix(vulnerability): revert ingress class tests --- internal/issue/checker/workload_v13s_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/issue/checker/workload_v13s_test.go b/internal/issue/checker/workload_v13s_test.go index 23055a450..898e467bd 100644 --- a/internal/issue/checker/workload_v13s_test.go +++ b/internal/issue/checker/workload_v13s_test.go @@ -30,12 +30,8 @@ func TestVulnerabilities_ExternalIngressActNowIssue(t *testing.T) { expectedIngress string wantIssue bool }{ - {name: "legacy external ingress class", workloadName: "ext-app-legacy", expectedIngress: "https://legacy.external.example.com", wantIssue: true}, - {name: "external haproxy ingress class", workloadName: "ext-app-haproxy", expectedIngress: "https://haproxy.external.example.com", wantIssue: true}, - {name: "external authenticated haproxy ingress class", workloadName: "ext-app-fa-haproxy", expectedIngress: "https://haproxy.fa.external.example.com", wantIssue: true}, - {name: "internal haproxy ingress class", workloadName: "internal-only-haproxy", wantIssue: false}, - {name: "unknown ingress class", workloadName: "unknown-class-ingress", wantIssue: false}, - {name: "missing ingress class", workloadName: "no-class-ingress", wantIssue: false}, + {name: "external ingress class", workloadName: "ext-app", expectedIngress: "https://ext.example.com", wantIssue: true}, + {name: "internal ingress class", workloadName: "internal-only", wantIssue: false}, } for _, tt := range tests { From 2f2c37f18fe624ede9e2e9195e3542eb2d0c6597 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Thu, 18 Jun 2026 14:12:07 +0200 Subject: [PATCH 27/36] refactor(vulnerability): rename CVE priority IMMEDIATE to ACT_NOW --- internal/graph/gengql/root_.generated.go | 8 ++++---- internal/graph/schema/issues.graphqls | 2 +- internal/graph/schema/vulnerability.graphqls | 6 +++--- internal/vulnerability/models.go | 10 +++++----- internal/vulnerability/transform.go | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index da372449f..05280b25a 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -23652,7 +23652,7 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -"Raised when a workload with external ingresses has one or more IMMEDIATE vulnerability-priority findings." +"Raised when a workload with external ingresses has one or more ACT_NOW vulnerability-priority findings." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! @@ -31904,7 +31904,7 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - "Number of vulnerabilities with vulnerability priority IMMEDIATE." + "Number of vulnerabilities with vulnerability priority ACT_NOW." priorityActNow: Int! "Number of vulnerabilities with vulnerability priority HIGH." @@ -32029,7 +32029,7 @@ type ImageVulnerability implements Node { enum CVEPriority { "Vulnerability is known to be actively exploited and requires immediate action." - IMMEDIATE + ACT_NOW "Vulnerability is associated with ransomware or has a high EPSS percentile." HIGH "Vulnerability has a critical or high severity and elevated EPSS percentile." @@ -32252,7 +32252,7 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by IMMEDIATE vulnerability-priority count. + Order by ACT_NOW vulnerability-priority count. """ VULNERABILITY_PRIORITY_ACT_NOW """ diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index 8e5ec9bcc..e86493fb9 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -246,7 +246,7 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -"Raised when a workload with external ingresses has one or more IMMEDIATE vulnerability-priority findings." +"Raised when a workload with external ingresses has one or more ACT_NOW vulnerability-priority findings." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index f4735e633..bed93a2f6 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -296,7 +296,7 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - "Number of vulnerabilities with vulnerability priority IMMEDIATE." + "Number of vulnerabilities with vulnerability priority ACT_NOW." priorityActNow: Int! "Number of vulnerabilities with vulnerability priority HIGH." @@ -421,7 +421,7 @@ type ImageVulnerability implements Node { enum CVEPriority { "Vulnerability is known to be actively exploited and requires immediate action." - IMMEDIATE + ACT_NOW "Vulnerability is associated with ransomware or has a high EPSS percentile." HIGH "Vulnerability has a critical or high severity and elevated EPSS percentile." @@ -644,7 +644,7 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by IMMEDIATE vulnerability-priority count. + Order by ACT_NOW vulnerability-priority count. """ VULNERABILITY_PRIORITY_ACT_NOW """ diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index 76321ab07..73698fbcc 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -432,15 +432,15 @@ type VulnerabilityFixSample struct { type CVEPriority string const ( - CVEPriorityImmediate CVEPriority = "IMMEDIATE" - CVEPriorityHigh CVEPriority = "HIGH" - CVEPriorityElevated CVEPriority = "ELEVATED" - CVEPriorityMonitor CVEPriority = "MONITOR" + CVEPriorityActNow CVEPriority = "ACT_NOW" + CVEPriorityHigh CVEPriority = "HIGH" + CVEPriorityElevated CVEPriority = "ELEVATED" + CVEPriorityMonitor CVEPriority = "MONITOR" ) func (e CVEPriority) IsValid() bool { switch e { - case CVEPriorityImmediate, CVEPriorityHigh, CVEPriorityElevated, CVEPriorityMonitor: + case CVEPriorityActNow, CVEPriorityHigh, CVEPriorityElevated, CVEPriorityMonitor: return true } return false diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index fb103a453..e12e1b92b 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -129,7 +129,7 @@ func cvePriorityFromV13s(cve *vulnerabilities.Cve) CVEPriority { switch cve.GetPriority() { case vulnerabilities.Priority_PRIORITY_ACT_NOW: - return CVEPriorityImmediate + return CVEPriorityActNow case vulnerabilities.Priority_PRIORITY_HIGH: return CVEPriorityHigh case vulnerabilities.Priority_PRIORITY_ELEVATED: @@ -161,7 +161,7 @@ func mapVulnerabilitySeverity(severity ImageVulnerabilitySeverity) vulnerabiliti func mapPriority(priority vulnerabilities.Priority) *CVEPriority { switch priority { case vulnerabilities.Priority_PRIORITY_ACT_NOW: - return new(CVEPriorityImmediate) + return new(CVEPriorityActNow) case vulnerabilities.Priority_PRIORITY_HIGH: return new(CVEPriorityHigh) case vulnerabilities.Priority_PRIORITY_ELEVATED: @@ -175,7 +175,7 @@ func mapPriority(priority vulnerabilities.Priority) *CVEPriority { func mapCVEPriorityToPriority(p CVEPriority) vulnerabilities.Priority { switch p { - case CVEPriorityImmediate: + case CVEPriorityActNow: return vulnerabilities.Priority_PRIORITY_ACT_NOW case CVEPriorityHigh: return vulnerabilities.Priority_PRIORITY_HIGH From cd253be201c17b5144fbc0bbf9888b57d7389791 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Fri, 19 Jun 2026 09:20:33 +0200 Subject: [PATCH 28/36] fix(issue): replace immediate wording with ACT_NOW --- integration_tests/issues_for_team.lua | 2 +- internal/issue/checker/workload_v13s.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index b35f9edce..592afb114 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -555,7 +555,7 @@ Test.gql("VulnerableImageIssue", function(t) nodes = { { __typename = "VulnerableImageIssue", - message = "Image 'vulnerable-image' has 2 immediate vulnerabilities", + message = "Image 'vulnerable-image' has 2 ACT_NOW vulnerabilities", severity = "CRITICAL", critical = 5, riskScore = 250, diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 0d13035de..f641cb793 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -153,7 +153,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: environmentmapper.EnvironmentName(node.Workload.GetCluster()), Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Image '%s' has %d immediate vulnerabilities", + "Image '%s' has %d ACT_NOW vulnerabilities", node.Workload.ImageName, summary.ActNow, ), @@ -217,7 +217,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload with external ingresses %s has %d immediate vulnerabilities", + "Workload with external ingresses %s has %d ACT_NOW vulnerabilities", strings.Join(externalIngresses, ", "), node.VulnerabilitySummary.ActNow, ), From ecae3a7a5c5ea31388663f683b3ced76cd449c04 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Fri, 19 Jun 2026 09:29:50 +0200 Subject: [PATCH 29/36] refactor(vulnerability): rename riskTier to priority and remove highEpssCount --- internal/graph/gengql/root_.generated.go | 15 +------- .../graph/gengql/vulnerability.generated.go | 36 +++---------------- internal/graph/schema/vulnerability.graphqls | 5 +-- internal/vulnerability/models.go | 3 +- internal/vulnerability/queries.go | 6 ++-- internal/vulnerability/transform.go | 1 - 6 files changed, 9 insertions(+), 57 deletions(-) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 05280b25a..98521e1aa 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -1088,7 +1088,6 @@ type ComplexityRoot struct { ImageVulnerabilitySummary struct { Critical func(childComplexity int) int High func(childComplexity int) int - HighEpssCount func(childComplexity int) int LastUpdated func(childComplexity int) int Low func(childComplexity int) int Medium func(childComplexity int) int @@ -7397,13 +7396,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.High(childComplexity), true - case "ImageVulnerabilitySummary.highEpssCount": - if e.ComplexityRoot.ImageVulnerabilitySummary.HighEpssCount == nil { - break - } - - return e.ComplexityRoot.ImageVulnerabilitySummary.HighEpssCount(childComplexity), true - case "ImageVulnerabilitySummary.lastUpdated": if e.ComplexityRoot.ImageVulnerabilitySummary.LastUpdated == nil { break @@ -31868,7 +31860,7 @@ input TeamVulnerabilitySummaryFilter { """ Only return vulnerability summaries at or above the given vulnerability priority. """ - riskTier: CVEPriority + priority: CVEPriority } """ @@ -31916,9 +31908,6 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with vulnerability priority MONITOR." priorityMonitor: Int! - "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." - highEpssCount: Int! - "The highest priority among vulnerabilities in this summary." topVulnerabilityPriority: CVEPriority @@ -34409,8 +34398,6 @@ func (ec *executionContext) childFields_ImageVulnerabilitySummary(ctx context.Co return ec.fieldContext_ImageVulnerabilitySummary_priorityElevated(ctx, field) case "priorityMonitor": return ec.fieldContext_ImageVulnerabilitySummary_priorityMonitor(ctx, field) - case "highEpssCount": - return ec.fieldContext_ImageVulnerabilitySummary_highEpssCount(ctx, field) case "topVulnerabilityPriority": return ec.fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field) case "lastUpdated": diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index b8dc0a17f..08ce0c354 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -1642,29 +1642,6 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityMonit return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_highEpssCount(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_highEpssCount(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.HighEpssCount, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { - return ec.marshalNInt2int(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_highEpssCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) -} - func (ec *executionContext) _ImageVulnerabilitySummary_topVulnerabilityPriority(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3446,7 +3423,7 @@ func (ec *executionContext) unmarshalInputTeamVulnerabilitySummaryFilter(ctx con asMap[k] = v } - fieldsInOrder := [...]string{"environmentName", "environments", "riskTier"} + fieldsInOrder := [...]string{"environmentName", "environments", "priority"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -3467,13 +3444,13 @@ func (ec *executionContext) unmarshalInputTeamVulnerabilitySummaryFilter(ctx con return it, err } it.Environments = data - case "riskTier": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("riskTier")) + case "priority": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("priority")) data, err := ec.unmarshalOCVEPriority2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx, v) if err != nil { return it, err } - it.RiskTier = data + it.Priority = data } } return it, nil @@ -4388,11 +4365,6 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel if out.Values[i] == graphql.Null { out.Invalids++ } - case "highEpssCount": - out.Values[i] = ec._ImageVulnerabilitySummary_highEpssCount(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } case "topVulnerabilityPriority": out.Values[i] = ec._ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field, obj) case "lastUpdated": diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index bed93a2f6..f75521f57 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -260,7 +260,7 @@ input TeamVulnerabilitySummaryFilter { """ Only return vulnerability summaries at or above the given vulnerability priority. """ - riskTier: CVEPriority + priority: CVEPriority } """ @@ -308,9 +308,6 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with vulnerability priority MONITOR." priorityMonitor: Int! - "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." - highEpssCount: Int! - "The highest priority among vulnerabilities in this summary." topVulnerabilityPriority: CVEPriority diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index 73698fbcc..ac42b444b 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -89,7 +89,6 @@ type ImageVulnerabilitySummary struct { PriorityHigh int `json:"priorityHigh"` PriorityElevated int `json:"priorityElevated"` PriorityMonitor int `json:"priorityMonitor"` - HighEpssCount int `json:"highEpssCount"` TopVulnerabilityPriority *CVEPriority `json:"topVulnerabilityPriority"` } @@ -391,7 +390,7 @@ func (e ImageVulnerabilitySuppressionState) MarshalGQL(w io.Writer) { type TeamVulnerabilitySummaryFilter struct { EnvironmentName *string `json:"environmentName,omitempty"` Environments []string `json:"environments,omitempty"` - RiskTier *CVEPriority `json:"riskTier,omitempty"` + Priority *CVEPriority `json:"priority,omitempty"` } type ImageVulnerabilitySample struct { diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index 884282c02..49967ba9a 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -70,8 +70,8 @@ func ListVulnerabilitySummaries(ctx context.Context, s slug.Slug, filter *TeamVu opts = append(opts, vulnerabilities.Offset(page.Offset())) opts = append(opts, vulnerabilities.Limit(page.Limit())) - if filter != nil && filter.RiskTier != nil { - opts = append(opts, vulnerabilities.PriorityFilter(mapCVEPriorityToPriority(*filter.RiskTier))) + if filter != nil && filter.Priority != nil { + opts = append(opts, vulnerabilities.PriorityFilter(mapCVEPriorityToPriority(*filter.Priority))) } if orderBy != nil { @@ -382,7 +382,6 @@ func GetImageVulnerabilitySummary(ctx context.Context, ref string) (*ImageVulner PriorityHigh: int(sum.GetHighRisk()), PriorityElevated: int(sum.GetElevatedRisk()), PriorityMonitor: int(sum.GetMonitor()), - HighEpssCount: int(sum.GetHighEpssCount()), TopVulnerabilityPriority: mapPriority(sum.GetTopPriority()), }, nil } @@ -507,7 +506,6 @@ func getVulnerabilityHistory(ctx context.Context, opts []vulnerabilities.Option) PriorityHigh: int(point.GetHighRisk()), PriorityElevated: int(point.GetElevatedRisk()), PriorityMonitor: int(point.GetMonitor()), - HighEpssCount: int(point.GetHighEpssCount()), TopVulnerabilityPriority: mapPriority(point.GetTopPriority()), }, Date: point.GetBucketTime().AsTime(), diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index e12e1b92b..088ce2fb9 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -90,7 +90,6 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa PriorityHigh: int(v13sSummary.HighRisk), PriorityElevated: int(v13sSummary.ElevatedRisk), PriorityMonitor: int(v13sSummary.Monitor), - HighEpssCount: int(v13sSummary.GetHighEpssCount()), TopVulnerabilityPriority: mapPriority(v13sSummary.GetTopPriority()), } From abcef0a0bce05ff818c6faee3d304c4577a52e27 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Fri, 19 Jun 2026 09:35:28 +0200 Subject: [PATCH 30/36] fix(vulnerability): update ACT_NOW wording to lowercase 'act_now' in messages --- integration_tests/issues_for_team.lua | 2 +- internal/issue/checker/workload_v13s.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index 592afb114..804846b03 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -555,7 +555,7 @@ Test.gql("VulnerableImageIssue", function(t) nodes = { { __typename = "VulnerableImageIssue", - message = "Image 'vulnerable-image' has 2 ACT_NOW vulnerabilities", + message = "Image 'vulnerable-image' has 2 'act_now' vulnerabilities", severity = "CRITICAL", critical = 5, riskScore = 250, diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index f641cb793..cbe9cc83a 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -153,7 +153,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: environmentmapper.EnvironmentName(node.Workload.GetCluster()), Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Image '%s' has %d ACT_NOW vulnerabilities", + "Image '%s' has %d 'act_now' vulnerabilities", node.Workload.ImageName, summary.ActNow, ), @@ -217,7 +217,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload with external ingresses %s has %d ACT_NOW vulnerabilities", + "Workload with external ingresses %s has %d act_now vulnerabilities", strings.Join(externalIngresses, ", "), node.VulnerabilitySummary.ActNow, ), From bf020ad1c3699afecea89284669d0b1a5b690062 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Thu, 25 Jun 2026 10:17:10 +0200 Subject: [PATCH 31/36] fix(vulnerability): deprecate ExternalIngressCriticalVulnerabilityIssue type and resolvers --- integration_tests/issues_for_team.lua | 2 +- internal/graph/gengql/issues.generated.go | 322 ++++++++++++++++++++++ internal/graph/gengql/root_.generated.go | 73 +++++ internal/graph/gengql/schema.generated.go | 7 + internal/graph/issues.resolvers.go | 22 +- internal/graph/schema/issues.graphqls | 13 + internal/issue/checker/workload_v13s.go | 4 +- internal/issue/model.go | 33 +-- internal/issue/queries.go | 9 + 9 files changed, 446 insertions(+), 39 deletions(-) diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index 804846b03..258c39be2 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -555,7 +555,7 @@ Test.gql("VulnerableImageIssue", function(t) nodes = { { __typename = "VulnerableImageIssue", - message = "Image 'vulnerable-image' has 2 'act_now' vulnerabilities", + message = "Image 'vulnerable-image' has 2 urgent vulnerabilities", severity = "CRITICAL", critical = 5, riskScore = 250, diff --git a/internal/graph/gengql/issues.generated.go b/internal/graph/gengql/issues.generated.go index 44e5af831..ea237acc8 100644 --- a/internal/graph/gengql/issues.generated.go +++ b/internal/graph/gengql/issues.generated.go @@ -50,6 +50,11 @@ type ExternalIngressActNowVulnerabilityIssueResolver interface { Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) } +type ExternalIngressCriticalVulnerabilityIssueResolver interface { + TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) + + Workload(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (workload.Workload, error) +} type FailedSynchronizationIssueResolver interface { TeamEnvironment(ctx context.Context, obj *issue.FailedSynchronizationIssue) (*team.TeamEnvironment, error) @@ -796,6 +801,185 @@ func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) } +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ID, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { + return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type ID does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().TeamEnvironment(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { + return ec.marshalNTeamEnvironment2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeamEnvironment(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ExternalIngressCriticalVulnerabilityIssue", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_TeamEnvironment(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_severity(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Severity, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v issue.Severity) graphql.Marshaler { + return ec.marshalNSeverity2githubᚗcomᚋnaisᚋapiᚋinternalᚋissueᚐSeverity(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Severity does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().Workload(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v workload.Workload) graphql.Marshaler { + return ec.marshalNWorkload2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚐWorkload(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ExternalIngressCriticalVulnerabilityIssue", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + return fc, nil +} + +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.CvssScore, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { + return ec.marshalNFloat2float64(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Ingresses, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []string) graphql.Marshaler { + return ec.marshalNString2ᚕstringᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +} + func (ec *executionContext) _FailedSynchronizationIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.FailedSynchronizationIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3322,6 +3506,13 @@ func (ec *executionContext) _Issue(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } return ec._FailedSynchronizationIssue(ctx, sel, obj) + case issue.ExternalIngressCriticalVulnerabilityIssue: + return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressCriticalVulnerabilityIssue: + if obj == nil { + return graphql.Null + } + return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) case issue.ExternalIngressActNowVulnerabilityIssue: return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) case *issue.ExternalIngressActNowVulnerabilityIssue: @@ -3877,6 +4068,137 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context return out } +var externalIngressCriticalVulnerabilityIssueImplementors = []string{"ExternalIngressCriticalVulnerabilityIssue", "Issue", "Node"} + +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressCriticalVulnerabilityIssue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, externalIngressCriticalVulnerabilityIssueImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ExternalIngressCriticalVulnerabilityIssue") + case "id": + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "teamEnvironment": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "severity": + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "message": + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "workload": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "cvssScore": + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "ingresses": + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var failedSynchronizationIssueImplementors = []string{"FailedSynchronizationIssue", "Issue", "Node"} func (ec *executionContext) _FailedSynchronizationIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.FailedSynchronizationIssue) graphql.Marshaler { diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 98521e1aa..3323333f9 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -83,6 +83,7 @@ type ResolverRoot interface { DeprecatedRegistryIssue() DeprecatedRegistryIssueResolver Environment() EnvironmentResolver ExternalIngressActNowVulnerabilityIssue() ExternalIngressActNowVulnerabilityIssueResolver + ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver Ingress() IngressResolver IngressMetrics() IngressMetricsResolver @@ -962,6 +963,16 @@ type ComplexityRoot struct { Workload func(childComplexity int) int } + ExternalIngressCriticalVulnerabilityIssue struct { + CvssScore func(childComplexity int) int + ID func(childComplexity int) int + Ingresses func(childComplexity int) int + Message func(childComplexity int) int + Severity func(childComplexity int) int + TeamEnvironment func(childComplexity int) int + Workload func(childComplexity int) int + } + ExternalNetworkPolicyHost struct { Ports func(childComplexity int) int Target func(childComplexity int) int @@ -6899,6 +6910,55 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload(childComplexity), true + case "ExternalIngressCriticalVulnerabilityIssue.cvssScore": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore == nil { + break + } + + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore(childComplexity), true + + case "ExternalIngressCriticalVulnerabilityIssue.id": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID == nil { + break + } + + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID(childComplexity), true + + case "ExternalIngressCriticalVulnerabilityIssue.ingresses": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses == nil { + break + } + + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses(childComplexity), true + + case "ExternalIngressCriticalVulnerabilityIssue.message": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message == nil { + break + } + + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message(childComplexity), true + + case "ExternalIngressCriticalVulnerabilityIssue.severity": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity == nil { + break + } + + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity(childComplexity), true + + case "ExternalIngressCriticalVulnerabilityIssue.teamEnvironment": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment == nil { + break + } + + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment(childComplexity), true + + case "ExternalIngressCriticalVulnerabilityIssue.workload": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload == nil { + break + } + + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload(childComplexity), true + case "ExternalNetworkPolicyHost.ports": if e.ComplexityRoot.ExternalNetworkPolicyHost.Ports == nil { break @@ -23627,6 +23687,7 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE + EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY.") EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." @@ -23644,6 +23705,18 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } +"Deprecated: use ExternalIngressActNowVulnerabilityIssue." +type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { + id: ID! + teamEnvironment: TeamEnvironment! + severity: Severity! + message: String! + + workload: Workload! + cvssScore: Float! + ingresses: [String!]! +} + "Raised when a workload with external ingresses has one or more ACT_NOW vulnerability-priority findings." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! diff --git a/internal/graph/gengql/schema.generated.go b/internal/graph/gengql/schema.generated.go index 998814ab2..3876800f0 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -6607,6 +6607,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._FailedSynchronizationIssue(ctx, sel, obj) + case issue.ExternalIngressCriticalVulnerabilityIssue: + return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressCriticalVulnerabilityIssue: + if obj == nil { + return graphql.Null + } + return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) case issue.ExternalIngressActNowVulnerabilityIssue: return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) case *issue.ExternalIngressActNowVulnerabilityIssue: diff --git a/internal/graph/issues.resolvers.go b/internal/graph/issues.resolvers.go index a36c969eb..80c46b961 100644 --- a/internal/graph/issues.resolvers.go +++ b/internal/graph/issues.resolvers.go @@ -185,6 +185,10 @@ func (r *Resolver) ExternalIngressActNowVulnerabilityIssue() gengql.ExternalIngr return &externalIngressActNowVulnerabilityIssueResolver{r} } +func (r *Resolver) ExternalIngressCriticalVulnerabilityIssue() gengql.ExternalIngressCriticalVulnerabilityIssueResolver { + return &externalIngressCriticalVulnerabilityIssueResolver{r} +} + func (r *Resolver) FailedSynchronizationIssue() gengql.FailedSynchronizationIssueResolver { return &failedSynchronizationIssueResolver{r} } @@ -236,7 +240,6 @@ func (r *Resolver) WorkloadProblemIssue() gengql.WorkloadProblemIssueResolver { } type ( -<<<<<<< HEAD applicationRestartLoopIssueResolver struct{ *Resolver } deprecatedIngressIssueResolver struct{ *Resolver } deprecatedRegistryIssueResolver struct{ *Resolver } @@ -255,21 +258,4 @@ type ( valkeyIssueResolver struct{ *Resolver } vulnerableImageIssueResolver struct{ *Resolver } workloadProblemIssueResolver struct{ *Resolver } -======= - applicationRestartLoopIssueResolver struct{ *Resolver } - deprecatedIngressIssueResolver struct{ *Resolver } - deprecatedRegistryIssueResolver struct{ *Resolver } - externalIngressActNowVulnerabilityIssueResolver struct{ *Resolver } - failedSynchronizationIssueResolver struct{ *Resolver } - invalidSpecIssueResolver struct{ *Resolver } - lastRunFailedIssueResolver struct{ *Resolver } - missingSbomIssueResolver struct{ *Resolver } - noRunningInstancesIssueResolver struct{ *Resolver } - openSearchIssueResolver struct{ *Resolver } - sqlInstanceStateIssueResolver struct{ *Resolver } - sqlInstanceVersionIssueResolver struct{ *Resolver } - unleashReleaseChannelIssueResolver struct{ *Resolver } - valkeyIssueResolver struct{ *Resolver } - vulnerableImageIssueResolver struct{ *Resolver } ->>>>>>> 48bfc261 (refactor(vulnerability): hard-remove priority summary extras) ) diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index e86493fb9..6707666dc 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -229,6 +229,7 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE + EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY.") EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." @@ -246,6 +247,18 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } +"Deprecated: use ExternalIngressActNowVulnerabilityIssue." +type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { + id: ID! + teamEnvironment: TeamEnvironment! + severity: Severity! + message: String! + + workload: Workload! + cvssScore: Float! + ingresses: [String!]! +} + "Raised when a workload with external ingresses has one or more ACT_NOW vulnerability-priority findings." type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index cbe9cc83a..c3e1c0666 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -153,7 +153,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: environmentmapper.EnvironmentName(node.Workload.GetCluster()), Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Image '%s' has %d 'act_now' vulnerabilities", + "Image '%s' has %d urgent vulnerabilities", node.Workload.ImageName, summary.ActNow, ), @@ -217,7 +217,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload with external ingresses %s has %d act_now vulnerabilities", + "Workload with external ingresses %s has %d urgent vulnerabilities", strings.Join(externalIngresses, ", "), node.VulnerabilitySummary.ActNow, ), diff --git a/internal/issue/model.go b/internal/issue/model.go index cd6d236a8..c3852ef24 100644 --- a/internal/issue/model.go +++ b/internal/issue/model.go @@ -189,10 +189,14 @@ type ExternalIngressActNowVulnerabilityIssueDetails struct { Ingresses []string `json:"ingresses"` } +type ExternalIngressCriticalVulnerabilityIssueDetails struct { + CvssScore float64 `json:"cvssScore"` + Ingresses []string `json:"ingresses"` +} + type IssueType string const ( -<<<<<<< HEAD IssueTypeOpenSearch IssueType = "OPENSEARCH" IssueTypeValkey IssueType = "VALKEY" IssueTypeSqlInstanceState IssueType = "SQLINSTANCE_STATE" @@ -212,23 +216,6 @@ const ( IssueTypeExternalIngressActNowVulnerability IssueType = "EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY" IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" -======= - IssueTypeOpenSearch IssueType = "OPENSEARCH" - IssueTypeValkey IssueType = "VALKEY" - IssueTypeSqlInstanceState IssueType = "SQLINSTANCE_STATE" - IssueTypeSqlInstanceVersion IssueType = "SQLINSTANCE_VERSION" - IssueTypeDeprecatedIngress IssueType = "DEPRECATED_INGRESS" - IssueTypeDeprecatedRegistry IssueType = "DEPRECATED_REGISTRY" - IssueTypeNoRunningInstances IssueType = "NO_RUNNING_INSTANCES" - IssueTypeLastRunFailed IssueType = "LAST_RUN_FAILED" - IssueTypeFailedSynchronization IssueType = "FAILED_SYNCHRONIZATION" - IssueTypeInvalidSpec IssueType = "INVALID_SPEC" - IssueTypeVulnerableImage IssueType = "VULNERABLE_IMAGE" - IssueTypeMissingSBOM IssueType = "MISSING_SBOM" - IssueTypeExternalIngressActNowVulnerability IssueType = "EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY" - IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" - IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" ->>>>>>> 48bfc261 (refactor(vulnerability): hard-remove priority summary extras) ) var AllIssueType = []IssueType{ @@ -245,6 +232,7 @@ var AllIssueType = []IssueType{ IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, + IssueTypeExternalIngressCriticalVulnerability, IssueTypeExternalIngressActNowVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop, @@ -524,6 +512,15 @@ func (ExternalIngressActNowVulnerabilityIssue) IsIssue() {} func (ExternalIngressActNowVulnerabilityIssue) IsNode() {} +type ExternalIngressCriticalVulnerabilityIssue struct { + Base + ExternalIngressCriticalVulnerabilityIssueDetails +} + +func (ExternalIngressCriticalVulnerabilityIssue) IsIssue() {} + +func (ExternalIngressCriticalVulnerabilityIssue) IsNode() {} + type UnleashReleaseChannelIssueDetails struct { ChannelName string `json:"channelName"` MajorVersion int `json:"majorVersion"` diff --git a/internal/issue/queries.go b/internal/issue/queries.go index c9ea89144..9e2ce352c 100644 --- a/internal/issue/queries.go +++ b/internal/issue/queries.go @@ -215,6 +215,15 @@ func convert(issue *issuesql.Issue) (Issue, error) { Base: base, ExternalIngressActNowVulnerabilityIssueDetails: *d, }, nil + case IssueTypeExternalIngressCriticalVulnerability: + d, err := unmarshal[ExternalIngressCriticalVulnerabilityIssueDetails](issue.IssueDetails) + if err != nil { + return nil, err + } + return &ExternalIngressCriticalVulnerabilityIssue{ + Base: base, + ExternalIngressCriticalVulnerabilityIssueDetails: *d, + }, nil case IssueTypeUnleashReleaseChannel: d, err := unmarshal[UnleashReleaseChannelIssueDetails](issue.IssueDetails) if err != nil { From fb12224f2775d49e6bdb76079647295d5ae3192f Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:12:35 +0200 Subject: [PATCH 32/36] refactor(vulnerability): clarify priority summary and urgent issue naming --- integration_tests/vulnerabilities.lua | 26 ++ internal/graph/gengql/issues.generated.go | 204 +++++----- internal/graph/gengql/root_.generated.go | 385 ++++++++++++------ internal/graph/gengql/schema.generated.go | 14 +- .../graph/gengql/vulnerability.generated.go | 381 ++++++++++++++--- internal/graph/issues.resolvers.go | 18 +- internal/graph/schema/issues.graphqls | 26 +- internal/graph/schema/vulnerability.graphqls | 85 +++- internal/issue/checker/workload_v13s.go | 10 +- internal/issue/checker/workload_v13s_test.go | 12 +- internal/issue/model.go | 18 +- internal/issue/queries.go | 8 +- internal/vulnerability/models.go | 72 ++-- internal/vulnerability/queries.go | 68 ++-- internal/vulnerability/sortfilter.go | 40 +- internal/vulnerability/transform.go | 50 +-- 16 files changed, 957 insertions(+), 460 deletions(-) diff --git a/integration_tests/vulnerabilities.lua b/integration_tests/vulnerabilities.lua index 2ede770b0..99c40851a 100644 --- a/integration_tests/vulnerabilities.lua +++ b/integration_tests/vulnerabilities.lua @@ -63,6 +63,19 @@ Test.gql("List vulnerability summaries for team", function(t) } vulnerabilitySummary{ total + countsBySeverity { + critical + high + medium + low + unassigned + } + countsByPriority { + urgent + highRisk + elevatedRisk + monitor + } critical high medium @@ -94,6 +107,19 @@ Test.gql("List vulnerability summaries for team", function(t) }, vulnerabilitySummary = { total = NotNull(), + countsBySeverity = { + critical = NotNull(), + high = NotNull(), + medium = NotNull(), + low = NotNull(), + unassigned = NotNull(), + }, + countsByPriority = { + urgent = NotNull(), + highRisk = NotNull(), + elevatedRisk = NotNull(), + monitor = NotNull(), + }, critical = NotNull(), high = NotNull(), medium = NotNull(), diff --git a/internal/graph/gengql/issues.generated.go b/internal/graph/gengql/issues.generated.go index ea237acc8..505bc749b 100644 --- a/internal/graph/gengql/issues.generated.go +++ b/internal/graph/gengql/issues.generated.go @@ -45,16 +45,16 @@ type DeprecatedRegistryIssueResolver interface { Workload(ctx context.Context, obj *issue.DeprecatedRegistryIssue) (workload.Workload, error) } -type ExternalIngressActNowVulnerabilityIssueResolver interface { - TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (*team.TeamEnvironment, error) - - Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) -} type ExternalIngressCriticalVulnerabilityIssueResolver interface { TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) Workload(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (workload.Workload, error) } +type ExternalIngressUrgentVulnerabilityIssueResolver interface { + TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (*team.TeamEnvironment, error) + + Workload(ctx context.Context, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (workload.Workload, error) +} type FailedSynchronizationIssueResolver interface { TeamEnvironment(ctx context.Context, obj *issue.FailedSynchronizationIssue) (*team.TeamEnvironment, error) @@ -622,13 +622,13 @@ func (ec *executionContext) fieldContext_DeprecatedRegistryIssue_workload(_ cont return fc, nil } -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_id(ctx, field) + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(ctx, field) }, func(ctx context.Context) (any, error) { return obj.ID, nil @@ -641,20 +641,20 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_id(ctx cont true, ) } -func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type ID does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type ID does not have child fields")) } -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx, field) + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressActNowVulnerabilityIssue().TeamEnvironment(ctx, obj) + return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().TeamEnvironment(ctx, obj) }, nil, func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { @@ -664,9 +664,9 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_teamEnviron true, ) } -func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ExternalIngressActNowVulnerabilityIssue", + Object: "ExternalIngressCriticalVulnerabilityIssue", Field: field, IsMethod: true, IsResolver: true, @@ -677,13 +677,13 @@ func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue return fc, nil } -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_severity(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_severity(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_severity(ctx, field) + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Severity, nil @@ -696,17 +696,17 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_severity(ct true, ) } -func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_severity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type Severity does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Severity does not have child fields")) } -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_message(ctx, field) + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Message, nil @@ -719,20 +719,20 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_message(ctx true, ) } -func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_workload(ctx, field) + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressActNowVulnerabilityIssue().Workload(ctx, obj) + return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().Workload(ctx, obj) }, nil, func(ctx context.Context, selections ast.SelectionSet, v workload.Workload) graphql.Marshaler { @@ -742,9 +742,9 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_workload(ct true, ) } -func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ExternalIngressActNowVulnerabilityIssue", + Object: "ExternalIngressCriticalVulnerabilityIssue", Field: field, IsMethod: true, IsResolver: true, @@ -755,36 +755,36 @@ func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue return fc, nil } -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_priorityActNow(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_priorityActNow(ctx, field) + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.PriorityActNow, nil + return obj.CvssScore, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { - return ec.marshalNInt2int(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { + return ec.marshalNFloat2float64(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_priorityActNow(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Float does not have child fields")) } -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressActNowVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_ingresses(ctx, field) + return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Ingresses, nil @@ -797,17 +797,17 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue_ingresses(c true, ) } -func (ec *executionContext) fieldContext_ExternalIngressActNowVulnerabilityIssue_ingresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressActNowVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(ctx, field) + return ec.fieldContext_ExternalIngressUrgentVulnerabilityIssue_id(ctx, field) }, func(ctx context.Context) (any, error) { return obj.ID, nil @@ -820,20 +820,20 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_id(ctx co true, ) } -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type ID does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressUrgentVulnerabilityIssue_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressUrgentVulnerabilityIssue", field, false, false, errors.New("field of type ID does not have child fields")) } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field) + return ec.fieldContext_ExternalIngressUrgentVulnerabilityIssue_teamEnvironment(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().TeamEnvironment(ctx, obj) + return ec.Resolvers.ExternalIngressUrgentVulnerabilityIssue().TeamEnvironment(ctx, obj) }, nil, func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { @@ -843,9 +843,9 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_teamEnvir true, ) } -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ExternalIngressUrgentVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ExternalIngressCriticalVulnerabilityIssue", + Object: "ExternalIngressUrgentVulnerabilityIssue", Field: field, IsMethod: true, IsResolver: true, @@ -856,13 +856,13 @@ func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIss return fc, nil } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_severity(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue_severity(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field) + return ec.fieldContext_ExternalIngressUrgentVulnerabilityIssue_severity(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Severity, nil @@ -875,17 +875,17 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_severity( true, ) } -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_severity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Severity does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressUrgentVulnerabilityIssue_severity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressUrgentVulnerabilityIssue", field, false, false, errors.New("field of type Severity does not have child fields")) } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(ctx, field) + return ec.fieldContext_ExternalIngressUrgentVulnerabilityIssue_message(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Message, nil @@ -898,20 +898,20 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_message(c true, ) } -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressUrgentVulnerabilityIssue_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressUrgentVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field) + return ec.fieldContext_ExternalIngressUrgentVulnerabilityIssue_workload(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().Workload(ctx, obj) + return ec.Resolvers.ExternalIngressUrgentVulnerabilityIssue().Workload(ctx, obj) }, nil, func(ctx context.Context, selections ast.SelectionSet, v workload.Workload) graphql.Marshaler { @@ -921,9 +921,9 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_workload( true, ) } -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ExternalIngressUrgentVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ExternalIngressCriticalVulnerabilityIssue", + Object: "ExternalIngressUrgentVulnerabilityIssue", Field: field, IsMethod: true, IsResolver: true, @@ -934,36 +934,36 @@ func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIss return fc, nil } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue_priorityUrgent(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field) + return ec.fieldContext_ExternalIngressUrgentVulnerabilityIssue_priorityUrgent(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.CvssScore, nil + return obj.PriorityUrgent, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { - return ec.marshalNFloat2float64(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_cvssScore(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type Float does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressUrgentVulnerabilityIssue_priorityUrgent(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressUrgentVulnerabilityIssue", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field) + return ec.fieldContext_ExternalIngressUrgentVulnerabilityIssue_ingresses(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Ingresses, nil @@ -976,8 +976,8 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue_ingresses true, ) } -func (ec *executionContext) fieldContext_ExternalIngressCriticalVulnerabilityIssue_ingresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ExternalIngressCriticalVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_ExternalIngressUrgentVulnerabilityIssue_ingresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ExternalIngressUrgentVulnerabilityIssue", field, false, false, errors.New("field of type String does not have child fields")) } func (ec *executionContext) _FailedSynchronizationIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.FailedSynchronizationIssue) (ret graphql.Marshaler) { @@ -3506,6 +3506,13 @@ func (ec *executionContext) _Issue(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } return ec._FailedSynchronizationIssue(ctx, sel, obj) + case issue.ExternalIngressUrgentVulnerabilityIssue: + return ec._ExternalIngressUrgentVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressUrgentVulnerabilityIssue: + if obj == nil { + return graphql.Null + } + return ec._ExternalIngressUrgentVulnerabilityIssue(ctx, sel, obj) case issue.ExternalIngressCriticalVulnerabilityIssue: return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, &obj) case *issue.ExternalIngressCriticalVulnerabilityIssue: @@ -3513,13 +3520,6 @@ func (ec *executionContext) _Issue(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) - case issue.ExternalIngressActNowVulnerabilityIssue: - return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) - case *issue.ExternalIngressActNowVulnerabilityIssue: - if obj == nil { - return graphql.Null - } - return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, obj) case issue.DeprecatedRegistryIssue: return ec._DeprecatedRegistryIssue(ctx, sel, &obj) case *issue.DeprecatedRegistryIssue: @@ -3937,19 +3937,19 @@ func (ec *executionContext) _DeprecatedRegistryIssue(ctx context.Context, sel as return out } -var externalIngressActNowVulnerabilityIssueImplementors = []string{"ExternalIngressActNowVulnerabilityIssue", "Issue", "Node"} +var externalIngressCriticalVulnerabilityIssueImplementors = []string{"ExternalIngressCriticalVulnerabilityIssue", "Issue", "Node"} -func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressActNowVulnerabilityIssue) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, externalIngressActNowVulnerabilityIssueImplementors) +func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressCriticalVulnerabilityIssue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, externalIngressCriticalVulnerabilityIssueImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("ExternalIngressActNowVulnerabilityIssue") + out.Values[i] = graphql.MarshalString("ExternalIngressCriticalVulnerabilityIssue") case "id": - out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_id(ctx, field, obj) + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_id(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -3962,7 +3962,7 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx, field, obj) + res = ec._ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -3990,12 +3990,12 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "severity": - out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_severity(ctx, field, obj) + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } case "message": - out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_message(ctx, field, obj) + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_message(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -4008,7 +4008,7 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ExternalIngressActNowVulnerabilityIssue_workload(ctx, field, obj) + res = ec._ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -4035,13 +4035,13 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "priorityActNow": - out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_priorityActNow(ctx, field, obj) + case "cvssScore": + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } case "ingresses": - out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_ingresses(ctx, field, obj) + out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -4068,19 +4068,19 @@ func (ec *executionContext) _ExternalIngressActNowVulnerabilityIssue(ctx context return out } -var externalIngressCriticalVulnerabilityIssueImplementors = []string{"ExternalIngressCriticalVulnerabilityIssue", "Issue", "Node"} +var externalIngressUrgentVulnerabilityIssueImplementors = []string{"ExternalIngressUrgentVulnerabilityIssue", "Issue", "Node"} -func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressCriticalVulnerabilityIssue) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, externalIngressCriticalVulnerabilityIssueImplementors) +func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressUrgentVulnerabilityIssue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, externalIngressUrgentVulnerabilityIssueImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("ExternalIngressCriticalVulnerabilityIssue") + out.Values[i] = graphql.MarshalString("ExternalIngressUrgentVulnerabilityIssue") case "id": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_id(ctx, field, obj) + out.Values[i] = ec._ExternalIngressUrgentVulnerabilityIssue_id(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -4093,7 +4093,7 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx conte ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field, obj) + res = ec._ExternalIngressUrgentVulnerabilityIssue_teamEnvironment(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -4121,12 +4121,12 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx conte out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "severity": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field, obj) + out.Values[i] = ec._ExternalIngressUrgentVulnerabilityIssue_severity(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } case "message": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_message(ctx, field, obj) + out.Values[i] = ec._ExternalIngressUrgentVulnerabilityIssue_message(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -4139,7 +4139,7 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx conte ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field, obj) + res = ec._ExternalIngressUrgentVulnerabilityIssue_workload(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -4166,13 +4166,13 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx conte } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "cvssScore": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field, obj) + case "priorityUrgent": + out.Values[i] = ec._ExternalIngressUrgentVulnerabilityIssue_priorityUrgent(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } case "ingresses": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field, obj) + out.Values[i] = ec._ExternalIngressUrgentVulnerabilityIssue_ingresses(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 3323333f9..32ddff379 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -82,8 +82,8 @@ type ResolverRoot interface { DeprecatedIngressIssue() DeprecatedIngressIssueResolver DeprecatedRegistryIssue() DeprecatedRegistryIssueResolver Environment() EnvironmentResolver - ExternalIngressActNowVulnerabilityIssue() ExternalIngressActNowVulnerabilityIssueResolver ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver + ExternalIngressUrgentVulnerabilityIssue() ExternalIngressUrgentVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver Ingress() IngressResolver IngressMetrics() IngressMetricsResolver @@ -953,21 +953,21 @@ type ComplexityRoot struct { Node func(childComplexity int) int } - ExternalIngressActNowVulnerabilityIssue struct { + ExternalIngressCriticalVulnerabilityIssue struct { + CvssScore func(childComplexity int) int ID func(childComplexity int) int Ingresses func(childComplexity int) int Message func(childComplexity int) int - PriorityActNow func(childComplexity int) int Severity func(childComplexity int) int TeamEnvironment func(childComplexity int) int Workload func(childComplexity int) int } - ExternalIngressCriticalVulnerabilityIssue struct { - CvssScore func(childComplexity int) int + ExternalIngressUrgentVulnerabilityIssue struct { ID func(childComplexity int) int Ingresses func(childComplexity int) int Message func(childComplexity int) int + PriorityUrgent func(childComplexity int) int Severity func(childComplexity int) int TeamEnvironment func(childComplexity int) int Workload func(childComplexity int) int @@ -1097,20 +1097,32 @@ type ComplexityRoot struct { } ImageVulnerabilitySummary struct { - Critical func(childComplexity int) int - High func(childComplexity int) int - LastUpdated func(childComplexity int) int - Low func(childComplexity int) int - Medium func(childComplexity int) int - PriorityActNow func(childComplexity int) int - PriorityElevated func(childComplexity int) int - PriorityHigh func(childComplexity int) int - PriorityMonitor func(childComplexity int) int - RiskScore func(childComplexity int) int - StaleImageTag func(childComplexity int) int - TopVulnerabilityPriority func(childComplexity int) int - Total func(childComplexity int) int - Unassigned func(childComplexity int) int + CountsByPriority func(childComplexity int) int + CountsBySeverity func(childComplexity int) int + Critical func(childComplexity int) int + High func(childComplexity int) int + LastUpdated func(childComplexity int) int + Low func(childComplexity int) int + Medium func(childComplexity int) int + RiskScore func(childComplexity int) int + StaleImageTag func(childComplexity int) int + Total func(childComplexity int) int + Unassigned func(childComplexity int) int + } + + ImageVulnerabilitySummaryCountsByPriority struct { + ElevatedRisk func(childComplexity int) int + HighRisk func(childComplexity int) int + Monitor func(childComplexity int) int + Urgent func(childComplexity int) int + } + + ImageVulnerabilitySummaryCountsBySeverity struct { + Critical func(childComplexity int) int + High func(childComplexity int) int + Low func(childComplexity int) int + Medium func(childComplexity int) int + Unassigned func(childComplexity int) int } ImageVulnerabilitySuppression struct { @@ -6861,103 +6873,103 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.EnvironmentEdge.Node(childComplexity), true - case "ExternalIngressActNowVulnerabilityIssue.id": - if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.ID == nil { + case "ExternalIngressCriticalVulnerabilityIssue.cvssScore": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore == nil { break } - return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.ID(childComplexity), true + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore(childComplexity), true - case "ExternalIngressActNowVulnerabilityIssue.ingresses": - if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Ingresses == nil { + case "ExternalIngressCriticalVulnerabilityIssue.id": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID == nil { break } - return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Ingresses(childComplexity), true + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID(childComplexity), true - case "ExternalIngressActNowVulnerabilityIssue.message": - if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Message == nil { + case "ExternalIngressCriticalVulnerabilityIssue.ingresses": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses == nil { break } - return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Message(childComplexity), true + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses(childComplexity), true - case "ExternalIngressActNowVulnerabilityIssue.priorityActNow": - if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.PriorityActNow == nil { + case "ExternalIngressCriticalVulnerabilityIssue.message": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message == nil { break } - return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.PriorityActNow(childComplexity), true + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message(childComplexity), true - case "ExternalIngressActNowVulnerabilityIssue.severity": - if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Severity == nil { + case "ExternalIngressCriticalVulnerabilityIssue.severity": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity == nil { break } - return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Severity(childComplexity), true + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity(childComplexity), true - case "ExternalIngressActNowVulnerabilityIssue.teamEnvironment": - if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.TeamEnvironment == nil { + case "ExternalIngressCriticalVulnerabilityIssue.teamEnvironment": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment == nil { break } - return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.TeamEnvironment(childComplexity), true + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment(childComplexity), true - case "ExternalIngressActNowVulnerabilityIssue.workload": - if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload == nil { + case "ExternalIngressCriticalVulnerabilityIssue.workload": + if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload == nil { break } - return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload(childComplexity), true + return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.cvssScore": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore == nil { + case "ExternalIngressUrgentVulnerabilityIssue.id": + if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.ID == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore(childComplexity), true + return e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.ID(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.id": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID == nil { + case "ExternalIngressUrgentVulnerabilityIssue.ingresses": + if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Ingresses == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID(childComplexity), true + return e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Ingresses(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.ingresses": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses == nil { + case "ExternalIngressUrgentVulnerabilityIssue.message": + if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Message == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses(childComplexity), true + return e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Message(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.message": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message == nil { + case "ExternalIngressUrgentVulnerabilityIssue.priorityUrgent": + if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.PriorityUrgent == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message(childComplexity), true + return e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.PriorityUrgent(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.severity": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity == nil { + case "ExternalIngressUrgentVulnerabilityIssue.severity": + if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Severity == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity(childComplexity), true + return e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Severity(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.teamEnvironment": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment == nil { + case "ExternalIngressUrgentVulnerabilityIssue.teamEnvironment": + if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.TeamEnvironment == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment(childComplexity), true + return e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.TeamEnvironment(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.workload": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload == nil { + case "ExternalIngressUrgentVulnerabilityIssue.workload": + if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Workload == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload(childComplexity), true + return e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.Workload(childComplexity), true case "ExternalNetworkPolicyHost.ports": if e.ComplexityRoot.ExternalNetworkPolicyHost.Ports == nil { @@ -7442,6 +7454,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySample.Summary(childComplexity), true + case "ImageVulnerabilitySummary.countsByPriority": + if e.ComplexityRoot.ImageVulnerabilitySummary.CountsByPriority == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.CountsByPriority(childComplexity), true + + case "ImageVulnerabilitySummary.countsBySeverity": + if e.ComplexityRoot.ImageVulnerabilitySummary.CountsBySeverity == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.CountsBySeverity(childComplexity), true + case "ImageVulnerabilitySummary.critical": if e.ComplexityRoot.ImageVulnerabilitySummary.Critical == nil { break @@ -7477,68 +7503,96 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.Medium(childComplexity), true - case "ImageVulnerabilitySummary.priorityActNow": - if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityActNow == nil { + case "ImageVulnerabilitySummary.riskScore": + if e.ComplexityRoot.ImageVulnerabilitySummary.RiskScore == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityActNow(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummary.RiskScore(childComplexity), true - case "ImageVulnerabilitySummary.priorityElevated": - if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityElevated == nil { + case "ImageVulnerabilitySummary.staleImageTag": + if e.ComplexityRoot.ImageVulnerabilitySummary.StaleImageTag == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityElevated(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummary.StaleImageTag(childComplexity), true - case "ImageVulnerabilitySummary.priorityHigh": - if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityHigh == nil { + case "ImageVulnerabilitySummary.total": + if e.ComplexityRoot.ImageVulnerabilitySummary.Total == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityHigh(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummary.Total(childComplexity), true - case "ImageVulnerabilitySummary.priorityMonitor": - if e.ComplexityRoot.ImageVulnerabilitySummary.PriorityMonitor == nil { + case "ImageVulnerabilitySummary.unassigned": + if e.ComplexityRoot.ImageVulnerabilitySummary.Unassigned == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.PriorityMonitor(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummary.Unassigned(childComplexity), true - case "ImageVulnerabilitySummary.riskScore": - if e.ComplexityRoot.ImageVulnerabilitySummary.RiskScore == nil { + case "ImageVulnerabilitySummaryCountsByPriority.elevatedRisk": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.ElevatedRisk == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.RiskScore(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.ElevatedRisk(childComplexity), true - case "ImageVulnerabilitySummary.staleImageTag": - if e.ComplexityRoot.ImageVulnerabilitySummary.StaleImageTag == nil { + case "ImageVulnerabilitySummaryCountsByPriority.highRisk": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.HighRisk == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.StaleImageTag(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.HighRisk(childComplexity), true - case "ImageVulnerabilitySummary.topVulnerabilityPriority": - if e.ComplexityRoot.ImageVulnerabilitySummary.TopVulnerabilityPriority == nil { + case "ImageVulnerabilitySummaryCountsByPriority.monitor": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.Monitor == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.TopVulnerabilityPriority(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.Monitor(childComplexity), true - case "ImageVulnerabilitySummary.total": - if e.ComplexityRoot.ImageVulnerabilitySummary.Total == nil { + case "ImageVulnerabilitySummaryCountsByPriority.urgent": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.Urgent == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.Total(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsByPriority.Urgent(childComplexity), true - case "ImageVulnerabilitySummary.unassigned": - if e.ComplexityRoot.ImageVulnerabilitySummary.Unassigned == nil { + case "ImageVulnerabilitySummaryCountsBySeverity.critical": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Critical == nil { break } - return e.ComplexityRoot.ImageVulnerabilitySummary.Unassigned(childComplexity), true + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Critical(childComplexity), true + + case "ImageVulnerabilitySummaryCountsBySeverity.high": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.High == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.High(childComplexity), true + + case "ImageVulnerabilitySummaryCountsBySeverity.low": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Low == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Low(childComplexity), true + + case "ImageVulnerabilitySummaryCountsBySeverity.medium": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Medium == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Medium(childComplexity), true + + case "ImageVulnerabilitySummaryCountsBySeverity.unassigned": + if e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Unassigned == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummaryCountsBySeverity.Unassigned(childComplexity), true case "ImageVulnerabilitySuppression.reason": if e.ComplexityRoot.ImageVulnerabilitySuppression.Reason == nil { @@ -23687,8 +23741,8 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE - EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY.") - EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY + EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_URGENT_VULNERABILITY.") + EXTERNAL_INGRESS_URGENT_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." APPLICATION_RESTART_LOOP @@ -23705,27 +23759,27 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -"Deprecated: use ExternalIngressActNowVulnerabilityIssue." +"Deprecated: use ExternalIngressUrgentVulnerabilityIssue." type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { - id: ID! - teamEnvironment: TeamEnvironment! - severity: Severity! - message: String! + id: ID! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + teamEnvironment: TeamEnvironment! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + severity: Severity! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + message: String! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - workload: Workload! - cvssScore: Float! - ingresses: [String!]! + workload: Workload! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + cvssScore: Float! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + ingresses: [String!]! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") } -"Raised when a workload with external ingresses has one or more ACT_NOW vulnerability-priority findings." -type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { +"Raised when a workload with external ingresses has one or more urgent vulnerability-priority findings." +type ExternalIngressUrgentVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! severity: Severity! message: String! workload: Workload! - priorityActNow: Int! + priorityUrgent: Int! ingresses: [String!]! } @@ -31954,35 +32008,31 @@ type ImageVulnerabilitySummary { "Risk score of the image." riskScore: Int! + "Vulnerability counts grouped by severity." + countsBySeverity: ImageVulnerabilitySummaryCountsBySeverity! + + "Vulnerability counts grouped by operational priority." + countsByPriority: ImageVulnerabilitySummaryCountsByPriority! + "Number of vulnerabilities with severity LOW." low: Int! + @deprecated(reason: "Use countsBySeverity.low instead.") "Number of vulnerabilities with severity MEDIUM." medium: Int! + @deprecated(reason: "Use countsBySeverity.medium instead.") "Number of vulnerabilities with severity HIGH." high: Int! + @deprecated(reason: "Use countsBySeverity.high instead.") "Number of vulnerabilities with severity CRITICAL." critical: Int! + @deprecated(reason: "Use countsBySeverity.critical instead.") "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - - "Number of vulnerabilities with vulnerability priority ACT_NOW." - priorityActNow: Int! - - "Number of vulnerabilities with vulnerability priority HIGH." - priorityHigh: Int! - - "Number of vulnerabilities with vulnerability priority ELEVATED." - priorityElevated: Int! - - "Number of vulnerabilities with vulnerability priority MONITOR." - priorityMonitor: Int! - - "The highest priority among vulnerabilities in this summary." - topVulnerabilityPriority: CVEPriority + @deprecated(reason: "Use countsBySeverity.unassigned instead.") "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -31995,6 +32045,37 @@ type ImageVulnerabilitySummary { staleImageTag: String } +type ImageVulnerabilitySummaryCountsBySeverity { + "Number of vulnerabilities with severity CRITICAL." + critical: Int! + + "Number of vulnerabilities with severity HIGH." + high: Int! + + "Number of vulnerabilities with severity MEDIUM." + medium: Int! + + "Number of vulnerabilities with severity LOW." + low: Int! + + "Number of vulnerabilities with severity UNASSIGNED." + unassigned: Int! +} + +type ImageVulnerabilitySummaryCountsByPriority { + "Known-exploited vulnerabilities that require immediate action." + urgent: Int! + + "Vulnerabilities with strong exploitation indicators." + highRisk: Int! + + "Vulnerabilities with elevated exploitation risk." + elevatedRisk: Int! + + "Vulnerabilities that should be monitored." + monitor: Int! +} + type ImageVulnerabilityConnection { "Information to aid in pagination." pageInfo: PageInfo! @@ -32314,13 +32395,33 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by ACT_NOW vulnerability-priority count. + Order by the number of urgent vulnerabilities. + + Urgent vulnerabilities are known to be actively exploited and should be + prioritized for immediate action. + """ + VULNERABILITY_PRIORITY_URGENT """ - VULNERABILITY_PRIORITY_ACT_NOW + Order by the number of high-risk vulnerabilities. + + High-risk vulnerabilities are not known exploited, but have strong + exploitation indicators such as known ransomware use or very high EPSS. """ - Order by HIGH vulnerability-priority count. + VULNERABILITY_PRIORITY_HIGH_RISK """ - VULNERABILITY_PRIORITY_HIGH + Order by the number of elevated-risk vulnerabilities. + + Elevated-risk vulnerabilities are lower priority than high-risk, but still + have meaningful exploitation risk signals. + """ + VULNERABILITY_PRIORITY_ELEVATED_RISK + """ + Order by the number of monitor vulnerabilities. + + Monitor vulnerabilities should be tracked, but do not currently indicate the + same operational urgency as the higher priority buckets. + """ + VULNERABILITY_PRIORITY_MONITOR } type TenantVulnerabilitySummary { @@ -34453,6 +34554,10 @@ func (ec *executionContext) childFields_ImageVulnerabilitySummary(ctx context.Co return ec.fieldContext_ImageVulnerabilitySummary_total(ctx, field) case "riskScore": return ec.fieldContext_ImageVulnerabilitySummary_riskScore(ctx, field) + case "countsBySeverity": + return ec.fieldContext_ImageVulnerabilitySummary_countsBySeverity(ctx, field) + case "countsByPriority": + return ec.fieldContext_ImageVulnerabilitySummary_countsByPriority(ctx, field) case "low": return ec.fieldContext_ImageVulnerabilitySummary_low(ctx, field) case "medium": @@ -34463,16 +34568,6 @@ func (ec *executionContext) childFields_ImageVulnerabilitySummary(ctx context.Co return ec.fieldContext_ImageVulnerabilitySummary_critical(ctx, field) case "unassigned": return ec.fieldContext_ImageVulnerabilitySummary_unassigned(ctx, field) - case "priorityActNow": - return ec.fieldContext_ImageVulnerabilitySummary_priorityActNow(ctx, field) - case "priorityHigh": - return ec.fieldContext_ImageVulnerabilitySummary_priorityHigh(ctx, field) - case "priorityElevated": - return ec.fieldContext_ImageVulnerabilitySummary_priorityElevated(ctx, field) - case "priorityMonitor": - return ec.fieldContext_ImageVulnerabilitySummary_priorityMonitor(ctx, field) - case "topVulnerabilityPriority": - return ec.fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field) case "lastUpdated": return ec.fieldContext_ImageVulnerabilitySummary_lastUpdated(ctx, field) case "staleImageTag": @@ -34481,6 +34576,36 @@ func (ec *executionContext) childFields_ImageVulnerabilitySummary(ctx context.Co return nil, fmt.Errorf("no field named %q was found under type ImageVulnerabilitySummary", field.Name) } +func (ec *executionContext) childFields_ImageVulnerabilitySummaryCountsByPriority(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "urgent": + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_urgent(ctx, field) + case "highRisk": + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_highRisk(ctx, field) + case "elevatedRisk": + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_elevatedRisk(ctx, field) + case "monitor": + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_monitor(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ImageVulnerabilitySummaryCountsByPriority", field.Name) +} + +func (ec *executionContext) childFields_ImageVulnerabilitySummaryCountsBySeverity(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "critical": + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_critical(ctx, field) + case "high": + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_high(ctx, field) + case "medium": + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_medium(ctx, field) + case "low": + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_low(ctx, field) + case "unassigned": + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_unassigned(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ImageVulnerabilitySummaryCountsBySeverity", field.Name) +} + func (ec *executionContext) childFields_ImageVulnerabilitySuppression(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "state": diff --git a/internal/graph/gengql/schema.generated.go b/internal/graph/gengql/schema.generated.go index 3876800f0..a3c9f01b6 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -6607,6 +6607,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._FailedSynchronizationIssue(ctx, sel, obj) + case issue.ExternalIngressUrgentVulnerabilityIssue: + return ec._ExternalIngressUrgentVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressUrgentVulnerabilityIssue: + if obj == nil { + return graphql.Null + } + return ec._ExternalIngressUrgentVulnerabilityIssue(ctx, sel, obj) case issue.ExternalIngressCriticalVulnerabilityIssue: return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, &obj) case *issue.ExternalIngressCriticalVulnerabilityIssue: @@ -6614,13 +6621,6 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) - case issue.ExternalIngressActNowVulnerabilityIssue: - return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) - case *issue.ExternalIngressActNowVulnerabilityIssue: - if obj == nil { - return graphql.Null - } - return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, obj) case issue.DeprecatedRegistryIssue: return ec._DeprecatedRegistryIssue(ctx, sel, &obj) case *issue.DeprecatedRegistryIssue: diff --git a/internal/graph/gengql/vulnerability.generated.go b/internal/graph/gengql/vulnerability.generated.go index 08ce0c354..cca6d7c06 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -1435,6 +1435,70 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_riskScore(_ c return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) } +func (ec *executionContext) _ImageVulnerabilitySummary_countsBySeverity(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_countsBySeverity(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.CountsBySeverity, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v vulnerability.ImageVulnerabilitySummaryCountsBySeverity) graphql.Marshaler { + return ec.marshalNImageVulnerabilitySummaryCountsBySeverity2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐImageVulnerabilitySummaryCountsBySeverity(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_countsBySeverity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageVulnerabilitySummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ImageVulnerabilitySummaryCountsBySeverity(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageVulnerabilitySummary_countsByPriority(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_countsByPriority(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.CountsByPriority, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v vulnerability.ImageVulnerabilitySummaryCountsByPriority) graphql.Marshaler { + return ec.marshalNImageVulnerabilitySummaryCountsByPriority2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐImageVulnerabilitySummaryCountsByPriority(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_countsByPriority(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageVulnerabilitySummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ImageVulnerabilitySummaryCountsByPriority(ctx, field) + }, + } + return fc, nil +} + func (ec *executionContext) _ImageVulnerabilitySummary_low(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -1550,16 +1614,62 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_unassigned(_ return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_priorityActNow(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummary_lastUpdated(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_lastUpdated(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.LastUpdated, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *time.Time) graphql.Marshaler { + return ec.marshalOTime2ᚖtimeᚐTime(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_lastUpdated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Time does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummary_staleImageTag(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_priorityActNow(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummary_staleImageTag(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.PriorityActNow, nil + return obj.StaleImageTag, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_staleImageTag(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummaryCountsByPriority_urgent(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsByPriority) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_urgent(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Urgent, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { @@ -1569,20 +1679,20 @@ func (ec *executionContext) _ImageVulnerabilitySummary_priorityActNow(ctx contex true, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityActNow(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsByPriority_urgent(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsByPriority", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_priorityHigh(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummaryCountsByPriority_highRisk(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsByPriority) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_priorityHigh(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_highRisk(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.PriorityHigh, nil + return obj.HighRisk, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { @@ -1592,20 +1702,20 @@ func (ec *executionContext) _ImageVulnerabilitySummary_priorityHigh(ctx context. true, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityHigh(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsByPriority_highRisk(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsByPriority", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_priorityElevated(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummaryCountsByPriority_elevatedRisk(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsByPriority) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_priorityElevated(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_elevatedRisk(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.PriorityElevated, nil + return obj.ElevatedRisk, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { @@ -1615,20 +1725,20 @@ func (ec *executionContext) _ImageVulnerabilitySummary_priorityElevated(ctx cont true, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityElevated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsByPriority_elevatedRisk(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsByPriority", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_priorityMonitor(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummaryCountsByPriority_monitor(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsByPriority) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_priorityMonitor(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummaryCountsByPriority_monitor(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.PriorityMonitor, nil + return obj.Monitor, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { @@ -1638,77 +1748,123 @@ func (ec *executionContext) _ImageVulnerabilitySummary_priorityMonitor(ctx conte true, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_priorityMonitor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsByPriority_monitor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsByPriority", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_topVulnerabilityPriority(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummaryCountsBySeverity_critical(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsBySeverity) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_critical(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.TopVulnerabilityPriority, nil + return obj.Critical, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *vulnerability.CVEPriority) graphql.Marshaler { - return ec.marshalOCVEPriority2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐCVEPriority(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) }, true, - false, + true, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type CVEPriority does not have child fields")) +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsBySeverity_critical(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsBySeverity", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_lastUpdated(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummaryCountsBySeverity_high(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsBySeverity) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_lastUpdated(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_high(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.LastUpdated, nil + return obj.High, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *time.Time) graphql.Marshaler { - return ec.marshalOTime2ᚖtimeᚐTime(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) }, true, - false, + true, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_lastUpdated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type Time does not have child fields")) +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsBySeverity_high(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsBySeverity", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _ImageVulnerabilitySummary_staleImageTag(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { +func (ec *executionContext) _ImageVulnerabilitySummaryCountsBySeverity_medium(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsBySeverity) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_ImageVulnerabilitySummary_staleImageTag(ctx, field) + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_medium(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.StaleImageTag, nil + return obj.Medium, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { - return ec.marshalOString2ᚖstring(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) }, true, - false, + true, ) } -func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_staleImageTag(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("ImageVulnerabilitySummary", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsBySeverity_medium(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsBySeverity", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummaryCountsBySeverity_low(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsBySeverity) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_low(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Low, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsBySeverity_low(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsBySeverity", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageVulnerabilitySummaryCountsBySeverity_unassigned(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummaryCountsBySeverity) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummaryCountsBySeverity_unassigned(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Unassigned, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageVulnerabilitySummaryCountsBySeverity_unassigned(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageVulnerabilitySummaryCountsBySeverity", field, false, false, errors.New("field of type Int does not have child fields")) } func (ec *executionContext) _ImageVulnerabilitySuppression_state(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySuppression) (ret graphql.Marshaler) { @@ -4320,6 +4476,16 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel if out.Values[i] == graphql.Null { out.Invalids++ } + case "countsBySeverity": + out.Values[i] = ec._ImageVulnerabilitySummary_countsBySeverity(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "countsByPriority": + out.Values[i] = ec._ImageVulnerabilitySummary_countsByPriority(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "low": out.Values[i] = ec._ImageVulnerabilitySummary_low(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -4345,32 +4511,123 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel if out.Values[i] == graphql.Null { out.Invalids++ } - case "priorityActNow": - out.Values[i] = ec._ImageVulnerabilitySummary_priorityActNow(ctx, field, obj) + case "lastUpdated": + out.Values[i] = ec._ImageVulnerabilitySummary_lastUpdated(ctx, field, obj) + case "staleImageTag": + out.Values[i] = ec._ImageVulnerabilitySummary_staleImageTag(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var imageVulnerabilitySummaryCountsByPriorityImplementors = []string{"ImageVulnerabilitySummaryCountsByPriority"} + +func (ec *executionContext) _ImageVulnerabilitySummaryCountsByPriority(ctx context.Context, sel ast.SelectionSet, obj *vulnerability.ImageVulnerabilitySummaryCountsByPriority) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, imageVulnerabilitySummaryCountsByPriorityImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ImageVulnerabilitySummaryCountsByPriority") + case "urgent": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsByPriority_urgent(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "priorityHigh": - out.Values[i] = ec._ImageVulnerabilitySummary_priorityHigh(ctx, field, obj) + case "highRisk": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsByPriority_highRisk(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "priorityElevated": - out.Values[i] = ec._ImageVulnerabilitySummary_priorityElevated(ctx, field, obj) + case "elevatedRisk": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsByPriority_elevatedRisk(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "priorityMonitor": - out.Values[i] = ec._ImageVulnerabilitySummary_priorityMonitor(ctx, field, obj) + case "monitor": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsByPriority_monitor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var imageVulnerabilitySummaryCountsBySeverityImplementors = []string{"ImageVulnerabilitySummaryCountsBySeverity"} + +func (ec *executionContext) _ImageVulnerabilitySummaryCountsBySeverity(ctx context.Context, sel ast.SelectionSet, obj *vulnerability.ImageVulnerabilitySummaryCountsBySeverity) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, imageVulnerabilitySummaryCountsBySeverityImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ImageVulnerabilitySummaryCountsBySeverity") + case "critical": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsBySeverity_critical(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "high": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsBySeverity_high(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "medium": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsBySeverity_medium(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "low": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsBySeverity_low(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "unassigned": + out.Values[i] = ec._ImageVulnerabilitySummaryCountsBySeverity_unassigned(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "topVulnerabilityPriority": - out.Values[i] = ec._ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field, obj) - case "lastUpdated": - out.Values[i] = ec._ImageVulnerabilitySummary_lastUpdated(ctx, field, obj) - case "staleImageTag": - out.Values[i] = ec._ImageVulnerabilitySummary_staleImageTag(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -5502,6 +5759,14 @@ func (ec *executionContext) marshalNImageVulnerabilitySummary2ᚖgithubᚗcomᚋ return ec._ImageVulnerabilitySummary(ctx, sel, v) } +func (ec *executionContext) marshalNImageVulnerabilitySummaryCountsByPriority2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐImageVulnerabilitySummaryCountsByPriority(ctx context.Context, sel ast.SelectionSet, v vulnerability.ImageVulnerabilitySummaryCountsByPriority) graphql.Marshaler { + return ec._ImageVulnerabilitySummaryCountsByPriority(ctx, sel, &v) +} + +func (ec *executionContext) marshalNImageVulnerabilitySummaryCountsBySeverity2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐImageVulnerabilitySummaryCountsBySeverity(ctx context.Context, sel ast.SelectionSet, v vulnerability.ImageVulnerabilitySummaryCountsBySeverity) graphql.Marshaler { + return ec._ImageVulnerabilitySummaryCountsBySeverity(ctx, sel, &v) +} + func (ec *executionContext) unmarshalNImageVulnerabilitySuppressionState2githubᚗcomᚋnaisᚋapiᚋinternalᚋvulnerabilityᚐImageVulnerabilitySuppressionState(ctx context.Context, v any) (vulnerability.ImageVulnerabilitySuppressionState, error) { var res vulnerability.ImageVulnerabilitySuppressionState err := res.UnmarshalGQL(v) diff --git a/internal/graph/issues.resolvers.go b/internal/graph/issues.resolvers.go index 80c46b961..4befe6098 100644 --- a/internal/graph/issues.resolvers.go +++ b/internal/graph/issues.resolvers.go @@ -40,19 +40,19 @@ func (r *deprecatedRegistryIssueResolver) Workload(ctx context.Context, obj *iss return getWorkloadByResourceType(ctx, obj.TeamSlug, obj.EnvironmentName, obj.ResourceName, obj.ResourceType) } -func (r *externalIngressActNowVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (*team.TeamEnvironment, error) { +func (r *externalIngressCriticalVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) { return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) } -func (r *externalIngressActNowVulnerabilityIssueResolver) Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) { +func (r *externalIngressCriticalVulnerabilityIssueResolver) Workload(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (workload.Workload, error) { return getWorkloadByResourceType(ctx, obj.TeamSlug, obj.EnvironmentName, obj.ResourceName, obj.ResourceType) } -func (r *externalIngressCriticalVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) { +func (r *externalIngressUrgentVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (*team.TeamEnvironment, error) { return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) } -func (r *externalIngressCriticalVulnerabilityIssueResolver) Workload(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (workload.Workload, error) { +func (r *externalIngressUrgentVulnerabilityIssueResolver) Workload(ctx context.Context, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (workload.Workload, error) { return getWorkloadByResourceType(ctx, obj.TeamSlug, obj.EnvironmentName, obj.ResourceName, obj.ResourceType) } @@ -181,14 +181,14 @@ func (r *Resolver) DeprecatedRegistryIssue() gengql.DeprecatedRegistryIssueResol return &deprecatedRegistryIssueResolver{r} } -func (r *Resolver) ExternalIngressActNowVulnerabilityIssue() gengql.ExternalIngressActNowVulnerabilityIssueResolver { - return &externalIngressActNowVulnerabilityIssueResolver{r} -} - func (r *Resolver) ExternalIngressCriticalVulnerabilityIssue() gengql.ExternalIngressCriticalVulnerabilityIssueResolver { return &externalIngressCriticalVulnerabilityIssueResolver{r} } +func (r *Resolver) ExternalIngressUrgentVulnerabilityIssue() gengql.ExternalIngressUrgentVulnerabilityIssueResolver { + return &externalIngressUrgentVulnerabilityIssueResolver{r} +} + func (r *Resolver) FailedSynchronizationIssue() gengql.FailedSynchronizationIssueResolver { return &failedSynchronizationIssueResolver{r} } @@ -243,8 +243,8 @@ type ( applicationRestartLoopIssueResolver struct{ *Resolver } deprecatedIngressIssueResolver struct{ *Resolver } deprecatedRegistryIssueResolver struct{ *Resolver } - externalIngressActNowVulnerabilityIssueResolver struct{ *Resolver } externalIngressCriticalVulnerabilityIssueResolver struct{ *Resolver } + externalIngressUrgentVulnerabilityIssueResolver struct{ *Resolver } failedSynchronizationIssueResolver struct{ *Resolver } invalidSpecIssueResolver struct{ *Resolver } issueConnectionResolver struct{ *Resolver } diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index 6707666dc..b13726871 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -229,8 +229,8 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE - EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY.") - EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY + EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_URGENT_VULNERABILITY.") + EXTERNAL_INGRESS_URGENT_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." APPLICATION_RESTART_LOOP @@ -247,27 +247,27 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -"Deprecated: use ExternalIngressActNowVulnerabilityIssue." +"Deprecated: use ExternalIngressUrgentVulnerabilityIssue." type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { - id: ID! - teamEnvironment: TeamEnvironment! - severity: Severity! - message: String! + id: ID! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + teamEnvironment: TeamEnvironment! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + severity: Severity! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + message: String! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - workload: Workload! - cvssScore: Float! - ingresses: [String!]! + workload: Workload! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + cvssScore: Float! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + ingresses: [String!]! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") } -"Raised when a workload with external ingresses has one or more ACT_NOW vulnerability-priority findings." -type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { +"Raised when a workload with external ingresses has one or more urgent vulnerability-priority findings." +type ExternalIngressUrgentVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! severity: Severity! message: String! workload: Workload! - priorityActNow: Int! + priorityUrgent: Int! ingresses: [String!]! } diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index f75521f57..04fa5a97d 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -281,35 +281,31 @@ type ImageVulnerabilitySummary { "Risk score of the image." riskScore: Int! + "Vulnerability counts grouped by severity." + countsBySeverity: ImageVulnerabilitySummaryCountsBySeverity! + + "Vulnerability counts grouped by operational priority." + countsByPriority: ImageVulnerabilitySummaryCountsByPriority! + "Number of vulnerabilities with severity LOW." low: Int! + @deprecated(reason: "Use countsBySeverity.low instead.") "Number of vulnerabilities with severity MEDIUM." medium: Int! + @deprecated(reason: "Use countsBySeverity.medium instead.") "Number of vulnerabilities with severity HIGH." high: Int! + @deprecated(reason: "Use countsBySeverity.high instead.") "Number of vulnerabilities with severity CRITICAL." critical: Int! + @deprecated(reason: "Use countsBySeverity.critical instead.") "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! - - "Number of vulnerabilities with vulnerability priority ACT_NOW." - priorityActNow: Int! - - "Number of vulnerabilities with vulnerability priority HIGH." - priorityHigh: Int! - - "Number of vulnerabilities with vulnerability priority ELEVATED." - priorityElevated: Int! - - "Number of vulnerabilities with vulnerability priority MONITOR." - priorityMonitor: Int! - - "The highest priority among vulnerabilities in this summary." - topVulnerabilityPriority: CVEPriority + @deprecated(reason: "Use countsBySeverity.unassigned instead.") "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -322,6 +318,37 @@ type ImageVulnerabilitySummary { staleImageTag: String } +type ImageVulnerabilitySummaryCountsBySeverity { + "Number of vulnerabilities with severity CRITICAL." + critical: Int! + + "Number of vulnerabilities with severity HIGH." + high: Int! + + "Number of vulnerabilities with severity MEDIUM." + medium: Int! + + "Number of vulnerabilities with severity LOW." + low: Int! + + "Number of vulnerabilities with severity UNASSIGNED." + unassigned: Int! +} + +type ImageVulnerabilitySummaryCountsByPriority { + "Known-exploited vulnerabilities that require immediate action." + urgent: Int! + + "Vulnerabilities with strong exploitation indicators." + highRisk: Int! + + "Vulnerabilities with elevated exploitation risk." + elevatedRisk: Int! + + "Vulnerabilities that should be monitored." + monitor: Int! +} + type ImageVulnerabilityConnection { "Information to aid in pagination." pageInfo: PageInfo! @@ -641,13 +668,33 @@ enum VulnerabilitySummaryOrderByField { """ VULNERABILITY_SEVERITY_UNASSIGNED """ - Order by ACT_NOW vulnerability-priority count. + Order by the number of urgent vulnerabilities. + + Urgent vulnerabilities are known to be actively exploited and should be + prioritized for immediate action. + """ + VULNERABILITY_PRIORITY_URGENT + """ + Order by the number of high-risk vulnerabilities. + + High-risk vulnerabilities are not known exploited, but have strong + exploitation indicators such as known ransomware use or very high EPSS. """ - VULNERABILITY_PRIORITY_ACT_NOW + VULNERABILITY_PRIORITY_HIGH_RISK """ - Order by HIGH vulnerability-priority count. + Order by the number of elevated-risk vulnerabilities. + + Elevated-risk vulnerabilities are lower priority than high-risk, but still + have meaningful exploitation risk signals. + """ + VULNERABILITY_PRIORITY_ELEVATED_RISK + """ + Order by the number of monitor vulnerabilities. + + Monitor vulnerabilities should be tracked, but do not currently indicate the + same operational urgency as the higher priority buckets. """ - VULNERABILITY_PRIORITY_HIGH + VULNERABILITY_PRIORITY_MONITOR } type TenantVulnerabilitySummary { diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index c3e1c0666..47241f407 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -210,19 +210,19 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { seenActNow[key] = struct{}{} ret = append(ret, &Issue{ - IssueType: issue.IssueTypeExternalIngressActNowVulnerability, + IssueType: issue.IssueTypeExternalIngressUrgentVulnerability, ResourceType: workloadType, ResourceName: node.Workload.GetName(), Team: node.Workload.GetNamespace(), Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload with external ingresses %s has %d urgent vulnerabilities", - strings.Join(externalIngresses, ", "), + "Workload '%s' (exposed via external ingress) has %d urgent vulnerabilities", + node.Workload.GetName(), node.VulnerabilitySummary.ActNow, ), - IssueDetails: issue.ExternalIngressActNowVulnerabilityIssueDetails{ - PriorityActNow: int(node.VulnerabilitySummary.ActNow), + IssueDetails: issue.ExternalIngressUrgentVulnerabilityIssueDetails{ + PriorityUrgent: int(node.VulnerabilitySummary.ActNow), Ingresses: externalIngresses, }, }) diff --git a/internal/issue/checker/workload_v13s_test.go b/internal/issue/checker/workload_v13s_test.go index 898e467bd..20cc57138 100644 --- a/internal/issue/checker/workload_v13s_test.go +++ b/internal/issue/checker/workload_v13s_test.go @@ -110,7 +110,7 @@ func testVulnerabilitiesExternalIngressActNowIssue(t *testing.T, workloadName, e issues := workload.vulnerabilities(ctx) actNowIssues := make([]*Issue, 0) for i := range issues { - if issues[i].IssueType == issue.IssueTypeExternalIngressActNowVulnerability { + if issues[i].IssueType == issue.IssueTypeExternalIngressUrgentVulnerability { actNowIssues = append(actNowIssues, issues[i]) } } @@ -127,21 +127,21 @@ func testVulnerabilitiesExternalIngressActNowIssue(t *testing.T, workloadName, e } got := actNowIssues[0] - if got.IssueType != issue.IssueTypeExternalIngressActNowVulnerability { - t.Fatalf("expected issue type %s, got %s", issue.IssueTypeExternalIngressActNowVulnerability, got.IssueType) + if got.IssueType != issue.IssueTypeExternalIngressUrgentVulnerability { + t.Fatalf("expected issue type %s, got %s", issue.IssueTypeExternalIngressUrgentVulnerability, got.IssueType) } if got.ResourceName != workloadName { t.Fatalf("expected resource %s, got %s", workloadName, got.ResourceName) } - details, ok := got.IssueDetails.(issue.ExternalIngressActNowVulnerabilityIssueDetails) + details, ok := got.IssueDetails.(issue.ExternalIngressUrgentVulnerabilityIssueDetails) if !ok { t.Fatalf("expected external ingress act-now details, got %T", got.IssueDetails) } - if details.PriorityActNow != 2 { - t.Fatalf("expected priorityActNow 2, got %v", details.PriorityActNow) + if details.PriorityUrgent != 2 { + t.Fatalf("expected priorityUrgent 2, got %v", details.PriorityUrgent) } if len(details.Ingresses) != 1 || details.Ingresses[0] != expectedIngress { diff --git a/internal/issue/model.go b/internal/issue/model.go index c3852ef24..03fa8bf3d 100644 --- a/internal/issue/model.go +++ b/internal/issue/model.go @@ -184,8 +184,8 @@ type VulnerableImageIssueDetails struct { Critical int `json:"critical"` } -type ExternalIngressActNowVulnerabilityIssueDetails struct { - PriorityActNow int `json:"priorityActNow"` +type ExternalIngressUrgentVulnerabilityIssueDetails struct { + PriorityUrgent int `json:"priorityUrgent"` Ingresses []string `json:"ingresses"` } @@ -213,7 +213,7 @@ const ( IssueTypeVulnerableImage IssueType = "VULNERABLE_IMAGE" IssueTypeMissingSBOM IssueType = "MISSING_SBOM" IssueTypeExternalIngressCriticalVulnerability IssueType = "EXTERNAL_INGRESS_CRITICAL_VULNERABILITY" - IssueTypeExternalIngressActNowVulnerability IssueType = "EXTERNAL_INGRESS_ACT_NOW_VULNERABILITY" + IssueTypeExternalIngressUrgentVulnerability IssueType = "EXTERNAL_INGRESS_URGENT_VULNERABILITY" IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" ) @@ -233,7 +233,7 @@ var AllIssueType = []IssueType{ IssueTypeVulnerableImage, IssueTypeMissingSBOM, IssueTypeExternalIngressCriticalVulnerability, - IssueTypeExternalIngressActNowVulnerability, + IssueTypeExternalIngressUrgentVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop, } @@ -245,7 +245,7 @@ func (e IssueType) IsValid() bool { IssueTypeNoRunningInstances, IssueTypeLastRunFailed, IssueTypeWorkloadProblem, IssueTypeInvalidSpec, IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, IssueTypeExternalIngressCriticalVulnerability, - IssueTypeExternalIngressActNowVulnerability, IssueTypeUnleashReleaseChannel, + IssueTypeExternalIngressUrgentVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop: return true } @@ -503,14 +503,14 @@ func (VulnerableImageIssue) IsIssue() {} func (VulnerableImageIssue) IsNode() {} -type ExternalIngressActNowVulnerabilityIssue struct { +type ExternalIngressUrgentVulnerabilityIssue struct { Base - ExternalIngressActNowVulnerabilityIssueDetails + ExternalIngressUrgentVulnerabilityIssueDetails } -func (ExternalIngressActNowVulnerabilityIssue) IsIssue() {} +func (ExternalIngressUrgentVulnerabilityIssue) IsIssue() {} -func (ExternalIngressActNowVulnerabilityIssue) IsNode() {} +func (ExternalIngressUrgentVulnerabilityIssue) IsNode() {} type ExternalIngressCriticalVulnerabilityIssue struct { Base diff --git a/internal/issue/queries.go b/internal/issue/queries.go index 9e2ce352c..9906ba79b 100644 --- a/internal/issue/queries.go +++ b/internal/issue/queries.go @@ -206,14 +206,14 @@ func convert(issue *issuesql.Issue) (Issue, error) { return &MissingSbomIssue{ Base: base, }, nil - case IssueTypeExternalIngressActNowVulnerability: - d, err := unmarshal[ExternalIngressActNowVulnerabilityIssueDetails](issue.IssueDetails) + case IssueTypeExternalIngressUrgentVulnerability: + d, err := unmarshal[ExternalIngressUrgentVulnerabilityIssueDetails](issue.IssueDetails) if err != nil { return nil, err } - return &ExternalIngressActNowVulnerabilityIssue{ + return &ExternalIngressUrgentVulnerabilityIssue{ Base: base, - ExternalIngressActNowVulnerabilityIssueDetails: *d, + ExternalIngressUrgentVulnerabilityIssueDetails: *d, }, nil case IssueTypeExternalIngressCriticalVulnerability: d, err := unmarshal[ExternalIngressCriticalVulnerabilityIssueDetails](issue.IssueDetails) diff --git a/internal/vulnerability/models.go b/internal/vulnerability/models.go index ac42b444b..cf4a68583 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -76,20 +76,32 @@ type ImageVulnerabilitySuppression struct { } type ImageVulnerabilitySummary struct { - Total int `json:"total"` - RiskScore int `json:"riskScore"` - Low int `json:"low"` - Medium int `json:"medium"` - High int `json:"high"` - Critical int `json:"critical"` - Unassigned int `json:"unassigned"` - LastUpdated *time.Time `json:"lastUpdated"` - StaleImageTag *string `json:"staleImageTag"` - PriorityActNow int `json:"priorityActNow"` - PriorityHigh int `json:"priorityHigh"` - PriorityElevated int `json:"priorityElevated"` - PriorityMonitor int `json:"priorityMonitor"` - TopVulnerabilityPriority *CVEPriority `json:"topVulnerabilityPriority"` + Total int `json:"total"` + RiskScore int `json:"riskScore"` + CountsBySeverity ImageVulnerabilitySummaryCountsBySeverity `json:"countsBySeverity"` + CountsByPriority ImageVulnerabilitySummaryCountsByPriority `json:"countsByPriority"` + Low int `json:"low"` + Medium int `json:"medium"` + High int `json:"high"` + Critical int `json:"critical"` + Unassigned int `json:"unassigned"` + LastUpdated *time.Time `json:"lastUpdated"` + StaleImageTag *string `json:"staleImageTag"` +} + +type ImageVulnerabilitySummaryCountsBySeverity struct { + Critical int `json:"critical"` + High int `json:"high"` + Medium int `json:"medium"` + Low int `json:"low"` + Unassigned int `json:"unassigned"` +} + +type ImageVulnerabilitySummaryCountsByPriority struct { + Urgent int `json:"urgent"` + HighRisk int `json:"highRisk"` + ElevatedRisk int `json:"elevatedRisk"` + Monitor int `json:"monitor"` } type ImageVulnerabilityOrderField string @@ -223,17 +235,19 @@ type VulnerabilitySummaryOrder struct { type VulnerabilitySummaryOrderByField string const ( - VulnerabilitySummaryOrderByFieldName VulnerabilitySummaryOrderByField = "NAME" - VulnerabilitySummaryOrderByFieldEnvironment VulnerabilitySummaryOrderByField = "ENVIRONMENT" - VulnerabilitySummaryOrderByFieldVulnerabilityRiskScore VulnerabilitySummaryOrderByField = "VULNERABILITY_RISK_SCORE" - VulnerabilitySummaryOrderByFieldVulnerabilitySeverityCritical VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_CRITICAL" - VulnerabilitySummaryOrderByFieldVulnerabilitySeverityHigh VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_HIGH" - VulnerabilitySummaryOrderByFieldVulnerabilitySeverityMedium VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_MEDIUM" - VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_LOW" - VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_UNASSIGNED" - VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned VulnerabilitySummaryOrderByField = "VULNERABILITY_LAST_SCANNED" - VulnerabilitySummaryOrderByFieldVulnerabilityPriorityActNow VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_ACT_NOW" - VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHigh VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_HIGH" + VulnerabilitySummaryOrderByFieldName VulnerabilitySummaryOrderByField = "NAME" + VulnerabilitySummaryOrderByFieldEnvironment VulnerabilitySummaryOrderByField = "ENVIRONMENT" + VulnerabilitySummaryOrderByFieldVulnerabilityRiskScore VulnerabilitySummaryOrderByField = "VULNERABILITY_RISK_SCORE" + VulnerabilitySummaryOrderByFieldVulnerabilitySeverityCritical VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_CRITICAL" + VulnerabilitySummaryOrderByFieldVulnerabilitySeverityHigh VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_HIGH" + VulnerabilitySummaryOrderByFieldVulnerabilitySeverityMedium VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_MEDIUM" + VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_LOW" + VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned VulnerabilitySummaryOrderByField = "VULNERABILITY_SEVERITY_UNASSIGNED" + VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned VulnerabilitySummaryOrderByField = "VULNERABILITY_LAST_SCANNED" + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityUrgent VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_URGENT" + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHighRisk VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_HIGH_RISK" + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityElevatedRisk VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_ELEVATED_RISK" + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityMonitor VulnerabilitySummaryOrderByField = "VULNERABILITY_PRIORITY_MONITOR" ) var AllVulnerabilitySummaryOrderByField = []VulnerabilitySummaryOrderByField{ @@ -246,13 +260,15 @@ var AllVulnerabilitySummaryOrderByField = []VulnerabilitySummaryOrderByField{ VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned, VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned, - VulnerabilitySummaryOrderByFieldVulnerabilityPriorityActNow, - VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHigh, + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityUrgent, + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHighRisk, + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityElevatedRisk, + VulnerabilitySummaryOrderByFieldVulnerabilityPriorityMonitor, } func (e VulnerabilitySummaryOrderByField) IsValid() bool { switch e { - case VulnerabilitySummaryOrderByFieldName, VulnerabilitySummaryOrderByFieldEnvironment, VulnerabilitySummaryOrderByFieldVulnerabilityRiskScore, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityCritical, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityHigh, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityMedium, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned, VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityActNow, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHigh: + case VulnerabilitySummaryOrderByFieldName, VulnerabilitySummaryOrderByFieldEnvironment, VulnerabilitySummaryOrderByFieldVulnerabilityRiskScore, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityCritical, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityHigh, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityMedium, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityLow, VulnerabilitySummaryOrderByFieldVulnerabilitySeverityUnassigned, VulnerabilitySummaryOrderByFieldVulnerabilityLastScanned, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityUrgent, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityHighRisk, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityElevatedRisk, VulnerabilitySummaryOrderByFieldVulnerabilityPriorityMonitor: return true } return false diff --git a/internal/vulnerability/queries.go b/internal/vulnerability/queries.go index 49967ba9a..bff5faf00 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -369,20 +369,28 @@ func GetImageVulnerabilitySummary(ctx context.Context, ref string) (*ImageVulner } return &ImageVulnerabilitySummary{ - Critical: int(sum.GetCritical()), - High: int(sum.GetHigh()), - Medium: int(sum.GetMedium()), - Low: int(sum.GetLow()), - Unassigned: int(sum.GetUnassigned()), - Total: int(sum.GetTotal()), - RiskScore: int(sum.GetRiskScore()), - LastUpdated: lastUpdated, - StaleImageTag: sum.StaleImageTag, - PriorityActNow: int(sum.GetActNow()), - PriorityHigh: int(sum.GetHighRisk()), - PriorityElevated: int(sum.GetElevatedRisk()), - PriorityMonitor: int(sum.GetMonitor()), - TopVulnerabilityPriority: mapPriority(sum.GetTopPriority()), + Critical: int(sum.GetCritical()), + High: int(sum.GetHigh()), + Medium: int(sum.GetMedium()), + Low: int(sum.GetLow()), + Unassigned: int(sum.GetUnassigned()), + Total: int(sum.GetTotal()), + RiskScore: int(sum.GetRiskScore()), + LastUpdated: lastUpdated, + StaleImageTag: sum.StaleImageTag, + CountsBySeverity: ImageVulnerabilitySummaryCountsBySeverity{ + Critical: int(sum.GetCritical()), + High: int(sum.GetHigh()), + Medium: int(sum.GetMedium()), + Low: int(sum.GetLow()), + Unassigned: int(sum.GetUnassigned()), + }, + CountsByPriority: ImageVulnerabilitySummaryCountsByPriority{ + Urgent: int(sum.GetActNow()), + HighRisk: int(sum.GetHighRisk()), + ElevatedRisk: int(sum.GetElevatedRisk()), + Monitor: int(sum.GetMonitor()), + }, }, nil } @@ -495,18 +503,26 @@ func getVulnerabilityHistory(ctx context.Context, opts []vulnerabilities.Option) for _, point := range resp.GetPoints() { samples = append(samples, &ImageVulnerabilitySample{ Summary: &ImageVulnerabilitySummary{ - Critical: int(point.GetCritical()), - High: int(point.GetHigh()), - Medium: int(point.GetMedium()), - Low: int(point.GetLow()), - Unassigned: int(point.GetUnassigned()), - Total: int(point.GetTotal()), - RiskScore: int(point.GetRiskScore()), - PriorityActNow: int(point.GetActNow()), - PriorityHigh: int(point.GetHighRisk()), - PriorityElevated: int(point.GetElevatedRisk()), - PriorityMonitor: int(point.GetMonitor()), - TopVulnerabilityPriority: mapPriority(point.GetTopPriority()), + Critical: int(point.GetCritical()), + High: int(point.GetHigh()), + Medium: int(point.GetMedium()), + Low: int(point.GetLow()), + Unassigned: int(point.GetUnassigned()), + Total: int(point.GetTotal()), + RiskScore: int(point.GetRiskScore()), + CountsBySeverity: ImageVulnerabilitySummaryCountsBySeverity{ + Critical: int(point.GetCritical()), + High: int(point.GetHigh()), + Medium: int(point.GetMedium()), + Low: int(point.GetLow()), + Unassigned: int(point.GetUnassigned()), + }, + CountsByPriority: ImageVulnerabilitySummaryCountsByPriority{ + Urgent: int(point.GetActNow()), + HighRisk: int(point.GetHighRisk()), + ElevatedRisk: int(point.GetElevatedRisk()), + Monitor: int(point.GetMonitor()), + }, }, Date: point.GetBucketTime().AsTime(), }) diff --git a/internal/vulnerability/sortfilter.go b/internal/vulnerability/sortfilter.go index a682a60c7..1abfbc758 100644 --- a/internal/vulnerability/sortfilter.go +++ b/internal/vulnerability/sortfilter.go @@ -20,16 +20,18 @@ var SortFilterImageVulnerabilities = map[ImageVulnerabilityOrderField]vulnerabil } var SortFilterWorkloadSummaries = map[VulnerabilitySummaryOrderByField]vulnerabilities.OrderByField{ - "NAME": vulnerabilities.OrderByWorkload, - "ENVIRONMENT": vulnerabilities.OrderByCluster, - "VULNERABILITY_RISK_SCORE": vulnerabilities.OrderByRiskScore, - "VULNERABILITY_SEVERITY_CRITICAL": vulnerabilities.OrderByCritical, - "VULNERABILITY_SEVERITY_HIGH": vulnerabilities.OrderByHigh, - "VULNERABILITY_SEVERITY_MEDIUM": vulnerabilities.OrderByMedium, - "VULNERABILITY_SEVERITY_LOW": vulnerabilities.OrderByLow, - "VULNERABILITY_SEVERITY_UNASSIGNED": vulnerabilities.OrderByUnassigned, - "VULNERABILITY_PRIORITY_ACT_NOW": vulnerabilities.OrderByActNow, - "VULNERABILITY_PRIORITY_HIGH": vulnerabilities.OrderByHighRisk, + "NAME": vulnerabilities.OrderByWorkload, + "ENVIRONMENT": vulnerabilities.OrderByCluster, + "VULNERABILITY_RISK_SCORE": vulnerabilities.OrderByRiskScore, + "VULNERABILITY_SEVERITY_CRITICAL": vulnerabilities.OrderByCritical, + "VULNERABILITY_SEVERITY_HIGH": vulnerabilities.OrderByHigh, + "VULNERABILITY_SEVERITY_MEDIUM": vulnerabilities.OrderByMedium, + "VULNERABILITY_SEVERITY_LOW": vulnerabilities.OrderByLow, + "VULNERABILITY_SEVERITY_UNASSIGNED": vulnerabilities.OrderByUnassigned, + "VULNERABILITY_PRIORITY_URGENT": vulnerabilities.OrderByActNow, + "VULNERABILITY_PRIORITY_HIGH_RISK": vulnerabilities.OrderByHighRisk, + "VULNERABILITY_PRIORITY_ELEVATED_RISK": vulnerabilities.OrderByElevatedRisk, + "VULNERABILITY_PRIORITY_MONITOR": vulnerabilities.OrderByMonitor, } const ( @@ -52,11 +54,11 @@ func workloadInit() { return func(ctx context.Context, a workload.Workload) int { summary, err := GetImageVulnerabilitySummary(ctx, a.GetImageString()) if err != nil { - return -1 + return 0 } if summary == nil { - return -1 + return 0 } return fn(summary) @@ -81,11 +83,17 @@ func workloadInit() { workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_SEVERITY_UNASSIGNED", summarySorter(func(sum *ImageVulnerabilitySummary) int { return sum.Unassigned }), "NAME", "ENVIRONMENT") - workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_ACT_NOW", summarySorter(func(sum *ImageVulnerabilitySummary) int { - return sum.PriorityActNow + workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_URGENT", summarySorter(func(sum *ImageVulnerabilitySummary) int { + return sum.CountsByPriority.Urgent }), "NAME", "ENVIRONMENT") - workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_HIGH", summarySorter(func(sum *ImageVulnerabilitySummary) int { - return sum.PriorityHigh + workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_HIGH_RISK", summarySorter(func(sum *ImageVulnerabilitySummary) int { + return sum.CountsByPriority.HighRisk + }), "NAME", "ENVIRONMENT") + workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_ELEVATED_RISK", summarySorter(func(sum *ImageVulnerabilitySummary) int { + return sum.CountsByPriority.ElevatedRisk + }), "NAME", "ENVIRONMENT") + workload.SortFilter.RegisterConcurrentSort("VULNERABILITY_PRIORITY_MONITOR", summarySorter(func(sum *ImageVulnerabilitySummary) int { + return sum.CountsByPriority.Monitor }), "NAME", "ENVIRONMENT") workload.SortFilter.RegisterConcurrentSort("HAS_SBOM", func(ctx context.Context, a workload.Workload) int { hasSBOM, err := GetImageHasSBOM(ctx, a.GetImageString()) diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 088ce2fb9..541b8063e 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -78,19 +78,28 @@ func toWorkloadVulnerabilitySummary(w *vulnerabilities.WorkloadSummary) *Workloa } summary := &ImageVulnerabilitySummary{ - Critical: int(v13sSummary.Critical), - High: int(v13sSummary.High), - Medium: int(v13sSummary.Medium), - Low: int(v13sSummary.Low), - Unassigned: int(v13sSummary.Unassigned), - Total: int(v13sSummary.Total), - RiskScore: int(v13sSummary.RiskScore), - LastUpdated: lastUpdated, - PriorityActNow: int(v13sSummary.ActNow), - PriorityHigh: int(v13sSummary.HighRisk), - PriorityElevated: int(v13sSummary.ElevatedRisk), - PriorityMonitor: int(v13sSummary.Monitor), - TopVulnerabilityPriority: mapPriority(v13sSummary.GetTopPriority()), + Critical: int(v13sSummary.Critical), + High: int(v13sSummary.High), + Medium: int(v13sSummary.Medium), + Low: int(v13sSummary.Low), + Unassigned: int(v13sSummary.Unassigned), + Total: int(v13sSummary.Total), + RiskScore: int(v13sSummary.RiskScore), + LastUpdated: lastUpdated, + StaleImageTag: v13sSummary.StaleImageTag, + CountsBySeverity: ImageVulnerabilitySummaryCountsBySeverity{ + Critical: int(v13sSummary.Critical), + High: int(v13sSummary.High), + Medium: int(v13sSummary.Medium), + Low: int(v13sSummary.Low), + Unassigned: int(v13sSummary.Unassigned), + }, + CountsByPriority: ImageVulnerabilitySummaryCountsByPriority{ + Urgent: int(v13sSummary.ActNow), + HighRisk: int(v13sSummary.HighRisk), + ElevatedRisk: int(v13sSummary.ElevatedRisk), + Monitor: int(v13sSummary.Monitor), + }, } return &WorkloadVulnerabilitySummary{ @@ -157,21 +166,6 @@ func mapVulnerabilitySeverity(severity ImageVulnerabilitySeverity) vulnerabiliti } } -func mapPriority(priority vulnerabilities.Priority) *CVEPriority { - switch priority { - case vulnerabilities.Priority_PRIORITY_ACT_NOW: - return new(CVEPriorityActNow) - case vulnerabilities.Priority_PRIORITY_HIGH: - return new(CVEPriorityHigh) - case vulnerabilities.Priority_PRIORITY_ELEVATED: - return new(CVEPriorityElevated) - case vulnerabilities.Priority_PRIORITY_MONITOR: - return new(CVEPriorityMonitor) - default: - return nil - } -} - func mapCVEPriorityToPriority(p CVEPriority) vulnerabilities.Priority { switch p { case CVEPriorityActNow: From 817f7e3f132a2c3e22abe388635d92444e49d790 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:24:43 +0200 Subject: [PATCH 33/36] chore: resolve dependency file conflicts after rebase --- go.mod | 2 +- go.sum | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/go.mod b/go.mod index e633c1c6a..c39245929 100644 --- a/go.mod +++ b/go.mod @@ -79,7 +79,7 @@ require ( golang.org/x/sync v0.21.0 golang.org/x/text v0.37.0 golang.org/x/tools v0.45.0 - google.golang.org/api v0.280.0 + google.golang.org/api v0.284.0 google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa google.golang.org/grpc v1.81.1 google.golang.org/protobuf v1.36.11 diff --git a/go.sum b/go.sum index 15ba152ee..3a5640eb0 100644 --- a/go.sum +++ b/go.sum @@ -1311,13 +1311,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -<<<<<<< HEAD -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -======= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= ->>>>>>> f6297e83 (chore(deps): align v13s api with main) golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1410,12 +1403,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1: google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 h1:PvEgGJf9C/1u5CHkInMg7UFYYUoiaQmW2LbtH0pjB78= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= From be7e343bf1606ee785688faed98edc834b3ddb9a Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:29:24 +0200 Subject: [PATCH 34/36] refactor(vulnerability): adjust formatting of deprecation annotations for clarity --- internal/graph/schema/issues.graphqls | 6 ++++-- internal/graph/schema/vulnerability.graphqls | 15 +++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index b13726871..7840ef252 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -229,7 +229,8 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE - EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_URGENT_VULNERABILITY.") + EXTERNAL_INGRESS_CRITICAL_VULNERABILITY + @deprecated(reason: "Use EXTERNAL_INGRESS_URGENT_VULNERABILITY.") EXTERNAL_INGRESS_URGENT_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." @@ -250,7 +251,8 @@ type VulnerableImageIssue implements Issue & Node { "Deprecated: use ExternalIngressUrgentVulnerabilityIssue." type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { id: ID! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - teamEnvironment: TeamEnvironment! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + teamEnvironment: TeamEnvironment! + @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") severity: Severity! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") message: String! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 04fa5a97d..8ed36ee37 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -288,24 +288,19 @@ type ImageVulnerabilitySummary { countsByPriority: ImageVulnerabilitySummaryCountsByPriority! "Number of vulnerabilities with severity LOW." - low: Int! - @deprecated(reason: "Use countsBySeverity.low instead.") + low: Int! @deprecated(reason: "Use countsBySeverity.low instead.") "Number of vulnerabilities with severity MEDIUM." - medium: Int! - @deprecated(reason: "Use countsBySeverity.medium instead.") + medium: Int! @deprecated(reason: "Use countsBySeverity.medium instead.") "Number of vulnerabilities with severity HIGH." - high: Int! - @deprecated(reason: "Use countsBySeverity.high instead.") + high: Int! @deprecated(reason: "Use countsBySeverity.high instead.") "Number of vulnerabilities with severity CRITICAL." - critical: Int! - @deprecated(reason: "Use countsBySeverity.critical instead.") + critical: Int! @deprecated(reason: "Use countsBySeverity.critical instead.") "Number of vulnerabilities with severity UNASSIGNED." - unassigned: Int! - @deprecated(reason: "Use countsBySeverity.unassigned instead.") + unassigned: Int! @deprecated(reason: "Use countsBySeverity.unassigned instead.") "Timestamp of the last update of the vulnerability summary." lastUpdated: Time From ac07782ed7a2dc9015557b665b1d9c2a23076fc3 Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:35:39 +0200 Subject: [PATCH 35/36] refactor(vulnerability): improve formatting of deprecation annotations for consistency --- internal/graph/gengql/root_.generated.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 32ddff379..ed2554744 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -23741,7 +23741,8 @@ enum IssueType { INVALID_SPEC @deprecated(reason: "Use WORKLOAD_PROBLEM instead.") MISSING_SBOM VULNERABLE_IMAGE - EXTERNAL_INGRESS_CRITICAL_VULNERABILITY @deprecated(reason: "Use EXTERNAL_INGRESS_URGENT_VULNERABILITY.") + EXTERNAL_INGRESS_CRITICAL_VULNERABILITY + @deprecated(reason: "Use EXTERNAL_INGRESS_URGENT_VULNERABILITY.") EXTERNAL_INGRESS_URGENT_VULNERABILITY UNLEASH_RELEASE_CHANNEL "Raised when an application is stuck in a restart loop." @@ -23762,7 +23763,8 @@ type VulnerableImageIssue implements Issue & Node { "Deprecated: use ExternalIngressUrgentVulnerabilityIssue." type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { id: ID! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - teamEnvironment: TeamEnvironment! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") + teamEnvironment: TeamEnvironment! + @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") severity: Severity! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") message: String! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") @@ -32015,24 +32017,19 @@ type ImageVulnerabilitySummary { countsByPriority: ImageVulnerabilitySummaryCountsByPriority! "Number of vulnerabilities with severity LOW." - low: Int! - @deprecated(reason: "Use countsBySeverity.low instead.") + low: Int! @deprecated(reason: "Use countsBySeverity.low instead.") "Number of vulnerabilities with severity MEDIUM." - medium: Int! - @deprecated(reason: "Use countsBySeverity.medium instead.") + medium: Int! @deprecated(reason: "Use countsBySeverity.medium instead.") "Number of vulnerabilities with severity HIGH." - high: Int! - @deprecated(reason: "Use countsBySeverity.high instead.") + high: Int! @deprecated(reason: "Use countsBySeverity.high instead.") "Number of vulnerabilities with severity CRITICAL." - critical: Int! - @deprecated(reason: "Use countsBySeverity.critical instead.") + critical: Int! @deprecated(reason: "Use countsBySeverity.critical instead.") "Number of vulnerabilities with severity UNASSIGNED." - unassigned: Int! - @deprecated(reason: "Use countsBySeverity.unassigned instead.") + unassigned: Int! @deprecated(reason: "Use countsBySeverity.unassigned instead.") "Timestamp of the last update of the vulnerability summary." lastUpdated: Time From 7d3d6935c7623e8380b8897be0118c82de7e389a Mon Sep 17 00:00:00 2001 From: Youssef Bel Mekki <38552193+ybelMekk@users.noreply.github.com> Date: Tue, 30 Jun 2026 14:13:11 +0200 Subject: [PATCH 36/36] refactor(vulnerability): enhance ordering enums and improve error handling in workload processing --- internal/graph/gengql/root_.generated.go | 12 ++++++++++++ internal/graph/schema/vulnerability.graphqls | 12 ++++++++++++ internal/issue/checker/workload_v13s.go | 17 +++++++++++------ internal/vulnerability/sortfilter.go | 4 ++-- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index ed2554744..5e40e16fe 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -31780,10 +31780,15 @@ input CVEOrder { } enum CVEOrderField { + "Order by CVE identifier." IDENTIFIER + "Order by CVE severity." SEVERITY + "Order by CVSS score." CVSS_SCORE + "Order by number of affected workloads." AFFECTED_WORKLOADS_COUNT + "Order by CVE priority derived from threat intelligence signals." PRIORITY } @@ -32319,12 +32324,19 @@ input ImageVulnerabilityOrder { } enum ImageVulnerabilityOrderField { + "Order by vulnerability identifier." IDENTIFIER + "Order by vulnerability severity." SEVERITY + "Order by when the vulnerability received its current severity." SEVERITY_SINCE + "Order by affected package name." PACKAGE + "Order by suppression state." STATE + "Order by whether the vulnerability is suppressed." SUPPRESSED + "Order by vulnerability priority derived from threat intelligence signals." PRIORITY } diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 8ed36ee37..9ecf39047 100644 --- a/internal/graph/schema/vulnerability.graphqls +++ b/internal/graph/schema/vulnerability.graphqls @@ -51,10 +51,15 @@ input CVEOrder { } enum CVEOrderField { + "Order by CVE identifier." IDENTIFIER + "Order by CVE severity." SEVERITY + "Order by CVSS score." CVSS_SCORE + "Order by number of affected workloads." AFFECTED_WORKLOADS_COUNT + "Order by CVE priority derived from threat intelligence signals." PRIORITY } @@ -590,12 +595,19 @@ input ImageVulnerabilityOrder { } enum ImageVulnerabilityOrderField { + "Order by vulnerability identifier." IDENTIFIER + "Order by vulnerability severity." SEVERITY + "Order by when the vulnerability received its current severity." SEVERITY_SINCE + "Order by affected package name." PACKAGE + "Order by suppression state." STATE + "Order by whether the vulnerability is suppressed." SUPPRESSED + "Order by vulnerability priority derived from threat intelligence signals." PRIORITY } diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 47241f407..f850cfec3 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -188,7 +188,12 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { seenActNow := map[string]struct{}{} for _, node := range resp.GetNodes() { - workloadType, ok := mapType(node.Workload.GetType()) + workloadRef := node.GetWorkload() + if workloadRef == nil { + continue + } + + workloadType, ok := mapType(workloadRef.GetType()) if !ok || workloadType != issue.ResourceTypeApplication { continue } @@ -197,8 +202,8 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { continue } - env := environmentmapper.EnvironmentName(node.Workload.GetCluster()) - key := workloadKey(env, node.Workload.GetNamespace(), node.Workload.GetName()) + env := environmentmapper.EnvironmentName(workloadRef.GetCluster()) + key := workloadKey(env, workloadRef.GetNamespace(), workloadRef.GetName()) if _, exists := seenActNow[key]; exists { continue } @@ -212,13 +217,13 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { ret = append(ret, &Issue{ IssueType: issue.IssueTypeExternalIngressUrgentVulnerability, ResourceType: workloadType, - ResourceName: node.Workload.GetName(), - Team: node.Workload.GetNamespace(), + ResourceName: workloadRef.GetName(), + Team: workloadRef.GetNamespace(), Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( "Workload '%s' (exposed via external ingress) has %d urgent vulnerabilities", - node.Workload.GetName(), + workloadRef.GetName(), node.VulnerabilitySummary.ActNow, ), IssueDetails: issue.ExternalIngressUrgentVulnerabilityIssueDetails{ diff --git a/internal/vulnerability/sortfilter.go b/internal/vulnerability/sortfilter.go index 1abfbc758..633941ab8 100644 --- a/internal/vulnerability/sortfilter.go +++ b/internal/vulnerability/sortfilter.go @@ -54,11 +54,11 @@ func workloadInit() { return func(ctx context.Context, a workload.Workload) int { summary, err := GetImageVulnerabilitySummary(ctx, a.GetImageString()) if err != nil { - return 0 + return -1 } if summary == nil { - return 0 + return -1 } return fn(summary)