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 +14 -0
- package/bin/mailauth.js +68 -72
- package/cli.md +278 -244
- package/lib/arc/index.js +4 -0
- package/lib/dkim/dkim-signer.js +1 -1
- package/man/mailauth.1 +1 -1
- package/package.json +6 -6
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
|
|
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: '
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
-
'
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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: '
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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: '
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
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:
|
|
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: '
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
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
|
-
'
|
|
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('
|
|
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: '
|
|
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
|
-
|
|
1
|
+
# mailauth CLI Usage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
##
|
|
7
|
+
## Table of Contents
|
|
8
8
|
|
|
9
9
|
- [Installation](#installation)
|
|
10
|
-
- [Help](#help)
|
|
11
|
-
- [Available
|
|
12
|
-
- [report](#report)
|
|
13
|
-
- [sign](#sign)
|
|
14
|
-
- [seal](#seal)
|
|
15
|
-
- [spf](#spf)
|
|
16
|
-
- [vmc](#vmc)
|
|
17
|
-
- [bodyhash](#bodyhash)
|
|
18
|
-
- [license](#license)
|
|
19
|
-
- [DNS
|
|
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
|
-
|
|
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
|
-
-
|
|
29
|
-
- [
|
|
30
|
-
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
69
|
+
#### Usage
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
mailauth report [options] [email]
|
|
50
73
|
```
|
|
51
74
|
|
|
52
|
-
|
|
75
|
+
- **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
|
|
53
76
|
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
111
|
+
The `sign` command signs an email message using a DKIM signature.
|
|
85
112
|
|
|
86
|
-
|
|
87
|
-
|
|
113
|
+
#### Usage
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
mailauth sign [options] [email]
|
|
88
117
|
```
|
|
89
118
|
|
|
90
|
-
|
|
119
|
+
- **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
|
|
91
120
|
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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:
|
|
147
|
+
Signing time: 2023-03-15T12:00:00.000Z
|
|
118
148
|
--------
|
|
119
|
-
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
|
|
120
|
-
h=MIME-Version:
|
|
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
|
|
156
|
+
The `seal` command adds an ARC (Authenticated Received Chain) seal to an email message.
|
|
127
157
|
|
|
128
|
-
|
|
129
|
-
|
|
158
|
+
#### Usage
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
mailauth seal [options] [email]
|
|
130
162
|
```
|
|
131
163
|
|
|
132
|
-
|
|
164
|
+
- **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
|
|
133
165
|
|
|
134
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
203
|
+
Sealing time: 2023-03-15T12:05:00.000Z
|
|
167
204
|
--------
|
|
168
|
-
ARC-Seal: i=3; a=rsa-sha256; t=
|
|
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
|
|
212
|
+
The `spf` command checks the SPF (Sender Policy Framework) record for a given email address and IP address.
|
|
176
213
|
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
#### Usage
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
mailauth spf [options]
|
|
179
218
|
```
|
|
180
219
|
|
|
181
|
-
|
|
220
|
+
#### Options
|
|
182
221
|
|
|
183
|
-
-
|
|
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
|
-
|
|
232
|
+
#### Example
|
|
186
233
|
|
|
187
|
-
|
|
188
|
-
-
|
|
189
|
-
|
|
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
|
-
**
|
|
238
|
+
**Sample Output:**
|
|
198
239
|
|
|
199
240
|
```
|
|
200
|
-
|
|
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
|
|
205
|
-
DNS query for
|
|
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": "
|
|
209
|
-
"client-ip": "
|
|
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
|
|
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
|
-
|
|
260
|
+
```bash
|
|
261
|
+
mailauth vmc [options]
|
|
262
|
+
```
|
|
224
263
|
|
|
225
|
-
|
|
264
|
+
#### Options
|
|
226
265
|
|
|
227
|
-
- `--authority <url
|
|
228
|
-
- `--authorityPath <path
|
|
229
|
-
- `--domain <domain
|
|
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
|
-
|
|
270
|
+
#### Example
|
|
232
271
|
|
|
233
|
-
```
|
|
234
|
-
|
|
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
|
-
|
|
276
|
+
**Sample Output:**
|
|
278
277
|
|
|
279
|
-
```
|
|
280
|
-
$ mailauth vmc -p /path/to/random/cert-bundle.pem
|
|
278
|
+
```json
|
|
281
279
|
{
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
"
|
|
285
|
-
"
|
|
286
|
-
|
|
287
|
-
"
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
"
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
|
327
|
+
The `bodyhash` command computes the body hash value of an email message, which is used in DKIM signatures.
|
|
329
328
|
|
|
330
|
-
|
|
331
|
-
|
|
329
|
+
#### Usage
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
mailauth bodyhash [options] [email]
|
|
332
333
|
```
|
|
333
334
|
|
|
334
|
-
|
|
335
|
+
- **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
|
|
335
336
|
|
|
336
|
-
|
|
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
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
+
#### Example
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
mailauth bodyhash /path/to/message.eml -a sha1 --verbose
|
|
348
|
+
```
|
|
344
349
|
|
|
345
|
-
**
|
|
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
|
-
|
|
361
|
+
The `license` command displays the licenses for mailauth and its included modules.
|
|
358
362
|
|
|
359
|
-
|
|
360
|
-
|
|
363
|
+
#### Usage
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
mailauth license
|
|
361
367
|
```
|
|
362
368
|
|
|
363
|
-
|
|
369
|
+
#### Example
|
|
364
370
|
|
|
365
|
-
|
|
371
|
+
```bash
|
|
372
|
+
mailauth license
|
|
373
|
+
```
|
|
366
374
|
|
|
367
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
396
|
+
**Example:**
|
|
380
397
|
|
|
381
398
|
```json
|
|
382
399
|
{
|
|
383
400
|
"example.com": {
|
|
384
|
-
"TXT": [["v=spf1 include:_spf.
|
|
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;
|
|
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
|
+
© 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
|
package/lib/dkim/dkim-signer.js
CHANGED
|
@@ -266,7 +266,7 @@ class DkimSigner extends MessageParser {
|
|
|
266
266
|
{},
|
|
267
267
|
signatureData,
|
|
268
268
|
{
|
|
269
|
-
instance: this.arc
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailauth",
|
|
3
|
-
"version": "4.8.
|
|
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.
|
|
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": "
|
|
45
|
+
"mocha": "11.0.1"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@postalsys/vmc": "1.1.0",
|
|
49
|
-
"fast-xml-parser": "4.5.
|
|
49
|
+
"fast-xml-parser": "4.5.1",
|
|
50
50
|
"ipaddr.js": "2.2.0",
|
|
51
51
|
"joi": "17.13.3",
|
|
52
|
-
"libmime": "5.3.
|
|
52
|
+
"libmime": "5.3.6",
|
|
53
53
|
"nodemailer": "6.9.16",
|
|
54
54
|
"punycode.js": "2.3.1",
|
|
55
|
-
"tldts": "6.1.
|
|
55
|
+
"tldts": "6.1.68",
|
|
56
56
|
"undici": "5.28.4",
|
|
57
57
|
"yargs": "17.7.2"
|
|
58
58
|
},
|