validators 3.3.0 → 3.4.0

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: 862ad7c2a49bcc38d917336b109047a330c6de6c6da16d83ea7953ddefe70096
4
- data.tar.gz: fb5e0b9db3ec0fdbc064eb6467cb36fab3d0f4bab6f29a5c8439c8d5edeb46fd
3
+ metadata.gz: 49077fb99da3c1c92a3b981cdfd0354acb5a62a9b433512658bfc5c1e4df649b
4
+ data.tar.gz: 9d48b1cf25f3782afeaa00c0fa7f51ecf5581ea7e6bb49ebd5796ab914aecb50
5
5
  SHA512:
6
- metadata.gz: b8842cf3ee2161ef2fe18c384efdb5ff6407c135560a95b91689d29b8ff65d87272c599288de55a6422dfaa6ff4be3b0b00fbd7ea8a8472e47bc08acfb973a85
7
- data.tar.gz: 7a6fe664d6ef7a33735b8bf41cc198f8509a5712f788a2db93d6f2c4b39678ad6965eea1557fb6e43551a4d947a0cfd2e89c16a53c0721b6ada4390baa8cf293
6
+ metadata.gz: 1b307ad09f0e26c9e1db7766a15512aab5c87eb5cbd6217e9da489a45abce98bad30c4c320d0042d46fbf92d1d2c061487736f45d723d569a04cf26fb27bc453
7
+ data.tar.gz: b72f412e65628ac2d4b1e14160e2fcc855383ff3e40641d78e77cfa99f3f9b781e82d9cc4ca269ecd45493f4b199d918102b713d7860c99bf73e88b73ab6e060
@@ -0,0 +1 @@
1
+ github: [fnando]
@@ -4,6 +4,8 @@ inherit_gem:
4
4
 
5
5
  AllCops:
6
6
  TargetRubyVersion: 2.6
7
+ Exclude:
8
+ - vendor/**/*
7
9
 
8
10
  Style/AsciiComments:
9
11
  Enabled: false
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/setup"
4
3
  require "bundler/gem_tasks"
5
4
  require "rake/testtask"
6
5
  require "rubocop/rake_task"
@@ -7,7 +7,7 @@ module Validators
7
7
  require "validators/ip"
8
8
  require "validators/tld"
9
9
  require "validators/hostname"
10
- require "validators/disposable_hostnames"
10
+ require "validators/disposable_domains"
11
11
  require "validators/disposable_emails"
12
12
  require "validators/reserved_subdomains"
13
13
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Validators
4
+ class DisposableDomains
5
+ def self.all
6
+ @all ||=
7
+ begin
8
+ require "email_data"
9
+ EmailData.disposable_domains
10
+ rescue LoadError
11
+ raise "email_data is not part of the bundle. Add it to Gemfile."
12
+ end
13
+ end
14
+
15
+ def self.include?(domain)
16
+ all.include?(domain)
17
+ end
18
+ end
19
+ end
@@ -2,10 +2,14 @@
2
2
 
3
3
  module Validators
4
4
  class DisposableEmails
5
- FILE_PATH = File.expand_path("../../data/disposable_emails.txt", __dir__)
6
-
7
5
  def self.all
8
- @all ||= File.read(FILE_PATH).lines.map(&:chomp)
6
+ @all ||=
7
+ begin
8
+ require "email_data"
9
+ EmailData.disposable_emails
10
+ rescue LoadError
11
+ raise "email_data is not part of the bundle. Add it to Gemfile."
12
+ end
9
13
  end
10
14
 
11
15
  def self.include?(email)
@@ -2,10 +2,14 @@
2
2
 
3
3
  module Validators
4
4
  class TLD
5
- FILE_PATH = File.expand_path("../../data/tld.txt", __dir__)
6
-
7
5
  def self.all
8
- @all ||= File.read(FILE_PATH).lines.map(&:chomp)
6
+ @all ||=
7
+ begin
8
+ require "email_data"
9
+ EmailData.tlds
10
+ rescue LoadError
11
+ raise "email_data is not part of the bundle. Add it to Gemfile."
12
+ end
9
13
  end
