diff --git a/app/models/concerns/security/latest_pipeline_information.rb b/app/models/concerns/security/latest_pipeline_information.rb index 87eae3cac682cefeff972f67dccccad2f4c49ded..d46051e276b55635690e581f6bc97c9ed4365eca 100644 --- a/app/models/concerns/security/latest_pipeline_information.rb +++ b/app/models/concerns/security/latest_pipeline_information.rb @@ -12,12 +12,31 @@ def latest_builds_reports(only_successful_builds: false) strong_memoize("latest_builds_reports_#{only_successful_builds}") do builds = latest_security_builds builds = builds.select { |build| build.status == 'success' } if only_successful_builds - builds.flat_map do |build| + reports = builds.flat_map do |build| build.options[:artifacts][:reports].keys end + + normalize_for_sast_reports(reports, builds) end end + # Because :sast_iac and :sast_advanced reports belong to a report with a name of 'sast', + # we have to do extra checking to determine which reports have been included + def normalize_for_sast_reports(reports, builds) + return reports unless reports.delete(:sast) + + reports.tap do |r| + build_names = builds.map(&:name) + + r.push(:sast_iac) if build_names.delete('kics-iac-sast') + + # When using adavanced sast, sast should also show in the report names + r.push(:sast, :sast_advanced) if build_names.delete('gitlab-advanced-sast') + + r.push(:sast) if build_names.any? { |name| name.include? '-sast' } + end.uniq + end + def latest_security_builds return [] unless latest_default_branch_pipeline diff --git a/ee/spec/features/projects/security/user_views_security_configuration_spec.rb b/ee/spec/features/projects/security/user_views_security_configuration_spec.rb index cb7f3bcb1aab893874f8de293987db71292e2f01..199f7c8d9d3ec1d6e1271082826d66d78204769e 100644 --- a/ee/spec/features/projects/security/user_views_security_configuration_spec.rb +++ b/ee/spec/features/projects/security/user_views_security_configuration_spec.rb @@ -42,7 +42,7 @@ context 'with SAST report' do before do - create(:ci_build, :sast, pipeline: pipeline, status: 'success') + create(:ci_build, :sast, name: 'semgrep-sast', pipeline: pipeline, status: 'success') end it 'shows SAST is enabled' do diff --git a/ee/spec/graphql/types/project_type_spec.rb b/ee/spec/graphql/types/project_type_spec.rb index 9fca9853bd59bc01c7c91566d0ca3c44a264392a..b61684c8c073b23c2fdccfa8ec16db27bfb75497 100644 --- a/ee/spec/graphql/types/project_type_spec.rb +++ b/ee/spec/graphql/types/project_type_spec.rb @@ -144,7 +144,7 @@ subject { GitlabSchema.execute(query, context: { current_user: user }).as_json } before do - create(:ci_build, :success, :sast, pipeline: pipeline) + create(:ci_build, :success, :sast, name: "semgrep-sast", pipeline: pipeline) create(:ci_build, :success, :dast, pipeline: pipeline) create(:ci_build, :success, :license_scanning, pipeline: pipeline) create(:ci_build, :pending, :secret_detection, pipeline: pipeline) diff --git a/ee/spec/models/concerns/ee/project_security_scanners_information_spec.rb b/ee/spec/models/concerns/ee/project_security_scanners_information_spec.rb index 731f9ddf31a67e6796c74b7772442fbf4435bb89..9ec99007c8116eba038a3f603fecd2e6fec2d9ba 100644 --- a/ee/spec/models/concerns/ee/project_security_scanners_information_spec.rb +++ b/ee/spec/models/concerns/ee/project_security_scanners_information_spec.rb @@ -7,7 +7,7 @@ let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) } before do - create(:ci_build, :success, :sast, pipeline: pipeline) + create(:ci_build, :success, :sast, name: 'semgrep-sast', pipeline: pipeline) create(:ci_build, :success, :dast, pipeline: pipeline) create(:ci_build, :success, :license_scanning, pipeline: pipeline) create(:ci_build, :pending, :secret_detection, pipeline: pipeline) diff --git a/spec/models/concerns/security/latest_pipeline_information_spec.rb b/spec/models/concerns/security/latest_pipeline_information_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..a805e642358f0c2af0aafdcf84d63b0bd48473c3 --- /dev/null +++ b/spec/models/concerns/security/latest_pipeline_information_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Security::LatestPipelineInformation, feature_category: :secure_artifacts do + subject { my_class.latest_builds_reports } + + let(:my_class) do + Class.new do + include Security::LatestPipelineInformation + include Gitlab::Utils::StrongMemoize + end + end + + let(:instance) { my_class.new } + + let_it_be(:pipeline) { create(:ci_pipeline) } + + describe '#scanner_enabled?' do + it 'returns true if the scan type is included in latest_builds_reports' do + allow(instance).to receive(:latest_builds_reports).and_return([:sast, :dast]) + expect(instance.send(:scanner_enabled?, :sast)).to be true + end + + it 'returns false if the scan type is not included in latest_builds_reports' do + allow(instance).to receive(:latest_builds_reports).and_return([:dast]) + expect(instance.send(:scanner_enabled?, :sast)).to be false + end + end + + describe '#latest_builds_reports' do + let_it_be(:sast_build) { create(:ci_build, :sast, :success, name: "semgrep-sast", pipeline: pipeline) } + let_it_be(:sast_iac_build) { create(:ci_build, :sast, name: "kics-iac-sast", pipeline: pipeline) } + let_it_be(:advanced_sast_build) { create(:ci_build, :sast, name: "gitlab-advanced-sast", pipeline: pipeline) } + let_it_be(:dast_build) { create(:ci_build, :dast, pipeline: pipeline) } + + it 'returns an array of unique reports' do + allow(instance).to receive(:latest_security_builds).and_return([sast_build, advanced_sast_build, dast_build]) + expect(instance.send(:latest_builds_reports)).to match_array([:sast, :sast_advanced, :dast]) + end + + context 'when limiting to successful builds' do + it 'returns an array of reports with only successful builds' do + allow(instance).to receive(:latest_security_builds).and_return([sast_build, sast_iac_build]) + expect(instance.send(:latest_builds_reports, only_successful_builds: true)).to match_array([:sast]) + end + end + + describe 'sast jobs' do + it 'does not include :sast when there are no sast related jobs' do + allow(instance).to receive(:latest_security_builds).and_return([dast_build]) + expect(instance.send(:latest_builds_reports)).to match_array([:dast]) + end + + it 'includes :sast when the only job is :sast_advanced' do + allow(instance).to receive(:latest_security_builds).and_return([advanced_sast_build]) + expect(instance.send(:latest_builds_reports)).to match_array([:sast, :sast_advanced]) + end + + it 'does not include :sast when the only job is :sast_iac' do + allow(instance).to receive(:latest_security_builds).and_return([sast_iac_build]) + expect(instance.send(:latest_builds_reports)).to match_array([:sast_iac]) + end + + it 'does not include :sast_iac or :sast_advanced when there are only :sast jobs' do + allow(instance).to receive(:latest_security_builds).and_return([sast_build]) + expect(instance.send(:latest_builds_reports)).to match_array([:sast]) + end + end + end +end diff --git a/spec/presenters/projects/security/configuration_presenter_spec.rb b/spec/presenters/projects/security/configuration_presenter_spec.rb index d8a4be7f380e0c91eccdee8fef3d1ff6b5aa2834..c78602abea08eb5ca26406b87002952ed33c5871 100644 --- a/spec/presenters/projects/security/configuration_presenter_spec.rb +++ b/spec/presenters/projects/security/configuration_presenter_spec.rb @@ -30,7 +30,7 @@ ) end - let!(:build_sast) { create(:ci_build, :sast, pipeline: pipeline) } + let!(:build_sast) { create(:ci_build, :sast, name: 'semgrep-sast', pipeline: pipeline) } let!(:build_dast) { create(:ci_build, :dast, pipeline: pipeline) } let!(:build_license_scanning) { create(:ci_build, :license_scanning, pipeline: pipeline) } @@ -133,7 +133,7 @@ { artifacts: { reports: { other_job: ['gl-other-report.json'], sast: ['gl-sast-report.json'] } } } end - let!(:complicated_job) { build_stubbed(:ci_build, options: artifacts) } + let!(:complicated_job) { build_stubbed(:ci_build, name: 'semgrep-sast', options: artifacts) } before do allow_next_instance_of(::Security::SecurityJobsFinder) do |finder| @@ -230,7 +230,7 @@ ) end - let!(:build_sast) { create(:ci_build, :sast, pipeline: pipeline, status: 'success') } + let!(:build_sast) { create(:ci_build, :sast, name: 'semgrep-sast', pipeline: pipeline, status: 'success') } let!(:build_dast) { create(:ci_build, :dast, pipeline: pipeline, status: 'success') } let!(:ci_build) { create(:ci_build, :secret_detection, pipeline: pipeline, status: 'pending') }