mini_defender 0.6.5 → 0.6.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12e38770ec94ea06cf7c9a6f40af0cc37fc2017deb30161c2804c9e3024d7ad4
4
- data.tar.gz: 36738ff2ca0ad2316dbb0f68fee90ad921690fab463cf4050103789edb975212
3
+ metadata.gz: 3bae6fdb0faacfd54fe463d30f80710e40d383bd868d64e65da7243bae0c5b1c
4
+ data.tar.gz: f9cf13d8acbc654b9c647c33b322937722b97f0a99b8d389d4311002a3a5a65e
5
5
  SHA512:
6
- metadata.gz: 94d2291d0f17296b246ae67f96685f20dd7c7316d7ed329d89fc2141d76af57c538ca3909b52be177b9d08663448c2941669668861ba0408d40bdb10f75ad462
7
- data.tar.gz: d5f5aab2a675b44ce991573938eea9ca9782e954971365d71102ba6e8081b802d76cffa4f3382354bbf04695028e88cede5686146c28003ef1c1ee4f8f33e30a
6
+ metadata.gz: 2c50350da6f341f0487357cf63cad4dbf070c5c8f89614344619182dffd9b7f345b3015466e185fe4114cadcd2a3f7159bbf87e7413bd0d887327cdb13cebbce
7
+ data.tar.gz: 8bb5e5e2702c985a7c3933147d0db5f59abde4e566f8747aa6158d21fb5bd440dcf6f1d8706ae0846e0532a14d918d2b36d04673f519d98c72412b480c25fef4
@@ -0,0 +1,64 @@
1
+ # RFC 2606 - Reserved Domain Names
2
+ # https://datatracker.ietf.org/doc/html/rfc2606
3
+ *.test
4
+ *.example
5
+ *.invalid
6
+ localhost
7
+ *.localhost
8
+ example.com
9
+ example.net
10
+ example.org
11
+
12
+ # RFC 6761 - Special-Use Domain Names
13
+ # https://datatracker.ietf.org/doc/html/rfc6761
14
+ *.local
15
+ *.onion
16
+ *.home.arpa
17
+
18
+ # RFC 1918 - Private Address Space
19
+ # https://datatracker.ietf.org/doc/html/rfc1918
20
+ 10.*
21
+ 172.(1[6-9]|2[0-9]|3[0-1]).*
22
+ 192.168.*
23
+
24
+ # RFC 3330 - Special-Use IPv4 Addresses
25
+ # https://datatracker.ietf.org/doc/html/rfc3330
26
+ 127.*
27
+ 169.254.*
28
+ 0.0.0.0
29
+
30
+ # RFC 4291 - IPv6 Addressing Architecture
31
+ # https://datatracker.ietf.org/doc/html/rfc4291
32
+ ::1
33
+ fe80:*
34
+ ::
35
+ ::ffff:*
36
+
37
+ # RFC 4193 - Unique Local IPv6 Unicast Addresses
38
+ # https://datatracker.ietf.org/doc/html/rfc4193
39
+ fc00:*
40
+ fd00:*
41
+
42
+ # Common Internal Network Patterns (based on RFC 2606 and 6761)
43
+ *.intranet
44
+ *.internal
45
+ *.corp
46
+ *.lan
47
+ intranet.*
48
+ internal.*
49
+ corp.*
50
+ lan.*
51
+
52
+ # Development Environments (based on RFC 2606)
53
+ *.dev.test
54
+ *.staging.test
55
+ *.qa.test
56
+ dev.example.*
57
+ staging.example.*
58
+ qa.example.*
59
+
60
+ # RFC 6052 - IPv6 Translation
61
+ 64:ff9b::*
62
+
63
+ # RFC 3986 - URI Encoded Forms
64
+ %6C%6F%63%61%6C%68%6F%73%74
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'action_dispatch'
4
+ require 'marcel'
4
5
 
5
6
  class MiniDefender::Rules::Image < MiniDefender::Rule
