Vulnerabilities created via API have invalid location metadata

Summary

Vulnerabilities created via the API have a symbolized location key in their raw_metadata, which causes the raw_metadata to be invalid JSON.

Steps to reproduce

Via GraphQL:

  1. Go to http://localhost:3000/-/graphql-explorer

  2. Enter this query:

    mutation vulnerabilityCreate($input: VulnerabilityCreateInput!) {
      vulnerabilityCreate(input: $input) {
        errors
        clientMutationId
        vulnerability: vulnerability {
          id
          vulnerabilityPath
          project {
            id
            fullPath
          }
        }
      }
    }

    with these query variables:

    {
      "input": {
        "project": "gid://gitlab/Project/1",
        "name": "A manual vulnerability number 1",
        "description": "A descriptive description.\nNumber 1.",
        "scanner": {
          "id": "SAST",
          "name": "Test",
          "url": "",
          "vendor": { "name": "some vendor"},
          "version": "v1"
        },
        "state": "DETECTED",
        "severity": "CRITICAL",
        "identifiers": [
          {
            "name": "CVE-1",
            "url": "http://localhost"
          }
        ]
      }
    }

    and receive response:

    {
      "data": {
        "vulnerabilityCreate": {
          "errors": [],
          "clientMutationId": null,
          "vulnerability": {
            "id": "gid://gitlab/Vulnerability/671",
            "vulnerabilityPath": "/gitlab-org/gitlab-test/-/security/vulnerabilities/671",
            "project": {
              "id": "gid://gitlab/Project/1",
              "fullPath": "gitlab-org/gitlab-test"
            }
          }
        }
      }
    }
  3. Take the vulnerability ID and check the metadata using the rails console:

    [5] pry(main)> vuln = Vulnerability.find(671)
    => #<Vulnerability id:671 [vulnerability:gitlab-org/gitlab-test/671]>
    [6] pry(main)> vuln.finding.raw_metadata
    => "{:location=>nil}"

Via Starboard API:

  1. Create a new KAS JWT using the rails console:

    JWT.encode({ 'iss' => Gitlab::Kas::JWT_ISSUER }, Gitlab::Kas.secret, 'HS256')
  2. Create a new gitlab project

  3. Create new agent:

    agent = Clusters::Agent.new(project_id: project.id, created_by_user: User.find(1), name: "test-agent")
    agent.save!
  4. Create new agent token and retrieve the value

    token = Clusters::AgentToken.new(agent: agent, created_by_user: User.find(1), name: "test-agent-token")
    token.save!
    token.token
  5. Send the API request:

    curl --request PUT --include \
         --header "Gitlab-Kas-Api-Request: $KAS_JWT" \
         --header "Authorization: Bearer $AGENT_TOKEN" --header "Content-Type: application/json" \
         --url "http://localhost:3000/api/v4/internal/kubernetes/modules/starboard_vulnerability" \
         --data '{
       "vulnerability": {
         "name": "CVE-123-4567 in libc",
         "severity": "high",
         "confidence": "unknown",
         "location": {
           "image": "index.docker.io/library/nginx:latest",
           "kubernetes_resource": {
             "namespace": "production",
             "kind": "deployment",
             "name": "nginx",
             "container_name": "nginx",
             "agent_id": "2"
           },
           "dependency": {
             "package": {
               "name": "libc"
             },
             "version": "v1.2.3"
           }
         },
         "identifiers": [
           {
             "type": "cve",
             "name": "CVE-123-4567",
             "value": "CVE-123-4567"
           }
         ]
       },
       "scanner": {
         "id": "starboard_trivy",
         "name": "Trivy (via Starboard Operator)",
         "vendor": {
           "name": "GitLab"
         }
       }
     }'
  6. Observe that raw_metadata has a symbolized location:

    [5] pry(main)> project.vulnerabilities.map { |vuln| vuln.finding.raw_metadata }
    => ["{:location=>{\"image\"=>\"index.docker.io/library/nginx:latest\", \"kubernetes_resource\"=>{\"namespace\"=>\"production\", \"kind\"=>\"deployment\", \"name\"=>\"nginx\", \"container_name\"=>\"nginx\", \"agent_id\"=>\"2\"}, \"dependency\"=>{\"package\"=>{\"name\"=>\"libc\"}, \"version\"=>\"v1.2.3\"}}}"]

What is the current bug behavior?

The raw_location is a stringified hash instead of valid JSON.

What is the expected correct behavior?

The raw_location must contain valid JSON.

Possible fixes

We need to call .to_json on this object.

and also purge the invalid data from the database

Edited by Brian Williams