mailauth 4.7.3 → 4.8.1

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/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);