mailauth 3.0.0 → 3.0.3
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 +2 -2
- package/bin/mailauth.js +19 -12
- package/cli.md +50 -21
- package/lib/bimi/index.js +102 -16
- package/lib/bimi/validate-svg.js +96 -0
- package/lib/commands/vmc.js +7 -2
- package/lib/mailauth.js +7 -1
- package/lib/tools.js +2 -1
- package/licenses.txt +12 -11
- package/man/mailauth.1 +1 -1
- package/package.json +5 -5
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
|
|
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/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
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
+

|
|
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
|
|
@@ -22,14 +26,6 @@ Download `mailauth` for your platform:
|
|
|
22
26
|
- [Windows](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.exe)
|
|
23
27
|
- Or install from the NPM registry: `npm install -g mailauth`
|
|
24
28
|
|
|
25
|
-
> **NB!** Downloadable files are quite large because these are packaged Node.js applications
|
|
26
|
-
|
|
27
|
-
Alternatively you can install `mailauth` from [npm](https://npmjs.com/package/mailauth).
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
npm install -g mailauth
|
|
31
|
-
```
|
|
32
|
-
|
|
33
29
|
## Help
|
|
34
30
|
|
|
35
31
|
```
|
|
@@ -224,25 +220,24 @@ Where
|
|
|
224
220
|
**Options**
|
|
225
221
|
|
|
226
222
|
- `--authority <url>` or `-a <url>` is the URL for the VMC resource
|
|
227
|
-
- `--
|
|
223
|
+
- `--authorityPath <path>` or `-p <path>` is the cached file for the authority URL to avoid network requests
|
|
224
|
+
- `--domain <domain>` or `-d <domain>` is the sender domain to compare the certificate against
|
|
228
225
|
|
|
229
226
|
**Example**
|
|
230
227
|
|
|
231
228
|
```
|
|
232
|
-
$ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem
|
|
229
|
+
$ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem -d cnn.com
|
|
233
230
|
{
|
|
234
231
|
"url": "https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem",
|
|
235
232
|
"success": true,
|
|
233
|
+
"domainVerified": true,
|
|
236
234
|
"vmc": {
|
|
237
235
|
"mediaType": "image/svg+xml",
|
|
238
236
|
"hashAlgo": "sha1",
|
|
239
237
|
"hashValue": "ea8c81da633c66a16262134a78576cdf067638e9",
|
|
240
|
-
"logoFile": "
|
|
238
|
+
"logoFile": "<2300B base64 encoded file>",
|
|
241
239
|
"validHash": true,
|
|
242
240
|
"certificate": {
|
|
243
|
-
"subjectAltName": [
|
|
244
|
-
"cnn.com"
|
|
245
|
-
],
|
|
246
241
|
"subject": {
|
|
247
242
|
"businessCategory": "Private Organization",
|
|
248
243
|
"jurisdictionCountryName": "US",
|
|
@@ -257,8 +252,13 @@ $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.
|
|
|
257
252
|
"trademarkCountryOrRegionName": "US",
|
|
258
253
|
"trademarkRegistration": "5817930"
|
|
259
254
|
},
|
|
255
|
+
"subjectAltName": [
|
|
256
|
+
"cnn.com"
|
|
257
|
+
],
|
|
260
258
|
"fingerprint": "17:B3:94:97:E6:6B:C8:6B:33:B8:0A:D2:F0:79:6B:08:A2:A6:84:BD",
|
|
261
259
|
"serialNumber": "0821B8FE0A9CBC3BAC10DA08C088EEF4",
|
|
260
|
+
"validFrom": "2021-08-12T00:00:00.000Z",
|
|
261
|
+
"validTo": "2022-08-12T23:59:59.000Z",
|
|
262
262
|
"issuer": {
|
|
263
263
|
"countryName": "US",
|
|
264
264
|
"organizationName": "DigiCert, Inc.",
|
|
@@ -269,26 +269,55 @@ $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.
|
|
|
269
269
|
}
|
|
270
270
|
```
|
|
271
271
|
|
|
272
|
-
If the certificate verification fails, then the contents are not returned.
|
|
272
|
+
If the certificate verification fails, then the logo contents are not returned.
|
|
273
273
|
|
|
274
274
|
```
|
|
275
|
-
$ mailauth vmc -
|
|
275
|
+
$ mailauth vmc -p /path/to/random/cert-bundle.pem
|
|
276
276
|
{
|
|
277
277
|
"success": false,
|
|
278
278
|
"error": {
|
|
279
279
|
"message": "Self signed certificate in certificate chain",
|
|
280
280
|
"details": {
|
|
281
|
-
"
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
+
}
|
|
286
298
|
},
|
|
287
299
|
"code": "SELF_SIGNED_CERT_IN_CHAIN"
|
|
288
300
|
}
|
|
289
301
|
}
|
|
290
302
|
```
|
|
291
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
|
+
|
|
292
321
|
### license
|
|
293
322
|
|
|
294
323
|
Display licenses for `mailauth` and included modules.
|
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({
|
|
@@ -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;
|
|
@@ -244,26 +245,30 @@ const validateVMC = async bimiData => {
|
|
|
244
245
|
return false;
|
|
245
246
|
}
|
|
246
247
|
|
|
248
|
+
let selector = bimiData?.status?.header?.selector;
|
|
249
|
+
let d = bimiData?.status?.header?.d;
|
|
250
|
+
|
|
247
251
|
let promises = [];
|
|
248
252
|
|
|
249
|
-
promises.push(downloadPromise(bimiData.location, bimiData.
|
|
250
|
-
promises.push(downloadPromise(bimiData.authority, bimiData.
|
|
253
|
+
promises.push(downloadPromise(bimiData.location, bimiData.locationPath));
|
|
254
|
+
promises.push(downloadPromise(bimiData.authority, bimiData.authorityPath));
|
|
251
255
|
|
|
252
256
|
if (!promises.length) {
|
|
253
257
|
return false;
|
|
254
258
|
}
|
|
255
259
|
|
|
256
|
-
let
|
|
260
|
+
let [{ reason: locationError, value: locationValue, status: locationStatus }, { reason: authorityError, value: authorityValue, status: authorityStatus }] =
|
|
261
|
+
await Promise.allSettled(promises);
|
|
257
262
|
|
|
258
263
|
let result = {};
|
|
259
|
-
if (
|
|
264
|
+
if (locationValue || locationError) {
|
|
260
265
|
result.location = {
|
|
261
266
|
url: bimiData.location,
|
|
262
|
-
success:
|
|
267
|
+
success: locationStatus === 'fulfilled'
|
|
263
268
|
};
|
|
264
269
|
|
|
265
|
-
if (
|
|
266
|
-
let err =
|
|
270
|
+
if (locationError) {
|
|
271
|
+
let err = locationError;
|
|
267
272
|
result.location.error = { message: err.message };
|
|
268
273
|
if (err.redirect) {
|
|
269
274
|
result.location.error.redirect = err.redirect;
|
|
@@ -274,18 +279,18 @@ const validateVMC = async bimiData => {
|
|
|
274
279
|
}
|
|
275
280
|
|
|
276
281
|
if (result.location.success) {
|
|
277
|
-
result.location.logoFile =
|
|
282
|
+
result.location.logoFile = locationValue.toString('base64');
|
|
278
283
|
}
|
|
279
284
|
}
|
|
280
285
|
|
|
281
|
-
if (
|
|
286
|
+
if (authorityValue || authorityError) {
|
|
282
287
|
result.authority = {
|
|
283
288
|
url: bimiData.authority,
|
|
284
|
-
success:
|
|
289
|
+
success: authorityStatus === 'fulfilled'
|
|
285
290
|
};
|
|
286
291
|
|
|
287
|
-
if (
|
|
288
|
-
let err =
|
|
292
|
+
if (authorityError) {
|
|
293
|
+
let err = authorityError;
|
|
289
294
|
result.authority.error = { message: err.message };
|
|
290
295
|
if (err.redirect) {
|
|
291
296
|
result.authority.error.redirect = err.redirect;
|
|
@@ -295,9 +300,90 @@ const validateVMC = async bimiData => {
|
|
|
295
300
|
}
|
|
296
301
|
}
|
|
297
302
|
|
|
298
|
-
if (
|
|
303
|
+
if (authorityValue) {
|
|
299
304
|
try {
|
|
300
|
-
|
|
305
|
+
let vmcData = await vmc(authorityValue);
|
|
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
|
+
|
|
313
|
+
if (vmcData?.mediaType?.toLowerCase() !== 'image/svg+xml') {
|
|
314
|
+
let error = new Error('Invalid media type for the logo file');
|
|
315
|
+
error.details = {
|
|
316
|
+
mediaType: vmcData.mediaType
|
|
317
|
+
};
|
|
318
|
+
error.code = 'INVALID_MEDIATYPE';
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
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
|
+
|
|
349
|
+
if (d) {
|
|
350
|
+
// validate domain
|
|
351
|
+
let selectorSet = [];
|
|
352
|
+
let domainSet = [];
|
|
353
|
+
vmcData?.certificate?.subjectAltName?.map(formatDomain)?.forEach(domain => {
|
|
354
|
+
if (/\b_bimi\./.test(domain)) {
|
|
355
|
+
selectorSet.push(domain);
|
|
356
|
+
} else {
|
|
357
|
+
domainSet.push(domain);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
let domainVerified = false;
|
|
362
|
+
|
|
363
|
+
if (selector && selectorSet.includes(formatDomain(`${selector}._bimi.${d}`))) {
|
|
364
|
+
domainVerified = true;
|
|
365
|
+
} else {
|
|
366
|
+
let alignedDomain = getAlignment(d, domainSet, false);
|
|
367
|
+
if (alignedDomain) {
|
|
368
|
+
domainVerified = true;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!domainVerified) {
|
|
373
|
+
let error = new Error('Domain can not be verified');
|
|
374
|
+
error.details = {
|
|
375
|
+
subjectAltName: vmcData?.certificate?.subjectAltName,
|
|
376
|
+
selector,
|
|
377
|
+
d
|
|
378
|
+
};
|
|
379
|
+
error.code = 'VMC_DOMAIN_MISMATCH';
|
|
380
|
+
throw error;
|
|
381
|
+
} else {
|
|
382
|
+
result.authority.domainVerified = true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
result.authority.vmc = vmcData;
|
|
301
387
|
} catch (err) {
|
|
302
388
|
result.authority.success = false;
|
|
303
389
|
result.authority.error = { message: err.message };
|
|
@@ -313,7 +399,7 @@ const validateVMC = async bimiData => {
|
|
|
313
399
|
if (result.location && result.location.success && result.authority.success) {
|
|
314
400
|
try {
|
|
315
401
|
if (result.location.success && result.authority.vmc.hashAlgo && result.authority.vmc.validHash) {
|
|
316
|
-
let hash = crypto.createHash(result.authority.vmc.hashAlgo).update(
|
|
402
|
+
let hash = crypto.createHash(result.authority.vmc.hashAlgo).update(locationValue).digest('hex');
|
|
317
403
|
result.location.hashAlgo = result.authority.vmc.hashAlgo;
|
|
318
404
|
result.location.hashValue = hash;
|
|
319
405
|
result.authority.hashMatch = hash === result.authority.vmc.hashValue;
|
|
@@ -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/commands/vmc.js
CHANGED
|
@@ -6,13 +6,18 @@ const fs = require('fs').promises;
|
|
|
6
6
|
|
|
7
7
|
const cmd = async argv => {
|
|
8
8
|
let bimiData = {};
|
|
9
|
-
if (argv.
|
|
10
|
-
bimiData.
|
|
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/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 = {
|
|
184
|
+
module.exports = {
|
|
185
|
+
authenticate,
|
|
186
|
+
sealMessage,
|
|
187
|
+
validateBimiVmc,
|
|
188
|
+
validateBimiSvg
|
|
189
|
+
};
|
package/lib/tools.js
CHANGED
package/licenses.txt
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
name
|
|
2
|
-
----
|
|
3
|
-
@
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailauth",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Email authentication library for Node.js",
|
|
5
5
|
"main": "lib/mailauth.js",
|
|
6
6
|
"scripts": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"homepage": "https://github.com/postalsys/mailauth",
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"chai": "4.3.6",
|
|
36
|
-
"eslint": "8.
|
|
36
|
+
"eslint": "8.20.0",
|
|
37
37
|
"eslint-config-nodemailer": "1.2.0",
|
|
38
38
|
"eslint-config-prettier": "8.5.0",
|
|
39
39
|
"js-yaml": "4.1.0",
|
|
@@ -42,11 +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.
|
|
45
|
+
"pkg": "5.8.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"
|
|
48
|
+
"@postalsys/vmc": "1.0.5",
|
|
49
|
+
"fast-xml-parser": "4.0.9",
|
|
50
50
|
"ipaddr.js": "2.0.1",
|
|
51
51
|
"joi": "17.6.0",
|
|
52
52
|
"libmime": "5.1.0",
|