Add proper authorization to vulnerability details fields

What does this MR do and why?

This MR adds proper authorization to fields required to fetch vulnerability details

The GraphQL query used to do it follows:

fragment Url on VulnerabilityDetailUrl {
  type: __typename
  name
  href
}

fragment Diff on VulnerabilityDetailDiff {
  type: __typename
  name
  before
  after
}

fragment Code on VulnerabilityDetailCode {
  type: __typename
  name
  value
}

fragment FileLocation on VulnerabilityDetailFileLocation {
  type: __typename
  name
  fileName
  lineStart
  lineEnd
}

fragment ModuleLocation on VulnerabilityDetailModuleLocation {
  type: __typename
  name
  moduleName
  offset
}

fragment Commit on VulnerabilityDetailCommit {
  type: __typename
  name
  value
}

fragment Text on VulnerabilityDetailText {
  type: __typename
  name
  value
}

fragment Markdown on VulnerabilityDetailMarkdown {
  type: __typename
  name
  value
}

fragment Boolean on VulnerabilityDetailBoolean {
  type: __typename
  name
  value
}

fragment Int on VulnerabilityDetailInt {
  type: __typename
  name
  value
}

fragment NonNestedReportTypes on VulnerabilityDetail {
  ...FileLocation
  ...Url
  ...Diff
  ...Code
  ...Commit
  ...Markdown
  ...Text
  ...Int
  ...Boolean
  ...ModuleLocation
}

fragment ListFields on VulnerabilityDetailList {
  type: __typename
  name
}

fragment List on VulnerabilityDetailList {
  ...ListFields
  items {
    ...NonNestedReportTypes
    ... on VulnerabilityDetailList {
      ...ListFields
      items {
        ...NonNestedReportTypes
      }
    }
  }
}

fragment NamedList on VulnerabilityDetailNamedList {
  type: __typename
  name
  items {
    name
    fieldName
    value {
      ...NonNestedReportTypes
      ...Table
      ... on VulnerabilityDetailList {
        ...ListFields
        items {
          ...NonNestedReportTypes
        }
      }
    }
  }
}

fragment CodeFlowNode on VulnerabilityDetailCodeFlowNode {
  type: __typename
  nodeType
  fileLocation {
    ...FileLocation
  }
}

fragment CodeFlows on VulnerabilityDetailCodeFlows {
  type: __typename
  name
  items {
    ... {
      ...CodeFlowNode
    }
  }
}

fragment TableFields on VulnerabilityDetailTable {
  type: __typename
  name
  headers {
    ...NonNestedReportTypes
  }
  rows {
    row {
      ...NonNestedReportTypes
    }
  }
}

fragment Table on VulnerabilityDetailTable {
  type: __typename
  name
  headers {
    ...NonNestedReportTypes
  }
  rows {
    row {
      ...NonNestedReportTypes
      ...TableFields
    }
  }
}

{
  vulnerability(id: "gid://gitlab/Vulnerability/567") {
    id
    title
    state
    description
    reachability
    cveEnrichment {
      cve
      epssScore
      isKnownExploit
    }
    detectedAt
    dismissedAt
    initialDetectedPipeline {
      id
      name
      createdAt
    }
    location {
      __typename
      ... on VulnerabilityLocationClusterImageScanning {
        image
        kubernetesResource {
          agent {
            id
            name
            webPath
          }
        }
      }
      ... on VulnerabilityLocationContainerScanning {
        image
        containerRepositoryUrl
      }
      ... on VulnerabilityLocationCoverageFuzzing {
        blobPath
        crashAddress
        crashType
        endLine
        file
        stacktraceSnippet
        startLine
        vulnerableClass
        vulnerableMethod
      }
      ... on VulnerabilityLocationDast {
        path
      }
      ... on VulnerabilityLocationDependencyScanning {
        blobPath
        file
      }
      ... on VulnerabilityLocationGeneric {
        description
      }
      ... on VulnerabilityLocationSast {
        blobPath
        file
        startLine
      }
      ... on VulnerabilityLocationSecretDetection {
        blobPath
        file
        startLine
      }
    }
    details {
      __typename
      ...NonNestedReportTypes
      ...List
      ...Table
      ...NamedList
      ...CodeFlows
      ...NonNestedReportTypes
    }
  }
}

