From 78afb464cb5218fe2a586b69dae20549fbb322ae Mon Sep 17 00:00:00 2001 From: Rafael Kitover Date: Sat, 19 Oct 2024 18:26:48 +0000 Subject: [PATCH] Replace isemail to fix deprecation warning Fix the deprecation warning for the punycode module, which is a dependency of isemail, by replacing isemail with node-email-verifier, which also checks that email domains have an MX record. Add support for the timeout option for domain MX verification. Add tests for MX verification and timeouts. Signed-off-by: Rafael Kitover --- README.md | 8 ++--- lib/proto/mailto.js | 20 +++++++++--- package-lock.json | 69 +++++++++++++++++++++-------------------- package.json | 2 +- test/link-check.test.js | 21 +++++++++++++ 5 files changed, 77 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index aedd8a4..4ee3092 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ eventually ends in a `200 OK` response. To minimize bandwidth, an HTTP HEAD is performed. If that fails (e.g. with a `405 Method Not Allowed`), an HTTP GET is performed. Redirects are followed. -In the case of `mailto:` links, this module validates the e-mail address -using [isemail](https://www.npmjs.com/package/isemail). +In the case of `mailto:` links, this module validates the e-mail address using +[node-email-verifier](https://www.npmjs.com/package/node-email-verifier). ## API @@ -52,7 +52,7 @@ Parameters: ```js 'use strict'; -const linkCheck = require('link-check'); +import linkCheck from 'link-check'; linkCheck('http://example.com', function (err, result) { if (err) { @@ -68,7 +68,7 @@ linkCheck('http://example.com', function (err, result) { ```js 'use strict'; -const linkCheck = require('link-check'); +import linkCheck from 'link-check'; linkCheck('http://example.com', { headers: { 'Authorization': 'Basic Zm9vOmJhcg==' } }, function (err, result) { if (err) { diff --git a/lib/proto/mailto.js b/lib/proto/mailto.js index ee5cc51..43b2a20 100644 --- a/lib/proto/mailto.js +++ b/lib/proto/mailto.js @@ -1,10 +1,10 @@ "use strict"; -const Isemail = require('isemail'); const LinkCheckResult = require('../LinkCheckResult'); -module.exports = { - check: function (link, opts, callback) { +module.exports.check = (link, opts, callback) => { + import('node-email-verifier').then((mod) => { + const emailValidator = mod.default; const address = link .substr(7) // strip "mailto:" .split('?')[0]; // trim ?subject=blah hfields @@ -13,6 +13,16 @@ module.exports = { * so it's safe to split on '?' and pick [0]. */ - callback(null, new LinkCheckResult(opts, link, Isemail.validate(address) ? 200 : 400, null)); - } + emailValidator(address, { checkMx: true, timeout: opts.timeout || '10s' }).then((emailValid) => { + if (!emailValid) { + return callback(null, new LinkCheckResult(opts, link, 400, null)); + } + return callback(null, new LinkCheckResult(opts, link, 200, null)); + }).catch((error) => { + if (error.message.match(/timed out/)) { + return callback(null, new LinkCheckResult(opts, link, 0, { message: 'Domain MX lookup timed out', code: 'ECONNRESET' })); + } + return callback(null, new LinkCheckResult(opts, link, 0, error)); + }); + }); }; diff --git a/package-lock.json b/package-lock.json index dd980e4..dafcddf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "license": "ISC", "dependencies": { "is-relative-url": "^4.0.0", - "isemail": "^3.2.0", "ms": "^2.1.3", "needle": "^3.3.1", + "node-email-verifier": "^2.0.0", "proxy-agent": "^6.4.0" }, "devDependencies": { @@ -1255,17 +1255,6 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, - "node_modules/isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "dependencies": { - "punycode": "2.x.x" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1553,6 +1542,19 @@ "node": ">= 0.4.0" } }, + "node_modules/node-email-verifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-email-verifier/-/node-email-verifier-2.0.0.tgz", + "integrity": "sha512-AHcppjOH2KT0mxakrxFMOMjV/gOVMRpYvnJUkNfgF9oJ3INdVmqcMFJ5TlM8elpTPwt6A7bSp1IMnnWcxGom/Q==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3", + "validator": "^13.11.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1734,14 +1736,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -2139,6 +2133,14 @@ "node": ">= 0.4.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3172,14 +3174,6 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "requires": { - "punycode": "2.x.x" - } - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3392,6 +3386,15 @@ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" }, + "node-email-verifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-email-verifier/-/node-email-verifier-2.0.0.tgz", + "integrity": "sha512-AHcppjOH2KT0mxakrxFMOMjV/gOVMRpYvnJUkNfgF9oJ3INdVmqcMFJ5TlM8elpTPwt6A7bSp1IMnnWcxGom/Q==", + "requires": { + "ms": "^2.1.3", + "validator": "^13.11.0" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3525,11 +3528,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -3833,6 +3831,11 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "dev": true }, + "validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index c2f1a7e..9bd7e0f 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,9 @@ "homepage": "https://github.com/tcort/link-check#readme", "dependencies": { "is-relative-url": "^4.0.0", - "isemail": "^3.2.0", "ms": "^2.1.3", "needle": "^3.3.1", + "node-email-verifier": "^2.0.0", "proxy-agent": "^6.4.0" }, "devDependencies": { diff --git a/test/link-check.test.js b/test/link-check.test.js index 887792f..b67d727 100644 --- a/test/link-check.test.js +++ b/test/link-check.test.js @@ -268,6 +268,18 @@ describe('link-check', function () { }); }); + it('should handle timeout for mailto validation', function (done) { + linkCheck('mailto:linuxgeek@gmail.com', { timeout: '1ms' }, function (err, result) { + expect(err).to.be(null); + expect(result.link).to.be('mailto:linuxgeek@gmail.com'); + expect(result.status).to.be('dead'); + expect(result.statusCode).to.be(0); + expect(result.err.code).to.be('ECONNRESET'); + expect(result.err.message).to.be('Domain MX lookup timed out'); + done(); + }); + }); + it('should handle valid mailto with encoded characters in address', function (done) { linkCheck('mailto:foo%20bar@example.org', function (err, result) { expect(err).to.be(null); @@ -286,6 +298,15 @@ describe('link-check', function () { }); }); + it('should handle valid mailto with invalid domain without MX record', function (done) { + linkCheck('mailto:linuxgeek@gmai.lcom', function (err, result) { + expect(err).to.be(null); + expect(result.link).to.be('mailto:linuxgeek@gmai.lcom'); + expect(result.status).to.be('dead'); + done(); + }); + }); + it('should handle invalid mailto', function (done) { linkCheck('mailto:foo@@bar@@baz', function (err, result) { expect(err).to.be(null);