mailauth 2.2.3 → 2.3.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/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2020-2021 Postal Systems OÜ
1
+ Copyright (c) 2020-2022 Postal Systems OÜ
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  - **BIMI** resolving
14
14
  - **MTA-STS** helpers
15
15
 
16
- Pure JavaScript implementation, no external applications or compilation needed. 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 14+ installed.
17
17
 
18
18
  ## Command line usage
19
19
 
@@ -23,7 +23,7 @@ See [command line documentation](cli.md) for the `mailauth` command.
23
23
 
24
24
  ## Authentication
25
25
 
26
- Validate DKIM signatures, SPF, DMARC, ARC and BIMI for an email.
26
+ Validate DKIM signatures, SPF, DMARC, ARC, and BIMI for an email.
27
27
 
28
28
  ```js
29
29
  await authenticate(message [,options]) ->
@@ -32,23 +32,23 @@ await authenticate(message [,options]) ->
32
32
 
33
33
  Where
34
34
 
35
- - **message** is either a String, a Buffer or a Readable stream that represents an email message
35
+ - **message** is either a String, a Buffer, or a Readable stream that represents an email message
36
36
  - **options** (_object_) is an optional options object
37
- - **sender** (_string_) is the email address from MAIL FROM command. If not set then it is parsed from the `Return-Path` header
38
- - **ip** (_string_) is the IP of remote client that sent this message
37
+ - **sender** (_string_) is the email address from MAIL FROM command. If not set, then it is parsed from the `Return-Path` header
38
+ - **ip** (_string_) is the IP of the remote client that sent this message
39
39
  - **helo** (_string_) is the hostname value from HELO/EHLO command
40
- - **trustReceived** (_boolean_) if true then parses values for `ip` and `helo` from the latest `Received` header if you have not set these values yourself. Defaults to `false`
40
+ - **trustReceived** (_boolean_) if true, then parses values for `ip` and `helo` from the latest `Received` header if you have not set these values yourself. Defaults to `false`.
41
41
  - **mta** (_string_) is the hostname of the server performing the authentication (defaults to `os.hostname()`)
42
- - **minBitLength** (_number_) is the minimum allowed bits of RSA public keys (defaults to 1024). If a DKIM or ARC key has less bits, then validation is considered as failed
42
+ - **minBitLength** (_number_) is the minimum allowed bits of RSA public keys (defaults to 1024). If a DKIM or ARC key has fewer bits, then validation is considered as failed
43
43
  - **disableArc** (_boolean_) if true then skip ARC checks
44
- - **disableDmarc** (_boolean_) if true then skip DMARC checks. This also disables checks that are dependent on DMARC (eg. BIMI)
45
- - **disableBimi** (_boolean_) if true then skip BIMI checks
44
+ - **disableDmarc** (_boolean_) if true then skip DMARC checks. It also disables checks that are dependent on DMARC (e.g., BIMI)
45
+ - **disableBimi** (_boolean_) if true, then skip BIMI checks
46
46
  - **seal** (_object_) if set and message does not have a broken ARC chain, then seals the message using these values
47
47
  - **signingDomain** (_string_) ARC key domain name
48
48
  - **selector** (_string_) ARC key selector
49
- - **privateKey** (_string_ or _buffer_) Private key for signing. Can be a RSA or an Ed25519 key
49
+ - **privateKey** (_string_ or _buffer_) Private key for signing. Either an RSA or an Ed25519 key
50
50
  - **resolver** (_async function_) is an optional async function for DNS requests. Defaults to [dns.promises.resolve](https://nodejs.org/api/dns.html#dns_dnspromises_resolve_hostname_rrtype)
51
- - **maxResolveCount** (_number_ defaults to _50_) is the DNS lookup limit for SPF. [RFC7208](https://datatracker.ietf.org/doc/html/rfc7208#section-4.6.4) requires this limit to be 10, mailauth is less strict and defaults to 50.
51
+ - **maxResolveCount** (_number_ defaults to _50_) is the DNS lookup limit for SPF. [RFC7208](https://datatracker.ietf.org/doc/html/rfc7208#section-4.6.4) requires this limit to be 10. Mailauth is less strict and defaults to 50.
52
52
 
53
53
  **Example**
54
54
 
@@ -90,11 +90,11 @@ Authentication-Results: mx.ethereal.email;
90
90
  From: ...
91
91
  ```
92
92
 
