Skip to content

GraphQL filter for vulnerabilities by container images with vulnerabilities

Why are we doing this work

To support #337883 (closed), we need a graphql query to get all images scanned.

Relevant links

Non-functional requirements

  • Documentation:
  • Feature flag:
  • Performance:
  • Testing:

Implementation plan

Graphql Schema

Project
project(fullPath: 'gitlab-org/gitlab') {
  clusterAgents(hasVulnerabilities: true) { ... }
  vulnerabilityContainerImages { ... }
}
Group
group(fullPath: 'gitlab-org') {
  clusterAgents(hasVulnerabilities: true) { ... }
  vulnerabilityContainerImages { ... }
}
Instance Security Dashboard
instanceSecurityDashboard\ {
  clusterAgents(hasVulnerabilities: true) { ... }
  vulnerabilityContainerImages { ... }
}

Model Changes

  • backend Add distinct_container_images scope to Vulnerabilities::Read model to select the distinct images:
scope :distinct_container_images, -> { where('location_image IS NOT NULL').distinct.select(:location_image).pluck(:location_image) }
  • backend Add has_vulnerabilities scope to Clusters::Agent model which checks if cluster_agent_id exists in vulnerability_reads table
scope :has_vulnerabilities, -> do
  joins('inner join vulnerability_reads on cluster_agents.id = CAST(vulnerability_reads.cluster_agent_id AS INTEGER)')
    .distinct
end
  • backend Add 'for_projectsscope toClusters::Agents::ProjectAuthorization`
scope :for_projects, -> (project_ids) { where(project_id: project_ids) }
  • backend Add cluster_agents to ee/app/models/ee/group.rb
def cluster_agents
  ::Clusters::Agents::GroupAuthorization.where(group: self)
end
  • backend Add cluster_agents to ee/app/models/instance_security_dashboard.rb
def cluster_agents
  return Clusters::Agent.none if projects.empty?

  Clusters::Agents::ProjectAuthorization.for_projects(projects)
end

Resolver and graphql type changes

  • backend Add has_vulnerabilities argument to Resolvers::Clusters::AgentsResolver
argument :has_vulnerabilities, GraphQL::Types::Boolean,
         required: false,
         default_value: false,
         description: 'Returns only the cluster agents which have atleast one vulnerability.'
  • backend Update Clusters::AgentsFinder to check if has_vulnerabilities argument is present and rename project to object so that it can take group or instance_security_dashboard :
 module Clusters
   class AgentsFinder
-    def initialize(project, current_user, params: {})
-      @project = project
+    def initialize(object, current_user, params: {})
+      @object = object
       @current_user = current_user
       @params = params
     end
 
     def execute
-      return ::Clusters::Agent.none unless can_read_cluster_agents?
+      return ::Clusters::Agent.none if object.is_a?(Project) && !can_read_cluster_agents?
 
-      agents = project.cluster_agents
+      agents = object.cluster_agents
       agents = agents.with_name(params[:name]) if params[:name].present?
+      agents = agents.has_vulnerabilities if params[:has_vulnerabilities].present?
 
       agents.ordered_by_name
     end
 
     private
 
-    attr_reader :project, :current_user, :params
+    attr_reader :object, :current_user, :params
 
     def can_read_cluster_agents?
-      current_user.can?(:read_cluster, project)
+      current_user.can?(:read_cluster, object)
     end
   end
 end
  • backend Update ee/app/graphql/ee/types/group_type.rb, ee/app/graphql/ee/types/project_type.rb, ee/app/graphql/types/instance_security_dashboard_type.rb to include :
field :cluster_agents,
      ::Types::Clusters::AgentType.connection_type,
      extras: [:lookahead],
      null: true,
      description: '...',
      resolver: ::Resolvers::Clusters::AgentsResolver

field :vulnerability_container_images,
      type: [GraphQL::Types::String],
      null: true,
      description: '...'

def vulnerability_container_images
  object.vulnerability_reads.distinct_container_images
end
Edited by Alan (Maciej) Paruszewski