mailauth 4.6.4 → 4.6.6

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/.ncurc.js CHANGED
@@ -7,6 +7,9 @@ module.exports = {
7
7
  'chai',
8
8
 
9
9
  // Fails in Node 16
10
- 'undici'
10
+ 'undici',
11
+
12
+ // fix later
13
+ 'eslint'
11
14
  ]
12
15
  };
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.6.6](https://github.com/postalsys/mailauth/compare/v4.6.5...v4.6.6) (2024-05-13)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **deps:** Bumped deps to clear out security warnings ([4ca35fe](https://github.com/postalsys/mailauth/commit/4ca35fef37e37ae715c420b8a52c7cb202e4b360))
9
+
10
+ ## [4.6.5](https://github.com/postalsys/mailauth/compare/v4.6.4...v4.6.5) (2024-02-12)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **dkim:** Added new output property mimeStructureStart ([8f25353](https://github.com/postalsys/mailauth/commit/8f25353fa6a67ba3e1f0c5091325007b2434a29d))
16
+
3
17
  ## [4.6.4](https://github.com/postalsys/mailauth/compare/v4.6.3...v4.6.4) (2024-02-05)
4
18
 
5
19
 
package/bin/mailauth.js CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  const yargs = require('yargs/yargs');
6
6
  const { hideBin } = require('yargs/helpers');
7
- const os = require('os');
8
- const assert = require('assert');
7
+ const os = require('node:os');
8
+ const assert = require('node:assert');
9
9
 
10
10
  const commandReport = require('../lib/commands/report');
11
11
  const commandSign = require('../lib/commands/sign');
@@ -14,8 +14,8 @@ const commandSpf = require('../lib/commands/spf');
14
14
  const commandVmc = require('../lib/commands/vmc');
15
15
  const commandBodyhash = require('../lib/commands/bodyhash');
16
16
 
17
- const fs = require('fs');
18
- const pathlib = require('path');
17
+ const fs = require('node:fs');
18
+ const pathlib = require('node:path');
19
19
 
20
20
  const argv = yargs(hideBin(process.argv))
21
21
  .command(
package/lib/arc/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { Buffer } = require('node:buffer');
3
4
  const {
4
5
  parseDkimHeaders,
5
6
  formatRelaxedLine,
@@ -9,7 +10,7 @@ const {
9
10
  writeToStream,
10
11
  validateAlgorithm
11
12
  } = require('../../lib/tools');
12
- const crypto = require('crypto');
13
+ const crypto = require('node:crypto');
13
14
  const { DkimSigner } = require('../dkim/dkim-signer');
14
15
 
15
16
  const verifyAS = async (chain, opts) => {
package/lib/bimi/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const crypto = require('crypto');
4
- const dns = require('dns');
3
+ const { Buffer } = require('node:buffer');
4
+ const crypto = require('node:crypto');
5
+ const dns = require('node:dns');
5
6
  const { formatAuthHeaderRow, parseDkimHeaders, formatDomain, getAlignment } = require('../tools');
6
7
  const Joi = require('joi');
7
8
  //const packageData = require('../../package.json');
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { DkimSigner } = require('../dkim/dkim-signer');
4
4
  const { writeToStream } = require('../tools');
5
- const fs = require('fs');
5
+ const fs = require('node:fs');
6
6
 
7
7
  const cmd = async argv => {
8
8
  let source = argv.email;
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const { authenticate } = require('../mailauth');
4
- const fs = require('fs');
5
- const { resolve } = require('dns').promises;
4
+ const fs = require('node:fs');
5
+ const { resolve } = require('node:dns').promises;
6
6
 
7
7
  const cmd = async argv => {
8
8
  let source = argv.email;
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const { authenticate } = require('../mailauth');
4
- const fs = require('fs');
4
+ const fs = require('node:fs');
5
5
  const { GathererStream } = require('../gatherer-stream');
6
- const { resolve } = require('dns').promises;
6
+ const { resolve } = require('node:dns').promises;
7
7
 
8
8
  const cmd = async argv => {
9
9
  let source = argv.email;
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { dkimSign } = require('../dkim/sign');
4
4
  const { GathererStream } = require('../gatherer-stream');
5
- const fs = require('fs');
5
+ const fs = require('node:fs');
6
6
 
7
7
  const cmd = async argv => {
8
8
  let source = argv.email;
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const { spf } = require('../spf');
4
- const fs = require('fs');
5
- const { resolve } = require('dns').promises;
4
+ const fs = require('node:fs');
5
+ const { resolve } = require('node:dns').promises;
6
6
 
7
7
  const cmd = async argv => {
8
8
  let address = argv.sender;
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { validateVMC } = require('../bimi');
4
4
 
5
- const fs = require('fs').promises;
5
+ const fs = require('node:fs').promises;
6
6
 
7
7
  const cmd = async argv => {
8
8
  let bimiData = {};
@@ -2,7 +2,9 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const crypto = require('crypto');
5
+ const { Buffer } = require('node:buffer');
6
+ const crypto = require('node:crypto');
7
+ const { MimeStructureStartFinder } = require('../mime-structure-start-finder');
6
8
 
7
9
  const CHAR_CR = 0x0d;
8
10
  const CHAR_LF = 0x0a;
@@ -39,9 +41,20 @@ class RelaxedHash {
39
41
  this.maxSizeReached = maxBodyLength === 0;
40
42
 
41
43
  this.emptyLinesQueue = [];
44
+
45
+ this.mimeStructureStartFinder = new MimeStructureStartFinder();
46
+ }
47
+
48
+ setContentType(contentTypeObj) {
49
+ if (/^multipart\//i.test(contentTypeObj.value) && contentTypeObj.params.boundary) {
50
+ this.mimeStructureStartFinder.setBoundary(contentTypeObj.params.boundary);
51
+ }
42
52
  }
43
53
 
44
54
  _updateBodyHash(chunk) {
55
+ // serach through the entire document, not just signed part
56
+ this.mimeStructureStartFinder.update(chunk);
57
+
45
58
  this.canonicalizedLength += chunk.length;
46
59
 
47
60
  if (this.maxSizeReached) {
@@ -270,6 +283,10 @@ class RelaxedHash {
270
283
  // finalize
271
284
  return this.bodyHash.digest(encoding);
272
285
  }
286
+
287
+ getMimeStructureStart() {
288
+ return this.mimeStructureStartFinder.getMimeStructureStart();
289
+ }
273
290
  }
274
291
 
275
292
  module.exports = { RelaxedHash };
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const crypto = require('crypto');
3
+ const { Buffer } = require('node:buffer');
4
+ const crypto = require('node:crypto');
5
+ const { MimeStructureStartFinder } = require('../mime-structure-start-finder');
4
6
 
5
7
  /**
6
8
  * Class for calculating body hash of an email message body stream
@@ -30,9 +32,20 @@ class SimpleHash {
30
32
  this.maxSizeReached = maxBodyLength === 0;
31
33
 
32
34
  this.lastNewline = false;
35
+
36
+ this.mimeStructureStartFinder = new MimeStructureStartFinder();
37
+ }
38
+
39
+ setContentType(contentTypeObj) {
40
+ if (/^multipart\//i.test(contentTypeObj.value) && contentTypeObj.params.boundary) {
41
+ this.mimeStructureStartFinder.setBoundary(contentTypeObj.params.boundary);
42
+ }
33
43
  }
34
44
 
35
45
  _updateBodyHash(chunk) {
46
+ // serach through the entire document, not just signed part
47
+ this.mimeStructureStartFinder.update(chunk);
48
+
36
49
  this.canonicalizedLength += chunk.length;
37
50
 
38
51
  if (this.maxSizeReached) {
@@ -115,6 +128,10 @@ class SimpleHash {
115
128
 
116
129
  return this.bodyHash.digest(encoding);
117
130
  }
131
+
132
+ getMimeStructureStart() {
133
+ return this.mimeStructureStartFinder.getMimeStructureStart();
134
+ }
118
135
  }
119
136
 
120
137
  module.exports = { SimpleHash };
@@ -11,7 +11,7 @@ const {
11
11
  const { MessageParser } = require('./message-parser');
12
12
  const { dkimBody } = require('./body');
13
13
  const { generateCanonicalizedHeader } = require('./header');
14
- const crypto = require('crypto');
14
+ const crypto = require('node:crypto');
15
15
 
16
16
  class DkimSigner extends MessageParser {
17
17
  constructor(options) {
@@ -1,13 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ const { Buffer } = require('node:buffer');
3
4
  const { getSigningHeaderLines, getPublicKey, parseDkimHeaders, formatAuthHeaderRow, getAlignment, getCurTime } = require('../../lib/tools');
4
5
  const { MessageParser } = require('./message-parser');
5
6
  const { dkimBody } = require('./body');
6
7
  const { generateCanonicalizedHeader } = require('./header');
7
8
  const { getARChain } = require('../arc');
8
9
  const addressparser = require('nodemailer/lib/addressparser');
9
- const crypto = require('crypto');
10
+ const crypto = require('node:crypto');
10
11
  const { v4: uuidv4 } = require('uuid');
12
+ const libmime = require('libmime');
11
13
 
12
14
  class DkimVerifier extends MessageParser {
13
15
  constructor(options) {
@@ -147,6 +149,23 @@ class DkimVerifier extends MessageParser {
147
149
  if (!this.bodyHashes.has(signatureHeader.bodyHashKey)) {
148
150
  this.bodyHashes.set(signatureHeader.bodyHashKey, dkimBody(signatureHeader.bodyCanon, signatureHeader.hashAlgo, signatureHeader.maxBodyLength));
149
151
  }
152
+
153
+ let headersArray = this.headers.parsed;
154
+ const findLastMethod = typeof headersArray.findLast === 'function' ? headersArray.findLast : headersArray.find;
155
+ if (typeof headersArray.findLast !== 'function') {
156
+ headersArray = [].concat(headersArray).reverse();
157
+ }
158
+ const contentTypeHeader = findLastMethod.call(headersArray, header => header.key === 'content-type');
159
+ if (contentTypeHeader) {
160
+ let line = contentTypeHeader.line.toString();
161
+ if (line.indexOf(':') >= 0) {
162
+ line = line.substring(line.indexOf(':') + 1).trim();
163
+ }
164
+ const parsedContentType = libmime.parseHeaderValue(line);
165
+ for (let hasher of this.bodyHashes.values()) {
166
+ hasher.setContentType(parsedContentType);
167
+ }
168
+ }
150
169
  }
151
170
  }
152
171
 
@@ -165,6 +184,7 @@ class DkimVerifier extends MessageParser {
165
184
  // convert bodyHashes from hash objects to base64 strings
166
185
  for (let [key, bodyHash] of this.bodyHashes.entries()) {
167
186
  this.bodyHashes.get(key).hash = bodyHash.digest('base64');
187
+ this.bodyHashes.get(key).mimeStructureStart = bodyHash.getMimeStructureStart();
168
188
  }
169
189
 
170
190
  for (let signatureHeader of this.signatureHeaders) {
@@ -210,7 +230,9 @@ class DkimVerifier extends MessageParser {
210
230
  : false;
211
231
  }
212
232
 
213
- let bodyHash = this.bodyHashes.get(signatureHeader.bodyHashKey)?.hash;
233
+ const bodyHash = this.bodyHashes.get(signatureHeader.bodyHashKey)?.hash;
234
+ const mimeStructureStart = this.bodyHashes.get(signatureHeader.bodyHashKey)?.mimeStructureStart;
235
+
214
236
  if (signatureHeader.parsed?.bh?.value !== bodyHash) {
215
237
  status.result = 'neutral';
216
238
  status.comment = `body hash did not verify`;
@@ -303,8 +325,9 @@ class DkimVerifier extends MessageParser {
303
325
  signatureHeader.sourceBodyLength = this.bodyHashes.get(signatureHeader.bodyHashKey)?.byteLength;
304
326
 
305
327
  if (typeof signatureHeader.maxBodyLength === 'number' && signatureHeader.maxBodyLength !== signatureHeader.bodyHashedBytes) {
306
- status.result = 'fail';
307
- status.comment = `invalid body length ${signatureHeader.bodyHashedBytes}`;
328
+ console.log('TOTAL', signatureHeader.bodyHashedBytes, 'EXPECTING', signatureHeader.maxBodyLength);
329
+ //status.result = 'fail';
330
+ //status.comment = `invalid body length ${signatureHeader.bodyHashedBytes}`;
308
331
  }
309
332
 
310
333
  let result = {
@@ -344,6 +367,10 @@ class DkimVerifier extends MessageParser {
344
367
  result.canonBodyLengthLimited = false;
345
368
  }
346
369
 
370
+ if (typeof mimeStructureStart === 'number') {
371
+ result.mimeStructureStart = mimeStructureStart;
372
+ }
373
+
347
374
  if (publicKey) {
348
375
  result.publicKey = publicKey.toString();
349
376
  }
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { Buffer } = require('node:buffer');
3
4
  const { formatSignatureHeaderLine, formatRelaxedLine, getCurTime } = require('../../../lib/tools');
4
5
 
5
6
  // generate headers for signing
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { Buffer } = require('node:buffer');
3
4
  const { formatSignatureHeaderLine, getCurTime } = require('../../../lib/tools');
4
5
 
5
6
  const formatSimpleLine = (line, suffix) => Buffer.from(line.toString('binary') + (suffix ? suffix : ''), 'binary');
@@ -2,8 +2,9 @@
2
2
 
3
3
  // Calculates relaxed body hash for a message body stream
4
4
 
5
+ const { Buffer } = require('node:buffer');
5
6
  const { parseHeaders } = require('../../lib/tools');
6
- const Writable = require('stream').Writable;
7
+ const Writable = require('node:stream').Writable;
7
8
 
8
9
  /**
9
10
  * Class for separating header from body
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ const { Buffer } = require('node:buffer');
4
+
5
+ class MimeStructureStartFinder {
6
+ constructor() {
7
+ this.byteCache = [];
8
+
9
+ this.matchFound = false;
10
+ this.noMatch = false;
11
+ this.lineStart = -1;
12
+
13
+ this.prevChunks = 0;
14
+
15
+ this.mimeStructureStart = -1;
16
+ }
17
+
18
+ setBoundary(boundary) {
19
+ this.boundary = (boundary || '').toString().trim();
20
+
21
+ this.boundaryBuf = Array.from(Buffer.from(`--${this.boundary}`));
22
+ this.boundaryBufLen = this.boundaryBuf.length;
23
+ }
24
+
25
+ update(chunk) {
26
+ if (this.matchFound || !this.boundary) {
27
+ return;
28
+ }
29
+
30
+ for (let i = 0, bufLen = chunk.length; i < bufLen; i++) {
31
+ let c = chunk[i];
32
+
33
+ // check ending
34
+ if (c === 0x0a || c === 0x0d) {
35
+ if (!this.noMatch && this.byteCache.length === this.boundaryBufLen) {
36
+ // match found
37
+ this.matchFound = true;
38
+ this.mimeStructureStart = this.lineStart;
39
+ break;
40
+ }
41
+ // reset counter
42
+ this.lineStart = -1;
43
+ this.noMatch = false;
44
+ this.byteCache = [];
45
+ continue;
46
+ }
47
+
48
+ if (this.noMatch) {
49
+ // no need to look
50
+ continue;
51
+ }
52
+
53
+ if (this.lineStart < 0) {
54
+ this.lineStart = this.prevChunks + i;
55
+ }
56
+
57
+ if (this.byteCache.length >= this.boundaryBufLen) {
58
+ this.noMatch = true;
59
+ continue;
60
+ }
61
+
62
+ const expectingByte = this.boundaryBuf[this.byteCache.length];
63
+ if (expectingByte !== c) {
64
+ this.noMatch = true;
65
+ continue;
66
+ }
67
+ this.byteCache[this.byteCache.length] = c;
68
+ }
69
+
70
+ this.prevChunks += chunk.length;
71
+ }
72
+
73
+ getMimeStructureStart() {
74
+ if (!this.boundary) {
75
+ return 0;
76
+ }
77
+
78
+ if (!this.matchFound && !this.noMatch && this.byteCache.length === this.boundaryBufLen) {
79
+ this.matchFound = true;
80
+ this.mimeStructureStart = this.lineStart;
81
+ }
82
+
83
+ return this.mimeStructureStart;
84
+ }
85
+ }
86
+
87
+ module.exports = { MimeStructureStartFinder };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const psl = require('psl');
4
- const dns = require('dns').promises;
4
+ const dns = require('node:dns').promises;
5
5
 
6
6
  const resolveTxt = async (domain, resolver) => {
7
7
  try {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const dns = require('dns').promises;
3
+ const dns = require('node:dns').promises;
4
4
  const punycode = require('punycode/');
5
5
  const psl = require('psl');
6
6
  const { formatAuthHeaderRow, getAlignment } = require('../tools');
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { Transform, PassThrough } = require('stream');
3
+ const { Buffer } = require('node:buffer');
4
+ const { Transform, PassThrough } = require('node:stream');
4
5
 
5
6
  class GathererStream extends Transform {
6
7
  constructor(opts) {
package/lib/mailauth.js CHANGED
@@ -9,7 +9,7 @@ const { validateSvg: validateBimiSvg } = require('./bimi/validate-svg');
9
9
  const { parseReceived } = require('./parse-received');
10
10
  const { sealMessage } = require('./arc');
11
11
  const libmime = require('libmime');
12
- const os = require('os');
12
+ const os = require('node:os');
13
13
  const { isIP } = require('net');
14
14
 
15
15
  /**
package/lib/mta-sts.js CHANGED
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ const { Buffer } = require('node:buffer');
3
4
  const punycode = require('punycode/');
4
- const dns = require('dns');
5
+ const dns = require('node:dns');
5
6
  const parseDkimHeaders = require('./parse-dkim-headers');
6
- const https = require('https');
7
+ const https = require('node:https');
7
8
 
8
9
  const HTTP_REQUEST_TIMEOUT = 15 * 1000;
9
10
 
package/lib/spf/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const { spfVerify } = require('./spf-verify');
4
- const os = require('os');
5
- const dns = require('dns');
4
+ const os = require('node:os');
5
+ const dns = require('node:dns');
6
6
  const libmime = require('libmime');
7
7
  const Joi = require('joi');
8
8
  const domainSchema = Joi.string().domain({ allowUnicode: false, tlds: false });
package/lib/spf/macro.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const net = require('net');
4
4
  const ipaddr = require('ipaddr.js');
5
- const os = require('os');
5
+ const os = require('node:os');
6
6
 
7
7
  /**
8
8
  * Renders macro into an output string.
@@ -3,7 +3,7 @@
3
3
  const punycode = require('punycode/');
4
4
  const net = require('net');
5
5
  const macro = require('./macro');
6
- const dns = require('dns').promises;
6
+ const dns = require('node:dns').promises;
7
7
  const ipaddr = require('ipaddr.js');
8
8
  const { getPtrHostname, formatDomain } = require('../tools');
9
9
 
package/lib/tools.js CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  'use strict';
4
4
 
5
+ const { Buffer } = require('node:buffer');
5
6
  const punycode = require('punycode/');
6
7
  const libmime = require('libmime');
7
- const dns = require('dns').promises;
8
- const crypto = require('crypto');
9
- const https = require('https');
8
+ const dns = require('node:dns').promises;
9
+ const crypto = require('node:crypto');
10
+ const https = require('node:https');
10
11
  const packageData = require('../package');
11
12
  const parseDkimHeaders = require('./parse-dkim-headers');
12
13
  const psl = require('psl');
package/man/mailauth.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "MAILAUTH" "1" "February 2024" "v4.6.4" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "May 2024" "v4.6.6" "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.6.4",
3
+ "version": "4.6.6",
4
4
  "description": "Email authentication library for Node.js",
5
5
  "main": "lib/mailauth.js",
6
6
  "scripts": {
@@ -42,20 +42,19 @@
42
42
  "marked": "0.7.0",
43
43
  "marked-man": "0.7.0",
44
44
  "mbox-reader": "1.1.5",
45
- "mocha": "10.2.0",
46
- "npm-check-updates": "16.14.14",
47
- "pkg": "5.8.1"
45
+ "mocha": "10.4.0",
46
+ "npm-check-updates": "16.14.20"
48
47
  },
49
48
  "dependencies": {
50
49
  "@postalsys/vmc": "1.0.8",
51
- "fast-xml-parser": "4.3.4",
52
- "ipaddr.js": "2.1.0",
53
- "joi": "17.12.1",
54
- "libmime": "5.2.1",
55
- "nodemailer": "6.9.9",
50
+ "fast-xml-parser": "4.3.6",
51
+ "ipaddr.js": "2.2.0",
52
+ "joi": "17.13.1",
53
+ "libmime": "5.3.5",
54
+ "nodemailer": "6.9.13",
56
55
  "psl": "1.9.0",
57
56
  "punycode": "2.3.1",
58
- "undici": "5.28.2",
57
+ "undici": "5.28.4",
59
58
  "uuid": "9.0.1",
60
59
  "yargs": "17.7.2"
61
60
  },