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,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Filled < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'filled'
|
6
|
+
end
|
7
|
+
|
8
|
+
def passes?(attribute, value, validator)
|
9
|
+
case value
|
10
|
+
when String
|
11
|
+
!value.strip.empty?
|
12
|
+
when Array, Hash
|
13
|
+
!value.empty?
|
14
|
+
else
|
15
|
+
!value.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def message(attribute, value, validator)
|
20
|
+
'The field should not be empty.'
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
require_relative 'size'
|
5
|
+
|
6
|
+
class MiniDefender::Rules::GreaterThan < MiniDefender::Rules::Size
|
7
|
+
def self.signature
|
8
|
+
'gt'
|
9
|
+
end
|
10
|
+
|
11
|
+
def passes?(attribute, value, validator)
|
12
|
+
case value
|
13
|
+
when String, Array, Hash
|
14
|
+
value.length > @size
|
15
|
+
when ActionDispatch::Http::UploadedFile
|
16
|
+
value.size > @size
|
17
|
+
when Numeric
|
18
|
+
value > @size
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
case value
|
26
|
+
when ActionDispatch::Http::UploadedFile
|
27
|
+
"The file size must be greater than #{@size} bytes."
|
28
|
+
when Numeric
|
29
|
+
"The value must be greater than #{@size}."
|
30
|
+
else
|
31
|
+
"The value length must be greater than #{@size}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
require_relative 'size'
|
5
|
+
|
6
|
+
class MiniDefender::Rules::GreaterThanOrEqual < MiniDefender::Rules::Size
|
7
|
+
def self.signature
|
8
|
+
'gte'
|
9
|
+
end
|
10
|
+
|
11
|
+
def passes?(attribute, value, validator)
|
12
|
+
case value
|
13
|
+
when String, Array, Hash
|
14
|
+
value.length >= @size
|
15
|
+
when ActionDispatch::Http::UploadedFile
|
16
|
+
value.size >= @size
|
17
|
+
when Numeric
|
18
|
+
value >= @size
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
case value
|
26
|
+
when ActionDispatch::Http::UploadedFile
|
27
|
+
"The file size must be greater than or equal to #{@size} bytes."
|
28
|
+
when Numeric
|
29
|
+
"The value must be greater than or equal to #{@size}."
|
30
|
+
else
|
31
|
+
"The value length must be greater than or equal to #{@size}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Hash < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'hash'
|
6
|
+
end
|
7
|
+
|
8
|
+
def passes?(attribute, value, validator)
|
9
|
+
value.is_a?(Hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
def message(attribute, value, validator)
|
13
|
+
"The field must be an object."
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Image < MiniDefender::Rule
|
6
|
+
MIMES = %w[image/jpeg image/png image/gif image/bmp image/png image/svg+xml image/webp]
|
7
|
+
|
8
|
+
def self.signature
|
9
|
+
'image'
|
10
|
+
end
|
11
|
+
|
12
|
+
def passes?(attribute, value, validator)
|
13
|
+
value.is_a?(ActionDispatch::Http::UploadedFile) && MIMES.include?(value.content_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def message(attribute, value, validator)
|
17
|
+
"The field should be an image of type jpg, jpeg, png, bmp, gif, svg, or webp."
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::In < 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
|
+
'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 must be one of #{@values.to_sentence}."
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::InField < MiniDefender::Rule
|
4
|
+
def initialize(field)
|
5
|
+
raise ArgumentError, 'Field must be a string.' unless field.is_a?(String)
|
6
|
+
|
7
|
+
@field = field
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signature
|
11
|
+
'in_field'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.make(args)
|
15
|
+
raise ArgumentError, 'Expected one argument, target field name.' unless args.length == 1
|
16
|
+
|
17
|
+
new(args[0])
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
@field_value = nil
|
22
|
+
return false unless validator.data.key(@field)
|
23
|
+
|
24
|
+
@field_value = validator.data[@field]
|
25
|
+
return false unless field.is_a?(Array)
|
26
|
+
|
27
|
+
@field_value.include?(value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def message(attribute, value, validator)
|
31
|
+
case @field_value
|
32
|
+
when nil
|
33
|
+
"The field (#{@field}) is missing."
|
34
|
+
when Array
|
35
|
+
"The field (#{@field}) must be an array."
|
36
|
+
else
|
37
|
+
"The value must be one of the values found in #{@field}."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Integer < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'integer'
|
6
|
+
end
|
7
|
+
|
8
|
+
def coerce(value)
|
9
|
+
value.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def passes?(attribute, value, validator)
|
13
|
+
value.is_a?(Integer) || value.is_a?(String) && value.match?(/^\d+$/)
|
14
|
+
end
|
15
|
+
|
16
|
+
def message(attribute, value, validator)
|
17
|
+
"The value must be an integer."
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ipaddr'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Ip < MiniDefender::Rule
|
6
|
+
MODES = %w[any public private]
|
7
|
+
|
8
|
+
def initialize(mode = 'any')
|
9
|
+
raise ArgumentError, 'Invalid mode' unless MODES.include?(mode)
|
10
|
+
|
11
|
+
@mode = mode
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.signature
|
15
|
+
'ip'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(args)
|
19
|
+
new(args[0] || 'any')
|
20
|
+
end
|
21
|
+
|
22
|
+
def passes?(attribute, value, validator)
|
23
|
+
ip = IPAddr.new(value.to_s)
|
24
|
+
(
|
25
|
+
@mode == 'any' ||
|
26
|
+
@mode == 'private' && (ip.private? || ip.link_local? || ip.loopback?) ||
|
27
|
+
@mode == 'public' && !(ip.private? || ip.link_local? || ip.loopback?)
|
28
|
+
)
|
29
|
+
rescue IPAddr::InvalidAddressError
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def message(attribute, value, validator)
|
34
|
+
case @mode
|
35
|
+
when 'public'
|
36
|
+
"The value must be a valid public IP address."
|
37
|
+
when 'private'
|
38
|
+
"The value must be a valid private IP address."
|
39
|
+
else
|
40
|
+
"The value must be a valid IP address."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ipaddr'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Ipv4 < MiniDefender::Rule
|
6
|
+
MODES = %w[any public private]
|
7
|
+
|
8
|
+
def initialize(mode = 'any')
|
9
|
+
raise ArgumentError, 'Invalid mode' unless MODES.include?(mode)
|
10
|
+
|
11
|
+
@mode = mode
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.signature
|
15
|
+
'ipv4'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(args)
|
19
|
+
new(args[0] || 'any')
|
20
|
+
end
|
21
|
+
|
22
|
+
def passes?(attribute, value, validator)
|
23
|
+
ip = IPAddr.new(value.to_s)
|
24
|
+
ip.ipv4? && (
|
25
|
+
@mode == 'any' ||
|
26
|
+
@mode == 'private' && (ip.private? || ip.link_local? || ip.loopback?) ||
|
27
|
+
@mode == 'public' && !(ip.private? || ip.link_local? || ip.loopback?)
|
28
|
+
)
|
29
|
+
rescue IPAddr::InvalidAddressError
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def message(attribute, value, validator)
|
34
|
+
case @mode
|
35
|
+
when 'public'
|
36
|
+
"The value must be a valid public IPv4 address."
|
37
|
+
when 'private'
|
38
|
+
"The value must be a valid private IPv4 address."
|
39
|
+
else
|
40
|
+
"The value must be a valid IPv4 address."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ipaddr'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Ipv6 < MiniDefender::Rule
|
6
|
+
MODES = %w[any public private]
|
7
|
+
|
8
|
+
def initialize(mode = 'any')
|
9
|
+
raise ArgumentError, 'Invalid mode' unless MODES.include?(mode)
|
10
|
+
|
11
|
+
@mode = mode
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.signature
|
15
|
+
'ipv6'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(args)
|
19
|
+
new(args[0] || 'any')
|
20
|
+
end
|
21
|
+
|
22
|
+
def passes?(attribute, value, validator)
|
23
|
+
ip = IPAddr.new(value.to_s)
|
24
|
+
ip.ipv6? && (
|
25
|
+
@mode == 'any' ||
|
26
|
+
@mode == 'private' && (ip.private? || ip.link_local? || ip.loopback?) ||
|
27
|
+
@mode == 'public' && !(ip.private? || ip.link_local? || ip.loopback?)
|
28
|
+
)
|
29
|
+
rescue IPAddr::InvalidAddressError
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def message(attribute, value, validator)
|
34
|
+
case @mode
|
35
|
+
when 'public'
|
36
|
+
"The value must be a valid public IPv6 address."
|
37
|
+
when 'private'
|
38
|
+
"The value must be a valid private IPv6 address."
|
39
|
+
else
|
40
|
+
"The value must be a valid IPv6 address."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::Json < MiniDefender::Rule
|
6
|
+
def self.signature
|
7
|
+
'json'
|
8
|
+
end
|
9
|
+
|
10
|
+
def passes?(attribute, value, validator)
|
11
|
+
JSON.parse(value)
|
12
|
+
rescue JSON::ParserError
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def message(attribute, value, validator)
|
17
|
+
"The value should be a valid JSON string."
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
require_relative 'size'
|
5
|
+
|
6
|
+
class MiniDefender::Rules::LessThan < MiniDefender::Rules::Size
|
7
|
+
def self.signature
|
8
|
+
'lt'
|
9
|
+
end
|
10
|
+
|
11
|
+
def passes?(attribute, value, validator)
|
12
|
+
case value
|
13
|
+
when String, Array, Hash
|
14
|
+
value.length < @size
|
15
|
+
when ActionDispatch::Http::UploadedFile
|
16
|
+
value.size < @size
|
17
|
+
when Numeric
|
18
|
+
value < @size
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
case value
|
26
|
+
when ActionDispatch::Http::UploadedFile
|
27
|
+
"The file size must be less than #{@size} bytes."
|
28
|
+
when Numeric
|
29
|
+
"The value must be less than #{@size}."
|
30
|
+
else
|
31
|
+
"The value length must be less than #{@size}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
require_relative 'size'
|
5
|
+
|
6
|
+
class MiniDefender::Rules::LessThanOrEqual < MiniDefender::Rules::Size
|
7
|
+
def self.signature
|
8
|
+
'lte'
|
9
|
+
end
|
10
|
+
|
11
|
+
def passes?(attribute, value, validator)
|
12
|
+
case value
|
13
|
+
when String, Array, Hash
|
14
|
+
value.length <= @size
|
15
|
+
when ActionDispatch::Http::UploadedFile
|
16
|
+
value.size <= @size
|
17
|
+
when Numeric
|
18
|
+
value <= @size
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
case value
|
26
|
+
when ActionDispatch::Http::UploadedFile
|
27
|
+
"The file size must be less than or equal to #{@size} bytes."
|
28
|
+
when Numeric
|
29
|
+
"The value must be less than or equal to #{@size}."
|
30
|
+
else
|
31
|
+
"The value length must be less than or equal to #{@size}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::Luhn < MiniDefender::Rule
|
4
|
+
def self.signature
|
5
|
+
'luhn'
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.valid_luhn?(value)
|
9
|
+
value = value.to_s
|
10
|
+
return false unless value.match?(/\A\d+\z/)
|
11
|
+
|
12
|
+
double_sum = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
|
13
|
+
sum = 0
|
14
|
+
|
15
|
+
value.to_i.digits.each_with_index do |digit, index|
|
16
|
+
sum += index.even? ? digit : double_sum[digit]
|
17
|
+
end
|
18
|
+
|
19
|
+
sum % 10 === 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def coerce(value)
|
23
|
+
value.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def passes?(attribute, value, validator)
|
27
|
+
value.is_a?(String) && self.class.valid_luhn?(value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def message(attribute, value, validator)
|
31
|
+
'The value must be a valid Luhn string.'
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::MacAddress < MiniDefender::Rule
|
4
|
+
MODES = %w[any local universal]
|
5
|
+
|
6
|
+
def initialize(mode = 'any')
|
7
|
+
raise ArgumentError, 'Invalid mode' unless MODES.include?(mode)
|
8
|
+
|
9
|
+
@mode = mode
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.signature
|
13
|
+
'mac'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.make(args)
|
17
|
+
new(args[0] || 'any')
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
clean = value.to_s.gsub(/[-:]/)
|
22
|
+
clean.match?(/[0-9A-F]{12}/i) && (
|
23
|
+
@mode == 'any' ||
|
24
|
+
@mode == 'local' && (clean.hex & 0x020000000000 > 0) ||
|
25
|
+
@mode == 'universal' && (clean.hex & 0x020000000000 == 0)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def message(attribute, value, validator)
|
30
|
+
case @mode
|
31
|
+
when 'local'
|
32
|
+
"The value must be a locally administrated MAC address."
|
33
|
+
when 'universal'
|
34
|
+
"The value must be a universally administrated MAC address."
|
35
|
+
else
|
36
|
+
"The value must be a properly formatted mac address."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::MaxDigits < MiniDefender::Rule
|
4
|
+
def initialize(limit)
|
5
|
+
raise ArgumentError, 'Limit must be a string.' unless limit.is_a?(Integer)
|
6
|
+
|
7
|
+
@limit = limit
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signature
|
11
|
+
'max_digits'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.make(args)
|
15
|
+
raise ArgumentError, 'Expected at least one argument for max_digits.' unless args.length == 1
|
16
|
+
|
17
|
+
new(args[0].to_i)
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
@integers = valid = value.is_a?(Integer) || value.is_a?(String) && value.match?(/^\d+$/)
|
22
|
+
valid && value.to_s.length <= @limit
|
23
|
+
end
|
24
|
+
|
25
|
+
def message(attribute, value, validator)
|
26
|
+
if @integers
|
27
|
+
'The field should only contain digits.'
|
28
|
+
else
|
29
|
+
"The field should have at most #{@limit} digits."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::MimeTypes < MiniDefender::Rule
|
6
|
+
def initialize(types)
|
7
|
+
unless types.is_a?(Array) && types.all?{ |t| t.is_a?(String) }
|
8
|
+
raise ArgumentError, 'Expected an array of strings.'
|
9
|
+
end
|
10
|
+
|
11
|
+
@types = types
|
12
|
+
@file = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.signature
|
16
|
+
'mime'
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.make(args)
|
20
|
+
raise ArgumentError, 'Expected at least one MIME type.' unless args.length > 0
|
21
|
+
|
22
|
+
new(args.split(',').map(&:downcase).map(&:strip))
|
23
|
+
end
|
24
|
+
|
25
|
+
def passes?(attribute, value, validator)
|
26
|
+
@file = value.is_a?(ActionDispatch::Http::UploadedFile)
|
27
|
+
@file && @types.include?(@file.content_type)
|
28
|
+
end
|
29
|
+
|
30
|
+
def message(attribute, value, validator)
|
31
|
+
if @file
|
32
|
+
"The file should be one of the following types #{@types.to_sentence}"
|
33
|
+
else
|
34
|
+
'The field should be a file.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::MinDigits < MiniDefender::Rule
|
4
|
+
def initialize(limit)
|
5
|
+
raise ArgumentError, 'Limit must be a string.' unless limit.is_a?(Integer)
|
6
|
+
|
7
|
+
@limit = limit
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signature
|
11
|
+
'min_digits'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.make(args)
|
15
|
+
raise ArgumentError, 'Expected at least one argument for min_digits.' unless args.length == 1
|
16
|
+
|
17
|
+
new(args[0].to_i)
|
18
|
+
end
|
19
|
+
|
20
|
+
def passes?(attribute, value, validator)
|
21
|
+
@integers = valid = value.is_a?(Integer) || value.is_a?(String) && value.match?(/^\d+$/)
|
22
|
+
valid && value.to_s.length >= @limit
|
23
|
+
end
|
24
|
+
|
25
|
+
def message(attribute, value, validator)
|
26
|
+
if @integers
|
27
|
+
'The field should only contain digits.'
|
28
|
+
else
|
29
|
+
"The field should have at least #{@limit} digits."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'luhn'
|
4
|
+
|
5
|
+
class MiniDefender::Rules::NationalId < MiniDefender::Rules::Luhn
|
6
|
+
def self.signature
|
7
|
+
'national_id'
|
8
|
+
end
|
9
|
+
|
10
|
+
def message(attribute, value, validator)
|
11
|
+
'The value must be a proper national ID number.'
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniDefender::Rules::NotEndingWith < 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_ending_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.end_with?(f) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def message(attribute, value, validator)
|
25
|
+
if @fragments.length == 1
|
26
|
+
"The value should not end with #{@fragments[0]}."
|
27
|
+
else
|
28
|
+
"The value should not end with one of the following #{@fragments.join(', ')}."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|