mini_defender 0.6.7 → 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0766463845ff12b982d3230cbd2d62a4506d5b470160256a2ec0684146f4e8bc'
4
- data.tar.gz: d24329e0981cb6dabe1d42a85e2c0bec719610a829dc2eb6d2dab480faace668
3
+ metadata.gz: 3bae6fdb0faacfd54fe463d30f80710e40d383bd868d64e65da7243bae0c5b1c
4
+ data.tar.gz: f9cf13d8acbc654b9c647c33b322937722b97f0a99b8d389d4311002a3a5a65e
5
5
  SHA512:
6
- metadata.gz: 3a232eba0c84024e5248cabcc3f324af4c86956eb494c5f8fb4c1531f76965a32de4b1b674584b1008d4f566b2752712498b600646bf1a1ed94770d5ddeacfa9
7
- data.tar.gz: 5afafaa73bd774d094540de2db462f1b2d0c840bf8fda34a5ff9c9959b0d1d6a9dfd0999343ac14d1ce31f1c94de2ff4a255f6650611fbe4d02cee63eb89f8e8
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,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.7"
4
+ VERSION = "0.6.8"
5
5
  end
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_runtime_dependency 'countries'
35
35
  spec.add_runtime_dependency 'money'
36
36
  spec.add_runtime_dependency 'marcel'
37
+ spec.add_runtime_dependency 'public_suffix'
37
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.7
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-11-10 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
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
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'
97
111
  description: A small and efficient validation library for Rails and anything that
98
112
  uses Ruby.
99
113
  email:
@@ -110,6 +124,7 @@ files:
110
124
  - RULES.md
111
125
  - Rakefile
112
126
  - lib/mini_defender.rb
127
+ - lib/mini_defender/data/private_network_patterns.txt
113
128
  - lib/mini_defender/extensions/enumerable.rb
114
129
  - lib/mini_defender/handles_validation_errors.rb
115
130
  - lib/mini_defender/rule.rb
@@ -181,7 +196,6 @@ files:
181
196
  - lib/mini_defender/rules/national_id.rb
182
197
  - lib/mini_defender/rules/not_ending_with.rb
183
198
  - lib/mini_defender/rules/not_in.rb
184
- - lib/mini_defender/rules/not_local_url.rb
185
199
  - lib/mini_defender/rules/not_regex.rb
186
200
  - lib/mini_defender/rules/not_starting_with.rb
187
201
  - lib/mini_defender/rules/numeric.rb
@@ -234,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
234
248
  - !ruby/object:Gem::Version
235
249
  version: '0'
236
250
  requirements: []
237
- rubygems_version: 3.5.11
251
+ rubygems_version: 3.5.3
238
252
  signing_key:
239
253
  specification_version: 4
240
254
  summary: A small and efficient validation library for Rails and anything that uses
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class MiniDefender::Rules::NotLocalURL < MiniDefender::Rule
4
- LOCALHOST_PATTERNS = [
5
- /^localhost$/i, # localhost, LOCALHOST
6
- /^127\./, # 127.x.x.x
7
- /^::1$/, # IPv6 localhost
8
- /^0\.0\.0\.0$/, # All interfaces IPv4
9
- /^::$/, # IPv6 unspecified
10
- /\.local$/i, # domain.local
11
- /^local\./i, # local.domain
12
- /^localhost\./i, # localhost.anything
13
- ]
14
-
15
- def self.signature
16
- 'not_local_url'
17
- end
18
-
19
- def passes?(attribute, value, validator)
20
- uri = URI.parse(value.to_s)
21
- host = uri.host.to_s.downcase
22
-
23
- !LOCALHOST_PATTERNS.any? { |pattern| host.match?(pattern) }
24
- rescue URI::InvalidURIError
25
- false
26
- end
27
-
28
- def message(attribute, value, validator)
29
- 'URL cannot point to localhost or local domain.'
30
- end
31
- end