From 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 19 May 2021 15:44:42 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-12-stable-ee --- lib/security/ci_configuration/base_build_action.rb | 50 ++++++ lib/security/ci_configuration/sast_build_action.rb | 129 ++++++++++++++++ .../ci_configuration/sast_build_actions.rb | 170 --------------------- .../secret_detection_build_action.rb | 19 +++ 4 files changed, 198 insertions(+), 170 deletions(-) create mode 100644 lib/security/ci_configuration/base_build_action.rb create mode 100644 lib/security/ci_configuration/sast_build_action.rb delete mode 100644 lib/security/ci_configuration/sast_build_actions.rb create mode 100644 lib/security/ci_configuration/secret_detection_build_action.rb (limited to 'lib/security') diff --git a/lib/security/ci_configuration/base_build_action.rb b/lib/security/ci_configuration/base_build_action.rb new file mode 100644 index 00000000000..b169d780cad --- /dev/null +++ b/lib/security/ci_configuration/base_build_action.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Security + module CiConfiguration + class BaseBuildAction + def initialize(auto_devops_enabled, existing_gitlab_ci_content) + @auto_devops_enabled = auto_devops_enabled + @existing_gitlab_ci_content = existing_gitlab_ci_content || {} + end + + def generate + action = @existing_gitlab_ci_content.present? ? 'update' : 'create' + + update_existing_content! + + { action: action, file_path: '.gitlab-ci.yml', content: prepare_existing_content, default_values_overwritten: @default_values_overwritten } + end + + private + + def generate_includes + includes = @existing_gitlab_ci_content['include'] || [] + includes = Array.wrap(includes) + includes << { 'template' => template } + includes.uniq + end + + def prepare_existing_content + content = @existing_gitlab_ci_content.to_yaml + content = remove_document_delimiter(content) + + content.prepend(comment) + end + + def remove_document_delimiter(content) + content.gsub(/^---\n/, '') + end + + def comment + <<~YAML + # You can override the included template(s) by including variable overrides + # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings + # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings + # Note that environment variables can be set in several places + # See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables + YAML + end + end + end +end diff --git a/lib/security/ci_configuration/sast_build_action.rb b/lib/security/ci_configuration/sast_build_action.rb new file mode 100644 index 00000000000..23dd4bd6d14 --- /dev/null +++ b/lib/security/ci_configuration/sast_build_action.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +module Security + module CiConfiguration + class SastBuildAction < BaseBuildAction + SAST_DEFAULT_ANALYZERS = 'bandit, brakeman, eslint, flawfinder, gosec, kubesec, nodejs-scan, phpcs-security-audit, pmd-apex, security-code-scan, semgrep, sobelow, spotbugs' + + def initialize(auto_devops_enabled, params, existing_gitlab_ci_content) + super(auto_devops_enabled, existing_gitlab_ci_content) + @variables = variables(params) + @default_sast_values = default_sast_values(params) + @default_values_overwritten = false + end + + private + + def variables(params) + collect_values(params, 'value') + end + + def default_sast_values(params) + collect_values(params, 'defaultValue') + end + + def collect_values(config, key) + global_variables = config['global']&.to_h { |k| [k['field'], k[key]] } || {} + pipeline_variables = config['pipeline']&.to_h { |k| [k['field'], k[key]] } || {} + + analyzer_variables = collect_analyzer_values(config, key) + + global_variables.merge!(pipeline_variables).merge!(analyzer_variables) + end + + def collect_analyzer_values(config, key) + analyzer_variables = analyzer_variables_for(config, key) + analyzer_variables['SAST_EXCLUDED_ANALYZERS'] = if key == 'value' + config['analyzers'] + &.reject {|a| a['enabled'] } + &.collect {|a| a['name'] } + &.sort + &.join(', ') + else + '' + end + + analyzer_variables + end + + def analyzer_variables_for(config, key) + config['analyzers'] + &.select {|a| a['enabled'] && a['variables'] } + &.flat_map {|a| a['variables'] } + &.collect {|v| [v['field'], v[key]] }.to_h + end + + def update_existing_content! + @existing_gitlab_ci_content['stages'] = set_stages + @existing_gitlab_ci_content['variables'] = set_variables(global_variables, @existing_gitlab_ci_content) + @existing_gitlab_ci_content['sast'] = set_sast_block + @existing_gitlab_ci_content['include'] = generate_includes + + @existing_gitlab_ci_content.select! { |k, v| v.present? } + @existing_gitlab_ci_content['sast'].select! { |k, v| v.present? } + end + + def set_stages + existing_stages = @existing_gitlab_ci_content['stages'] || [] + base_stages = @auto_devops_enabled ? auto_devops_stages : ['test'] + (existing_stages + base_stages + [sast_stage]).uniq + end + + def auto_devops_stages + auto_devops_template = YAML.safe_load( Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content ) + auto_devops_template['stages'] + end + + def sast_stage + @variables['stage'].presence ? @variables['stage'] : 'test' + end + + def set_variables(variables, hash_to_update = {}) + hash_to_update['variables'] ||= {} + + variables.each do |key| + if @variables[key].present? && @variables[key].to_s != @default_sast_values[key].to_s + hash_to_update['variables'][key] = @variables[key] + @default_values_overwritten = true + else + hash_to_update['variables'].delete(key) + end + end + + hash_to_update['variables'] + end + + def set_sast_block + sast_content = @existing_gitlab_ci_content['sast'] || {} + sast_content['variables'] = set_variables(sast_variables) + sast_content['stage'] = sast_stage + sast_content.select { |k, v| v.present? } + end + + def template + return 'Auto-DevOps.gitlab-ci.yml' if @auto_devops_enabled + + 'Security/SAST.gitlab-ci.yml' + end + + def global_variables + %w( + SECURE_ANALYZERS_PREFIX + ) + end + + def sast_variables + %w( + SAST_ANALYZER_IMAGE_TAG + SAST_EXCLUDED_PATHS + SEARCH_MAX_DEPTH + SAST_EXCLUDED_ANALYZERS + SAST_BRAKEMAN_LEVEL + SAST_BANDIT_EXCLUDED_PATHS + SAST_FLAWFINDER_LEVEL + SAST_GOSEC_LEVEL + ) + end + end + end +end diff --git a/lib/security/ci_configuration/sast_build_actions.rb b/lib/security/ci_configuration/sast_build_actions.rb deleted file mode 100644 index b2d684bc1e1..00000000000 --- a/lib/security/ci_configuration/sast_build_actions.rb +++ /dev/null @@ -1,170 +0,0 @@ -# frozen_string_literal: true - -module Security - module CiConfiguration - class SastBuildActions - SAST_DEFAULT_ANALYZERS = 'bandit, brakeman, eslint, flawfinder, gosec, kubesec, nodejs-scan, phpcs-security-audit, pmd-apex, security-code-scan, sobelow, spotbugs' - - def initialize(auto_devops_enabled, params, existing_gitlab_ci_content) - @auto_devops_enabled = auto_devops_enabled - @variables = variables(params) - @existing_gitlab_ci_content = existing_gitlab_ci_content || {} - @default_sast_values = default_sast_values(params) - @default_values_overwritten = false - end - - def generate - action = @existing_gitlab_ci_content.present? ? 'update' : 'create' - - update_existing_content! - - [{ action: action, file_path: '.gitlab-ci.yml', content: prepare_existing_content, default_values_overwritten: @default_values_overwritten }] - end - - private - - def variables(params) - # This early return is necessary for supporting REST API. - # Will be removed during the implementation of - # https://gitlab.com/gitlab-org/gitlab/-/issues/246737 - return params unless params['global'].present? - - collect_values(params, 'value') - end - - def default_sast_values(params) - collect_values(params, 'defaultValue') - end - - def collect_values(config, key) - global_variables = config['global']&.to_h { |k| [k['field'], k[key]] } || {} - pipeline_variables = config['pipeline']&.to_h { |k| [k['field'], k[key]] } || {} - - analyzer_variables = collect_analyzer_values(config, key) - - global_variables.merge!(pipeline_variables).merge!(analyzer_variables) - end - - def collect_analyzer_values(config, key) - analyzer_variables = analyzer_variables_for(config, key) - analyzer_variables['SAST_EXCLUDED_ANALYZERS'] = if key == 'value' - config['analyzers'] - &.reject {|a| a['enabled'] } - &.collect {|a| a['name'] } - &.sort - &.join(', ') - else - '' - end - - analyzer_variables - end - - def analyzer_variables_for(config, key) - config['analyzers'] - &.select {|a| a['enabled'] && a['variables'] } - &.flat_map {|a| a['variables'] } - &.collect {|v| [v['field'], v[key]] }.to_h - end - - def update_existing_content! - @existing_gitlab_ci_content['stages'] = set_stages - @existing_gitlab_ci_content['variables'] = set_variables(global_variables, @existing_gitlab_ci_content) - @existing_gitlab_ci_content['sast'] = set_sast_block - @existing_gitlab_ci_content['include'] = set_includes - - @existing_gitlab_ci_content.select! { |k, v| v.present? } - @existing_gitlab_ci_content['sast'].select! { |k, v| v.present? } - end - - def set_includes - includes = @existing_gitlab_ci_content['include'] || [] - includes = includes.is_a?(Array) ? includes : [includes] - includes << { 'template' => template } - includes.uniq - end - - def set_stages - existing_stages = @existing_gitlab_ci_content['stages'] || [] - base_stages = @auto_devops_enabled ? auto_devops_stages : ['test'] - (existing_stages + base_stages + [sast_stage]).uniq - end - - def auto_devops_stages - auto_devops_template = YAML.safe_load( Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content ) - auto_devops_template['stages'] - end - - def sast_stage - @variables['stage'].presence ? @variables['stage'] : 'test' - end - - def set_variables(variables, hash_to_update = {}) - hash_to_update['variables'] ||= {} - - variables.each do |key| - if @variables[key].present? && @variables[key].to_s != @default_sast_values[key].to_s - hash_to_update['variables'][key] = @variables[key] - @default_values_overwritten = true - else - hash_to_update['variables'].delete(key) - end - end - - hash_to_update['variables'] - end - - def set_sast_block - sast_content = @existing_gitlab_ci_content['sast'] || {} - sast_content['variables'] = set_variables(sast_variables) - sast_content['stage'] = sast_stage - sast_content.select { |k, v| v.present? } - end - - def prepare_existing_content - content = @existing_gitlab_ci_content.to_yaml - content = remove_document_delimeter(content) - - content.prepend(sast_comment) - end - - def remove_document_delimeter(content) - content.gsub(/^---\n/, '') - end - - def sast_comment - <<~YAML - # You can override the included template(s) by including variable overrides - # See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings - # Note that environment variables can be set in several places - # See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables - YAML - end - - def template - return 'Auto-DevOps.gitlab-ci.yml' if @auto_devops_enabled - - 'Security/SAST.gitlab-ci.yml' - end - - def global_variables - %w( - SECURE_ANALYZERS_PREFIX - ) - end - - def sast_variables - %w( - SAST_ANALYZER_IMAGE_TAG - SAST_EXCLUDED_PATHS - SEARCH_MAX_DEPTH - SAST_EXCLUDED_ANALYZERS - SAST_BRAKEMAN_LEVEL - SAST_BANDIT_EXCLUDED_PATHS - SAST_FLAWFINDER_LEVEL - SAST_GOSEC_LEVEL - ) - end - end - end -end diff --git a/lib/security/ci_configuration/secret_detection_build_action.rb b/lib/security/ci_configuration/secret_detection_build_action.rb new file mode 100644 index 00000000000..5d513bf5547 --- /dev/null +++ b/lib/security/ci_configuration/secret_detection_build_action.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Security + module CiConfiguration + class SecretDetectionBuildAction < BaseBuildAction + private + + def update_existing_content! + @existing_gitlab_ci_content['include'] = generate_includes + end + + def template + return 'Auto-DevOps.gitlab-ci.yml' if @auto_devops_enabled + + 'Security/Secret-Detection.gitlab-ci.yml' + end + end + end +end -- cgit v1.2.3