From c7b818beaf8a7ac256703deb7d5242888e067acb Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 28 Dec 2022 15:09:07 -0500 Subject: [PATCH 01/11] add two factor validation --- src/CONST.js | 1 + src/libs/ValidationUtils.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/CONST.js b/src/CONST.js index ef02585f6ae..dd5f9639f81 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -760,6 +760,7 @@ const CONST = { EMOJI_NAME: /:[\w+-]+:/g, EMOJI_SUGGESTIONS: /:[a-zA-Z]{1,20}(\s[a-zA-Z]{0,20})?$/, AFTER_FIRST_LINE_BREAK: /\n.*/g, + BASE64: /[\w\d+/]+={0,2}/, }, PRONOUNS: { diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js index da0abf746fc..a4c7dd83ed4 100644 --- a/src/libs/ValidationUtils.js +++ b/src/libs/ValidationUtils.js @@ -274,6 +274,14 @@ function isValidPassword(password) { return password.match(CONST.PASSWORD_COMPLEXITY_REGEX_STRING); } +/** + * @param {String} code + * @returns {Boolean} + */ +function isValidTwoFactorCode(code) { + return code.match(CONST.REGEX.BASE64); +} + /** * @param {String} input * @returns {Boolean} @@ -409,6 +417,7 @@ export { isValidURL, validateIdentity, isValidPassword, + isValidTwoFactorCode, isPositiveInteger, isNumericWithSpecialChars, isValidPaypalUsername, From 9c104195e3e10c207d469762375f7e92ee7fa7c6 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 28 Dec 2022 15:20:37 -0500 Subject: [PATCH 02/11] added unit tests for two factor validation --- tests/unit/ValidationUtilsTest.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/unit/ValidationUtilsTest.js diff --git a/tests/unit/ValidationUtilsTest.js b/tests/unit/ValidationUtilsTest.js new file mode 100644 index 00000000000..fa95ae06b14 --- /dev/null +++ b/tests/unit/ValidationUtilsTest.js @@ -0,0 +1,21 @@ +const ValidationUtils = require('../../src/libs/ValidationUtils'); + +describe('ValidationUtils', () => { + describe('isValidTwoFactorCode', () => { + test('numeric two factor code', () => { + expect(ValidationUtils.isValidTwoFactorCode('123456')).toBe(true); + }); + + test('alphanumeric two factor code', () => { + expect(ValidationUtils.isValidTwoFactorCode('abc123')).toBe(true); + }); + + test('special characters two factor code', () => { + expect(ValidationUtils.isValidTwoFactorCode('!@#$%^')).toBe(false); + }); + + test('partial special characters two factor code', () => { + expect(ValidationUtils.isValidTwoFactorCode('abc!@#')).toBe(false); + }); + }); +}); From 1d4fe66e45ae22c2cb6fa0943f154c1807ff4395 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 28 Dec 2022 15:25:52 -0500 Subject: [PATCH 03/11] test entire two factor string for base64 --- src/libs/ValidationUtils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js index a4c7dd83ed4..f9324b08af4 100644 --- a/src/libs/ValidationUtils.js +++ b/src/libs/ValidationUtils.js @@ -279,7 +279,8 @@ function isValidPassword(password) { * @returns {Boolean} */ function isValidTwoFactorCode(code) { - return code.match(CONST.REGEX.BASE64); + const result = code.match(CONST.REGEX.BASE64); + return Boolean(result) && result[0].length === code.length; // ensure the whole code is base64 } /** From 35cf1cf5146f9f780915d890098992be2e553a57 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 28 Dec 2022 15:27:52 -0500 Subject: [PATCH 04/11] use two factor code validation --- src/pages/signin/PasswordForm.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js index 0ae644469d6..1aad5bc0085 100755 --- a/src/pages/signin/PasswordForm.js +++ b/src/pages/signin/PasswordForm.js @@ -129,9 +129,18 @@ class PasswordForm extends React.Component { return; } - if (this.props.account.requiresTwoFactorAuth && !this.state.twoFactorAuthCode.trim()) { - this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'}); - return; + if (this.props.account.requiresTwoFactorAuth) { + const twoFactorCode = this.state.twoFactorAuthCode.trim(); + + if (!twoFactorCode) { + this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'}); + return; + } + + if (!ValidationUtils.isValidTwoFactorCode(twoFactorCode)) { + this.setState({formError: 'passwordForm.error.incorrect2fa'}); + return; + } } this.setState({ From ef1693b1e9bd373e86ca59accb400ddde9fe21dc Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 29 Dec 2022 11:35:18 -0500 Subject: [PATCH 05/11] rename to CODE_2FA --- src/CONST.js | 2 +- src/libs/ValidationUtils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index dd5f9639f81..595aa093c1c 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -760,7 +760,7 @@ const CONST = { EMOJI_NAME: /:[\w+-]+:/g, EMOJI_SUGGESTIONS: /:[a-zA-Z]{1,20}(\s[a-zA-Z]{0,20})?$/, AFTER_FIRST_LINE_BREAK: /\n.*/g, - BASE64: /[\w\d+/]+={0,2}/, + CODE_2FA: /[\w\d+/]+={0,2}/, // matches twoFactorAuthCode WAF rule }, PRONOUNS: { diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js index f9324b08af4..6e45b123fc7 100644 --- a/src/libs/ValidationUtils.js +++ b/src/libs/ValidationUtils.js @@ -279,7 +279,7 @@ function isValidPassword(password) { * @returns {Boolean} */ function isValidTwoFactorCode(code) { - const result = code.match(CONST.REGEX.BASE64); + const result = code.match(CONST.REGEX.CODE_2FA); return Boolean(result) && result[0].length === code.length; // ensure the whole code is base64 } From d014d03b1096fb76f57e80dee3adb6bfca9bf2d5 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 29 Dec 2022 11:37:39 -0500 Subject: [PATCH 06/11] simplify regex validation --- src/CONST.js | 2 +- src/libs/ValidationUtils.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 595aa093c1c..2631115b5e8 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -760,7 +760,7 @@ const CONST = { EMOJI_NAME: /:[\w+-]+:/g, EMOJI_SUGGESTIONS: /:[a-zA-Z]{1,20}(\s[a-zA-Z]{0,20})?$/, AFTER_FIRST_LINE_BREAK: /\n.*/g, - CODE_2FA: /[\w\d+/]+={0,2}/, // matches twoFactorAuthCode WAF rule + CODE_2FA: /^[\w\d+/]+={0,2}$/, // matches twoFactorAuthCode WAF rule }, PRONOUNS: { diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js index 6e45b123fc7..b496860a3d8 100644 --- a/src/libs/ValidationUtils.js +++ b/src/libs/ValidationUtils.js @@ -279,8 +279,7 @@ function isValidPassword(password) { * @returns {Boolean} */ function isValidTwoFactorCode(code) { - const result = code.match(CONST.REGEX.CODE_2FA); - return Boolean(result) && result[0].length === code.length; // ensure the whole code is base64 + return Boolean(code.match(CONST.REGEX.CODE_2FA)); } /** From cf2f2b9767ec8e702b317d1e4a467ec3d53a6747 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 29 Dec 2022 11:47:43 -0500 Subject: [PATCH 07/11] simplify password and twofactor validation --- src/pages/signin/PasswordForm.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js index 1aad5bc0085..b241c61302d 100755 --- a/src/pages/signin/PasswordForm.js +++ b/src/pages/signin/PasswordForm.js @@ -114,24 +114,25 @@ class PasswordForm extends React.Component { * Check that all the form fields are valid, then trigger the submit callback */ validateAndSubmitForm() { - if (!this.state.password.trim() && this.props.account.requiresTwoFactorAuth && !this.state.twoFactorAuthCode.trim()) { + const password = this.state.password.trim(); + const twoFactorCode = this.state.twoFactorAuthCode.trim(); + + if (!password && this.props.account.requiresTwoFactorAuth && !twoFactorCode) { this.setState({formError: 'passwordForm.pleaseFillOutAllFields'}); return; } - if (!this.state.password.trim()) { + if (!password) { this.setState({formError: 'passwordForm.pleaseFillPassword'}); return; } - if (!ValidationUtils.isValidPassword(this.state.password)) { + if (!ValidationUtils.isValidPassword(password)) { this.setState({formError: 'passwordForm.error.incorrectPassword'}); return; } if (this.props.account.requiresTwoFactorAuth) { - const twoFactorCode = this.state.twoFactorAuthCode.trim(); - if (!twoFactorCode) { this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'}); return; @@ -147,7 +148,7 @@ class PasswordForm extends React.Component { formError: null, }); - Session.signIn(this.state.password, this.state.twoFactorAuthCode); + Session.signIn(password, twoFactorCode); } render() { From 2c9ec97e9332b16789902397e118b8470ef3cb2a Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 29 Dec 2022 11:51:54 -0500 Subject: [PATCH 08/11] de-nest and further simplify validation --- src/pages/signin/PasswordForm.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js index b241c61302d..89d3199dfc0 100755 --- a/src/pages/signin/PasswordForm.js +++ b/src/pages/signin/PasswordForm.js @@ -116,8 +116,9 @@ class PasswordForm extends React.Component { validateAndSubmitForm() { const password = this.state.password.trim(); const twoFactorCode = this.state.twoFactorAuthCode.trim(); + const {requiresTwoFactorAuth} = this.props.account; - if (!password && this.props.account.requiresTwoFactorAuth && !twoFactorCode) { + if (!password && requiresTwoFactorAuth && !twoFactorCode) { this.setState({formError: 'passwordForm.pleaseFillOutAllFields'}); return; } @@ -132,16 +133,14 @@ class PasswordForm extends React.Component { return; } - if (this.props.account.requiresTwoFactorAuth) { - if (!twoFactorCode) { - this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'}); - return; - } + if (requiresTwoFactorAuth && !twoFactorCode) { + this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'}); + return; + } - if (!ValidationUtils.isValidTwoFactorCode(twoFactorCode)) { - this.setState({formError: 'passwordForm.error.incorrect2fa'}); - return; - } + if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(twoFactorCode)) { + this.setState({formError: 'passwordForm.error.incorrect2fa'}); + return; } this.setState({ From 0580cf8bfadfcd1c7fcc38f95d6f5256f826db18 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 29 Dec 2022 16:13:03 -0500 Subject: [PATCH 09/11] remove object destructuring --- src/pages/signin/PasswordForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js index 89d3199dfc0..d984e6fd27b 100755 --- a/src/pages/signin/PasswordForm.js +++ b/src/pages/signin/PasswordForm.js @@ -116,7 +116,7 @@ class PasswordForm extends React.Component { validateAndSubmitForm() { const password = this.state.password.trim(); const twoFactorCode = this.state.twoFactorAuthCode.trim(); - const {requiresTwoFactorAuth} = this.props.account; + const requiresTwoFactorAuth = this.props.account.requiresTwoFactorAuth; if (!password && requiresTwoFactorAuth && !twoFactorCode) { this.setState({formError: 'passwordForm.pleaseFillOutAllFields'}); From 0bb5d15d18acb12a3bed1a0b9fa9cc648b57f20f Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 2 Jan 2023 10:41:42 -0600 Subject: [PATCH 10/11] restrict 2FA codes to 6 digits --- src/CONST.js | 2 +- tests/unit/ValidationUtilsTest.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 2631115b5e8..69586c54d6d 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -760,7 +760,7 @@ const CONST = { EMOJI_NAME: /:[\w+-]+:/g, EMOJI_SUGGESTIONS: /:[a-zA-Z]{1,20}(\s[a-zA-Z]{0,20})?$/, AFTER_FIRST_LINE_BREAK: /\n.*/g, - CODE_2FA: /^[\w\d+/]+={0,2}$/, // matches twoFactorAuthCode WAF rule + CODE_2FA: /^\d{6}$/, // matches twoFactorAuthCode WAF rule }, PRONOUNS: { diff --git a/tests/unit/ValidationUtilsTest.js b/tests/unit/ValidationUtilsTest.js index fa95ae06b14..82f28a37664 100644 --- a/tests/unit/ValidationUtilsTest.js +++ b/tests/unit/ValidationUtilsTest.js @@ -6,8 +6,12 @@ describe('ValidationUtils', () => { expect(ValidationUtils.isValidTwoFactorCode('123456')).toBe(true); }); + test('numeric two factor code with leading zeroes', () => { + expect(ValidationUtils.isValidTwoFactorCode('000001')).toBe(true); + }); + test('alphanumeric two factor code', () => { - expect(ValidationUtils.isValidTwoFactorCode('abc123')).toBe(true); + expect(ValidationUtils.isValidTwoFactorCode('abc123')).toBe(false); }); test('special characters two factor code', () => { @@ -15,7 +19,7 @@ describe('ValidationUtils', () => { }); test('partial special characters two factor code', () => { - expect(ValidationUtils.isValidTwoFactorCode('abc!@#')).toBe(false); + expect(ValidationUtils.isValidTwoFactorCode('123$%^')).toBe(false); }); }); }); From 1ad0dc6c968a147d0cb07538d7ce82b1effe4a87 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 2 Jan 2023 10:55:05 -0600 Subject: [PATCH 11/11] remove outdated comment --- src/CONST.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index 69586c54d6d..e540d4a063f 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -760,7 +760,7 @@ const CONST = { EMOJI_NAME: /:[\w+-]+:/g, EMOJI_SUGGESTIONS: /:[a-zA-Z]{1,20}(\s[a-zA-Z]{0,20})?$/, AFTER_FIRST_LINE_BREAK: /\n.*/g, - CODE_2FA: /^\d{6}$/, // matches twoFactorAuthCode WAF rule + CODE_2FA: /^\d{6}$/, }, PRONOUNS: {