Draft: Vulnerability bulk confirm mutation
What does this MR do and why?
This change introduces a new mutation (Mutation.VulnerabilitiesConfirm) to confirm multiple vulnerabilities at the same time. It accepts a maximum of 100 vulnerability id's at a time and performs bulk updates and inserts into the vulnerabilities, vulnerability_state_transitions, and vulnerability_reads tables.
SELECT DISTINCT
"vulnerabilities"."project_id"
FROM
"vulnerabilities"
WHERE
"vulnerabilities"."id" IN (
649, 648, 647, 646, 645, 644, 643, 641, 640, 639,
638, 636, 635, 634, 632, 631, 630, 629, 628, 627,
626, 625, 624, 623, 621, 620, 619, 618, 616, 615,
614, 613, 612, 611, 610, 609, 607, 606, 605, 604,
603, 602, 601, 600, 599, 598, 597, 596, 595, 593,
592, 591, 590, 589, 588, 587, 586, 584, 583, 582,
581, 580, 579, 578, 577, 576, 575, 574, 572, 571,
570, 569, 568, 567, 566, 564, 563, 562, 561, 560,
559, 558, 557, 556, 555, 554, 553, 552, 551, 550,
549, 547, 546, 545, 541
);
SELECT
"vulnerabilities"."id",
"vulnerabilities"."state",
"vulnerabilities"."project_id"
FROM
"vulnerabilities"
WHERE
"vulnerabilities"."id" IN (
649, 648, 647, 646, 645, 644, 643, 641, 640, 639, 638, 636, 635, 634, 632,
631, 630, 629, 628, 627, 626, 625, 624, 623, 621, 620, 619, 618, 616, 615,
614, 613, 612, 611, 610, 609, 607, 606, 605, 604, 603, 602, 601, 600, 599,
598, 597, 596, 595, 593, 592, 591, 590, 589, 588, 587, 586, 584, 583, 582,
581, 580, 579, 578, 577, 576, 575, 574, 573, 572, 571, 570, 569, 568, 567,
566, 564, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551,
550, 549, 547, 546, 545, 541
);
INSERT INTO "vulnerability_state_transitions" (
"vulnerability_id", "from_state", "to_state", "comment", "author_id", "created_at", "updated_at"
)
VALUES
(545, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(546, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(549, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(553, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(554, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(555, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(557, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(561, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(570, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(581, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(584, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(586, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(588, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(589, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(595, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(599, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(600, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(604, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(609, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(610, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(612, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(616, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(618, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(629, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(634, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(636, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(639, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(640, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(641, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(643, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(646, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(647, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(648, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(649, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(551, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(558, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(559, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(560, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(564, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(566, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(567, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(568, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(571, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(573, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(574, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(577, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(578, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(579, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(590, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(591, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(592, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(596, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(597, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(598, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(601, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(602, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(603, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(605, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(606, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(607, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(613, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(619, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(621, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(623, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(625, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(626, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(627, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(628, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(630, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(631, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(632, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(635, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(644, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(550, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(552, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(556, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(562, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(563, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(569, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(572, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(575, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(576, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(580, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(582, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(583, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(587, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(593, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(611, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(614, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(615, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(620, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(624, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(638, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(645, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(541, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876'),
(547, 4, 4, 'Test comment', 1, '2025-07-04 00:22:17.029876', '2025-07-04 00:22:17.029876')
RETURNING
"id";
UPDATE
"vulnerability_reads"
SET
"auto_resolved" = FALSE
WHERE
"vulnerability_reads"."vulnerability_id" IN (
SELECT
"vulnerabilities"."id"
FROM
"vulnerabilities"
WHERE
"vulnerabilities"."id" IN (
649, 648, 647, 646, 645, 644, 643, 641, 640, 639, 638, 636, 635, 634, 632,
631, 630, 629, 628, 627, 626, 625, 624, 623, 621, 620, 619, 618, 616, 615,
614, 613, 612, 611, 610, 609, 607, 606, 605, 604, 603, 602, 601, 600, 599,
598, 597, 596, 595, 593, 592, 591, 590, 589, 588, 587, 586, 584, 583, 582,
581, 580, 579, 578, 577, 576, 575, 574, 573, 572, 571, 570, 569, 568, 567,
566, 564, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551,
550, 549, 547, 546, 545, 541
)
);
UPDATE
"vulnerabilities"
SET
"state" = 4,
"auto_resolved" = FALSE,
"confirmed_by_id" = 1,
"confirmed_at" = '2025-07-04 00:22:17.029876',
"updated_at" = '2025-07-04 00:22:17.029876',
"dismissed_at" = NULL,
"dismissed_by_id" = NULL,
"resolved_at" = NULL,
"resolved_by_id" = NULL
WHERE
"vulnerabilities"."id" IN (
649, 648, 647, 646, 645, 644, 643, 641, 640, 639, 638, 636, 635, 634, 632,
631, 630, 629, 628, 627, 626, 625, 624, 623, 621, 620, 619, 618, 616, 615,
614, 613, 612, 611, 610, 609, 607, 606, 605, 604, 603, 602, 601, 600, 599,
598, 597, 596, 595, 593, 592, 591, 590, 589, 588, 587, 586, 584, 583, 582,
581, 580, 579, 578, 577, 576, 575, 574, 573, 572, 571, 570, 569, 568, 567,
566, 564, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551,
550, 549, 547, 546, 545, 541
);
How to set up and validate locally
- Enable the feature flag:
bundle exec rails cFeature.enable(:confirm_multiple_vulnerabilities)
- Run the mutation in
http://localhost:3000/-/graphql-explorer:
Firstly, enter the vulnerability ids into the variables field:
{
"comment": "Test comment",
"ids" : [
"gid://gitlab/Vulnerability/649",
"gid://gitlab/Vulnerability/648",
...
]
}
Then, execute.
mutation BulkConfirmVulnerabilities($ids: [VulnerabilityID!]!, $comment: String!) {
vulnerabilitiesConfirm(input: {
vulnerabilityIds: $ids,
comment: $comment,
}) {
vulnerabilities {
id
state
confirmedAt
confirmedBy {
name
}
stateTransitions {
nodes {
fromState
toState
comment
author {
name
}
}
}
}
errors
}
}
Screenshots or screen recordings
When the feature flag is disabled:

When the feature flag is enabled and executed with valid arguments:

When > 100 vulnerabilities are confirmed:

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 #395677
Edited by Patrick He