Commit 5f17c0e6 authored by Tetiana Chupryna's avatar Tetiana Chupryna Committed by Matthias Käppler

Move Security ConfigurationPresenter to FOSS

As SAST and SecretDetection features are moved to GitLab Free
We need to migrate some parts of the codebase to FOSS.
This MR move ConfigurationPresenter and related code.
That enable us to create proper security configuration
for GitLab Free users.

Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/342135
parent 6f84455c
...@@ -468,7 +468,6 @@ RSpec/AnyInstanceOf: ...@@ -468,7 +468,6 @@ RSpec/AnyInstanceOf:
- 'ee/spec/models/project_import_state_spec.rb' - 'ee/spec/models/project_import_state_spec.rb'
- 'ee/spec/models/push_rule_spec.rb' - 'ee/spec/models/push_rule_spec.rb'
- 'ee/spec/presenters/ci/pipeline_presenter_spec.rb' - 'ee/spec/presenters/ci/pipeline_presenter_spec.rb'
- 'ee/spec/presenters/projects/security/configuration_presenter_spec.rb'
- 'ee/spec/requests/api/geo_nodes_spec.rb' - 'ee/spec/requests/api/geo_nodes_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/dast_on_demand_scans/create_spec.rb' - 'ee/spec/requests/api/graphql/mutations/dast_on_demand_scans/create_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb' - 'ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb'
......
...@@ -3,11 +3,10 @@ ...@@ -3,11 +3,10 @@
module Projects module Projects
module Security module Security
class ConfigurationPresenter < Gitlab::View::Presenter::Delegated class ConfigurationPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
include AutoDevopsHelper include AutoDevopsHelper
include ::Security::LatestPipelineInformation include ::Security::LatestPipelineInformation
delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate delegator_override_with Gitlab::Utils::StrongMemoize
presents ::Project, as: :project presents ::Project, as: :project
...@@ -20,14 +19,14 @@ module Projects ...@@ -20,14 +19,14 @@ module Projects
features: features, features: features,
help_page_path: help_page_path('user/application_security/index'), help_page_path: help_page_path('user/application_security/index'),
latest_pipeline_path: latest_pipeline_path, latest_pipeline_path: latest_pipeline_path,
auto_fix_enabled: autofix_enabled,
can_toggle_auto_fix_settings: auto_fix_permission,
# TODO: gitlab_ci_present will incorrectly report `false` if the CI/CD configuration file name # TODO: gitlab_ci_present will incorrectly report `false` if the CI/CD configuration file name
# has been customized and a file with the given custom name exists in the repo. This edge case # has been customized and a file with the given custom name exists in the repo. This edge case
# will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/342465 # will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/342465
gitlab_ci_present: project.repository.gitlab_ci_yml.present?, gitlab_ci_present: project.repository.gitlab_ci_yml.present?,
gitlab_ci_history_path: gitlab_ci_history_path, gitlab_ci_history_path: gitlab_ci_history_path,
auto_fix_user_path: '/' # TODO: real link will be updated with https://gitlab.com/gitlab-org/gitlab/-/issues/215669 auto_fix_enabled: autofix_enabled,
can_toggle_auto_fix_settings: can_toggle_autofix,
auto_fix_user_path: auto_fix_user_path
} }
end end
...@@ -41,12 +40,9 @@ module Projects ...@@ -41,12 +40,9 @@ module Projects
private private
def autofix_enabled def autofix_enabled; end
{
dependency_scanning: project_settings&.auto_fix_dependency_scanning, def auto_fix_user_path; end
container_scanning: project_settings&.auto_fix_container_scanning
}
end
def can_enable_auto_devops? def can_enable_auto_devops?
feature_available?(:builds, current_user) && feature_available?(:builds, current_user) &&
...@@ -54,11 +50,13 @@ module Projects ...@@ -54,11 +50,13 @@ module Projects
!archived? !archived?
end end
def can_toggle_autofix; end
def gitlab_ci_history_path def gitlab_ci_history_path
return '' if project.empty_repo? return '' if project.empty_repo?
gitlab_ci = Gitlab::FileDetector::PATTERNS[:gitlab_ci] gitlab_ci = ::Gitlab::FileDetector::PATTERNS[:gitlab_ci]
Gitlab::Routing.url_helpers.project_blame_path(project, File.join(project.default_branch_or_main, gitlab_ci)) ::Gitlab::Routing.url_helpers.project_blame_path(project, File.join(project.default_branch_or_main, gitlab_ci))
end end
def features def features
...@@ -78,11 +76,13 @@ module Projects ...@@ -78,11 +76,13 @@ module Projects
end end
def scan(type, configured: false) def scan(type, configured: false)
scan = ::Gitlab::Security::ScanConfiguration.new(project: project, type: type, configured: configured)
{ {
type: type, type: scan.type,
configured: configured, configured: scan.configured?,
configuration_path: configuration_path(type), configuration_path: scan.configuration_path,
available: feature_available(type) available: scan.available?
} }
end end
...@@ -93,23 +93,8 @@ module Projects ...@@ -93,23 +93,8 @@ module Projects
def project_settings def project_settings
project.security_setting project.security_setting
end end
def configuration_path(type)
{
sast: project_security_configuration_sast_path(project),
dast: project_security_configuration_dast_path(project),
dast_profiles: project_security_configuration_dast_scans_path(project),
api_fuzzing: project_security_configuration_api_fuzzing_path(project),
corpus_management: (project_security_configuration_corpus_management_path(project) if ::Feature.enabled?(:corpus_management, project, default_enabled: :yaml) && scanner_enabled?(:coverage_fuzzing))
}[type]
end
def feature_available(type)
# SAST and Secret Detection are always available, but this isn't
# reflected by our license model yet.
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/333113
%w[sast secret_detection].include?(type) || project.licensed_feature_available?(type)
end
end end
end end
end end
Projects::Security::ConfigurationPresenter.prepend_mod_with('Projects::Security::ConfigurationPresenter')
# frozen_string_literal: true
module EE
module Projects
module Security
module ConfigurationPresenter
private
def can_toggle_autofix
try(:auto_fix_permission)
end
def autofix_enabled
{
dependency_scanning: project_settings&.auto_fix_dependency_scanning,
container_scanning: project_settings&.auto_fix_container_scanning
}
end
def auto_fix_user_path
'/' # TODO: real link will be updated with https://gitlab.com/gitlab-org/gitlab/-/issues/215669
end
end
end
end
end
# frozen_string_literal: true
module EE
module Gitlab
module Security
module ScanConfiguration
def available?
super || project.licensed_feature_available?(type)
end
def configured?
configured
end
def configuration_path
configurable_scans[type] if available? || type == :corpus_management
end
private
def configurable_scans
strong_memoize(:configurable_scans) do
{
dast: project_security_configuration_dast_path(project),
dast_profiles: project_security_configuration_dast_scans_path(project),
api_fuzzing: project_security_configuration_api_fuzzing_path(project),
corpus_management: (project_security_configuration_corpus_management_path(project) if ::Feature.enabled?(:corpus_management, project, default_enabled: :yaml))
}.merge(super)
end
end
end
end
end
end
...@@ -39,7 +39,7 @@ module Gitlab ...@@ -39,7 +39,7 @@ module Gitlab
# The record hasn't been loaded yet, so # The record hasn't been loaded yet, so
# hit the database with all pending IDs to prevent N+1 # hit the database with all pending IDs to prevent N+1
profiles_by_project_id = @lazy_state[:dast_pending_profiles].group_by(&:project_id) profiles_by_project_id = @lazy_state[:dast_pending_profiles].group_by(&:project_id)
policy_configurations = Security::OrchestrationPolicyConfiguration.for_project(profiles_by_project_id.keys).index_by(&:project_id) policy_configurations = ::Security::OrchestrationPolicyConfiguration.for_project(profiles_by_project_id.keys).index_by(&:project_id)
profiles_by_project_id.each do |project_id, dast_pending_profiles| profiles_by_project_id.each do |project_id, dast_pending_profiles|
dast_pending_profiles.each do |profile| dast_pending_profiles.each do |profile|
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Gitlab::Security::ScanConfiguration do
let_it_be(:project) { create(:project, :repository) }
let(:scan) { described_class.new(project: project, type: type, configured: configured) }
describe '#available?' do
subject { scan.available? }
let(:configured) { true }
context 'with a core scanner' do
let(:type) { :sast }
before do
stub_licensed_features(sast: false)
end
it { is_expected.to be_truthy }
end
context 'with licensed scanner that is available' do
let(:type) { :api_fuzzing }
before do
stub_licensed_features(api_fuzzing: true)
end
it { is_expected.to be_truthy }
end
context 'with licensed scanner that is not available' do
let(:type) { :api_fuzzing }
before do
stub_licensed_features(api_fuzzing: false)
end
it { is_expected.to be_falsey }
end
context 'with custom scanner' do
let(:type) { :my_scanner }
it { is_expected.to be_falsey }
end
end
describe '#configuration_path' do
subject { scan.configuration_path }
let(:configured) { true }
context 'with licensed scanner' do
let(:type) { :dast }
let(:configuration_path) { "/#{project.namespace.path}/#{project.name}/-/security/configuration/dast" }
before do
stub_licensed_features(dast: true)
end
it { is_expected.to eq(configuration_path) }
end
context 'with a scanner under feature flag' do
let(:type) { :corpus_management }
let(:configuration_path) { "/#{project.namespace.path}/#{project.name}/-/security/configuration/corpus_management" }
it { is_expected.to eq(configuration_path) }
context 'when feature flag is disabled' do
before do
stub_feature_flags(corpus_management: false)
end
it { is_expected.to be_nil }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::ConfigurationPresenter do
include Gitlab::Routing.url_helpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
describe '#to_h' do
subject(:result) { described_class.new(project, auto_fix_permission: true, current_user: current_user).to_h }
it 'includes settings for auto_fix feature' do
auto_fix = result[:auto_fix_enabled]
expect(auto_fix[:dependency_scanning]).to be_truthy
expect(auto_fix[:container_scanning]).to be_truthy
end
it 'reports auto_fix permissions' do
expect(result[:can_toggle_auto_fix_settings]).to be_truthy
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::ConfigurationPresenter do
include Gitlab::Routing.url_helpers
let(:project) { create(:project, :repository) }
let(:project_with_no_repo) { create(:project) }
let(:current_user) { create(:user) }
it 'presents the given project' do
presenter = described_class.new(project)
expect(presenter.id).to be(project.id)
end
before do
project.add_maintainer(current_user)
stub_licensed_features(licensed_scan_types.to_h { |type| [type, true] })
end
describe '#to_h' do
subject { described_class.new(project, auto_fix_permission: true, current_user: current_user).to_html_data_attribute }
it 'includes links to auto devops and secure product docs' do
expect(subject[:auto_devops_help_page_path]).to eq(help_page_path('topics/autodevops/index'))
expect(subject[:help_page_path]).to eq(help_page_path('user/application_security/index'))
end
it 'includes settings for auto_fix feature' do
auto_fix = Gitlab::Json.parse(subject[:auto_fix_enabled])
expect(auto_fix['dependency_scanning']).to be_truthy
expect(auto_fix['container_scanning']).to be_truthy
end
it 'includes the path to gitlab_ci history' do
expect(subject[:gitlab_ci_history_path]).to eq(project_blame_path(project, 'master/.gitlab-ci.yml'))
end
context 'when the project is empty' do
subject { described_class.new(project_with_no_repo, auto_fix_permission: true, current_user: current_user).to_html_data_attribute }
it 'includes a blank gitlab_ci history path' do
expect(subject[:gitlab_ci_history_path]).to eq('')
end
end
context 'when the project has no default branch set' do
before do
allow(project).to receive(:default_branch).and_return(nil)
end
it 'includes the path to gitlab_ci history' do
expect(subject[:gitlab_ci_history_path]).to eq(project_blame_path(project, 'master/.gitlab-ci.yml'))
end
end
context "when the latest default branch pipeline's source is auto devops" do
before do
pipeline = create(
:ci_pipeline,
:auto_devops_source,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
create(:ci_build, :sast, pipeline: pipeline, status: 'success')
create(:ci_build, :dast, pipeline: pipeline, status: 'success')
create(:ci_build, :secret_detection, pipeline: pipeline, status: 'pending')
end
it 'reports that auto devops is enabled' do
expect(subject[:auto_devops_enabled]).to be_truthy
end
it 'reports auto_fix permissions' do
expect(subject[:can_toggle_auto_fix_settings]).to be_truthy
end
it 'reports that all scanners are configured for which latest pipeline has builds' do
expect(Gitlab::Json.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: true),
security_scan(:sast, configured: true),
security_scan(:sast_iac, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:cluster_image_scanning, configured: false),
security_scan(:dependency_scanning, configured: false),
security_scan(:license_scanning, configured: false),
security_scan(:secret_detection, configured: true),
security_scan(:coverage_fuzzing, configured: false),
security_scan(:api_fuzzing, configured: false),
security_scan(:dast_profiles, configured: true),
security_scan(:corpus_management, configured: true)
)
end
end
context "when coverage fuzzing has run in a pipeline with feature flag off" do
before do
stub_feature_flags(corpus_management: false)
pipeline = create(
:ci_pipeline,
:auto_devops_source,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
create(:ci_build, :coverage_fuzzing, pipeline: pipeline, status: 'success')
end
it 'reports that coverage fuzzing, corpus management, and DAST are configured' do
expect(Gitlab::Json.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: false),
security_scan(:sast, configured: false),
security_scan(:sast_iac, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:cluster_image_scanning, configured: false),
security_scan(:dependency_scanning, configured: false),
security_scan(:license_scanning, configured: false),
security_scan(:secret_detection, configured: false),
security_scan(:coverage_fuzzing, configured: true),
security_scan(:api_fuzzing, configured: false),
security_scan(:dast_profiles, configured: true),
security_scan(:corpus_management, configured: true)
)
end
end
context "when coverage fuzzing has run in a pipeline with feature flag on" do
before do
stub_feature_flags(corpus_management: true)
pipeline = create(
:ci_pipeline,
:auto_devops_source,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
create(:ci_build, :coverage_fuzzing, pipeline: pipeline, status: 'success')
end
it 'reports that coverage fuzzing, corpus management, and DAST are configured' do
expect(Gitlab::Json.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: false),
security_scan(:sast, configured: false),
security_scan(:sast_iac, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:cluster_image_scanning, configured: false),
security_scan(:dependency_scanning, configured: false),
security_scan(:license_scanning, configured: false),
security_scan(:secret_detection, configured: false),
security_scan(:coverage_fuzzing, configured: true),
security_scan(:api_fuzzing, configured: false),
security_scan(:dast_profiles, configured: true),
security_scan(:corpus_management, configured: true, configuration_path: project_security_configuration_corpus_management_path(project))
)
end
end
context 'when the project has no default branch pipeline' do
it 'reports that auto devops is disabled' do
expect(subject[:auto_devops_enabled]).to be_falsy
end
it 'includes a link to CI pipeline docs' do
expect(subject[:latest_pipeline_path]).to eq(help_page_path('ci/pipelines'))
end
it 'reports all security jobs as unconfigured' do
expect(Gitlab::Json.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: false),
security_scan(:sast, configured: false),
security_scan(:sast_iac, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:cluster_image_scanning, configured: false),
security_scan(:dependency_scanning, configured: false),
security_scan(:license_scanning, configured: false),
security_scan(:secret_detection, configured: false),
security_scan(:coverage_fuzzing, configured: false),
security_scan(:api_fuzzing, configured: false),
security_scan(:dast_profiles, configured: true),
security_scan(:corpus_management, configured: true)
)
end
end
context 'when latest default branch pipeline`s source is not auto devops' do
let(:pipeline) do
create(
:ci_pipeline,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
end
before do
create(:ci_build, :sast, pipeline: pipeline)
create(:ci_build, :dast, pipeline: pipeline)
create(:ci_build, :secret_detection, pipeline: pipeline)
end
it 'uses the latest default branch pipeline to determine whether a security job is configured' do
expect(Gitlab::Json.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: true),
security_scan(:dast_profiles, configured: true),
security_scan(:sast, configured: true),
security_scan(:sast_iac, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:cluster_image_scanning, configured: false),
security_scan(:dependency_scanning, configured: false),
security_scan(:license_scanning, configured: false),
security_scan(:secret_detection, configured: true),
security_scan(:coverage_fuzzing, configured: false),
security_scan(:api_fuzzing, configured: false),
security_scan(:corpus_management, configured: true)
)
end
it 'detects security jobs even when the job has more than one report' do
config = { artifacts: { reports: { other_job: ['gl-other-report.json'], sast: ['gl-sast-report.json'] } } }
complicated_job = build_stubbed(:ci_build, options: config)
allow_next_instance_of(::Security::SecurityJobsFinder) do |finder|
allow(finder).to receive(:execute).and_return([complicated_job])
end
subject
expect(Gitlab::Json.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: false),
security_scan(:dast_profiles, configured: true),
security_scan(:sast, configured: true),
security_scan(:sast_iac, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:cluster_image_scanning, configured: false),
security_scan(:dependency_scanning, configured: false),
security_scan(:license_scanning, configured: false),
security_scan(:secret_detection, configured: false),
security_scan(:coverage_fuzzing, configured: false),
security_scan(:api_fuzzing, configured: false),
security_scan(:corpus_management, configured: true)
)
end
it 'detect new license compliance job' do
create(:ci_build, :license_scanning, pipeline: pipeline)
expect(Gitlab::Json.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: true),
security_scan(:dast_profiles, configured: true),
security_scan(:sast, configured: true),
security_scan(:sast_iac, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:cluster_image_scanning, configured: false),
security_scan(:dependency_scanning, configured: false),
security_scan(:license_scanning, configured: true),
security_scan(:secret_detection, configured: true),
security_scan(:coverage_fuzzing, configured: false),
security_scan(:api_fuzzing, configured: false),
security_scan(:corpus_management, configured: true)
)
end
it 'includes a link to the latest pipeline' do
expect(subject[:latest_pipeline_path]).to eq(project_pipeline_path(project, pipeline))
end
context "while retrieving information about gitlab ci file" do
context 'when a .gitlab-ci.yml file exists' do
before do
project.repository.create_file(
project.creator,
Gitlab::FileDetector::PATTERNS[:gitlab_ci],
'contents go here',
message: 'test',
branch_name: 'master')
end
it 'expects gitlab_ci_present to be true' do
expect(subject[:gitlab_ci_present]).to eq(true)
end
end
context 'when a .gitlab-ci.yml file does not exist' do
it 'expects gitlab_ci_present to be false if the file is not present' do
expect(subject[:gitlab_ci_present]).to eq(false)
end
end
end
it 'includes the auto_devops_path' do
expect(subject[:auto_devops_path]).to eq(project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
context "while retrieving information about user's ability to enable auto_devops" do
using RSpec::Parameterized::TableSyntax
where(:is_admin, :archived, :feature_available, :result) do
true | true | true | false
false | true | true | false
true | false | true | true
false | false | true | false
true | true | false | false
false | true | false | false
true | false | false | false
false | false | false | false
end
with_them do
before do
allow_any_instance_of(described_class).to receive(:can?).and_return(is_admin)
allow_any_instance_of(described_class).to receive(:archived?).and_return(archived)
allow_any_instance_of(described_class).to receive(:feature_available?).and_return(feature_available)
end
it 'includes can_enable_auto_devops' do
expect(subject[:can_enable_auto_devops]).to eq(result)
end
end
end
end
end
def security_scan(type, configured:, configuration_path: nil)
path = configuration_path || configuration_path(type)
{
"type" => type.to_s,
"configured" => configured,
"configuration_path" => path,
"available" => licensed_scan_types.include?(type)
}
end
def configuration_path(type)
{
dast: project_security_configuration_dast_path(project),
dast_profiles: project_security_configuration_dast_scans_path(project),
sast: project_security_configuration_sast_path(project),
api_fuzzing: project_security_configuration_api_fuzzing_path(project),
corpus_management: nil
}[type]
end
def licensed_scan_types
::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types - [:cluster_image_scanning]
end
end
# frozen_string_literal: true
module Gitlab
module Security
class ScanConfiguration
include ::Gitlab::Utils::StrongMemoize
include Gitlab::Routing.url_helpers
attr_reader :type
def initialize(project:, type:, configured: false)
@project = project
@type = type
@configured = configured
end
def available?
# SAST and Secret Detection are always available, but this isn't
# reflected by our license model yet.
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/333113
%i[sast secret_detection].include?(type)
end
def configured?
configured
end
def configuration_path
configurable_scans[type]
end
private
attr_reader :project, :configured
def configurable_scans
strong_memoize(:configurable_scans) do
{
sast: project_security_configuration_sast_path(project)
}
end
end
end
end
end
Gitlab::Security::ScanConfiguration.prepend_mod_with('Gitlab::Security::ScanConfiguration')
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Gitlab::Security::ScanConfiguration do
let_it_be(:project) { create(:project, :repository) }
let(:scan) { described_class.new(project: project, type: type, configured: configured) }
describe '#available?' do
subject { scan.available? }
let(:configured) { true }
context 'with a core scanner' do
let(:type) { :sast }
it { is_expected.to be_truthy }
end
context 'with custom scanner' do
let(:type) { :my_scanner }
it { is_expected.to be_falsey }
end
end
describe '#configured?' do
subject { scan.configured? }
let(:type) { :sast }
let(:configured) { false }
it { is_expected.to be_falsey }
end
describe '#configuration_path' do
subject { scan.configuration_path }
let(:configured) { true }
context 'with a non configurable scaner' do
let(:type) { :secret_detection }
it { is_expected.to be_nil }
end
context 'with licensed scanner for FOSS environment' do
let(:type) { :dast }
before do
stub_env('FOSS_ONLY', '1')
end
it { is_expected.to be_nil }
end
context 'with custom scanner' do
let(:type) { :my_scanner }
it { is_expected.to be_nil }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::ConfigurationPresenter do
include Gitlab::Routing.url_helpers
using RSpec::Parameterized::TableSyntax
let(:project_with_repo) { create(:project, :repository) }
let(:project_with_no_repo) { create(:project) }
let(:current_user) { create(:user) }
let(:presenter) { described_class.new(project, current_user: current_user) }
before do
stub_licensed_features(licensed_scan_types.to_h { |type| [type, true] })
stub_feature_flags(corpus_management: false)
end
describe '#to_html_data_attribute' do
subject(:html_data) { presenter.to_html_data_attribute }
context 'when latest default branch pipeline`s source is not auto devops' do
let(:project) { project_with_repo }
let(:pipeline) do
create(
:ci_pipeline,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
end
let!(:build_sast) { create(:ci_build, :sast, pipeline: pipeline) }
let!(:build_dast) { create(:ci_build, :dast, pipeline: pipeline) }
let!(:build_license_scanning) { create(:ci_build, :license_scanning, pipeline: pipeline) }
it 'includes links to auto devops and secure product docs' do
expect(html_data[:auto_devops_help_page_path]).to eq(help_page_path('topics/autodevops/index'))
expect(html_data[:help_page_path]).to eq(help_page_path('user/application_security/index'))
end
it 'returns info that Auto DevOps is not enabled' do
expect(html_data[:auto_devops_enabled]).to eq(false)
expect(html_data[:auto_devops_path]).to eq(project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
it 'includes a link to the latest pipeline' do
expect(html_data[:latest_pipeline_path]).to eq(project_pipeline_path(project, pipeline))
end
it 'has stubs for autofix' do
expect(html_data.keys).to include(:can_toggle_auto_fix_settings, :auto_fix_enabled, :auto_fix_user_path)
end
context "while retrieving information about user's ability to enable auto_devops" do
where(:is_admin, :archived, :feature_available, :result) do
true | true | true | false
false | true | true | false
true | false | true | true
false | false | true | false
true | true | false | false
false | true | false | false
true | false | false | false
false | false | false | false
end
with_them do
before do
allow_next_instance_of(described_class) do |presenter|
allow(presenter).to receive(:can?).and_return(is_admin)
allow(presenter).to receive(:archived?).and_return(archived)
allow(presenter).to receive(:feature_available?).and_return(feature_available)
end
end
it 'includes can_enable_auto_devops' do
expect(html_data[:can_enable_auto_devops]).to eq(result)
end
end
end
it 'includes feature information' do
feature = Gitlab::Json.parse(html_data[:features]).find { |scan| scan['type'] == 'sast' }
expect(feature['type']).to eq('sast')
expect(feature['configured']).to eq(true)
expect(feature['configuration_path']).to eq(project_security_configuration_sast_path(project))
expect(feature['available']).to eq(true)
end
context 'when checking features configured status' do
let(:features) { Gitlab::Json.parse(html_data[:features]) }
where(:type, :configured) do
:dast | true
:dast_profiles | true
:sast | true
:sast_iac | false
:container_scanning | false
:cluster_image_scanning | false
:dependency_scanning | false
:license_scanning | true
:secret_detection | false
:coverage_fuzzing | false
:api_fuzzing | false
:corpus_management | true
end
with_them do
it 'returns proper configuration status' do
feature = features.find { |scan| scan['type'] == type.to_s }
expect(feature['configured']).to eq(configured)
end
end
end
context 'when the job has more than one report' do
let(:features) { Gitlab::Json.parse(html_data[:features]) }
let!(:artifacts) do
{ artifacts: { reports: { other_job: ['gl-other-report.json'], sast: ['gl-sast-report.json'] } } }
end
let!(:complicated_job) { build_stubbed(:ci_build, options: artifacts) }
before do
allow_next_instance_of(::Security::SecurityJobsFinder) do |finder|
allow(finder).to receive(:execute).and_return([complicated_job])
end
end
where(:type, :configured) do
:dast | false
:dast_profiles | true
:sast | true
:sast_iac | false
:container_scanning | false
:cluster_image_scanning | false
:dependency_scanning | false
:license_scanning | true
:secret_detection | false
:coverage_fuzzing | false
:api_fuzzing | false
:corpus_management | true
end
with_them do
it 'properly detects security jobs' do
feature = features.find { |scan| scan['type'] == type.to_s }
expect(feature['configured']).to eq(configured)
end
end
end
it 'includes a link to the latest pipeline' do
expect(subject[:latest_pipeline_path]).to eq(project_pipeline_path(project, pipeline))
end
context "while retrieving information about gitlab ci file" do
context 'when a .gitlab-ci.yml file exists' do
let!(:ci_config) do
project.repository.create_file(
project.creator,
Gitlab::FileDetector::PATTERNS[:gitlab_ci],
'contents go here',
message: 'test',
branch_name: 'master')
end
it 'expects gitlab_ci_present to be true' do
expect(html_data[:gitlab_ci_present]).to eq(true)
end
end
context 'when a .gitlab-ci.yml file does not exist' do
it 'expects gitlab_ci_present to be false if the file is not present' do
expect(html_data[:gitlab_ci_present]).to eq(false)
end
end
end
it 'includes the path to gitlab_ci history' do
expect(subject[:gitlab_ci_history_path]).to eq(project_blame_path(project, 'master/.gitlab-ci.yml'))
end
end
context 'when the project is empty' do
let(:project) { project_with_no_repo }
it 'includes a blank gitlab_ci history path' do
expect(html_data[:gitlab_ci_history_path]).to eq('')
end
end
context 'when the project has no default branch set' do
let(:project) { project_with_repo }
it 'includes the path to gitlab_ci history' do
allow(project).to receive(:default_branch).and_return(nil)
expect(html_data[:gitlab_ci_history_path]).to eq(project_blame_path(project, 'master/.gitlab-ci.yml'))
end
end
context "when the latest default branch pipeline's source is auto devops" do
let(:project) { project_with_repo }
let(:pipeline) do
create(
:ci_pipeline,
:auto_devops_source,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
end
let!(:build_sast) { create(:ci_build, :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') }
it 'reports that auto devops is enabled' do
expect(html_data[:auto_devops_enabled]).to be_truthy
end
context 'when gathering feature data' do
let(:features) { Gitlab::Json.parse(html_data[:features]) }
where(:type, :configured) do
:dast | true
:dast_profiles | true
:sast | true
:sast_iac | false
:container_scanning | false
:cluster_image_scanning | false
:dependency_scanning | false
:license_scanning | false
:secret_detection | true
:coverage_fuzzing | false
:api_fuzzing | false
:corpus_management | true
end
with_them do
it 'reports that all scanners are configured for which latest pipeline has builds' do
feature = features.find { |scan| scan['type'] == type.to_s }
expect(feature['configured']).to eq(configured)
end
end
end
end
context 'when the project has no default branch pipeline' do
let(:project) { project_with_repo }
it 'reports that auto devops is disabled' do
expect(html_data[:auto_devops_enabled]).to be_falsy
end
it 'includes a link to CI pipeline docs' do
expect(html_data[:latest_pipeline_path]).to eq(help_page_path('ci/pipelines'))
end
context 'when gathering feature data' do
let(:features) { Gitlab::Json.parse(html_data[:features]) }
where(:type, :configured) do
:dast | false
:dast_profiles | true
:sast | false
:sast_iac | false
:container_scanning | false
:cluster_image_scanning | false
:dependency_scanning | false
:license_scanning | false
:secret_detection | false
:coverage_fuzzing | false
:api_fuzzing | false
:corpus_management | true
end
with_them do
it 'reports all security jobs as unconfigured with exception of "fake" jobs' do
feature = features.find { |scan| scan['type'] == type.to_s }
expect(feature['configured']).to eq(configured)
end
end
end
end
def licensed_scan_types
::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types - [:cluster_image_scanning]
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment