mailauth 4.8.0 → 4.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.8.2](https://github.com/postalsys/mailauth/compare/v4.8.1...v4.8.2) (2024-12-19)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **ARC:** ensure that instance value is 1 if ARC chain does not exist yet ([ab4c5e9](https://github.com/postalsys/mailauth/commit/ab4c5e9ae0158e196b10f346321ca55b8f06c679))
9
+
10
+ ## [4.8.1](https://github.com/postalsys/mailauth/compare/v4.8.0...v4.8.1) (2024-11-05)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **cli:** Updated help strings for the cli script ([8a86e51](https://github.com/postalsys/mailauth/commit/8a86e51bff0300a7daea26062481ac56904202a8))
16
+
3
17
  ## [4.8.0](https://github.com/postalsys/mailauth/compare/v4.7.3...v4.8.0) (2024-11-05)
4
18
 
5
19
 
package/bin/mailauth.js CHANGED
@@ -20,49 +20,51 @@ const pathlib = require('node:path');
20
20
  const argv = yargs(hideBin(process.argv))
21
21
  .command(
22
22
  ['report [email]', '$0 [email]'],
23
- 'Validate email message and return a report in JSON format',
23
+ 'Validate an email message and return a detailed JSON report',
24
24
  yargs => {
25
25
  yargs
26
26
  .option('client-ip', {
27
27
  alias: 'i',
28
28
  type: 'string',
29
- description: 'Client IP used for SPF checks. If not set then parsed from the latest Received header'
29
+ description: 'IP address of the remote client (used for SPF checks). If not provided, it is parsed from the latest Received header.'
30
30
  })
31
31
  .option('mta', {
32
32
  alias: 'm',
33
33
  type: 'string',
34
- description: 'Hostname of this machine, used in the Authentication-Results header',
34
+ description:
35
+ 'Hostname of the server performing the validation (used in the Authentication-Results header). Defaults to the local hostname.',
35
36
  default: os.hostname()
36
37
  })
37
38
  .option('helo', {
38
39
  alias: 'e',
39
40
  type: 'string',
40
- description: 'Client hostname from the EHLO/HELO command, used in some specific SPF checks'
41
+ description: 'Client hostname from the HELO/EHLO command (used in some SPF checks).'
41
42
  })
42
43
  .option('sender', {
43
44
  alias: 'f',
44
45
  type: 'string',
45
- description: 'Email address from the MAIL FROM command. If not set then the address from the latest Return-Path header is used instead'
46
+ description: 'Email address from the MAIL FROM command. If not provided, the address from the latest Return-Path header is used.'
46
47
  })
47
48
  .option('dns-cache', {
48
49
  alias: 'n',
49
50
  type: 'string',
50
- description: 'Path to a JSON file with cached DNS responses. If this file is given then no actual DNS requests are performed'
51
+ description:
52
+ 'Path to a JSON file with cached DNS responses. When provided, DNS queries use these cached responses instead of performing actual DNS lookups.'
51
53
  })
52
54
  .option('max-lookups', {
53
55
  alias: 'x',
54
56
  type: 'number',
55
- description: 'Maximum allowed DNS lookups',
57
+ description: 'Maximum allowed DNS lookups during SPF checks. Defaults to 10.',
56
58
  default: 10
57
59
  })
58
60
  .option('max-void-lookups', {
59
61
  alias: 'z',
60
62
  type: 'number',
61
- description: 'Maximum allowed empty DNS lookups',
63
+ description: 'Maximum allowed DNS lookups that return no data (void lookups) during SPF checks. Defaults to 2.',
62
64
  default: 2
63
65
  });
64
66
  yargs.positional('email', {
65
- describe: 'Path to the email message file in EML format. If not specified then content is read from stdin'
67
+ describe: 'Path to the email message file in EML format. If not specified, the content is read from standard input.'
66
68
  });
67
69
  },
68
70
  argv => {
@@ -71,7 +73,7 @@ const argv = yargs(hideBin(process.argv))
71
73
  process.exit();
72
74
  })
73
75
  .catch(err => {
74
- console.error('Failed to generate report for input message');
76
+ console.error('Failed to generate report for the input message.');
75
77
  console.error(err);
76
78
  process.exit(1);
77
79
  });
@@ -82,59 +84,58 @@ const argv = yargs(hideBin(process.argv))
82
84
  'Sign an email with a DKIM digital signature',
83
85
  yargs => {
84
86
  yargs
85
-
86
87
  .option('private-key', {
87
88
  alias: 'k',
88
89
  type: 'string',
89
- description: 'Path to a private key for signing',
90
+ description: 'Path to the private key file used for signing.',
90
91
  demandOption: true
91
92
  })
92
93
  .option('domain', {
93
94
  alias: 'd',
94
95
  type: 'string',
95
- description: 'Domain name for signing (d= tag)',
96
+ description: 'Domain name to use in the DKIM signature (d= tag).',
96
97
  demandOption: true
97
98
  })
98
99
  .option('selector', {
99
100
  alias: 's',
100
101
  type: 'string',
101
- description: 'Key selector for signing (s= tag)',
102
+ description: 'Selector to use in the DKIM signature (s= tag).',
102
103
  demandOption: true
103
104
  })
104
105
  .option('algo', {
105
106
  alias: 'a',
106
107
  type: 'string',
107
- description: 'Signing algorithm. Defaults either to rsa-sha256 or ed25519-sha256 depending on the private key format.',
108
+ description: 'Signing algorithm. Defaults to "rsa-sha256" or "ed25519-sha256" depending on the private key type.',
108
109
  default: 'rsa-sha256'
109
110
  })
110
111
  .option('canonicalization', {
111
112
  alias: 'c',
112
113
  type: 'string',
113
- description: 'Canonicalization algorithm (c= tag)',
114
+ description: 'Canonicalization method (c= tag). Defaults to "relaxed/relaxed".',
114
115
  default: 'relaxed/relaxed'
115
116
  })
116
117
  .option('time', {
117
118
  alias: 't',
118
119
  type: 'number',
119
- description: 'Signing time as a unix timestamp (t= tag)'
120
+ description: 'Signing time as a UNIX timestamp (t= tag). Defaults to the current time.'
120
121
  })
121
122
  .option('body-length', {
122
123
  alias: 'l',
123
124
  type: 'number',
124
- description: 'Maximum length of canonicalizated body to sign (l= tag)'
125
+ description: 'Maximum length of the canonicalized body to include in the signature (l= tag). Not recommended for general use.'
125
126
  })
126
127
  .option('header-fields', {
127
128
  alias: 'h',
128
129
  type: 'string',
129
- description: 'Colon separated list of header field names to sign (h= tag)'
130
+ description: 'Colon-separated list of header field names to include in the signature (h= tag).'
130
131
  })
131
132
  .option('headers-only', {
132
133
  alias: 'o',
133
134
  type: 'boolean',
134
- description: 'Return signing headers only'
135
+ description: 'If set, outputs only the DKIM signature headers without the message body.'
135
136
  });
136
137
  yargs.positional('email', {
137
- describe: 'Path to the email message file in EML format. If not specified then content is read from stdin'
138
+ describe: 'Path to the email message file in EML format. If not specified, the content is read from standard input.'
138
139
  });
139
140
  },
140
141
  argv => {
@@ -144,7 +145,7 @@ const argv = yargs(hideBin(process.argv))
144
145
  })
145
146
  .catch(err => {
146
147
  if (!err.suppress) {
147
- console.error('Failed to sign input message');
148
+ console.error('Failed to sign the input message.');
148
149
  console.error(err);
149
150
  }
150
151
  process.exit(1);
@@ -153,83 +154,85 @@ const argv = yargs(hideBin(process.argv))
153
154
  )
154
155
  .command(
155
156
  ['seal [email]'],
156
- 'Authenticates an email and seals it with an ARC digital signature',
157
+ 'Authenticate and seal an email with an ARC digital signature',
157
158
  yargs => {
158
159
  yargs
159
160
  .option('private-key', {
160
161
  alias: 'k',
161
162
  type: 'string',
162
- description: 'Path to a private key for sealing',
163
+ description: 'Path to the private key file used for sealing.',
163
164
  demandOption: true
164
165
  })
165
166
  .option('domain', {
166
167
  alias: 'd',
167
168
  type: 'string',
168
- description: 'Domain name for sealing (d= tag)',
169
+ description: 'Domain name to use in the ARC seal (d= tag).',
169
170
  demandOption: true
170
171
  })
171
172
  .option('selector', {
172
173
  alias: 's',
173
174
  type: 'string',
174
- description: 'Key selector for sealing (s= tag)',
175
+ description: 'Selector to use in the ARC seal (s= tag).',
175
176
  demandOption: true
176
177
  })
177
178
  .option('algo', {
178
179
  alias: 'a',
179
180
  type: 'string',
180
181
  description:
181
- 'Sealing algorithm. Defaults either to rsa-sha256 or ed25519-sha256 depending on the private key format. NB! Only rsa-sha256 is allowed by RFC8617 (a= tag)',
182
+ 'Sealing algorithm. Defaults to "rsa-sha256" or "ed25519-sha256" depending on the private key type. Note: RFC8617 only allows "rsa-sha256" (a= tag).',
182
183
  default: 'rsa-sha256'
183
184
  })
184
185
  .option('canonicalization', {
185
186
  alias: 'c',
186
187
  type: 'string',
187
- description: 'Canonicalization algorithm. NB! Only relaxed/relaxed is allowed by RFC8617 (c= tag)',
188
+ description: 'Canonicalization method. Note: RFC8617 only allows "relaxed/relaxed" (c= tag).',
188
189
  default: 'relaxed/relaxed'
189
190
  })
190
191
  .option('time', {
191
192
  alias: 't',
192
193
  type: 'number',
193
- description: 'Signing time as a unix timestamp (t= tag)'
194
+ description: 'Sealing time as a UNIX timestamp (t= tag). Defaults to the current time.'
194
195
  })
195
196
  .option('header-fields', {
196
197
  alias: 'h',
197
198
  type: 'string',
198
- description: 'Colon separated list of header field names to sign (h= tag)'
199
+ description: 'Colon-separated list of header field names to include in the seal (h= tag).'
199
200
  })
200
201
  .option('client-ip', {
201
202
  alias: 'i',
202
203
  type: 'string',
203
- description: 'Client IP used for SPF checks. If not set then parsed from the latest Received header'
204
+ description: 'IP address of the remote client (used for SPF checks). If not provided, it is parsed from the latest Received header.'
204
205
  })
205
206
  .option('mta', {
206
207
  alias: 'm',
207
208
  type: 'string',
208
- description: 'Hostname of this machine, used in the Authentication-Results header',
209
+ description:
210
+ 'Hostname of the server performing the validation (used in the Authentication-Results header). Defaults to the local hostname.',
209
211
  default: os.hostname()
210
212
  })
211
213
  .option('helo', {
212
214
  alias: 'e',
213
215
  type: 'string',
214
- description: 'Client hostname from the EHLO/HELO command, used in some specific SPF checks'
216
+ description: 'Client hostname from the HELO/EHLO command (used in some SPF checks).'
215
217
  })
216
218
  .option('sender', {
217
219
  alias: 'f',
218
220
  type: 'string',
219
- description: 'Email address from the MAIL FROM command. If not set then the address from the latest Return-Path header is used instead'
221
+ description: 'Email address from the MAIL FROM command. If not provided, the address from the latest Return-Path header is used.'
220
222
  })
221
223
  .option('dns-cache', {
222
224
  alias: 'n',
223
225
  type: 'string',
224
- description: 'Path to a JSON file with cached DNS responses. If this file is given then no actual DNS requests are performed'
226
+ description:
227
+ 'Path to a JSON file with cached DNS responses. When provided, DNS queries use these cached responses instead of performing actual DNS lookups.'
225
228
  })
226
229
  .option('headers-only', {
227
230
  alias: 'o',
228
231
  type: 'boolean',
229
- description: 'Return signing headers only'
232
+ description: 'If set, outputs only the ARC seal headers without the message body.'
230
233
  });
231
234
  yargs.positional('email', {
232
- describe: 'Path to the email message file in EML format. If not specified then content is read from stdin'
235
+ describe: 'Path to the email message file in EML format. If not specified, the content is read from standard input.'
233
236
  });
234
237
  },
235
238
  argv => {
@@ -239,7 +242,7 @@ const argv = yargs(hideBin(process.argv))
239
242
  })
240
243
  .catch(err => {
241
244
  if (!err.suppress) {
242
- console.error('Failed to sign input message');
245
+ console.error('Failed to seal the input message.');
243
246
  console.error(err);
244
247
  }
245
248
  process.exit(1);
@@ -254,46 +257,47 @@ const argv = yargs(hideBin(process.argv))
254
257
  .option('sender', {
255
258
  alias: 'f',
256
259
  type: 'string',
257
- description: 'Email address from the MAIL FROM command',
260
+ description: 'Email address from the MAIL FROM command.',
258
261
  demandOption: true
259
262
  })
260
263
  .option('client-ip', {
261
264
  alias: 'i',
262
265
  type: 'string',
263
- description: 'Client IP used for SPF checks. If not set then parsed from the latest Received header',
266
+ description: 'IP address of the remote client (used for SPF checks).',
264
267
  demandOption: true
265
268
  })
266
269
  .option('helo', {
267
270
  alias: 'e',
268
271
  type: 'string',
269
- description: 'Client hostname from the EHLO/HELO command, used in some specific SPF checks'
272
+ description: 'Client hostname from the HELO/EHLO command (used in some SPF checks).'
270
273
  })
271
274
  .option('mta', {
272
275
  alias: 'm',
273
276
  type: 'string',
274
- description: 'Hostname of this machine, used in the Authentication-Results header',
277
+ description: 'Hostname of the server performing the SPF check (used in the Authentication-Results header). Defaults to the local hostname.',
275
278
  default: os.hostname()
276
279
  })
277
280
  .option('dns-cache', {
278
281
  alias: 'n',
279
282
  type: 'string',
280
- description: 'Path to a JSON file with cached DNS responses. If this file is given then no actual DNS requests are performed'
283
+ description:
284
+ 'Path to a JSON file with cached DNS responses. When provided, DNS queries use these cached responses instead of performing actual DNS lookups.'
281
285
  })
282
286
  .option('headers-only', {
283
287
  alias: 'o',
284
288
  type: 'boolean',
285
- description: 'Return signing headers only'
289
+ description: 'If set, outputs only the SPF authentication header.'
286
290
  })
287
291
  .option('max-lookups', {
288
292
  alias: 'x',
289
293
  type: 'number',
290
- description: 'Maximum allowed DNS lookups',
294
+ description: 'Maximum allowed DNS lookups during SPF checks. Defaults to 10.',
291
295
  default: 10
292
296
  })
293
297
  .option('max-void-lookups', {
294
298
  alias: 'z',
295
299
  type: 'number',
296
- description: 'Maximum allowed empty DNS lookups',
300
+ description: 'Maximum allowed DNS lookups that return no data (void lookups) during SPF checks. Defaults to 2.',
297
301
  default: 2
298
302
  });
299
303
  },
@@ -303,7 +307,7 @@ const argv = yargs(hideBin(process.argv))
303
307
  process.exit();
304
308
  })
