diff --git a/ee/app/assets/javascripts/security_dashboard/components/shared/vulnerability_report/vulnerability_report_header.vue b/ee/app/assets/javascripts/security_dashboard/components/shared/vulnerability_report/vulnerability_report_header.vue index 62ac836d21a104a83cd3256aede1917cf29aaeb0..3c697e2bd2e9abdaf02c5e5652d1158f7674867b 100644 --- a/ee/app/assets/javascripts/security_dashboard/components/shared/vulnerability_report/vulnerability_report_header.vue +++ b/ee/app/assets/javascripts/security_dashboard/components/shared/vulnerability_report/vulnerability_report_header.vue @@ -51,7 +51,10 @@ export default { <gl-button v-if="shouldShowNewVulnerabilityButton" :href="newVulnerabilityPath" icon="plus"> {{ $options.i18n.submitVulnerability }} </gl-button> - <csv-export-button :class="shouldShowNewVulnerabilityButton ? 'gl-ml-4' : 'gl-ml-auto'" /> + <csv-export-button + data-qa-selector="export_vulnerabilities_button" + :class="shouldShowNewVulnerabilityButton ? 'gl-ml-4' : 'gl-ml-auto'" + /> </div> </header> </template> diff --git a/qa/qa/ee/page/project/secure/security_dashboard.rb b/qa/qa/ee/page/project/secure/security_dashboard.rb index a8f4b7b390624eb2a104fb8282695ee2b589440c..e27e1ed8dd7c9604372a9cfaf32144437c915b9d 100644 --- a/qa/qa/ee/page/project/secure/security_dashboard.rb +++ b/qa/qa/ee/page/project/secure/security_dashboard.rb @@ -20,6 +20,10 @@ class SecurityDashboard < QA::Page::Base element :change_status_button end + view 'ee/app/assets/javascripts/security_dashboard/components/shared/vulnerability_report/vulnerability_report_header.vue' do + element :export_vulnerabilities_button + end + def has_vulnerability?(description:) has_element?(:vulnerability, vulnerability_description: description) end @@ -55,6 +59,10 @@ def has_remediated_badge?(vulnerability_name) def has_issue_created_icon?(vulnerability_name) has_element?(:vulnerability_issue_created_badge_content, badge_description: vulnerability_name) end + + def export_vulnerabilities_to_csv + click_element(:export_vulnerabilities_button) + end end end end diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index af1a4e06473753ef86c0e6a5ba9b4fe5edffb056..faf2023f7c2399c8eb59aeaa92eac8e31cea645f 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -96,12 +96,11 @@ def self.configure! capabilities['goog:chromeOptions'][:args] << 'disable-dev-shm-usage' if QA::Runtime::Env.disable_dev_shm? # Set chrome default download path - if QA::Runtime::Env.chrome_default_download_path - capabilities['goog:chromeOptions'][:prefs] = { - 'download.default_directory' => File.expand_path(QA::Runtime::Env.chrome_default_download_path), - 'download.prompt_for_download' => false - } - end + + capabilities['goog:chromeOptions'][:prefs] = { + 'download.default_directory' => File.expand_path(QA::Runtime::Env.chrome_default_download_path), + 'download.prompt_for_download' => false + } # Specify the user-agent to allow challenges to be bypassed # See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938 diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index b2a5890e960f8dfbb892e571218ecfd0be4f10d0..346d40780fd1a4f1f877fb8f017e83c8bca528cf 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -495,7 +495,7 @@ def allow_local_requests? end def chrome_default_download_path - ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] + ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir end private diff --git a/qa/qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb b/qa/qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..8a23caebd0f9b45f490de40f638c986d0041ed23 --- /dev/null +++ b/qa/qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Govern', product_group: :threat_insights do + describe 'Vulnerability report in a project' do + let!(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'project-export-vulnerability-report' + project.description = 'Project to check export vulnerability report feature' + project.initialize_with_readme = true + end + end + + let(:vuln_severity) { :HIGH } + + let(:download_dir) { QA::Runtime::Env.chrome_default_download_path } + + let(:vulnerabilities) do + { "Elves vulnerability": "Pale skin and pointy ears", + "Dwarves vulnerability": "Short, but stern and tough", + "Men vulnerability": "Good and bad, greedy and selfless", + "Orcs vulnerability": "Faithful to the dark lord", + "CVE-2017-18269 in glibc": "Short description to match in specs" } + end + + let!(:vulnerability_report) do + vulnerabilities.each do |name, description| + QA::EE::Resource::VulnerabilityItem.fabricate_via_api! do |vulnerability| + vulnerability.id = project.id + vulnerability.severity = vuln_severity + vulnerability.name = name + vulnerability.description = description + end + end + end + + let(:vulnerability_name) { "CVE-2017-18269 in glibc" } + let(:vulnerability_description) { "Short description to match in specs" } + + before do + Flow::Login.sign_in + project.visit! + end + + it 'can export vulnerability report to csv', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/384370' do + Page::Project::Menu.perform(&:click_on_vulnerability_report) + + EE::Page::Project::Secure::SecurityDashboard.perform do |security_dashboard| + expect(security_dashboard).to have_vulnerability(description: vulnerability_name) + security_dashboard.export_vulnerabilities_to_csv + end + file_name = get_exported_csv_filename + validate_csv(file_name) + end + + def get_exported_csv_filename + csv_file = nil + file_name_glob = "#{project.full_path.split('/').join('-')}_vulnerabilities" + Support::Waiter.wait_until(max_duration: 20, sleep_interval: 1, + message: "Waiting for vulnerabilities csv export") do + csv_file = Dir["#{download_dir}/*"].find { |file| file =~ /#{file_name_glob}.*csv/ } + end + + csv_file + end + + def validate_csv(file_name) + vulnerabilities_from_csv = [] + CSV.foreach(file_name, headers: true) do |row| + vulnerabilities_from_csv << [row['Vulnerability'], row['Details']] + end + expect(vulnerabilities.stringify_keys.to_a).to match_array vulnerabilities_from_csv + end + end + end +end