diff --git a/go.mod b/go.mod index 0dd2b21b6..f2a0c9b72 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/nais/api -go 1.26.3 +go 1.26.4 tool ( github.com/99designs/gqlgen @@ -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-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 @@ -76,10 +76,10 @@ require ( go.opentelemetry.io/otel/trace v1.43.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.44.0 - google.golang.org/api v0.280.0 + google.golang.org/api v0.284.0 google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 google.golang.org/grpc v1.81.1 google.golang.org/protobuf v1.36.11 @@ -240,7 +240,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 @@ -465,7 +465,7 @@ require ( gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genai v1.54.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/go.sum b/go.sum index 08b8ab991..0a31e6531 100644 --- a/go.sum +++ b/go.sum @@ -524,8 +524,8 @@ 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= 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= @@ -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-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= @@ -1323,8 +1323,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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/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= @@ -1416,8 +1416,8 @@ 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.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= @@ -1430,8 +1430,8 @@ google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgn google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -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-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= diff --git a/integration_tests/issues_for_team.lua b/integration_tests/issues_for_team.lua index 6fe9ec3a9..81658a02b 100644 --- a/integration_tests/issues_for_team.lua +++ b/integration_tests/issues_for_team.lua @@ -579,8 +579,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 immediate vulnerabilities", + severity = "CRITICAL", critical = 5, riskScore = 250, workload = { diff --git a/internal/graph/gengql/issues.generated.go b/internal/graph/gengql/issues.generated.go index b121b8129..561b6ba21 100644 --- a/internal/graph/gengql/issues.generated.go +++ b/internal/graph/gengql/issues.generated.go @@ -43,10 +43,10 @@ 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) +type ExternalIngressActNowVulnerabilityIssueResolver interface { + TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (*team.TeamEnvironment, error) - Workload(ctx context.Context, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (workload.Workload, error) + Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) } type FailedSynchronizationIssueResolver interface { TeamEnvironment(ctx context.Context, obj *issue.FailedSynchronizationIssue) (*team.TeamEnvironment, error) @@ -607,13 +607,13 @@ 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) { +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_ExternalIngressCriticalVulnerabilityIssue_id(ctx, field) + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_id(ctx, field) }, func(ctx context.Context) (any, error) { return obj.ID, nil @@ -626,20 +626,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_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) _ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +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_ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field) + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().TeamEnvironment(ctx, obj) + return ec.Resolvers.ExternalIngressActNowVulnerabilityIssue().TeamEnvironment(ctx, obj) }, nil, func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { @@ -649,9 +649,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_ExternalIngressActNowVulnerabilityIssue_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ExternalIngressCriticalVulnerabilityIssue", + Object: "ExternalIngressActNowVulnerabilityIssue", Field: field, IsMethod: true, IsResolver: true, @@ -662,13 +662,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) _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_ExternalIngressCriticalVulnerabilityIssue_severity(ctx, field) + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_severity(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Severity, nil @@ -681,17 +681,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_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) _ExternalIngressCriticalVulnerabilityIssue_message(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +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_ExternalIngressCriticalVulnerabilityIssue_message(ctx, field) + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_message(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Message, nil @@ -704,20 +704,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_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) _ExternalIngressCriticalVulnerabilityIssue_workload(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +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_ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field) + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_workload(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.ExternalIngressCriticalVulnerabilityIssue().Workload(ctx, obj) + return ec.Resolvers.ExternalIngressActNowVulnerabilityIssue().Workload(ctx, obj) }, nil, func(ctx context.Context, selections ast.SelectionSet, v workload.Workload) graphql.Marshaler { @@ -727,9 +727,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_ExternalIngressActNowVulnerabilityIssue_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ExternalIngressCriticalVulnerabilityIssue", + Object: "ExternalIngressActNowVulnerabilityIssue", Field: field, IsMethod: true, IsResolver: true, @@ -740,36 +740,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) _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_ExternalIngressCriticalVulnerabilityIssue_cvssScore(ctx, field) + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_priorityActNow(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.CvssScore, nil + return obj.PriorityActNow, 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_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) _ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx context.Context, field graphql.CollectedField, obj *issue.ExternalIngressCriticalVulnerabilityIssue) (ret graphql.Marshaler) { +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_ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field) + return ec.fieldContext_ExternalIngressActNowVulnerabilityIssue_ingresses(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Ingresses, nil @@ -782,8 +782,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_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) _FailedSynchronizationIssue_id(ctx context.Context, field graphql.CollectedField, obj *issue.FailedSynchronizationIssue) (ret graphql.Marshaler) { @@ -2805,13 +2805,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: + case issue.ExternalIngressActNowVulnerabilityIssue: + return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressActNowVulnerabilityIssue: if obj == nil { return graphql.Null } - return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) + return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, obj) case issue.DeprecatedRegistryIssue: return ec._DeprecatedRegistryIssue(ctx, sel, &obj) case *issue.DeprecatedRegistryIssue: @@ -3229,19 +3229,19 @@ func (ec *executionContext) _DeprecatedRegistryIssue(ctx context.Context, sel as return out } -var externalIngressCriticalVulnerabilityIssueImplementors = []string{"ExternalIngressCriticalVulnerabilityIssue", "Issue", "Node"} +var externalIngressActNowVulnerabilityIssueImplementors = []string{"ExternalIngressActNowVulnerabilityIssue", "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) _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("ExternalIngressCriticalVulnerabilityIssue") + out.Values[i] = graphql.MarshalString("ExternalIngressActNowVulnerabilityIssue") case "id": - out.Values[i] = ec._ExternalIngressCriticalVulnerabilityIssue_id(ctx, field, obj) + out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_id(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -3254,7 +3254,7 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx conte ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ExternalIngressCriticalVulnerabilityIssue_teamEnvironment(ctx, field, obj) + res = ec._ExternalIngressActNowVulnerabilityIssue_teamEnvironment(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -3282,12 +3282,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._ExternalIngressActNowVulnerabilityIssue_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._ExternalIngressActNowVulnerabilityIssue_message(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -3300,7 +3300,7 @@ func (ec *executionContext) _ExternalIngressCriticalVulnerabilityIssue(ctx conte ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ExternalIngressCriticalVulnerabilityIssue_workload(ctx, field, obj) + res = ec._ExternalIngressActNowVulnerabilityIssue_workload(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -3327,13 +3327,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 "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._ExternalIngressCriticalVulnerabilityIssue_ingresses(ctx, field, obj) + out.Values[i] = ec._ExternalIngressActNowVulnerabilityIssue_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 3261a9c2a..7e755b204 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -81,7 +81,7 @@ type ResolverRoot interface { DeprecatedIngressIssue() DeprecatedIngressIssueResolver DeprecatedRegistryIssue() DeprecatedRegistryIssueResolver Environment() EnvironmentResolver - ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver + ExternalIngressActNowVulnerabilityIssue() ExternalIngressActNowVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver Ingress() IngressResolver IngressMetrics() IngressMetricsResolver @@ -523,14 +523,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 { @@ -935,11 +940,11 @@ type ComplexityRoot struct { Node func(childComplexity int) int } - ExternalIngressCriticalVulnerabilityIssue struct { - CvssScore 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 @@ -1034,8 +1039,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 @@ -1064,15 +1074,21 @@ 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 + 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 { @@ -5239,6 +5255,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 @@ -5253,6 +5290,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 @@ -6741,54 +6792,54 @@ 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 { + case "ExternalIngressActNowVulnerabilityIssue.id": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.ID == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.CvssScore(childComplexity), true + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.ID(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.id": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID == nil { + case "ExternalIngressActNowVulnerabilityIssue.ingresses": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Ingresses == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.ID(childComplexity), true + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Ingresses(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.ingresses": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses == nil { + case "ExternalIngressActNowVulnerabilityIssue.message": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Message == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Ingresses(childComplexity), true + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Message(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.message": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message == nil { + case "ExternalIngressActNowVulnerabilityIssue.priorityActNow": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.PriorityActNow == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Message(childComplexity), true + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.PriorityActNow(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.severity": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity == nil { + case "ExternalIngressActNowVulnerabilityIssue.severity": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Severity == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Severity(childComplexity), true + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Severity(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.teamEnvironment": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment == nil { + case "ExternalIngressActNowVulnerabilityIssue.teamEnvironment": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.TeamEnvironment == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.TeamEnvironment(childComplexity), true + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.TeamEnvironment(childComplexity), true - case "ExternalIngressCriticalVulnerabilityIssue.workload": - if e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload == nil { + case "ExternalIngressActNowVulnerabilityIssue.workload": + if e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload == nil { break } - return e.ComplexityRoot.ExternalIngressCriticalVulnerabilityIssue.Workload(childComplexity), true + return e.ComplexityRoot.ExternalIngressActNowVulnerabilityIssue.Workload(childComplexity), true case "ExternalNetworkPolicyHost.ports": if e.ComplexityRoot.ExternalNetworkPolicyHost.Ports == nil { @@ -7133,6 +7184,34 @@ 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 + } + + 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 @@ -7147,6 +7226,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 @@ -7252,6 +7338,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 @@ -7273,6 +7366,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 @@ -7287,6 +7408,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ImageVulnerabilitySummary.StaleImageTag(childComplexity), true + case "ImageVulnerabilitySummary.topVulnerabilityPriority": + if e.ComplexityRoot.ImageVulnerabilitySummary.TopVulnerabilityPriority == nil { + break + } + + return e.ComplexityRoot.ImageVulnerabilitySummary.TopVulnerabilityPriority(childComplexity), true + case "ImageVulnerabilitySummary.total": if e.ComplexityRoot.ImageVulnerabilitySummary.Total == nil { break @@ -23226,7 +23354,7 @@ enum IssueType { INVALID_SPEC 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 @@ -23243,14 +23371,15 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { +"Raised when a workload with external ingresses has one or more IMMEDIATE vulnerability-priority findings." +type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! severity: Severity! message: String! workload: Workload! - cvssScore: Float! + priorityActNow: Int! ingresses: [String!]! } @@ -31248,6 +31377,7 @@ enum CVEOrderField { SEVERITY CVSS_SCORE AFFECTED_WORKLOADS_COUNT + PRIORITY } extend interface Workload { @@ -31448,6 +31578,11 @@ input TeamVulnerabilitySummaryFilter { """ environments: [String!] @deprecated(reason: "Use environmentName instead. Only one value is supported.") + + """ + Only return vulnerability summaries at or above the given vulnerability priority. + """ + riskTier: CVEPriority } """ @@ -31483,6 +31618,24 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! + "Number of vulnerabilities with vulnerability priority IMMEDIATE." + 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! + + "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." + highEpssCount: Int! + + "The highest priority among vulnerabilities in this summary." + topVulnerabilityPriority: CVEPriority + "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -31561,6 +31714,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." @@ -31571,6 +31727,29 @@ 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 { + "Vulnerability is known to be actively exploited and requires immediate action." + IMMEDIATE + "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 { @@ -31595,6 +31774,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." @@ -31705,6 +31899,7 @@ enum ImageVulnerabilityOrderField { PACKAGE STATE SUPPRESSED + PRIORITY } type WorkloadVulnerabilitySummary implements Node { @@ -31747,29 +31942,37 @@ 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 vulnerability-priority count. + """ + VULNERABILITY_PRIORITY_ACT_NOW + """ + Order by HIGH vulnerability-priority count. + """ + VULNERABILITY_PRIORITY_HIGH } type TenantVulnerabilitySummary { @@ -33061,6 +33264,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) } @@ -33805,6 +34018,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": @@ -33813,6 +34028,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) } @@ -33873,6 +34096,18 @@ 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 "highEpssCount": + return ec.fieldContext_ImageVulnerabilitySummary_highEpssCount(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 80345a50b..f5df61d16 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -6509,13 +6509,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: + case issue.ExternalIngressActNowVulnerabilityIssue: + return ec._ExternalIngressActNowVulnerabilityIssue(ctx, sel, &obj) + case *issue.ExternalIngressActNowVulnerabilityIssue: if obj == nil { return graphql.Null } - return ec._ExternalIngressCriticalVulnerabilityIssue(ctx, sel, obj) + 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 c2a7a9a1c..b8dc0a17f 100644 --- a/internal/graph/gengql/vulnerability.generated.go +++ b/internal/graph/gengql/vulnerability.generated.go @@ -258,6 +258,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, @@ -820,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, @@ -921,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, @@ -1320,6 +1550,144 @@ 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_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, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.TopVulnerabilityPriority, 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_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) _ImageVulnerabilitySummary_lastUpdated(ctx context.Context, field graphql.CollectedField, obj *vulnerability.ImageVulnerabilitySummary) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3078,7 +3446,7 @@ func (ec *executionContext) unmarshalInputTeamVulnerabilitySummaryFilter(ctx con asMap[k] = v } - fieldsInOrder := [...]string{"environmentName", "environments"} + fieldsInOrder := [...]string{"environmentName", "environments", "riskTier"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -3099,6 +3467,13 @@ func (ec *executionContext) unmarshalInputTeamVulnerabilitySummaryFilter(ctx con 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 @@ -3243,6 +3618,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 @@ -3702,6 +4096,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": @@ -3713,6 +4109,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)) } @@ -3958,6 +4368,33 @@ 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) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "priorityHigh": + out.Values[i] = ec._ImageVulnerabilitySummary_priorityHigh(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "priorityElevated": + out.Values[i] = ec._ImageVulnerabilitySummary_priorityElevated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "priorityMonitor": + out.Values[i] = ec._ImageVulnerabilitySummary_priorityMonitor(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 "topVulnerabilityPriority": + out.Values[i] = ec._ImageVulnerabilitySummary_topVulnerabilityPriority(ctx, field, obj) case "lastUpdated": out.Values[i] = ec._ImageVulnerabilitySummary_lastUpdated(ctx, field, obj) case "staleImageTag": @@ -4879,6 +5316,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) } @@ -5348,6 +5795,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/issues.resolvers.go b/internal/graph/issues.resolvers.go index 0301eec5e..314834394 100644 --- a/internal/graph/issues.resolvers.go +++ b/internal/graph/issues.resolvers.go @@ -40,11 +40,11 @@ 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) { +func (r *externalIngressActNowVulnerabilityIssueResolver) TeamEnvironment(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (*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 *externalIngressActNowVulnerabilityIssueResolver) Workload(ctx context.Context, obj *issue.ExternalIngressActNowVulnerabilityIssue) (workload.Workload, error) { return getWorkloadByResourceType(ctx, obj.TeamSlug, obj.EnvironmentName, obj.ResourceName, obj.ResourceType) } @@ -157,8 +157,8 @@ func (r *Resolver) DeprecatedRegistryIssue() gengql.DeprecatedRegistryIssueResol return &deprecatedRegistryIssueResolver{r} } -func (r *Resolver) ExternalIngressCriticalVulnerabilityIssue() gengql.ExternalIngressCriticalVulnerabilityIssueResolver { - return &externalIngressCriticalVulnerabilityIssueResolver{r} +func (r *Resolver) ExternalIngressActNowVulnerabilityIssue() gengql.ExternalIngressActNowVulnerabilityIssueResolver { + return &externalIngressActNowVulnerabilityIssueResolver{r} } func (r *Resolver) FailedSynchronizationIssue() gengql.FailedSynchronizationIssueResolver { @@ -204,19 +204,19 @@ func (r *Resolver) VulnerableImageIssue() gengql.VulnerableImageIssueResolver { } type ( - applicationRestartLoopIssueResolver struct{ *Resolver } - deprecatedIngressIssueResolver struct{ *Resolver } - deprecatedRegistryIssueResolver struct{ *Resolver } - externalIngressCriticalVulnerabilityIssueResolver 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 } + 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 } ) diff --git a/internal/graph/schema/issues.graphqls b/internal/graph/schema/issues.graphqls index 391159923..54cb8fd89 100644 --- a/internal/graph/schema/issues.graphqls +++ b/internal/graph/schema/issues.graphqls @@ -163,7 +163,7 @@ enum IssueType { INVALID_SPEC 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 @@ -180,14 +180,15 @@ type VulnerableImageIssue implements Issue & Node { critical: Int! } -type ExternalIngressCriticalVulnerabilityIssue implements Issue & Node { +"Raised when a workload with external ingresses has one or more IMMEDIATE vulnerability-priority findings." +type ExternalIngressActNowVulnerabilityIssue implements Issue & Node { id: ID! teamEnvironment: TeamEnvironment! severity: Severity! message: String! workload: Workload! - cvssScore: Float! + priorityActNow: Int! ingresses: [String!]! } diff --git a/internal/graph/schema/vulnerability.graphqls b/internal/graph/schema/vulnerability.graphqls index 37645d575..f4735e633 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 { @@ -255,6 +256,11 @@ input TeamVulnerabilitySummaryFilter { """ environments: [String!] @deprecated(reason: "Use environmentName instead. Only one value is supported.") + + """ + Only return vulnerability summaries at or above the given vulnerability priority. + """ + riskTier: CVEPriority } """ @@ -290,6 +296,24 @@ type ImageVulnerabilitySummary { "Number of vulnerabilities with severity UNASSIGNED." unassigned: Int! + "Number of vulnerabilities with vulnerability priority IMMEDIATE." + 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! + + "Number of vulnerabilities with a high EPSS percentile (≥ 0.90)." + highEpssCount: Int! + + "The highest priority among vulnerabilities in this summary." + topVulnerabilityPriority: CVEPriority + "Timestamp of the last update of the vulnerability summary." lastUpdated: Time @@ -368,6 +392,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." @@ -378,6 +405,29 @@ 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 { + "Vulnerability is known to be actively exploited and requires immediate action." + IMMEDIATE + "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 { @@ -402,6 +452,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." @@ -512,6 +577,7 @@ enum ImageVulnerabilityOrderField { PACKAGE STATE SUPPRESSED + PRIORITY } type WorkloadVulnerabilitySummary implements Node { @@ -554,29 +620,37 @@ 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 vulnerability-priority count. + """ + VULNERABILITY_PRIORITY_ACT_NOW + """ + Order by HIGH vulnerability-priority count. + """ + VULNERABILITY_PRIORITY_HIGH } type TenantVulnerabilitySummary { diff --git a/internal/issue/checker/testdata/dev-gcp/devteam/apps.yaml b/internal/issue/checker/testdata/dev-gcp/devteam/apps.yaml index bf2eb704f..9b6818fbf 100644 --- a/internal/issue/checker/testdata/dev-gcp/devteam/apps.yaml +++ b/internal/issue/checker/testdata/dev-gcp/devteam/apps.yaml @@ -1,51 +1,118 @@ apiVersion: nais.io/v1alpha1 kind: Application metadata: - name: ext-app + name: ext-app-legacy spec: - image: ghcr.io/nais/ext-app:latest + image: ghcr.io/nais/ext-app-legacy:latest ingresses: - - https://ext.example.com - - https://internal.example.com + - https://legacy.external.example.com --- apiVersion: nais.io/v1alpha1 kind: Application metadata: - name: internal-only + name: ext-app-haproxy spec: - image: ghcr.io/nais/internal-only:latest + image: ghcr.io/nais/ext-app-haproxy:latest ingresses: - - https://internal-only.example.com + - https://haproxy.external.example.com +--- +apiVersion: nais.io/v1alpha1 +kind: Application +metadata: + name: ext-app-fa-haproxy +spec: + image: ghcr.io/nais/ext-app-fa-haproxy:latest + ingresses: + - https://haproxy.fa.external.example.com +--- +apiVersion: nais.io/v1alpha1 +kind: Application +metadata: + name: internal-only-haproxy +spec: + image: ghcr.io/nais/internal-only-haproxy:latest + ingresses: + - https://internal-only.haproxy.example.com +--- +apiVersion: nais.io/v1alpha1 +kind: Application +metadata: + name: unknown-class-ingress +spec: + image: ghcr.io/nais/unknown-class-ingress:latest + ingresses: + - https://unknown-class.example.com +--- +apiVersion: nais.io/v1alpha1 +kind: Application +metadata: + name: no-class-ingress +spec: + image: ghcr.io/nais/no-class-ingress:latest + ingresses: + - https://no-class.example.com --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: ext-app-external + name: ext-app-legacy-external labels: - app: ext-app + app: ext-app-legacy spec: ingressClassName: nais-ingress-external rules: - - host: ext.example.com + - host: legacy.external.example.com +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ext-app-haproxy-external + labels: + app: ext-app-haproxy +spec: + ingressClassName: external-haproxy + rules: + - host: haproxy.external.example.com +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ext-app-fa-haproxy-external + labels: + app: ext-app-fa-haproxy +spec: + ingressClassName: external-fa-haproxy + rules: + - host: haproxy.fa.external.example.com +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: internal-only-haproxy-internal + labels: + app: internal-only-haproxy +spec: + ingressClassName: internal-haproxy + rules: + - host: internal-only.haproxy.example.com --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: ext-app-internal + name: unknown-class-ingress labels: - app: ext-app + app: unknown-class-ingress spec: - ingressClassName: nais-ingress + ingressClassName: unknown-ingress-class rules: - - host: internal.example.com + - host: unknown-class.example.com --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: internal-only-internal + name: no-class-ingress labels: - app: internal-only + app: no-class-ingress spec: - ingressClassName: nais-ingress rules: - - host: internal-only.example.com + - host: no-class.example.com diff --git a/internal/issue/checker/workload_v13s.go b/internal/issue/checker/workload_v13s.go index 1fc65db5d..56e97b7f0 100644 --- a/internal/issue/checker/workload_v13s.go +++ b/internal/issue/checker/workload_v13s.go @@ -8,18 +8,16 @@ import ( "github.com/nais/api/internal/environmentmapper" "github.com/nais/api/internal/issue" + "github.com/nais/api/internal/workload/application" "github.com/nais/v13s/pkg/api/vulnerabilities" - "k8s.io/utils/ptr" ) const ( - externalIngressClassName = "nais-ingress-external" - v13sQueryLimit = 69000 + v13sQueryLimit = 69000 ) 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{} @@ -40,6 +38,8 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... VulnerabilitySummary: &vulnerabilities.Summary{ Critical: 5, RiskScore: 250, + ActNow: 2, + HighRisk: 3, }, SbomStatus: &vulnerabilities.SbomStatusInfo{ Status: vulnerabilities.SbomStatus_SBOM_STATUS_READY, @@ -72,6 +72,8 @@ func (f fakeV13sClient) ListVulnerabilitySummaries(ctx context.Context, opts ... VulnerabilitySummary: &vulnerabilities.Summary{ Critical: 5, RiskScore: 250, + ActNow: 2, + HighRisk: 3, }, SbomStatus: &vulnerabilities.SbomStatusInfo{ Status: vulnerabilities.SbomStatus_SBOM_STATUS_READY, @@ -109,47 +111,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" { @@ -181,23 +142,23 @@ 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.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: issue.SeverityWarning, + Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Image '%s' has %d critical vulnerabilities and a risk score of %d", + "Image '%s' has %d immediate vulnerabilities", node.Workload.ImageName, - node.VulnerabilitySummary.Critical, - node.VulnerabilitySummary.RiskScore, + summary.ActNow, ), IssueDetails: issue.VulnerableImageIssueDetails{ - Critical: int(node.VulnerabilitySummary.Critical), - RiskScore: int(node.VulnerabilitySummary.RiskScore), + Critical: int(summary.Critical), + RiskScore: int(summary.RiskScore), }, }) } @@ -222,36 +183,22 @@ 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 { + seenActNow := map[string]struct{}{} + for _, node := range resp.GetNodes() { + workloadType, ok := mapType(node.Workload.GetType()) + if !ok || workloadType != issue.ResourceTypeApplication { continue } - workloadType, ok := mapType(workloadRef.GetType()) - if !ok || workloadType != issue.ResourceTypeApplication { + if node.VulnerabilitySummary == nil || node.VulnerabilitySummary.ActNow == 0 { continue } - env := environmentmapper.EnvironmentName(workloadRef.GetCluster()) - key := workloadKey(env, workloadRef.GetNamespace(), workloadRef.GetName()) - if _, exists := seen[key]; exists { + env := environmentmapper.EnvironmentName(node.Workload.GetCluster()) + key := workloadKey(env, node.Workload.GetNamespace(), node.Workload.GetName()) + if _, exists := seenActNow[key]; exists { continue } @@ -259,23 +206,23 @@ func (w Workload) vulnerabilities(ctx context.Context) []*Issue { if len(externalIngresses) == 0 { continue } - seen[key] = struct{}{} + seenActNow[key] = struct{}{} ret = append(ret, &Issue{ - IssueType: issue.IssueTypeExternalIngressCriticalVulnerability, + IssueType: issue.IssueTypeExternalIngressActNowVulnerability, ResourceType: workloadType, - ResourceName: workloadRef.GetName(), - Team: workloadRef.GetNamespace(), + ResourceName: node.Workload.GetName(), + Team: node.Workload.GetNamespace(), Env: env, Severity: issue.SeverityCritical, Message: fmt.Sprintf( - "Workload with external ingresses %s has a vulnerability with CVSS score %.1f", + "Workload with external ingresses %s has %d immediate vulnerabilities", strings.Join(externalIngresses, ", "), - cvss, + node.VulnerabilitySummary.ActNow, ), - IssueDetails: issue.ExternalIngressCriticalVulnerabilityIssueDetails{ - CvssScore: cvss, - Ingresses: externalIngresses, + IssueDetails: issue.ExternalIngressActNowVulnerabilityIssueDetails{ + PriorityActNow: int(node.VulnerabilitySummary.ActNow), + Ingresses: externalIngresses, }, }) } @@ -331,7 +278,12 @@ func (w Workload) externalIngressHostsByWorkload() map[string]map[string]struct{ ret := map[string]map[string]struct{}{} for _, ing := range w.IngressWatcher.All() { - if ptr.Deref(ing.Obj.Spec.IngressClassName, "") != externalIngressClassName { + ingressClassName := "" + if ing.Obj.Spec.IngressClassName != nil { + ingressClassName = *ing.Obj.Spec.IngressClassName + } + + if !application.IsIngressClassExternallyExposed(ingressClassName) { continue } 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 315ff3d69..0dd8ada84 100644 --- a/internal/issue/model.go +++ b/internal/issue/model.go @@ -183,29 +183,29 @@ 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"` } type IssueType string const ( - 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" - IssueTypeExternalIngressCriticalVulnerability IssueType = "EXTERNAL_INGRESS_CRITICAL_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" ) var AllIssueType = []IssueType{ @@ -221,14 +221,14 @@ var AllIssueType = []IssueType{ IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, - IssueTypeExternalIngressCriticalVulnerability, + IssueTypeExternalIngressActNowVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop, } func (e IssueType) IsValid() bool { switch e { - case IssueTypeOpenSearch, IssueTypeValkey, IssueTypeSqlInstanceState, IssueTypeSqlInstanceVersion, IssueTypeDeprecatedIngress, IssueTypeDeprecatedRegistry, IssueTypeNoRunningInstances, IssueTypeLastRunFailed, IssueTypeInvalidSpec, IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, IssueTypeExternalIngressCriticalVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop: + case IssueTypeOpenSearch, IssueTypeValkey, IssueTypeSqlInstanceState, IssueTypeSqlInstanceVersion, IssueTypeDeprecatedIngress, IssueTypeDeprecatedRegistry, IssueTypeNoRunningInstances, IssueTypeLastRunFailed, IssueTypeInvalidSpec, IssueTypeFailedSynchronization, IssueTypeVulnerableImage, IssueTypeMissingSBOM, IssueTypeExternalIngressActNowVulnerability, IssueTypeUnleashReleaseChannel, IssueTypeApplicationRestartLoop: return true } return false @@ -384,14 +384,14 @@ func (VulnerableImageIssue) IsIssue() {} func (VulnerableImageIssue) IsNode() {} -type ExternalIngressCriticalVulnerabilityIssue struct { +type ExternalIngressActNowVulnerabilityIssue struct { Base - ExternalIngressCriticalVulnerabilityIssueDetails + ExternalIngressActNowVulnerabilityIssueDetails } -func (ExternalIngressCriticalVulnerabilityIssue) IsIssue() {} +func (ExternalIngressActNowVulnerabilityIssue) IsIssue() {} -func (ExternalIngressCriticalVulnerabilityIssue) IsNode() {} +func (ExternalIngressActNowVulnerabilityIssue) IsNode() {} type UnleashReleaseChannelIssueDetails struct { ChannelName string `json:"channelName"` diff --git a/internal/issue/queries.go b/internal/issue/queries.go index 4af566436..f962ba05b 100644 --- a/internal/issue/queries.go +++ b/internal/issue/queries.go @@ -173,14 +173,14 @@ func convert(issue *issuesql.Issue) (Issue, error) { return &MissingSbomIssue{ Base: base, }, nil - case IssueTypeExternalIngressCriticalVulnerability: - d, err := unmarshal[ExternalIngressCriticalVulnerabilityIssueDetails](issue.IssueDetails) + case IssueTypeExternalIngressActNowVulnerability: + d, err := unmarshal[ExternalIngressActNowVulnerabilityIssueDetails](issue.IssueDetails) if err != nil { return nil, err } - return &ExternalIngressCriticalVulnerabilityIssue{ + return &ExternalIngressActNowVulnerabilityIssue{ Base: base, - ExternalIngressCriticalVulnerabilityIssueDetails: *d, + ExternalIngressActNowVulnerabilityIssueDetails: *d, }, nil case IssueTypeUnleashReleaseChannel: d, err := unmarshal[UnleashReleaseChannelIssueDetails](issue.IssueDetails) diff --git a/internal/vulnerability/fake/fakedata.go b/internal/vulnerability/fake/fakedata.go index eb9f3ff0a..59424def1 100644 --- a/internal/vulnerability/fake/fakedata.go +++ b/internal/vulnerability/fake/fakedata.go @@ -79,15 +79,21 @@ func createWorkloadSummary(env, team, workloadType, name, image string, vulnFact imageName := parts[0] imageTag := parts[1] 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()), + ActNow: vulnFactor, + HighRisk: vulnFactor * 2, + ElevatedRisk: vulnFactor * 3, + Monitor: vulnFactor * 4, + HighEpssCount: vulnFactor * 2, + TopPriority: vulnerabilities.Priority_PRIORITY_ACT_NOW, } if name == "no-errors" { @@ -120,38 +126,43 @@ func createWorkloadSummary(env, team, workloadType, name, image string, vulnFact func createVulnerabilities(w *vulnerabilities.WorkloadSummary) []*vulnerabilities.Vulnerability { findings := make([]*vulnerabilities.Vulnerability, 0) + epssScore := 0.85 + epssPercentile := 0.973 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), &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), 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), 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), 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), nil, nil, false, false)) } return findings } -func createVulnerability(severity vulnerabilities.Severity, componentName string) *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), 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, + 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/fake/v13s.go b/internal/vulnerability/fake/v13s.go index f623006bc..3e5480e44 100644 --- a/internal/vulnerability/fake/v13s.go +++ b/internal/vulnerability/fake/v13s.go @@ -189,13 +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, - 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 a05217e76..76321ab07 100644 --- a/internal/vulnerability/models.go +++ b/internal/vulnerability/models.go @@ -46,8 +46,13 @@ 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"` SeveritySince *time.Time `json:"severitySince"` Suppression *ImageVulnerabilitySuppression `json:"suppression"` VulnerabilityDetailsLink string `json:"vulnerabilityDetailsLink"` @@ -71,15 +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"` + 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 @@ -222,6 +233,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{ @@ -234,11 +247,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 @@ -374,8 +389,9 @@ func (e ImageVulnerabilitySuppressionState) MarshalGQL(w io.Writer) { } type TeamVulnerabilitySummaryFilter struct { - EnvironmentName *string `json:"environmentName,omitempty"` - Environments []string `json:"environments,omitempty"` + EnvironmentName *string `json:"environmentName,omitempty"` + Environments []string `json:"environments,omitempty"` + RiskTier *CVEPriority `json:"riskTier,omitempty"` } type ImageVulnerabilitySample struct { @@ -413,14 +429,57 @@ type VulnerabilityFixSample struct { TotalWorkloads int `json:"totalWorkloads"` } +type CVEPriority string + +const ( + CVEPriorityImmediate CVEPriority = "IMMEDIATE" + CVEPriorityHigh CVEPriority = "HIGH" + CVEPriorityElevated CVEPriority = "ELEVATED" + CVEPriorityMonitor CVEPriority = "MONITOR" +) + +func (e CVEPriority) IsValid() bool { + switch e { + case CVEPriorityImmediate, 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. @@ -477,6 +536,7 @@ const ( CVEOrderFieldSeverity CVEOrderField = "SEVERITY" CVEOrderFieldCVSSScore CVEOrderField = "CVSS_SCORE" CVEOrderFieldAffectedWorkloadsCount CVEOrderField = "AFFECTED_WORKLOADS_COUNT" + CVEOrderFieldPriority CVEOrderField = "PRIORITY" ) var AllCVEOrderField = []CVEOrderField{ @@ -484,11 +544,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 86992af64..c80a65a60 100644 --- a/internal/vulnerability/queries.go +++ b/internal/vulnerability/queries.go @@ -74,6 +74,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.PriorityFilter(mapCVEPriorityToPriority(*filter.RiskTier))) + } + if orderBy != nil { direction := vulnerabilities.Direction_ASC if orderBy.Direction == model.OrderDirectionDesc { @@ -369,15 +373,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, + 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 } @@ -490,13 +500,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()), + 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(), }) @@ -655,6 +671,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 } @@ -699,7 +717,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) } @@ -721,7 +739,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) { diff --git a/internal/vulnerability/sortfilter.go b/internal/vulnerability/sortfilter.go index cad3014d3..a682a60c7 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.OrderByActNow, + "VULNERABILITY_PRIORITY_HIGH": vulnerabilities.OrderByHighRisk, } 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 { diff --git a/internal/vulnerability/transform.go b/internal/vulnerability/transform.go index 000f50ca4..fb103a453 100644 --- a/internal/vulnerability/transform.go +++ b/internal/vulnerability/transform.go @@ -26,8 +26,13 @@ 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, SeveritySince: severitySince, VulnerabilityDetailsLink: v.Cve.Link, } @@ -61,26 +66,37 @@ 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.ActNow), + PriorityHigh: int(v13sSummary.HighRisk), + PriorityElevated: int(v13sSummary.ElevatedRisk), + PriorityMonitor: int(v13sSummary.Monitor), + HighEpssCount: int(v13sSummary.GetHighEpssCount()), + TopVulnerabilityPriority: mapPriority(v13sSummary.GetTopPriority()), } 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{ @@ -92,12 +108,36 @@ 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: cvePriorityFromV13s(cve), + EpssScore: cve.EpssScore, + EpssPercentile: cve.EpssPercentile, + HasKevEntry: cve.HasKevEntry, + KnownRansomwareUse: cve.KnownRansomwareUse, + } +} + +func cvePriorityFromV13s(cve *vulnerabilities.Cve) CVEPriority { + if cve == nil { + return CVEPriorityMonitor + } + + 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 } } @@ -117,3 +157,33 @@ func mapVulnerabilitySeverity(severity ImageVulnerabilitySeverity) vulnerabiliti return vulnerabilities.Severity_UNASSIGNED } } + +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 mapCVEPriorityToPriority(p CVEPriority) vulnerabilities.Priority { + switch p { + case CVEPriorityImmediate: + return vulnerabilities.Priority_PRIORITY_ACT_NOW + case CVEPriorityHigh: + return vulnerabilities.Priority_PRIORITY_HIGH + case CVEPriorityElevated: + return vulnerabilities.Priority_PRIORITY_ELEVATED + case CVEPriorityMonitor: + return vulnerabilities.Priority_PRIORITY_MONITOR + default: + return vulnerabilities.Priority_PRIORITY_UNSPECIFIED + } +} diff --git a/internal/workload/application/ingress_class_mapping.go b/internal/workload/application/ingress_class_mapping.go index 880980038..eebeccc69 100644 --- a/internal/workload/application/ingress_class_mapping.go +++ b/internal/workload/application/ingress_class_mapping.go @@ -8,3 +8,17 @@ var ingressClassMapping = map[string]IngressType{ "external-haproxy": IngressTypeExternal, "external-fa-haproxy": IngressTypeAuthenticated, } + +// IsIngressClassExternallyExposed reports whether an ingress class represents +// external exposure of an application, including authenticated external +// ingress classes. +// +// Unknown or empty class names are treated as not externally exposed. +func IsIngressClassExternallyExposed(className string) bool { + ingressType, ok := ingressClassMapping[className] + if !ok { + return false + } + + return ingressType == IngressTypeExternal || ingressType == IngressTypeAuthenticated +}