mailauth 2.3.0 → 2.3.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/.gitattributes +1 -0
- package/README.md +36 -36
- package/cli.md +6 -0
- package/lib/dkim/dkim-verifier.js +2 -2
- package/lib/dmarc/verify.js +9 -4
- package/lib/mailauth.js +2 -1
- package/lib/spf/spf-verify.js +11 -4
- package/lib/tools.js +7 -3
- package/licenses.txt +9 -9
- package/man/mailauth.1 +12 -12
- package/man/man.md +11 -11
- package/package.json +22 -22
package/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.js text eol=lf
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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,10 +258,10 @@ process.stdout.write(message);
|
|
|
258
258
|
|
|
259
259
|
#### After modifications
|
|
260
260
|
|
|
261
|
-
If you want to modify the message before sealing
|
|
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
|
-
const { authenticate, sealMessage } = require('
|
|
264
|
+
const { authenticate, sealMessage } = require('mailauth');
|
|
265
265
|
|
|
266
266
|
// 1. authenticate the message
|
|
267
267
|
const { arc, headers } = await authenticate(
|
|
@@ -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.
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
package/cli.md
CHANGED
|
@@ -23,6 +23,12 @@ Download `mailauth` for your platform:
|
|
|
23
23
|
|
|
24
24
|
> **NB!** Downloadable files are quite large because these are packaged Node.js applications
|
|
25
25
|
|
|
26
|
+
Alternatively you can install `mailauth` from [npm](https://npmjs.com/package/mailauth).
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
npm install -g mailauth
|
|
30
|
+
```
|
|
31
|
+
|
|
26
32
|
## Help
|
|
27
33
|
|
|
28
34
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getSigningHeaderLines, getPublicKey, parseDkimHeaders, formatAuthHeaderRow,
|
|
3
|
+
const { getSigningHeaderLines, getPublicKey, parseDkimHeaders, formatAuthHeaderRow, getAlignment } = require('../../lib/tools');
|
|
4
4
|
const { MessageParser } = require('./message-parser');
|
|
5
5
|
const { dkimBody } = require('./body');
|
|
6
6
|
const { generateCanonicalizedHeader } = require('./header');
|
|
@@ -195,7 +195,7 @@ class DkimVerifier extends MessageParser {
|
|
|
195
195
|
};
|
|
196
196
|
|
|
197
197
|
if (signatureHeader.type === 'DKIM' && this.headerFrom?.length) {
|
|
198
|
-
status.aligned = this.headerFrom?.length ?
|
|
198
|
+
status.aligned = this.headerFrom?.length ? getAlignment(this.headerFrom[0].split('@').pop(), [signatureHeader.signingDomain]) : false;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
let bodyHash = this.bodyHashes.get(signatureHeader.bodyHashKey)?.hash;
|
package/lib/dmarc/verify.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const dns = require('dns').promises;
|
|
4
4
|
const punycode = require('punycode/');
|
|
5
5
|
const psl = require('psl');
|
|
6
|
-
const { formatAuthHeaderRow,
|
|
6
|
+
const { formatAuthHeaderRow, getAlignment } = require('../tools');
|
|
7
7
|
|
|
8
8
|
const resolveTxt = async (domain, resolver) => {
|
|
9
9
|
try {
|
|
@@ -146,8 +146,8 @@ const verifyDmarc = async opts => {
|
|
|
146
146
|
// use "sp" if this is a subdomain of an org domain and "sp" is set, otherwise use "p"
|
|
147
147
|
const policy = dmarcRecord.isOrgRecord && dmarcRecord.sp ? dmarcRecord.sp : dmarcRecord.p;
|
|
148
148
|
|
|
149
|
-
const dkimAlignment =
|
|
150
|
-
const spfAlignment =
|
|
149
|
+
const dkimAlignment = getAlignment(domain, dkimDomains, { strict: dmarcRecord.adkim === 's' });
|
|
150
|
+
const spfAlignment = getAlignment(domain, spfDomains, { strict: dmarcRecord.aspf === 's' });
|
|
151
151
|
|
|
152
152
|
if (dkimAlignment || spfAlignment) {
|
|
153
153
|
// pass
|
|
@@ -164,7 +164,12 @@ const verifyDmarc = async opts => {
|
|
|
164
164
|
p: dmarcRecord.p,
|
|
165
165
|
sp: dmarcRecord.sp || dmarcRecord.p,
|
|
166
166
|
pct: dmarcRecord.pct,
|
|
167
|
-
rr: dmarcRecord.rr
|
|
167
|
+
rr: dmarcRecord.rr,
|
|
168
|
+
|
|
169
|
+
alignment: {
|
|
170
|
+
spf: { result: spfAlignment, strict: dmarcRecord.aspf === 's' },
|
|
171
|
+
dkim: { result: dkimAlignment, strict: dmarcRecord.adkim === 's' }
|
|
172
|
+
}
|
|
168
173
|
});
|
|
169
174
|
};
|
|
170
175
|
|
package/lib/mailauth.js
CHANGED
|
@@ -6,6 +6,7 @@ const { dmarc } = require('./dmarc');
|
|
|
6
6
|
const { arc, createSeal } = require('./arc');
|
|
7
7
|
const { bimi } = require('./bimi');
|
|
8
8
|
const { parseReceived } = require('./parse-received');
|
|
9
|
+
const { sealMessage } = require('./arc');
|
|
9
10
|
const libmime = require('libmime');
|
|
10
11
|
const os = require('os');
|
|
11
12
|
const { isIP } = require('net');
|
|
@@ -179,4 +180,4 @@ const authenticate = async (input, opts) => {
|
|
|
179
180
|
};
|
|
180
181
|
};
|
|
181
182
|
|
|
182
|
-
module.exports = { authenticate };
|
|
183
|
+
module.exports = { authenticate, sealMessage };
|
package/lib/spf/spf-verify.js
CHANGED
|
@@ -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
|
@@ -273,7 +273,11 @@ const getPublicKey = async (type, name, minBitLength, resolver) => {
|
|
|
273
273
|
throw err;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
|
|
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
|
+
);
|
|
277
281
|
const publicKeyObj = crypto.createPublicKey({
|
|
278
282
|
key: publicKeyPem,
|
|
279
283
|
format: 'pem'
|
|
@@ -425,7 +429,7 @@ const formatDomain = domain => {
|
|
|
425
429
|
return domain;
|
|
426
430
|
};
|
|
427
431
|
|
|
428
|
-
const
|
|
432
|
+
const getAlignment = (fromDomain, domainList, strict) => {
|
|
429
433
|
domainList = [].concat(domainList || []);
|
|
430
434
|
if (strict) {
|
|
431
435
|
fromDomain = formatDomain(fromDomain);
|
|
@@ -526,7 +530,7 @@ module.exports = {
|
|
|
526
530
|
|
|
527
531
|
validateAlgorithm,
|
|
528
532
|
|
|
529
|
-
|
|
533
|
+
getAlignment,
|
|
530
534
|
|
|
531
535
|
formatRelaxedLine,
|
|
532
536
|
|
package/licenses.txt
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
name license type link installed version author
|
|
2
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
|
|
5
|
-
joi BSD-3-Clause git://github.com/sideway/joi.git 17.
|
|
6
|
-
libmime MIT git://github.com/andris9/libmime.git 5.0.0 Andris Reinman
|
|
7
|
-
node-forge (BSD-3-Clause OR GPL-2.0) git+https://github.com/digitalbazaar/forge.git 1.
|
|
8
|
-
nodemailer MIT git+https://github.com/nodemailer/nodemailer.git 6.7.
|
|
9
|
-
psl MIT git+ssh://git@github.com/lupomontero/psl.git 1.8.0 Lupo Montero
|
|
10
|
-
punycode MIT git+https://github.com/bestiejs/punycode.js.git 2.1.1 Mathias Bynens
|
|
11
|
-
yargs MIT git+https://github.com/yargs/yargs.git 17.
|
|
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.0.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.3 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.4.1
|
package/man/mailauth.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH "MAILAUTH" "1" "
|
|
1
|
+
.TH "MAILAUTH" "1" "May 2022" "v2.3.2" "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
|
|
@@ -53,7 +53,7 @@ Display licenses for mailauth and included modules
|
|
|
53
53
|
\fBmailauth spf \-f andris@wildduck\.email \-i 217\.146\.76\.20\fP
|
|
54
54
|
.SH EMAIL ARGUMENT
|
|
55
55
|
.P
|
|
56
|
-
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
|
|
57
57
|
content is read from standard input\.
|
|
58
58
|
.SH OPTIONS
|
|
59
59
|
.RS 0
|
|
@@ -65,19 +65,19 @@ Enable silly verbose mode
|
|
|
65
65
|
Print application version
|
|
66
66
|
.IP \(bu 2
|
|
67
67
|
\fB\-\-client\-ip\fP, \fB\-i <ip>\fP
|
|
68
|
-
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)
|
|
69
69
|
.IP \(bu 2
|
|
70
70
|
\fB\-\-mta\fP, \fB\-m <hostname>\fP
|
|
71
|
-
|
|
71
|
+
The hostname of this machine, used in the \fBAuthentication\-Results\fP header\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
|
|
72
72
|
.IP \(bu 2
|
|
73
73
|
\fB\-\-helo\fP, \fB\-e <hostname>\fP
|
|
74
74
|
Client hostname from the EHLO/HELO command, used in some specific SPF checks\. (\fBreport\fP, \fBseal\fP, \fBspf\fP)
|
|
75
75
|
.IP \(bu 2
|
|
76
76
|
\fB\-\-sender\fP, \fB\-f <address>\fP
|
|
77
|
-
|
|
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)
|
|
78
78
|
.IP \(bu 2
|
|
79
79
|
\fB\-\-dns\-cache\fP, \fB\-n <file>\fP
|
|
80
|
-
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)
|
|
81
81
|
.IP \(bu 2
|
|
82
82
|
\fB\-\-private\-key\fP, \fB\-k <file>\fP
|
|
83
83
|
Path to a private key for signing\. Allowed key types are RSA and Ed25519 (\fBsign\fP, \fBseal\fP)
|
|
@@ -95,16 +95,16 @@ Signing algorithm\. Defaults either to \fIrsa\-sha256\fR or \fIed25519\-sha256\f
|
|
|
95
95
|
Canonicalization algorithm\. Defaults to \fIrelaxed/relaxed\fR\|\. (\fBsign\fP)
|
|
96
96
|
.IP \(bu 2
|
|
97
97
|
\fB\-\-body\-length\fP, \fB\-l <number>\fP
|
|
98
|
-
|
|
98
|
+
The maximum length of the canonicalized body to sign\. (\fBsign\fP)
|
|
99
99
|
.IP \(bu 2
|
|
100
100
|
\fB\-\-time\fP, \fB\-t <number>\fP
|
|
101
|
-
Signing time as a
|
|
101
|
+
Signing time as a Unix timestamp\. (\fBsign\fP, \fBseal\fP)
|
|
102
102
|
.IP \(bu 2
|
|
103
103
|
\fB\-\-header\-fields\fP, \fB\-h <list>\fP
|
|
104
104
|
Colon separated list of header field names to sign\. (\fBsign\fP, \fBseal\fP)
|
|
105
105
|
.IP \(bu 2
|
|
106
106
|
\fB\-\-headers\-only\fP, \fB\-o\fP
|
|
107
|
-
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)
|
|
108
108
|
.IP \(bu 2
|
|
109
109
|
\fB\-\-max\-lookups\fP, \fB\-x\fP
|
|
110
110
|
How many DNS lookups allowed for SPF validation\. Defaults to 50\. (\fBreport\fP, \fBspf\fP)
|
|
@@ -112,7 +112,7 @@ How many DNS lookups allowed for SPF validation\. Defaults to 50\. (\fBreport\fP
|
|
|
112
112
|
.RE
|
|
113
113
|
.SH DNS CACHE
|
|
114
114
|
.P
|
|
115
|
-
For cached DNS requests use the following JSON structure
|
|
115
|
+
For cached DNS requests, use the following JSON object structure: primary keys are domain names, and subkeys are resource record types\.
|
|
116
116
|
.P
|
|
117
117
|
.RS 2
|
|
118
118
|
.nf
|
|
@@ -129,7 +129,7 @@ For cached DNS requests use the following JSON structure where main keys are dom
|
|
|
129
129
|
.fi
|
|
130
130
|
.RE
|
|
131
131
|
.P
|
|
132
|
-
|
|
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\.
|
|
133
133
|
.SH BUGS
|
|
134
134
|
.P
|
|
135
135
|
Please report any bugs to https://github\.com/postalsys/mailauth/issues\.
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
94
|
+
The maximum length of the canonicalized body to sign. (`sign`)
|
|
95
95
|
|
|
96
96
|
- `--time`, `-t <number>`
|
|
97
|
-
Signing time as a
|
|
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
|
|
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
|
-
|
|
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
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailauth",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
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-
|
|
10
|
+
"build-source": "rm -rf node_modules package-lock.json && npm install && npm run man && npm run licenses && rm -rf node_modules package-lock.json && npm install --production && rm -rf package-lock.json",
|
|
11
|
+
"build-dist": "npx pkg --compress Brotli package.json && rm -rf package-lock.json && npm install",
|
|
11
12
|
"licenses": "license-report --only=prod --output=table --config license-report-config.json > licenses.txt"
|
|
12
13
|
},
|
|
13
14
|
"repository": {
|
|
@@ -31,28 +32,28 @@
|
|
|
31
32
|
},
|
|
32
33
|
"homepage": "https://github.com/postalsys/mailauth",
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"chai": "4.3.
|
|
35
|
-
"eslint": "8.
|
|
35
|
+
"chai": "4.3.6",
|
|
36
|
+
"eslint": "8.15.0",
|
|
36
37
|
"eslint-config-nodemailer": "1.2.0",
|
|
37
|
-
"eslint-config-prettier": "8.
|
|
38
|
+
"eslint-config-prettier": "8.5.0",
|
|
38
39
|
"js-yaml": "4.1.0",
|
|
39
|
-
"license-report": "
|
|
40
|
+
"license-report": "5.0.2",
|
|
40
41
|
"marked": "0.7.0",
|
|
41
42
|
"marked-man": "0.7.0",
|
|
42
43
|
"mbox-reader": "1.1.5",
|
|
43
|
-
"mocha": "
|
|
44
|
-
"pkg": "5.
|
|
44
|
+
"mocha": "10.0.0",
|
|
45
|
+
"pkg": "5.6.0"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"@fidm/x509": "1.2.1",
|
|
48
49
|
"ipaddr.js": "2.0.1",
|
|
49
|
-
"joi": "17.
|
|
50
|
-
"libmime": "5.
|
|
51
|
-
"node-forge": "1.
|
|
52
|
-
"nodemailer": "6.7.
|
|
50
|
+
"joi": "17.6.0",
|
|
51
|
+
"libmime": "5.1.0",
|
|
52
|
+
"node-forge": "1.3.1",
|
|
53
|
+
"nodemailer": "6.7.5",
|
|
53
54
|
"psl": "1.8.0",
|
|
54
55
|
"punycode": "2.1.1",
|
|
55
|
-
"yargs": "17.
|
|
56
|
+
"yargs": "17.5.0"
|
|
56
57
|
},
|
|
57
58
|
"engines": {
|
|
58
59
|
"node": ">=14.0.0"
|
|
@@ -60,21 +61,20 @@
|
|
|
60
61
|
"bin": {
|
|
61
62
|
"mailauth": "bin/mailauth.js"
|
|
62
63
|
},
|
|
63
|
-
"
|
|
64
|
-
"man"
|
|
65
|
-
|
|
64
|
+
"man": [
|
|
65
|
+
"man/mailauth.1"
|
|
66
|
+
],
|
|
66
67
|
"pkg": {
|
|
67
|
-
"scripts": [
|
|
68
|
-
"workers/**/*.js"
|
|
69
|
-
],
|
|
70
68
|
"assets": [
|
|
71
69
|
"man/**/*",
|
|
72
70
|
"licenses.txt",
|
|
73
71
|
"LICENSE.txt"
|
|
74
72
|
],
|
|
75
|
-
"
|
|
76
|
-
"node16-
|
|
73
|
+
"targets": [
|
|
74
|
+
"node16-linux-x64",
|
|
75
|
+
"node16-macos-x64",
|
|
76
|
+
"node16-win-x64"
|
|
77
77
|
],
|
|
78
|
-
"outputPath": "dist"
|
|
78
|
+
"outputPath": "ee-dist"
|
|
79
79
|
}
|
|
80
80
|
}
|