diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index 7fb9b931c..642147ddb 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -2,6 +2,7 @@ Helper.readK8sResources("k8s_resources/issues") local user = User.new("name", "auth@user.com", "sdf") Team.new("myteam", "purpose", "#slack_channel") Team.new("sortteam", "purpose", "#slack_channel") +Team.new("devteam", "purpose", "#slack_channel") local checker = IssueChecker.new() checker:runChecks() @@ -552,48 +553,38 @@ Test.gql("VulnerableImageIssue", function(t) data = { team = { issues = { - nodes = { - { - __typename = "VulnerableImageIssue", - message = "Image 'vulnerable-image' has 2 known-exploited vulnerabilities", - severity = "CRITICAL", - critical = 5, - riskScore = 250, - workload = { - name = "vulnerable", - }, - }, - }, + nodes = {}, }, }, }, } end) -Test.gql("ExternalIngressUrgentVulnerabilityIssue", function(t) +Test.gql("Known exploited workload without external ingress has only image issue", function(t) t.addHeader("x-user-email", user:email()) t.query [[ query { - team(slug: "myteam") { - issues( - filter: { - issueType: EXTERNAL_INGRESS_URGENT_VULNERABILITY - }, - ) { + team(slug: "devteam") { + imageIssues: issues(filter: {issueType: VULNERABLE_IMAGE}) { nodes { __typename severity message - ... on ExternalIngressUrgentVulnerabilityIssue { - priorityUrgent - ingresses + ... on VulnerableImageIssue { + critical + riskScore workload { name } } } } + urgentIssues: issues(filter: {issueType: EXTERNAL_INGRESS_URGENT_VULNERABILITY}) { + nodes { + __typename + } + } } } ]] @@ -601,26 +592,29 @@ Test.gql("ExternalIngressUrgentVulnerabilityIssue", function(t) t.check { data = { team = { - issues = { + imageIssues = { nodes = { { - __typename = "ExternalIngressUrgentVulnerabilityIssue", - message = "Workload 'vulnerable' (exposed via external ingress) has 2 urgent vulnerabilities", - severity = "CRITICAL", - priorityUrgent = 2, - ingresses = { "https://vulnerable.example.com" }, + __typename = "VulnerableImageIssue", + severity = "WARNING", + message = "Image 'vulnerable-image' has 2 known-exploited vulnerabilities", + critical = 5, + riskScore = 250, workload = { name = "vulnerable", }, }, }, }, + urgentIssues = { + nodes = {}, + }, }, }, } end) -Test.gql("ExternalIngressCriticalVulnerabilityIssue remains queryable", function(t) +Test.gql("ExternalIngressUrgentVulnerabilityIssue", function(t) t.addHeader("x-user-email", user:email()) t.query [[ @@ -628,15 +622,15 @@ Test.gql("ExternalIngressCriticalVulnerabilityIssue remains queryable", function team(slug: "myteam") { issues( filter: { - issueType: EXTERNAL_INGRESS_CRITICAL_VULNERABILITY + issueType: EXTERNAL_INGRESS_URGENT_VULNERABILITY }, ) { nodes { __typename severity message - ... on ExternalIngressCriticalVulnerabilityIssue { - cvssScore + ... on ExternalIngressUrgentVulnerabilityIssue { + priorityUrgent ingresses workload { name @@ -654,10 +648,10 @@ Test.gql("ExternalIngressCriticalVulnerabilityIssue remains queryable", function issues = { nodes = { { - __typename = "ExternalIngressCriticalVulnerabilityIssue", - message = "Workload 'vulnerable' (exposed via external ingress) has 2 urgent vulnerabilities", + __typename = "ExternalIngressUrgentVulnerabilityIssue", + message = "Urgent: Workload 'vulnerable' is exposed via external ingress and has 2 known-exploited vulnerabilities", severity = "CRITICAL", - cvssScore = 9.0, + priorityUrgent = 2, ingresses = { "https://vulnerable.example.com" }, workload = { name = "vulnerable", diff --git a/integration_tests/k8s_resources/issues/dev-gcp/devteam/app-vulnerable.yaml b/integration_tests/k8s_resources/issues/dev-gcp/devteam/app-vulnerable.yaml new file mode 100644 index 000000000..04bb6b06f --- /dev/null +++ b/integration_tests/k8s_resources/issues/dev-gcp/devteam/app-vulnerable.yaml @@ -0,0 +1,6 @@ +apiVersion: nais.io/v1alpha1 +kind: Application +metadata: + name: vulnerable +spec: + image: europe-north1-docker.pkg.dev/nais/navikt/app-name:latest diff --git a/internal/database/migrations/0078_remove_legacy_critical_vulnerability_issues.sql b/internal/database/migrations/0078_remove_legacy_critical_vulnerability_issues.sql new file mode 100644 index 000000000..7eb66e838 --- /dev/null +++ b/internal/database/migrations/0078_remove_legacy_critical_vulnerability_issues.sql @@ -0,0 +1,5 @@ +-- +goose Up +DELETE FROM issues +WHERE + issue_type = 'EXTERNAL_INGRESS_CRITICAL_VULNERABILITY' +; diff --git a/internal/graph/gengql/issues.generated.go b/internal/graph/gengql/issues.generated.go index 505bc749b..60b11c0d8 100644 --- a/internal/graph/gengql/issues.generated.go +++ b/internal/graph/gengql/issues.generated.go @@ -45,11 +45,6 @@ type DeprecatedRegistryIssueResolver interface { Workload(ctx context.Context, obj *issue.DeprecatedRegistryIssue) (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) @@ -622,185 +617,6 @@ func (ec *executionContext) fieldContext_DeprecatedRegistryIssue_workload(_ cont return fc, nil } -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) _ExternalIngressUrgentVulnerabilityIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3513,13 +3329,6 @@ func (ec *executionContext) _Issue(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } return ec._ExternalIngressUrgentVulnerabilityIssue(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.DeprecatedRegistryIssue: return ec._DeprecatedRegistryIssue(ctx, sel, &obj) case *issue.DeprecatedRegistryIssue: @@ -3937,137 +3746,6 @@ func (ec *executionContext) _DeprecatedRegistryIssue(ctx context.Context, sel as 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 externalIngressUrgentVulnerabilityIssueImplementors = []string{"ExternalIngressUrgentVulnerabilityIssue", "Issue", "Node"} func (ec *executionContext) _ExternalIngressUrgentVulnerabilityIssue(ctx context.Context, sel ast.SelectionSet, obj *issue.ExternalIngressUrgentVulnerabilityIssue) graphql.Marshaler { diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 6c8edc9f6..eaf768dc6 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -82,7 +82,6 @@ type ResolverRoot interface { DeprecatedIngressIssue() DeprecatedIngressIssueResolver DeprecatedRegistryIssue() DeprecatedRegistryIssueResolver Environment() EnvironmentResolver - ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver ExternalIngressUrgentVulnerabilityIssue() ExternalIngressUrgentVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver Ingress() IngressResolver @@ -971,16 +970,6 @@ type ComplexityRoot struct { Node 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 - } - ExternalIngressUrgentVulnerabilityIssue struct { ID func(childComplexity int) int Ingresses func(childComplexity int) int @@ -7304,55 +7293,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.EnvironmentEdge.Node(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 "ExternalIngressUrgentVulnerabilityIssue.id": if e.ComplexityRoot.ExternalIngressUrgentVulnerabilityIssue.ID == nil { break @@ -26171,8 +26111,6 @@ 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.") "Raised when a workload has external ingress exposure and urgent vulnerabilities." EXTERNAL_INGRESS_URGENT_VULNERABILITY UNLEASH_RELEASE_CHANNEL @@ -26191,26 +26129,6 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -"Deprecated: use ExternalIngressUrgentVulnerabilityIssue." -type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { - "The globally unique identifier for this issue." - id: ID! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "The team environment where the affected workload is deployed." - teamEnvironment: TeamEnvironment! - @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "The severity assigned to this issue." - severity: Severity! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "A human-readable description of the issue." - message: String! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - - "The workload with critical vulnerabilities and external ingresses." - workload: Workload! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "The highest CVSS score among vulnerabilities relevant to this issue." - cvssScore: Float! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "External ingress URLs that expose the workload." - ingresses: [String!]! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") -} - "Raised when a workload with external ingresses has one or more urgent vulnerability-priority findings." type ExternalIngressUrgentVulnerabilityIssue implements Issue & Node { "The globally unique identifier for this issue." diff --git a/internal/graph/gengql/schema.generated.go b/internal/graph/gengql/schema.generated.go index 5e9c61d88..04668d41e 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -7165,13 +7165,6 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._ExternalIngressUrgentVulnerabilityIssue(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.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 4befe6098..d22e801ec 100644 --- a/internal/graph/issues.resolvers.go +++ b/internal/graph/issues.resolvers.go @@ -40,14 +40,6 @@ func (r *deprecatedRegistryIssueResolver) Workload(ctx context.Context, obj *iss return getWorkloadByResourceType(ctx, obj.TeamSlug, obj.EnvironmentName, obj.ResourceName, obj.ResourceType) } -func (r *externalIngressCriticalVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (*team.TeamEnvironment, error) { - return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) -} - -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 *externalIngressUrgentVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressUrgentVulnerabilityIssue) (*team.TeamEnvironment, error) { return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) } @@ -181,10 +173,6 @@ func (r *Resolver) DeprecatedRegistryIssue() gengql.DeprecatedRegistryIssueResol return &deprecatedRegistryIssueResolver{r} } -func (r *Resolver) ExternalIngressCriticalVulnerabilityIssue() gengql.ExternalIngressCriticalVulnerabilityIssueResolver { - return &externalIngressCriticalVulnerabilityIssueResolver{r} -} - func (r *Resolver) ExternalIngressUrgentVulnerabilityIssue() gengql.ExternalIngressUrgentVulnerabilityIssueResolver { return &externalIngressUrgentVulnerabilityIssueResolver{r} } @@ -240,22 +228,21 @@ func (r *Resolver) WorkloadProblemIssue() gengql.WorkloadProblemIssueResolver { } type ( - applicationRestartLoopIssueResolver struct{ *Resolver } - deprecatedIngressIssueResolver struct{ *Resolver } - deprecatedRegistryIssueResolver struct{ *Resolver } - externalIngressCriticalVulnerabilityIssueResolver struct{ *Resolver } - externalIngressUrgentVulnerabilityIssueResolver struct{ *Resolver } - failedSynchronizationIssueResolver struct{ *Resolver } - invalidSpecIssueResolver struct{ *Resolver } - issueConnectionResolver 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 } - workloadProblemIssueResolver struct{ *Resolver } + applicationRestartLoopIssueResolver struct{ *Resolver } + deprecatedIngressIssueResolver struct{ *Resolver } + deprecatedRegistryIssueResolver struct{ *Resolver } + externalIngressUrgentVulnerabilityIssueResolver struct{ *Resolver } + failedSynchronizationIssueResolver struct{ *Resolver } + invalidSpecIssueResolver struct{ *Resolver } + issueConnectionResolver 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 } + workloadProblemIssueResolver struct{ *Resolver } ) diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index 8bcf2d93d..809727c6d 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -229,8 +229,6 @@ 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.") "Raised when a workload has external ingress exposure and urgent vulnerabilities." EXTERNAL_INGRESS_URGENT_VULNERABILITY UNLEASH_RELEASE_CHANNEL @@ -249,26 +247,6 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -"Deprecated: use ExternalIngressUrgentVulnerabilityIssue." -type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { - "The globally unique identifier for this issue." - id: ID! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "The team environment where the affected workload is deployed." - teamEnvironment: TeamEnvironment! - @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "The severity assigned to this issue." - severity: Severity! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "A human-readable description of the issue." - message: String! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - - "The workload with critical vulnerabilities and external ingresses." - workload: Workload! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "The highest CVSS score among vulnerabilities relevant to this issue." - cvssScore: Float! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") - "External ingress URLs that expose the workload." - ingresses: [String!]! @deprecated(reason: "Use ExternalIngressUrgentVulnerabilityIssue instead.") -} - "Raised when a workload with external ingresses has one or more urgent vulnerability-priority findings." type ExternalIngressUrgentVulnerabilityIssue implements Issue & Node { "The globally unique identifier for this issue." diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index eb24481e0..1874021a8 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -14,10 +14,7 @@ import ( "k8s.io/utils/ptr" ) -const ( - v13sQueryLimit = 69000 - legacyCriticalCvssScore = 9.0 -) +const v13sQueryLimit = 69000 type V13sClient interface { ListVulnerabilitySummaries(ctx context.Context, opts ...vulnerabilities.Option) (*vulnerabilities.ListVulnerabilitySummariesResponse, error) @@ -133,64 +130,11 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { return nil } - var ret []*Issue - - for _, node := range resp.GetNodes() { - workloadType, ok := mapType(node.Workload.GetType()) - if !ok { - continue - } - - if !w.exists(node, workloadType) { - continue - } - - summary := node.VulnerabilitySummary - env := environmentmapper.EnvironmentName(node.Workload.GetCluster()) - kevCount := int(summary.GetKevCount()) - if summary != nil && kevCount > 0 { - ret = append(ret, &Issue{ - IssueType: issue.IssueTypeVulnerableImage, - ResourceType: workloadType, - ResourceName: node.Workload.GetName(), - Team: node.Workload.GetNamespace(), - Env: env, - Severity: issue.SeverityCritical, - Message: fmt.Sprintf( - "Image '%s' has %d known-exploited vulnerabilities", - node.Workload.ImageName, - kevCount, - ), - IssueDetails: issue.VulnerableImageIssueDetails{ - Critical: int(summary.Critical), - RiskScore: int(summary.RiskScore), - }, - }) - } - - sbomStatus := node.GetSbomStatus().GetStatus() - if sbomStatus != vulnerabilities.SbomStatus_SBOM_STATUS_READY && - sbomStatus != vulnerabilities.SbomStatus_SBOM_STATUS_PROCESSING && - sbomStatus != vulnerabilities.SbomStatus_SBOM_STATUS_UNSPECIFIED { - ret = append(ret, &Issue{ - IssueType: issue.IssueTypeMissingSBOM, - ResourceType: workloadType, - ResourceName: node.Workload.GetName(), - Team: node.Workload.GetNamespace(), - Env: env, - Severity: issue.SeverityWarning, - Message: fmt.Sprintf( - "Image '%s:%s' is missing a Software Bill of Materials (SBOM)", - node.Workload.ImageName, - node.Workload.ImageTag, - ), - }) - } - } - ingress := w.ingressExposureByWorkload() + var ret []*Issue seenUrgent := map[string]struct{}{} + seenKnownExploited := map[string]struct{}{} for _, node := range resp.GetNodes() { workloadRef := node.GetWorkload() if workloadRef == nil { @@ -223,6 +167,9 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { if len(externalIngresses) == 0 { continue } + if !w.exists(node, workloadType) { + continue + } seenUrgent[key] = struct{}{} ret = append(ret, &Issue{ @@ -233,7 +180,7 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload '%s' (exposed via external ingress) has %d urgent vulnerabilities", + "Urgent: Workload '%s' is exposed via external ingress and has %d known-exploited vulnerabilities", workloadRef.GetName(), kevCount, ), @@ -243,23 +190,65 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { }, }) - ret = append(ret, &Issue{ - IssueType: issue.IssueTypeExternalIngressCriticalVulnerability, - ResourceType: workloadType, - ResourceName: workloadRef.GetName(), - Team: workloadRef.GetNamespace(), - Env: env, - Severity: issue.SeverityCritical, - Message: fmt.Sprintf( - "Workload '%s' (exposed via external ingress) has %d urgent vulnerabilities", - workloadRef.GetName(), - kevCount, - ), - IssueDetails: issue.ExternalIngressCriticalVulnerabilityIssueDetails{ - CvssScore: legacyCriticalCvssScore, - Ingresses: externalIngresses, - }, - }) + } + + for _, node := range resp.GetNodes() { + workloadRef := node.GetWorkload() + if workloadRef == nil { + continue + } + workloadType, ok := mapType(workloadRef.GetType()) + if !ok || !w.exists(node, workloadType) { + continue + } + + summary := node.GetVulnerabilitySummary() + env := environmentmapper.EnvironmentName(workloadRef.GetCluster()) + key := workloadKey(env, workloadRef.GetNamespace(), workloadRef.GetName()) + if summary.GetKevCount() > 0 { + _, urgent := seenUrgent[key] + knownExploitedKey := string(workloadType) + "/" + key + _, seen := seenKnownExploited[knownExploitedKey] + if !seen && (workloadType != issue.ResourceTypeApplication || !urgent) { + seenKnownExploited[knownExploitedKey] = struct{}{} + ret = append(ret, &Issue{ + IssueType: issue.IssueTypeVulnerableImage, + ResourceType: workloadType, + ResourceName: workloadRef.GetName(), + Team: workloadRef.GetNamespace(), + Env: env, + Severity: issue.SeverityWarning, + Message: fmt.Sprintf( + "Image '%s' has %d known-exploited vulnerabilities", + workloadRef.GetImageName(), + summary.GetKevCount(), + ), + IssueDetails: issue.VulnerableImageIssueDetails{ + Critical: int(summary.GetCritical()), + RiskScore: int(summary.GetRiskScore()), + }, + }) + } + } + + sbomStatus := node.GetSbomStatus().GetStatus() + if sbomStatus != vulnerabilities.SbomStatus_SBOM_STATUS_READY && + sbomStatus != vulnerabilities.SbomStatus_SBOM_STATUS_PROCESSING && + sbomStatus != vulnerabilities.SbomStatus_SBOM_STATUS_UNSPECIFIED { + ret = append(ret, &Issue{ + IssueType: issue.IssueTypeMissingSBOM, + ResourceType: workloadType, + ResourceName: workloadRef.GetName(), + Team: workloadRef.GetNamespace(), + Env: env, + Severity: issue.SeverityWarning, + Message: fmt.Sprintf( + "Image '%s:%s' is missing a Software Bill of Materials (SBOM)", + workloadRef.GetImageName(), + workloadRef.GetImageTag(), + ), + }) + } } return ret diff --git a/internal/issue/checker/workload_v13s_test.go b/internal/issue/checker/workload_v13s_test.go index 86fbd41fc..d366fe579 100644 --- a/internal/issue/checker/workload_v13s_test.go +++ b/internal/issue/checker/workload_v13s_test.go @@ -106,35 +106,31 @@ func testVulnerabilitiesExternalIngressUrgentIssue(t *testing.T, workloadName, e } issues := workload.vulnerabilities(ctx) - actNowIssues := make([]*Issue, 0) - criticalIssues := make([]*Issue, 0) + urgentIssues := make([]*Issue, 0) for i := range issues { if issues[i].IssueType == issue.IssueTypeExternalIngressUrgentVulnerability { - actNowIssues = append(actNowIssues, issues[i]) - } - if issues[i].IssueType == issue.IssueTypeExternalIngressCriticalVulnerability { - criticalIssues = append(criticalIssues, issues[i]) + urgentIssues = append(urgentIssues, issues[i]) } } if !wantIssue { - if len(actNowIssues) != 0 { - t.Fatalf("expected 0 external ingress act-now issues, got %d", len(actNowIssues)) + if len(urgentIssues) != 0 { + t.Fatalf("expected 0 external ingress urgent issues, got %d", len(urgentIssues)) } - if len(criticalIssues) != 0 { - t.Fatalf("expected 0 external ingress critical issues, got %d", len(criticalIssues)) + if got := len(filterIssues(issues, issue.IssueTypeVulnerableImage)); got != 1 { + t.Fatalf("expected 1 known-exploited image issue for duplicate summaries, got %d", got) } return } - if len(actNowIssues) != 1 { - t.Fatalf("expected 1 external ingress act-now issue, got %d", len(actNowIssues)) + if len(urgentIssues) != 1 { + t.Fatalf("expected 1 external ingress urgent issue, got %d", len(urgentIssues)) } - if len(criticalIssues) != 1 { - t.Fatalf("expected 1 external ingress critical issue, got %d", len(criticalIssues)) + if got := len(filterIssues(issues, issue.IssueTypeVulnerableImage)); got != 0 { + t.Fatalf("expected no known-exploited image issues when urgent exists, got %d", got) } - got := actNowIssues[0] + got := urgentIssues[0] if got.IssueType != issue.IssueTypeExternalIngressUrgentVulnerability { t.Fatalf("expected issue type %s, got %s", issue.IssueTypeExternalIngressUrgentVulnerability, got.IssueType) } @@ -142,10 +138,13 @@ func testVulnerabilitiesExternalIngressUrgentIssue(t *testing.T, workloadName, e if got.ResourceName != workloadName { t.Fatalf("expected resource %s, got %s", workloadName, got.ResourceName) } + if want := "Urgent: Workload '" + workloadName + "' is exposed via external ingress and has 2 known-exploited vulnerabilities"; got.Message != want { + t.Fatalf("expected %q, got %q", want, got.Message) + } details, ok := got.IssueDetails.(issue.ExternalIngressUrgentVulnerabilityIssueDetails) if !ok { - t.Fatalf("expected external ingress act-now details, got %T", got.IssueDetails) + t.Fatalf("expected external ingress urgent details, got %T", got.IssueDetails) } if details.PriorityUrgent != 2 { @@ -155,18 +154,6 @@ func testVulnerabilitiesExternalIngressUrgentIssue(t *testing.T, workloadName, e if len(details.Ingresses) != 1 || details.Ingresses[0] != expectedIngress { t.Fatalf("expected only external ingress URL, got %+v", details.Ingresses) } - - criticalDetails, ok := criticalIssues[0].IssueDetails.(issue.ExternalIngressCriticalVulnerabilityIssueDetails) - if !ok { - t.Fatalf("expected external ingress critical details, got %T", criticalIssues[0].IssueDetails) - } - - if len(criticalDetails.Ingresses) != 1 || criticalDetails.Ingresses[0] != expectedIngress { - t.Fatalf("expected only external ingress URL on critical issue, got %+v", criticalDetails.Ingresses) - } - if criticalDetails.CvssScore != legacyCriticalCvssScore { - t.Fatalf("expected cvssScore %.1f on critical issue, got %v", legacyCriticalCvssScore, criticalDetails.CvssScore) - } } func TestVulnerabilities_UrgentGatesIssueEmission(t *testing.T) { @@ -204,16 +191,13 @@ func TestVulnerabilities_UrgentGatesIssueEmission(t *testing.T) { highRisk int32 wantVulnerableImage bool wantUrgentIngress bool - wantCriticalIngress bool wantExternalIngressURL string }{ { name: "kev findings with confirmed exposure resolve to urgent", workloadName: "ext-app", kevCount: 2, - wantVulnerableImage: true, wantUrgentIngress: true, - wantCriticalIngress: true, wantExternalIngressURL: "https://ext.example.com", }, { @@ -250,6 +234,7 @@ func TestVulnerabilities_UrgentGatesIssueEmission(t *testing.T) { Namespace: "devteam", Type: "app", Name: tt.workloadName, + ImageName: "test-image", }, VulnerabilitySummary: &vulnerabilities.Summary{ Critical: 2, @@ -270,11 +255,24 @@ func TestVulnerabilities_UrgentGatesIssueEmission(t *testing.T) { vulnerableImageIssues := filterIssues(issues, issue.IssueTypeVulnerableImage) urgentIssues := filterIssues(issues, issue.IssueTypeExternalIngressUrgentVulnerability) - criticalIssues := filterIssues(issues, issue.IssueTypeExternalIngressCriticalVulnerability) - assertIssueCount(t, vulnerableImageIssues, tt.wantVulnerableImage, issue.IssueTypeVulnerableImage) assertIssueCount(t, urgentIssues, tt.wantUrgentIngress, issue.IssueTypeExternalIngressUrgentVulnerability) - assertIssueCount(t, criticalIssues, tt.wantCriticalIngress, issue.IssueTypeExternalIngressCriticalVulnerability) + + if tt.wantUrgentIngress && urgentIssues[0].Severity != issue.SeverityCritical { + t.Fatalf("expected urgent issue to have critical severity, got %s", urgentIssues[0].Severity) + } + if tt.wantUrgentIngress { + urgentDetails, ok := urgentIssues[0].IssueDetails.(issue.ExternalIngressUrgentVulnerabilityIssueDetails) + if !ok { + t.Fatalf("expected urgent ingress details, got %T", urgentIssues[0].IssueDetails) + } + if urgentDetails.PriorityUrgent != int(tt.kevCount) { + t.Fatalf("expected priorityUrgent %d, got %d", tt.kevCount, urgentDetails.PriorityUrgent) + } + if len(urgentDetails.Ingresses) != 1 || urgentDetails.Ingresses[0] != tt.wantExternalIngressURL { + t.Fatalf("expected only %q, got %+v", tt.wantExternalIngressURL, urgentDetails.Ingresses) + } + } if !tt.wantVulnerableImage { return @@ -290,31 +288,11 @@ func TestVulnerabilities_UrgentGatesIssueEmission(t *testing.T) { if details.RiskScore != 100 { t.Fatalf("expected risk score 100, got %d", details.RiskScore) } - - if !tt.wantUrgentIngress { - return - } - - urgentDetails, ok := urgentIssues[0].IssueDetails.(issue.ExternalIngressUrgentVulnerabilityIssueDetails) - if !ok { - t.Fatalf("expected urgent ingress details, got %T", urgentIssues[0].IssueDetails) - } - if urgentDetails.PriorityUrgent != int(tt.kevCount) { - t.Fatalf("expected priorityUrgent %d, got %d", tt.kevCount, urgentDetails.PriorityUrgent) - } - if len(urgentDetails.Ingresses) != 1 || urgentDetails.Ingresses[0] != tt.wantExternalIngressURL { - t.Fatalf("expected only %q, got %+v", tt.wantExternalIngressURL, urgentDetails.Ingresses) - } - - criticalDetails, ok := criticalIssues[0].IssueDetails.(issue.ExternalIngressCriticalVulnerabilityIssueDetails) - if !ok { - t.Fatalf("expected critical ingress details, got %T", criticalIssues[0].IssueDetails) - } - if criticalDetails.CvssScore != legacyCriticalCvssScore { - t.Fatalf("expected cvssScore %.1f, got %v", legacyCriticalCvssScore, criticalDetails.CvssScore) + if vulnerableImageIssues[0].Severity != issue.SeverityWarning { + t.Fatalf("expected known-exploited image issue to have warning severity, got %s", vulnerableImageIssues[0].Severity) } - if len(criticalDetails.Ingresses) != 1 || criticalDetails.Ingresses[0] != tt.wantExternalIngressURL { - t.Fatalf("expected only %q on critical issue, got %+v", tt.wantExternalIngressURL, criticalDetails.Ingresses) + if want := "Image 'test-image' has 2 known-exploited vulnerabilities"; vulnerableImageIssues[0].Message != want { + t.Fatalf("expected %q, got %q", want, vulnerableImageIssues[0].Message) } }) } diff --git a/internal/issue/model.go b/internal/issue/model.go index 662e83660..7b1f51423 100644 --- a/internal/issue/model.go +++ b/internal/issue/model.go @@ -189,13 +189,6 @@ type ExternalIngressUrgentVulnerabilityIssueDetails struct { Ingresses []string `json:"ingresses"` } -// Deprecated: superseded by ExternalIngressUrgentVulnerabilityIssueDetails. Kept -// for GraphQL backwards compatibility during the migration window. -type ExternalIngressCriticalVulnerabilityIssueDetails struct { - CvssScore float64 `json:"cvssScore"` - Ingresses []string `json:"ingresses"` -} - type IssueType string const ( @@ -211,15 +204,12 @@ const ( // Deprecated: superseded by IssueTypeWorkloadProblem. IssueTypeFailedSynchronization IssueType = "FAILED_SYNCHRONIZATION" // Deprecated: superseded by IssueTypeWorkloadProblem. - IssueTypeInvalidSpec IssueType = "INVALID_SPEC" - IssueTypeVulnerableImage IssueType = "VULNERABLE_IMAGE" - IssueTypeMissingSBOM IssueType = "MISSING_SBOM" - // Deprecated: superseded by IssueTypeExternalIngressUrgentVulnerability. Kept - // for GraphQL backwards compatibility during the migration window. - IssueTypeExternalIngressCriticalVulnerability IssueType = "EXTERNAL_INGRESS_CRITICAL_VULNERABILITY" - IssueTypeExternalIngressUrgentVulnerability IssueType = "EXTERNAL_INGRESS_URGENT_VULNERABILITY" - IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" - IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" + IssueTypeInvalidSpec IssueType = "INVALID_SPEC" + IssueTypeVulnerableImage IssueType = "VULNERABLE_IMAGE" + IssueTypeMissingSBOM IssueType = "MISSING_SBOM" + IssueTypeExternalIngressUrgentVulnerability IssueType = "EXTERNAL_INGRESS_URGENT_VULNERABILITY" + IssueTypeUnleashReleaseChannel IssueType = "UNLEASH_RELEASE_CHANNEL" + IssueTypeApplicationRestartLoop IssueType = "APPLICATION_RESTART_LOOP" ) var AllIssueType = []IssueType{ @@ -236,7 +226,6 @@ var AllIssueType = []IssueType{ IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, - IssueTypeExternalIngressCriticalVulnerability, IssueTypeExternalIngressUrgentVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop, @@ -248,7 +237,7 @@ func (e IssueType) IsValid() bool { IssueTypeSqlInstanceVersion, IssueTypeDeprecatedIngress, IssueTypeDeprecatedRegistry, IssueTypeNoRunningInstances, IssueTypeLastRunFailed, IssueTypeWorkloadProblem, IssueTypeInvalidSpec, IssueTypeFailedSynchronization, IssueTypeVulnerableImage, - IssueTypeMissingSBOM, IssueTypeExternalIngressCriticalVulnerability, + IssueTypeMissingSBOM, IssueTypeExternalIngressUrgentVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop: return true @@ -516,15 +505,6 @@ func (ExternalIngressUrgentVulnerabilityIssue) IsIssue() {} func (ExternalIngressUrgentVulnerabilityIssue) 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 b9240f9b3..951b1a2df 100644 --- a/internal/issue/queries.go +++ b/internal/issue/queries.go @@ -215,17 +215,6 @@ func convert(issue *issuesql.Issue) (Issue, error) { Base: base, ExternalIngressUrgentVulnerabilityIssueDetails: *d, }, nil - // Deprecated: retained for GraphQL backwards compatibility during the - // migration window. - 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 {