From 7978689f7f723b514a74b79e1906d26f74cb76fa Mon Sep 17 00:00:00 2001 From: Iskander Khaziev Date: Tue, 13 May 2025 20:01:28 +0000 Subject: [PATCH 1/2] added array content size validation --- lib/hash_cast/casters/array_caster.rb | 18 ++++++---- lib/hash_cast/config.rb | 17 +++++++++- spec/hash_cast/casters/array_caster_spec.rb | 37 +++++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 spec/hash_cast/casters/array_caster_spec.rb diff --git a/lib/hash_cast/casters/array_caster.rb b/lib/hash_cast/casters/array_caster.rb index 6e1abf7..d833b2e 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 + raise HashCast::Errors::CastingError, "array is too large" 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/config.rb b/lib/hash_cast/config.rb index 5c0b874..1669692 100644 --- a/lib/hash_cast/config.rb +++ b/lib/hash_cast/config.rb @@ -1,5 +1,8 @@ class HashCast::Config - attr_accessor :input_keys, :output_keys, :validate_string_null_byte + attr_accessor :input_keys, :output_keys, :validate_string_null_byte, + :array_size_validator_enabled, :array_size_validator_limit + + DEFAULT_ARRAY_SIZE_VALIDATOR_LIMIT = 1000_000 def input_keys @input_keys || :symbol @@ -14,4 +17,16 @@ 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 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..d87c7b2 --- /dev/null +++ b/spec/hash_cast/casters/array_caster_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe HashCast::Casters::ArrayCaster do + subject { HashCast::Casters::ArrayCaster } + + it "should cast 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 + end +end From 8f4d090297283cacbf2b871cf9dd94148abf60a3 Mon Sep 17 00:00:00 2001 From: Iskander Khaziev Date: Tue, 13 May 2025 21:29:11 +0000 Subject: [PATCH 2/2] added string size validation --- lib/hash_cast/casters/array_caster.rb | 2 +- lib/hash_cast/casters/string_caster.rb | 6 +++ lib/hash_cast/config.rb | 42 ++++++++++++++++++- spec/hash_cast/casters/array_caster_spec.rb | 14 ++++++- spec/hash_cast/casters/string_caster_spec.rb | 43 ++++++++++++++++++++ 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 spec/hash_cast/casters/string_caster_spec.rb diff --git a/lib/hash_cast/casters/array_caster.rb b/lib/hash_cast/casters/array_caster.rb index d833b2e..2a04381 100644 --- a/lib/hash_cast/casters/array_caster.rb +++ b/lib/hash_cast/casters/array_caster.rb @@ -7,7 +7,7 @@ def self.cast(value, attr_name, options = {}) if HashCast.config.array_size_validator_enabled if value.size > HashCast.config.array_size_validator_limit - raise HashCast::Errors::CastingError, "array is too large" + HashCast.config.array_size_validator_callback.call(value, attr_name, options) 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 1669692..2d858cc 100644 --- a/lib/hash_cast/config.rb +++ b/lib/hash_cast/config.rb @@ -1,8 +1,14 @@ class HashCast::Config - attr_accessor :input_keys, :output_keys, :validate_string_null_byte, - :array_size_validator_enabled, :array_size_validator_limit + 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 @@ -29,4 +35,36 @@ def array_size_validator_limit @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 index d87c7b2..2c6d93d 100644 --- a/spec/hash_cast/casters/array_caster_spec.rb +++ b/spec/hash_cast/casters/array_caster_spec.rb @@ -3,7 +3,7 @@ describe HashCast::Casters::ArrayCaster do subject { HashCast::Casters::ArrayCaster } - it "should cast array" do + it "should cast an array" do result = subject.cast([1,2,3], :ids) expect(result).to eq([1,2,3]) end @@ -33,5 +33,17 @@ 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