email_validator 2.2.0 → 2.2.1

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: 0bd676f012ed7b109082842ed227bc1881aec6ac24f0e1ed4be24772b206b14f
4
- data.tar.gz: 6c2f92e0a494f751b690743b15d4d97fd8dd1f9213b8cb2640d6f8f729fca51f
3
+ metadata.gz: faae1443109b7d1c95aca6addd9d2be7c3cfaf58a771e003d80a1ea75dd0993f
4
+ data.tar.gz: c1094ac3f9d4887193eb2145c62f46cefe1d4ed0a734a6d7add1cf68222de1dd
5
5
  SHA512:
6
- metadata.gz: 8dd2ab72b087727acec6c7509d3ffa2a51e35074b99113bc8b105c16fc62e9c3288712516e3fc0f15be125d83e0d7db50d368da269672d7ecdddec9ba21f925e
7
- data.tar.gz: e35ab67f7e7337b1bb1cd04d27d0f47dee128b093cdd6015fdade7967b404441d7abc522dcec18bfda8e682e7ae5418a24ba3a400afa9292b6585e796b644c27
6
+ metadata.gz: e99e6549ebfa01f6fc43b0a8f8bfba80ce0037700f2672be89bb0936b71f929bde127d667fc634a38bc6ea9e26d08a61393c5f261db3581362f8497001305790
7
+ data.tar.gz: 7e74593c1bb75c71d8864c1db31d9a415ec6fbc4d56a7f190363224f3906b884f0bd39af9208969112f12978b733ae09271e3a94b42ed1fc84b88553cd71df9d
@@ -5,6 +5,13 @@ This file is used to list changes made in `email_validator`.
5
5
  All notable changes to this project will be documented in this file.
6
6
  This project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
