mini_defender 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|