Resolve: Security Dashboard - VulnerabilitiesOverTime: Validate and Update Filtering Logic
What does this MR do and why?
This MR reviews and updates filtering for the VulnerabilitiesOverTime view in the Security Dashboard, ensuring it matches the clarified scope from epic &17874 (closed) and this comment. With this MR, we are making sure the following scope is correctly implemented:
Panel-level
- Filter by Severity
- Grouping by Severity
- Grouping by Report Type
Page-level (inherited filters)
- Filter by Report Type
- Filter by Project
This MR includes:
-
Adds support for filtering by report_type at the page level -
Ensures project_id filtering works as intended (page level) -
Ensures severity filtering is applied correctly (panel level) -
Removes scanner filtering (see discussion)
References
- Main Issue: Security Dashboard - VulnerabilitiesOverTime: V... (#555949 - closed) • Charlie Kroon • 18.3
- Epic: Security Dashboard Backend – GraphQL Support, F... (&17874 - closed) • Charlie Kroon • 18.3
- Related discussion: &17874 (comment 2516849760)+s
Screenshots or screen recordings
How to set up and validate locally
Step 1: ElasticSearch Setup + Feature Flag
- Make sure you have ElasticSearch running on your local env. Follow the steps: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/elasticsearch.md#setup
- Run the migration in Rails console:
Elastic::DataMigrationService[20250408180015].migrate
- Index vulnerability data:
Vulnerabilities::Read.all.each { |v| ::Elastic::ProcessBookkeepingService.track!(Search::Elastic::References::Vulnerability.new(v.vulnerability_id, "group_#{v.project.namespace.root_ancestor.id}")) }
- Then run:
Elastic::ProcessBookkeepingService.new.execute
- Now enable the feature flag in Rails console:
Feature.enable(:group_security_dashboard_new)
Step 2: Testing
Page Level Filtering
- Now that you have ES running on your gdk, navigate to http://gdk.test:3000/-/graphql-explorer and run the following query. Only
SASTvulnerabilities should be returned:
{
group(fullPath: "gitlab-org") {
id
securityMetrics(
reportType:[SAST]
) {
vulnerabilitiesOverTime(
startDate: "2025-01-01T00:00:00Z"
endDate: "2026-01-01T00:00:00Z"
) {
nodes {
date
byReportType {
reportType
count
}
}
}
}
}
}
- Now, let's test this on with multiple report types. Only
SAST,DAST, andCOVERAGE_FUZZINGshould be returned:
{
group(fullPath: "gitlab-org") {
id
securityMetrics(
reportType:[SAST, DAST, COVERAGE_FUZZING]
) {
vulnerabilitiesOverTime(
startDate: "2025-01-01T00:00:00Z"
endDate: "2026-01-01T00:00:00Z"
) {
nodes {
date
byReportType {
reportType
count
}
}
}
}
}
}
- Now, let's test for project filtering. First, run the following query, so we know which
project_idto use while filtering.
{
group(fullPath: "gitlab-org") {
id
projects {
edges {
node {
id
}
}
}
}
}
- Now filter for one project, then for more than one
{
group(fullPath: "gitlab-org") {
id
securityMetrics(projectId: [45]) { # Use one of the project_id's you got returned in the previous query
vulnerabilitiesOverTime(
startDate: "2025-04-20T00:00:00Z"
endDate: "2025-07-01T00:00:00Z"
) {
nodes {
date
byReportType { reportType count }
bySeverity { severity count }
}
}
}
}
}
- Now, filter on more than one
project_id
{
group(fullPath: "gitlab-org") {
id
securityMetrics(projectId: [45, 46, 47]) { # Use the project_id's you got returned in the previous query
vulnerabilitiesOverTime(
startDate: "2025-04-20T00:00:00Z"
endDate: "2025-07-01T00:00:00Z"
) {
nodes {
date
byReportType {
reportType
count
}
bySeverity {
severity
count
}
}
}
}
}
}
Panel Level Filtering
- Run the following query. Only severities that are
criticalshould be returned.
{
group(fullPath: "gitlab-org") {
id
securityMetrics {
vulnerabilitiesOverTime(
startDate: "2025-04-20T00:00:00Z"
endDate: "2025-07-01T00:00:00Z"
severity: [CRITICAL]
) {
nodes {
date
bySeverity {
severity
count
}
}
}
}
}
}
- Run the same query, but this time with multiple severities:
{
group(fullPath: "gitlab-org") {
id
securityMetrics {
vulnerabilitiesOverTime(
startDate: "2025-04-20T00:00:00Z"
endDate: "2025-07-01T00:00:00Z"
severity: [CRITICAL, HIGH, MEDIUM]
) {
nodes {
date
bySeverity {
severity
count
}
}
}
}
}
}
Page level and Panel level filtering
- Now let's filter on both page, and panel level:
{
group(fullPath: "gitlab-org") {
id
projects {
edges {
node {
id
}
}
}
securityMetrics(
projectId: [47, 46, 45] # use the project ids you got returned
reportType:[SAST, DAST, COVERAGE_FUZZING]
) {
vulnerabilitiesOverTime(
startDate: "2025-01-01T00:00:00Z"
endDate: "2026-01-01T00:00:00Z"
severity: [LOW, MEDIUM, HIGH, INFO]
) {
nodes {
date
byReportType {
reportType
count
}
bySeverity {
severity
count
}
}
}
}
}
}
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Edited by Charlie Kroon