Skip to content

Commit b986f3d

Browse files
authored
fix: ReDOS in isEmail and isHSL (#1651)
* chore: bump mocha version to fix npm audit warning * fix(isHSL): update hslComma regex to prevent ReDOS * fix(isEmail): update splitNameAddress regex to prevent ReDOS * chore: rollback mocha version to allow testing on node 8 and 6 * fix(isHSL): remove unnecessary use of let closes #1597 #1598
1 parent 2a3a1c3 commit b986f3d

3 files changed

Lines changed: 22 additions & 11 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"eslint": "^4.19.1",
4646
"eslint-config-airbnb-base": "^12.1.0",
4747
"eslint-plugin-import": "^2.11.0",
48-
"mocha": "^5.1.1",
48+
"mocha": "^6.2.3",
4949
"nyc": "^14.1.0",
5050
"rimraf": "^3.0.0",
5151
"rollup": "^0.43.0",

src/lib/isEmail.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const default_email_options = {
1616

1717
/* eslint-disable max-len */
1818
/* eslint-disable no-control-regex */
19-
const splitNameAddress = /^([^\x00-\x1F\x7F-\x9F\cX]+)<(.+)>$/i;
19+
const splitNameAddress = /^([^\x00-\x1F\x7F-\x9F\cX]+)</i;
2020
const emailUserPart = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i;
2121
const gmailUserPart = /^[a-z\d]+$/;
2222
const quotedEmailUser = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i;
@@ -31,9 +31,7 @@ const defaultMaxEmailLength = 254;
3131
* @param {String} display_name
3232
*/
3333
function validateDisplayName(display_name) {
34-
const trim_quotes = display_name.match(/^"(.+)"$/i);
35-
const display_name_without_quotes = trim_quotes ? trim_quotes[1] : display_name;
36-
34+
const display_name_without_quotes = display_name.replace(/^"(.+)"$/, '$1');
3735
// display name with only spaces is not valid
3836
if (!display_name_without_quotes.trim()) {
3937
return false;
@@ -44,7 +42,7 @@ function validateDisplayName(display_name) {
4442
if (contains_illegal) {
4543
// if contains illegal characters,
4644
// must to be enclosed in double-quotes, otherwise it's not a valid display name
47-
if (!trim_quotes) {
45+
if (display_name_without_quotes === display_name) {
4846
return false;
4947
}
5048

@@ -67,8 +65,12 @@ export default function isEmail(str, options) {
6765
if (options.require_display_name || options.allow_display_name) {
6866
const display_email = str.match(splitNameAddress);
6967
if (display_email) {
70-
let display_name;
71-
[, display_name, str] = display_email;
68+
let display_name = display_email[1];
69+
70+
// Remove display name and angle brackets to get email address
71+
// Can be done in the regex but will introduce a ReDOS (See #1597 for more info)
72+
str = str.replace(display_name, '').replace(/(^<|>$)/g, '');
73+
7274
// sometimes need to trim the last space to get the display name
7375
// because there may be a space between display name and email address
7476
// eg. myname <address@gmail.com>

src/lib/isHSL.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import assertString from './util/assertString';
22

33

4-
const hslcomma = /^(hsl)a?\(\s*((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?))(deg|grad|rad|turn|\s*)(\s*,\s*(\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%){2}\s*(,\s*((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%?)\s*)?\)$/i;
5-
const hslspace = /^(hsl)a?\(\s*((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?))(deg|grad|rad|turn|\s)(\s*(\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%){2}\s*(\/\s*((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%?)\s*)?\)$/i;
4+
const hslComma = /^hsla?\(((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?))(deg|grad|rad|turn)?(,(\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%){2}(,((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%?))?\)$/i;
5+
const hslSpace = /^hsla?\(((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?))(deg|grad|rad|turn)?(\s(\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%){2}\s?(\/\s((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%?)\s?)?\)$/i;
6+
67

78
export default function isHSL(str) {
89
assertString(str);
9-
return hslcomma.test(str) || hslspace.test(str);
10+
11+
// Strip duplicate spaces before calling the validation regex (See #1598 for more info)
12+
const strippedStr = str.replace(/\s+/g, ' ').replace(/\s?(hsla?\(|\)|,)\s?/ig, '$1');
13+
14+
if (strippedStr.indexOf(',') !== -1) {
15+
return hslComma.test(strippedStr);
16+
}
17+
18+
return hslSpace.test(strippedStr);
1019
}

0 commit comments

Comments
 (0)