+ ## 2.2.1 (2020-12-10)
9
+
10
+ * [karlwilbur] - Modify regexp to:
11
+ - allow numeric-only hosts [#68]
12
+ - allow mailbox-only addresses in `:rfc` mode
13
+ - enforce the 255-char domain limit (not in `:loose` mode unless using `:domain`)
14
+
8
15
  ## 2.2.0 (2020-12-09)
9
16
 
10
17
  * [karlwilbur] - Rename `:strict` -> `:rfc`; `:moderate` -> `:strict`
data/README.md CHANGED
@@ -45,7 +45,7 @@ Add the following to your model:
45
45
  validates :my_email_attribute, email: true
46
46
  ```
47
47
 
48
- You may wish to allow domains without a FDQN, like `user@somehost`. While this
48
+ You may wish to allow domains without a FQDN, like `user@somehost`. While this
49
49
  is technically a valid address, it is uncommon to consider such address valid.
50
50
  We will consider them valid by default with the `:loose` checking. Disallowed
51
51
  by setting `require_fqdn: true` or by enabling `:strict` checking:
@@ -30,15 +30,31 @@ class EmailValidator < ActiveModel::EachValidator
30
30
  # https://tools.ietf.org/html/rfc5321 : 4.1.2. Command Argument Syntax
31
31
  def regexp(options = {})
32
32
  options = parse_options(options)
33
- if options[:mode] == :loose
34
- return /\A[^\s]+@[^\s]+\z/ if options[:domain].nil?
35
- return /\A[^\s]+@#{domain_pattern(options)}\z/
33
+ case options[:mode]
34
+ when :loose
35
+ loose_regexp(options)
36
+ when :rfc
37
+ rfc_regexp(options)
38
+ else
39
+ strict_regexp(options)
36
40
  end
37
- /\A(?>#{local_part_pattern})@#{domain_pattern(options)}\z/i
38
41
  end
39
42
 
40
43
  protected
41
44
 
45
+ def loose_regexp(options = {})
46
+ return /\A[^\s]+@[^\s]+\z/ if options[:domain].nil?
47
+ /\A[^\s]+@#{domain_part_pattern(options)}\z/
48
+ end
49
+
50
+ def strict_regexp(options = {})
51
+ /\A(?>#{local_part_pattern})@#{domain_part_pattern(options)}\z/i
52
+ end
53
+
54
+ def rfc_regexp(options = {})
55
+ /\A(?>#{local_part_pattern})(?:@#{domain_part_pattern(options)})?\z/i
56
+ end
57
+
42
58
  def alpha
43
59
  '[[:alpha:]]'
44
60
  end
@@ -64,8 +80,32 @@ class EmailValidator < ActiveModel::EachValidator
64
80
  "\\[(?:#{ipv4}|#{ipv6})\\]"
65
81
  end
66
82
 
67
- def label_pattern
68
- "#{alpha}(?:#{alnumhy}{,62}#{alnum})?"
83
+ def host_label_pattern
84
+ "#{alnum}(?:#{alnumhy}{,61}#{alnum})?"
85
+ end
86
+
87
+ # splitting this up into separate regex pattern for performance; let's not
88
+ # try the "contains" pattern unless we have to
89
+ def domain_label_pattern
90
+ '(?=[^.]{1,63}(?:\.|$))' \
91
+ '(?:' \
92
+ "#{alpha}" \
93
+ "|#{domain_label_starts_with_a_letter_pattern}" \
94
+ "|#{domain_label_ends_with_a_letter_pattern}" \
95
+ "|#{domain_label_contains_a_letter_pattern}" \
96
+ ')'
97
+ end
98
+
99
+ def domain_label_starts_with_a_letter_pattern
100
+ "#{alpha}#{alnumhy}{,61}#{alnum}"
101
+ end
102
+
103
+ def domain_label_ends_with_a_letter_pattern
104
+ "#{alnum}#{alnumhy}{,61}#{alpha}"
105
+ end
106
+
107
+ def domain_label_contains_a_letter_pattern
108
+ "(?:[[:digit:]])(?:[[:digit:]]|-)*#{alpha}#{alnumhy}*#{alnum}"
69
109
  end
70
110
 
71
111
  def atom_char
@@ -79,10 +119,14 @@ class EmailValidator < ActiveModel::EachValidator
79
119
  "#{atom_char}(?:\\.?#{atom_char}){,63}"
80
120
  end
81
121
 
82
- def domain_pattern(options)
122
+ def domain_part_pattern(options)
83
123
  return options[:domain].sub(/\./, '\.') if options[:domain].present?
84
- return "(?:#{label_pattern}\\.)+#{label_pattern}" if options[:require_fqdn]
85
- "(?:#{address_literal}|(?:#{label_pattern}\\.)*#{label_pattern})"
124
+ return fqdn_pattern if options[:require_fqdn]
125
+ "(?=.{1,255}$)(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{domain_label_pattern})"
126
+ end
127
+
128
+ def fqdn_pattern
129
+ "(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}\\.#{domain_label_pattern}"
86
130
  end
87
131
 
88
132
  private
@@ -140,7 +140,13 @@ RSpec.describe EmailValidator do
140
140
  'someuser@somehost.somedomain',
141
141
  'mixed-1234-in-{+^}-local@sld.dev',
142
142
  'partially."quoted"@sld.com',
143
- 'areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca'
143
+ 'areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca',
144
+ 'john.doe@2020.example.com',
145
+ 'john.doe@2a.com',
146
+ 'john.doe@a2.com',
147
+ 'john.doe@2020.a-z.com',
148
+ 'john.doe@2020.a2z.com',
149
+ 'john.doe@2020.12345a6789.com'
144
150
  ]).flatten.each do |email|
145
151
  context 'when using defaults' do
146
152
  it "'#{email}' should be valid" do
@@ -260,6 +266,132 @@ RSpec.describe EmailValidator do
260
266
  end
261
267
  end
262
268
 
269
+ context 'when given the numeric domain' do
270
+ [
271
+ 'only-numbers-in-domain-label@sub.123.custom'
272
+ ].each do |email|
273
+ context 'when using defaults' do
274
+ it "'#{email}' should be valid" do
275
+ expect(DefaultUser.new(:email => email)).to be_valid
276
+ end
277
+
278
+ it "'#{email}' should be valid using EmailValidator.valid?" do
279
+ expect(described_class).to be_valid(email)
280
+ end
281
+
282
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
283
+ expect(described_class).not_to be_invalid(email)
284
+ end
285
+
286
+ it "'#{email}' should match the regexp" do
287
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
288
+ end
289
+ end
290
+
291
+ context 'when in `:strict` mode' do
292
+ it "'#{email}' should not be valid" do
293
+ expect(StrictUser.new(:email => email)).not_to be_valid
294
+ end
295
+
296
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
297
+ expect(described_class).not_to be_valid(email, :mode => :strict)
298
+ end
299
+
300
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
301
+ expect(described_class).to be_invalid(email, :mode => :strict)
302
+ end
303
+
304
+ it "'#{email}' should not match the regexp" do
305
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
306
+ end
307
+ end
308
+
309
+ context 'when in `:rfc` mode' do
310
+ it "'#{email}' should be valid" do
311
+ expect(RfcUser.new(:email => email)).to be_valid
312
+ end
313
+
314
+ it "'#{email}' should be valid using EmailValidator.valid?" do
315
+ expect(described_class).to be_valid(email, :mode => :rfc)
316
+ end
317
+
318
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
319
+ expect(described_class).not_to be_invalid(email, :mode => :rfc)
320
+ end
321
+
322
+ it "'#{email}' should match the regexp" do
323
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+ context 'when given the valid mailbox-only email' do
330
+ valid_includable.map { |k, v| [
331
+ "user-#{v}-#{k}-name"
332
+ ]}.concat(valid_beginable.map { |k, v| [
333
+ "#{v}-start-with-#{k}-user"
334
+ ]}).concat(valid_endable.map { |k, v| [
335
+ "end-with-#{k}-#{v}"
336
+ ]}).concat([
337
+ 'user'
338
+ ]).flatten.each do |email|
339
+ context 'when using defaults' do
340
+ it "'#{email}' should not be valid" do
341
+ expect(DefaultUser.new(:email => email)).not_to be_valid
342
+ end
343
+
344
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
345
+ expect(described_class).not_to be_valid(email)
346
+ end
347
+
348
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
349
+ expect(described_class).to be_invalid(email)
350
+ end
351
+
352
+ it "'#{email}' should not match the regexp" do
353
+ expect(!!(email.strip =~ described_class.regexp)).to be(false)
354
+ end
355
+ end
356
+
357
+ context 'when in `:strict` mode' do
358
+ it "'#{email}' should not be valid" do
359
+ expect(StrictUser.new(:email => email)).not_to be_valid
360
+ end
361
+
362
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
363
+ expect(described_class).not_to be_valid(email, :mode => :strict)
364
+ end
365
+
366
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
367
+ expect(described_class).to be_invalid(email, :mode => :strict)
368
+ end
369
+
370
+ it "'#{email}' should not match the regexp" do
371
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
372
+ end
373
+ end
374
+
375
+ context 'when in `:rfc` mode' do
376
+ it "'#{email}' should be valid" do
377
+ expect(RfcUser.new(:email => email)).to be_valid
378
+ end
379
+
380
+ it "'#{email}' should be valid using EmailValidator.valid?" do
381
+ expect(described_class).to be_valid(email, :mode => :rfc)
382
+ end
383
+
384
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
385
+ expect(described_class).not_to be_invalid(email, :mode => :rfc)
386
+ end
387
+
388
+ it "'#{email}' should match the regexp" do
389
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
390
+ end
391
+ end
392
+ end
393
+ end
394
+
263
395
  context 'when given the valid IP address email' do
264
396
  [
265
397
  'bracketed-IP@[127.0.0.1]',
@@ -334,8 +466,6 @@ RSpec.describe EmailValidator do
334
466
  'test@example.com@example.com',
335
467
  'missing-sld@.com',
336
468
  'missing-tld@sld.',
337
- 'only-numbers-in-domain-label@sub.123.com',
338
- 'only-numbers-in-domain-label@123.example.com',
339
469
  'unbracketed-IPv6@abcd:ef01:1234:5678:9abc:def0:1234:5678',
340
470
  'unbracketed-and-labled-IPv6@IPv6:abcd:ef01:1234:5678:9abc:def0:1234:5678',
341
471
  'bracketed-and-unlabeled-IPv6@[abcd:ef01:1234:5678:9abc:def0:1234:5678]',
@@ -347,6 +477,7 @@ RSpec.describe EmailValidator do
347
477
  'domain-beginning-with-dash@-example.com',
348
478
  'domain-ending-with-dash@example-.com',
349
479
  'the-local-part-is-invalid-if-it-is-longer-than-sixty-four-characters@sld.dev',
480
+ "domain-too-long@t#{".#{'o' * 63}" * 5}.long",
350
481
  "user@example.com<script>alert('hello')</script>"
351
482
  ]).flatten.each do |email|
352
483
  context 'when using defaults' do
@@ -504,8 +635,7 @@ RSpec.describe EmailValidator do
504
635
  '@bar.com',
505
636
  'test@',
506
637
  '@missing-local.dev',
507
- ' ',
508
- 'missing-at-sign.dev'
638
+ ' '
509
639
  ].each do |email|
510
640
  context 'when using defaults' do
511
641
  it "'#{email}' should not be valid" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: email_validator
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Alexander
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-09 00:00:00.000000000 Z
12
+ date: 2020-12-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel