mailauth 3.0.0 → 3.0.1

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/bin/mailauth.js CHANGED
@@ -294,18 +294,25 @@ const argv = yargs(hideBin(process.argv))
294
294
  ['vmc'],
295
295
  'Validate VMC logo',
296
296
  yargs => {
297
- yargs.option('authorityFile', {
298
- alias: 'f',
299
- type: 'string',
300
- description: 'Path to a VMC file',
301
- demandOption: false
302
- });
303
- yargs.option('authority', {
304
- alias: 'a',
305
- type: 'string',
306
- description: 'URL to a VMC file',
307
- demandOption: false
308
- });
297
+ yargs
298
+ .option('authorityPath', {
299
+ alias: 'p',
300
+ type: 'string',
301
+ description: 'Path to a VMC file',
302
+ demandOption: false
303
+ })
304
+ .option('authority', {
305
+ alias: 'a',
306
+ type: 'string',
307
+ description: 'URL to a VMC file',
308
+ demandOption: false
309
+ })
310
+ .option('domain', {
311
+ alias: 'd',
312
+ type: 'string',
313
+ description: 'Sending domain to validate',
314
+ demandOption: false
315
+ });
309
316
  },
310
317
  argv => {
311
318
  commandVmc(argv)
package/cli.md CHANGED
@@ -1,3 +1,7 @@
1
+ ![](https://github.com/postalsys/mailauth/raw/master/assets/mailauth.png)
2
+
3
+ Command line utility and a [Node.js library](README.md) for email authentication.
4
+
1
5
  # CLI USAGE
2
6
 
3
7
  ## TOC
@@ -224,20 +228,22 @@ Where
224
228
  **Options**
225
229
 
226
230
  - `--authority <url>` or `-a <url>` is the URL for the VMC resource
227
- - `--authorityFile <path>` or `-f <path>` is the cached file for the authority URL to avoid network requests
231
+ - `--authorityPath <path>` or `-p <path>` is the cached file for the authority URL to avoid network requests
232
+ - `--domain <domain>` or `-d <domain>` is the sender domain to compare the certificate against
228
233
 
229
234
  **Example**
230
235
 
231
236
  ```
232
- $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem
237
+ $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem -d cnn.com
233
238
  {
234
239
  "url": "https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem",
235
240
  "success": true,
241
+ "domainVerified": true,
236
242
  "vmc": {
237
243
  "mediaType": "image/svg+xml",
238
244
  "hashAlgo": "sha1",
239
245
  "hashValue": "ea8c81da633c66a16262134a78576cdf067638e9",
240
- "logoFile": "PD94bWwgdmVyc...",
246
+ "logoFile": "<2300B base64 encoded file>",
241
247
  "validHash": true,
242
248
  "certificate": {
243
249
  "subjectAltName": [
@@ -272,17 +278,17 @@ $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.
272
278
  If the certificate verification fails, then the contents are not returned.
273
279
 
274
280
  ```
275
- $ mailauth vmc -f /path/to/random/cert-bundle.pem
281
+ $ mailauth vmc -p /path/to/random/cert-bundle.pem
276
282
  {
277
283
  "success": false,
278
284
  "error": {
279
285
  "message": "Self signed certificate in certificate chain",
280
286
  "details": {
281
- "subject": "CN=catchall.delivery",
282
- "fingerprint": "35:EF:C9:9A:52:D5:A9:94:00:68:C6:D4:17:F1:26:61:01:0F:70:6D",
283
- "fingerprint235": "09:AB:0F:6B:F5:4F:16:58:F8:94:80:DE:E2:1A:D1:47:CC:64:F2:BF:63:E7:73:E4:02:F9:D3:C3:F6:9E:CC:86",
284
- "validFrom": "Jul 6 23:10:49 2022 GMT",
285
- "validTo": "Oct 4 23:10:48 2022 GMT"
287
+ "subject": "CN=postal.vmc.local\nO=Postal Systems OU.\nC=EE",
288
+ "fingerprint": "CC:49:83:ED:3F:6B:77:45:5B:A5:3B:9E:EC:99:0E:A1:EF:D7:FF:97",
289
+ "fingerprint235": "D4:36:6F:B4:EF:2B:4F:9E:84:23:3D:F2:3A:F7:13:21:C6:C3:CF:CB:03:5F:BB:54:5B:69:A4:AC:6A:43:61:7D",
290
+ "validFrom": "Jul 9 06:13:33 2022 GMT",
291
+ "validTo": "Jul 9 06:13:33 2023 GMT"
286
292
  },
287
293
  "code": "SELF_SIGNED_CERT_IN_CHAIN"
288
294
  }
package/lib/bimi/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const crypto = require('crypto');
4
4
  const dns = require('dns');
5
- const { formatAuthHeaderRow, parseDkimHeaders } = require('../tools');
5
+ const { formatAuthHeaderRow, parseDkimHeaders, formatDomain, getAlignment } = require('../tools');
6
6
  const Joi = require('joi');
7
7
  const packageData = require('../../package.json');
8
8
  const httpsSchema = Joi.string().uri({
@@ -244,26 +244,30 @@ const validateVMC = async bimiData => {
244
244
  return false;
245
245
  }
246
246
 
247
+ let selector = bimiData?.status?.header?.selector;
248
+ let d = bimiData?.status?.header?.d;
249
+
247
250
  let promises = [];
248
251
 
249
- promises.push(downloadPromise(bimiData.location, bimiData.locationFile));
250
- promises.push(downloadPromise(bimiData.authority, bimiData.authorityFile));
252
+ promises.push(downloadPromise(bimiData.location, bimiData.locationPath));
253
+ promises.push(downloadPromise(bimiData.authority, bimiData.authorityPath));
251
254
 
252
255
  if (!promises.length) {
253
256
  return false;
254
257
  }
255
258
 
256
- let results = await Promise.allSettled(promises);
259
+ let [{ reason: locationError, value: locationValue, status: locationStatus }, { reason: authorityError, value: authorityValue, status: authorityStatus }] =
260
+ await Promise.allSettled(promises);
257
261
 
258
262
  let result = {};
259
- if (results[0].value || results[0].reason) {
263
+ if (locationValue || locationError) {
260
264
  result.location = {
261
265
  url: bimiData.location,
262
- success: results[0].status === 'fulfilled'
266
+ success: locationStatus === 'fulfilled'
263
267
  };
264
268
 
265
- if (results[0].reason) {
266
- let err = results[0].reason;
269
+ if (locationError) {
270
+ let err = locationError;
267
271
  result.location.error = { message: err.message };
268
272
  if (err.redirect) {
269
273
  result.location.error.redirect = err.redirect;
@@ -274,18 +278,18 @@ const validateVMC = async bimiData => {
274
278
  }
275
279
 
276
280
  if (result.location.success) {
277
- result.location.logoFile = results[0].value.toString('base64');
281
+ result.location.logoFile = locationValue.toString('base64');
278
282
  }
279
283
  }
280
284
 
281
- if (results[1].value || results[1].reason) {
285
+ if (authorityValue || authorityError) {
282
286
  result.authority = {
283
287
  url: bimiData.authority,
284
- success: results[1].status === 'fulfilled'
288
+ success: authorityStatus === 'fulfilled'
285
289
  };
286
290
 
287
- if (results[1].reason) {
288
- let err = results[1].reason;
291
+ if (authorityError) {
292
+ let err = authorityError;
289
293
  result.authority.error = { message: err.message };
290
294
  if (err.redirect) {
291
295
  result.authority.error.redirect = err.redirect;
@@ -295,9 +299,57 @@ const validateVMC = async bimiData => {
295
299
  }
296
300
  }
297
301
 
298
- if (results[1].value) {
302
+ if (authorityValue) {
299
303
  try {
300
- result.authority.vmc = await vmc(results[1].value);
304
+ let vmcData = await vmc(authorityValue);
305
+
306
+ if (vmcData?.mediaType?.toLowerCase() !== 'image/svg+xml') {
307
+ let error = new Error('Invalid media type for the logo file');
308
+ error.details = {
309
+ mediaType: vmcData.mediaType
310
+ };
311
+ error.code = 'INVALID_MEDIATYPE';
312
+ throw error;
313
+ }
314
+
315
+ if (d) {
316
+ // validate domain
317
+ let selectorSet = [];
318
+ let domainSet = [];
319
+ vmcData?.certificate?.subjectAltName?.map(formatDomain)?.forEach(domain => {
320
+ if (/\b_bimi\./.test(domain)) {
321
+ selectorSet.push(domain);
322
+ } else {
323
+ domainSet.push(domain);
324
+ }
325
+ });
326
+
327
+ let domainVerified = false;
328
+
329
+ if (selector && selectorSet.includes(formatDomain(`${selector}._bimi.${d}`))) {
330
+ domainVerified = true;
331
+ } else {
332
+ let alignedDomain = getAlignment(d, domainSet, false);
333
+ if (alignedDomain) {
334
+ domainVerified = true;
335
+ }
336
+ }
337
+
338
+ if (!domainVerified) {
339
+ let error = new Error('Domain can not be verified');
340
+ error.details = {
341
+ subjectAltName: vmcData?.certificate?.subjectAltName,
342
+ selector,
343
+ d
344
+ };
345
+ error.code = 'VMC_DOMAIN_MISMATCH';
346
+ throw error;
347
+ } else {
348
+ result.authority.domainVerified = true;
349
+ }
350
+ }
351
+
352
+ result.authority.vmc = vmcData;
301
353
  } catch (err) {
302
354
  result.authority.success = false;
303
355
  result.authority.error = { message: err.message };
@@ -313,7 +365,7 @@ const validateVMC = async bimiData => {
313
365
  if (result.location && result.location.success && result.authority.success) {
314
366
  try {
315
367
  if (result.location.success && result.authority.vmc.hashAlgo && result.authority.vmc.validHash) {
316
- let hash = crypto.createHash(result.authority.vmc.hashAlgo).update(results[0].value).digest('hex');
368
+ let hash = crypto.createHash(result.authority.vmc.hashAlgo).update(locationValue).digest('hex');
317
369
  result.location.hashAlgo = result.authority.vmc.hashAlgo;
318
370
  result.location.hashValue = hash;
319
371
  result.authority.hashMatch = hash === result.authority.vmc.hashValue;
@@ -6,13 +6,18 @@ const fs = require('fs').promises;
6
6
 
7
7
  const cmd = async argv => {
8
8
  let bimiData = {};
9
- if (argv.authorityFile) {
10
- bimiData.authorityFile = await fs.readFile(argv.authorityFile);
9
+ if (argv.authorityPath) {
10
+ bimiData.authorityPath = await fs.readFile(argv.authorityPath);
11
11
  }
12
+
12
13
  if (argv.authority) {
13
14
  bimiData.authority = argv.authority;
14
15
  }
15
16
 
17
+ if (argv.domain) {
18
+ bimiData.status = { header: { d: argv.domain } };
19
+ }
20
+
16
21
  const result = await validateVMC(bimiData);
17
22
  process.stdout.write(JSON.stringify(result.authority, false, 2) + '\n');
18
23
  };
package/lib/tools.js CHANGED
@@ -490,5 +490,6 @@ module.exports = {
490
490
 
491
491
  getAlignment,
492
492
 
493
- formatRelaxedLine
493
+ formatRelaxedLine,
494
+ formatDomain
494
495
  };
package/licenses.txt CHANGED
@@ -1,11 +1,12 @@
1
- name license type link installed version author
2
- ---- ------------ ---- ----------------- ------
3
- @fidm/x509 MIT git+ssh://git@github.com/fidm/x509.git 1.2.1
4
- ipaddr.js MIT git://github.com/whitequark/ipaddr.js.git 2.0.1 whitequark whitequark@whitequark.org
5
- joi BSD-3-Clause git://github.com/sideway/joi.git 17.6.0
6
- libmime MIT git://github.com/andris9/libmime.git 5.1.0 Andris Reinman andris@kreata.ee
7
- 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/
8
- nodemailer MIT git+https://github.com/nodemailer/nodemailer.git 6.7.5 Andris Reinman
9
- psl MIT git+ssh://git@github.com/lupomontero/psl.git 1.8.0 Lupo Montero lupomontero@gmail.com https://lupomontero.com/
10
- punycode MIT git+https://github.com/bestiejs/punycode.js.git 2.1.1 Mathias Bynens https://mathiasbynens.be/
11
- yargs MIT git+https://github.com/yargs/yargs.git 17.5.1
1
+ name license type link installed version author
2
+ ---- ------------ ---- ----------------- ------
3
+ @fidm/x509 MIT git+ssh://git@github.com/fidm/x509.git 1.2.1
4
+ @postalsys/vmc MIT https://registry.npmjs.org/@postalsys/vmc/-/vmc-1.0.1.tgz 1.0.1 Postal Systems OÜ
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
7
+ libmime MIT git://github.com/andris9/libmime.git 5.1.0 Andris Reinman andris@kreata.ee
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
10
+ psl MIT git+ssh://git@github.com/lupomontero/psl.git 1.9.0 Lupo Montero lupomontero@gmail.com https://lupomontero.com/
11
+ punycode MIT git+https://github.com/bestiejs/punycode.js.git 2.1.1 Mathias Bynens https://mathiasbynens.be/
12
+ yargs MIT git+https://github.com/yargs/yargs.git 17.5.1
package/man/mailauth.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "MAILAUTH" "1" "July 2022" "v2.3.4" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "July 2022" "v3.0.0" "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": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Email authentication library for Node.js",
5
5
  "main": "lib/mailauth.js",
6
6
  "scripts": {
@@ -45,8 +45,7 @@
45
45
  "pkg": "5.7.0"
46
46
  },
47
47
  "dependencies": {
48
- "@fidm/x509": "1.2.1",
49
- "@postalsys/vmc": "1.0.1",
48
+ "@postalsys/vmc": "1.0.3",
50
49
  "ipaddr.js": "2.0.1",
51
50
  "joi": "17.6.0",
52
51
  "libmime": "5.1.0",