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.
@@ -31,7 +31,7 @@ class RelaxedHash {
31
31
  this.bodyHashedBytes = 0;
32
32
  this.maxBodyLength = maxBodyLength;
33
33
 
34
- this.maxSizeReached = false;
34
+ this.maxSizeReached = maxBodyLength === 0;
35
35
 
36
36
  this.emptyLinesQueue = [];
37
37
  }
@@ -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++) {
@@ -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 } = options || {};
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
- if (typeof signTime === 'string' || typeof signTime === 'number') {
37
- signTime = new Date(signTime);
38
- }
37
+ opts.t = Math.floor(getCurTime(signTime).getTime() / 1000);
38
+ }
39
39
 
40
- if (Object.prototype.toString.call(signTime) === '[object Date]' && signTime.toString() !== 'Invalid Date') {
41
- // we need a unix timestamp value
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 } = options || {};
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
- if (typeof signTime === 'string' || typeof signTime === 'number') {
39
- signTime = new Date(signTime);
40
- }
39
+ opts.t = Math.floor(getCurTime(signTime).getTime() / 1000);
40
+ }
41
41
 
42
- if (Object.prototype.toString.call(signTime) === '[object Date]' && signTime.toString() !== 'Invalid Date') {
43
- // we need a unix timestamp value
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.5.tgz 1.0.5 Postal Systems OÜ
4
- fast-xml-parser MIT git+https://github.com/NaturalIntelligence/fast-xml-parser.git 4.0.9 Amit Gupta (https://amitkumargupta.work/)
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/sideway/joi.git 17.6.0 n/a
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.7 Andris Reinman
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
@@ -1,4 +1,4 @@
1
- .TH "MAILAUTH" "1" "August 2022" "v4.0.1" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "January 2023" "v4.0.2" "Mailauth Help"
2
2
  .SH "NAME"
3
3
  \fBmailauth\fR
4
4
  .QP
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailauth",
3
- "version": "4.0.1",
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.6",
36
- "eslint": "8.22.0",
35
+ "chai": "4.3.7",
36
+ "eslint": "8.32.0",
37
37
  "eslint-config-nodemailer": "1.2.0",
38
- "eslint-config-prettier": "8.5.0",
38
+ "eslint-config-prettier": "8.6.0",
39
39
  "js-yaml": "4.1.0",
40
- "license-report": "6.0.0",
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.0.0",
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.9",
49
+ "fast-xml-parser": "4.0.15",
50
50
  "ipaddr.js": "2.0.1",
51
- "joi": "17.6.0",
52
- "libmime": "5.1.0",
53
- "node-forge": "1.3.1",
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.1.1",
57
- "yargs": "17.5.1"
55
+ "punycode": "2.3.0",
56
+ "yargs": "17.6.2"
58
57
  },
59
58
  "engines": {
60
59
  "node": ">=16.0.0"