305
309
  .catch(err => {
306
- console.error('Failed to verify SPF for an email address');
310
+ console.error('Failed to verify SPF for the email address.');
307
311
  console.error(err);
308
312
  process.exit(1);
309
313
  });
@@ -311,32 +315,28 @@ const argv = yargs(hideBin(process.argv))
311
315
  )
312
316
  .command(
313
317
  ['vmc'],
314
- 'Validate VMC logo',
318
+ 'Validate a Verified Mark Certificate (VMC) logo file',
315
319
  yargs => {
316
320
  yargs
317
321
  .option('authorityPath', {
318
322
  alias: 'p',
319
323
  type: 'string',
320
- description: 'Path to a VMC file',
321
- demandOption: false
324
+ description: 'Path to a local VMC file.'
322
325
  })
323
326
  .option('authority', {
324
327
  alias: 'a',
325
328
  type: 'string',
326
- description: 'URL to a VMC file',
327
- demandOption: false
329
+ description: 'URL of the VMC file.'
328
330
  })
329
331
  .option('domain', {
330
332
  alias: 'd',
331
333
  type: 'string',
332
- description: 'Sending domain to validate',
333
- demandOption: false
334
+ description: 'Sending domain to validate against the VMC.'
334
335
  })
335
336
  .option('date', {
336
337
  alias: 't',
337
338
  type: 'string',
338
- description: 'ISO formatted timestamp for the certificate expiration checks',
339
- demandOption: false
339
+ description: 'ISO-formatted timestamp to use for certificate expiration checks.'
340
340
  });
341
341
  },
342
342
  argv => {
@@ -345,7 +345,7 @@ const argv = yargs(hideBin(process.argv))
345
345
  process.exit();
346
346
  })
347
347
  .catch(err => {
348
- console.error('Failed to verify VMC file');
348
+ console.error('Failed to verify the VMC file.');
349
349
  console.error(err);
350
350
  process.exit(1);
351
351
  });
@@ -353,32 +353,28 @@ const argv = yargs(hideBin(process.argv))
353
353
  )
354
354
  .command(
355
355
  ['bodyhash [email]'],
356
- 'Generate a signature body hash for an email',
356
+ 'Generate a DKIM body hash for an email message',
357
357
  yargs => {
358
358
  yargs
359
-
360
359
  .option('algo', {
361
360
  alias: 'a',
362
361
  type: 'string',
363
- description: 'Hashing algorithm. Defaults to sha256.',
362
+ description: 'Hashing algorithm to use. Defaults to "sha256". Can also use DKIM-style algorithms like "rsa-sha256".',
364
363
  default: 'sha256'
365
364
  })
366
-
367
365
  .option('canonicalization', {
368
366
  alias: 'c',
369
367
  type: 'string',
370
- description: 'Canonicalization algorithm (c= tag)',
368
+ description: 'Body canonicalization method (c= tag). Defaults to "relaxed". Can use DKIM-style formats like "relaxed/relaxed".',
371
369
  default: 'relaxed'
372
370
  })
373
-
374
371
  .option('body-length', {
375
372
  alias: 'l',
376
373
  type: 'number',
377
- description: 'Maximum length of canonicalizated body to sign (l= tag)'
374
+ description: 'Maximum length of the canonicalized body to include in the hash (l= tag).'
378
375
  });
379
-
380
376
  yargs.positional('email', {
381
- describe: 'Path to the email message file in EML format. If not specified then content is read from stdin'
377
+ describe: 'Path to the email message file in EML format. If not specified, the content is read from standard input.'
382
378
  });
383
379
  },
384
380
  argv => {
@@ -388,7 +384,7 @@ const argv = yargs(hideBin(process.argv))
388
384
  })