How this beast was created? It was inspired by [relevant part] of security findings report

References

#554679+

Screenshots or screen recordings

N/A

How to set up and validate locally

  1. Seed the vulnerabilities by running bin/rake 'gitlab:seed:vulnerabilities[gitlab-org/gitlab-shell]'
  2. In the rails console of the GDK, create a new OAuth access token with ai_workflows scope and get the last id of seeded vulnerability.
[1] pry(main)> application = Doorkeeper::Application.create!(
  name: "AI Workflows App",
  redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
  scopes: "ai_workflows",
  owner: User.find_by_username("root")
=> #<Doorkeeper::Application:0x000000031eb34cc0
[2] pry(main)> Doorkeeper::AccessToken.create!(
  application_id: application.id,
  resource_owner_id: application.owner.id,
  scopes: "ai_workflows",
  expires_in: 1.year.to_i,
=> "30bad7653d06a57435358cf774cd094df403ff981b38d9de4e262b5ff0c2ab51"
[3] pry(main)> Vulnerability.last.id
=> 570
  1. Send a request with the new token in Authorization header to a relevant endpoint. Do not forget to replace 999 at the start of --data-raw section of command line with number you've retrieved on previous step (570 in my example)
% curl 'http://gdk.test:3000/api/graphql' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer 30bad7653d06a57435358cf774cd094df403ff981b38d9de4e262b5ff0c2ab51' \
  --data-raw $'{"variables":{"vulnerabilityId":"gid://gitlab/Vulnerability/999"}, "operationName":"VulnerabilityDetails","query":"fragment Url on VulnerabilityDetailUrl {\\n  type: __typename\\n  name\\n  href\\n}\\n\\nfragment Diff on VulnerabilityDetailDiff {\\n  type: __typename\\n  name\\n  before\\n  after\\n}\\n\\nfragment Code on VulnerabilityDetailCode {\\n  type: __typename\\n  name\\n  value\\n}\\n\\nfragment FileLocation on VulnerabilityDetailFileLocation {\\n  type: __typename\\n  name\\n  fileName\\n  lineStart\\n  lineEnd\\n}\\n\\nfragment ModuleLocation on VulnerabilityDetailModuleLocation {\\n  type: __typename\\n  name\\n  moduleName\\n  offset\\n}\\n\\nfragment Commit on VulnerabilityDetailCommit {\\n  type: __typename\\n  name\\n  value\\n}\\n\\nfragment Text on VulnerabilityDetailText {\\n  type: __typename\\n  name\\n  value\\n}\\n\\nfragment Markdown on VulnerabilityDetailMarkdown {\\n  type: __typename\\n  name\\n  value\\n}\\n\\nfragment Boolean on VulnerabilityDetailBoolean {\\n  type: __typename\\n  name\\n  value\\n}\\n\\nfragment Int on VulnerabilityDetailInt {\\n  type: __typename\\n  name\\n  value\\n}\\n\\nfragment NonNestedReportTypes on VulnerabilityDetail {\\n  ...FileLocation\\n  ...Url\\n  ...Diff\\n  ...Code\\n  ...Commit\\n  ...Markdown\\n  ...Text\\n  ...Int\\n  ...Boolean\\n  ...ModuleLocation\\n}\\n\\nfragment ListFields on VulnerabilityDetailList {\\n  type: __typename\\n  name\\n}\\n\\nfragment List on VulnerabilityDetailList {\\n  ...ListFields\\n  items {\\n    ...NonNestedReportTypes\\n    ... on VulnerabilityDetailList {\\n      ...ListFields\\n      items {\\n        ...NonNestedReportTypes\\n      }\\n    }\\n  }\\n}\\n\\nfragment NamedList on VulnerabilityDetailNamedList {\\n  type: __typename\\n  name\\n  items {\\n    name\\n    fieldName\\n    value {\\n      ...NonNestedReportTypes\\n      ...Table\\n      ... on VulnerabilityDetailList {\\n        ...ListFields\\n        items {\\n          ...NonNestedReportTypes\\n        }\\n      }\\n    }\\n  }\\n}\\n\\nfragment CodeFlowNode on VulnerabilityDetailCodeFlowNode {\\n  type: __typename\\n  nodeType\\n  fileLocation {\\n    ...FileLocation\\n  }\\n}\\n\\nfragment CodeFlows on VulnerabilityDetailCodeFlows {\\n  type: __typename\\n  name\\n  items {\\n    ... {\\n      ...CodeFlowNode\\n    }\\n  }\\n}\\n\\nfragment TableFields on VulnerabilityDetailTable {\\n  type: __typename\\n  name\\n  headers {\\n    ...NonNestedReportTypes\\n  }\\n  rows {\\n    row {\\n      ...NonNestedReportTypes\\n    }\\n  }\\n}\\n\\nfragment Table on VulnerabilityDetailTable {\\n  type: __typename\\n  name\\n  headers {\\n    ...NonNestedReportTypes\\n  }\\n  rows {\\n    row {\\n      ...NonNestedReportTypes\\n      ...TableFields\\n    }\\n  }\\n}\\n\\nquery VulnerabilityDetails($vulnerabilityId: VulnerabilityID\u0021) {\\n  vulnerability(id: $vulnerabilityId) {\\n    id\\n    title\\n    state\\n    description\\n    reachability\\n    cveEnrichment {\\n      cve\\n      epssScore\\n      isKnownExploit\\n    }\\n    detectedAt\\n    dismissedAt\\n    initialDetectedPipeline {\\n      id\\n      name\\n      createdAt\\n    }\\n    location {\\n      __typename\\n      ... on VulnerabilityLocationClusterImageScanning {\\n        image\\n        kubernetesResource {\\n          agent {\\n            id\\n            name\\n            webPath\\n          }\\n        }\\n      }\\n      ... on VulnerabilityLocationContainerScanning {\\n        image\\n        containerRepositoryUrl\\n      }\\n      ... on VulnerabilityLocationCoverageFuzzing {\\n        blobPath\\n        crashAddress\\n        crashType\\n        endLine\\n        file\\n        stacktraceSnippet\\n        startLine\\n        vulnerableClass\\n        vulnerableMethod\\n      }\\n      ... on VulnerabilityLocationDast {\\n        path\\n      }\\n      ... on VulnerabilityLocationDependencyScanning {\\n        blobPath\\n        file\\n      }\\n      ... on VulnerabilityLocationGeneric {\\n        description\\n      }\\n      ... on VulnerabilityLocationSast {\\n        blobPath\\n        file\\n        startLine\\n      }\\n      ... on VulnerabilityLocationSecretDetection {\\n        blobPath\\n        file\\n        startLine\\n      }\\n    }\\n    details {\\n      __typename\\n      ...NonNestedReportTypes\\n      ...List\\n      ...Table\\n      ...NamedList\\n      ...CodeFlows\\n      ...NonNestedReportTypes\\n    }\\n  }\\n}"}' | jq

Observe the output:

{
  "data": {
    "vulnerability": {
      "id": "gid://gitlab/Vulnerability/570",
      "title": "Cypher with no integrity",
      "state": "DISMISSED",
      "description": "Description of Cypher with no integrity",
      "reachability": null,
      "cveEnrichment": null,
      "detectedAt": "2025-07-25T16:20:23Z",
      "dismissedAt": "2025-07-25T16:20:23Z",
      "initialDetectedPipeline": {
        "id": "gid://gitlab/Ci::Pipeline/594",
        "name": null,
        "createdAt": "2025-07-25T16:20:23Z"
      },
      "location": {
        "__typename": "VulnerabilityLocationClusterImageScanning",
        "image": null,
        "kubernetesResource": null
      },
      "details": [
        {
          "__typename": "VulnerabilityDetailUrl",
          "type": "VulnerabilityDetailUrl",
          "name": "URL",
          "href": "http://site.com"
        }
      ]
    }
  }
}

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 Illya Klymov

Merge request reports

Loading