Limit extendable CI/CD config entry nesting levels
This commit is contained in:
parent
d2f46c3025
commit
afbe5490f0
3 changed files with 47 additions and 9 deletions
|
@ -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
|
||||||
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
|
||||||
@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
|
||||||
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
|
||||||
"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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
expect(child.path).to eq [:test, :job]
|
let(:child) do
|
||||||
|
described_class.new(:job, { job: { script: 'something' } }, parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
.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
|
||||||
|
|
Loading…
Reference in a new issue