diff --git a/assets/src/bundles/vault/vault-create-tasks.js b/assets/src/bundles/vault/vault-create-tasks.js --- a/assets/src/bundles/vault/vault-create-tasks.js +++ b/assets/src/bundles/vault/vault-create-tasks.js @@ -5,6 +5,8 @@ * See top-level LICENSE file for more information */ +import * as EmailValidator from 'email-validator'; + import {handleFetchError, csrfPost, htmlAlert} from 'utils/functions'; const alertStyle = { @@ -71,7 +73,7 @@ cookingUrl = Urls.api_1_vault_cook_git_bare(cookingTask.swhid); } if (cookingTask.email) { - cookingUrl += '?email=' + cookingTask.email; + cookingUrl += '?email=' + encodeURIComponent(cookingTask.email); } try { @@ -102,14 +104,9 @@ } } -function validateEmail(email) { - const re = /^(([^<>()[\]\\.,;:\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,}))$/; - return re.test(String(email).toLowerCase()); -} - export function cookDirectoryArchive(swhid) { const email = $('#swh-vault-directory-email').val().trim(); - if (!email || validateEmail(email)) { + if (!email || EmailValidator.validate(email)) { const cookingTask = { 'bundle_type': 'flat', 'swhid': swhid, @@ -133,7 +130,7 @@ export function cookRevisionArchive(revisionId) { const email = $('#swh-vault-revision-email').val().trim(); - if (!email || validateEmail(email)) { + if (!email || EmailValidator.validate(email)) { const cookingTask = { 'bundle_type': 'git_bare', 'swhid': revisionId, diff --git a/cypress/integration/vault.spec.js b/cypress/integration/vault.spec.js --- a/cypress/integration/vault.spec.js +++ b/cypress/integration/vault.spec.js @@ -407,6 +407,38 @@ .should('contain', 'Archive cooking request successfully submitted.'); }); + it('should create a directory cooking task with an email address', function() { + // Browse a directory + cy.visit(this.directoryUrl); + + // Stub responses when checking vault task status + cy.intercept('GET', this.vaultDirectoryUrl, {body: {'exception': 'NotFoundExc'}}) + .as('checkVaultCookingTask'); + + // Stub responses when requesting the vault API to simulate + // a task has been created + cy.intercept('POST', this.vaultDirectoryUrl + '?email=foo%2Bbar%40example.org', { + body: this.genVaultDirCookingResponse('new') + }).as('createVaultCookingTask'); + + // Open vault cook directory modal + cy.contains('button', 'Download') + .click(); + + cy.wait('@checkVaultCookingTask'); + + // Create a vault cooking task through the GUI and fill email input + cy.get('#vault-cook-directory-modal input[type="email"]') + .type('foo+bar@example.org', {force: true}); + + cy.get('#vault-cook-directory-modal') + .contains('button:visible', 'Ok') + .click(); + + cy.wait('@createVaultCookingTask'); + + }); + it('should offer to recook an archive if no longer available for download', function() { updateVaultItemList(this.vaultItems); diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "d3": "^7.0.3", "datatables.net-responsive-bs4": "^2.2.9", "dompurify": "^2.3.3", + "email-validator": "^2.0.4", "highlight.js": "^11.2.0", "highlightjs-line-numbers.js": "^2.8.0", "html-encoder-decoder": "^1.3.9", diff --git a/yarn.lock b/yarn.lock --- a/yarn.lock +++ b/yarn.lock @@ -5857,6 +5857,11 @@ minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +email-validator@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" + integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"