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