From 43c39fa47e7d77c83ada46f8429db23cfcb2f87e Mon Sep 17 00:00:00 2001
From: mo khan <mo@mokhan.ca>
Date: Sat, 4 Nov 2023 05:14:14 +0000
Subject: [PATCH] Add custom role spec for `Mutation.vulnerabilityCreate`

---
 ee/spec/factories/member_roles.rb             |   1 -
 .../mutations/vulnerabilities/confirm_spec.rb |   2 +-
 .../mutations/vulnerabilities/create_spec.rb  | 159 ++++++++++++++++++
 .../mutations/vulnerabilities/resolve_spec.rb |   2 +-
 .../admin_vulnerability/request_spec.rb       |  52 ++++--
 spec/support/helpers/graphql_helpers.rb       |   1 +
 6 files changed, 201 insertions(+), 16 deletions(-)
 create mode 100644 ee/spec/requests/api/graphql/mutations/vulnerabilities/create_spec.rb

diff --git a/ee/spec/factories/member_roles.rb b/ee/spec/factories/member_roles.rb
index 0f52f0278b288..9132fde33640a 100644
--- a/ee/spec/factories/member_roles.rb
+++ b/ee/spec/factories/member_roles.rb
@@ -10,7 +10,6 @@
     trait(:guest) { base_access_level { Gitlab::Access::GUEST } }
 
     trait :admin_vulnerability do
-      guest
       admin_vulnerability { true }
       read_vulnerability { true }
     end
diff --git a/ee/spec/requests/api/graphql/mutations/vulnerabilities/confirm_spec.rb b/ee/spec/requests/api/graphql/mutations/vulnerabilities/confirm_spec.rb
index 91e6aa73d6412..6eb7e17947c1f 100644
--- a/ee/spec/requests/api/graphql/mutations/vulnerabilities/confirm_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/vulnerabilities/confirm_spec.rb
@@ -27,7 +27,7 @@
     end
 
     context "with `admin_vulnerability` enabled" do
-      let(:role) { create(:member_role, :admin_vulnerability, namespace: project.group) }
+      let(:role) { create(:member_role, :guest, :admin_vulnerability, namespace: project.group) }
 
       it "returns a successful response" do
         post_graphql_mutation(mutation, current_user: current_user)
