Implement export API for security status in group security dashboard
What does this MR do and why?
Implement PDF export request for the "project security status" in the Group security dashboard
Example PDF: secure-ex_vulnerabilities_2025-06-24T1500.pdf
| Selection | Group page | Mailer | |
|---|---|---|---|
| Default = F | ![]() |
![]() |
|
| Select A | ![]() |
![]() |
|
References
Screenshots or screen recordings
| Before | After |
|---|---|
How to set up and validate locally
- Enable feature flag:
vulnerabilities_pdf_export - Clone this project: https://gitlab.com/gitlab-examples/security/security-reports
- Run a successful pipeline
- Go to the group security dashboard
- Open the dev tool and navigate to the "Network tab"
- Click on the export button
- From the response, check it's sending the correct data
{
"vulnerability_grades": [],
"expanded_grade": "A"
}
Example Response
{
"vulnerability_grades": [
{
"__typename": "VulnerableProjectsByGrade",
"grade": "A",
"count": 1,
"projects": {
"__typename": "ProjectConnection",
"nodes": [
{
"__typename": "Project",
"id": "gid://gitlab/Project/10",
"name": "520778 Multi Project",
"nameWithNamespace": "Secure Ex / 520778 Multi Project",
"securityDashboardPath": "/secure-ex/520778-multi-project/-/security/dashboard",
"fullPath": "secure-ex/520778-multi-project",
"avatarUrl": null,
"path": "520778-multi-project",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 0,
"high": 0,
"info": 0,
"low": 0,
"medium": 0,
"unknown": 0
}
}
]
}
},
{
"__typename": "VulnerableProjectsByGrade",
"grade": "D",
"count": 1,
"projects": {
"__typename": "ProjectConnection",
"nodes": [
{
"__typename": "Project",
"id": "gid://gitlab/Project/11",
"name": "Many Dependencies",
"nameWithNamespace": "Secure Ex / Many Dependencies",
"securityDashboardPath": "/secure-ex/many-dependencies/-/security/dashboard",
"fullPath": "secure-ex/many-dependencies",
"avatarUrl": null,
"path": "many-dependencies",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 0,
"high": 58,
"info": 0,
"low": 0,
"medium": 12,
"unknown": 0
}
}
]
}
},
{
"__typename": "VulnerableProjectsByGrade",
"grade": "C",
"count": 1,
"projects": {
"__typename": "ProjectConnection",
"nodes": [
{
"__typename": "Project",
"id": "gid://gitlab/Project/20",
"name": "Gitlab Terraform Eks",
"nameWithNamespace": "Secure Ex / Gitlab Terraform Eks",
"securityDashboardPath": "/secure-ex/gitlab-terraform-eks/-/security/dashboard",
"fullPath": "secure-ex/gitlab-terraform-eks",
"avatarUrl": null,
"path": "gitlab-terraform-eks",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 0,
"high": 0,
"info": 1,
"low": 0,
"medium": 2,
"unknown": 0
}
}
]
}
},
{
"__typename": "VulnerableProjectsByGrade",
"grade": "F",
"count": 6,
"projects": {
"__typename": "ProjectConnection",
"nodes": [
{
"__typename": "Project",
"id": "gid://gitlab/Project/22",
"name": "515553 Test Data Project For Filtering With Es",
"nameWithNamespace": "Secure Ex / 515553 Test Data Project For Filtering With Es",
"securityDashboardPath": "/secure-ex/515553-test-data-project-for-filtering-with-es/-/security/dashboard",
"fullPath": "secure-ex/515553-test-data-project-for-filtering-with-es",
"avatarUrl": null,
"path": "515553-test-data-project-for-filtering-with-es",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 55,
"high": 0,
"info": 0,
"low": 1045,
"medium": 0,
"unknown": 0
}
},
{
"__typename": "Project",
"id": "gid://gitlab/Project/21",
"name": "Cloud Native Lab",
"nameWithNamespace": "Secure Ex / Cloud Native Lab",
"securityDashboardPath": "/secure-ex/cloud-native-lab/-/security/dashboard",
"fullPath": "secure-ex/cloud-native-lab",
"avatarUrl": null,
"path": "cloud-native-lab",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 4,
"high": 32,
"info": 0,
"low": 6,
"medium": 28,
"unknown": 4
}
},
{
"__typename": "Project",
"id": "gid://gitlab/Project/16",
"name": "Cwe Samples",
"nameWithNamespace": "Secure Ex / Cwe Samples",
"securityDashboardPath": "/secure-ex/cwe-samples/-/security/dashboard",
"fullPath": "secure-ex/cwe-samples",
"avatarUrl": null,
"path": "cwe-samples",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 8,
"high": 22,
"info": 1,
"low": 6,
"medium": 162,
"unknown": 0
}
},
{
"__typename": "Project",
"id": "gid://gitlab/Project/13",
"name": "Security Reports With Dependency Paths",
"nameWithNamespace": "Secure Ex / Security Reports With Dependency Paths",
"securityDashboardPath": "/secure-ex/security-reports-with-dependency-paths/-/security/dashboard",
"fullPath": "secure-ex/security-reports-with-dependency-paths",
"avatarUrl": null,
"path": "security-reports-with-dependency-paths",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 11,
"high": 3,
"info": 0,
"low": 6,
"medium": 48,
"unknown": 6
}
},
{
"__typename": "Project",
"id": "gid://gitlab/Project/12",
"name": "Verify 520569",
"nameWithNamespace": "Secure Ex / Verify 520569",
"securityDashboardPath": "/secure-ex/verify-520569/-/security/dashboard",
"fullPath": "secure-ex/verify-520569",
"avatarUrl": null,
"path": "verify-520569",
"vulnerabilitySeveritiesCount": {
"__typename": "VulnerabilitySeveritiesCount",
"critical": 1,
"high": 32,
"info": 0,
"low": 6,
"medium": 17,
"unknown": 61
}
}
]
}
}
],
"expanded_grade": "A"
}
Note the BE is still in draft mode > Add group project security status to the pdf ex... (!195149 - merged). To get the PDF to generate, apply this patch:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195149.patch
- From the response, copy the "download" link and open it to download the file
- Change the extension to "pdf" and when open you should get some thing like this: secure-ex_vulnerabilities_2025-06-17T1726.pdf
Alternatively, you can also check the mailer. It can take awhile, apply this patch to have it sent immediately:
1 file changed, 1 insertion(+), 1 deletion(-)
ee/app/services/vulnerability_exports/create_service.rb | 2 +-
modified ee/app/services/vulnerability_exports/create_service.rb
@@ -27,7 +27,7 @@ def execute
**create_params
)
- ::VulnerabilityExports::ExportWorker.perform_async(vulnerability_export.id)
+ ::VulnerabilityExports::ExportWorker.perform_inline(vulnerability_export.id)
if vulnerability_export.persisted?
ServiceResponse.success(payload: { vulnerability_export: vulnerability_export })
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.
Related to #546166 (closed)
Edited by Samantha Ming





