mini_defender 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +70 -0
- data/LICENSE.md +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +33 -0
- data/RULES.md +92 -0
- data/Rakefile +12 -0
- data/lib/mini_defender/extensions/enumerable.rb +34 -0
- data/lib/mini_defender/extensions/hash.rb +28 -0
- data/lib/mini_defender/rule.rb +77 -0
- data/lib/mini_defender/rules/accepted.rb +17 -0
- data/lib/mini_defender/rules/accepted_if.rb +26 -0
- data/lib/mini_defender/rules/alpha.rb +15 -0
- data/lib/mini_defender/rules/alpha_dash.rb +15 -0
- data/lib/mini_defender/rules/alpha_num.rb +15 -0
- data/lib/mini_defender/rules/array.rb +15 -0
- data/lib/mini_defender/rules/bail.rb +19 -0
- data/lib/mini_defender/rules/between.rb +29 -0
- data/lib/mini_defender/rules/boolean.rb +26 -0
- data/lib/mini_defender/rules/confirmed.rb +24 -0
- data/lib/mini_defender/rules/country_code.rb +23 -0
- data/lib/mini_defender/rules/credit_card.rb +13 -0
- data/lib/mini_defender/rules/date.rb +27 -0
- data/lib/mini_defender/rules/date_eq.rb +43 -0
- data/lib/mini_defender/rules/date_format.rb +38 -0
- data/lib/mini_defender/rules/date_gt.rb +24 -0
- data/lib/mini_defender/rules/date_gte.rb +24 -0
- data/lib/mini_defender/rules/date_lt.rb +24 -0
- data/lib/mini_defender/rules/date_lte.rb +24 -0
- data/lib/mini_defender/rules/declined.rb +17 -0
- data/lib/mini_defender/rules/declined_if.rb +26 -0
- data/lib/mini_defender/rules/default.rb +33 -0
- data/lib/mini_defender/rules/different.rb +31 -0
- data/lib/mini_defender/rules/digits.rb +26 -0
- data/lib/mini_defender/rules/digits_between.rb +29 -0
- data/lib/mini_defender/rules/distinct.rb +18 -0
- data/lib/mini_defender/rules/email.rb +19 -0
- data/lib/mini_defender/rules/ending_with.rb +31 -0
- data/lib/mini_defender/rules/equal.rb +9 -0
- data/lib/mini_defender/rules/excluded.rb +15 -0
- data/lib/mini_defender/rules/excluded_if.rb +28 -0
- data/lib/mini_defender/rules/excluded_unless.rb +28 -0
- data/lib/mini_defender/rules/excluded_with.rb +27 -0
- data/lib/mini_defender/rules/excluded_without.rb +27 -0
- data/lib/mini_defender/rules/exists.rb +29 -0
- data/lib/mini_defender/rules/expiry_date.rb +28 -0
- data/lib/mini_defender/rules/file.rb +17 -0
- data/lib/mini_defender/rules/filled.rb +22 -0
- data/lib/mini_defender/rules/greater_than.rb +34 -0
- data/lib/mini_defender/rules/greater_than_or_equal.rb +34 -0
- data/lib/mini_defender/rules/hash.rb +15 -0
- data/lib/mini_defender/rules/image.rb +19 -0
- data/lib/mini_defender/rules/in.rb +27 -0
- data/lib/mini_defender/rules/in_field.rb +40 -0
- data/lib/mini_defender/rules/integer.rb +19 -0
- data/lib/mini_defender/rules/ip.rb +43 -0
- data/lib/mini_defender/rules/ipv4.rb +43 -0
- data/lib/mini_defender/rules/ipv6.rb +43 -0
- data/lib/mini_defender/rules/json.rb +19 -0
- data/lib/mini_defender/rules/less_than.rb +34 -0
- data/lib/mini_defender/rules/less_than_or_equal.rb +34 -0
- data/lib/mini_defender/rules/luhn.rb +33 -0
- data/lib/mini_defender/rules/mac_address.rb +39 -0
- data/lib/mini_defender/rules/max.rb +9 -0
- data/lib/mini_defender/rules/max_digits.rb +32 -0
- data/lib/mini_defender/rules/mime_types.rb +37 -0
- data/lib/mini_defender/rules/min.rb +9 -0
- data/lib/mini_defender/rules/min_digits.rb +32 -0
- data/lib/mini_defender/rules/national_id.rb +13 -0
- data/lib/mini_defender/rules/not_ending_with.rb +31 -0
- data/lib/mini_defender/rules/not_in.rb +27 -0
- data/lib/mini_defender/rules/not_regex.rb +27 -0
- data/lib/mini_defender/rules/not_starting_with.rb +31 -0
- data/lib/mini_defender/rules/numeric.rb +19 -0
- data/lib/mini_defender/rules/present.rb +19 -0
- data/lib/mini_defender/rules/prohibited.rb +15 -0
- data/lib/mini_defender/rules/prohibited_if.rb +32 -0
- data/lib/mini_defender/rules/prohibited_unless.rb +32 -0
- data/lib/mini_defender/rules/regex.rb +27 -0
- data/lib/mini_defender/rules/required.rb +32 -0
- data/lib/mini_defender/rules/required_if.rb +26 -0
- data/lib/mini_defender/rules/required_unless.rb +26 -0
- data/lib/mini_defender/rules/required_with.rb +27 -0
- data/lib/mini_defender/rules/required_with_all.rb +27 -0
- data/lib/mini_defender/rules/required_without.rb +27 -0
- data/lib/mini_defender/rules/required_without_all.rb +27 -0
- data/lib/mini_defender/rules/size.rb +45 -0
- data/lib/mini_defender/rules/starting_with.rb +31 -0
- data/lib/mini_defender/rules/string.rb +15 -0
- data/lib/mini_defender/rules/timezone.rb +19 -0
- data/lib/mini_defender/rules/unique.rb +37 -0
- data/lib/mini_defender/rules/url.rb +17 -0
- data/lib/mini_defender/rules/uuid.rb +37 -0
- data/lib/mini_defender/rules.rb +8 -0
- data/lib/mini_defender/rules_expander.rb +19 -0
- data/lib/mini_defender/rules_factory.rb +36 -0
- data/lib/mini_defender/validates_input.rb +12 -0
- data/lib/mini_defender/validation_error.rb +10 -0
- data/lib/mini_defender/validator.rb +127 -0
- data/lib/mini_defender/version.rb +5 -0
- data/lib/mini_defender.rb +24 -0
- data/mini_defender.gemspec +38 -0
- data/sig/mini_defender.rbs +4 -0
- metadata +206 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::NotIn < MiniDefender::Rule
|
4
|
+
def initialize(values)
|
5
|
+
raise ArgumentError, 'Expected an array of values.' unless values.is_a?(Array)
|
6
|
+
|
7
|
+
@values = values
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signature
|
11
|
+
'not_in'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.make(args)
|
15
|
+
raise ArgumentError, 'Expected at least one argument.' unless args.length > 0
|
16
|
+
|
17
|
+
new(args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
@values.include?(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
"The value should not be any of the following #{@values.to_sentence}."
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::NotRegex < MiniDefender::Rule
|
4
|
+
def initialize(pattern)
|
5
|
+
raise ArgumentError, 'Expected a Regexp instance.' unless pattern.is_a?(Regexp)
|
6
|
+
|
7
|
+
@pattern = pattern
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signature
|
11
|
+
'not_regex'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.make(args)
|
15
|
+
raise ArgumentError, 'Expected a pattern as input.' unless args.length == 1
|
16
|
+
|
17
|
+
new(Regexp.compile(args[0]))
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
!value.to_s.match?(@pattern)
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
"The value must not match #{@pattern.to_s}."
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::NotStartingWith < MiniDefender::Rule
|
4
|
+
def initialize(fragments)
|
5
|
+
unless fragments.is_a?(Array) && !fragments.empty? && fragments.all? { |f| f.is_a?(String) }
|
6
|
+
raise ArgumentError, 'Expected an array of strings.'
|
7
|
+
end
|
8
|
+
|
9
|
+
@fragments = fragments
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.signature
|
13
|
+
'not_starting_with'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.make(args)
|
17
|
+
new(args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
@fragments.any? { |f| !value.to_s.start_with?(f) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
if @fragments.length == 1
|
26
|
+
"The value should not start with #{@fragments[0]}."
|
27
|
+
else
|
28
|
+
"The value should not start with one of the following #{@fragments.join(', ')}."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Numeric < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'numeric'
|
6
|
+
end
|
7
|
+
|
8
|
+
def coerce(value)
|
9
|
+
value.is_a?(Numeric) ? value : Float(value.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def passes?(attribute, value, validator)
|
13
|
+
value.is_a?(Numeric) || Float(value.to_s) rescue false
|
14
|
+
end
|
15
|
+
|
16
|
+
def message(attribute, value, validator)
|
17
|
+
"The field must contain a numeric value."
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Present < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'present'
|
6
|
+
end
|
7
|
+
|
8
|
+
def implicit?(validator)
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def passes?(attribute, value, validator)
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def message(attribute, value, validator)
|
17
|
+
"The field should be present."
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Prohibited < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'prohibited'
|
6
|
+
end
|
7
|
+
|
8
|
+
def passes?(attribute, value, validator)
|
9
|
+
value.blank?
|
10
|
+
end
|
11
|
+
|
12
|
+
def message(attribute, value, validator)
|
13
|
+
"This field is prohibited."
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::ProhibitedIf < MiniDefender::Rule
|
4
|
+
def initialize(target, value)
|
5
|
+
raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
|
6
|
+
|
7
|
+
@target = target
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.signature
|
12
|
+
'prohibited_if'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.make(args)
|
16
|
+
raise ArgumentError, 'Target and expected value are required.' unless args.length == 2
|
17
|
+
|
18
|
+
self.new(args[0], args[1])
|
19
|
+
end
|
20
|
+
|
21
|
+
def active?(validator)
|
22
|
+
validator.data.key?(@target) && validator.data[@target] == @value
|
23
|
+
end
|
24
|
+
|
25
|
+
def passes?(attribute, value, validator)
|
26
|
+
value.blank?
|
27
|
+
end
|
28
|
+
|
29
|
+
def message(attribute, value, validator)
|
30
|
+
"This field is prohibited."
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::ProhibitedUnless < MiniDefender::Rule
|
4
|
+
def initialize(target, value)
|
5
|
+
raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
|
6
|
+
|
7
|
+
@target = target
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.signature
|
12
|
+
'prohibited_unless'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.make(args)
|
16
|
+
raise ArgumentError, 'Target and expected value are required.' unless args.length == 2
|
17
|
+
|
18
|
+
self.new(args[0], args[1])
|
19
|
+
end
|
20
|
+
|
21
|
+
def active?(validator)
|
22
|
+
validator.data.key?(@target) && validator.data[@target] != @value
|
23
|
+
end
|
24
|
+
|
25
|
+
def passes?(attribute, value, validator)
|
26
|
+
value.blank?
|
27
|
+
end
|
28
|
+
|
29
|
+
def message(attribute, value, validator)
|
30
|
+
"This field is prohibited."
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Regex < MiniDefender::Rule
|
4
|
+
def initialize(pattern)
|
5
|
+
raise ArgumentError, 'Expected a Regexp instance.' unless pattern.is_a?(Regexp)
|
6
|
+
|
7
|
+
@pattern = pattern
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signature
|
11
|
+
'regex'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.make(args)
|
15
|
+
raise ArgumentError, 'Expected a pattern as input.' unless args.length == 1
|
16
|
+
|
17
|
+
new(Regexp.compile(args[0]))
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
value.to_s.match?(@pattern)
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
"The value must match #{@pattern.to_s}."
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Required < MiniDefender::Rule
|
6
|
+
def self.signature
|
7
|
+
'required'
|
8
|
+
end
|
9
|
+
|
10
|
+
def implicit?(validator)
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def passes?(attribute, value, validator)
|
15
|
+
case value
|
16
|
+
when nil
|
17
|
+
false
|
18
|
+
when String
|
19
|
+
value.strip.length > 0
|
20
|
+
when Enumerable
|
21
|
+
value.length > 0
|
22
|
+
when ActionDispatch::Http::UploadedFile
|
23
|
+
value.path && value.path.length > 0 && value.size > 0
|
24
|
+
else
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def message(attribute, value, validator)
|
30
|
+
"This field is required."
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'required'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::RequiredIf < MiniDefender::Rules::Required
|
6
|
+
def initialize(target, value)
|
7
|
+
raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
|
8
|
+
|
9
|
+
@target = target
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.signature
|
14
|
+
'required_if'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.make(args)
|
18
|
+
raise ArgumentError, 'Target and expected value are required.' unless args.length == 2
|
19
|
+
|
20
|
+
self.new(args[0], args[1])
|
21
|
+
end
|
22
|
+
|
23
|
+
def implicit?(validator)
|
24
|
+
validator.data.key?(@target) && validator.data[@target] == @value
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'required'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::RequiredUnless < MiniDefender::Rules::Required
|
6
|
+
def initialize(target, value)
|
7
|
+
raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
|
8
|
+
|
9
|
+
@target = target
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.signature
|
14
|
+
'required_unless'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.make(args)
|
18
|
+
raise ArgumentError, 'Target and expected value are required.' unless args.length == 2
|
19
|
+
|
20
|
+
self.new(args[0], args[1])
|
21
|
+
end
|
22
|
+
|
23
|
+
def implicit?(validator)
|
24
|
+
validator.data.key?(@target) && validator.data[@target] != @value
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'required'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::RequiredWith < MiniDefender::Rules::Required
|
6
|
+
def initialize(targets)
|
7
|
+
unless targets.is_a?(Array) && targets.all?{ |t| t.is_a?(String) }
|
8
|
+
raise ArgumentError, 'Expected an array of strings.'
|
9
|
+
end
|
10
|
+
|
11
|
+
@targets = targets
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.signature
|
15
|
+
'required_with'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(args)
|
19
|
+
raise ArgumentError, 'Expected at least one argument.' unless args.length >= 1
|
20
|
+
|
21
|
+
self.new(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def implicit?(validator)
|
25
|
+
@targets.any? { |t| validator.data.key?(t) }
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'required'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::RequiredWithAll < MiniDefender::Rules::Required
|
6
|
+
def initialize(targets)
|
7
|
+
unless targets.is_a?(Array) && targets.all?{ |t| t.is_a?(String) }
|
8
|
+
raise ArgumentError, 'Expected an array of strings.'
|
9
|
+
end
|
10
|
+
|
11
|
+
@targets = targets
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.signature
|
15
|
+
'required_with_all'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(args)
|
19
|
+
raise ArgumentError, 'Expected at least one argument.' unless args.length >= 1
|
20
|
+
|
21
|
+
self.new(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def implicit?(validator)
|
25
|
+
@targets.all? { |t| validator.data.key?(t) }
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'required'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::RequiredWithout < MiniDefender::Rules::Required
|
6
|
+
def initialize(targets)
|
7
|
+
unless targets.is_a?(Array) && targets.all?{ |t| t.is_a?(String) }
|
8
|
+
raise ArgumentError, 'Expected an array of strings.'
|
9
|
+
end
|
10
|
+
|
11
|
+
@targets = targets
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.signature
|
15
|
+
'required_without'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(args)
|
19
|
+
raise ArgumentError, 'Expected at least one argument.' unless args.length >= 1
|
20
|
+
|
21
|
+
self.new(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def implicit?(validator)
|
25
|
+
!@targets.any? { |t| validator.data.key?(t) }
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'required'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::RequiredWithoutAll < MiniDefender::Rules::Required
|
6
|
+
def initialize(targets)
|
7
|
+
unless targets.is_a?(Array) && targets.all?{ |t| t.is_a?(String) }
|
8
|
+
raise ArgumentError, 'Expected an array of strings.'
|
9
|
+
end
|
10
|
+
|
11
|
+
@targets = targets
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.signature
|
15
|
+
'required_without_all'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(args)
|
19
|
+
raise ArgumentError, 'Expected at least one argument.' unless args.length >= 1
|
20
|
+
|
21
|
+
self.new(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def implicit?(validator)
|
25
|
+
@targets.none? { |t| validator.data.key?(t) }
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Size < MiniDefender::Rule
|
6
|
+
def initialize(size)
|
7
|
+
raise ArgumentError, 'Size must be an integer.' unless size.is_a?(Integer)
|
8
|
+
|
9
|
+
@size = size
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.signature
|
13
|
+
'size'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.make(args)
|
17
|
+
raise ArgumentError, 'Expected exactly one argument.' unless args.length == 1
|
18
|
+
|
19
|
+
self.new(args[0].to_i)
|
20
|
+
end
|
21
|
+
|
22
|
+
def passes?(attribute, value, validator)
|
23
|
+
case value
|
24
|
+
when String, Array, Hash
|
25
|
+
value.length == @size
|
26
|
+
when ActionDispatch::Http::UploadedFile
|
27
|
+
value.size == @size
|
28
|
+
when Numeric
|
29
|
+
value == @size
|
30
|
+
else
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def message(attribute, value, validator)
|
36
|
+
case value
|
37
|
+
when ActionDispatch::Http::UploadedFile
|
38
|
+
"The file size must be equal to #{@size} bytes."
|
39
|
+
when Numeric
|
40
|
+
"The value must be equal to #{@size}."
|
41
|
+
else
|
42
|
+
"The value length must be equal to #{@size}."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::StartingWith < MiniDefender::Rule
|
4
|
+
def initialize(fragments)
|
5
|
+
unless fragments.is_a?(Array) && !fragments.empty? && fragments.all? { |f| f.is_a?(String) }
|
6
|
+
raise ArgumentError, 'Expected an array of strings.'
|
7
|
+
end
|
8
|
+
|
9
|
+
@fragments = fragments
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.signature
|
13
|
+
'starting_with'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.make(args)
|
17
|
+
new(args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
@fragments.any? { |f| value.to_s.start_with?(f) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
if @fragments.length == 1
|
26
|
+
"The value should start with #{@fragments[0]}."
|
27
|
+
else
|
28
|
+
"The value should start with one of the following #{@fragments.join(', ')}."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::String < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'string'
|
6
|
+
end
|
7
|
+
|
8
|
+
def passes?(attribute, value, validator)
|
9
|
+
value.is_a?(String)
|
10
|
+
end
|
11
|
+
|
12
|
+
def message(attribute, value, validator)
|
13
|
+
"The value must be a string."
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tzinfo'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Timezone < MiniDefender::Rule
|
6
|
+
def self.signature
|
7
|
+
'timezone'
|
8
|
+
end
|
9
|
+
|
10
|
+
def passes?(attribute, value, validator)
|
11
|
+
value.is_a?(String) && !!TZInfo::Timezone.get(value)
|
12
|
+
rescue TZInfo::InvalidTimezoneIdentifier
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def message(attribute, value, validator)
|
17
|
+
'The field should contain a valid time zone value.'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Unique < MiniDefender::Rule
|
4
|
+
def initialize(model, column)
|
5
|
+
raise ArgumentError, 'model name must be a string or ActiveRecord::Base' unless model.is_a?(String)
|
6
|
+
raise ArgumentError, 'Column name must be a string' unless column.is_a?(String)
|
7
|
+
|
8
|
+
@model = model.camelcase.constantize
|
9
|
+
@column = column
|
10
|
+
@ignore = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.signature
|
14
|
+
'unique'
|
15
|
+
end
|
16
|
+
|
17
|
+
def should_ignore(value)
|
18
|
+
@ignore = value
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.make(args)
|
23
|
+
raise ArgumentError, 'Model and column are required.' unless args.length == 2
|
24
|
+
|
25
|
+
self.new(args[0], args[1])
|
26
|
+
end
|
27
|
+
|
28
|
+
def passes?(attribute, value, validator)
|
29
|
+
query = @model.where(@column => value)
|
30
|
+
query = query.where.not(@column, @ignore) unless @ignore.nil?
|
31
|
+
query.exists?
|
32
|
+
end
|
33
|
+
|
34
|
+
def message(attribute, value, validator)
|
35
|
+
"The value already exists."
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Url < MiniDefender::Rule
|
6
|
+
def self.signature
|
7
|
+
'url'
|
8
|
+
end
|
9
|
+
|
10
|
+
def passes?(attribute, value, validator)
|
11
|
+
value.is_a?(String) && URI.regexp(%w[http https]).match?(value)
|
12
|
+
end
|
13
|
+
|
14
|
+
def message(attribute, value, validator)
|
15
|
+
'The field must contain a valid URL.'
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Uuid < MiniDefender::Rule
|
4
|
+
def initialize(version = nil)
|
5
|
+
raise ArgumentError, 'Expected version to be an integer or nil' unless version.nil? || version.is_a?(Integer)
|
6
|
+
|
7
|
+
@version = version
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signature
|
11
|
+
'uuid'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.make(args)
|
15
|
+
raise ArgumentError, 'Expected exactly one or zero argument' unless args.length <= 1
|
16
|
+
|
17
|
+
new(args[0]&.to_i)
|
18
|
+
end
|
19
|
+
|
20
|
+
def coerce(value)
|
21
|
+
value.downcase
|
22
|
+
end
|
23
|
+
|
24
|
+
def passes?(attribute, value, validator)
|
25
|
+
value.is_a?(String) &&
|
26
|
+
/^\h{8}-(\h{4}-){3}\h{12}$/i.match?(value) &&
|
27
|
+
(@version.nil? || value[14].to_i(16) == @version)
|
28
|
+
end
|
29
|
+
|
30
|
+
def message(attribute, value, validator)
|
31
|
+
if @version
|
32
|
+
"The value should be a valid UUID v#{@version}."
|
33
|
+
else
|
34
|
+
'The value should be a valid UUID.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal
|
2
|
+
|
3
|
+
module MiniDefender
|
4
|
+
class RulesExpander
|
5
|
+
# @param [Hash] rules
|
6
|
+
# @param [Hash] flat_data
|
7
|
+
# @return [Hash]
|
8
|
+
def expand(rules, flat_data)
|
9
|
+
rules
|
10
|
+
.map { |k, v| [Regexp.compile('\A' + k.gsub(/\*/, '\d+') + '\Z'), v] }.to_h
|
11
|
+
.map { |p, set|
|
12
|
+
data_rules = flat_data.filter { |k, _| p.match? k }.map { |k, _| [k, set] }
|
13
|
+
data_rules.length > 0 ? data_rules : [[p.source.gsub(/\\[AZ]/, '').gsub('\d+', '0'), set]]
|
14
|
+
}
|
15
|
+
.flatten(1)
|
16
|
+
.to_h
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|