Add granular token authorization to Vulnerability management mutations

What does this MR do and why?

Adds granular (fine-grained) personal access token authorization to the Vulnerability management group of GraphQL mutations (group 4 of the authorization todo list), following the GraphQL implementation guide.

For each mutation this MR adds an authorize_granular_token directive, raw/assignable permission definitions where none existed, an authorization request spec, and removes the mutation's entry from config/authz/graphql/authorization_todo.txt.

Mutation → permission → boundary

Mutation Permission Boundary
AddProjectToSecurityDashboard update_security_dashboard project (boundary_argument: :id)
RemoveProjectFromSecurityDashboard update_security_dashboard project (boundary_argument: :id)
BulkSetVulnerabilityFindingsDueDates update_finding_due_date project (boundary_argument: :project_full_path)
RefreshFindingTokenStatus update_finding_token_status project (boundary_argument: :vulnerability_id, boundary: :project)
RefreshVulnerabilityFindingTokenStatus update_finding_token_status project (boundary_argument: :vulnerability_id, boundary: :project)
SecurityFindingCreateIssue create_vulnerability_issue_link project (boundary_argument: :project)
SecurityFindingExternalIssueLinkCreate create_vulnerability_external_issue_link project (boundary_argument: :project)
SecurityFindingJiraIssueFormUrlCreate create_vulnerability_external_issue_link project (boundary_argument: :project)
VulnerabilitiesArchive archive_vulnerability project (boundary_argument: :project_id)
VulnerabilitiesCreateIssue create_vulnerability_issue_link project (boundary_argument: :project)
VulnerabilityDismissFalsePositiveFlag update_vulnerability_flag project (boundary_argument: :id, boundary: :project)
VulnerabilityExternalIssueLinkCreate create_vulnerability_external_issue_link project (boundary_argument: :id, boundary: :project)
VulnerabilityExternalIssueLinkDestroy delete_vulnerability_external_issue_link project (boundary_argument: :id, boundary: :project)
VulnerabilityIssueLinkCreate create_vulnerability_issue_link project (boundary_argument: :issue_id, boundary: :project)
VulnerabilityLinkMergeRequest create_vulnerability_merge_request_link project (boundary_argument: :vulnerability_id, boundary: :project)
VulnerabilityUnlinkMergeRequest delete_vulnerability_merge_request_link project (boundary_argument: :vulnerability_id, boundary: :project)

Permissions

  • Reused existing permissions: create_vulnerability_issue_link, update_vulnerability_flag.
  • New raw permissions (all project-bounded, available_for: granular_access_token): archive_vulnerability, create_vulnerability_external_issue_link, delete_vulnerability_external_issue_link, create_vulnerability_merge_request_link, delete_vulnerability_merge_request_link, update_finding_due_date, update_finding_token_status, update_security_dashboard.
  • Assignable grouping: rather than adding a parallel assignable per new raw, the nested and lifecycle raws are folded into the existing master assignables (raw permission names and mutation directives are unchanged; only assignable membership moves):
    • update_finding_due_date → existing update_vulnerability assignable, alongside the confirm/dismiss/resolve/revert lifecycle raws. Findings' due dates are vulnerability attributes, so they belong in the same assignable.
    • archive_vulnerabilitynew standalone archive_vulnerability assignable (project-bounded). Archiving is semi-destructive and removes items from the active view, so it is kept out of update_vulnerability rather than broadening that assignable.
    • create_vulnerability_external_issue_link → existing create_vulnerability_issue_link assignable; delete_vulnerability_external_issue_link → existing delete_vulnerability_issue_link assignable (external Jira/Zentao links are the same user-facing capability as internal issue links).
    • Standalone assignables kept: create_vulnerability_merge_request_link, delete_vulnerability_merge_request_link, update_security_dashboard, update_finding_token_status.

Judgment calls

  • Vulnerabilities::ExternalIssueLink gained has_one :project, through: :vulnerability so the boundary extractor can reach the project from the link GlobalID in VulnerabilityExternalIssueLinkDestroy. A real association (rather than a delegate) is required because the boundary preloader resolves paths via reflect_on_association.
  • AddProjectToSecurityDashboard / RemoveProjectFromSecurityDashboard operate on the user-scoped instance security dashboard, but the mutation argument is a project GlobalID and the effective capability is exposing that project's security data, so they are bounded to the project with a shared update_security_dashboard permission.
  • The token status refresh mutations use a single update_finding_token_status permission — they perform the same domain action on the same resource.

Skipped mutations (todo entries kept)

These cannot be authorized with the current framework because Gitlab::Graphql::Authz::BoundaryExtractor#locate only resolves a single GlobalID or a full-path string; it cannot resolve raw Security::Finding UUID strings or arrays of IDs:

Mutation Blocking argument
SecurityFindingCreateMergeRequest uuid (raw finding UUID string)
SecurityFindingCreateVulnerability uuid
SecurityFindingDismiss uuid
SecurityFindingRevertToDetected uuid
SecurityFindingSeverityOverride uuid
RefreshSecurityFindingTokenStatus security_finding_uuid
VulnerabilitiesDismiss vulnerability_ids (array)
vulnerabilitiesSeverityOverride vulnerability_ids (array)
VulnerabilitiesRemoveAllFromProject project_ids (array)

How was this validated?

  • bundle exec rake gitlab:permissions:validate passes.
  • bundle exec rake gitlab:permissions:graphql:compile_docs and bundle exec rake gitlab:permissions:routes:compile_docs regenerated the reference docs (committed).
  • Every declared permission/boundary has an authorizing granular token permissions for GraphQL shared-example request spec; specs run in CI.
  • RuboCop clean on all changed files.
Edited by Alex Buijs

Merge request reports

Loading
Loading