fix(client): validate emails against IETF rfc5321

* email must have >= 2 part tld
* email can have [1, 64] byte local length
* email can have [3, 255] byte domain length
* email can be up to 256 bytes long

For more info, see http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1

Closes #563
This commit is contained in:
Shane Tomlinson 2014-02-26 14:54:19 +00:00
Родитель ee3618cb8d
Коммит d7d3a0ba93
2 изменённых файлов: 165 добавлений и 0 удалений

Просмотреть файл

@ -109,6 +109,13 @@ function (_, $, BaseView, Tooltip) {
*/ */
isElementValid: function (selector) { isElementValid: function (selector) {
var el = this.$(selector); var el = this.$(selector);
var type = el.attr('type');
// email follows our own rules.
if (type === 'email') {
return this.validateEmail(selector);
}
var value = el.val(); var value = el.val();
var isValid = !!(value && el[0].validity.valid); var isValid = !!(value && el[0].validity.valid);
return isValid; return isValid;
@ -183,6 +190,38 @@ function (_, $, BaseView, Tooltip) {
showValidationErrorsEnd: function () { showValidationErrorsEnd: function () {
}, },
/**
* Validate an email field
*
* @return true if email is valid, false otw.
*/
validateEmail: function (selector) {
var email = this.$(selector).val();
if (typeof(email) !== 'string') {
return false;
}
var parts = email.split('@');
var localLength = parts[0] && parts[0].length;
var domainLength = parts[1] && parts[1].length;
// Original regexp from:
// http://blog.gerv.net/2011/05/html5_email_email_regexp/
// Modified to require at least a 2 part tld and remove the
// length checks, which are done later.
// IETF spec: http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
// NOTE: this does *NOT* allow internationalized domain names.
return (/^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d][a-z\d\-]*(?:\.[a-z\d][a-z\d\-]*)+$/i).test(email) &&
// total email allwed to be 256 bytes long
email.length <= 256 &&
// local side only allowed to be 64 bytes long
1 <= localLength && localLength <= 64 &&
// domain side allowed to be up to 255 bytes long which
// doesn't make much sense unless the local side has 0 length;
3 <= domainLength && domainLength <= 255;
},
showEmailValidationError: function (which) { showEmailValidationError: function (which) {
this.showValidationError(which, t('Valid email required')); this.showValidationError(which, t('Valid email required'));
}, },

Просмотреть файл

@ -62,6 +62,7 @@ function (chai, _, $, View, Session, FxaClient, RouterMock) {
}); });
it('returns false if email is empty', function () { it('returns false if email is empty', function () {
$('[type=email]').val('');
$('[type=password]').val('password'); $('[type=password]').val('password');
$('#fxa-age-year').val('1960'); $('#fxa-age-year').val('1960');
@ -76,6 +77,131 @@ function (chai, _, $, View, Session, FxaClient, RouterMock) {
assert.isFalse(view.isValid()); assert.isFalse(view.isValid());
}); });
it('returns false if email contain one part TLD', function () {
$('[type=email]').val('a@b');
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isFalse(view.isValid());
});
it('returns true if email contain two part TLD', function () {
$('[type=email]').val('a@b.c');
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isTrue(view.isValid());
});
it('returns true if email contain three part TLD', function () {
$('[type=email]').val('a@b.c.d');
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isTrue(view.isValid());
});
it('returns false if local side of email === 0 chars', function () {
var email = '@testuser.com';
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isFalse(view.isValid());
});
it('returns false if local side of email > 64 chars', function () {
var email = '';
do {
email += 'a';
} while (email.length < 65);
email += '@testuser.com';
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isFalse(view.isValid());
});
it('returns true if local side of email === 64 chars', function () {
var email = '';
do {
email += 'a';
} while (email.length < 64);
email += '@testuser.com';
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isTrue(view.isValid());
});
it('returns false if domain side of email === 0 chars', function () {
var email = 'testuser@';
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isFalse(view.isValid());
});
it('returns false if domain side of email > 255 chars', function () {
var domain = 'testuser.com';
do {
domain += 'a';
} while (domain.length < 256);
var email = 'testuser@' + domain;
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isFalse(view.isValid());
});
it('returns true if domain side of email === 254 chars', function () {
var domain = 'testuser.com';
do {
domain += 'a';
} while (domain.length < 254);
var email = 'a@' + domain;
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isTrue(view.isValid());
});
it('returns false total length > 256 chars', function () {
var domain = 'testuser.com';
do {
domain += 'a';
} while (domain.length < 254);
// ab@ + 254 characters = 257 chars
var email = 'ab@' + domain;
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isFalse(view.isValid());
});
it('returns true if total length === 256 chars', function () {
var email = 'testuser@testuser.com';
do {
email += 'a';
} while (email.length < 256);
$('[type=email]').val(email);
$('[type=password]').val('password');
$('#fxa-age-year').val('1960');
assert.isTrue(view.isValid());
});
it('returns false if password is empty', function () { it('returns false if password is empty', function () {
$('[type=email]').val(email); $('[type=email]').val(email);
$('#fxa-age-year').val('1960'); $('#fxa-age-year').val('1960');