Skip to content

Create a new GraphQL type & resolver to return vulnerability grades

What does this MR do?

Before FCV(Fist class vulnerabilities) we were using a RESTish endpoint to fetch all the vulnerable projects and their respective vulnerability stats and frontend application was calculating the letter grades based on the payload given by the backend.

There was no pagination in the old endpoint but since we have the builtin pagination on GraphQL we can not return all the vulnerable projects with their statistics at once so we decided to introduce a new type and move the logic to backend.

With this new type, clients will be able to retrieve the vulnerability letter grade information for group & instance-level security dashboards.

API Changes

Example query;

fragment letterGradeFields on VulnerabilityLetterGrade {
  count
  projects {
    nodes {
      name
    }
  }
}

fragment gradeFields on VulnerabilityGrades {
  a { ...letterGradeFields }
  b { ...letterGradeFields }
  c { ...letterGradeFields }
  d { ...letterGradeFields }
  f { ...letterGradeFields }
}

query getGroup($fullPath: ID!) {
  group(fullPath: $fullPath) {
    name
    
    vulnerabilityGrades {
      ...gradeFields
    }
  }
}

Model Changes

This MR introduces 2 new models;

  1. Vulnerabilities::Stats is a read model based on vulnerabilities table which supports access to vulnerability statistics of a specific project. Currently we are calculating the data on the fly which can be later calculated by a trigger or application layer logic to be persisted into its own database table to improve the performance.
  2. Vulnerabilities::LetterGrade is a read model as well based on the data we have in Vulnerabilities::Stats model.

SQL Queries

Vulnerability stats of a project
SELECT 
  "vulnerabilities"."project_id", 
  COUNT(*) AS count_all, 
  COUNT(*) FILTER (WHERE severity = 1 AND state IN (1,4)) as info, 
  COUNT(*) FILTER (WHERE severity = 2 AND state IN (1,4)) as unknown, 
  COUNT(*) FILTER (WHERE severity = 4 AND state IN (1,4)) as low, 
  COUNT(*) FILTER (WHERE severity = 5 AND state IN (1,4)) as medium, 
  COUNT(*) FILTER (WHERE severity = 6 AND state IN (1,4)) as high, 
  COUNT(*) FILTER (WHERE severity = 7 AND state IN (1,4)) as critical 
FROM "vulnerabilities" 
WHERE "vulnerabilities"."project_id" = $project_id
GROUP BY "vulnerabilities"."project_id" 
LIMIT 1
Letter grades of a group
SELECT 
  count(*) filter (where critical = 0 and high = 0 and unknown = 0 and medium = 0 and low = 0) as a, 
  count(*) filter (where critical = 0 and high = 0 and unknown = 0 and medium = 0 and low > 0) as b, 
  count(*) filter (where critical = 0 and high = 0 and unknown = 0 and medium > 0) as c, 
  count(*) filter (where critical = 0 and (high > 0 or unknown > 0)) as d, 
  count(*) filter (where critical > 0) as f 
FROM ($similar_to_previous_query) as stats

/related to #213623 (closed)

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • [-] Label as security and @ mention @gitlab-com/gl-security/appsec
  • [-] The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • [-] Security reports checked/validated by a reviewer from the AppSec team
Edited by Mehmet Emin INAC

Merge request reports