Commit afbe5490 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Limit extendable CI/CD config entry nesting levels

parent d2f46c30
...@@ -8,6 +8,7 @@ module Gitlab ...@@ -8,6 +8,7 @@ module Gitlab
ExtensionError = Class.new(StandardError) ExtensionError = Class.new(StandardError)
InvalidExtensionError = Class.new(ExtensionError) InvalidExtensionError = Class.new(ExtensionError)
CircularDependencyError = Class.new(ExtensionError) CircularDependencyError = Class.new(ExtensionError)
NestingTooDeepError = Class.new(ExtensionError)
def initialize(hash) def initialize(hash)
@hash = hash.to_h.deep_dup @hash = hash.to_h.deep_dup
......
...@@ -3,6 +3,8 @@ module Gitlab ...@@ -3,6 +3,8 @@ module Gitlab
class Config class Config
module Extendable module Extendable
class Entry class Entry
MAX_NESTING_LEVELS = 10
attr_reader :key attr_reader :key
def initialize(key, context, parent = nil) def initialize(key, context, parent = nil)
...@@ -10,7 +12,9 @@ module Gitlab ...@@ -10,7 +12,9 @@ module Gitlab
@context = context @context = context
@parent = parent @parent = parent
raise StandardError, 'Invalid entry key!' unless @context.key?(@key) unless @context.key?(@key)
raise StandardError, 'Invalid entry key!'
end
end end
def extensible? def extensible?
...@@ -31,8 +35,8 @@ module Gitlab ...@@ -31,8 +35,8 @@ module Gitlab
value.fetch(:extends).to_s.to_sym if extensible? value.fetch(:extends).to_s.to_sym if extensible?
end end
def path def ancestors
Array(@parent&.path).compact.push(key) @ancestors ||= Array(@parent&.ancestors) + Array(@parent&.key)
end end
def extend! def extend!
...@@ -48,6 +52,11 @@ module Gitlab ...@@ -48,6 +52,11 @@ module Gitlab
"Invalid base hash in extended `#{key}`!" "Invalid base hash in extended `#{key}`!"
end end
if nesting_too_deep?
raise Extendable::Collection::NestingTooDeepError,
"`extends` nesting too deep in `#{key}`!"
end
if circular_dependency? if circular_dependency?
raise Extendable::Collection::CircularDependencyError, raise Extendable::Collection::CircularDependencyError,
"Circular dependency detected in extended `#{key}`!" "Circular dependency detected in extended `#{key}`!"
...@@ -58,8 +67,12 @@ module Gitlab ...@@ -58,8 +67,12 @@ module Gitlab
private private
def nesting_too_deep?
ancestors.count > MAX_NESTING_LEVELS
end
def circular_dependency? def circular_dependency?
path.count(key) > 1 ancestors.include?(key)
end end
def unknown_extension? def unknown_extension?
......
...@@ -62,12 +62,17 @@ describe Gitlab::Ci::Config::Extendable::Entry do ...@@ -62,12 +62,17 @@ describe Gitlab::Ci::Config::Extendable::Entry do
end end
end end
describe '#path' do describe '#ancestors' do
it 'returns inheritance chain path' do let(:parent) do
parent = described_class.new(:test, test: { extends: 'something' }) described_class.new(:test, test: { extends: 'something' })
child = described_class.new(:job, { job: { script: 'something' } }, parent) end
let(:child) do
described_class.new(:job, { job: { script: 'something' } }, parent)
end
expect(child.path).to eq [:test, :job] it 'returns ancestors keys' do
expect(child.ancestors).to eq [:test]
end end
end end
...@@ -196,5 +201,24 @@ describe Gitlab::Ci::Config::Extendable::Entry do ...@@ -196,5 +201,24 @@ describe Gitlab::Ci::Config::Extendable::Entry do
.to raise_error(StandardError, /Circular dependency detected/) .to raise_error(StandardError, /Circular dependency detected/)
end end
end end
context 'when nesting level is too deep' do
before do
stub_const("#{described_class}::MAX_NESTING_LEVELS", 0)
end
let(:hash) do
{
first: { script: 'my value' },
second: { extends: 'first' },
test: { extends: 'second' }
}
end
it 'raises an error' do
expect { subject.extend! }
.to raise_error(Gitlab::Ci::Config::Extendable::Collection::NestingTooDeepError)
end
end
end 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