Add policy violations to vulnerability graph api
What does this MR do and why?
Add policy violations to vulnerability graph api
This MR adds a new argument to VulnerabilitiesResolver, to filter vulnerabilities dismissed by security policies
ES Query
Gitlab::Search::Client.new.search(
index: 'gitlab-development-vulnerabilities',
routing: 'group_490', # data is distributed across shards and the query builder passes routing information.
body:{
query: {
bool: {
filter: [{
terms: {
_name: "filters:policy_violations",
policy_violations: [1]
}
}],
must_not: [],
must: [],
should: []
}
}
}
)
total_time=0.016665
=> {"took"=>8,
"timed_out"=>false,
"_shards"=>{"total"=>1, "successful"=>1, "skipped"=>0, "failed"=>0},
"hits"=>
{"total"=>{"value"=>2, "relation"=>"eq"},
"max_score"=>0.0,
"hits"=>
[{"_index"=>"gitlab-development-vulnerabilities-20251003-2328",
"_id"=>"1026",
"_score"=>0.0,
"_routing"=>"group_490",
"_source"=>
{"schema_version"=>2543,
"type"=>"vulnerability",
"vulnerability_id"=>1026,
"project_id"=>223,
"scanner_id"=>620,
"uuid"=>"7fb061fa-c1a1-5758-9f88-a4f9a79e7b9d",
"location_image"=>nil,
"cluster_agent_id"=>nil,
"casted_cluster_agent_id"=>nil,
"has_issues"=>false,
"resolved_on_default_branch"=>false,
"has_merge_request"=>false,
"has_remediations"=>false,
"archived"=>false,
"has_vulnerability_resolution"=>false,
"auto_resolved"=>false,
"identifier_names"=>["Gitleaks rule ID typeform-api-token"],
"report_type"=>4,
"severity"=>7,
"state"=>1,
"dismissal_reason"=>nil,
"scanner_external_id"=>"gitleaks",
"created_at"=>"2025-10-22T20:31:16.505Z",
"updated_at"=>"2025-10-22T20:31:16.505Z",
"traversal_ids"=>"490-",
"epss_scores"=>[],
"risk_score"=>0.0,
"reachability"=>0,
"token_status"=>0,
"policy_violations"=>1,
"resolved_at"=>nil,
"dismissed_at"=>nil},
"matched_queries"=>["filters:policy_violations"]},
{"_index"=>"gitlab-development-vulnerabilities-20251003-2328",
"_id"=>"1148",
"_score"=>0.0,
"_routing"=>"group_490",
"_source"=>
{"schema_version"=>2543,
"type"=>"vulnerability",
"vulnerability_id"=>1148,
"project_id"=>225,
"scanner_id"=>628,
"uuid"=>"16640ea1-2bda-5ea0-a983-f5ad57554811",
"location_image"=>nil,
"cluster_agent_id"=>nil,
"casted_cluster_agent_id"=>nil,
"has_issues"=>false,
"resolved_on_default_branch"=>false,
"has_merge_request"=>false,
"has_remediations"=>false,
"archived"=>false,
"has_vulnerability_resolution"=>false,
"auto_resolved"=>false,
"identifier_names"=>["Gitleaks rule ID twitch-api-token"],
"report_type"=>4,
"severity"=>7,
"state"=>1,
"dismissal_reason"=>nil,
"scanner_external_id"=>"gitleaks",
"created_at"=>"2025-10-22T20:48:59.900Z",
"updated_at"=>"2025-10-22T20:48:59.900Z",
"traversal_ids"=>"490-",
"epss_scores"=>[],
"risk_score"=>0.0,
"reachability"=>0,
"token_status"=>0,
"policy_violations"=>1,
"resolved_at"=>nil,
"dismissed_at"=>nil},
"matched_queries"=>["filters:policy_violations"]}]}}
References
Screenshots or screen recordings
Project level
Group level
How to set up and validate locally
Elasticsearch
Setup
- Enable the
Elasticsearch
in GDK
gdk config set elasticsearch.enabled true
gdk reconfigure
gdk start elasticsearch
Simulate SAAS mode
- add
export GITLAB_SIMULATE_SAAS=1
toenv.runit
- Restart the gdk
gdk kill && gdk start
Advanced Search Setup
- Enable the
advanced_vulnerability_management
andpolicy_violations_es_filter
feature flags
Feature.enable(:advanced_vulnerability_management)
Feature.enable(:policy_violations_es_filter)
Follow the steps of https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/elasticsearch.md#setup
Dismiss a security finding blocked by a security policy
- Import the security-reports project.
- Run a new pipeline on the default branch to create vulnerabilities
- Go to Secure > Policies and create a Merge Request Approval policy
- Create an MR editing the README file
- Create a record of
Security::PolicyDismissal
to simulate a bypassed/dismissed policy
Security::PolicyDismissal.create(project: Project.second_to_last, merge_request: MergeRequest.last, status: 1, security_policy: Security::Policy.last, user: User.first, dismissal_types: [0], security_findings_uuids: ["some-uuid-from-vulnerability-report"])
- Update the ES index in Rails console loading the vulnerability read with the same dismissed UUID from the previous step
vulnerability_read = Vulnerabilities::Read.where(uuid: "some-uuid-from-vulnerability-report").first
::Elastic::ProcessBookkeepingService.track!(Search::Elastic::References::Vulnerability.new(vulnerability_read.vulnerability_id, "group_#{vulnerability_read.project.namespace.root_ancestor.id}"))
- Process the Redis refs into ES, run below command multiple times unless the results show
[0, 0]
.
Elastic::ProcessBookkeepingService.new.execute
- Go to
/-/graphql-explorer
- Query the vulnerabilities by the
securityPolicyBypassReason
{
project(fullPath: "project-full-path") {
vulnerabilities(policyViolations: [DISMISSED_IN_MR]) {
nodes {
description
uuid
}
}
}
}
To test at the group level
- Repeat the steps 1 to 8
- Go to
/-/graphql-explorer
- Query the vulnerabilities by the
securityPolicyBypassReason
{
group(fullPath: "group-full-path") {
projects {
nodes {
id
vulnerabilities(policyViolations: [DISMISSED_IN_MR]) {
nodes {
description
uuid
}
}
}
}
}
}
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 Marcos Rocha