BE - GraphQL Support for Total Risk Score

Problem to Solve

In this issue we focus on the backend work for the Security Dashboard, Total Risk Score &17425

In Scope

Panel Level

  • Grouping by Project

Page Level

  • Filtering by Project
  • Filtering by Report Type

Out of Scope

Page Level

  • Severity filter
  • Report Type filter
  • Scanner filter
  • Time/date range (is “time” meant as date range?)
  • Grouping by anything other than project

Panel Level

  • Grouping by
  • Severity filtering
  • Scanner filter
  • Date range

Other

  • Business context
  • Environment
  • Secret status/validity
  • Business Impact
  • Exposure

Dependencies

Implementation Plan

Phase 1
  • Add feature flag (e.g. new_security_dashboard_total_risk_score )
  • Add GraphQL Schema Extensions
    • Create RiskScoreType with fields:

      • score
      • rating
      • factors
      • by_project
    • Create RiskFactorsType (make all fields nullable for now)

      • vulnerabilities_average_score

      Once &17425 (comment 2683380788) has been confirmed, we can add:

      • update_frequency
      • environment
      • code_visibility
    • Create Security::RiskScoreResolver

    • Create RiskRating enum

      • low (0–25)
      • medium (26–50)
      • high (51–75)
      • critical (76–100)
      • unknown
    • Add risk_score field null: true)to of SecurityMetricsType

    • Add testing

    • Implement Dummy Data - to unblock and test while developing (Phase 1) - See GraphQL Example Response for

Phase 2
  • Integrate real backend when Epic #18188 completes and remove dummy data implementation (Phase 2)

Outstanding Questions

Question Answer Assignee Priority Blocking?

Will factor fields environement, update_frequency and code_visbility be available in this iteration?

&17425 (comment 2683380788)

groupsecurity infrastructure

Is reachability available now or later?

&17425 (comment 2683380788)

groupsecurity infrastructure

Resources

GraphQL Example Query 1

Current Scope (until &17425 (comment 2683380788) has been confirmed)

query getGroupTotalRiskScore(
  $fullPath: ID!
  $projectId: [ProjectType!]
  $reportType: [VulnerabilityReportType!]
  $includeByProject: Boolean!
) {
  group(fullPath: $fullPath) {
    id
    securityMetrics(projectId: $projectId) {
      riskScore {
        score
        rating
        factors {
          vulnerabilitiesAverageScore {
            factor
          }
        }
        ... @include(if: $includeByProject) {
          byProject {
            count
            nodes {
              project {   # `Project` type
                id 
                name 
              }
              score
              rating
            }
          }
        }
      }
    }
  }
}
GraphQL Example Response
{
  "data": {
    "group": {
      "id": "gid://gitlab/Group/123",
      "securityMetrics": {
        "riskScore": {
          "score": 5.2,
          "rating": "MEDIUM",
          "factors": {
            "vulnerabilitiesAverageScore": {
              "factor": 1.0
            }
          },
          "byProject": {
            "count": 2, 
            "nodes": [
              {
                "project": {
                  "id": "gid://gitlab/Project/123",
                  "name": "test-project"
                },
                "score": 5.2,
                "rating": "MEDIUM"
              }
            ]
          }
        }
      }
    }
  }
}

GraphQL Example Query 2

Once &17425 (comment 2683380788) has been confirmed

query getGroupTotalRiskScore( # not sure about this naming
  $fullPath: ID!
  $projectId: [ProjectType!]
  $reportType: [VulnerabilityReportType!]
  $includeByProject: Boolean!
) {
  group(fullPath: $fullPath) {
    id
    securityMetrics(projectId: $projectId, reportType: $reportType) {
      riskScore {
        score
        rating 
        factors {
          codeVisibility { 
            visibility 
            factor
          }
          environment {
            name
            factor
          }
          updateFrequency {
            frequency 
            factor
          }
          vulnerabilitiesAverageScore {
            factor
          }
        }
        ... @include(if: $includeByProject) {
          byProject {
            count 
            nodes {
             project {   # `Project` type
                id 
                name 
              }
              score
              rating
            }
          }
        }
      }
    }
  }
}
{
  "data": {
    "group": {
      "id": "gid://gitlab/Group/123",
      "securityMetrics": {
        "riskScore": {
          "score": 5.2,
          "rating": "MEDIUM",
          "factors": {
            "codeVisibility": {
              "visibility": "Public",
              "factor": 1.0
            },
            "environment": {
              "name": "Production", 
              "factor": 1.0
            },
            "updateFrequency": {
              "frequency": "Daily",
              "factor": 1.0
            },
            "vulnerabilitiesAverageScore": {
              "factor": 1.0
            }
          },
          "byProject": {
            "count": 2, 
            "nodes": [
              {
                "project": {
                  "id": "gid://gitlab/Project/123",
                  "name": "test-project"
                },
                "score": 5.2,
                "rating": "MEDIUM"
              }
            ]
          }
        }
      }
    }
  }
}
Edited by Subashis Chakraborty