10
14
 
11
15
  def self.host_with_valid_tld?(host)
@@ -13,10 +17,10 @@ module Validators
13
17
 
14
18
  return false if host.split(".").size == 1
15
19
 
16
- valid?(host[/\.([^.]+)$/, 1].to_s.downcase)
20
+ include?(host[/\.([^.]+)$/, 1].to_s.downcase)
17
21
  end
18
22
 
19
- def self.valid?(tld)
23
+ def self.include?(tld)
20
24
  all.include?(tld)
21
25
  end
22
26
  end
@@ -50,7 +50,7 @@ module ActiveModel
50
50
  hostname = value.to_s.split(AT_SIGN).last.to_s.downcase
51
51
  root_domain = RootDomain.call(hostname)
52
52
 
53
- return unless Validators::DisposableHostnames.all.include?(root_domain)
53
+ return unless Validators::DisposableDomains.include?(root_domain)
54
54
 
55
55
  record.errors.add(
56
56
  attribute,
@@ -3,7 +3,7 @@
3
3
  module Validators
4
4
  module Version
5
5
  MAJOR = 3
6
- MINOR = 3
6
+ MINOR = 4
7
7
  PATCH = 0
8
8
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
9
9
  end
@@ -3,7 +3,6 @@
3
3
  $VERBOSE = nil
4
4
 
5
5
  require "simplecov"
6
- SimpleCov.start
7
6
 
8
7
  SimpleCov.start do
9
8
  add_filter "test/support"
@@ -31,7 +30,7 @@ end
31
30
 
32
31
  Time.zone = "America/Sao_Paulo"
33
32
  TLDs = Validators::TLD.all.sample(10)
34
- DISPOSABLE_DOMAINS = Validators::DisposableHostnames.all.sample(10)
33
+ DISPOSABLE_DOMAINS = Validators::DisposableDomains.all.sample(10)
35
34
  DISPOSABLE_EMAILS = Validators::DisposableEmails.all +
36
35
  Validators::DisposableEmails.all.sample(10).map {|email| build_email_with_filter(email) } +
37
36
  Validators::DisposableEmails.all.sample(10).map {|email| build_email_with_dots(email) } +
@@ -14,4 +14,22 @@ class ValidatesurlFormatUrlWithTldValidationTest < Minitest::Test
14
14
  assert user.valid?
15
15
  end
16
16
  end
17
+
18
+ test "rejects invalid TLD (alternative syntax)" do
19
+ user_model = Class.new do
20
+ include ActiveModel::Validations
21
+ attr_accessor :site_url
22
+
23
+ def self.name
24
+ "User"
25
+ end
26
+
27
+ validates :site_url, url: {tld: true}
28
+ end
29
+
30
+ user = user_model.new
31
+ user.site_url = "https://example.xy"
32
+
33
+ refute user.valid?
34
+ end
17
35
  end
@@ -47,4 +47,22 @@ class ValidatesurlFormatUrlWithoutTldValidationTest < Minitest::Test
47
47
  user = User.new(url: "")
48
48
  refute user.valid?
49
49
  end
50
+
51
+ test "accepts invalid TLD (alternative syntax)" do
52
+ user_model = Class.new do
53
+ include ActiveModel::Validations
54
+ attr_accessor :site_url
55
+
56
+ def self.name
57
+ "User"
58
+ end
59
+
60
+ validates :site_url, url: {tld: false}
61
+ end
62
+
63
+ user = user_model.new
64
+ user.site_url = "https://example.xy"
65
+
66
+ assert user.valid?
67
+ end
50
68
  end
@@ -12,6 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.summary = "Add some nice ActiveModel/ActiveRecord validators."
13
13
  s.description = s.summary
14
14
  s.license = "MIT"
15
+ s.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
15
16
 
16
17
  s.files = `git ls-files`.split("\n")
17
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -23,6 +24,7 @@ Gem::Specification.new do |s|
23
24
  s.add_development_dependency "activerecord"
24
25
  s.add_development_dependency "aitch"
25
26
  s.add_development_dependency "cpf_cnpj"
27
+ s.add_development_dependency "email_data"
26
28
  s.add_development_dependency "minitest-utils"
27
29
  s.add_development_dependency "mocha"
28
30
  s.add_development_dependency "nokogiri"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validators
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Vieira
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-22 00:00:00.000000000 Z
11
+ date: 2020-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: email_data
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: minitest-utils
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -265,29 +279,22 @@ dependencies:
265
279
  description: Add some nice ActiveModel/ActiveRecord validators.
266
280
  email:
267
281
  - fnando.vieira@gmail.com
268
- executables:
269
- - sync-disposable-hostnames
270
- - sync-tld
282
+ executables: []
271
283
  extensions: []
272
284
  extra_rdoc_files: []
273
285
  files:
286
+ - ".github/FUNDING.yml"
274
287
  - ".gitignore"
275
288
  - ".rubocop.yml"
276
289
  - ".travis.yml"
277
290
  - Gemfile
278
291
  - README.md
279
292
  - Rakefile
280
- - bin/sync-disposable-hostnames
281
- - bin/sync-tld
282
- - data/country_tlds.txt
283
- - data/disposable_domains.txt
284
- - data/disposable_emails.txt
285
293
  - data/reserved_subdomains.txt
286
- - data/tld.txt
287
294
  - lib/validators.rb
288
295
  - lib/validators/constants.rb
296
+ - lib/validators/disposable_domains.rb
289
297
  - lib/validators/disposable_emails.rb
290
- - lib/validators/disposable_hostnames.rb
291
298
  - lib/validators/hostname.rb
292
299
  - lib/validators/ip.rb
293
300
  - lib/validators/locale/en.yml
@@ -351,7 +358,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
351
358
  requirements:
352
359
  - - ">="
353
360
  - !ruby/object:Gem::Version
354
- version: '0'
361
+ version: 2.3.0
355
362
  required_rubygems_version: !ruby/object:Gem::Requirement
356
363
  requirements:
357
364
  - - ">="
@@ -1,230 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require_relative "helpers"
5
-
6
- def ten_minute_mail
7
- path = "disposable/10minutemail.txt"
8
- url = "https://10minutemail.com/session/address"
9
-
10
- 20.times do
11
- refresh_list(url: url, path: path) do |response|
12
- _account, host = response.data.fetch("address").split("@")
13
-
14
- [host]
15
- end
16
-
17
- sleep random_timeout
18
- end
19
- end
20
-
21
- def temp_mail
22
- path = "disposable/tempmail.txt"
23
- url = "https://api4.temp-mail.org/request/domains/format/json"
24
-
25
- refresh_list(url: url, path: path) do |response|
26
- response.data.map {|domain| domain.tr("@", "") }
27
- end
28
- end
29
-
30
- def temp_mail_address
31
- path = "disposable/tempmailaddress.txt"
32
- url = "https://www.tempmailaddress.com/index/index"
33
-
34
- refresh_list(url: url, path: path) do |response|
35
- data = JSON.parse(
36
- response.body.gsub(/[^-,:\w@.{}"]/, ""),
37
- symbolize_names: true
38
- )
39
- [data[:email].split("@").last]
40
- end
41
- end
42
-
43
- def tempmail_io
44
- path = "disposable/tempmail_io.txt"
45
- url = "https://api.internal.temp-mail.io/api/v2/domains"
46
-
47
- refresh_list(url: url, path: path) do |response|
48
- response.data["domains"]
49
- end
50
- end
51
-
52
- def gmailnator
53
- emails = []
54
-
55
- 5.times do
56
- url = "https://gmailnator.com/bulk-emails"
57
- default_headers = {"user-agent" => USER_AGENT.sample}
58
-
59
- response = Aitch.get(url: url, headers: default_headers)
60
-
61
- throw "Received #{response.status} when getting CSRF token" unless response.ok?
62
-
63
- cookie_header = response.headers["set-cookie"]
64
- attr = response.data.css("#csrf_token").first
65
- csrf_token = attr[:value]
66
- csrf_field = attr[:name]
67
-
68
- response = Aitch.post(
69
- url: url,
70
- params: {email_list: "1000", email: [3], csrf_field => csrf_token},
71
- headers: default_headers.merge({"cookie" => cookie_header})
72
- )
73
-
74
- throw "Received #{response.status} when fetching list" unless response.ok?
75
-
76
- emails += response.data.css("#email-list-message a").map do |node|
77
- mailbox, domain = node.text.gsub(/\+[^@]+/, "").split("@")
78
- mailbox = mailbox.gsub(/\./m, "")
79
- "#{mailbox}@#{domain}"
80
- end
81
-
82
- sleep random_timeout
83
- end
84
-
85
- append_to_file("disposable/gmailnator.txt", emails)
86
- end
87
-
88
- def domain_scraping(name, url, selector)
89
- timeout(10) do
90
- puts "=> Scraping #{url}"
91
-
92
- selector, value_selector = selector.split("::")
93
- path = "disposable/#{name}.txt"
94
- host_regex = /@?(.*?(\.[^.]+)+)/
95
-
96
- refresh_list(url: url, path: path) do |response|
97
- new_domains = response
98
- .data
99
- .css(selector)
100
- .map {|element| process_scraping(element, value_selector) }
101
-
102
- new_domains = new_domains
103
- .map(&:squish)
104
- .reject(&:empty?)
105
- .map {|domain| domain[host_regex, 1]&.squish&.tr("@", "") }
106
- .reject(&:nil?)
107
- .reject(&:empty?)
108
- .map {|domain| domain.gsub(/\s*\((.*?)\)/, "") }
109
-
110
- raise "No #{name} hosts found" if new_domains.empty?
111
-
112
- new_domains
113
- end
114
- end
115
- rescue StandardError => error
116
- puts "=> [ERROR] Unable to scrape #{url}; #{error.class}: #{error.message}"
117
- []
118
- end
119
-
120
- def process_scraping(element, value_selector)
121
- value = nil
122
-
123
- case value_selector
124
- when "text()"
125
- value = element.text
126
- when /^attr\((.*?)\)/
127
- value = element[Regexp.last_match(1)]
128
- else
129
- element.attributes.each do |_name, attr|
130
- attr = attr.value.to_s
131
- value = attr if attr =~ host_regex
132
- end
133
- end
134
-
135
- raise "no value found: #{element} (value_selector: #{value_selector})" unless value
136
-
137
- value
138
- end
139
-
140
- def load_github_url(url)
141
- puts "=> Fetching #{url}"
142
-
143
- basename = URI.parse(url).path[%r{/([^/]+/[^/]+)}, 1].tr("/", "_").tr("-", "_")
144
- path = "disposable/#{basename}.txt"
145
- domains = load_file(path)
146
-
147
- ext = File.extname(url)
148
-
149
- domains += case ext
150
- when ".json"
151
- JSON.parse(http_request(:get, url).body)
152
- when ".txt"
153
- http_request(:get, url).body.lines.map(&:chomp)
154
- else
155
- raise "Unknown extension"
156
- end
157
-
158
- append_to_file(path, domains)
159
- domains
160
- rescue StandardError => error
161
- puts "=> Unable to load #{url}; #{error.class}: #{error.message}"
162
- []
163
- end
164
-
165
- threads = []
166
-
167
- threads << thread { load_github_url("https://raw.githubusercontent.com/ivolo/disposable-email-domains/master/index.json") }
168
- threads << thread { load_github_url("https://raw.githubusercontent.com/andreis/disposable-email-domains/master/domains.json") }
169
- threads << thread { load_github_url("https://raw.githubusercontent.com/FGRibreau/mailchecker/master/list.txt") }
170
- threads << thread { load_github_url("https://raw.githubusercontent.com/willwhite/freemail/master/data/disposable.txt") }
171
- threads << thread { load_github_url("https://raw.githubusercontent.com/maxmalysh/disposable-emails/master/disposable_emails/data/domains.txt") }
172
- threads << thread { load_github_url("https://raw.githubusercontent.com/jespernissen/disposable-maildomain-list/master/disposable-maildomain-list.txt") }
173
- threads << thread { load_github_url("https://raw.githubusercontent.com/wesbos/burner-email-providers/master/emails.txt") }
174
- threads << thread { load_github_url("https://gist.github.com/fnando/dafe542cac13f831bbf5521a55248116/raw/disposable.txt") }
175
- threads << thread { ten_minute_mail }
176
- threads << thread { temp_mail }
177
- threads << thread { temp_mail_address }
178
- threads << thread { tempmail_io }
179
- threads << thread { load_file("disposable/disposable_manually_added.txt") }
180
- threads << thread { domain_scraping("guerrillamail", "https://www.guerrillamail.com/", "select option::attr(value)") }
181
- threads << thread { domain_scraping("moakt", "https://www.moakt.com", "select option::attr(value)") }
182
- threads << thread { domain_scraping("tempr", "https://tempr.email/", "select[name=DomainId] option::text()") }
183
- threads << thread { domain_scraping("yepmail", "https://yepmail.co/", "select[name=domain] option::text()") }
184
- threads << thread { domain_scraping("fake_email_generator", "https://fakemailgenerator.net", "[data-mailhost]::attr(data-mailhost)") }
185
- threads << thread { domain_scraping("tempemails", "https://www.tempemails.net/", "select[name=domain] option::attr(value)") }
186
- threads << thread { domain_scraping("clipmails", "https://clipmails.com/", "select[name=domain] option::attr(value)") }
187
- threads << thread { domain_scraping("1secmail", "https://www.1secmail.com/", "select[id=domain] option::attr(value)") }
188
- threads << thread { domain_scraping("emailfake", "https://generator.email", ".tt-suggestion p::text()") }
189
- threads << thread { domain_scraping("emailfake", "https://emailfake.com/", ".tt-suggestion p::text()") }
190
- threads << thread { domain_scraping("emailfake", "https://email-fake.com/", ".tt-suggestion p::text()") }
191
- threads << thread { domain_scraping("receivemail", "https://www.receivemail.org/", "select[name=domain] option::text()") }
192
- threads << thread { domain_scraping("itemp", "https://itemp.email", "select[name=domain] option::text()") }
193
- threads << thread { domain_scraping("cs", "https://www.cs.email", "select[id=gm-host-select] option::text()") }
194
- threads << thread { domain_scraping("tempmail", "https://tempmail.io/settings/", "select[id=domain] option::text()") }
195
- threads << thread { domain_scraping("tempemail", "https://tempemail.co", "select[name=email_domain] option::text()") }
196
- threads << thread { domain_scraping("tmail", "https://mytemp-email.com/", "a.domain-selector::text()") }
197
-
198
- threads.each_slice(5) do |slice|
199
- slice.each(&:join)
200
- end
201
-
202
- threads.clear
203
-
204
- domains = []
205
-
206
- puts "=> Loading disposable_domains.txt"
207
- domains += File.read("#{__dir__}/../data/disposable_domains.txt").lines.map(&:chomp)
208
-
209
- puts "=> Loading disposable/*.txt"
210
- Dir["./data/disposable/**/*.txt"].map do |file|
211
- file = File.expand_path(file)
212
- domains += File.read(file).lines.map(&:chomp).flatten.compact
213
- end
214
-
215
- ignore_domains = %w[gmail.com hotmail.com]
216
-
217
- puts "=> Normalize domains (count: #{domains.size})"
218
- domains = domains
219
- .uniq
220
- .map {|domain| RootDomain.call(domain.split("@").last.downcase) }
221
- .compact
222
- .uniq
223
- .reject {|domain| ignore_domains.include?(domain) }
224
-
225
- puts "=> Saving domains (count: #{domains.size})"
226
- save_file("disposable_domains.txt", domains)
227
-
228
- emails = gmailnator
229
- puts "=> Saving email proxies (count: #{emails.size})"
230
- save_file("disposable_emails.txt", emails)