mailauth 3.0.1 → 3.0.2

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/README.md CHANGED
@@ -10,10 +10,10 @@
10
10
  - ARC sealing
11
11
  - Sealing on authentication
12
12
  - Sealing after modifications
13
- - **BIMI** resolving
13
+ - **BIMI** resolving and **VMC** validation
14
14
  - **MTA-STS** helpers
15
15
 
16
- Pure JavaScript implementation, no external applications or compilation needed. It runs on any server/device that has Node 14+ installed.
16
+ Pure JavaScript implementation, no external applications or compilation needed. It runs on any server/device that has Node 16+ installed.
17
17
 
18
18
  ## Command line usage
19
19
 
package/cli.md CHANGED
@@ -26,14 +26,6 @@ Download `mailauth` for your platform:
26
26
  - [Windows](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.exe)
27
27
  - Or install from the NPM registry: `npm install -g mailauth`
28
28
 
29
- > **NB!** Downloadable files are quite large because these are packaged Node.js applications
30
-
31
- Alternatively you can install `mailauth` from [npm](https://npmjs.com/package/mailauth).
32
-
33
- ```
34
- npm install -g mailauth
35
- ```
36
-
37
29
  ## Help
38
30
 
39
31
  ```
@@ -246,9 +238,6 @@ $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.
246
238
  "logoFile": "<2300B base64 encoded file>",
247
239
  "validHash": true,
248
240
  "certificate": {
249
- "subjectAltName": [
250
- "cnn.com"
251
- ],
252
241
  "subject": {
253
242
  "businessCategory": "Private Organization",
254
243
  "jurisdictionCountryName": "US",
@@ -263,8 +252,13 @@ $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.
263
252
  "trademarkCountryOrRegionName": "US",
264
253
  "trademarkRegistration": "5817930"
265
254
  },
255
+ "subjectAltName": [
256
+ "cnn.com"
257
+ ],
266
258
  "fingerprint": "17:B3:94:97:E6:6B:C8:6B:33:B8:0A:D2:F0:79:6B:08:A2:A6:84:BD",
267
259
  "serialNumber": "0821B8FE0A9CBC3BAC10DA08C088EEF4",
260
+ "validFrom": "2021-08-12T00:00:00.000Z",
261
+ "validTo": "2022-08-12T23:59:59.000Z",
268
262
  "issuer": {
269
263
  "countryName": "US",
270
264
  "organizationName": "DigiCert, Inc.",
@@ -275,7 +269,7 @@ $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.
275
269
  }
276
270
  ```
277
271
 
278
- If the certificate verification fails, then the contents are not returned.
272
+ If the certificate verification fails, then the logo contents are not returned.
279
273
 
280
274
  ```
281
275
  $ mailauth vmc -p /path/to/random/cert-bundle.pem
@@ -284,17 +278,46 @@ $ mailauth vmc -p /path/to/random/cert-bundle.pem
284
278
  "error": {
285
279
  "message": "Self signed certificate in certificate chain",
286
280
  "details": {
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"
281
+ "certificate": {
282
+ "subject": {
283
+ "commonName": "postal.vmc.local",
284
+ "organizationName": "Postal Systems OU.",
285
+ "countryName": "EE"
286
+ },
287
+ "subjectAltName": [],
288
+ "fingerprint": "CC:49:83:ED:3F:6B:77:45:5B:A5:3B:9E:EC:99:0E:A1:EF:D7:FF:97",
289
+ "serialNumber": "B61FBFBA917B15D9",
290
+ "validFrom": "2022-07-09T06:13:33.000Z",
291
+ "validTo": "2023-07-09T06:13:33.000Z",
292
+ "issuer": {
293
+ "commonName": "postal.vmc.local",
294
+ "organizationName": "Postal Systems OU.",
295
+ "countryName": "EE"
296
+ }
297
+ }
292
298
  },
293
299
  "code": "SELF_SIGNED_CERT_IN_CHAIN"
294
300
  }
295
301
  }
296
302
  ```
297
303
 
304
+ The embedded SVG file is also validated.
305
+
306
+ ```
307
+ $ mailauth vmc -p /path/to/vmc-with-invalid-svg.pem
308
+ {
309
+ "success": false,
310
+ "error": {
311
+ "message": "VMC logo SVG validation failed",
312
+ "details": {
313
+ "message": "Not a Tiny PS profile",
314
+ "code": "INVALID_BASE_PROFILE"
315
+ },
316
+ "code": "SVG_VALIDATION_FAILED"
317
+ }
318
+ }
319
+ ```
320
+
298
321
  ### license
299
322
 
300
323
  Display licenses for `mailauth` and included modules.
package/lib/bimi/index.js CHANGED
@@ -12,6 +12,7 @@ const httpsSchema = Joi.string().uri({
12
12
  const https = require('https');
13
13
  const http = require('http');
14
14
  const { vmc } = require('@postalsys/vmc');
15
+ const { validateSvg } = require('./validate-svg');
15
16
 
16
17
  const lookup = async data => {
17
18
  let { dmarc, headers, resolver } = data;
@@ -303,6 +304,12 @@ const validateVMC = async bimiData => {
303
304
  try {
304
305
  let vmcData = await vmc(authorityValue);
305
306
 
307
+ if (!vmcData.logoFile) {
308
+ let error = new Error('VMC does not contain a log file');
309
+ error.code = 'MISSING_VMC_LOGO';
310
+ throw error;
311
+ }
312
+
306
313
  if (vmcData?.mediaType?.toLowerCase() !== 'image/svg+xml') {
307
314
  let error = new Error('Invalid media type for the logo file');
308
315
  error.details = {
@@ -312,6 +319,33 @@ const validateVMC = async bimiData => {
312
319
  throw error;
313
320
  }
314
321
 
322
+ if (!vmcData.validHash) {
323
+ let error = new Error('VMC hash does not match logo file');
324
+ error.details = {
325
+ hashAlgo: vmcData.hashAlgo,
326
+ hashValue: vmcData.hashValue,
327
+ logoFile: vmcData.logoFile
328
+ };
329
+ error.code = 'INVALID_LOGO_HASH';
330
+ throw error;
331
+ }
332
+
333
+ // throws on invalid logo file
334
+ try {
335
+ validateSvg(Buffer.from(vmcData.logoFile, 'base64'));
336
+ } catch (err) {
337
+ let error = new Error('VMC logo SVG validation failed');
338
+ error.details = Object.assign(
339
+ {
340
+ message: err.message
341
+ },
342
+ error.details || {},
343
+ err.code ? { code: err.code } : {}
344
+ );
345
+ error.code = 'SVG_VALIDATION_FAILED';
346
+ throw error;
347
+ }
348
+
315
349
  if (d) {
316
350
  // validate domain
317
351
  let selectorSet = [];
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ const { XMLParser } = require('fast-xml-parser');
4
+
5
+ function validateSvg(logo) {
6
+ const parser = new XMLParser({
7
+ ignoreAttributes: false,
8
+ attributeNamePrefix: '@_'
9
+ });
10
+
11
+ let logoObj;
12
+ try {
13
+ logoObj = parser.parse(logo);
14
+ if (!logoObj) {
15
+ throw new Error('Emtpy file');
16
+ }
17
+ } catch (err) {
18
+ let error = new Error('Invalid SVG file');
19
+ error._err = err;
20
+ error.code = 'INVALID_XML_FILE';
21
+ throw error;
22
+ }
23
+
24
+ if (!logoObj.svg) {
25
+ let error = new Error('Invalid SVG file');
26
+ error.code = 'INVALID_SVG_FILE';
27
+ throw error;
28
+ }
29
+
30
+ if (logoObj.svg['@_baseProfile'] !== 'tiny-ps') {
31
+ let error = new Error('Not a Tiny PS profile');
32
+ error.code = 'INVALID_BASE_PROFILE';
33
+ throw error;
34
+ }
35
+
36
+ if (!logoObj.svg.title) {
37
+ let error = new Error('Logo file is missing title');
38
+ error.code = 'LOGO_MISSING_TITLE';
39
+ throw error;
40
+ }
41
+
42
+ if ('@_x' in logoObj.svg || '@_y' in logoObj.svg) {
43
+ let error = new Error('Logo root includes x/y attributes');
44
+ error.code = 'LOGO_INVALID_ROOT_ATTRS';
45
+ throw error;
46
+ }
47
+
48
+ let walkElm = (node, name, path) => {
49
+ if (!node) {
50
+ return;
51
+ }
52
+ if (Array.isArray(node)) {
53
+ for (let entry of node) {
54
+ walkElm(entry, name, path + '.' + name + '[]');
55
+ }
56
+ } else if (typeof node === 'object') {
57
+ if (node['@_xlink:href'] && !/^#/.test(node['@_xlink:href'])) {
58
+ let error = new Error('External reference found from file');
59
+ error.details = {
60
+ element: name,
61
+ link: node['@_xlink:href'],
62
+ path
63
+ };
64
+ error.code = 'LOGO_INCLUDES_REFERENCE';
65
+ throw error;
66
+ }
67
+
68
+ for (let key of Object.keys(node)) {
69
+ if (['script', 'animate', 'animatemotion', 'animatetransform', 'discard', 'set'].includes(key.toLowerCase())) {
70
+ let error = new Error('Unallowed element found from file');
71
+ error.details = {
72
+ element: key,
73
+ path: path + '.' + key
74
+ };
75
+ error.code = 'LOGO_INVALID_ELEMENT';
76
+ throw error;
77
+ }
78
+
79
+ if (Array.isArray(node[key])) {
80
+ for (let entry of node[key]) {
81
+ walkElm(entry, key, path + '.' + key + '[]');
82
+ }
83
+ } else if (node[key] && typeof node[key] === 'object') {
84
+ walkElm(node[key], key, path + '.' + key);
85
+ }
86
+ }
87
+ }
88
+ };
89
+
90
+ walkElm(logoObj, 'root', '');
91
+
92
+ // all validations passed
93
+ return true;
94
+ }
95
+
96
+ module.exports = { validateSvg };
package/lib/mailauth.js CHANGED
@@ -5,6 +5,7 @@ const { spf } = require('./spf');
5
5
  const { dmarc } = require('./dmarc');
6
6
  const { arc, createSeal } = require('./arc');
7
7
  const { bimi, validateVMC: validateBimiVmc } = require('./bimi');
8
+ const { validateSvg: validateBimiSvg } = require('./bimi/validate-svg');
8
9
  const { parseReceived } = require('./parse-received');
9
10
  const { sealMessage } = require('./arc');
10
11
  const libmime = require('libmime');
@@ -180,4 +181,9 @@ const authenticate = async (input, opts) => {
180
181
  };
181
182
  };
182
183
 
183
- module.exports = { authenticate, sealMessage, validateBimiVmc };
184
+ module.exports = {
185
+ authenticate,
186
+ sealMessage,
187
+ validateBimiVmc,
188
+ validateBimiSvg
189
+ };
package/licenses.txt CHANGED
@@ -1,12 +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
- @postalsys/vmc MIT https://registry.npmjs.org/@postalsys/vmc/-/vmc-1.0.1.tgz 1.0.1 Postal Systems
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
1
+ name license type link installed version author
2
+ ---- ------------ ---- ----------------- ------
3
+ @postalsys/vmc MIT https://registry.npmjs.org/@postalsys/vmc/-/vmc-1.0.4.tgz 1.0.4 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/
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" "v3.0.0" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "July 2022" "v3.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": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Email authentication library for Node.js",
5
5
  "main": "lib/mailauth.js",
6
6
  "scripts": {
@@ -42,10 +42,11 @@
42
42
  "marked-man": "0.7.0",
43
43
  "mbox-reader": "1.1.5",
44
44
  "mocha": "10.0.0",
45
- "pkg": "5.7.0"
45
+ "pkg": "5.8.0"
46
46
  },
47
47
  "dependencies": {
48
- "@postalsys/vmc": "1.0.3",
48
+ "@postalsys/vmc": "1.0.4",
49
+ "fast-xml-parser": "4.0.9",
49
50
  "ipaddr.js": "2.0.1",
50
51
  "joi": "17.6.0",
51
52
  "libmime": "5.1.0",