# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Issues::CreateFromVulnerabilityDataService, '#execute' do
  let(:group)   { create(:group) }
  let(:project) { create(:project, :public, namespace: group) }
  let(:user)    { create(:user) }

  before do
    group.add_developer(user)
  end

  shared_examples 'a created issue' do
    let(:result) { described_class.new(project, user, params).execute }

    it 'creates the issue with the given params' do
      expect(result[:status]).to eq(:success)
      issue = result[:issue]
      expect(issue).to be_persisted
      expect(issue.project).to eq(project)
      expect(issue.author).to eq(user)
      expect(issue.title).to eq(expected_title)
      expect(issue.description).to eq(expected_description)
      expect(issue).to be_confidential
    end
  end

  context 'when user does not have permission to create issue' do
    let(:result) { described_class.new(project, user, {}).execute }

    before do
      allow_next_instance_of(described_class) do |instance|
        allow(instance).to receive(:can?).with(user, :create_issue, project).and_return(false)
      end
    end

    it 'returns expected error' do
      expect(result[:status]).to eq(:error)
      expect(result[:message]).to eq("User is not permitted to create issue")
    end
  end

  context 'when issues are disabled on project' do
    let(:result) { described_class.new(project, user, {}).execute }
    let(:project) { create(:project, :public, namespace: group, issues_access_level: ProjectFeature::DISABLED) }

    it 'returns expected error' do
      expect(result[:status]).to eq(:error)
      expect(result[:message]).to eq("User is not permitted to create issue")
    end
  end

  context 'when params are valid' do
    context 'when category is SAST' do
      context 'when a description is present' do
        let(:params) do
          {
            blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
            category: 'sast',
            severity: 'Low', confidence: 'High',
            solution: 'Please do something!',
            file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
            line: '15',
            cve: '818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM',
            title: 'Predictable pseudorandom number generator',
            description: 'Description of Predictable pseudorandom number generator',
            tool: 'find_sec_bugs',
            identifiers: [{
              type: 'CVE',
              name: 'CVE-2017-15650',
              value: 'CVE-2017-15650',
              url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650'
            }, {
              type: 'CWE',
              name: 'CWE-16',
              value: '16',
              url: 'https://cwe.mitre.org/data/definitions/16.html'
            }, {
              type: 'GAS_RULE_ID',
              name: 'GAS Rule ID G105',
              value: 'G105'
            }],
            links: [{
              name: 'Awesome-security blog post',
              url: 'https;//example.com/blog-post'
            }, {
              url: 'https://example.com/another-link'
            }],
            scanner: { external_id: 'gemnasium', name: 'Gemnasium' },
            scan: { type: 'dependency_scanning', status: 'success', start_time: 'placeholder', end_time: 'placeholder' }
          }
        end

        let(:expected_title) { 'Investigate vulnerability: Predictable pseudorandom number generator' }
        let(:expected_description) do
          <<~DESC.chomp
            ### Description:

            Description of Predictable pseudorandom number generator

            * Severity: Low
            * Confidence: High
            * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)

            ### Solution:

            Please do something!

            ### Identifiers:

            * [CVE-2017-15650](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650)
            * [CWE-16](https://cwe.mitre.org/data/definitions/16.html)
            * GAS Rule ID G105

            ### Links:

            * [Awesome-security blog post](https;//example.com/blog-post)
            * https://example.com/another-link

            ### Scanner:

            * Name: Gemnasium
            * Type: dependency_scanning
            * Status: success
            * Start Time: placeholder
            * End Time: placeholder
          DESC
        end

        it_behaves_like 'a created issue'
      end

      context 'when a description is NOT present' do
        let(:params) do
          {
            blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
            category: 'sast',
            severity: 'Low', confidence: 'High',
            solution: 'Please do something!',
            file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
            line: '15',
            cve: '818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM',
            title: 'Predictable pseudorandom number generator',
            tool: 'find_sec_bugs',
            scanner: { external_id: 'gemnasium', name: 'Gemnasium' },
            scan: { type: 'dependency_scanning', status: 'success', start_time: 'placeholder', end_time: 'placeholder' }
          }
        end

        let(:expected_title) { 'Investigate vulnerability: Predictable pseudorandom number generator' }
        let(:expected_description) do
          <<~DESC.chomp
            ### Description:

            Predictable pseudorandom number generator

            * Severity: Low
            * Confidence: High
            * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)

            ### Solution:

            Please do something!

            ### Scanner:

            * Name: Gemnasium
            * Type: dependency_scanning
            * Status: success
            * Start Time: placeholder
            * End Time: placeholder
          DESC
        end

        it_behaves_like 'a created issue'
      end

      context 'when scan is NOT present but scanner is' do
        let(:params) do
          {
            blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
            category: 'sast',
            severity: 'Low', confidence: 'High',
            solution: 'Please do something!',
            file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
            line: '15',
            cve: '818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM',
            title: 'Predictable pseudorandom number generator',
            description: 'Description of Predictable pseudorandom number generator',
            tool: 'find_sec_bugs',
            identifiers: [{
              type: 'CVE',
              name: 'CVE-2017-15650',
              value: 'CVE-2017-15650',
              url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650'
            }, {
              type: 'CWE',
              name: 'CWE-16',
              value: '16',
              url: 'https://cwe.mitre.org/data/definitions/16.html'
            }, {
              type: 'GAS_RULE_ID',
              name: 'GAS Rule ID G105',
              value: 'G105'
            }],
            links: [{
              name: 'Awesome-security blog post',
              url: 'https;//example.com/blog-post'
            }, {
              url: 'https://example.com/another-link'
            }],
            scanner: { external_id: 'gemnasium', name: 'Gemnasium' },
            scan: nil
          }
        end

        let(:expected_title) { 'Investigate vulnerability: Predictable pseudorandom number generator' }
        let(:expected_description) do
          <<~DESC.chomp
            ### Description:

            Description of Predictable pseudorandom number generator

            * Severity: Low
            * Confidence: High
            * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)

            ### Solution:

            Please do something!

            ### Identifiers:

            * [CVE-2017-15650](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650)
            * [CWE-16](https://cwe.mitre.org/data/definitions/16.html)
            * GAS Rule ID G105

            ### Links:

            * [Awesome-security blog post](https;//example.com/blog-post)
            * https://example.com/another-link

            ### Scanner:

            * Name: Gemnasium
          DESC
        end

        it_behaves_like 'a created issue'
      end
    end

    context 'when category is dependency scanning' do
      context 'when a description is present' do
        let(:params) do
          {
            blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
            category: 'dependency_scanning',
            severity: 'Low', confidence: 'High',
            solution: 'Please do something!',
            file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
            line: '15',
            cve: '818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM',
            title: 'Predictable pseudorandom number generator',
            description: 'Description of Predictable pseudorandom number generator',
            tool: 'find_sec_bugs',
            identifiers: [{
              type: 'CVE',
              name: 'CVE-2017-15650',
              value: 'CVE-2017-15650',
              url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650'
            }, {
              type: 'CWE',
              name: 'CWE-16',
              value: '16',
              url: 'https://cwe.mitre.org/data/definitions/16.html'
            }, {
              type: 'GAS_RULE_ID',
              name: 'GAS Rule ID G105',
              value: 'G105'
            }],
            links: [{
              name: 'Awesome-security blog post',
              url: 'https;//example.com/blog-post'
            }, {
              url: 'https://example.com/another-link'
            }]
          }
        end

        let(:expected_title) { 'Investigate vulnerability: Predictable pseudorandom number generator' }
        let(:expected_description) do
          <<~DESC.chomp
            ### Description:

            Description of Predictable pseudorandom number generator

            * Severity: Low
            * Confidence: High
            * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)

            ### Solution:

            Please do something!

            ### Identifiers:

            * [CVE-2017-15650](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650)
            * [CWE-16](https://cwe.mitre.org/data/definitions/16.html)
            * GAS Rule ID G105

            ### Links:

            * [Awesome-security blog post](https;//example.com/blog-post)
            * https://example.com/another-link
          DESC
        end

        it_behaves_like 'a created issue'
      end

      context 'when a description is NOT present' do
        let(:params) do
          {
            blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
            category: 'dependency_scanning',
            priority: 'Low', line: '41',
            severity: 'Low', confidence: 'High',
            solution: 'Please do something!',
            file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
            cve: '818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM',
            title: 'Predictable pseudorandom number generator',
            tool: 'find_sec_bugs'
          }
        end

        let(:expected_title) { 'Investigate vulnerability: Predictable pseudorandom number generator' }
        let(:expected_description) do
          <<~DESC.chomp
            ### Description:

            Predictable pseudorandom number generator

            * Severity: Low
            * Confidence: High
            * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:41](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)

            ### Solution:

            Please do something!
          DESC
        end

        it_behaves_like 'a created issue'
      end
    end

    context 'when category is container scanning' do
      context 'when a description is present' do
        let(:params) do
          {
            category: 'container_scanning',
            priority: 'Low',
            severity: 'Low',
            namespace: 'alpine:v3.4',
            featurename: 'musl',
            featureversion: '1.1.14-r15',
            fixedby: '1.1.14-r16',
            title: 'CVE-2017-15650',
            vulnerability: 'CVE-2017-15650',
            description: 'This is a description for CVE-2017-15650.',
            tool: 'find_sec_bugs',
            identifiers: [{
              type: 'CVE',
              name: 'CVE-2017-15650',
              value: 'CVE-2017-15650',
              url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650'
            }]
          }
        end

        let(:expected_title) { 'Investigate vulnerability: CVE-2017-15650 in musl' }
        let(:expected_description) do
          <<~DESC.chomp
            ### Description:

            This is a description for CVE-2017-15650.

            * Severity: Low

            ### Solution:

            Upgrade **musl** from `1.1.14-r15` to `1.1.14-r16`

            ### Identifiers:

            * [CVE-2017-15650](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650)
          DESC
        end

        it_behaves_like 'a created issue'
      end
      context 'when a description is NOT present' do
        let(:params) do
          {
            category: 'container_scanning',
            priority: 'Low',
            severity: 'Low',
            namespace: 'alpine:v3.4',
            featurename: 'musl',
            featureversion: '1.1.14-r15',
            fixedby: '1.1.14-r16',
            title: 'CVE-2017-15650',
            vulnerability: 'CVE-2017-15650',
            description: '',
            tool: 'find_sec_bugs',
            identifiers: [{
              type: 'CVE',
              name: 'CVE-2017-15650',
              value: 'CVE-2017-15650',
              url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650'
            }]
          }
        end

        let(:expected_title) { 'Investigate vulnerability: CVE-2017-15650 in musl' }
        let(:expected_description) do
          <<~DESC.chomp
            ### Description:

            **musl** `1.1.14-r15` is affected by CVE-2017-15650

            * Severity: Low

            ### Solution:

            Upgrade **musl** from `1.1.14-r15` to `1.1.14-r16`

            ### Identifiers:

            * [CVE-2017-15650](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650)
          DESC
        end

        it_behaves_like 'a created issue'
      end
    end

    context 'when category is DAST' do
      let(:params) do
        {
          category: 'dast',
          priority: 'Low',
          severity: 'Low',
          title: 'X-Content-Type-Options Header Missing',
          desc: '<p>The Anti-MIME-Sniffing header X-Content-Type-Options was not set to nosniff.</p>',
          description: 'The Anti-MIME-Sniffing header X-Content-Type-Options was not set to nosniff.',
          cweid: '123',
          wascid: '456',
          solution: 'Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to nosniff for all web pages.',
          identifiers: [{
            type: 'CWE',
            name: 'CWE-123',
            value: '123',
            url: 'https://cwe.mitre.org/data/definitions/123.html'
          }, {
            type: 'WASC',
            name: 'WASC-456',
            value: '456'
          }]
        }
      end

      let(:expected_title) { 'Investigate vulnerability: X-Content-Type-Options Header Missing' }
      let(:expected_description) do
        <<~DESC.chomp
          ### Description:

          The Anti-MIME-Sniffing header X-Content-Type-Options was not set to nosniff.

          * Severity: Low

          ### Solution:

          Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to nosniff for all web pages.

          ### Identifiers:

          * [CWE-123](https://cwe.mitre.org/data/definitions/123.html)
          * WASC-456
        DESC
      end

      it_behaves_like 'a created issue'
    end
  end

  context 'when params are invalid' do
    context 'when category is unknown' do
      let(:params) { { category: 'foo' } }
      let(:result) { described_class.new(project, user, params).execute }

      it 'return expected error' do
        expect(result[:status]).to eq(:error)
        expect(result[:message]).to eq('Invalid vulnerability category')
      end
    end

    context 'when category is missing' do
      let(:params) { {} }
      let(:result) { described_class.new(project, user, params).execute }

      it 'return expected error' do
        expect(result[:status]).to eq(:error)
        expect(result[:message]).to eq('Invalid vulnerability category')
      end
    end
  end
end