diff --git a/ee/spec/requests/api/graphql/mutations/vulnerabilities/create_spec.rb b/ee/spec/requests/api/graphql/mutations/vulnerabilities/create_spec.rb
new file mode 100644
index 0000000000000..ca1dc83bb15e7
--- /dev/null
+++ b/ee/spec/requests/api/graphql/mutations/vulnerabilities/create_spec.rb
@@ -0,0 +1,159 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe "Mutation.vulnerabilityCreate", feature_category: :vulnerability_management do
+  include GraphqlHelpers
+
+  subject(:mutation) { graphql_mutation(:vulnerability_create, arguments) }
+
+  let_it_be(:current_user) { create(:user) }
+  let_it_be(:project) { create(:project, :in_group) }
+  let(:arguments) do
+    {
+      project: project.to_global_id,
+      name: "Test vulnerability",
+      description: "Test vulnerability created via GraphQL",
+      scanner: {
+        id: "my-custom-scanner",
+        name: "My Custom Scanner",
+        url: "https://superscanner.com",
+        vendor: { name: "Custom Scanner Vendor" },
+        version: "21.37.00"
+      },
+      identifiers: [{
+        name: "Test identifier",
+        url: "https://vulnerabilities.com/test"
+      }],
+      state: "DETECTED",
+      severity: "UNKNOWN",
+      confidence: "UNKNOWN",
+      solution: "rm -rf --no-preserve-root /",
+      message: "You can't fix this"
+    }
+  end
+
+  let(:mutation_response) { graphql_mutation_response(:vulnerability_create) }
+
+  context "with a Maintainer role" do
+    let(:at) { Time.new(2020, 6, 21, 14, 22, 20) }
+
+    before_all do
+      project.add_maintainer(current_user)
+    end
+
+    before do
+      stub_licensed_features(security_dashboard: true)
+    end
+
+    it "returns a successful response" do
+      post_graphql_mutation(mutation, current_user: current_user)
+
+      expect(response).to have_gitlab_http_status(:success)
+      expect(mutation_response["vulnerability"]).to be_present
+      expect(mutation_response["vulnerability"]["state"]).to eq("DETECTED")
+      expect(mutation_response["vulnerability"]["description"]).to eq(arguments[:description])
+      expect(mutation_response["vulnerability"]["solution"]).to eq(arguments[:solution])
+      expect(mutation_response["errors"]).to be_empty
+    end
+
+    context "when confirming a vulnerability" do
+      let(:arguments) { super().merge(state: "CONFIRMED", confirmed_at: at) }
+
+      it "returns a successful response" do
+        post_graphql_mutation(mutation, current_user: current_user)
+
+        expect(response).to have_gitlab_http_status(:success)
+        expect(mutation_response["vulnerability"]).to be_present
+        expect(mutation_response["vulnerability"]["state"]).to eq("CONFIRMED")
+        expect(mutation_response["vulnerability"]["confirmedAt"]).to eq(at.utc.iso8601)
+        expect(mutation_response.dig("vulnerability", "confirmedBy", "id")).to eq(current_user.to_global_id.to_s)
+        expect(mutation_response["errors"]).to be_empty
+      end
+    end
+
+    context "when resolving a vulnerability" do
+      let(:arguments) { super().merge(state: "RESOLVED", resolved_at: at) }
+
+      it "returns a successful response" do
+        post_graphql_mutation(mutation, current_user: current_user)
+
+        expect(response).to have_gitlab_http_status(:success)
+        expect(mutation_response["vulnerability"]).to be_present
+        expect(mutation_response["vulnerability"]["state"]).to eq("RESOLVED")
+        expect(mutation_response["vulnerability"]["resolvedAt"]).to eq(at.utc.iso8601)
+        expect(mutation_response.dig("vulnerability", "resolvedBy", "id")).to eq(current_user.to_global_id.to_s)
+        expect(mutation_response["errors"]).to be_empty
+      end
+    end
+
+    context "when dismissing a vulnerability" do
+      let(:arguments) { super().merge(state: "DISMISSED", dismissed_at: at) }
+
+      it "returns a successful response" do
+        post_graphql_mutation(mutation, current_user: current_user)
+
+        expect(response).to have_gitlab_http_status(:success)
+        expect(mutation_response["vulnerability"]).to be_present
+        expect(mutation_response["vulnerability"]["state"]).to eq("DISMISSED")
+        expect(mutation_response["vulnerability"]["dismissedAt"]).to eq(at.utc.iso8601)
+        expect(mutation_response.dig("vulnerability", "dismissedBy", "id")).to eq(current_user.to_global_id.to_s)
+        expect(mutation_response["errors"]).to be_empty
+      end
+    end
+  end
+
+  context "with an unauthorized role" do
+    before_all do
+      project.add_guest(current_user)
+    end
+
+    before do
+      stub_licensed_features(security_dashboard: true)
+    end
+
+    it "returns an empty response" do
+      post_graphql_mutation(mutation, current_user: current_user)
+
+      expect(response).to have_gitlab_http_status(:success)
+      expect(mutation_response).to be_blank
+    end
+
+    it "does not create a new vulnerability" do
+      expect do
+        post_graphql_mutation(mutation, current_user: current_user)
+      end.not_to change { Vulnerability.count }
+    end
+  end
+
+  context "with a custom role" do
+    let!(:membership) { create(:project_member, :guest, user: current_user, source: project, member_role: role) }
+
+    before do
+      stub_licensed_features(security_dashboard: true, custom_roles: true)
+    end
+
+    context "with `admin_vulnerability` enabled" do
+      let(:role) { create(:member_role, :guest, :admin_vulnerability, namespace: project.group) }
+
+      it "returns a successful response" do
+        post_graphql_mutation(mutation, current_user: current_user)
+
+        expect(response).to have_gitlab_http_status(:success)
+        expect(mutation_response["vulnerability"]).to be_present
+        expect(mutation_response["errors"]).to be_empty
+      end
+    end
+
+    context "with `admin_vulnerability` disabled" do
+      let(:role) { create(:member_role, :guest, namespace: project.group) }
+
+      it "returns an empty response" do
+        post_graphql_mutation(mutation, current_user: current_user)
+
+        expect(response).to have_gitlab_http_status(:success)
+        expect(mutation_response).to be_nil
+      end
+    end
+  end
+end
diff --git a/ee/spec/requests/api/graphql/mutations/vulnerabilities/resolve_spec.rb b/ee/spec/requests/api/graphql/mutations/vulnerabilities/resolve_spec.rb
index a62894e5b636c..9ec8a5d9edd94 100644
--- a/ee/spec/requests/api/graphql/mutations/vulnerabilities/resolve_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/vulnerabilities/resolve_spec.rb
@@ -26,7 +26,7 @@
     end
 
     context "with `admin_vulnerability` enabled" do
