mailauth 6.0.0 → 7.1.0
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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +113 -39
- package/bin/mailauth.js +15 -6
- package/cli.md +9 -3
- package/docs/arc.md +126 -43
- package/docs/bimi.md +54 -37
- package/docs/dkim.md +79 -40
- package/docs/dmarc.md +80 -33
- package/docs/mta-sts.md +140 -42
- package/docs/spf.md +69 -4
- package/index.d.ts +508 -43
- package/lib/arc/index.d.ts +10 -8
- package/lib/arc/index.js +505 -124
- package/lib/bimi/index.js +162 -81
- package/lib/commands/report.js +3 -1
- package/lib/commands/seal.js +35 -5
- package/lib/commands/sign.js +8 -1
- package/lib/commands/spf.js +8 -0
- package/lib/dkim/body/relaxed.js +6 -5
- package/lib/dkim/body/simple.js +1 -3
- package/lib/dkim/dkim-signer.js +219 -33
- package/lib/dkim/dkim-verifier.js +577 -231
- package/lib/dkim/header/relaxed.js +6 -43
- package/lib/dkim/header/signature-opts.js +87 -0
- package/lib/dkim/header/simple.js +6 -43
- package/lib/dkim/message-parser.js +18 -4
- package/lib/dkim/sign.d.ts +13 -7
- package/lib/dkim/sign.js +18 -2
- package/lib/dkim/syntax.js +31 -0
- package/lib/dkim/verify.js +1 -0
- package/lib/dmarc/author-domain.js +181 -0
- package/lib/dmarc/get-dmarc-record.js +154 -47
- package/lib/dmarc/index.d.ts +29 -2
- package/lib/dmarc/verify.js +120 -44
- package/lib/mailauth.js +77 -36
- package/lib/mta-sts.d.ts +77 -11
- package/lib/mta-sts.js +497 -182
- package/lib/parse-dkim-headers.js +376 -191
- package/lib/parse-received.js +104 -32
- package/lib/spf/index.d.ts +37 -1
- package/lib/spf/index.js +260 -28
- package/lib/spf/macro.js +50 -13
- package/lib/spf/spf-verify.js +440 -204
- package/lib/spf/syntax.js +366 -0
- package/lib/tools.js +522 -116
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -55,6 +55,13 @@ export interface AuthenticateOptions {
|
|
|
55
55
|
*/
|
|
56
56
|
minBitLength?: number;
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* If true, rsa-sha1 DKIM signatures get `policy` (weak-algorithm) as in strict mode and are
|
|
60
|
+
* not counted for DMARC, while every other check keeps the lenient default. Verification
|
|
61
|
+
* only (default: false)
|
|
62
|
+
*/
|
|
63
|
+
rejectRsaSha1?: boolean;
|
|
64
|
+
|
|
58
65
|
/**
|
|
59
66
|
* Custom DNS resolver function
|
|
60
67
|
*/
|
|
@@ -84,6 +91,24 @@ export interface AuthenticateOptions {
|
|
|
84
91
|
* ARC sealing options
|
|
85
92
|
*/
|
|
86
93
|
seal?: ARCSealOptions;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Follow the RFCs exactly instead of the lenient defaults (default: false). Passed on
|
|
97
|
+
* to every check (DKIM, SPF, ARC, DMARC, BIMI and ARC sealing), BIMI only uses it for the
|
|
98
|
+
* format of its Authentication-Results entry. In strict mode:
|
|
99
|
+
*
|
|
100
|
+
* - rsa-sha1 DKIM signatures are reported as `dkim=policy` (`policy.dkim-rules=weak-algorithm`)
|
|
101
|
+
* - DKIM-Signature and key record syntax is validated (RFC 6376 sections 3.2, 3.5, 3.6.1, 6.1.1)
|
|
102
|
+
* - a body hash mismatch is reported as `dkim=fail`, and a signature that can not be
|
|
103
|
+
* processed as `dkim=neutral` instead of being left out
|
|
104
|
+
* - DKIM results report `header.d` and the AUID as `header.i`
|
|
105
|
+
* - email identities in Authentication-Results use the `local-part@domain` form of RFC 8601 section 2.2
|
|
106
|
+
* - Authentication-Results is placed above Received-SPF (RFC 8601 section 5)
|
|
107
|
+
*
|
|
108
|
+
* What the lenient default accepts and strict mode would reject is listed in the
|
|
109
|
+
* `warnings` array of the affected result. It is never written into the headers
|
|
110
|
+
*/
|
|
111
|
+
strict?: boolean;
|
|
87
112
|
}
|
|
88
113
|
|
|
89
114
|
/**
|
|
@@ -111,20 +136,47 @@ export interface ARCSealOptions {
|
|
|
111
136
|
canonicalization?: string;
|
|
112
137
|
|
|
113
138
|
/**
|
|
114
|
-
* Signing algorithm
|
|
115
|
-
*
|
|
139
|
+
* Signing algorithm, 'rsa-sha256' or 'ed25519-sha256'. Follows the private key type when
|
|
140
|
+
* not set. Used for both the ARC-Message-Signature and the ARC-Seal
|
|
116
141
|
*/
|
|
117
142
|
algorithm?: string;
|
|
118
143
|
|
|
119
144
|
/**
|
|
120
|
-
*
|
|
145
|
+
* Chain validation status for the ARC-Seal cv= tag: 'none', 'pass' or 'fail' (compared
|
|
146
|
+
* case-insensitively). Defaults to 'none' for the first set and is required for any later
|
|
147
|
+
* set. `authenticate()` sets it from the ARC validation result
|
|
121
148
|
*/
|
|
122
|
-
|
|
149
|
+
cv?: 'none' | 'pass' | 'fail' | string;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* ARC instance (i= tag), 1 to 50. Defaults to one more than the highest instance on the
|
|
153
|
+
* message. An instance that already exists is refused, and one that leaves a gap is
|
|
154
|
+
* refused in strict mode (the default mode seals it and adds an `arc-instance-gap` warning)
|
|
155
|
+
*/
|
|
156
|
+
i?: number;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Authentication-Results payload for the ARC-Authentication-Results header (the part after
|
|
160
|
+
* "i=N;"). `authenticate()` sets it from its own results
|
|
161
|
+
*/
|
|
162
|
+
authResults?: string;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Header fields to include in the ARC-Message-Signature, as an array or a colon separated
|
|
166
|
+
* string. Defaults to DKIM-Signature and the default DKIM header list. ARC header fields and
|
|
167
|
+
* Authentication-Results are never included (RFC 8617 section 4.1.2)
|
|
168
|
+
*/
|
|
169
|
+
headerList?: string[] | string;
|
|
123
170
|
|
|
124
171
|
/**
|
|
125
172
|
* Signing timestamp
|
|
126
173
|
*/
|
|
127
174
|
signTime?: Date | string | number;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Follow the RFCs exactly instead of the lenient defaults (default: false)
|
|
178
|
+
*/
|
|
179
|
+
strict?: boolean;
|
|
128
180
|
}
|
|
129
181
|
|
|
130
182
|
/**
|
|
@@ -132,7 +184,8 @@ export interface ARCSealOptions {
|
|
|
132
184
|
*/
|
|
133
185
|
export interface AuthPolicy {
|
|
134
186
|
/**
|
|
135
|
-
* DKIM policy rules
|
|
187
|
+
* DKIM policy rules: 'weak-key' when the key is shorter than minBitLength, 'weak-algorithm'
|
|
188
|
+
* for an rsa-sha1 signature in strict mode
|
|
136
189
|
*/
|
|
137
190
|
'dkim-rules'?: string;
|
|
138
191
|
|
|
@@ -146,7 +199,10 @@ export interface AuthPolicy {
|
|
|
146
199
|
* Status result from authentication checks
|
|
147
200
|
*/
|
|
148
201
|
export interface AuthStatus {
|
|
149
|
-
|
|
202
|
+
/**
|
|
203
|
+
* The result keyword. `temperr` is deprecated, BIMI reports `temperror` now
|
|
204
|
+
*/
|
|
205
|
+
result: 'pass' | 'fail' | 'neutral' | 'none' | 'temperror' | 'temperr' | 'permerror' | 'policy' | 'softfail' | 'skipped' | 'declined';
|
|
150
206
|
comment?: string;
|
|
151
207
|
header?: Record<string, any>;
|
|
152
208
|
smtp?: {
|
|
@@ -156,25 +212,94 @@ export interface AuthStatus {
|
|
|
156
212
|
policy?: AuthPolicy;
|
|
157
213
|
}
|
|
158
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Lax acceptance markers of a DKIM result: what the default mode accepted and strict mode
|
|
217
|
+
* would reject
|
|
218
|
+
*
|
|
219
|
+
* - `rsa-sha1`: the signature uses rsa-sha1 (RFC 8301 section 3.1)
|
|
220
|
+
* - `tag-syntax`: the signature is not valid tag-list syntax (duplicate tags, upper case tag
|
|
221
|
+
* names, a malformed t=, x= or l= value, or a d= or s= that is not a domain name)
|
|
222
|
+
* - `missing-v`, `missing-h` (and the other `missing-*`): a required tag is missing
|
|
223
|
+
* - `invalid-v`: v= is not "1"
|
|
224
|
+
* - `identity-domain`: the i= domain is not d= or its subdomain, or it is a subdomain while
|
|
225
|
+
* the key has t=s
|
|
226
|
+
* - `invalid-expiration`: x= is equal to t=
|
|
227
|
+
* - `query-method`: q= does not list dns/txt
|
|
228
|
+
* - `key-syntax`: the key record is not valid tag-list syntax
|
|
229
|
+
* - `key-v-syntax`: the key record v= is not the first tag, or not exactly "DKIM1"
|
|
230
|
+
* - `key-type-inferred`: an ed25519 key was found from its length, without k=ed25519
|
|
231
|
+
*/
|
|
232
|
+
export type DKIMWarning =
|
|
233
|
+
| 'rsa-sha1'
|
|
234
|
+
| 'tag-syntax'
|
|
235
|
+
| 'missing-v'
|
|
236
|
+
| 'missing-a'
|
|
237
|
+
| 'missing-b'
|
|
238
|
+
| 'missing-bh'
|
|
239
|
+
| 'missing-d'
|
|
240
|
+
| 'missing-h'
|
|
241
|
+
| 'missing-s'
|
|
242
|
+
| 'invalid-v'
|
|
243
|
+
| 'identity-domain'
|
|
244
|
+
| 'invalid-expiration'
|
|
245
|
+
| 'query-method'
|
|
246
|
+
| 'key-syntax'
|
|
247
|
+
| 'key-v-syntax'
|
|
248
|
+
| 'key-type-inferred';
|
|
249
|
+
|
|
159
250
|
/**
|
|
160
251
|
* DKIM verification result for a single signature
|
|
161
252
|
*/
|
|
162
253
|
export interface DKIMResult {
|
|
163
254
|
/**
|
|
164
|
-
* Signature identifier
|
|
255
|
+
* Signature identifier, the sha256 hash of the signature value
|
|
165
256
|
*/
|
|
166
257
|
id?: string;
|
|
167
258
|
|
|
168
259
|
/**
|
|
169
|
-
* Signing domain
|
|
260
|
+
* Signing domain (d= tag)
|
|
170
261
|
*/
|
|
171
|
-
signingDomain
|
|
262
|
+
signingDomain?: string;
|
|
172
263
|
|
|
173
264
|
/**
|
|
174
|
-
* Key selector
|
|
265
|
+
* Key selector (s= tag)
|
|
175
266
|
*/
|
|
176
267
|
selector?: string;
|
|
177
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Signature value (b= tag)
|
|
271
|
+
*/
|
|
272
|
+
signature?: string;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Signature algorithm (a= tag), eg. 'rsa-sha256'
|
|
276
|
+
*/
|
|
277
|
+
algo?: string;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Canonicalization (c= tag), eg. 'relaxed/relaxed'
|
|
281
|
+
*/
|
|
282
|
+
format?: string;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Body hash calculated for the message
|
|
286
|
+
*/
|
|
287
|
+
bodyHash?: string;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Body hash from the bh= tag
|
|
291
|
+
*/
|
|
292
|
+
bodyHashExpecting?: string;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Signed header fields and the canonicalized header data (base64)
|
|
296
|
+
*/
|
|
297
|
+
signingHeaders?: {
|
|
298
|
+
keys: string;
|
|
299
|
+
headers: string[];
|
|
300
|
+
canonicalizedHeader: string;
|
|
301
|
+
};
|
|
302
|
+
|
|
178
303
|
/**
|
|
179
304
|
* Verification status
|
|
180
305
|
*/
|
|
@@ -190,6 +315,18 @@ export interface DKIMResult {
|
|
|
190
315
|
* Number of body bytes left unsigned by an l= tag
|
|
191
316
|
*/
|
|
192
317
|
underSized?: number;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* True when the key record has the t=y flag, the signing domain is testing DKIM
|
|
321
|
+
* (RFC 6376 section 3.6.1)
|
|
322
|
+
*/
|
|
323
|
+
testing?: boolean;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* What the default mode accepted and strict mode would reject. Only present when
|
|
327
|
+
* there is something to report, never written into the Authentication-Results text
|
|
328
|
+
*/
|
|
329
|
+
warnings?: DKIMWarning[];
|
|
193
330
|
};
|
|
194
331
|
|
|
195
332
|
/**
|
|
@@ -198,22 +335,82 @@ export interface DKIMResult {
|
|
|
198
335
|
info: string;
|
|
199
336
|
|
|
200
337
|
/**
|
|
201
|
-
*
|
|
338
|
+
* Signing time from the t= tag, as an ISO string
|
|
339
|
+
*/
|
|
340
|
+
signTime?: string | null;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Expiration time from the x= tag, as an ISO string
|
|
344
|
+
*/
|
|
345
|
+
expiresAfter?: string | null;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* False when t= is in the future or x= has passed
|
|
349
|
+
*/
|
|
350
|
+
signatureTimeValid?: boolean;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Size of the message body in bytes
|
|
354
|
+
*/
|
|
355
|
+
sourceBodyLength?: number;
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Number of canonicalized body bytes covered by the signature
|
|
359
|
+
*/
|
|
360
|
+
canonBodyLength?: number;
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Number of canonicalized body bytes in total
|
|
364
|
+
*/
|
|
365
|
+
canonBodyLengthTotal?: number;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* True when the signature has an l= tag
|
|
369
|
+
*/
|
|
370
|
+
canonBodyLengthLimited?: boolean;
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* The l= value
|
|
374
|
+
*/
|
|
375
|
+
canonBodyLengthLimit?: number;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Where the MIME structure starts in the body, for multipart messages
|
|
379
|
+
*/
|
|
380
|
+
mimeStructureStart?: number;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Public key in PEM format
|
|
384
|
+
*/
|
|
385
|
+
publicKey?: string;
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* RSA key size in bits
|
|
389
|
+
*/
|
|
390
|
+
modulusLength?: number;
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Key record as found in DNS
|
|
394
|
+
*/
|
|
395
|
+
rr?: string;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* @deprecated Never set, use `algo`
|
|
202
399
|
*/
|
|
203
400
|
algorithm?: string;
|
|
204
401
|
|
|
205
402
|
/**
|
|
206
|
-
*
|
|
403
|
+
* @deprecated Never set, use `format`
|
|
207
404
|
*/
|
|
208
405
|
canonicalization?: string;
|
|
209
406
|
|
|
210
407
|
/**
|
|
211
|
-
*
|
|
408
|
+
* @deprecated Never set, use `signTime`
|
|
212
409
|
*/
|
|
213
410
|
signingTime?: Date;
|
|
214
411
|
|
|
215
412
|
/**
|
|
216
|
-
*
|
|
413
|
+
* @deprecated Never set, use `expiresAfter`
|
|
217
414
|
*/
|
|
218
415
|
expiration?: Date;
|
|
219
416
|
}
|
|
@@ -223,10 +420,16 @@ export interface DKIMResult {
|
|
|
223
420
|
*/
|
|
224
421
|
export interface DKIMVerifyResult {
|
|
225
422
|
/**
|
|
226
|
-
*
|
|
423
|
+
* Addresses from the From header, as full addr-specs (the domain is what follows the last
|
|
424
|
+
* "@"). The mailboxes of a group (RFC 6854) are included
|
|
227
425
|
*/
|
|
228
426
|
headerFrom: string[];
|
|
229
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Number of From header fields in the message. RFC 5322 allows only one
|
|
430
|
+
*/
|
|
431
|
+
fromFields: number;
|
|
432
|
+
|
|
230
433
|
/**
|
|
231
434
|
* Domain from Return-Path header
|
|
232
435
|
*/
|
|
@@ -242,8 +445,8 @@ export interface DKIMVerifyResult {
|
|
|
242
445
|
* Access with result.headers or Object.getOwnPropertyDescriptor()
|
|
243
446
|
*/
|
|
244
447
|
readonly headers?: {
|
|
245
|
-
parsed: Array<{ key: string;
|
|
246
|
-
|
|
448
|
+
parsed: Array<{ key: string; casedKey: string; line: Buffer }>;
|
|
449
|
+
original: Buffer;
|
|
247
450
|
};
|
|
248
451
|
|
|
249
452
|
/**
|
|
@@ -312,6 +515,18 @@ export interface SPFResult {
|
|
|
312
515
|
void: number;
|
|
313
516
|
subqueries: Record<string, number>;
|
|
314
517
|
};
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Explanation string published by the domain owner with the "exp" modifier
|
|
521
|
+
* (RFC 7208 section 6.2). Only set for a "fail" result. This is third party text
|
|
522
|
+
*/
|
|
523
|
+
explanation?: string;
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Set in the default mode when something was accepted that strict mode would have
|
|
527
|
+
* rejected: "syntax-error", "void-lookup-limit" or "non-ascii-local-part"
|
|
528
|
+
*/
|
|
529
|
+
warnings?: string[];
|
|
315
530
|
}
|
|
316
531
|
|
|
317
532
|
/**
|
|
@@ -354,7 +569,18 @@ export interface ARCResult {
|
|
|
354
569
|
* Why sealing failed, when a seal was requested but could not be created.
|
|
355
570
|
* The message is left unsealed in that case
|
|
356
571
|
*/
|
|
357
|
-
sealErrors?:
|
|
572
|
+
sealErrors?: ARCSealError[];
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* What the default mode accepted in the chain that strict mode would reject, for example
|
|
576
|
+
* 'arc-tag-syntax' (a tag-list syntax error or a tag name that is not lower case),
|
|
577
|
+
* 'arc-instance-syntax' (an i= value that is not 1*2DIGIT, an ARC-Authentication-Results
|
|
578
|
+
* whose i= is not first, or an ARC header field that was ignored for its i= value),
|
|
579
|
+
* 'ams-c-default' (an ARC-Message-Signature without c= that only verified with
|
|
580
|
+
* relaxed/relaxed), and the key record warnings of the DKIM result. Not set for a failing
|
|
581
|
+
* chain, and never written into the headers
|
|
582
|
+
*/
|
|
583
|
+
warnings?: string[];
|
|
358
584
|
|
|
359
585
|
/**
|
|
360
586
|
* ARC chain entries (non-enumerable property)
|
|
@@ -373,10 +599,16 @@ export interface DMARCResult {
|
|
|
373
599
|
domain: string;
|
|
374
600
|
|
|
375
601
|
/**
|
|
376
|
-
* DMARC policy ('none', 'quarantine', 'reject')
|
|
602
|
+
* Effective DMARC policy ('none', 'quarantine', 'reject'): p for the author domain's own
|
|
603
|
+
* record, np or sp for an inherited one, one level lower with t=y
|
|
377
604
|
*/
|
|
378
605
|
policy: string;
|
|
379
606
|
|
|
607
|
+
/**
|
|
608
|
+
* Domain whose DMARC record the policy was taken from
|
|
609
|
+
*/
|
|
610
|
+
policyDomain?: string;
|
|
611
|
+
|
|
380
612
|
/**
|
|
381
613
|
* Policy for organizational domain
|
|
382
614
|
*/
|
|
@@ -388,9 +620,14 @@ export interface DMARCResult {
|
|
|
388
620
|
sp: string;
|
|
389
621
|
|
|
390
622
|
/**
|
|
391
|
-
*
|
|
623
|
+
* Policy for non-existent subdomains, if published
|
|
392
624
|
*/
|
|
393
|
-
|
|
625
|
+
np?: string;
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* True when the record has t=y, in which case policy is one level below the published one
|
|
629
|
+
*/
|
|
630
|
+
testMode: boolean;
|
|
394
631
|
|
|
395
632
|
/**
|
|
396
633
|
* DMARC DNS record
|
|
@@ -400,7 +637,26 @@ export interface DMARCResult {
|
|
|
400
637
|
/**
|
|
401
638
|
* Verification status
|
|
402
639
|
*/
|
|
403
|
-
status: AuthStatus
|
|
640
|
+
status: AuthStatus & {
|
|
641
|
+
header?: {
|
|
642
|
+
/**
|
|
643
|
+
* Author Domain
|
|
644
|
+
*/
|
|
645
|
+
from?: string;
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Domain of the DMARC record, omitted in strict mode (not a registered property for dmarc)
|
|
649
|
+
*/
|
|
650
|
+
d?: string;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
policy?: {
|
|
654
|
+
/**
|
|
655
|
+
* The policy that was applied (RFC 8601 section 2.7.2 policy.dmarc)
|
|
656
|
+
*/
|
|
657
|
+
dmarc?: string;
|
|
658
|
+
};
|
|
659
|
+
};
|
|
404
660
|
|
|
405
661
|
/**
|
|
406
662
|
* Alignment results
|
|
@@ -438,6 +694,12 @@ export interface DMARCResult {
|
|
|
438
694
|
* Error message if verification failed
|
|
439
695
|
*/
|
|
440
696
|
error?: string;
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* What the default mode accepted and strict mode would reject, only present when there
|
|
700
|
+
* is something to report
|
|
701
|
+
*/
|
|
702
|
+
warnings?: string[];
|
|
441
703
|
}
|
|
442
704
|
|
|
443
705
|
/**
|
|
@@ -445,9 +707,12 @@ export interface DMARCResult {
|
|
|
445
707
|
*/
|
|
446
708
|
export interface BIMIResult {
|
|
447
709
|
/**
|
|
448
|
-
* Verification status
|
|
710
|
+
* Verification status. `declined` means the domain published a declination record,
|
|
711
|
+
* `temperror` a DNS failure. `temperr` is deprecated and no longer reported
|
|
449
712
|
*/
|
|
450
|
-
status: AuthStatus
|
|
713
|
+
status: AuthStatus & {
|
|
714
|
+
result: 'pass' | 'none' | 'fail' | 'skipped' | 'declined' | 'temperror' | 'temperr';
|
|
715
|
+
};
|
|
451
716
|
|
|
452
717
|
/**
|
|
453
718
|
* BIMI DNS record
|
|
@@ -484,7 +749,8 @@ export interface ReceivedChainEntry {
|
|
|
484
749
|
value: string;
|
|
485
750
|
|
|
486
751
|
/**
|
|
487
|
-
*
|
|
752
|
+
* The comments after the value, joined with spaces. Usually the TCP-info of
|
|
753
|
+
* RFC 5321 section 4.4, whose last address literal is the connecting IP address
|
|
488
754
|
*/
|
|
489
755
|
comment?: string;
|
|
490
756
|
};
|
|
@@ -581,6 +847,13 @@ export interface AuthenticateResult {
|
|
|
581
847
|
* Combined authentication headers to prepend to message
|
|
582
848
|
*/
|
|
583
849
|
headers: string;
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Why DMARC was not evaluated when `dmarc` is false because the From header has no
|
|
853
|
+
* domain or more than one (RFC 9989 section 5.3.1). Such a message can be treated as
|
|
854
|
+
* suspicious (RFC 9989 section 11.5)
|
|
855
|
+
*/
|
|
856
|
+
dmarcSkipReason?: 'no-author-domain' | 'multiple-author-domains' | 'invalid-author-domain' | 'multiple-from-fields';
|
|
584
857
|
}
|
|
585
858
|
|
|
586
859
|
/**
|
|
@@ -622,16 +895,17 @@ export interface DKIMSignOptions {
|
|
|
622
895
|
canonicalization?: string;
|
|
623
896
|
|
|
624
897
|
/**
|
|
625
|
-
* Signing algorithm
|
|
626
|
-
* Supported: 'rsa-sha256', 'rsa-sha1'
|
|
898
|
+
* Signing algorithm, defaults to 'rsa-sha256' or 'ed25519-sha256' depending on the key type.
|
|
899
|
+
* Supported: 'rsa-sha256', 'ed25519-sha256' and 'rsa-sha1'. rsa-sha1 is historic
|
|
900
|
+
* (RFC 8301): it adds the 'rsa-sha1' warning, and strict mode refuses it
|
|
627
901
|
*/
|
|
628
902
|
algorithm?: string;
|
|
629
903
|
|
|
630
904
|
/**
|
|
631
|
-
*
|
|
632
|
-
*
|
|
905
|
+
* Header fields to sign, as an array or a colon separated string. Default includes
|
|
906
|
+
* From, Subject, Date, To, etc. Set per signature in `signatureData` or for all of them
|
|
633
907
|
*/
|
|
634
|
-
headerList?: string[];
|
|
908
|
+
headerList?: string[] | string;
|
|
635
909
|
|
|
636
910
|
/**
|
|
637
911
|
* Signing timestamp (defaults to current time)
|
|
@@ -649,7 +923,7 @@ export interface DKIMSignOptions {
|
|
|
649
923
|
maxBodyLength?: number;
|
|
650
924
|
|
|
651
925
|
/**
|
|
652
|
-
* Identity (i= tag)
|
|
926
|
+
* Identity (i= tag), an address whose domain is the signing domain or its subdomain
|
|
653
927
|
*/
|
|
654
928
|
identity?: string;
|
|
655
929
|
|
|
@@ -657,6 +931,15 @@ export interface DKIMSignOptions {
|
|
|
657
931
|
* Multiple signature configurations
|
|
658
932
|
*/
|
|
659
933
|
signatureData?: DKIMSignOptions[];
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Follow the RFCs exactly (default: false). Strict mode refuses rsa-sha1, RSA keys
|
|
937
|
+
* shorter than 1024 bits, an expiration that is not after the signing time, a signing
|
|
938
|
+
* time that does not fit into t=, and a d=, s= or i= that is not valid RFC 6376 syntax.
|
|
939
|
+
* The default mode signs these and lists them in `warnings`. A d=, s= or identity that
|
|
940
|
+
* would break out of its tag is refused in both modes
|
|
941
|
+
*/
|
|
942
|
+
strict?: boolean;
|
|
660
943
|
}
|
|
661
944
|
|
|
662
945
|
/**
|
|
@@ -829,12 +1112,43 @@ export interface ARCSigningData {
|
|
|
829
1112
|
privateKey?: string | Buffer;
|
|
830
1113
|
}
|
|
831
1114
|
|
|
1115
|
+
/**
|
|
1116
|
+
* A signature that could not be created
|
|
1117
|
+
*/
|
|
1118
|
+
export interface DKIMSignError {
|
|
1119
|
+
/**
|
|
1120
|
+
* Why the signature was not created. `err.code` is eg. 'ENOFROM', 'EINVALIDALGO',
|
|
1121
|
+
* 'EINVALIDTYPE', 'ESHORTKEY', 'EINVALIDDOMAIN', 'EINVALIDSELECTOR', 'EINVALIDIDENTITY',
|
|
1122
|
+
* 'EINVALIDTIME', 'EINVALIDCANON' or 'EINVALIDINSTANCE'
|
|
1123
|
+
*/
|
|
1124
|
+
err: Error & { code?: string };
|
|
1125
|
+
|
|
1126
|
+
type?: 'DKIM' | 'ARC';
|
|
1127
|
+
selector?: string;
|
|
1128
|
+
signingDomain?: string;
|
|
1129
|
+
algorithm?: string;
|
|
1130
|
+
canonicalization?: string;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* What the default mode signed although strict mode would have refused it
|
|
1135
|
+
*
|
|
1136
|
+
* - `rsa-sha1`: signed with rsa-sha1
|
|
1137
|
+
* - `weak-key`: signed with an RSA key shorter than 1024 bits
|
|
1138
|
+
* - `invalid-expiration`: the expiration is not after the signing time, or does not fit into x=
|
|
1139
|
+
* - `invalid-signtime`: the signing time does not fit into t=, which was left out
|
|
1140
|
+
* - `d-syntax`, `s-syntax`: d= or s= is not valid RFC 6376 syntax
|
|
1141
|
+
* - `identity-domain`: the identity domain is not the signing domain or its subdomain
|
|
1142
|
+
*/
|
|
1143
|
+
export type DKIMSignWarning = 'rsa-sha1' | 'weak-key' | 'invalid-expiration' | 'invalid-signtime' | 'd-syntax' | 's-syntax' | 'identity-domain';
|
|
1144
|
+
|
|
832
1145
|
/**
|
|
833
1146
|
* DKIM signing result
|
|
834
1147
|
*/
|
|
835
1148
|
export interface DKIMSignResult {
|
|
836
1149
|
/**
|
|
837
|
-
* DKIM-Signature header(s) to prepend to message
|
|
1150
|
+
* DKIM-Signature header(s) to prepend to message, each ending with a CRLF. An empty
|
|
1151
|
+
* string when no signature was created
|
|
838
1152
|
*/
|
|
839
1153
|
signatures: string;
|
|
840
1154
|
|
|
@@ -844,9 +1158,14 @@ export interface DKIMSignResult {
|
|
|
844
1158
|
arc?: ARCSigningData;
|
|
845
1159
|
|
|
846
1160
|
/**
|
|
847
|
-
*
|
|
1161
|
+
* The signatures that could not be created, and why
|
|
848
1162
|
*/
|
|
849
|
-
errors:
|
|
1163
|
+
errors: DKIMSignError[];
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* What was signed although strict mode would have refused it
|
|
1167
|
+
*/
|
|
1168
|
+
warnings: DKIMSignWarning[];
|
|
850
1169
|
}
|
|
851
1170
|
|
|
852
1171
|
/**
|
|
@@ -871,9 +1190,14 @@ export class DkimSignStream extends Transform {
|
|
|
871
1190
|
constructor(options: DKIMSignOptions);
|
|
872
1191
|
|
|
873
1192
|
/**
|
|
874
|
-
*
|
|
1193
|
+
* The signatures that could not be created, set when the stream ends
|
|
875
1194
|
*/
|
|
876
|
-
errors:
|
|
1195
|
+
errors: DKIMSignError[] | null;
|
|
1196
|
+
|
|
1197
|
+
/**
|
|
1198
|
+
* What was signed although strict mode would have refused it, set when the stream ends
|
|
1199
|
+
*/
|
|
1200
|
+
warnings: DKIMSignWarning[] | null;
|
|
877
1201
|
}
|
|
878
1202
|
|
|
879
1203
|
// ============================================================================
|
|
@@ -899,6 +1223,12 @@ export interface DKIMVerifyOptions {
|
|
|
899
1223
|
*/
|
|
900
1224
|
minBitLength?: number;
|
|
901
1225
|
|
|
1226
|
+
/**
|
|
1227
|
+
* If true, rsa-sha1 signatures get `policy` (weak-algorithm) as in strict mode, while every
|
|
1228
|
+
* other check keeps the lenient default (default: false)
|
|
1229
|
+
*/
|
|
1230
|
+
rejectRsaSha1?: boolean;
|
|
1231
|
+
|
|
902
1232
|
/**
|
|
903
1233
|
* Current time for signature expiration checks
|
|
904
1234
|
*/
|
|
@@ -908,6 +1238,12 @@ export interface DKIMVerifyOptions {
|
|
|
908
1238
|
* ARC sealing options (if sealing should be prepared)
|
|
909
1239
|
*/
|
|
910
1240
|
seal?: ARCSealOptions;
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* Follow RFC 6376, RFC 8301 and RFC 8463 exactly instead of the lenient default
|
|
1244
|
+
* (default: false). See `DKIMWarning` for what the default mode accepts
|
|
1245
|
+
*/
|
|
1246
|
+
strict?: boolean;
|
|
911
1247
|
}
|
|
912
1248
|
|
|
913
1249
|
/**
|
|
@@ -961,6 +1297,17 @@ export interface SPFOptions {
|
|
|
961
1297
|
* Custom DNS resolver function
|
|
962
1298
|
*/
|
|
963
1299
|
resolver?: DNSResolver;
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* Follow RFC 7208 exactly instead of the lenient default (default: false)
|
|
1303
|
+
*/
|
|
1304
|
+
strict?: boolean;
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Maximum time in milliseconds for the whole evaluation, after which the result is
|
|
1308
|
+
* temperror (RFC 7208 section 4.6.4). No limit by default
|
|
1309
|
+
*/
|
|
1310
|
+
maxElapsedTime?: number;
|
|
964
1311
|
}
|
|
965
1312
|
|
|
966
1313
|
/**
|
|
@@ -984,6 +1331,12 @@ export interface DMARCOptions {
|
|
|
984
1331
|
*/
|
|
985
1332
|
headerFrom: string | string[];
|
|
986
1333
|
|
|
1334
|
+
/**
|
|
1335
|
+
* Number of From header fields the addresses came from. With more than one, DMARC
|
|
1336
|
+
* validation is not possible (reason "multiple-from-fields")
|
|
1337
|
+
*/
|
|
1338
|
+
fromFields?: number;
|
|
1339
|
+
|
|
987
1340
|
/**
|
|
988
1341
|
* Domains that passed SPF
|
|
989
1342
|
*/
|
|
@@ -1008,6 +1361,11 @@ export interface DMARCOptions {
|
|
|
1008
1361
|
* Custom DNS resolver function
|
|
1009
1362
|
*/
|
|
1010
1363
|
resolver?: DNSResolver;
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Follow RFC 9989 exactly instead of the lenient default (default: false)
|
|
1367
|
+
*/
|
|
1368
|
+
strict?: boolean;
|
|
1011
1369
|
}
|
|
1012
1370
|
|
|
1013
1371
|
/**
|
|
@@ -1042,6 +1400,24 @@ export interface ARCData {
|
|
|
1042
1400
|
error?: Error;
|
|
1043
1401
|
}
|
|
1044
1402
|
|
|
1403
|
+
/**
|
|
1404
|
+
* An error from ARC sealing
|
|
1405
|
+
*/
|
|
1406
|
+
export interface ARCSealError {
|
|
1407
|
+
/**
|
|
1408
|
+
* The error. Codes include EINVALIDCV (a missing or invalid cv), EINVALIDINSTANCE (an
|
|
1409
|
+
* instance out of 1-50, a chain that already has 50 sets, one that already exists, or in
|
|
1410
|
+
* strict mode one that leaves a gap),
|
|
1411
|
+
* EARCCHAINFAILED (the newest ARC-Seal already has cv=fail, RFC 8617 section 5.1 step 2),
|
|
1412
|
+
* EINVALIDALGO and EINVALIDTYPE (an algorithm that is not supported or does not match the key)
|
|
1413
|
+
*/
|
|
1414
|
+
err: Error & { code?: string };
|
|
1415
|
+
type?: string;
|
|
1416
|
+
selector?: string;
|
|
1417
|
+
signingDomain?: string;
|
|
1418
|
+
[key: string]: any;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1045
1421
|
/**
|
|
1046
1422
|
* ARC verification options
|
|
1047
1423
|
*/
|
|
@@ -1055,6 +1431,47 @@ export interface ARCOptions {
|
|
|
1055
1431
|
* Minimal allowed public key length in bits (default: 1024)
|
|
1056
1432
|
*/
|
|
1057
1433
|
minBitLength?: number;
|
|
1434
|
+
|
|
1435
|
+
/**
|
|
1436
|
+
* Follow RFC 8617 exactly instead of the lenient default (default: false). In strict mode
|
|
1437
|
+
* ARC-Seal and ARC-Message-Signature tag lists must be valid RFC 6376 section 3.2 syntax
|
|
1438
|
+
* with lower case tag names and every required tag, instance values must be 1*2DIGIT from
|
|
1439
|
+
* 1 to 50 (and come first in ARC-Authentication-Results), ARC header fields with an
|
|
1440
|
+
* invalid instance fail the chain instead of being ignored, an ARC-Message-Signature
|
|
1441
|
+
* without c= is only verified as simple/simple, and the arc= result also reports
|
|
1442
|
+
* arc=none and smtp.remote-ip
|
|
1443
|
+
*/
|
|
1444
|
+
strict?: boolean;
|
|
1445
|
+
|
|
1446
|
+
/**
|
|
1447
|
+
* Client IP address, reported as smtp.remote-ip in strict mode (RFC 8617 section 6)
|
|
1448
|
+
*/
|
|
1449
|
+
ip?: string;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
* Seal creation data for createSeal()
|
|
1454
|
+
*/
|
|
1455
|
+
export interface ARCCreateSealData {
|
|
1456
|
+
/**
|
|
1457
|
+
* Parsed message headers, when `input` is false
|
|
1458
|
+
*/
|
|
1459
|
+
headers?: any;
|
|
1460
|
+
|
|
1461
|
+
/**
|
|
1462
|
+
* ARC chain data of the message, when `input` is false
|
|
1463
|
+
*/
|
|
1464
|
+
arc?: ARCData | ARCSigningData;
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* Sealing options, with `bodyHash` (the relaxed/relaxed sha256 body hash) when `input` is false
|
|
1468
|
+
*/
|
|
1469
|
+
seal: ARCSealOptions & { bodyHash?: string };
|
|
1470
|
+
|
|
1471
|
+
/**
|
|
1472
|
+
* Follow the RFCs exactly (default: false, or `seal.strict`)
|
|
1473
|
+
*/
|
|
1474
|
+
strict?: boolean;
|
|
1058
1475
|
}
|
|
1059
1476
|
|
|
1060
1477
|
/**
|
|
@@ -1076,30 +1493,34 @@ export function arc(data: ARCData, opts?: ARCOptions): Promise<ARCResult>;
|
|
|
1076
1493
|
export function sealMessage(input: MessageInput, seal: ARCSealOptions): Promise<Buffer>;
|
|
1077
1494
|
|
|
1078
1495
|
/**
|
|
1079
|
-
* Gets ARC chain from parsed headers
|
|
1496
|
+
* Gets ARC chain from parsed headers and validates its structure. Throws for a chain that is not valid
|
|
1080
1497
|
*
|
|
1081
1498
|
* @param headers - Parsed message headers
|
|
1499
|
+
* @param opts - `strict` applies the RFC 8617 syntax rules, lenient acceptances are added to `warnings`
|
|
1082
1500
|
* @returns ARC chain or false if no chain found
|
|
1083
1501
|
*/
|
|
1084
|
-
export function getARChain(headers: any): ARCChainEntry[] | false;
|
|
1502
|
+
export function getARChain(headers: any, opts?: { strict?: boolean; warnings?: string[] }): ARCChainEntry[] | false;
|
|
1085
1503
|
|
|
1086
1504
|
/**
|
|
1087
|
-
* Verifies ARC seal chain
|
|
1505
|
+
* Verifies ARC seal chain. Throws if any seal of the chain is not valid, including a chain whose
|
|
1506
|
+
* newest seal has cv=fail
|
|
1088
1507
|
*
|
|
1089
1508
|
* @param data - ARC chain data
|
|
1090
1509
|
* @param opts - ARC verification options
|
|
1091
|
-
* @returns true if chain is valid
|
|
1510
|
+
* @returns true if chain is valid, false if there is no chain
|
|
1092
1511
|
*/
|
|
1093
|
-
export function verifyASChain(data: ARCData, opts: ARCOptions): Promise<boolean>;
|
|
1512
|
+
export function verifyASChain(data: ARCData, opts: ARCOptions & { warnings?: string[] }): Promise<boolean>;
|
|
1094
1513
|
|
|
1095
1514
|
/**
|
|
1096
1515
|
* Creates ARC seal headers
|
|
1097
1516
|
*
|
|
1098
1517
|
* @param input - RFC822 formatted message or false for pre-calculated data
|
|
1099
1518
|
* @param data - Seal creation data
|
|
1100
|
-
* @returns Seal headers
|
|
1519
|
+
* @returns Seal headers (empty when no set was created), the errors that say why, and what the
|
|
1520
|
+
* default mode sealed that strict mode would have refused ('arc-instance-gap',
|
|
1521
|
+
* 'arc-cv-instance', and the DKIM signing warnings)
|
|
1101
1522
|
*/
|
|
1102
|
-
export function createSeal(input: MessageInput | false, data:
|
|
1523
|
+
export function createSeal(input: MessageInput | false, data: ARCCreateSealData): Promise<{ headers: string[]; errors: ARCSealError[]; warnings: string[] }>;
|
|
1103
1524
|
|
|
1104
1525
|
// ============================================================================
|
|
1105
1526
|
// BIMI
|
|
@@ -1128,6 +1549,12 @@ export interface BIMIOptions {
|
|
|
1128
1549
|
* Custom DNS resolver function
|
|
1129
1550
|
*/
|
|
1130
1551
|
resolver?: DNSResolver;
|
|
1552
|
+
|
|
1553
|
+
/**
|
|
1554
|
+
* Format the Authentication-Results entry in the RFC 8601 section 2.2 form (default: false).
|
|
1555
|
+
* BIMI has no other strict-only rules, the draft checks apply in both modes
|
|
1556
|
+
*/
|
|
1557
|
+
strict?: boolean;
|
|
1131
1558
|
}
|
|
1132
1559
|
|
|
1133
1560
|
/**
|
|
@@ -1338,10 +1765,33 @@ export interface MTASTSPolicyResult {
|
|
|
1338
1765
|
|
|
1339
1766
|
/**
|
|
1340
1767
|
* Status of the fetch operation
|
|
1768
|
+
*
|
|
1769
|
+
* - `found`: a new or changed policy was fetched
|
|
1770
|
+
* - `renewed`: the cached policy has the same id and has not expired, it is returned without a fetch
|
|
1771
|
+
* - `not_found`: the domain has no MTA-STS policy
|
|
1772
|
+
* - `errored`: the policy could not be fetched. With a valid cached policy, that policy is
|
|
1773
|
+
* returned with `error` set
|
|
1341
1774
|
*/
|
|
1342
1775
|
status: 'found' | 'renewed' | 'not_found' | 'errored';
|
|
1776
|
+
|
|
1777
|
+
/**
|
|
1778
|
+
* Lax acceptance markers, only present when the default lax mode accepted something strict mode would reject
|
|
1779
|
+
*/
|
|
1780
|
+
warnings?: MTASTSWarning[];
|
|
1343
1781
|
}
|
|
1344
1782
|
|
|
1783
|
+
/**
|
|
1784
|
+
* Markers for input that the default lax mode accepted but `strict: true` would reject
|
|
1785
|
+
*
|
|
1786
|
+
* - `txt-syntax`: the TXT record does not match the RFC 8461 3.1 syntax
|
|
1787
|
+
* - `http-status`: the policy was served with a 2xx status other than 200
|
|
1788
|
+
* - `content-type`: the policy was not served as `text/plain`
|
|
1789
|
+
* - `policy-syntax`: the policy file does not match the RFC 8461 3.2 syntax
|
|
1790
|
+
* - `cert-identity`: the Policy Host certificate matched only through the CN or a partial-label wildcard
|
|
1791
|
+
* - `expired-cache`: an expired cached policy was returned because the policy could not be fetched
|
|
1792
|
+
*/
|
|
1793
|
+
export type MTASTSWarning = 'txt-syntax' | 'http-status' | 'content-type' | 'policy-syntax' | 'cert-identity' | 'expired-cache';
|
|
1794
|
+
|
|
1345
1795
|
/**
|
|
1346
1796
|
* MTA-STS MX validation result
|
|
1347
1797
|
*/
|
|
@@ -1375,6 +1825,21 @@ export interface MTASTSOptions {
|
|
|
1375
1825
|
* Custom DNS resolver function
|
|
1376
1826
|
*/
|
|
1377
1827
|
resolver?: DNSResolver;
|
|
1828
|
+
|
|
1829
|
+
/**
|
|
1830
|
+
* Apply the RFC 8461 rules exactly (default false)
|
|
1831
|
+
*/
|
|
1832
|
+
strict?: boolean;
|
|
1833
|
+
|
|
1834
|
+
/**
|
|
1835
|
+
* Overall time limit for the HTTPS policy request in milliseconds (default 60000)
|
|
1836
|
+
*/
|
|
1837
|
+
timeout?: number;
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* Maximum size of the policy file in bytes (default 65536)
|
|
1841
|
+
*/
|
|
1842
|
+
maxPolicySize?: number;
|
|
1378
1843
|
}
|
|
1379
1844
|
|
|
1380
1845
|
// Note: MTA-STS functions (resolvePolicy, fetchPolicy, parsePolicy, validateMx, getPolicy)
|