389
385
  .catch(err => {
390
386
  if (!err.suppress) {
391
- console.error('Failed to calculate body hash for the input message');
387
+ console.error('Failed to calculate the body hash for the input message.');
392
388
  console.error(err);
393
389
  }
394
390
  process.exit(1);
@@ -397,17 +393,17 @@ const argv = yargs(hideBin(process.argv))
397
393
  )
398
394
  .command(
399
395
  ['license'],
400
- 'Show license information',
396
+ 'Display license information for mailauth and included modules',
401
397
  () => false,
402
398
  () => {
403
399
  fs.readFile(pathlib.join(__dirname, '..', 'LICENSE.txt'), (err, license) => {
404
400
  if (err) {
405
- console.error('Failed to load license information');
401
+ console.error('Failed to load license information.');
406
402
  console.error(err);
407
403
  return process.exit(1);
408
404
  }
409
405
 
410
- console.error('Mailauth License');
406
+ console.error('mailauth License');
411
407
  console.error('================');
412
408
 
413
409
  console.error(license.toString().trim());
@@ -416,7 +412,7 @@ const argv = yargs(hideBin(process.argv))
416
412
 
417
413
  fs.readFile(pathlib.join(__dirname, '..', 'licenses.txt'), (err, data) => {
418
414
  if (err) {
419
- console.error('Failed to load license information');
415
+ console.error('Failed to load included modules license information.');
420
416
  console.error(err);
421
417
  return process.exit(1);
422
418
  }
@@ -433,7 +429,7 @@ const argv = yargs(hideBin(process.argv))
433
429
  .option('verbose', {
434
430
  alias: 'v',
435
431
  type: 'boolean',
436
- description: 'Run with verbose logging'
432
+ description: 'Enable verbose logging for debugging purposes.'
437
433
  }).argv;
438
434
 
439
435
  assert.ok(argv);
package/cli.md CHANGED
@@ -1,351 +1,355 @@
1
- ![](https://github.com/postalsys/mailauth/raw/master/assets/mailauth.png)
1
+ # mailauth CLI Usage
2
2
 
3
- Command line utility and a [Node.js library](README.md) for email authentication.
3
+ ![mailauth Logo](https://github.com/postalsys/mailauth/raw/master/assets/mailauth.png)
4
4
 
5
- # CLI USAGE
5
+ mailauth provides a command-line utility for email authentication, complementing its [Node.js library](README.md). This guide explains how to use the mailauth CLI to perform various email authentication tasks.
6
6
 
7
- ## TOC
7
+ ## Table of Contents
8
8
 
9
9
  - [Installation](#installation)
10
- - [Help](#help)
11
- - [Available commands](#available-commands)
12
- - [report](#report) to validate SPF, DKIM, DMARC, ARC
13
- - [sign](#sign) - to sign an email with DKIM
14
- - [seal](#seal) - to seal an email with ARC
15
- - [spf](#spf) - to validate SPF for an IP address and an email address
16
- - [vmc](#vmc) - to validate BIMI VMC logo files
17
- - [bodyhash](#bodyhash) - to generate the signature body hash value for an email
18
- - [license](#license) - display licenses for `mailauth` and included modules
19
- - [DNS cache file](#dns-cache-file)
10
+ - [Getting Help](#getting-help)
11
+ - [Available Commands](#available-commands)
12
+ - [`report`](#report) — Validate SPF, DKIM, DMARC, ARC, and BIMI
13
+ - [`sign`](#sign) — Sign an email with DKIM
14
+ - [`seal`](#seal) — Seal an email with ARC
15
+ - [`spf`](#spf) — Validate SPF for an IP address and email address
16
+ - [`vmc`](#vmc) — Validate BIMI VMC logo files
17
+ - [`bodyhash`](#bodyhash) — Generate the body hash value for an email
18
+ - [`license`](#license) — Display licenses for mailauth and included modules
19
+ - [DNS Cache File](#dns-cache-file)
20
+ - [License](#license)
20
21
 
21
22
  ## Installation
22
23
 
23
- Download `mailauth` for your platform:
24
+ Install the mailauth CLI by downloading the appropriate package for your platform or via npm:
24
25
 
25
- - MacOS
26
+ - **MacOS:**
26
27
  - [Intel processors](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.pkg)
27
28
  - [Apple silicon](https://github.com/postalsys/mailauth/releases/latest/download/mailauth-arm.pkg)
28
- - [Linux](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.tar.gz)
29
- - [Windows](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.exe)
30
- - Or install from the NPM registry: `npm install -g mailauth`
29
+ - **Linux:**
30
+ - [Download mailauth.tar.gz](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.tar.gz)
31
+ - **Windows:**
32
+ - [Download mailauth.exe](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.exe)
33
+ - **NPM Registry:**
31
34
 
32
- ## Help
35
+ - Install globally using npm:
33
36
 
37
+ ```bash
38
+ npm install -g mailauth
39
+ ```
40
+
41
+ ## Getting Help
42
+
43
+ To display help information for the mailauth CLI or any specific command, use the `--help` flag:
44
+
45
+ ```bash
46
+ mailauth --help
47
+ mailauth report --help
48
+ mailauth sign --help
49
+ mailauth seal --help
50
+ mailauth spf --help
34
51
  ```
35
- $ mailauth --help
36
- $ mailauth report --help
37
- $ mailauth sign --help
38
- $ mailauth seal --help
39
- $ mailauth spf --help
40
- ```
41
52
 
42
- ## Available commands
53
+ ## Available Commands
54
+
55
+ The mailauth CLI offers several commands to perform different email authentication tasks:
56
+
57
+ 1. [`report`](#report) — Validate SPF, DKIM, DMARC, ARC, and BIMI.
58
+ 2. [`sign`](#sign) — Sign an email with DKIM.
59
+ 3. [`seal`](#seal) — Seal an email with ARC.
60
+ 4. [`spf`](#spf) — Validate SPF for an IP address and email address.
61
+ 5. [`vmc`](#vmc) — Validate BIMI VMC logo files.
62
+ 6. [`bodyhash`](#bodyhash) — Generate the body hash value for an email.
63
+ 7. [`license`](#license) — Display licenses for mailauth and included modules.
43
64
 
44
65
  ### report
45
66
 
46
- `report` command takes an email message and returns a JSON formatted report for SPF, DKIM, ARC, DMARC and BIMI. Not all reports might make sense for your use case, eg. SPF check for an outbound message usually gives no useful info, so you can ignore the parts you're not interested in.
67
+ The `report` command analyzes an email message and returns a JSON-formatted report detailing the results of SPF, DKIM, DMARC, ARC, and BIMI validations.
47
68
 
48
- ```
49
- $ mailauth report [options] [email]
69
+ #### Usage
70
+
71
+ ```bash
72
+ mailauth report [options] [email]
50
73
  ```
51
74
 
52
- Where
75
+ - **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
53
76
 
54
- - **options** are option flags and arguments
55
- - **email** is the path to EML formatted email message file. If not provided then email message is read from standard input
77
+ #### Options
56
78
 
57
- **Options**
79
+ - `--client-ip x.x.x.x`, `-i x.x.x.x`: IP address of the remote client that sent the email. If not provided, it's parsed from the latest `Received` header.
80
+ - `--sender user@example.com`, `-f user@example.com`: Email address from the MAIL FROM command. If not provided, it's parsed from the latest `Return-Path` header.
81
+ - `--helo hostname`, `-e hostname`: Hostname from the HELO/EHLO command. Used in some SPF validations.
82
+ - `--mta hostname`, `-m hostname`: Hostname of the server performing validations. Defaults to the local hostname.
83
+ - `--dns-cache /path/to/dns.json`, `-n /path/to/dns.json`: Path to a DNS cache file. When provided, DNS queries use cached responses.
84
+ - `--verbose`, `-v`: Enables verbose output, displaying debugging information.
85
+ - `--max-lookups number`, `-x number`: Sets the maximum number of DNS lookups for SPF checks. Defaults to `10`.
86
+ - `--max-void-lookups number`, `-z number`: Sets the maximum number of void DNS lookups for SPF checks. Defaults to `2`.
58
87
 
59
- - `--client-ip x.x.x.x` or `-i x.x.x.x` is the IP of the remote client that sent the email. If not provided then it is parsed from the latest `Received` header
60
- - `--sender user@example.com` or `-f address` is the email address from the MAIL FROM command. If not provided then it is parsed from the latest Return-Path header
61
- - `--helo hostname` or `-e hostname` is the client hostname from the HELO/EHLO command. Used in some obscure SPF validation operations
62
- - `--mta hostname` or `-m hostname` is the server hostname doing the validation checks. Defaults to `os.hostname()`
63
- - `--dns-cache /path/to/dns.json` or `-n path` is the path to a file with cached DNS query responses. If this file is provided then no actual DNS requests are performed, only cached values from this file are used.
64
- - `--verbose` or `-v` if this flag is set then mailauth writes some debugging info to standard error
65
- - `--max-lookups nr` or `-x nr` defines the allowed DNS lookup limit for SPF checks. Defaults to 10.
66
- - `--max-void-lookups nr` or `-z nr` defines the allowed DNS lookup limit for SPF checks. Defaults to 2.
88
+ #### Example
67
89
 
68
- **Example**
90
+ ```bash
91
+ mailauth report --verbose --dns-cache examples/dns-cache.json test/fixtures/message2.eml
92
+ ```
93
+
94
+ **Sample Output:**
69
95
 
70
96
  ```
71
- $ mailauth report --verbose --dns-cache examples/dns-cache.json test/fixtures/message2.eml
72
97
  Reading email message from test/fixtures/message2.eml
73
98
  DNS query for TXT mail.projectpending.com: not found
74
99
  DNS query for TXT _dmarc.projectpending.com: not found
75
100
  {
76
101
  "receivedChain": [
77
- ...
102
+ "..."
103
+ ]
104
+ }
78
105
  ```
79
106
 
80
- See full example for DKIM checks [here](https://gist.github.com/andris9/8d4ab527282041f6725a640d80da4872).
107
+ For a detailed example of DKIM checks, refer to [this gist](https://gist.github.com/andris9/8d4ab527282041f6725a640d80da4872).
81
108
 
82
109
  ### sign
83
110
 
84
- `sign` command takes an email message and signs it with a DKIM digital signature.
111
+ The `sign` command signs an email message using a DKIM signature.
85
112
 
86
- ```
87
- $ mailauth sign [options] [email]
113
+ #### Usage
114
+
115
+ ```bash
116
+ mailauth sign [options] [email]
88
117
  ```
89
118
 
90
- Where
119
+ - **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
91
120
 
92
- - **options** are option flags and arguments
93
- - **email** is the path to EML formatted email message file. If not provided then email message is read from standard input
121
+ #### Options
94
122
 
95
- **Options**
123
+ - `--private-key /path/to/private.key`, `-k /path/to/private.key`: Path to the private key used for signing.
124
+ - `--domain example.com`, `-d example.com`: Domain name for the DKIM signature (`d=` tag).
125
+ - `--selector selector`, `-s selector`: Selector for the DKIM key (`s=` tag).
126
+ - `--algo algorithm`, `-a algorithm`: Signing algorithm (e.g., `rsa-sha256`). Defaults based on the private key type.
127
+ - `--canonicalization method`, `-c method`: Canonicalization method (e.g., `relaxed/relaxed`). Defaults to `relaxed/relaxed`.
128
+ - `--time timestamp`, `-t timestamp`: Signing time as a Unix timestamp (`t=` tag).
129
+ - `--header-fields "field1:field2"`, `-h "field1:field2"`: Colon-separated list of header fields to include in the signature (`h=` tag).
130
+ - `--body-length length`, `-l length`: Maximum length of the body to include in the signature (`l=` tag).
131
+ - `--headers-only`, `-o`: Outputs only the DKIM signature headers without the entire message.
96
132
 
97
- - `--private-key /path` or `-k /path` is the path to a private key for signing
98
- - `--domain example.com` or `-d example.com` is the domain name for signing (d= tag)
99
- - `--selector xxx` or `-s xxx` is the key selector name for signing (s= tag)
100
- - `--algo rsa-sha256` or `-a rsa-sha256` is the signing algorithm. Defaults either to "rsa-sha256" or
101
- "ed25519-sha256" depending on the private key format (a= tag)
102
- - `--canonicalization algo` or `-c algo` is the canonicalization algorithm, defaults to "relaxed/relaxed" (c= tag)
103
- - `--time 12345` or `-t 12345` is the signing time as a unix timestamp (t= tag)
104
- - `--header-fields "message-id:date"` or `-h keys` is a colon separated list of header field names to sign (h= tag)
105
- - `--body-length 12345` or `-l 12345` is the maximum length of canonicalizated body to sign (l= tag)
106
- - `--headers-only` or `-o` If set return signing headers only. Default is to return the entire message.
133
+ #### Example
107
134
 
108
- **Example**
135
+ ```bash
136
+ mailauth sign /path/to/message.eml --domain example.com --selector s1 --private-key /path/to/private.key --verbose
137
+ ```
138
+
139
+ **Sample Output:**
109
140
 
110
141
  ```
111
- $ mailauth sign /path/message.eml --domain kreata.ee --selector test --privateKey /path/private-rsa.pem --verbose
112
- Reading email message from /path/message.eml
113
- Signing domain: kreata.ee
114
- Key selector: test
142
+ Reading email message from /path/to/message.eml
143
+ Signing domain: example.com
144
+ Key selector: s1
115
145
  Canonicalization algorithm: relaxed/relaxed
116
146
  Hashing algorithm: rsa-sha256
117
- Signing time: 2020-12-03T23:00:14.956Z
147
+ Signing time: 2023-03-15T12:00:00.000Z
118
148
  --------
119
- DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kreata.ee;
120
- h=MIME-Version: Date: Message-ID: Subject: To: From: Content-Type;
149
+ DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;
150
+ h=MIME-Version:Date:Message-ID:Subject:To:From:Content-Type;
121
151
  ...
122
152
  ```
123
153
 
124
154
  ### seal
125
155
 
126
- `seal` command takes an email message and seals it with an ARC digital signature.
156
+ The `seal` command adds an ARC (Authenticated Received Chain) seal to an email message.
127
157
 
128
- ```
129
- $ mailauth seal [options] [email]
158
+ #### Usage
159
+
160
+ ```bash
161
+ mailauth seal [options] [email]
130
162
  ```
131
163
 
132
- Where
164
+ - **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
133
165
 
134
- - **options** are option flags and arguments
135
- - **email** is the path to EML formatted email message file. If not provided then email message is read from standard input
166
+ #### Options
136
167
 
137
- **Options**
168
+ **Sealing Options:**
138
169
 
139
- As the emails needs to be authenticated before sealing then `seal` command expects in additon to sealing key information also the authentication options from the `report` command.
170
+ - `--private-key /path/to/private.key`, `-k /path/to/private.key`: Path to the private key used for sealing.
171
+ - `--domain example.com`, `-d example.com`: Domain name for the ARC seal (`d=` tag).
172
+ - `--selector selector`, `-s selector`: Selector for the ARC key (`s=` tag).
173
+ - `--algo algorithm`, `-a algorithm`: Sealing algorithm (e.g., `rsa-sha256`). Defaults based on the private key type.
174
+ - `--time timestamp`, `-t timestamp`: Sealing time as a Unix timestamp (`t=` tag).
175
+ - `--header-fields "field1:field2"`, `-h "field1:field2"`: Colon-separated list of header fields to include in the seal (`h=` tag).
176
+ - `--headers-only`, `-o`: Outputs only the ARC seal headers without the entire message.
140
177
 
141
- - `--private-key /path` or `-k /path` is the path to a private key for sealing
142
- - `--domain example.com` or `-d example.com` is the domain name for sealing (d= tag)
143
- - `--selector xxx` or `-s xxx` is the key selector name for sealing (s= tag)
144
- - `--algo rsa-sha256` or `-a rsa-sha256` is the sealing algorithm. Defaults either to "rsa-sha256" or
145
- "ed25519-sha256" depending on the private key format (a= tag)
146
- - `--time 12345` or `-t 12345` is the sealing time as a unix timestamp (t= tag)
147
- - `--header-fields "message-id:date"` or `-h keys` is a colon separated list of header field names to seal (h= tag)
148
- - `--client-ip x.x.x.x` or `-i x.x.x.x` is the IP of the remote client that sent the email. If not provided then it is parsed from the latest `Received` header
149
- - `--sender user@example.com` or `-f address` is the email address from the MAIL FROM command. If not provided then it is parsed from the latest Return-Path header
150
- - `--helo hostname` or `-e hostname` is the client hostname from the HELO/EHLO command. Used in some obscure SPF validation operations
151
- - `--mta hostname` or `-m hostname` is the server hostname doing the validation checks. Defaults to `os.hostname()`
152
- - `--dns-cache /path/to/dns.json` or `-n path` is the path to a file with cached DNS query responses. If this file is provided then no actual DNS requests are performed, only cached values from this file are used.
153
- - `--headers-only` or `-o` If set return signing headers only. Default is to return the entire message.
178
+ **Authentication Options (from `report` command):**
154
179
 
155
- > Canonicalization (c= tag) is always "relaxed/relaxed" when sealing, this can not be changed
180
+ - `--client-ip x.x.x.x`, `-i x.x.x.x`: IP address of the remote client that sent the email.
181
+ - `--sender user@example.com`, `-f user@example.com`: Email address from the MAIL FROM command.
182
+ - `--helo hostname`, `-e hostname`: Hostname from the HELO/EHLO command.
183
+ - `--mta hostname`, `-m hostname`: Hostname of the server performing validations.
184
+ - `--dns-cache /path/to/dns.json`, `-n /path/to/dns.json`: Path to a DNS cache file.
185
+ - `--verbose`, `-v`: Enables verbose output.
156
186
 
157
- **Example**
187
+ **Note:** The canonicalization method (`c=` tag) for ARC sealing is always `relaxed/relaxed` and cannot be changed.
158
188
 
189
+ #### Example
190
+
191
+ ```bash
192
+ mailauth seal /path/to/message.eml --domain example.com --selector s1 --private-key /path/to/private.key --verbose
159
193
  ```
160
- $ mailauth seal /path/message.eml --domain kreata.ee --selector test --privateKey /path/private-rsa.pem --verbose
161
- Reading email message from /path/message.eml
162
- Signing domain: kreata.ee
163
- Key selector: test
194
+
195
+ **Sample Output:**
196
+
197
+ ```
198
+ Reading email message from /path/to/message.eml
199
+ Signing domain: example.com
200
+ Key selector: s1
164
201
  Canonicalization algorithm: relaxed/relaxed
165
202
  Hashing algorithm: rsa-sha256
166
- Signing time: 2020-12-03T23:04:41.082Z
203
+ Sealing time: 2023-03-15T12:05:00.000Z
167
204
  --------
168
- ARC-Seal: i=3; a=rsa-sha256; t=1607036681; cv=pass; d=kreata.ee; s=test;
205
+ ARC-Seal: i=3; a=rsa-sha256; t=1678884300; cv=pass; d=example.com; s=s1;
169
206
  b=Fo3hayVos+J77lzzgmr6J92gsUBKlPt/ZkoQt9ZCi514zy8+1WLvTHmI8CMUXAcegdcqP0NHt
170
207
  ...
171
208
  ```
172
209
 
173
210
  ### spf
174
211
 
175
- `spf` command takes an email address and an IP address and returns a JSON formatted SPF report.
212
+ The `spf` command checks the SPF (Sender Policy Framework) record for a given email address and IP address.
176
213
 
177
- ```
178
- $ mailauth spf [options]
214
+ #### Usage
215
+
216
+ ```bash
217
+ mailauth spf [options]
179
218
  ```
180
219
 
181
- Where
220
+ #### Options
182
221
 
183
- - **options** are option flags and arguments
222
+ - `--sender user@example.com`, `-f user@example.com`: Email address from the MAIL FROM command. **Required.**
223
+ - `--client-ip x.x.x.x`, `-i x.x.x.x`: IP address of the remote client that sent the email. **Required.**
224
+ - `--helo hostname`, `-e hostname`: Hostname from the HELO/EHLO command.
225
+ - `--mta hostname`, `-m hostname`: Hostname of the server performing the SPF check.
226
+ - `--dns-cache /path/to/dns.json`, `-n /path/to/dns.json`: Path to a DNS cache file.
227
+ - `--verbose`, `-v`: Enables verbose output.
228
+ - `--headers-only`, `-o`: Outputs only the SPF authentication header.
229
+ - `--max-lookups number`, `-x number`: Sets the maximum number of DNS lookups. Defaults to `10`.
230
+ - `--max-void-lookups number`, `-z number`: Sets the maximum number of void DNS lookups. Defaults to `2`.
184
231
 
185
- **Options**
232
+ #### Example
186
233
 
187
- - `--sender user@example.com` or `-f address` is the email address from the MAIL FROM command. Required.
188
- - `--client-ip x.x.x.x` or `-i x.x.x.x` is the IP of the remote client that sent the email. Required.
189
- - `--helo hostname` or `-e hostname` is the client hostname from the HELO/EHLO command. Used in some obscure SPF validation operations
190
- - `--mta hostname` or `-m hostname` is the server hostname doing the validation checks. Defaults to `os.hostname()`. Used in authentication headers.
191
- - `--dns-cache /path/to/dns.json` or `-n path` is the path to a file with cached DNS query responses. If this file is provided then no actual DNS requests are performed, only cached values from this file are used.
192
- - `--verbose` or `-v` if this flag is set then mailauth writes some debugging info to standard error
193
- - `--headers-only` or `-o` If set return SPF authentication header only. Default is to return a JSON structure.
194
- - `--max-lookups nr` or `-x nr` defines the allowed DNS lookup limit for SPF checks. Defaults to 10.
195
- - `--max-void-lookups nr` or `-z nr` defines the allowed DNS lookup limit for SPF checks. Defaults to 2.
234
+ ```bash
235
+ mailauth spf --verbose -f user@example.com -i 192.0.2.1
236
+ ```
196
237
 
197
- **Example**
238
+ **Sample Output:**
198
239
 
199
240
  ```
200
- $ mailauth spf --verbose -f andris@wildduck.email -i 217.146.76.20
201
- Checking SPF for andris@wildduck.email
241
+ Checking SPF for user@example.com
202
242
  Maximum DNS lookups: 10
203
243
  --------
204
- DNS query for TXT wildduck.email: [["v=spf1 mx a -all"]]
205
- DNS query for MX wildduck.email: [{"exchange":"mail.wildduck.email","priority":1}]
206
- DNS query for A mail.wildduck.email: ["217.146.76.20"]
244
+ DNS query for TXT example.com: [["v=spf1 include:_spf.example.com -all"]]
245
+ DNS query for TXT _spf.example.com: [["v=spf1 ip4:192.0.2.0/24 -all"]]
207
246
  {
208
- "domain": "wildduck.email",
209
- "client-ip": "217.146.76.20",
210
- ...
247
+ "domain": "example.com",
248
+ "client-ip": "192.0.2.1",
249
+ "result": "pass",
250
+ "..."
251
+ }
211
252
  ```
212
253
 
213
254
  ### vmc
214
255
 
215
- `vmc` command takes either the URL for a VMC file or a file path or both. It then verifies if the VMC resource is a valid file or not and exposes its contents.
256
+ The `vmc` command validates a Verified Mark Certificate (VMC) used in BIMI (Brand Indicators for Message Identification).
216
257
 
217
- ```
218
- $ mailauth vmc [options]
219
- ```
220
-
221
- Where
258
+ #### Usage
222
259
 
223
- - **options** are option flags and arguments
260
+ ```bash
261
+ mailauth vmc [options]
262
+ ```
224
263
 
225
- **Options**
264
+ #### Options
226
265
 
227
- - `--authority <url>` or `-a <url>` is the URL for the VMC resource
228
- - `--authorityPath <path>` or `-p <path>` is the cached file for the authority URL to avoid network requests
229
- - `--domain <domain>` or `-d <domain>` is the sender domain to compare the certificate against
266
+ - `--authority <url>`, `-a <url>`: URL of the VMC resource.
267
+ - `--authorityPath <path>`, `-p <path>`: Path to a local VMC file, used to avoid network requests.
268
+ - `--domain <domain>`, `-d <domain>`: Sender domain to validate against the certificate.
230
269
 
231
- **Example**
270
+ #### Example
232
271
 
233
- ```
234
- $ mailauth vmc -a https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem -d cnn.com
235
- {
236
- "url": "https://amplify.valimail.com/bimi/time-warner/yV3KRIg4nJW-cnn.pem",
237
- "success": true,
238
- "domainVerified": true,
239
- "vmc": {
240
- "mediaType": "image/svg+xml",
241
- "hashAlgo": "sha1",
242
- "hashValue": "ea8c81da633c66a16262134a78576cdf067638e9",
243
- "logoFile": "<2300B base64 encoded file>",
244
- "validHash": true,
245
- "certificate": {
246
- "subject": {
247
- "businessCategory": "Private Organization",
248
- "jurisdictionCountryName": "US",
249
- "jurisdictionStateOrProvinceName": "Delaware",
250
- "serialNumber": "2976730",
251
- "countryName": "US",
252
- "stateOrProvinceName": "Georgia",
253
- "localityName": "Atlanta",
254
- "street": "190 Marietta St NW",
255
- "organizationName": "Cable News Network, Inc.",
256
- "commonName": "Cable News Network, Inc.",
257
- "trademarkCountryOrRegionName": "US",
258
- "trademarkRegistration": "5817930"
259
- },
260
- "subjectAltName": [
261
- "cnn.com"
262
- ],
263
- "fingerprint": "17:B3:94:97:E6:6B:C8:6B:33:B8:0A:D2:F0:79:6B:08:A2:A6:84:BD",
264
- "serialNumber": "0821B8FE0A9CBC3BAC10DA08C088EEF4",
265
- "validFrom": "2021-08-12T00:00:00.000Z",
266
- "validTo": "2022-08-12T23:59:59.000Z",
267
- "issuer": {
268
- "countryName": "US",
269
- "organizationName": "DigiCert, Inc.",
270
- "commonName": "DigiCert Verified Mark RSA4096 SHA256 2021 CA1"
271
- }
272
- }
273
- }
274
- }
272
+ ```bash
273
+ mailauth vmc -a https://example.com/path/to/vmc.pem -d example.com
275
274
  ```
276
275
 
277
- If the certificate verification fails, then the logo contents are not returned.
276
+ **Sample Output:**
278
277
 
279
- ```
280
- $ mailauth vmc -p /path/to/random/cert-bundle.pem
278
+ ```json
281
279
  {
282
- "success": false,
283
- "error": {
284
- "message": "Self signed certificate in certificate chain",
285
- "details": {
286
- "certificate": {
287
- "subject": {
288
- "commonName": "postal.vmc.local",
289
- "organizationName": "Postal Systems OU.",
290
- "countryName": "EE"
291
- },
292
- "subjectAltName": [],
293
- "fingerprint": "CC:49:83:ED:3F:6B:77:45:5B:A5:3B:9E:EC:99:0E:A1:EF:D7:FF:97",
294
- "serialNumber": "B61FBFBA917B15D9",
295
- "validFrom": "2022-07-09T06:13:33.000Z",
296
- "validTo": "2023-07-09T06:13:33.000Z",
297
- "issuer": {
298
- "commonName": "postal.vmc.local",
299
- "organizationName": "Postal Systems OU.",
300
- "countryName": "EE"
280
+ "url": "https://example.com/path/to/vmc.pem",
281
+ "success": true,
282
+ "domainVerified": true,
283
+ "vmc": {
284
+ "mediaType": "image/svg+xml",
285
+ "hashAlgo": "sha256",
286
+ "hashValue": "abc123...",
287
+ "logoFile": "<Base64 encoded SVG>",
288
+ "validHash": true,
289
+ "type": "VMC",
290
+ "certificate": {
291
+ "subject": {
292
+ "commonName": "Example Inc.",
293
+ "markType": "Registered Mark",
294
+ "..."
295
+ },
296
+ "subjectAltName": ["example.com"],
297
+ "fingerprint": "12:34:56:78:9A:BC:DE:F0...",
298
+ "serialNumber": "0123456789ABCDEF",
299
+ "validFrom": "2023-01-01T00:00:00.000Z",
300
+ "validTo": "2024-01-01T23:59:59.000Z",
301
+ "issuer": {
302
+ "commonName": "Trusted CA"
303
+ "..."
304
+ }
301
305
  }
302
- }
303
- },
304
- "code": "SELF_SIGNED_CERT_IN_CHAIN"
305
- }
306
+ }
306
307
  }
307
308
  ```
308
309
 
309
- The embedded SVG file is also validated.
310
+ If validation fails, the output includes error details:
310
311
 
311
- ```
312
- $ mailauth vmc -p /path/to/vmc-with-invalid-svg.pem
312
+ ```json
313
313
  {
314
- "success": false,
315
- "error": {
316
- "message": "VMC logo SVG validation failed",
317
- "details": {
318
- "message": "Not a Tiny PS profile",
319
- "code": "INVALID_BASE_PROFILE"
320
- },
321
- "code": "SVG_VALIDATION_FAILED"
322
- }
314
+ "success": false,
315
+ "error": {
316
+ "message": "Self signed certificate in certificate chain",
317
+ "details": {
318
+ "..."
319
+ },
320
+ "code": "SELF_SIGNED_CERT_IN_CHAIN"
321
+ }
323
322
  }
324
323
  ```
325
324
 
326
325
  ### bodyhash
327
326
 
328
- `bodyhash` command takes an email message and calculates the body hash value for it
327
+ The `bodyhash` command computes the body hash value of an email message, which is used in DKIM signatures.
329
328
 
330
- ```
331
- $ mailauth bodyhash [options] [email]
329
+ #### Usage
330
+
331
+ ```bash
332
+ mailauth bodyhash [options] [email]
332
333
  ```
333
334
 
334
- Where
335
+ - **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
335
336
 
336
- - **options** are option flags and arguments
337
- - **email** is the path to EML formatted email message file. If not provided then email message is read from standard input
337
+ #### Options
338
338
 
339
- **Options**
339
+ - `--algo algorithm`, `-a algorithm`: Hashing algorithm (e.g., `sha256`). Defaults to `sha256`. Can also specify DKIM-style algorithms (e.g., `rsa-sha256`).
340
+ - `--canonicalization method`, `-c method`: Body canonicalization method (e.g., `relaxed`). Defaults to `relaxed`. Can use DKIM-style (e.g., `relaxed/relaxed`).
341
+ - `--body-length length`, `-l length`: Maximum length of the body to hash (`l=` tag).
342
+ - `--verbose`, `-v`: Enables verbose output.
340
343
 
341
- - `--algo sha256` or `-a sha256` is the signing algorithm. Defaults to "sha256". Can also use the a= tag format ("rsa-sha256").
342
- - `--canonicalization algo` or `-c algo` is the body canonicalization algorithm, defaults to "relaxed". Can also use the c= tag format ("relaxed/relaxed").
343
- - `--body-length 12345` or `-l 12345` is the maximum length of canonicalizated body to sign (l= tag)
344
+ #### Example
345
+
346
+ ```bash
347
+ mailauth bodyhash /path/to/message.eml -a sha1 --verbose
348
+ ```
344
349
 
345
- **Example**
350
+ **Sample Output:**
346
351
 
347
352
  ```
348
- $ mailauth bodyhash /path/message.eml -a sha1 --verbose
349
353
  Hashing algorithm: sha1
350
354
  Body canonicalization algorithm: relaxed
351
355
  --------
@@ -354,37 +358,67 @@ j+dD7whKXS1yDmyoWtvClYSyYiQ=
354
358
 
355
359
  ### license
356
360
 
357
- Display licenses for `mailauth` and included modules.
361
+ The `license` command displays the licenses for mailauth and its included modules.
358
362
 
359
- ```
360
- $ mailauth licenses
363
+ #### Usage
364
+
365
+ ```bash
366
+ mailauth license
361
367
  ```
362
368
 
363
- ## DNS cache file
369
+ #### Example
364
370
 
365
- In general you would use the `--dns-cache` option only when testing. This way you can provide different kind of DNS responses without actually setting up a DNS server and unlike when using real DNS you do not have to wait for the changes in the DNS server to propagate – whatever is in the provided cache file, is used for the DNS query responses.
371
+ ```bash
372
+ mailauth license
373
+ ```
366
374
 
367
- DNS cache file includes a JSON encoded object where main keys are the domain names (eg. `"_dmarc.example.com"`), sub keys are resource record types (eg. `"TXT"`) and values are the corresponding values as provided by the [dns module](https://nodejs.org/api/dns.html#dns_dns_resolvetxt_hostname_callback).
375
+ **Sample Output:**
368
376
 
369
- ```json
370
- {
371
- "full_domain_name": {
372
- "TXT": [["string1"]]
373
- }
374
- }
375
377
  ```
378
+ mailauth License: MIT License
379
+ Included Modules:
380
+ - module1: MIT License
381
+ - module2: Apache License 2.0
382
+ ...
383
+ ```
384
+
385
+ ## DNS Cache File
386
+
387
+ The `--dns-cache` option allows you to use a JSON-formatted DNS cache file for testing purposes. This avoids the need to set up a DNS server or wait for DNS propagation.
388
+
389
+ ### Format
390
+
391
+ The DNS cache file is a JSON object where:
376
392
 
377
- **Example**
393
+ - **Keys**: Fully qualified domain names (e.g., `"example.com"`).
394
+ - **Values**: Objects with DNS record types as keys (e.g., `"TXT"`, `"MX"`) and their corresponding values.
378
395
 
379
- This example provides SPF and DMARC policy records for "example.com":
396
+ **Example:**
380
397
 
381
398
  ```json
382
399
  {
383
400
  "example.com": {
384
- "TXT": [["v=spf1 include:_spf.google.com include:sendgrid.net", " include:servers.mcsv.net include:servers.outfunnel.com ip4:18.194.223.2 ~all"]]
401
+ "TXT": [["v=spf1 include:_spf.example.com -all"]],
402
+ "MX": [{ "exchange": "mail.example.com", "priority": 10 }]
385
403
  },
386
404
  "_dmarc.example.com": {
387
- "TXT": [["v=DMARC1; p=reject; sp=reject;"]]
405
+ "TXT": [["v=DMARC1; p=reject; rua=mailto:dmarc@example.com;"]]
388
406
  }
389
407
  }
390
408
  ```
409
+
410
+ ### Usage
411
+
412
+ Specify the DNS cache file using the `--dns-cache` option:
413
+
414
+ ```bash
415
+ mailauth report --dns-cache /path/to/dns-cache.json email.eml
416
+ ```
417
+
418
+ When this option is used, mailauth will not perform actual DNS queries but will use the data from the cache file instead.
419
+
420
+ ## License
421
+
422
+ &copy; 2020-2024 Postal Systems OÜ
423
+
424
+ Licensed under the [MIT License](LICENSE).
package/lib/arc/index.js CHANGED
@@ -78,6 +78,8 @@ const verifyAS = async (chain, opts) => {
78
78
  const signAS = async (chain, entry, signatureData) => {
79
79
  let { instance, algorithm, selector, signingDomain, bodyHash, cv, signTime, privateKey } = signatureData;
80
80
 
81
+ instance = instance || 1;
82
+
81
83
  const signAlgo = algorithm?.split('-').shift();
82
84
 
83
85
  signTime = signTime || new Date();
@@ -497,6 +499,8 @@ const createSeal = async (input, data) => {
497
499
  await dkimSigner.finalize();
498
500
  }
499
501
 
502
+ seal.i = seal.i || 1;
503
+
500
504
  const authResults = `ARC-Authentication-Results: i=${seal.i}; ${seal.authResults}`;
501
505
 
502
506
  // Step 2. Calculate ARC-Seal
@@ -266,7 +266,7 @@ class DkimSigner extends MessageParser {
266
266
  {},
267
267
  signatureData,
268
268
  {
269
- instance: this.arc?.instance, // ARC only
269
+ instance: 'instance' in this.arc && (this.arc.instance || 1), // ARC only
270
270
  algorithm,
271
271
  canonicalization: this.getCanonicalization(signatureData).canonicalization,
272
272
 
package/man/mailauth.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "MAILAUTH" "1" "November 2024" "v4.8.0" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "December 2024" "v4.8.2" "Mailauth Help"
2
2
  .SH "NAME"
3
3
  \fBmailauth\fR
4
4
  .QP
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailauth",
3
- "version": "4.8.0",
3
+ "version": "4.8.2",
4
4
  "description": "Email authentication library for Node.js",
5
5
  "main": "lib/mailauth.js",
6
6
  "scripts": {
@@ -38,21 +38,21 @@
38
38
  "eslint-config-nodemailer": "1.2.0",
39
39
  "eslint-config-prettier": "9.1.0",
40
40
  "js-yaml": "4.1.0",
41
- "license-report": "6.7.0",
41
+ "license-report": "6.7.1",
42
42
  "marked": "0.7.0",
43
43
  "marked-man": "0.7.0",
44
44
  "mbox-reader": "1.2.0",
45
- "mocha": "10.8.2"
45
+ "mocha": "11.0.1"
46
46
  },
47
47
  "dependencies": {
48
48
  "@postalsys/vmc": "1.1.0",
49
- "fast-xml-parser": "4.5.0",
49
+ "fast-xml-parser": "4.5.1",
50
50
  "ipaddr.js": "2.2.0",
51
51
  "joi": "17.13.3",
52
- "libmime": "5.3.5",
52
+ "libmime": "5.3.6",
53
53
  "nodemailer": "6.9.16",
54
54
  "punycode.js": "2.3.1",
55
- "tldts": "6.1.58",
55
+ "tldts": "6.1.68",
56
56
  "undici": "5.28.4",
57
57
  "yargs": "17.7.2"
58
58
  },