-      let(:role) { create(:member_role, :admin_vulnerability, namespace: project.group) }
+      let(:role) { create(:member_role, :guest, :admin_vulnerability, namespace: project.group) }
 
       it "returns a successful response" do
         post_graphql_mutation(mutation, current_user: current_user)
diff --git a/ee/spec/requests/custom_roles/admin_vulnerability/request_spec.rb b/ee/spec/requests/custom_roles/admin_vulnerability/request_spec.rb
index 29eb7bb8ee025..4e834dbe6e4ed 100644
--- a/ee/spec/requests/custom_roles/admin_vulnerability/request_spec.rb
+++ b/ee/spec/requests/custom_roles/admin_vulnerability/request_spec.rb
@@ -5,25 +5,18 @@
 RSpec.describe 'User with admin_vulnerability custom role', feature_category: :system_access do
   let_it_be(:user) { create(:user) }
   let_it_be(:project) { create(:project, :repository, :in_group) }
-  let_it_be(:vulnerability) { create(:vulnerability, :with_finding, project: project) }
+  let_it_be(:role) { create(:member_role, :guest, :admin_vulnerability, namespace: project.group) }
+  let_it_be(:membership) { create(:group_member, :guest, user: user, source: project.group, member_role: role) }
 
   before do
     stub_licensed_features(custom_roles: true, security_dashboard: true)
-
-    group_member = create(:group_member, :guest, user: user, source: project.group)
-    create(
-      :member_role,
-      :guest,
-      admin_vulnerability: true,
-      read_code: false,
-      read_vulnerability: true,
-      members: [group_member],
-      namespace: project.group
-    )
-    sign_in(user)
   end
 
   describe Projects::Security::VulnerabilitiesController do
+    before do
+      sign_in(user)
+    end
+
     describe "#new" do
       it 'user has access via a custom role' do
         get new_project_security_vulnerability_path(project)
@@ -35,4 +28,37 @@
       end
     end
   end
+
+  describe Mutations::Vulnerabilities::Create do
+    include GraphqlHelpers
+
+    it "has access via a custom role" do
+      post_graphql_mutation(graphql_mutation(:vulnerability_create, {
+        project: project.to_global_id,
+        name: "example",
+        description: "example",
+        scanner: {
+          id: "my-custom-scanner",
+          name: "example",
+          url: "https://example.org",
+          vendor: { name: "example" },
+          version: "1.0.0"
+        },
+        identifiers: [{
+          name: "example",
+          url: "https://example.org/example"
+        }],
+        state: "DETECTED",
+        severity: "UNKNOWN",
+        confidence: "UNKNOWN",
+        solution: "curl -s 'https://unpkg.com/emoji.json@13.1.0/emoji.json' | jq -r '.[] | .char'",
+        message: "example"
+      }), current_user: user)
+
+      expect(response).to have_gitlab_http_status(:success)
+      mutation_response = graphql_mutation_response(:vulnerability_create)
+      expect(mutation_response["vulnerability"]).to be_present
+      expect(mutation_response["errors"]).to be_empty
+    end
+  end
 end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index 5eba982e3da70..085340d6cb96c 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -2,6 +2,7 @@
 
 module GraphqlHelpers
   def self.included(base)
+    base.include(::ApiHelpers)
     base.include(::Gitlab::Graphql::Laziness)
   end
 
-- 
GitLab