6
7
  MIMES = %w[image/jpeg image/png image/gif image/bmp image/png image/svg+xml image/webp]
@@ -10,7 +11,10 @@ class MiniDefender::Rules::Image < MiniDefender::Rule
10
11
  end
11
12
 
12
13
  def passes?(attribute, value, validator)
13
- value.is_a?(ActionDispatch::Http::UploadedFile) && MIMES.include?(value.content_type)
14
+ content_type = Marcel::MimeType.for(value.read)
15
+ value.rewind
16
+
17
+ value.is_a?(ActionDispatch::Http::UploadedFile) && MIMES.include?(content_type)
14
18
  end
15
19
 
16
20
  def message(attribute, value, validator)
@@ -54,9 +54,10 @@ class MiniDefender::Rules::Integer < MiniDefender::Rule
54
54
  end
55
55
 
56
56
  # Remove leading zero so Integer will not treat it as octal
57
+ # Handle leading zeros while preserving both + and - signs
57
58
  value = value
58
59
  .to_s
59
- .gsub(/^0+/, '')
60
+ .gsub(/^([+-])?0+(?=\d)/, '\1')
60
61
 
61
62
  if @mode == 'relaxed'
62
63
  value = normalize_digits(value)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'action_dispatch'
4
+ require 'marcel'
4
5
 
5
6
  class MiniDefender::Rules::MimeTypes < MiniDefender::Rule
6
7
  def initialize(types)
@@ -26,7 +27,10 @@ class MiniDefender::Rules::MimeTypes < MiniDefender::Rule
26
27
 
27
28
  def passes?(attribute, value, validator)
28
29
  @file = value.is_a?(ActionDispatch::Http::UploadedFile)
29
- @file && @types.include?(value.content_type)
30
+ content_type = Marcel::MimeType.for(value.read)
31
+ value.rewind
32
+
33
+ @file && @types.include?(content_type)
30
34
  end
31
35
 
32
36
  def message(attribute, value, validator)
@@ -1,17 +1,140 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'uri'
4
+ require 'ipaddr'
5
+ require 'public_suffix'
4
6
 
5
7
  class MiniDefender::Rules::Url < MiniDefender::Rule
8
+ ALLOWED_MODIFIERS = %w[https public not_ip not_private]
9
+
10
+ def initialize(modifiers = [])
11
+ @modifiers = Array(modifiers).map(&:to_s)
12
+
13
+ unless @modifiers.empty?
14
+ validate_modifiers!
15
+ end
16
+
17
+ @validation_error = nil
18
+ end
19
+
6
20
  def self.signature
7
21
  'url'
8
22
  end
9
23
 
24
+ def self.make(modifiers) # no need to raise an error when no modifier is entered; as 'url' rule checks URL structure on its own
25
+ new(modifiers)
26
+ end
27
+
10
28
  def passes?(attribute, value, validator)
11
- value.is_a?(String) && URI.regexp(%w[http https]).match?(value)
29
+ unless value.is_a?(String)
30
+ return false
31
+ end
32
+
33
+ begin
34
+ uri = URI.parse(value)
35
+
36
+ if uri.host.blank? || uri.scheme.blank?
37
+ return false
38
+ end
39
+
40
+ unless %w[http https].include?(uri.scheme)
41
+ @validation_error = 'URL protocol must be HTTP or HTTPS.'
42
+ return false
43
+ end
44
+
45
+ if @modifiers.empty?
46
+ return true
47
+ end
48
+
49
+ if @modifiers.include?('https') && uri.scheme != 'https'
50
+ @validation_error = 'The URL must use HTTPS.'
51
+ return false
52
+ end
53
+
54
+ if @modifiers.include?('public') && (!PublicSuffix.valid?(uri.host) || private_network?(uri.host))
55
+ @validation_error = 'The URL must use a valid public domain.'
56
+ return false
57
+ end
58
+
59
+ if @modifiers.include?('not_ip') && ip_address?(uri.host)
60
+ @validation_error = 'IP addresses are not allowed in URLs.'
61
+ return false
62
+ end
63
+
64
+ if @modifiers.include?('not_private') && private_network?(uri.host)
65
+ @validation_error = 'Private or reserved resources are not allowed.'
66
+ return false
67
+ end
68
+
69
+ true
70
+ rescue URI::InvalidURIError
71
+ @validation_error = 'The field must contain a valid URL.'
72
+ false
73
+ rescue PublicSuffix::Error
74
+ false
75
+ end
12
76
  end
