From 80f66629571d034e1ba48ce36b045dc24a1cb1e6 Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Mon, 23 May 2022 12:56:55 +0100 Subject: [PATCH] Add option for parsing valid RFC5322 email addresses (#8065) GitOrigin-RevId: 6af8ae850bd8075e6bf0ebcafd2731177cdf49ad --- package-lock.json | 12 +++++++ .../CollaboratorsInviteController.js | 2 +- .../app/src/Features/Helpers/EmailHelper.js | 13 +++++++- services/web/package.json | 1 + .../unit/src/HelperFiles/EmailHelperTests.js | 32 +++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 services/web/test/unit/src/HelperFiles/EmailHelperTests.js diff --git a/package-lock.json b/package-lock.json index c91a683e5c..1257a99fae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11998,6 +11998,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz", "integrity": "sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==" }, + "node_modules/email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==" + }, "node_modules/emitter-listener": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", @@ -35051,6 +35056,7 @@ "daterangepicker": "https://github.com/overleaf/daterangepicker/archive/e496d2d44ca53e208c930e4cb4bcf29bcefa4550.tar.gz", "downshift": "^6.1.0", "east": "^2.0.2", + "email-addresses": "^5.0.0", "events": "^3.3.0", "express": "4.17.1", "express-bearer-token": "^2.4.0", @@ -42796,6 +42802,7 @@ "daterangepicker": "https://github.com/overleaf/daterangepicker/archive/e496d2d44ca53e208c930e4cb4bcf29bcefa4550.tar.gz", "downshift": "^6.1.0", "east": "^2.0.2", + "email-addresses": "^5.0.0", "es6-promise": "^4.2.8", "escodegen": "^2.0.0", "eslint-config-standard-jsx": "^11.0.0-0", @@ -50335,6 +50342,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz", "integrity": "sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==" }, + "email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==" + }, "emitter-listener": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", diff --git a/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js b/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js index 262be3cb3f..e87ea0cb62 100644 --- a/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js +++ b/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js @@ -125,7 +125,7 @@ module.exports = CollaboratorsInviteController = { return res.json({ invite: null }) } ;({ email, privileges } = req.body) - email = EmailHelper.parseEmail(email) + email = EmailHelper.parseEmail(email, true) if (email == null || email === '') { logger.debug( { projectId, email, sendingUserId }, diff --git a/services/web/app/src/Features/Helpers/EmailHelper.js b/services/web/app/src/Features/Helpers/EmailHelper.js index 79d112f7a5..97e4afb84d 100644 --- a/services/web/app/src/Features/Helpers/EmailHelper.js +++ b/services/web/app/src/Features/Helpers/EmailHelper.js @@ -1,3 +1,5 @@ +const { parseOneAddress } = require('email-addresses') + const EMAIL_REGEXP = // eslint-disable-next-line no-useless-escape /^([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ @@ -7,10 +9,19 @@ function getDomain(email) { return email ? email.split('@').pop() : null } -function parseEmail(email) { +function parseEmail(email, parseRfcAddress = false) { if (email == null) { return null } + + if (parseRfcAddress) { + const result = parseOneAddress(email) + if (!result) { + return null + } + email = result.address + } + if (email.length > 254) { return null } diff --git a/services/web/package.json b/services/web/package.json index 995ef6bfc5..8955f74823 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -106,6 +106,7 @@ "daterangepicker": "https://github.com/overleaf/daterangepicker/archive/e496d2d44ca53e208c930e4cb4bcf29bcefa4550.tar.gz", "downshift": "^6.1.0", "east": "^2.0.2", + "email-addresses": "^5.0.0", "events": "^3.3.0", "express": "4.17.1", "express-bearer-token": "^2.4.0", diff --git a/services/web/test/unit/src/HelperFiles/EmailHelperTests.js b/services/web/test/unit/src/HelperFiles/EmailHelperTests.js new file mode 100644 index 0000000000..4e8aa6fc6e --- /dev/null +++ b/services/web/test/unit/src/HelperFiles/EmailHelperTests.js @@ -0,0 +1,32 @@ +const { expect } = require('chai') +const { + parseEmail, +} = require('../../../../app/src/Features/Helpers/EmailHelper') + +describe('EmailHelper', function () { + it('should parse a single email', function () { + const address = 'test@example.com' + const expected = 'test@example.com' + expect(parseEmail(address)).to.equal(expected) + expect(parseEmail(address, true)).to.equal(expected) + }) + + it('should parse a valid email address', function () { + const address = '"Test Person" ' + const expected = 'test@example.com' + expect(parseEmail(address)).to.equal(null) + expect(parseEmail(address, true)).to.equal(expected) + }) + + it('should return null for an invalid single email', function () { + const address = 'testexample.com' + expect(parseEmail(address)).to.equal(null) + expect(parseEmail(address, true)).to.equal(null) + }) + + it('should return null for an invalid email address', function () { + const address = '"Test Person" test@example.com>' + expect(parseEmail(address)).to.equal(null) + expect(parseEmail(address, true)).to.equal(null) + }) +})