diff options
Diffstat (limited to 'lib/gitlab/ci/config')
-rw-r--r-- | lib/gitlab/ci/config/entry/artifacts.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/job.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/external/file/base.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/ci/config/external/file/component.rb | 18 | ||||
-rw-r--r-- | lib/gitlab/ci/config/header/input.rb | 8 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/block.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/context.rb | 23 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/functions/base.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/functions/expand_vars.rb | 33 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/functions_stack.rb | 10 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/inputs/base_input.rb | 10 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/inputs/string_input.rb | 18 | ||||
-rw-r--r-- | lib/gitlab/ci/config/interpolation/interpolator.rb | 7 | ||||
-rw-r--r-- | lib/gitlab/ci/config/yaml/loader.rb | 13 | ||||
-rw-r--r-- | lib/gitlab/ci/config/yaml/result.rb | 4 |
15 files changed, 120 insertions, 42 deletions
diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb index 27206d7e3a8..3fd07811daf 100644 --- a/lib/gitlab/ci/config/entry/artifacts.rb +++ b/lib/gitlab/ci/config/entry/artifacts.rb @@ -14,7 +14,7 @@ module Gitlab ALLOWED_WHEN = %w[on_success on_failure always].freeze ALLOWED_KEYS = %i[name untracked paths reports when expire_in expose_as exclude public].freeze - EXPOSE_AS_REGEX = /\A\w[-\w ]*\z/.freeze + EXPOSE_AS_REGEX = /\A\w[-\w ]*\z/ EXPOSE_AS_ERROR_MESSAGE = "can contain only letters, digits, '-', '_' and spaces" attributes ALLOWED_KEYS diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb index c40d665f320..bf8a99ef45e 100644 --- a/lib/gitlab/ci/config/entry/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -177,7 +177,7 @@ module Gitlab def parsed_timeout return unless has_timeout? - ChronicDuration.parse(timeout.to_s, use_complete_matcher: true) + ChronicDuration.parse(timeout.to_s) end def ignored? diff --git a/lib/gitlab/ci/config/external/file/base.rb b/lib/gitlab/ci/config/external/file/base.rb index efba81c7420..b3c802e5657 100644 --- a/lib/gitlab/ci/config/external/file/base.rb +++ b/lib/gitlab/ci/config/external/file/base.rb @@ -10,7 +10,7 @@ module Gitlab attr_reader :location, :params, :context, :errors - YAML_WHITELIST_EXTENSION = /.+\.(yml|yaml)$/i.freeze + YAML_WHITELIST_EXTENSION = /.+\.(yml|yaml)$/i def initialize(params, context) @params = params @@ -114,7 +114,9 @@ module Gitlab def content_result context.logger.instrument(:config_file_fetch_content_hash) do - ::Gitlab::Ci::Config::Yaml::Loader.new(content, inputs: content_inputs).load + ::Gitlab::Ci::Config::Yaml::Loader.new( + content, inputs: content_inputs, variables: context.variables + ).load end end strong_memoize_attr :content_result diff --git a/lib/gitlab/ci/config/external/file/component.rb b/lib/gitlab/ci/config/external/file/component.rb index de6de1bb7a8..03063e76dde 100644 --- a/lib/gitlab/ci/config/external/file/component.rb +++ b/lib/gitlab/ci/config/external/file/component.rb @@ -20,7 +20,7 @@ module Gitlab ::Gitlab::UsageDataCounters::HLLRedisCounter.track_event('cicd_component_usage', values: context.user.id) - component_result.payload.fetch(:content) + component_payload.fetch(:content) end strong_memoize_attr :content @@ -65,30 +65,30 @@ module Gitlab override :expand_context_attrs def expand_context_attrs { - project: component_path.project, - sha: component_path.sha, + project: component_payload.fetch(:project), + sha: component_payload.fetch(:sha), user: context.user, variables: context.variables } end def masked_blob - return unless component_path + return unless component_payload context.mask_variables_from( Gitlab::Routing.url_helpers.project_blob_url( - component_path.project, - ::File.join(component_path.sha, component_path.project_file_path)) + component_payload.fetch(:project), + ::File.join(component_payload.fetch(:sha), component_payload.fetch(:path))) ) end strong_memoize_attr :masked_blob - def component_path + def component_payload return unless component_result.success? - component_result.payload.fetch(:path) + component_result.payload end - strong_memoize_attr :component_path + strong_memoize_attr :component_payload end end end diff --git a/lib/gitlab/ci/config/header/input.rb b/lib/gitlab/ci/config/header/input.rb index 76a89a3080e..dcb96006459 100644 --- a/lib/gitlab/ci/config/header/input.rb +++ b/lib/gitlab/ci/config/header/input.rb @@ -11,12 +11,16 @@ module Gitlab include ::Gitlab::Config::Entry::Validatable include ::Gitlab::Config::Entry::Attributable - attributes :default, :type, prefix: :input + ALLOWED_KEYS = %i[default description regex type].freeze + + attributes ALLOWED_KEYS, prefix: :input validations do - validates :config, type: Hash, allowed_keys: [:default, :type] + validates :config, type: Hash, allowed_keys: ALLOWED_KEYS validates :key, alphanumeric: true validates :input_default, alphanumeric: true, allow_nil: true + validates :input_description, alphanumeric: true, allow_nil: true + validates :input_regex, type: String, allow_nil: true validates :input_type, allow_nil: true, allowed_values: Interpolation::Inputs.input_types end end diff --git a/lib/gitlab/ci/config/interpolation/block.rb b/lib/gitlab/ci/config/interpolation/block.rb index cf8420f924e..aec19299e86 100644 --- a/lib/gitlab/ci/config/interpolation/block.rb +++ b/lib/gitlab/ci/config/interpolation/block.rb @@ -62,7 +62,7 @@ module Gitlab return @errors.concat(access.errors) unless access.valid? return @errors.push('too many functions in interpolation block') if functions.count > MAX_FUNCTIONS - result = Interpolation::FunctionsStack.new(functions).evaluate(access.value) + result = Interpolation::FunctionsStack.new(functions, ctx).evaluate(access.value) if result.success? @value = result.value diff --git a/lib/gitlab/ci/config/interpolation/context.rb b/lib/gitlab/ci/config/interpolation/context.rb index f5e7db03291..19ea619f7da 100644 --- a/lib/gitlab/ci/config/interpolation/context.rb +++ b/lib/gitlab/ci/config/interpolation/context.rb @@ -14,8 +14,11 @@ module Gitlab MAX_DEPTH = 3 - def initialize(hash) - @context = hash + attr_reader :variables + + def initialize(data, variables: []) + @data = data + @variables = Ci::Variables::Collection.fabricate(variables) raise ContextTooComplexError if depth > MAX_DEPTH end @@ -32,25 +35,25 @@ module Gitlab end def depth - deep_depth(@context) + deep_depth(@data) end def fetch(field) - @context.fetch(field) + @data.fetch(field) end def key?(name) - @context.key?(name) + @data.key?(name) end def to_h - @context.to_h + @data.to_h end private - def deep_depth(context, depth = 0) - values = context.values.map do |value| + def deep_depth(data, depth = 0) + values = data.values.map do |value| if value.is_a?(Hash) deep_depth(value, depth + 1) else @@ -61,10 +64,10 @@ module Gitlab values.max.to_i end - def self.fabricate(context) + def self.fabricate(context, variables: []) case context when Hash - new(context) + new(context, variables: variables) when Interpolation::Context context else diff --git a/lib/gitlab/ci/config/interpolation/functions/base.rb b/lib/gitlab/ci/config/interpolation/functions/base.rb index b9ce8cdc5bc..b04152a1558 100644 --- a/lib/gitlab/ci/config/interpolation/functions/base.rb +++ b/lib/gitlab/ci/config/interpolation/functions/base.rb @@ -20,9 +20,10 @@ module Gitlab function_expression_pattern.match?(function_expression) end - def initialize(function_expression) + def initialize(function_expression, ctx) @errors = [] @function_args = parse_args(function_expression) + @ctx = ctx end def valid? @@ -35,10 +36,11 @@ module Gitlab private - attr_reader :function_args + attr_reader :function_args, :ctx def error(message) errors << "error in `#{self.class.name}` function: #{message}" + nil end def parse_args(function_expression) diff --git a/lib/gitlab/ci/config/interpolation/functions/expand_vars.rb b/lib/gitlab/ci/config/interpolation/functions/expand_vars.rb new file mode 100644 index 00000000000..658964018b5 --- /dev/null +++ b/lib/gitlab/ci/config/interpolation/functions/expand_vars.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + class Config + module Interpolation + module Functions + class ExpandVars < Base + def self.function_expression_pattern + /^#{name}$/ + end + + def self.name + 'expand_vars' + end + + def execute(input_value) + unless input_value.is_a?(String) + error("invalid input type: #{self.class.name} can only be used with string inputs") + return + end + + ExpandVariables.expand_existing(input_value, ctx.variables, fail_on_masked: true) + rescue ExpandVariables::VariableExpansionError => e + error("variable expansion error: #{e.message}") + nil + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/interpolation/functions_stack.rb b/lib/gitlab/ci/config/interpolation/functions_stack.rb index 951d1121d4f..4cb3e67b3e3 100644 --- a/lib/gitlab/ci/config/interpolation/functions_stack.rb +++ b/lib/gitlab/ci/config/interpolation/functions_stack.rb @@ -16,12 +16,14 @@ module Gitlab end FUNCTIONS = [ - Functions::Truncate + Functions::Truncate, + Functions::ExpandVars ].freeze attr_reader :errors - def initialize(function_expressions) + def initialize(function_expressions, ctx) + @ctx = ctx @errors = [] @functions = build_stack(function_expressions) end @@ -48,14 +50,14 @@ module Gitlab private - attr_reader :functions + attr_reader :functions, :ctx def build_stack(function_expressions) function_expressions.map do |function_expression| matching_function = FUNCTIONS.find { |function| function.matches?(function_expression) } if matching_function.present? - matching_function.new(function_expression) + matching_function.new(function_expression, ctx) else message = "no function matching `#{function_expression}`: " \ 'check that the function name, arguments, and types are correct' diff --git a/lib/gitlab/ci/config/interpolation/inputs/base_input.rb b/lib/gitlab/ci/config/interpolation/inputs/base_input.rb index 5648c4d31ea..ba519776635 100644 --- a/lib/gitlab/ci/config/interpolation/inputs/base_input.rb +++ b/lib/gitlab/ci/config/interpolation/inputs/base_input.rb @@ -62,7 +62,15 @@ module Gitlab end # validate provided value - error("provided value is not a #{self.class.type_name}") unless valid_value?(actual_value) + return error("provided value is not a #{self.class.type_name}") unless valid_value?(actual_value) + + validate_regex! + end + + def validate_regex! + return unless spec.key?(:regex) + + error('RegEx validation can only be used with string inputs') end def error(message) diff --git a/lib/gitlab/ci/config/interpolation/inputs/string_input.rb b/lib/gitlab/ci/config/interpolation/inputs/string_input.rb index 39870582d0c..3f40e851f11 100644 --- a/lib/gitlab/ci/config/interpolation/inputs/string_input.rb +++ b/lib/gitlab/ci/config/interpolation/inputs/string_input.rb @@ -25,6 +25,24 @@ module Gitlab def valid_value?(value) value.nil? || value.is_a?(String) end + + private + + def validate_regex! + return unless spec.key?(:regex) + + safe_regex = ::Gitlab::UntrustedRegexp.new(spec[:regex]) + + return if safe_regex.match?(actual_value) + + if value.nil? + error('default value does not match required RegEx pattern') + else + error('provided value does not match required RegEx pattern') + end + rescue RegexpError + error('invalid regular expression') + end end end end diff --git a/lib/gitlab/ci/config/interpolation/interpolator.rb b/lib/gitlab/ci/config/interpolation/interpolator.rb index 95c419d7427..5b21b777c1d 100644 --- a/lib/gitlab/ci/config/interpolation/interpolator.rb +++ b/lib/gitlab/ci/config/interpolation/interpolator.rb @@ -8,11 +8,12 @@ module Gitlab # Performs CI config file interpolation, and surfaces all possible interpolation errors. # class Interpolator - attr_reader :config, :args, :errors + attr_reader :config, :args, :variables, :errors - def initialize(config, args) + def initialize(config, args, variables) @config = config @args = args.to_h + @variables = variables @errors = [] @interpolated = false end @@ -86,7 +87,7 @@ module Gitlab end def context - @context ||= Context.new({ inputs: inputs.to_hash }) + @context ||= Context.new({ inputs: inputs.to_hash }, variables: variables) end def template diff --git a/lib/gitlab/ci/config/yaml/loader.rb b/lib/gitlab/ci/config/yaml/loader.rb index 5d56061a8bb..1e9ac2b3dd5 100644 --- a/lib/gitlab/ci/config/yaml/loader.rb +++ b/lib/gitlab/ci/config/yaml/loader.rb @@ -10,9 +10,10 @@ module Gitlab AVAILABLE_TAGS = [Config::Yaml::Tags::Reference].freeze MAX_DOCUMENTS = 2 - def initialize(content, inputs: {}) + def initialize(content, inputs: {}, variables: []) @content = content @inputs = inputs + @variables = variables end def load @@ -20,7 +21,7 @@ module Gitlab return yaml_result unless yaml_result.valid? - interpolator = Interpolation::Interpolator.new(yaml_result, inputs) + interpolator = Interpolation::Interpolator.new(yaml_result, inputs, variables) interpolator.interpolate! @@ -32,16 +33,16 @@ module Gitlab end end - private - - attr_reader :content, :inputs - def load_uninterpolated_yaml Yaml::Result.new(config: load_yaml!, error: nil) rescue ::Gitlab::Config::Loader::FormatError => e Yaml::Result.new(error: e.message, error_class: e) end + private + + attr_reader :content, :inputs, :variables + def load_yaml! ensure_custom_tags diff --git a/lib/gitlab/ci/config/yaml/result.rb b/lib/gitlab/ci/config/yaml/result.rb index a68cfde6653..0e7e9230467 100644 --- a/lib/gitlab/ci/config/yaml/result.rb +++ b/lib/gitlab/ci/config/yaml/result.rb @@ -39,6 +39,10 @@ module Gitlab @config.first || {} end + + def inputs + (has_header? && header[:spec][:inputs]) || {} + end end end end |