13
77
 
14
78
  def message(attribute, value, validator)
15
- 'The field must contain a valid URL.'
79
+ @validation_error || 'The field must contain a valid URL.'
80
+ end
81
+
82
+ def private_network?(host)
83
+ unless host
84
+ return false
85
+ end
86
+
87
+ host = host.downcase
88
+
89
+ private_patterns.any? { |pattern| pattern.match?(host) }
90
+ end
91
+
92
+ private
93
+
94
+ def validate_modifiers!
95
+ invalid_modifiers = @modifiers - ALLOWED_MODIFIERS
96
+ if invalid_modifiers.empty?
97
+ return
98
+ end
99
+
100
+ raise ArgumentError, "Invalid URL modifiers: #{invalid_modifiers.join(', ')}"
101
+ end
102
+
103
+ def ip_address?(host)
104
+ unless host
105
+ return false
106
+ end
107
+
108
+ begin
109
+ IPAddr.new(host)
110
+ true
111
+ rescue IPAddr::InvalidAddressError
112
+ false
113
+ end
114
+ end
115
+
116
+ def private_patterns
117
+ # Cache the result in class variable to avoid loading again
118
+ # across multiple instances
119
+ @@private_patterns ||= begin
120
+ pattern_file = File.expand_path('../data/private_network_patterns.txt', __dir__)
121
+
122
+ File.readlines(pattern_file).filter_map do |line|
123
+ line = line.strip
124
+
125
+ if line.empty? || line.start_with?('#')
126
+ next
127
+ end
128
+
129
+ # Pattern => regex (once)
130
+ pattern = line
131
+ .gsub('.', '\.') # escape dots
132
+ .gsub('*', '.*') # wildcards => regex
133
+ .gsub('[0-9]+', '\d+') # convert number ranges
134
+ .gsub(/\[(.+?)\]/, '(\1)') # convert chars classes
135
+
136
+ Regexp.new("^#{pattern}$", Regexp::IGNORECASE)
137
+ end
138
+ end
16
139
  end
17
140
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniDefender
4
- VERSION = "0.6.5"
4
+ VERSION = "0.6.8"
5
5
  end
@@ -33,4 +33,6 @@ Gem::Specification.new do |spec|
33
33
  spec.add_runtime_dependency 'actionpack', '>= 6.0'
34
34
  spec.add_runtime_dependency 'countries'
35
35
  spec.add_runtime_dependency 'money'
36
+ spec.add_runtime_dependency 'marcel'
37
+ spec.add_runtime_dependency 'public_suffix'
36
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_defender
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.6.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ali Alhoshaiyan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-29 00:00:00.000000000 Z
11
+ date: 2024-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: marcel
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: public_suffix
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  description: A small and efficient validation library for Rails and anything that
84
112
  uses Ruby.
85
113
  email:
@@ -96,6 +124,7 @@ files:
96
124
  - RULES.md
97
125
  - Rakefile
98
126
  - lib/mini_defender.rb
127
+ - lib/mini_defender/data/private_network_patterns.txt
99
128
  - lib/mini_defender/extensions/enumerable.rb
100
129
  - lib/mini_defender/handles_validation_errors.rb
101
130
  - lib/mini_defender/rule.rb
@@ -219,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
248
  - !ruby/object:Gem::Version
220
249
  version: '0'
221
250
  requirements: []
222
- rubygems_version: 3.4.10
251
+ rubygems_version: 3.5.3
223
252
  signing_key:
224
253
  specification_version: 4
225
254
  summary: A small and efficient validation library for Rails and anything that uses