mailauth 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dkim/body/relaxed.js +1 -1
- package/lib/dkim/body/simple.js +13 -0
- package/lib/dkim/dkim-signer.js +6 -1
- package/lib/dkim/dkim-verifier.js +21 -1
- package/lib/dkim/header/relaxed.js +7 -10
- package/lib/dkim/header/simple.js +7 -10
- package/lib/parse-dkim-headers.js +1 -1
- package/lib/tools.js +27 -10
- package/licenses.txt +4 -4
- package/man/mailauth.1 +1 -1
- package/package.json +12 -13
package/lib/dkim/body/relaxed.js
CHANGED
package/lib/dkim/body/simple.js
CHANGED
|
@@ -21,12 +21,18 @@ class SimpleHash {
|
|
|
21
21
|
this.byteLength = 0;
|
|
22
22
|
|
|
23
23
|
this.bodyHashedBytes = 0;
|
|
24
|
+
|
|
24
25
|
this.maxBodyLength = maxBodyLength;
|
|
26
|
+
this.maxSizeReached = maxBodyLength === 0;
|
|
25
27
|
|
|
26
28
|
this.lastNewline = false;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
_updateBodyHash(chunk) {
|
|
32
|
+
if (this.maxSizeReached) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
// the following is needed for l= option
|
|
31
37
|
if (
|
|
32
38
|
typeof this.maxBodyLength === 'number' &&
|
|
@@ -34,10 +40,12 @@ class SimpleHash {
|
|
|
34
40
|
this.maxBodyLength >= 0 &&
|
|
35
41
|
this.bodyHashedBytes + chunk.length > this.maxBodyLength
|
|
36
42
|
) {
|
|
43
|
+
this.maxSizeReached = true;
|
|
37
44
|
if (this.bodyHashedBytes >= this.maxBodyLength) {
|
|
38
45
|
// nothing to do here, skip entire chunk
|
|
39
46
|
return;
|
|
40
47
|
}
|
|
48
|
+
|
|
41
49
|
// only use allowed size of bytes
|
|
42
50
|
chunk = chunk.slice(0, this.maxBodyLength - this.bodyHashedBytes);
|
|
43
51
|
}
|
|
@@ -49,6 +57,11 @@ class SimpleHash {
|
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
update(chunk) {
|
|
60
|
+
this.byteLength += (chunk && chunk.length) || 0;
|
|
61
|
+
if (this.maxSizeReached) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
52
65
|
if (this.remainder.length) {
|
|
53
66
|
// see if we can release the last remainder
|
|
54
67
|
for (let i = 0; i < chunk.length; i++) {
|
package/lib/dkim/dkim-signer.js
CHANGED
|
@@ -10,14 +10,16 @@ class DkimSigner extends MessageParser {
|
|
|
10
10
|
constructor(options) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
|
-
let { canonicalization, algorithm, signTime, headerList, signatureData, arc, bodyHash, headers, getARChain } = options || {};
|
|
13
|
+
let { canonicalization, algorithm, signTime, headerList, signatureData, arc, bodyHash, headers, getARChain, expires } = options || {};
|
|
14
14
|
|
|
15
15
|
this.algorithm = algorithm || false;
|
|
16
16
|
this.canonicalization = canonicalization || 'relaxed/relaxed';
|
|
17
17
|
|
|
18
18
|
this.errors = [];
|
|
19
19
|
|
|
20
|
+
this.expires = expires;
|
|
20
21
|
this.signTime = signTime;
|
|
22
|
+
|
|
21
23
|
this.headerList = headerList;
|
|
22
24
|
|
|
23
25
|
this.signatureData = [].concat(signatureData || []).map(entry => {
|
|
@@ -243,7 +245,10 @@ class DkimSigner extends MessageParser {
|
|
|
243
245
|
instance: this.arc?.instance, // ARC only
|
|
244
246
|
algorithm,
|
|
245
247
|
canonicalization: this.getCanonicalization(signatureData).canonicalization,
|
|
248
|
+
|
|
246
249
|
signTime: this.signTime,
|
|
250
|
+
expires: this.expires,
|
|
251
|
+
|
|
247
252
|
bodyHash: this.bodyHashes.has(hashKey) ? this.bodyHashes.get(hashKey).hash : null
|
|
248
253
|
},
|
|
249
254
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getSigningHeaderLines, getPublicKey, parseDkimHeaders, formatAuthHeaderRow, getAlignment } = require('../../lib/tools');
|
|
3
|
+
const { getSigningHeaderLines, getPublicKey, parseDkimHeaders, formatAuthHeaderRow, getAlignment, getCurTime } = require('../../lib/tools');
|
|
4
4
|
const { MessageParser } = require('./message-parser');
|
|
5
5
|
const { dkimBody } = require('./body');
|
|
6
6
|
const { generateCanonicalizedHeader } = require('./header');
|
|
@@ -16,6 +16,8 @@ class DkimVerifier extends MessageParser {
|
|
|
16
16
|
this.resolver = this.options.resolver;
|
|
17
17
|
this.minBitLength = this.options.minBitLength;
|
|
18
18
|
|
|
19
|
+
this.curTime = getCurTime(this.options.curTime);
|
|
20
|
+
|
|
19
21
|
this.results = [];
|
|
20
22
|
|
|
21
23
|
this.signatureHeaders = [];
|
|
@@ -114,6 +116,12 @@ class DkimVerifier extends MessageParser {
|
|
|
114
116
|
signatureHeader.signingDomain = signatureHeader.parsed?.d?.value || '';
|
|
115
117
|
signatureHeader.selector = signatureHeader.parsed?.s?.value || '';
|
|
116
118
|
|
|
119
|
+
signatureHeader.timestamp =
|
|
120
|
+
signatureHeader.parsed?.t && !isNaN(signatureHeader.parsed?.t?.value) ? new Date(signatureHeader.parsed?.t?.value * 1000) : null;
|
|
121
|
+
|
|
122
|
+
signatureHeader.expiration =
|
|
123
|
+
signatureHeader.parsed?.x && !isNaN(signatureHeader.parsed?.x?.value) ? new Date(signatureHeader.parsed?.x?.value * 1000) : null;
|
|
124
|
+
|
|
117
125
|
signatureHeader.maxBodyLength =
|
|
118
126
|
signatureHeader.parsed?.l?.value && !isNaN(signatureHeader.parsed?.l?.value) ? signatureHeader.parsed?.l?.value : '';
|
|
119
127
|
|
|
@@ -228,6 +236,18 @@ class DkimVerifier extends MessageParser {
|
|
|
228
236
|
if (status.result === 'fail') {
|
|
229
237
|
status.comment = 'bad signature';
|
|
230
238
|
}
|
|
239
|
+
|
|
240
|
+
if (status.result === 'pass') {
|
|
241
|
+
if (signatureHeader.expiration && signatureHeader.timestamp && signatureHeader.expiration < signatureHeader.timestamp) {
|
|
242
|
+
status.result = 'neutral';
|
|
243
|
+
status.comment = 'invalid expiration';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (signatureHeader.expiration && signatureHeader.expiration < this.curTime) {
|
|
247
|
+
status.result = 'neutral';
|
|
248
|
+
status.comment = 'expired';
|
|
249
|
+
}
|
|
250
|
+
}
|
|
231
251
|
} catch (err) {
|
|
232
252
|
status.result = 'neutral';
|
|
233
253
|
status.comment = err.message;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { formatSignatureHeaderLine, formatRelaxedLine } = require('../../../lib/tools');
|
|
3
|
+
const { formatSignatureHeaderLine, formatRelaxedLine, getCurTime } = require('../../../lib/tools');
|
|
4
4
|
|
|
5
5
|
// generate headers for signing
|
|
6
6
|
const relaxedHeaders = (type, signingHeaderLines, options) => {
|
|
7
|
-
let { signatureHeaderLine, signingDomain, selector, algorithm, canonicalization, bodyHash, signTime, signature, instance, bodyHashedBytes } =
|
|
7
|
+
let { signatureHeaderLine, signingDomain, selector, algorithm, canonicalization, bodyHash, signTime, signature, instance, bodyHashedBytes, expires } =
|
|
8
|
+
options || {};
|
|
8
9
|
let chunks = [];
|
|
9
10
|
|
|
10
11
|
for (let signedHeaderLine of signingHeaderLines.headers) {
|
|
@@ -33,15 +34,11 @@ const relaxedHeaders = (type, signingHeaderLines, options) => {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
if (signTime) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
37
|
+
opts.t = Math.floor(getCurTime(signTime).getTime() / 1000);
|
|
38
|
+
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
signTime = Math.round(signTime.getTime() / 1000);
|
|
43
|
-
opts.t = signTime;
|
|
44
|
-
}
|
|
40
|
+
if (expires) {
|
|
41
|
+
opts.x = Math.floor(getCurTime(expires).getTime() / 1000);
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
signatureHeaderLine = formatSignatureHeaderLine(
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { formatSignatureHeaderLine } = require('../../../lib/tools');
|
|
3
|
+
const { formatSignatureHeaderLine, getCurTime } = require('../../../lib/tools');
|
|
4
4
|
|
|
5
5
|
const formatSimpleLine = (line, suffix) => Buffer.from(line.toString('binary') + (suffix ? suffix : ''), 'binary');
|
|
6
6
|
|
|
7
7
|
// generate headers for signing
|
|
8
8
|
const simpleHeaders = (type, signingHeaderLines, options) => {
|
|
9
|
-
let { signatureHeaderLine, signingDomain, selector, algorithm, canonicalization, bodyHash, signTime, signature, instance, bodyHashedBytes } =
|
|
9
|
+
let { signatureHeaderLine, signingDomain, selector, algorithm, canonicalization, bodyHash, signTime, signature, instance, bodyHashedBytes, expires } =
|
|
10
|
+
options || {};
|
|
10
11
|
let chunks = [];
|
|
11
12
|
|
|
12
13
|
for (let signedHeaderLine of signingHeaderLines.headers) {
|
|
@@ -35,15 +36,11 @@ const simpleHeaders = (type, signingHeaderLines, options) => {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
if (signTime) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
39
|
+
opts.t = Math.floor(getCurTime(signTime).getTime() / 1000);
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
signTime = Math.round(signTime.getTime() / 1000);
|
|
45
|
-
opts.t = signTime;
|
|
46
|
-
}
|
|
42
|
+
if (expires) {
|
|
43
|
+
opts.x = Math.floor(getCurTime(expires).getTime() / 1000);
|
|
47
44
|
}
|
|
48
45
|
|
|
49
46
|
signatureHeaderLine = formatSignatureHeaderLine(
|
|
@@ -245,7 +245,7 @@ const headerParser = buf => {
|
|
|
245
245
|
} else if (['bh', 'b', 'p', 'h'].includes(parts[i].key)) {
|
|
246
246
|
// remove unneeded whitespace
|
|
247
247
|
parts[i].value = parts[i].value.replace(/\s+/g, '');
|
|
248
|
-
} else if (['l', 'v', 't'].includes(parts[i].key) && !isNaN(parts[i].value)) {
|
|
248
|
+
} else if (['l', 'v', 't', 'x'].includes(parts[i].key) && !isNaN(parts[i].value)) {
|
|
249
249
|
parts[i].value = Number(parts[i].value);
|
|
250
250
|
} else if (parts[i].key === 'i' && /^arc-/i.test(headerKey)) {
|
|
251
251
|
parts[i].value = Number(parts[i].value);
|
package/lib/tools.js
CHANGED
|
@@ -10,7 +10,6 @@ const https = require('https');
|
|
|
10
10
|
const packageData = require('../package');
|
|
11
11
|
const parseDkimHeaders = require('./parse-dkim-headers');
|
|
12
12
|
const psl = require('psl');
|
|
13
|
-
const pki = require('node-forge').pki;
|
|
14
13
|
const Joi = require('joi');
|
|
15
14
|
const base64Schema = Joi.string().base64({ paddingRequired: false });
|
|
16
15
|
|
|
@@ -288,14 +287,7 @@ const getPublicKey = async (type, name, minBitLength, resolver) => {
|
|
|
288
287
|
throw err;
|
|
289
288
|
}
|
|
290
289
|
|
|
291
|
-
let modulusLength;
|
|
292
|
-
if (publicKeyObj.asymmetricKeyDetails) {
|
|
293
|
-
modulusLength = publicKeyObj.asymmetricKeyDetails.modulusLength;
|
|
294
|
-
} else {
|
|
295
|
-
// fall back to node-forge
|
|
296
|
-
const pubKeyData = pki.publicKeyFromPem(publicKeyPem.toString());
|
|
297
|
-
modulusLength = pubKeyData.n.bitLength();
|
|
298
|
-
}
|
|
290
|
+
let modulusLength = publicKeyObj.asymmetricKeyDetails.modulusLength;
|
|
299
291
|
|
|
300
292
|
if (keyType === 'rsa' && modulusLength < 1024) {
|
|
301
293
|
let err = new Error('RSA key too short');
|
|
@@ -485,6 +477,29 @@ const getPtrHostname = parsedAddr => {
|
|
|
485
477
|
}
|
|
486
478
|
};
|
|
487
479
|
|
|
480
|
+
function getCurTime(timeValue) {
|
|
481
|
+
if (timeValue) {
|
|
482
|
+
if (typeof timeValue === 'object' && typeof timeValue.toISOString === 'function') {
|
|
483
|
+
return timeValue;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (typeof timeValue === 'number' || !isNaN(timeValue)) {
|
|
487
|
+
let timestamp = Number(timeValue);
|
|
488
|
+
let curTime = new Date(timestamp);
|
|
489
|
+
if (curTime.toString !== 'Invalid Date') {
|
|
490
|
+
return curTime;
|
|
491
|
+
}
|
|
492
|
+
} else if (typeof timeValue === 'string') {
|
|
493
|
+
let curTime = new Date(timeValue);
|
|
494
|
+
if (curTime.toString !== 'Invalid Date') {
|
|
495
|
+
return curTime;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return new Date();
|
|
501
|
+
}
|
|
502
|
+
|
|
488
503
|
module.exports = {
|
|
489
504
|
writeToStream,
|
|
490
505
|
parseHeaders,
|
|
@@ -508,5 +523,7 @@ module.exports = {
|
|
|
508
523
|
formatRelaxedLine,
|
|
509
524
|
formatDomain,
|
|
510
525
|
|
|
511
|
-
getPtrHostname
|
|
526
|
+
getPtrHostname,
|
|
527
|
+
|
|
528
|
+
getCurTime
|
|
512
529
|
};
|
package/licenses.txt
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
name license type link installed version author
|
|
2
2
|
---- ------------ ---- ----------------- ------
|
|
3
|
-
@postalsys/vmc MIT https://registry.npmjs.org/@postalsys/vmc/-/vmc-1.0.
|
|
4
|
-
fast-xml-parser MIT git+https://github.com/NaturalIntelligence/fast-xml-parser.git 4.0.
|
|
3
|
+
@postalsys/vmc MIT https://registry.npmjs.org/@postalsys/vmc/-/vmc-1.0.6.tgz 1.0.6 Postal Systems OÜ
|
|
4
|
+
fast-xml-parser MIT git+https://github.com/NaturalIntelligence/fast-xml-parser.git 4.0.10 Amit Gupta (https://amitkumargupta.work/)
|
|
5
5
|
ipaddr.js MIT git://github.com/whitequark/ipaddr.js.git 2.0.1 whitequark <whitequark@whitequark.org>
|
|
6
|
-
joi BSD-3-Clause git://github.com/
|
|
6
|
+
joi BSD-3-Clause git://github.com/hapijs/joi.git 17.6.1 n/a
|
|
7
7
|
libmime MIT git://github.com/andris9/libmime.git 5.1.0 Andris Reinman <andris@kreata.ee>
|
|
8
8
|
node-forge (BSD-3-Clause OR GPL-2.0) git+https://github.com/digitalbazaar/forge.git 1.3.1 Digital Bazaar, Inc. support@digitalbazaar.com http://digitalbazaar.com/
|
|
9
|
-
nodemailer MIT git+https://github.com/nodemailer/nodemailer.git 6.7.
|
|
9
|
+
nodemailer MIT git+https://github.com/nodemailer/nodemailer.git 6.7.8 Andris Reinman
|
|
10
10
|
psl MIT git+ssh://git@github.com/lupomontero/psl.git 1.9.0 Lupo Montero <lupomontero@gmail.com> (https://lupomontero.com/)
|
|
11
11
|
punycode MIT git+https://github.com/bestiejs/punycode.js.git 2.1.1 Mathias Bynens https://mathiasbynens.be/
|
|
12
12
|
yargs MIT git+https://github.com/yargs/yargs.git 17.5.1 n/a
|
package/man/mailauth.1
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailauth",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Email authentication library for Node.js",
|
|
5
5
|
"main": "lib/mailauth.js",
|
|
6
6
|
"scripts": {
|
|
@@ -32,29 +32,28 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/postalsys/mailauth",
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"chai": "4.3.
|
|
36
|
-
"eslint": "8.
|
|
35
|
+
"chai": "4.3.7",
|
|
36
|
+
"eslint": "8.32.0",
|
|
37
37
|
"eslint-config-nodemailer": "1.2.0",
|
|
38
|
-
"eslint-config-prettier": "8.
|
|
38
|
+
"eslint-config-prettier": "8.6.0",
|
|
39
39
|
"js-yaml": "4.1.0",
|
|
40
|
-
"license-report": "6.
|
|
40
|
+
"license-report": "6.3.0",
|
|
41
41
|
"marked": "0.7.0",
|
|
42
42
|
"marked-man": "0.7.0",
|
|
43
43
|
"mbox-reader": "1.1.5",
|
|
44
|
-
"mocha": "10.
|
|
44
|
+
"mocha": "10.2.0",
|
|
45
45
|
"pkg": "5.8.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@postalsys/vmc": "1.0.6",
|
|
49
|
-
"fast-xml-parser": "4.0.
|
|
49
|
+
"fast-xml-parser": "4.0.15",
|
|
50
50
|
"ipaddr.js": "2.0.1",
|
|
51
|
-
"joi": "17.
|
|
52
|
-
"libmime": "5.
|
|
53
|
-
"
|
|
54
|
-
"nodemailer": "6.7.8",
|
|
51
|
+
"joi": "17.7.0",
|
|
52
|
+
"libmime": "5.2.0",
|
|
53
|
+
"nodemailer": "6.9.0",
|
|
55
54
|
"psl": "1.9.0",
|
|
56
|
-
"punycode": "2.
|
|
57
|
-
"yargs": "17.
|
|
55
|
+
"punycode": "2.3.0",
|
|
56
|
+
"yargs": "17.6.2"
|
|
58
57
|
},
|
|
59
58
|
"engines": {
|
|
60
59
|
"node": ">=16.0.0"
|