93
- You can see full output (structured data for DKIM, SPF, DMARC and ARC) from [this example](https://gist.github.com/andris9/6514b5e7c59154a5b08636f99052ce37).
93
+ You can see the full output (structured data for DKIM, SPF, DMARC, and ARC) from [this example](https://gist.github.com/andris9/6514b5e7c59154a5b08636f99052ce37).
94
94
 
95
95
  ### receivedChain
96
96
 
97
- `receivedChain` property is an array of parsed representations of the `Received:` headers
97
+ `receivedChain` property is an array of parsed representations of the `Received:` headers.
98
98
 
99
99
  ## DKIM
100
100
 
@@ -132,7 +132,7 @@ const signResult = await dkimSign(
132
132
  // Optional signature specifc canonicalization, overrides whatever was set in parent object
133
133
  canonicalization: 'relaxed/relaxed' // c=
134
134
 
135
- // Maximum number of canonicalizated body bytes to sign (eg. the "l=" tag).
135
+ // Maximum number of canonicalized body bytes to sign (eg. the "l=" tag).
136
136
  // Do not use though. This is available only for compatibility testing.
137
137
  // maxBodyLength: 12345
138
138
  }
@@ -216,7 +216,7 @@ const { arc } = await authenticate(
216
216
  console.log(arc);
217
217
  ```
218
218
 
219
- Output being something like this:
219
+ The output is something like this:
220
220
 
221
221
  ```
222
222
  {
@@ -233,7 +233,7 @@ Output being something like this:
233
233
 
234
234
  #### During authentication
235
235
 
236
- You can seal messages with ARC automatically in the authentication step by providing the sealing key. In this case you can not modify the message anymore as this would break the seal.
236
+ You can seal messages with ARC automatically in the authentication step by providing the sealing key. In this case, you can not modify the message any more as this would break the seal.
237
237
 
238
238
  ```js
239
239
  const { authenticate } = require('mailauth');
@@ -258,7 +258,7 @@ process.stdout.write(message);
258
258
 
259
259
  #### After modifications
260
260
 
261
- If you want to modify the message before sealing then you have to authenticate the message first and then use authentication results as input for the sealing step.
261
+ If you want to modify the message before sealing, you have to authenticate the message first and then use authentication results as input for the sealing step.
262
262
 
263
263
  ```js
264
264
  const { authenticate, sealMessage } = require('@postalsys/mailauth');
@@ -297,7 +297,7 @@ process.stdout.write(message);
297
297
 
298
298
  Brand Indicators for Message Identification (BIMI) support is based on [draft-blank-ietf-bimi-01](https://tools.ietf.org/html/draft-blank-ietf-bimi-01).
299
299
 
300
- BIMI information is resolved in the authentication step and the results can be found from the `bimi` property. Message must pass DMARC validation in order to be processed for BIMI. DMARC policy can not be "none" for BIMI to pass.
300
+ BIMI information is resolved in the authentication step, and the results can be found from the `bimi` property. The message must pass DMARC validation to be processed for BIMI. DMARC policy can not be "none" for BIMI to pass.
301
301
 
302
302
  ```js
303
303
  const { bimi } = await authenticate(
@@ -314,7 +314,7 @@ if (bimi?.location) {
314
314
  }
315
315
  ```
316
316
 
317
- `BIMI-Location` header is ignored by `mailauth`, it is not checked for and it is not modified in any way if it is present. `BIMI-Selector` is used for selector selection (if available).
317
+ `BIMI-Location` header is ignored by `mailauth`, it is not checked for, and it is not modified in any way if it is present. `BIMI-Selector` is used for selector selection (if available).
318
318
 
319
319
  ### Verified Mark Certificate
320
320
 
@@ -327,14 +327,14 @@ Some example authority evidence documents:
327
327
  - [from default.\_bimi.cnn.com](https://amplify.valimail.com/bimi/time-warner/LysAFUdG-Hw-cnn_vmc.pem)
328
328
  - [from default.\_bimi.entrustdatacard.com](https://www.entrustdatacard.com/-/media/certificate/Entrust%20VMC%20July%2014%202020.pem)
329
329
 
330
- You can parse logos from these certificate files by using the `parseLogoFromX509` function
330
+ You can parse logos from these certificate files using the `parseLogoFromX509` function.
331
331
 
332
332
  ```js
333
333
  const { parseLogoFromX509 } = require('mailauth/lib/tools');
334
334
  let { altnNames, svg } = await parseLogoFromX509(fs.readFileSync('vmc.pem'));
335
335
  ```
336
336
 
337
- > **NB!** `parseLogoFromX509` does not verify the validity of the VMC certificate. It could be self signed or expired and still be processed.
337
+ > **NB!** `parseLogoFromX509` does not verify the validity of the VMC certificate. It could be self-signed or expired and still be processed.
338
338
 
339
339
  ## MTA-STS
340
340
 
@@ -372,10 +372,10 @@ async getPolicy(domain [,knownPolicy]) -> {policy, status}
372
372
 
373
373
  Where
374
374
 
375
- - **domain** is the domain to check for (eg. "gmail.com")
376
- - **knownPolicy** (optional) is the policy object from last check for this domain. This is used to check if the policy is still valid or it was updated.
375
+ - **domain** is the domain to check for (e.g. "gmail.com")
376
+ - **knownPolicy** (optional) is the policy object from the last check for this domain. This is used to check if the policy is still valid or it was updated.
377
377
 
378
- Function returns an object with the following properties:
378
+ The function returns an object with the following properties:
379
379
 
380
380
  - **policy** (object)
381
381
  - **id** (string or `false`) ID of the policy
@@ -387,11 +387,11 @@ Function returns an object with the following properties:
387
387
  - _"cached"_ no changes detected, current policy is still valid and can be used
388
388
  - _"found"_ new or updated policy was found. Cache this in your system until _policy.expires_
389
389
  - _"renew"_ existing policy is still valid, renew cached version until _policy.expires_
390
- - _"errored"_ policy discovery failed for some temporary error (eg. failing DNS queries). See _policy.error_ for details
390
+ - _"errored"_ policy discovery failed for some temporary error (e.g., failing DNS queries). See _policy.error_ for details
391
391
 
392
392
  ### Validate MX hostname
393
393
 
394
- Check if a resolved MX hostname is valid by MTA-STS policy or not
394
+ Check if a resolved MX hostname is valid by MTA-STS policy or not.
395
395
 
396
396
  ```
397
397
  validateMx(mx, policy) -> Boolean
@@ -402,7 +402,7 @@ Where
402
402
  - **mx** is the resolved MX hostname (eg. "gmail-smtp-in.l.google.com")
403
403
  - **policy** is the policy object returned by `getPolicy()`
404
404
 
405
- Function returns a boolean. If it is `true` then MX hostname is allowed to use.
405
+ The function returns a boolean. If it is `true`, then MX hostname is allowed to use.
406
406
 
407
407
  ## Testing
408
408
 
@@ -412,23 +412,23 @@ Function returns a boolean. If it is `true` then MX hostname is allowed to use.
412
412
 
413
413
  [OpenSPF test suite](http://www.openspf.org/Test_Suite) ([archive.org mirror](https://web.archive.org/web/20190130131432/http://www.openspf.org/Test_Suite)) with the following differences:
414
414
 
415
- - No PTR support in `mailauth`, all PTR related tests are ignored
416
- - Less strict whitespace checks (`mailauth` accepts multiple spaces between tags etc)
415
+ - No PTR support in `mailauth`. All PTR related tests are ignored
416
+ - Less strict whitespace checks (`mailauth` accepts multiple spaces between tags etc.)
417
417
  - Some macro tests are skipped (macro expansion is supported _in most parts_)
418
- - Some tests where invalid component is listed after a matching part (mailauth processes from left to right and returns on first match found)
419
- - Other than that all tests pass
418
+ - Some tests where the invalid component is listed after a matching part (mailauth processes from left to right and returns on the first match found)
419
+ - Other than that, all tests pass
420
420
 
421
421
  ### ARC test suite from ValiMail
422
422
 
423
423
  ValiMail [arc_test_suite](https://github.com/ValiMail/arc_test_suite)
424
424
 
425
- - `mailauth` is less strict on header tags and casing, for example uppercase `S=` for a selector passes in `mailauth` but fails in ValiMail.
426
- - Signing test suite is used for input only. All listed messages are signed using provided keys but signatures are not matched against reference. Instead `mailauth` validates the signatures itself and looks for the same cv= output that the ARC-Seal header in the test suite has
427
- - Other than that all tests pass
425
+ - `mailauth` is less strict on header tags and casing. For example, uppercase `S=` for a selector passes in `mailauth` but fails in ValiMail.
426
+ - Signing test suite is used for input only. All listed messages are signed using provided keys, but signatures are not matched against the reference. Instead, `mailauth` validates the signatures itself and looks for the same cv= output that the ARC-Seal header in the test suite has
427
+ - Other than that, all tests pass
428
428
 
429
429
  ## Setup
430
430
 
431
- First install the module from npm:
431
+ First, install the module from npm:
432
432
 
433
433
  ```
434
434
  $ npm install mailauth
@@ -442,6 +442,6 @@ const { authenticate } = require('mailauth');
442
442
 
443
443
  ## License
444
444
 
445
- © 2020-2021 Postal Systems OÜ
445
+ © 2020-2022 Postal Systems OÜ
446
446
 
447
447
  Licensed under MIT license
package/cli.md CHANGED
@@ -17,10 +17,18 @@
17
17
  Download `mailauth` for your platform:
18
18
 
19
19
  - [MacOS](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.pkg)
20
- - [Linux](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.gz)
20
+ - [Linux](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.tar.gz)
21
21
  - [Windows](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.exe)
22
22
  - Or install from the NPM registry: `npm install -g mailauth`
23
23
 
24
+ > **NB!** Downloadable files are quite large because these are packaged Node.js applications
25
+
26
+ Alternatively you can install `mailauth` from [npm](https://npmjs.com/package/mailauth).
27
+
28
+ ```
29
+ npm install -g mailauth
30
+ ```
31
+
24
32
  ## Help
25
33
 
26
34
  ```
@@ -172,7 +172,12 @@ class DkimVerifier extends MessageParser {
172
172
  instance: ['ARC', 'AS'].includes(signatureHeader.type) ? signatureHeader.parsed?.i?.value : false
173
173
  });
174
174
 
175
- let publicKey, rr;
175
+ let signingHeaders = {
176
+ keys: signingHeaderLines.keys,
177
+ headers: signingHeaderLines.headers.map(l => l.line.toString())
178
+ };
179
+
180
+ let publicKey, rr, modulusLength;
176
181
  let status = {
177
182
  result: 'neutral',
178
183
  comment: false,
@@ -208,6 +213,7 @@ class DkimVerifier extends MessageParser {
208
213
 
209
214
  publicKey = res?.publicKey;
210
215
  rr = res?.rr;
216
+ modulusLength = res?.modulusLength;
211
217
 
212
218
  try {
213
219
  status.result = crypto.verify(
@@ -283,6 +289,7 @@ class DkimVerifier extends MessageParser {
283
289
  format: signatureHeader.parsed?.c?.value,
284
290
  bodyHash,
285
291
  bodyHashExpecting: signatureHeader.parsed?.bh?.value,
292
+ signingHeaders,
286
293
  status
287
294
  };
288
295
 
@@ -298,6 +305,10 @@ class DkimVerifier extends MessageParser {
298
305
  result.publicKey = publicKey.toString();
299
306
  }
300
307
 
308
+ if (modulusLength) {
309
+ result.modulusLength = modulusLength;
310
+ }
311
+
301
312
  if (rr) {
302
313
  result.rr = rr;
303
314
  }
@@ -15,7 +15,7 @@ const matchIp = (addr, range) => {
15
15
  }
16
16
  };
17
17
 
18
- const parseCidrValue = (val, defaultValue) => {
18
+ const parseCidrValue = (val, defaultValue, type) => {
19
19
  val = val || '';
20
20
  let domain = '';
21
21
  let cidr4 = '';
@@ -29,8 +29,15 @@ const parseCidrValue = (val, defaultValue) => {
29
29
  throw err;
30
30
  }
31
31
  domain = cidrMatch[1] || '';
32
+
32
33
  cidr4 = cidrMatch[2] ? Number(cidrMatch[2].substr(1)) : '';
33
34
  cidr6 = cidrMatch[3] ? Number(cidrMatch[3].substr(2)) : '';
35
+
36
+ if (type === 'ip6' && cidr4 && !cidr6) {
37
+ // there is no dual cidr for IP addresses
38
+ cidr6 = cidr4;
39
+ cidr4 = '';
40
+ }
34
41
  }
35
42
 
36
43
  domain = domain.toLowerCase().trim() || defaultValue;
@@ -270,7 +277,7 @@ const spfVerify = async (domain, opts) => {
270
277
  case 'ip4':
271
278
  case 'ip6':
272
279
  {
273
- let { domain: range, cidr4, cidr6 } = parseCidrValue(val);
280
+ let { domain: range, cidr4, cidr6 } = parseCidrValue(val, false, type);
274
281
  if (!range) {
275
282
  let err = new Error('SPF failure');
276
283
  err.spfResult = { error: 'permerror', text: `bare IP address` };
@@ -315,7 +322,7 @@ const spfVerify = async (domain, opts) => {
315
322
 
316
323
  case 'a':
317
324
  {
318
- let { domain: a, cidr4, cidr6 } = parseCidrValue(val, domain);
325
+ let { domain: a, cidr4, cidr6 } = parseCidrValue(val, domain, type);
319
326
  let cidr = net.isIPv6(opts.ip) ? cidr6 : cidr4;
320
327
 
321
328
  a = macro(a, opts);
@@ -339,7 +346,7 @@ const spfVerify = async (domain, opts) => {
339
346
 
340
347
  case 'mx':
341
348
  {
342
- let { domain: mxDomain, cidr4, cidr6 } = parseCidrValue(val, domain);
349
+ let { domain: mxDomain, cidr4, cidr6 } = parseCidrValue(val, domain, type);
343
350
  let cidr = net.isIPv6(opts.ip) ? cidr6 : cidr4;
344
351
 
345
352
  try {
package/lib/tools.js CHANGED
@@ -6,7 +6,6 @@ const punycode = require('punycode/');
6
6
  const libmime = require('libmime');
7
7
  const dns = require('dns').promises;
8
8
  const crypto = require('crypto');
9
- const pki = require('node-forge').pki;
10
9
  const https = require('https');
11
10
  const packageData = require('../package');
12
11
  const parseDkimHeaders = require('./parse-dkim-headers');
@@ -15,6 +14,9 @@ const { Certificate } = require('@fidm/x509');
15
14
  const zlib = require('zlib');
16
15
  const util = require('util');
17
16
  const gunzip = util.promisify(zlib.gunzip);
17
+ const pki = require('node-forge').pki;
18
+ const Joi = require('joi');
19
+ const base64Schema = Joi.string().base64({ paddingRequired: false });
18
20
 
19
21
  const defaultDKIMFieldNames =
20
22
  'From:Sender:Reply-To:Subject:Date:Message-ID:To:' +
@@ -247,14 +249,23 @@ const getPublicKey = async (type, name, minBitLength, resolver) => {
247
249
  // prefix value for parsing as there is no default value
248
250
  let entry = parseDkimHeaders(`DNS: TXT;${rr}`);
249
251
 
250
- let publicKey = entry?.parsed?.p?.value;
251
- if (!publicKey) {
252
+ const publicKeyValue = entry?.parsed?.p?.value;
253
+ if (!publicKeyValue) {
252
254
  let err = new Error('Missing key value');
253
255
  err.code = 'EINVALIDVAL';
254
256
  err.rr = rr;
255
257
  throw err;
256
258
  }
257
259
 
260
+ let validation = base64Schema.validate(publicKeyValue);
261
+ if (validation.error) {
262
+ let err = new Error('Invalid base64 format for public key');
263
+ err.code = 'EINVALIDVAL';
264
+ err.rr = rr;
265
+ err.details = validation.error;
266
+ throw err;
267
+ }
268
+
258
269
  if (type === 'DKIM' && entry?.parsed?.v && (entry?.parsed?.v?.value || '').toString().toLowerCase().trim() !== 'dkim1') {
259
270
  let err = new Error('Unknown key version');
260
271
  err.code = 'EINVALIDVER';
@@ -262,28 +273,46 @@ const getPublicKey = async (type, name, minBitLength, resolver) => {
262
273
  throw err;
263
274
  }
264
275
 
265
- publicKey = Buffer.from(`-----BEGIN PUBLIC KEY-----\n${publicKey}\n-----END PUBLIC KEY-----`);
266
- let keyType = crypto.createPublicKey({ key: publicKey, format: 'pem' }).asymmetricKeyType;
276
+ let paddingNeeded = publicKeyValue.length % 4 ? 4 - (publicKeyValue.length % 4) : 0;
277
+
278
+ const publicKeyPem = Buffer.from(
279
+ `-----BEGIN PUBLIC KEY-----\n${(publicKeyValue + '='.repeat(paddingNeeded)).replace(/.{64}/g, '$&\n')}\n-----END PUBLIC KEY-----`
280
+ );
281
+ const publicKeyObj = crypto.createPublicKey({
282
+ key: publicKeyPem,
283
+ format: 'pem'
284
+ });
285
+
286
+ let keyType = publicKeyObj.asymmetricKeyType;
267
287
 
268
288
  if (!['rsa', 'ed25519'].includes(keyType) || (entry?.parsed?.k && entry?.parsed?.k?.value?.toLowerCase() !== keyType)) {
269
- let err = new Error('Unknown key type');
289
+ let err = new Error('Unknown key type (${keyType})');
270
290
  err.code = 'EINVALIDTYPE';
271
291
  err.rr = rr;
272
292
  throw err;
273
293
  }
274
294
 
275
- if (keyType === 'rsa') {
276
- // check key length
277
- const pubKeyData = pki.publicKeyFromPem(publicKey.toString());
278
- if (pubKeyData.n.bitLength() < 1024) {
279
- let err = new Error('Key too short');
280
- err.code = 'ESHORTKEY';
281
- err.rr = rr;
282
- throw err;
283
- }
295
+ let modulusLength;
296
+ if (publicKeyObj.asymmetricKeyDetails) {
297
+ modulusLength = publicKeyObj.asymmetricKeyDetails.modulusLength;
298
+ } else {
299
+ // fall back to node-forge
300
+ const pubKeyData = pki.publicKeyFromPem(publicKeyPem.toString());
301
+ modulusLength = pubKeyData.n.bitLength();
302
+ }
303
+
304
+ if (keyType === 'rsa' && modulusLength < 1024) {
305
+ let err = new Error('RSA key too short');
306
+ err.code = 'ESHORTKEY';
307
+ err.rr = rr;
308
+ throw err;
284
309
  }
285
310
 
286
- return { publicKey, rr };
311
+ return {
312
+ publicKey: publicKeyPem,
313
+ rr,
314
+ modulusLength
315
+ };
287
316
  }
288
317
 
289
318
  let err = new Error('Missing key value');
package/man/mailauth.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "MAILAUTH" "1" "October 2021" "v2.2.3" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "April 2022" "v2.3.1" "Mailauth Help"
2
2
  .SH "NAME"
3
3
  \fBmailauth\fR
4
4
  .QP
@@ -15,7 +15,7 @@ mailauth \- authenticate, sign and seal emails
15
15
  \fBmailauth\fP \fIcommand\fR \fBhelp\fP
16
16
  .SH DESCRIPTION
17
17
  .P
18
- Mailauth is an email authentication application to validate SPF, DKIM, DMARC and ARC\. You can also sign emails with DKIM digital signatures and seal messages with ARC\.
18
+ Mailauth is an email authentication application to validate SPF, DKIM, DMARC, and ARC\. You can also sign emails with DKIM digital signatures and seal messages with ARC\.
19
19
  .SH COMMANDS
20
20
  .P
21
21
  \fBreport\fR
@@ -33,9 +33,13 @@ Authenticates an email and seals it with an ARC digital signature
33
33
  \fBspf\fR
34
34
  .br
35
35
  Authenticates SPF for an IP address and email address
36
+ .P
37
+ \fBlicense\fR
38
+ .br
39
+ Display licenses for mailauth and included modules
36
40
  .SH Website
37
41
  .P
38
- \fIhttps://github\.com/andris9/mailauth\fR
42
+ \fIhttps://github\.com/postalsys/mailauth\fR
39
43
  .SH EXAMPLES
40
44
  .P
41
45
  \fBnpm install mailauth \-g\fP
@@ -49,7 +53,7 @@ Authenticates SPF for an IP address and email address
49
53
  \fBmailauth spf \-f andris@wildduck\.email \-i 217\.146\.76\.20\fP
50
54
  .SH EMAIL ARGUMENT
51
55
  .P
52
- Email argument defines path to the email message file in EML format\. If not specified then
56
+ Email argument defines the path to the email message file in EML format\. If not specified, then
53
57
  content is read from standard input\.
54
58
  .SH OPTIONS
55
59
  .RS 0
@@ -61,19 +65,19 @@ Enable silly verbose mode
61
65
  Print application version
62
66
  .IP \(bu 2
63
67
  \fB\-\-client\-ip\fP, \fB\-i <ip>\fP
64
- Client IP used for SPF checks\. If not set then parsed from the latest Received header\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
68
+ Client IP used for SPF checks\. If not set, then parsed from the latest Received header\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
65
69
  .IP \(bu 2
66
70
  \fB\-\-mta\fP, \fB\-m <hostname>\fP
67
- Hostname of this machine, used in the Authentication\-Results header\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
71
+ The hostname of this machine, used in the \fBAuthentication\-Results\fP header\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
68
72
  .IP \(bu 2
69
73
  \fB\-\-helo\fP, \fB\-e <hostname>\fP
70
74
  Client hostname from the EHLO/HELO command, used in some specific SPF checks\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
71
75
  .IP \(bu 2
72
76
  \fB\-\-sender\fP, \fB\-f <address>\fP
73
- Email address from the \fBMAIL FROM\fP command\. If not set then the address from the latest \fIReturn\-Path\fR header is used instead\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
77
+ The email address from the \fBMAIL FROM\fP command\. If not set, the address from the latest \fIReturn\-Path\fR header is used instead\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
74
78
  .IP \(bu 2
75
79
  \fB\-\-dns\-cache\fP, \fB\-n <file>\fP
76
- Path to a JSON file with cached DNS responses\. If this file is given then no actual DNS requests are performed\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
80
+ Path to a JSON file with cached DNS responses\. If this file is given, then no actual DNS requests are performed\. Anything that is not listed returns an \fBENOTFOUND\fP error\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
77
81
  .IP \(bu 2
78
82
  \fB\-\-private\-key\fP, \fB\-k <file>\fP
79
83
  Path to a private key for signing\. Allowed key types are RSA and Ed25519 (\fBsign\fP, \fBseal\fP)
@@ -91,16 +95,16 @@ Signing algorithm\. Defaults either to \fIrsa\-sha256\fR or \fIed25519\-sha256\f
91
95
  Canonicalization algorithm\. Defaults to \fIrelaxed/relaxed\fR\|\. (\fBsign\fP)
92
96
  .IP \(bu 2
93
97
  \fB\-\-body\-length\fP, \fB\-l <number>\fP
94
- \|'Maximum length of canonicalizated body to sign\. (\fBsign\fP)
98
+ The maximum length of the canonicalized body to sign\. (\fBsign\fP)
95
99
  .IP \(bu 2
96
100
  \fB\-\-time\fP, \fB\-t <number>\fP
97
- Signing time as a unix timestamp\. (\fBsign\fP, \fBseal\fP)
101
+ Signing time as a Unix timestamp\. (\fBsign\fP, \fBseal\fP)
98
102
  .IP \(bu 2
99
103
  \fB\-\-header\-fields\fP, \fB\-h <list>\fP
100
104
  Colon separated list of header field names to sign\. (\fBsign\fP, \fBseal\fP)
101
105
  .IP \(bu 2
102
106
  \fB\-\-headers\-only\fP, \fB\-o\fP
103
- Return signing headers only\. By default the entire message is printed to console\. (\fBsign\fP, \fBseal\fP, \fBspf\fP)
107
+ Return signing headers only\. By default, the entire message is printed to the console\. (\fBsign\fP, \fBseal\fP, \fBspf\fP)
104
108
  .IP \(bu 2
105
109
  \fB\-\-max\-lookups\fP, \fB\-x\fP
106
110
  How many DNS lookups allowed for SPF validation\. Defaults to 50\. (\fBreport\fP, \fBspf\fP)
@@ -108,7 +112,7 @@ How many DNS lookups allowed for SPF validation\. Defaults to 50\. (\fBreport\fP
108
112
  .RE
109
113
  .SH DNS CACHE
110
114
  .P
111
- For cached DNS requests use the following JSON structure where main keys are domain names and subkeys are rr types\.
115
+ For cached DNS requests, use the following JSON object structure: primary keys are domain names, and subkeys are resource record types\.
112
116
  .P
113
117
  .RS 2
114
118
  .nf
@@ -125,13 +129,13 @@ For cached DNS requests use the following JSON structure where main keys are dom
125
129
  .fi
126
130
  .RE
127
131
  .P
128
- Longer TXT strings can be split into multiple strings\. Unlike in real DNS there is no length limit, so you can put the entire public key into a single string\.
132
+ You can split longer TXT strings into multiple strings\. There is no length limit, unlike in actual DNS so you can put the entire public key into a single string\.
129
133
  .SH BUGS
130
134
  .P
131
- Please report any bugs to https://github\.com/andris9/mailauth/issues\.
135
+ Please report any bugs to https://github\.com/postalsys/mailauth/issues\.
132
136
  .SH LICENSE
133
137
  .P
134
- Copyright (c) 2020, Andris Reinman (MIT)\.
138
+ Copyright (c) 2020\-2022, Postal Systems (MIT)\.
135
139
  .SH SEE ALSO
136
140
  .P
137
141
  node\.js(1)
package/man/man.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  ## DESCRIPTION
14
14
 
15
- Mailauth is an email authentication application to validate SPF, DKIM, DMARC and ARC. You can also sign emails with DKIM digital signatures and seal messages with ARC.
15
+ Mailauth is an email authentication application to validate SPF, DKIM, DMARC, and ARC. You can also sign emails with DKIM digital signatures and seal messages with ARC.
16
16
 
17
17
  ## COMMANDS
18
18
 
@@ -49,7 +49,7 @@ Display licenses for mailauth and included modules
49
49
 
50
50
  ## EMAIL ARGUMENT
51
51
 
52
- Email argument defines path to the email message file in EML format. If not specified then
52
+ Email argument defines the path to the email message file in EML format. If not specified, then
53
53
  content is read from standard input.
54
54
 
55
55
  ## OPTIONS
@@ -61,19 +61,19 @@ content is read from standard input.
61
61
  Print application version
62
62
 
63
63
  - `--client-ip`, `-i <ip>`
64
- Client IP used for SPF checks. If not set then parsed from the latest Received header. (`report`, `seal`, `spf`)
64
+ Client IP used for SPF checks. If not set, then parsed from the latest Received header. (`report`, `seal`, `spf`)
65
65
 
66
66
  - `--mta`, `-m <hostname>`
67
- Hostname of this machine, used in the Authentication-Results header. (`report`, `seal`, `spf`)
67
+ The hostname of this machine, used in the `Authentication-Results` header. (`report`, `seal`, `spf`)
68
68
 
69
69
  - `--helo`, `-e <hostname>`
70
70
  Client hostname from the EHLO/HELO command, used in some specific SPF checks. (`report`, `seal`, `spf`)
71
71
 
72
72
  - `--sender`, `-f <address>`
73
- Email address from the `MAIL FROM` command. If not set then the address from the latest _Return-Path_ header is used instead. (`report`, `seal`, `spf`)
73
+ The email address from the `MAIL FROM` command. If not set, the address from the latest _Return-Path_ header is used instead. (`report`, `seal`, `spf`)
74
74
 
75
75
  - `--dns-cache`, `-n <file>`
76
- Path to a JSON file with cached DNS responses. If this file is given then no actual DNS requests are performed. (`report`, `seal`, `spf`)
76
+ Path to a JSON file with cached DNS responses. If this file is given, then no actual DNS requests are performed. Anything that is not listed returns an `ENOTFOUND` error. (`report`, `seal`, `spf`)
77
77
 
78
78
  - `--private-key`, `-k <file>`
79
79
  Path to a private key for signing. Allowed key types are RSA and Ed25519 (`sign`, `seal`)
@@ -91,23 +91,23 @@ content is read from standard input.
91
91
  Canonicalization algorithm. Defaults to _relaxed/relaxed_. (`sign`)
92
92
 
93
93
  - `--body-length`, `-l <number>`
94
- 'Maximum length of canonicalizated body to sign. (`sign`)
94
+ The maximum length of the canonicalized body to sign. (`sign`)
95
95
 
96
96
  - `--time`, `-t <number>`
97
- Signing time as a unix timestamp. (`sign`, `seal`)
97
+ Signing time as a Unix timestamp. (`sign`, `seal`)
98
98
 
99
99
  - `--header-fields`, `-h <list>`
100
100
  Colon separated list of header field names to sign. (`sign`, `seal`)
101
101
 
102
102
  - `--headers-only`, `-o`
103
- Return signing headers only. By default the entire message is printed to console. (`sign`, `seal`, `spf`)
103
+ Return signing headers only. By default, the entire message is printed to the console. (`sign`, `seal`, `spf`)
104
104
 
105
105
  - `--max-lookups`, `-x`
106
106
  How many DNS lookups allowed for SPF validation. Defaults to 50. (`report`, `spf`)
107
107
 
108
108
  ## DNS CACHE
109
109
 
110
- For cached DNS requests use the following JSON structure where main keys are domain names and subkeys are rr types.
110
+ For cached DNS requests, use the following JSON object structure: primary keys are domain names, and subkeys are resource record types.
111
111
 
112
112
  ```
113
113
  {
@@ -122,7 +122,7 @@ For cached DNS requests use the following JSON structure where main keys are dom
122
122
  }
123
123
  ```
124
124
 
125
- Longer TXT strings can be split into multiple strings. Unlike in real DNS there is no length limit, so you can put the entire public key into a single string.
125
+ You can split longer TXT strings into multiple strings. There is no length limit, unlike in actual DNS so you can put the entire public key into a single string.
126
126
 
127
127
  ## BUGS
128
128
 
@@ -130,7 +130,7 @@ Please report any bugs to https://github.com/postalsys/mailauth/issues.
130
130
 
131
131
  ## LICENSE
132
132
 
133
- Copyright (c) 2020-2021, Postal Systems (MIT).
133
+ Copyright (c) 2020-2022, Postal Systems (MIT).
134
134
 
135
135
  ## SEE ALSO
136
136
 
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "mailauth",
3
- "version": "2.2.3",
3
+ "version": "2.3.2",
4
4
  "description": "Email authentication library for Node.js",
5
5
  "main": "lib/mailauth.js",
6
6
  "scripts": {
7
7
  "test": "eslint \"lib/**/*.js\" \"test/**/*.js\" && mocha --recursive \"./test/**/*.js\" --reporter spec",
8
8
  "prepublish": "npm run man || true",
9
9
  "man": "cd man && marked-man --version `node -e \"console.log('v'+require('../package.json').version)\"` --manual 'Mailauth Help' --section 1 man.md > mailauth.1",
10
- "build-dist": "npm run man && npm run licenses && pkg package.json",
10
+ "build-dist": "npm run man && npm run licenses && pkg --compress Brotli package.json",
11
11
  "licenses": "license-report --only=prod --output=table --config license-report-config.json > licenses.txt"
12
12
  },
13
13
  "repository": {
@@ -31,28 +31,28 @@
31
31
  },
32
32
  "homepage": "https://github.com/postalsys/mailauth",
33
33
  "devDependencies": {
34
- "chai": "4.3.4",
35
- "eslint": "8.0.0",
34
+ "chai": "4.3.6",
35
+ "eslint": "8.14.0",
36
36
  "eslint-config-nodemailer": "1.2.0",
37
- "eslint-config-prettier": "8.3.0",
37
+ "eslint-config-prettier": "8.5.0",
38
38
  "js-yaml": "4.1.0",
39
- "license-report": "4.5.0",
39
+ "license-report": "5.0.2",
40
40
  "marked": "0.7.0",
41
41
  "marked-man": "0.7.0",
42
42
  "mbox-reader": "1.1.5",
43
- "mocha": "9.1.2",
44
- "pkg": "5.3.3"
43
+ "mocha": "9.2.2",
44
+ "pkg": "5.6.0"
45
45
  },
46
46
  "dependencies": {
47
47
  "@fidm/x509": "1.2.1",
48
48
  "ipaddr.js": "2.0.1",
49
- "joi": "17.4.2",
49
+ "joi": "17.6.0",
50
50
  "libmime": "5.0.0",
51
- "node-forge": "0.10.0",
52
- "nodemailer": "6.7.0",
51
+ "node-forge": "1.3.1",
52
+ "nodemailer": "6.7.3",
53
53
  "psl": "1.8.0",
54
54
  "punycode": "2.1.1",
55
- "yargs": "17.2.1"
55
+ "yargs": "17.4.1"
56
56
  },
57
57
  "engines": {
58
58
  "node": ">=14.0.0"
@@ -60,20 +60,21 @@
60
60
  "bin": {
61
61
  "mailauth": "bin/mailauth.js"
62
62
  },
63
- "directories": {
64
- "man": "man"
65
- },
63
+ "man": [
64
+ "man/mailauth.1"
65
+ ],
66
66
  "pkg": {
67
67
  "scripts": [
68
68
  "workers/**/*.js"
69
69
  ],
70
70
  "assets": [
71
71
  "man/**/*",
72
- "licenses.txt"
72
+ "licenses.txt",
73
+ "LICENSE.txt"
73
74
  ],
74
75
  "_targets": [
75
76
  "node16-macos-x64"
76
77
  ],
77
- "outputPath": "dist"
78
+ "outputPath": "ee-dist"
78
79
  }
79
80
  }
package/licenses.txt DELETED
@@ -1,11 +0,0 @@
1
- name license type link author
2
- ---- ------------ ---- ------
3
- @fidm/x509 MIT git+ssh://git@github.com/fidm/x509.git n/a
4
- ipaddr.js MIT git://github.com/whitequark/ipaddr.js.git whitequark
5
- joi BSD-3-Clause git://github.com/sideway/joi.git n/a
6
- libmime MIT git://github.com/andris9/libmime.git Andris Reinman
7
- node-forge (BSD-3-Clause OR GPL-2.0) git+https://github.com/digitalbazaar/forge.git Digital Bazaar, Inc.
8
- nodemailer MIT git+https://github.com/nodemailer/nodemailer.git Andris Reinman
9
- psl MIT git+ssh://git@github.com/lupomontero/psl.git Lupo Montero
10
- punycode MIT git+https://github.com/bestiejs/punycode.js.git Mathias Bynens
11
- yargs MIT git+https://github.com/yargs/yargs.git n/a