diff --git a/lib/hash_cast/casters/array_caster.rb b/lib/hash_cast/casters/array_caster.rb index 6e1abf7..2a04381 100644 --- a/lib/hash_cast/casters/array_caster.rb +++ b/lib/hash_cast/casters/array_caster.rb @@ -1,14 +1,20 @@ class HashCast::Casters::ArrayCaster def self.cast(value, attr_name, options = {}) - if value.is_a?(Array) - if options[:each] - cast_array_items(value, attr_name, options) - else - value + unless value.is_a?(Array) + raise HashCast::Errors::CastingError, "should be an array" + end + + if HashCast.config.array_size_validator_enabled + if value.size > HashCast.config.array_size_validator_limit + HashCast.config.array_size_validator_callback.call(value, attr_name, options) end + end + + if options[:each] + cast_array_items(value, attr_name, options) else - raise HashCast::Errors::CastingError, "should be an array" + value end end diff --git a/lib/hash_cast/casters/string_caster.rb b/lib/hash_cast/casters/string_caster.rb index e40035c..675fb17 100644 --- a/lib/hash_cast/casters/string_caster.rb +++ b/lib/hash_cast/casters/string_caster.rb @@ -8,6 +8,12 @@ def self.cast(value, attr_name, options = {}) raise HashCast::Errors::CastingError, 'contains invalid characters' end + if HashCast.config.string_size_validator_enabled + if value.size > HashCast.config.string_size_validator_limit + HashCast.config.string_size_validator_callback.call(value, attr_name, options) + end + end + casted_value end diff --git a/lib/hash_cast/config.rb b/lib/hash_cast/config.rb index 5c0b874..2d858cc 100644 --- a/lib/hash_cast/config.rb +++ b/lib/hash_cast/config.rb @@ -1,5 +1,14 @@ class HashCast::Config - attr_accessor :input_keys, :output_keys, :validate_string_null_byte + attr_writer :input_keys, :output_keys, :validate_string_null_byte, + :array_size_validator_enabled, + :array_size_validator_limit, + :array_size_validator_callback, + :string_size_validator_enabled, + :string_size_validator_limit, + :string_size_validator_callback + + DEFAULT_ARRAY_SIZE_VALIDATOR_LIMIT = 1000_000 + DEFAULT_STRING_SIZE_VALIDATOR_LIMIT = 1000_000 def input_keys @input_keys || :symbol @@ -14,4 +23,48 @@ def validate_string_null_byte @validate_string_null_byte end + + def array_size_validator_enabled + return false if @array_size_validator_enabled.nil? + + @array_size_validator_enabled + end + + def array_size_validator_limit + return DEFAULT_ARRAY_SIZE_VALIDATOR_LIMIT if @array_size_validator_limit.nil? + + @array_size_validator_limit + end + + def array_size_validator_callback + if @array_size_validator_callback.nil? + return lambda{ |value, name, options| + raise HashCast::Errors::CastingError, "array is too large" + } + end + + @array_size_validator_callback + end + + def string_size_validator_enabled + return false if @string_size_validator_enabled.nil? + + @string_size_validator_enabled + end + + def string_size_validator_limit + return DEFAULT_STRING_SIZE_VALIDATOR_LIMIT if @string_size_validator_limit.nil? + + @string_size_validator_limit + end + + def string_size_validator_callback + if @string_size_validator_callback.nil? + return lambda{ |value, name, options| + raise HashCast::Errors::CastingError, "string is too large" + } + end + + @string_size_validator_callback + end end diff --git a/spec/hash_cast/casters/array_caster_spec.rb b/spec/hash_cast/casters/array_caster_spec.rb new file mode 100644 index 0000000..2c6d93d --- /dev/null +++ b/spec/hash_cast/casters/array_caster_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe HashCast::Casters::ArrayCaster do + subject { HashCast::Casters::ArrayCaster } + + it "should cast an array" do + result = subject.cast([1,2,3], :ids) + expect(result).to eq([1,2,3]) + end + + it "should raise an error for non-array" do + expect { + subject.cast(1, :ids) + }.to raise_error(HashCast::Errors::CastingError, "should be an array") + end + + context "array size validation" do + after{ + HashCast.config.array_size_validator_enabled = nil + HashCast.config.array_size_validator_limit = nil + } + + it "should not raise an error for large array by default" do + result = subject.cast([1] * 10_000, :ids) + expect(result).to eq([1] * 10_000) + end + + it "should raise an error for large array when validation is enabled" do + HashCast.config.array_size_validator_enabled = true + HashCast.config.array_size_validator_limit = 1000 + + expect { + subject.cast([1] * 10_000, :ids) + }.to raise_error(HashCast::Errors::CastingError, "array is too large") + end + + it "should allow overriding the callback" do + HashCast.config.array_size_validator_enabled = true + HashCast.config.array_size_validator_limit = 1000 + HashCast.config.array_size_validator_callback = lambda { |value, name, options| + raise HashCast::Errors::UnexpectedAttributeError, 'test' + } + + expect { + subject.cast([1] * 10_000, :ids) + }.to raise_error(HashCast::Errors::UnexpectedAttributeError, "test") + end + end +end diff --git a/spec/hash_cast/casters/string_caster_spec.rb b/spec/hash_cast/casters/string_caster_spec.rb new file mode 100644 index 0000000..f54c54b --- /dev/null +++ b/spec/hash_cast/casters/string_caster_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe HashCast::Casters::StringCaster do + subject { HashCast::Casters::StringCaster } + + it "should cast a string" do + result = subject.cast("foobar", :name) + expect(result).to eq("foobar") + end + + context "string size validation" do + after{ + HashCast.config.string_size_validator_enabled = nil + HashCast.config.string_size_validator_limit = nil + } + + it "should not raise an error for large string by default" do + result = subject.cast("a" * 10_000, :name) + expect(result).to eq("a" * 10_000) + end + + it "should raise an error for large string when validation is enabled" do + HashCast.config.string_size_validator_enabled = true + HashCast.config.string_size_validator_limit = 1000 + + expect { + subject.cast("a" * 10_000, :ids) + }.to raise_error(HashCast::Errors::CastingError, "string is too large") + end + + it "should allow overriding the callback" do + HashCast.config.string_size_validator_enabled = true + HashCast.config.string_size_validator_limit = 1000 + HashCast.config.string_size_validator_callback = lambda { |value, name, options| + raise HashCast::Errors::UnexpectedAttributeError, 'test' + } + + expect { + subject.cast("a" * 10_000, :ids) + }.to raise_error(HashCast::Errors::UnexpectedAttributeError, "test") + end + end +end