mailauth 6.0.0 → 7.0.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 +12 -0
- package/README.md +111 -39
- package/bin/mailauth.js +14 -6
- package/cli.md +8 -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 +495 -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 +2 -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 +574 -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 +140 -47
- package/lib/dmarc/index.d.ts +29 -2
- package/lib/dmarc/verify.js +120 -44
- package/lib/mailauth.js +75 -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
|
@@ -84,6 +84,24 @@ export interface AuthenticateOptions {
|
|
|
84
84
|
* ARC sealing options
|
|
85
85
|
*/
|
|
86
86
|
seal?: ARCSealOptions;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Follow the RFCs exactly instead of the lenient defaults (default: false). Passed on
|
|
90
|
+
* to every check (DKIM, SPF, ARC, DMARC, BIMI and ARC sealing), BIMI only uses it for the
|
|
91
|
+
* format of its Authentication-Results entry. In strict mode:
|
|
92
|
+
*
|
|
93
|
+
* - rsa-sha1 DKIM signatures are reported as `dkim=policy` (`policy.dkim-rules=weak-algorithm`)
|
|
94
|
+
* - DKIM-Signature and key record syntax is validated (RFC 6376 sections 3.2, 3.5, 3.6.1, 6.1.1)
|
|
95
|
+
* - a body hash mismatch is reported as `dkim=fail`, and a signature that can not be
|
|
96
|
+
* processed as `dkim=neutral` instead of being left out
|
|
97
|
+
* - DKIM results report `header.d` and the AUID as `header.i`
|
|
98
|
+
* - email identities in Authentication-Results use the `local-part@domain` form of RFC 8601 section 2.2
|
|
99
|
+
* - Authentication-Results is placed above Received-SPF (RFC 8601 section 5)
|
|
100
|
+
*
|
|
101
|
+
* What the lenient default accepts and strict mode would reject is listed in the
|
|
102
|
+
* `warnings` array of the affected result. It is never written into the headers
|
|
103
|
+
*/
|
|
104
|
+
strict?: boolean;
|
|
87
105
|
}
|
|
88
106
|
|
|
89
107
|
/**
|
|
@@ -111,20 +129,47 @@ export interface ARCSealOptions {
|
|
|
111
129
|
canonicalization?: string;
|
|
112
130
|
|
|
113
131
|
/**
|
|
114
|
-
* Signing algorithm
|
|
115
|
-
*
|
|
132
|
+
* Signing algorithm, 'rsa-sha256' or 'ed25519-sha256'. Follows the private key type when
|
|
133
|
+
* not set. Used for both the ARC-Message-Signature and the ARC-Seal
|
|
116
134
|
*/
|
|
117
135
|
algorithm?: string;
|
|
118
136
|
|
|
119
137
|
/**
|
|
120
|
-
*
|
|
138
|
+
* Chain validation status for the ARC-Seal cv= tag: 'none', 'pass' or 'fail' (compared
|
|
139
|
+
* case-insensitively). Defaults to 'none' for the first set and is required for any later
|
|
140
|
+
* set. `authenticate()` sets it from the ARC validation result
|
|
141
|
+
*/
|
|
142
|
+
cv?: 'none' | 'pass' | 'fail' | string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* ARC instance (i= tag), 1 to 50. Defaults to one more than the highest instance on the
|
|
146
|
+
* message. An instance that already exists is refused, and one that leaves a gap is
|
|
147
|
+
* refused in strict mode (the default mode seals it and adds an `arc-instance-gap` warning)
|
|
148
|
+
*/
|
|
149
|
+
i?: number;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Authentication-Results payload for the ARC-Authentication-Results header (the part after
|
|
153
|
+
* "i=N;"). `authenticate()` sets it from its own results
|
|
154
|
+
*/
|
|
155
|
+
authResults?: string;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Header fields to include in the ARC-Message-Signature, as an array or a colon separated
|
|
159
|
+
* string. Defaults to DKIM-Signature and the default DKIM header list. ARC header fields and
|
|
160
|
+
* Authentication-Results are never included (RFC 8617 section 4.1.2)
|
|
121
161
|
*/
|
|
122
|
-
headerList?: string[];
|
|
162
|
+
headerList?: string[] | string;
|
|
123
163
|
|
|
124
164
|
/**
|
|
125
165
|
* Signing timestamp
|
|
126
166
|
*/
|
|
127
167
|
signTime?: Date | string | number;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Follow the RFCs exactly instead of the lenient defaults (default: false)
|
|
171
|
+
*/
|
|
172
|
+
strict?: boolean;
|
|
128
173
|
}
|
|
129
174
|
|
|
130
175
|
/**
|
|
@@ -132,7 +177,8 @@ export interface ARCSealOptions {
|
|
|
132
177
|
*/
|
|
133
178
|
export interface AuthPolicy {
|
|
134
179
|
/**
|
|
135
|
-
* DKIM policy rules
|
|
180
|
+
* DKIM policy rules: 'weak-key' when the key is shorter than minBitLength, 'weak-algorithm'
|
|
181
|
+
* for an rsa-sha1 signature in strict mode
|
|
136
182
|
*/
|
|
137
183
|
'dkim-rules'?: string;
|
|
138
184
|
|
|
@@ -146,7 +192,10 @@ export interface AuthPolicy {
|
|
|
146
192
|
* Status result from authentication checks
|
|
147
193
|
*/
|
|
148
194
|
export interface AuthStatus {
|
|
149
|
-
|
|
195
|
+
/**
|
|
196
|
+
* The result keyword. `temperr` is deprecated, BIMI reports `temperror` now
|
|
197
|
+
*/
|
|
198
|
+
result: 'pass' | 'fail' | 'neutral' | 'none' | 'temperror' | 'temperr' | 'permerror' | 'policy' | 'softfail' | 'skipped' | 'declined';
|
|
150
199
|
comment?: string;
|
|
151
200
|
header?: Record<string, any>;
|
|
152
201
|
smtp?: {
|
|
@@ -156,25 +205,94 @@ export interface AuthStatus {
|
|
|
156
205
|
policy?: AuthPolicy;
|
|
157
206
|
}
|
|
158
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Lax acceptance markers of a DKIM result: what the default mode accepted and strict mode
|
|
210
|
+
* would reject
|
|
211
|
+
*
|
|
212
|
+
* - `rsa-sha1`: the signature uses rsa-sha1 (RFC 8301 section 3.1)
|
|
213
|
+
* - `tag-syntax`: the signature is not valid tag-list syntax (duplicate tags, upper case tag
|
|
214
|
+
* names, a malformed t=, x= or l= value, or a d= or s= that is not a domain name)
|
|
215
|
+
* - `missing-v`, `missing-h` (and the other `missing-*`): a required tag is missing
|
|
216
|
+
* - `invalid-v`: v= is not "1"
|
|
217
|
+
* - `identity-domain`: the i= domain is not d= or its subdomain, or it is a subdomain while
|
|
218
|
+
* the key has t=s
|
|
219
|
+
* - `invalid-expiration`: x= is equal to t=
|
|
220
|
+
* - `query-method`: q= does not list dns/txt
|
|
221
|
+
* - `key-syntax`: the key record is not valid tag-list syntax
|
|
222
|
+
* - `key-v-syntax`: the key record v= is not the first tag, or not exactly "DKIM1"
|
|
223
|
+
* - `key-type-inferred`: an ed25519 key was found from its length, without k=ed25519
|
|
224
|
+
*/
|
|
225
|
+
export type DKIMWarning =
|
|
226
|
+
| 'rsa-sha1'
|
|
227
|
+
| 'tag-syntax'
|
|
228
|
+
| 'missing-v'
|
|
229
|
+
| 'missing-a'
|
|
230
|
+
| 'missing-b'
|
|
231
|
+
| 'missing-bh'
|
|
232
|
+
| 'missing-d'
|
|
233
|
+
| 'missing-h'
|
|
234
|
+
| 'missing-s'
|
|
235
|
+
| 'invalid-v'
|
|
236
|
+
| 'identity-domain'
|
|
237
|
+
| 'invalid-expiration'
|
|
238
|
+
| 'query-method'
|
|
239
|
+
| 'key-syntax'
|
|
240
|
+
| 'key-v-syntax'
|
|
241
|
+
| 'key-type-inferred';
|
|
242
|
+
|
|
159
243
|
/**
|
|
160
244
|
* DKIM verification result for a single signature
|
|
161
245
|
*/
|
|
162
246
|
export interface DKIMResult {
|
|
163
247
|
/**
|
|
164
|
-
* Signature identifier
|
|
248
|
+
* Signature identifier, the sha256 hash of the signature value
|
|
165
249
|
*/
|
|
166
250
|
id?: string;
|
|
167
251
|
|
|
168
252
|
/**
|
|
169
|
-
* Signing domain
|
|
253
|
+
* Signing domain (d= tag)
|
|
170
254
|
*/
|
|
171
|
-
signingDomain
|
|
255
|
+
signingDomain?: string;
|
|
172
256
|
|
|
173
257
|
/**
|
|
174
|
-
* Key selector
|
|
258
|
+
* Key selector (s= tag)
|
|
175
259
|
*/
|
|
176
260
|
selector?: string;
|
|
177
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Signature value (b= tag)
|
|
264
|
+
*/
|
|
265
|
+
signature?: string;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Signature algorithm (a= tag), eg. 'rsa-sha256'
|
|
269
|
+
*/
|
|
270
|
+
algo?: string;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Canonicalization (c= tag), eg. 'relaxed/relaxed'
|
|
274
|
+
*/
|
|
275
|
+
format?: string;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Body hash calculated for the message
|
|
279
|
+
*/
|
|
280
|
+
bodyHash?: string;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Body hash from the bh= tag
|
|
284
|
+
*/
|
|
285
|
+
bodyHashExpecting?: string;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Signed header fields and the canonicalized header data (base64)
|
|
289
|
+
*/
|
|
290
|
+
signingHeaders?: {
|
|
291
|
+
keys: string;
|
|
292
|
+
headers: string[];
|
|
293
|
+
canonicalizedHeader: string;
|
|
294
|
+
};
|
|
295
|
+
|
|
178
296
|
/**
|
|
179
297
|
* Verification status
|
|
180
298
|
*/
|
|
@@ -190,6 +308,18 @@ export interface DKIMResult {
|
|
|
190
308
|
* Number of body bytes left unsigned by an l= tag
|
|
191
309
|
*/
|
|
192
310
|
underSized?: number;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* True when the key record has the t=y flag, the signing domain is testing DKIM
|
|
314
|
+
* (RFC 6376 section 3.6.1)
|
|
315
|
+
*/
|
|
316
|
+
testing?: boolean;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* What the default mode accepted and strict mode would reject. Only present when
|
|
320
|
+
* there is something to report, never written into the Authentication-Results text
|
|
321
|
+
*/
|
|
322
|
+
warnings?: DKIMWarning[];
|
|
193
323
|
};
|
|
194
324
|
|
|
195
325
|
/**
|
|
@@ -198,22 +328,82 @@ export interface DKIMResult {
|
|
|
198
328
|
info: string;
|
|
199
329
|
|
|
200
330
|
/**
|
|
201
|
-
*
|
|
331
|
+
* Signing time from the t= tag, as an ISO string
|
|
332
|
+
*/
|
|
333
|
+
signTime?: string | null;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Expiration time from the x= tag, as an ISO string
|
|
337
|
+
*/
|
|
338
|
+
expiresAfter?: string | null;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* False when t= is in the future or x= has passed
|
|
342
|
+
*/
|
|
343
|
+
signatureTimeValid?: boolean;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Size of the message body in bytes
|
|
347
|
+
*/
|
|
348
|
+
sourceBodyLength?: number;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Number of canonicalized body bytes covered by the signature
|
|
352
|
+
*/
|
|
353
|
+
canonBodyLength?: number;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Number of canonicalized body bytes in total
|
|
357
|
+
*/
|
|
358
|
+
canonBodyLengthTotal?: number;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* True when the signature has an l= tag
|
|
362
|
+
*/
|
|
363
|
+
canonBodyLengthLimited?: boolean;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* The l= value
|
|
367
|
+
*/
|
|
368
|
+
canonBodyLengthLimit?: number;
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Where the MIME structure starts in the body, for multipart messages
|
|
372
|
+
*/
|
|
373
|
+
mimeStructureStart?: number;
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Public key in PEM format
|
|
377
|
+
*/
|
|
378
|
+
publicKey?: string;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* RSA key size in bits
|
|
382
|
+
*/
|
|
383
|
+
modulusLength?: number;
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Key record as found in DNS
|
|
387
|
+
*/
|
|
388
|
+
rr?: string;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @deprecated Never set, use `algo`
|
|
202
392
|
*/
|
|
203
393
|
algorithm?: string;
|
|
204
394
|
|
|
205
395
|
/**
|
|
206
|
-
*
|
|
396
|
+
* @deprecated Never set, use `format`
|
|
207
397
|
*/
|
|
208
398
|
canonicalization?: string;
|
|
209
399
|
|
|
210
400
|
/**
|
|
211
|
-
*
|
|
401
|
+
* @deprecated Never set, use `signTime`
|
|
212
402
|
*/
|
|
213
403
|
signingTime?: Date;
|
|
214
404
|
|
|
215
405
|
/**
|
|
216
|
-
*
|
|
406
|
+
* @deprecated Never set, use `expiresAfter`
|
|
217
407
|
*/
|
|
218
408
|
expiration?: Date;
|
|
219
409
|
}
|
|
@@ -223,10 +413,16 @@ export interface DKIMResult {
|
|
|
223
413
|
*/
|
|
224
414
|
export interface DKIMVerifyResult {
|
|
225
415
|
/**
|
|
226
|
-
*
|
|
416
|
+
* Addresses from the From header, as full addr-specs (the domain is what follows the last
|
|
417
|
+
* "@"). The mailboxes of a group (RFC 6854) are included
|
|
227
418
|
*/
|
|
228
419
|
headerFrom: string[];
|
|
229
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Number of From header fields in the message. RFC 5322 allows only one
|
|
423
|
+
*/
|
|
424
|
+
fromFields: number;
|
|
425
|
+
|
|
230
426
|
/**
|
|
231
427
|
* Domain from Return-Path header
|
|
232
428
|
*/
|
|
@@ -242,8 +438,8 @@ export interface DKIMVerifyResult {
|
|
|
242
438
|
* Access with result.headers or Object.getOwnPropertyDescriptor()
|
|
243
439
|
*/
|
|
244
440
|
readonly headers?: {
|
|
245
|
-
parsed: Array<{ key: string;
|
|
246
|
-
|
|
441
|
+
parsed: Array<{ key: string; casedKey: string; line: Buffer }>;
|
|
442
|
+
original: Buffer;
|
|
247
443
|
};
|
|
248
444
|
|
|
249
445
|
/**
|
|
@@ -312,6 +508,18 @@ export interface SPFResult {
|
|
|
312
508
|
void: number;
|
|
313
509
|
subqueries: Record<string, number>;
|
|
314
510
|
};
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Explanation string published by the domain owner with the "exp" modifier
|
|
514
|
+
* (RFC 7208 section 6.2). Only set for a "fail" result. This is third party text
|
|
515
|
+
*/
|
|
516
|
+
explanation?: string;
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Set in the default mode when something was accepted that strict mode would have
|
|
520
|
+
* rejected: "syntax-error", "void-lookup-limit" or "non-ascii-local-part"
|
|
521
|
+
*/
|
|
522
|
+
warnings?: string[];
|
|
315
523
|
}
|
|
316
524
|
|
|
317
525
|
/**
|
|
@@ -354,7 +562,18 @@ export interface ARCResult {
|
|
|
354
562
|
* Why sealing failed, when a seal was requested but could not be created.
|
|
355
563
|
* The message is left unsealed in that case
|
|
356
564
|
*/
|
|
357
|
-
sealErrors?:
|
|
565
|
+
sealErrors?: ARCSealError[];
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* What the default mode accepted in the chain that strict mode would reject, for example
|
|
569
|
+
* 'arc-tag-syntax' (a tag-list syntax error or a tag name that is not lower case),
|
|
570
|
+
* 'arc-instance-syntax' (an i= value that is not 1*2DIGIT, an ARC-Authentication-Results
|
|
571
|
+
* whose i= is not first, or an ARC header field that was ignored for its i= value),
|
|
572
|
+
* 'ams-c-default' (an ARC-Message-Signature without c= that only verified with
|
|
573
|
+
* relaxed/relaxed), and the key record warnings of the DKIM result. Not set for a failing
|
|
574
|
+
* chain, and never written into the headers
|
|
575
|
+
*/
|
|
576
|
+
warnings?: string[];
|
|
358
577
|
|
|
359
578
|
/**
|
|
360
579
|
* ARC chain entries (non-enumerable property)
|
|
@@ -373,10 +592,16 @@ export interface DMARCResult {
|
|
|
373
592
|
domain: string;
|
|
374
593
|
|
|
375
594
|
/**
|
|
376
|
-
* DMARC policy ('none', 'quarantine', 'reject')
|
|
595
|
+
* Effective DMARC policy ('none', 'quarantine', 'reject'): p for the author domain's own
|
|
596
|
+
* record, np or sp for an inherited one, one level lower with t=y
|
|
377
597
|
*/
|
|
378
598
|
policy: string;
|
|
379
599
|
|
|
600
|
+
/**
|
|
601
|
+
* Domain whose DMARC record the policy was taken from
|
|
602
|
+
*/
|
|
603
|
+
policyDomain?: string;
|
|
604
|
+
|
|
380
605
|
/**
|
|
381
606
|
* Policy for organizational domain
|
|
382
607
|
*/
|
|
@@ -388,9 +613,14 @@ export interface DMARCResult {
|
|
|
388
613
|
sp: string;
|
|
389
614
|
|
|
390
615
|
/**
|
|
391
|
-
*
|
|
616
|
+
* Policy for non-existent subdomains, if published
|
|
392
617
|
*/
|
|
393
|
-
|
|
618
|
+
np?: string;
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* True when the record has t=y, in which case policy is one level below the published one
|
|
622
|
+
*/
|
|
623
|
+
testMode: boolean;
|
|
394
624
|
|
|
395
625
|
/**
|
|
396
626
|
* DMARC DNS record
|
|
@@ -400,7 +630,26 @@ export interface DMARCResult {
|
|
|
400
630
|
/**
|
|
401
631
|
* Verification status
|
|
402
632
|
*/
|
|
403
|
-
status: AuthStatus
|
|
633
|
+
status: AuthStatus & {
|
|
634
|
+
header?: {
|
|
635
|
+
/**
|
|
636
|
+
* Author Domain
|
|
637
|
+
*/
|
|
638
|
+
from?: string;
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Domain of the DMARC record, omitted in strict mode (not a registered property for dmarc)
|
|
642
|
+
*/
|
|
643
|
+
d?: string;
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
policy?: {
|
|
647
|
+
/**
|
|
648
|
+
* The policy that was applied (RFC 8601 section 2.7.2 policy.dmarc)
|
|
649
|
+
*/
|
|
650
|
+
dmarc?: string;
|
|
651
|
+
};
|
|
652
|
+
};
|
|
404
653
|
|
|
405
654
|
/**
|
|
406
655
|
* Alignment results
|
|
@@ -438,6 +687,12 @@ export interface DMARCResult {
|
|
|
438
687
|
* Error message if verification failed
|
|
439
688
|
*/
|
|
440
689
|
error?: string;
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* What the default mode accepted and strict mode would reject, only present when there
|
|
693
|
+
* is something to report
|
|
694
|
+
*/
|
|
695
|
+
warnings?: string[];
|
|
441
696
|
}
|
|
442
697
|
|
|
443
698
|
/**
|
|
@@ -445,9 +700,12 @@ export interface DMARCResult {
|
|
|
445
700
|
*/
|
|
446
701
|
export interface BIMIResult {
|
|
447
702
|
/**
|
|
448
|
-
* Verification status
|
|
703
|
+
* Verification status. `declined` means the domain published a declination record,
|
|
704
|
+
* `temperror` a DNS failure. `temperr` is deprecated and no longer reported
|
|
449
705
|
*/
|
|
450
|
-
status: AuthStatus
|
|
706
|
+
status: AuthStatus & {
|
|
707
|
+
result: 'pass' | 'none' | 'fail' | 'skipped' | 'declined' | 'temperror' | 'temperr';
|
|
708
|
+
};
|
|
451
709
|
|
|
452
710
|
/**
|
|
453
711
|
* BIMI DNS record
|
|
@@ -484,7 +742,8 @@ export interface ReceivedChainEntry {
|
|
|
484
742
|
value: string;
|
|
485
743
|
|
|
486
744
|
/**
|
|
487
|
-
*
|
|
745
|
+
* The comments after the value, joined with spaces. Usually the TCP-info of
|
|
746
|
+
* RFC 5321 section 4.4, whose last address literal is the connecting IP address
|
|
488
747
|
*/
|
|
489
748
|
comment?: string;
|
|
490
749
|
};
|
|
@@ -581,6 +840,13 @@ export interface AuthenticateResult {
|
|
|
581
840
|
* Combined authentication headers to prepend to message
|
|
582
841
|
*/
|
|
583
842
|
headers: string;
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Why DMARC was not evaluated when `dmarc` is false because the From header has no
|
|
846
|
+
* domain or more than one (RFC 9989 section 5.3.1). Such a message can be treated as
|
|
847
|
+
* suspicious (RFC 9989 section 11.5)
|
|
848
|
+
*/
|
|
849
|
+
dmarcSkipReason?: 'no-author-domain' | 'multiple-author-domains' | 'invalid-author-domain' | 'multiple-from-fields';
|
|
584
850
|
}
|
|
585
851
|
|
|
586
852
|
/**
|
|
@@ -622,16 +888,17 @@ export interface DKIMSignOptions {
|
|
|
622
888
|
canonicalization?: string;
|
|
623
889
|
|
|
624
890
|
/**
|
|
625
|
-
* Signing algorithm
|
|
626
|
-
* Supported: 'rsa-sha256', 'rsa-sha1'
|
|
891
|
+
* Signing algorithm, defaults to 'rsa-sha256' or 'ed25519-sha256' depending on the key type.
|
|
892
|
+
* Supported: 'rsa-sha256', 'ed25519-sha256' and 'rsa-sha1'. rsa-sha1 is historic
|
|
893
|
+
* (RFC 8301): it adds the 'rsa-sha1' warning, and strict mode refuses it
|
|
627
894
|
*/
|
|
628
895
|
algorithm?: string;
|
|
629
896
|
|
|
630
897
|
/**
|
|
631
|
-
*
|
|
632
|
-
*
|
|
898
|
+
* Header fields to sign, as an array or a colon separated string. Default includes
|
|
899
|
+
* From, Subject, Date, To, etc. Set per signature in `signatureData` or for all of them
|
|
633
900
|
*/
|
|
634
|
-
headerList?: string[];
|
|
901
|
+
headerList?: string[] | string;
|
|
635
902
|
|
|
636
903
|
/**
|
|
637
904
|
* Signing timestamp (defaults to current time)
|
|
@@ -649,7 +916,7 @@ export interface DKIMSignOptions {
|
|
|
649
916
|
maxBodyLength?: number;
|
|
650
917
|
|
|
651
918
|
/**
|
|
652
|
-
* Identity (i= tag)
|
|
919
|
+
* Identity (i= tag), an address whose domain is the signing domain or its subdomain
|
|
653
920
|
*/
|
|
654
921
|
identity?: string;
|
|
655
922
|
|
|
@@ -657,6 +924,15 @@ export interface DKIMSignOptions {
|
|
|
657
924
|
* Multiple signature configurations
|
|
658
925
|
*/
|
|
659
926
|
signatureData?: DKIMSignOptions[];
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Follow the RFCs exactly (default: false). Strict mode refuses rsa-sha1, RSA keys
|
|
930
|
+
* shorter than 1024 bits, an expiration that is not after the signing time, a signing
|
|
931
|
+
* time that does not fit into t=, and a d=, s= or i= that is not valid RFC 6376 syntax.
|
|
932
|
+
* The default mode signs these and lists them in `warnings`. A d=, s= or identity that
|
|
933
|
+
* would break out of its tag is refused in both modes
|
|
934
|
+
*/
|
|
935
|
+
strict?: boolean;
|
|
660
936
|
}
|
|
661
937
|
|
|
662
938
|
/**
|
|
@@ -829,12 +1105,43 @@ export interface ARCSigningData {
|
|
|
829
1105
|
privateKey?: string | Buffer;
|
|
830
1106
|
}
|
|
831
1107
|
|
|
1108
|
+
/**
|
|
1109
|
+
* A signature that could not be created
|
|
1110
|
+
*/
|
|
1111
|
+
export interface DKIMSignError {
|
|
1112
|
+
/**
|
|
1113
|
+
* Why the signature was not created. `err.code` is eg. 'ENOFROM', 'EINVALIDALGO',
|
|
1114
|
+
* 'EINVALIDTYPE', 'ESHORTKEY', 'EINVALIDDOMAIN', 'EINVALIDSELECTOR', 'EINVALIDIDENTITY',
|
|
1115
|
+
* 'EINVALIDTIME', 'EINVALIDCANON' or 'EINVALIDINSTANCE'
|
|
1116
|
+
*/
|
|
1117
|
+
err: Error & { code?: string };
|
|
1118
|
+
|
|
1119
|
+
type?: 'DKIM' | 'ARC';
|
|
1120
|
+
selector?: string;
|
|
1121
|
+
signingDomain?: string;
|
|
1122
|
+
algorithm?: string;
|
|
1123
|
+
canonicalization?: string;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* What the default mode signed although strict mode would have refused it
|
|
1128
|
+
*
|
|
1129
|
+
* - `rsa-sha1`: signed with rsa-sha1
|
|
1130
|
+
* - `weak-key`: signed with an RSA key shorter than 1024 bits
|
|
1131
|
+
* - `invalid-expiration`: the expiration is not after the signing time, or does not fit into x=
|
|
1132
|
+
* - `invalid-signtime`: the signing time does not fit into t=, which was left out
|
|
1133
|
+
* - `d-syntax`, `s-syntax`: d= or s= is not valid RFC 6376 syntax
|
|
1134
|
+
* - `identity-domain`: the identity domain is not the signing domain or its subdomain
|
|
1135
|
+
*/
|
|
1136
|
+
export type DKIMSignWarning = 'rsa-sha1' | 'weak-key' | 'invalid-expiration' | 'invalid-signtime' | 'd-syntax' | 's-syntax' | 'identity-domain';
|
|
1137
|
+
|
|
832
1138
|
/**
|
|
833
1139
|
* DKIM signing result
|
|
834
1140
|
*/
|
|
835
1141
|
export interface DKIMSignResult {
|
|
836
1142
|
/**
|
|
837
|
-
* DKIM-Signature header(s) to prepend to message
|
|
1143
|
+
* DKIM-Signature header(s) to prepend to message, each ending with a CRLF. An empty
|
|
1144
|
+
* string when no signature was created
|
|
838
1145
|
*/
|
|
839
1146
|
signatures: string;
|
|
840
1147
|
|
|
@@ -844,9 +1151,14 @@ export interface DKIMSignResult {
|
|
|
844
1151
|
arc?: ARCSigningData;
|
|
845
1152
|
|
|
846
1153
|
/**
|
|
847
|
-
*
|
|
1154
|
+
* The signatures that could not be created, and why
|
|
1155
|
+
*/
|
|
1156
|
+
errors: DKIMSignError[];
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* What was signed although strict mode would have refused it
|
|
848
1160
|
*/
|
|
849
|
-
|
|
1161
|
+
warnings: DKIMSignWarning[];
|
|
850
1162
|
}
|
|
851
1163
|
|
|
852
1164
|
/**
|
|
@@ -871,9 +1183,14 @@ export class DkimSignStream extends Transform {
|
|
|
871
1183
|
constructor(options: DKIMSignOptions);
|
|
872
1184
|
|
|
873
1185
|
/**
|
|
874
|
-
*
|
|
1186
|
+
* The signatures that could not be created, set when the stream ends
|
|
875
1187
|
*/
|
|
876
|
-
errors:
|
|
1188
|
+
errors: DKIMSignError[] | null;
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* What was signed although strict mode would have refused it, set when the stream ends
|
|
1192
|
+
*/
|
|
1193
|
+
warnings: DKIMSignWarning[] | null;
|
|
877
1194
|
}
|
|
878
1195
|
|
|
879
1196
|
// ============================================================================
|
|
@@ -908,6 +1225,12 @@ export interface DKIMVerifyOptions {
|
|
|
908
1225
|
* ARC sealing options (if sealing should be prepared)
|
|
909
1226
|
*/
|
|
910
1227
|
seal?: ARCSealOptions;
|
|
1228
|
+
|
|
1229
|
+
/**
|
|
1230
|
+
* Follow RFC 6376, RFC 8301 and RFC 8463 exactly instead of the lenient default
|
|
1231
|
+
* (default: false). See `DKIMWarning` for what the default mode accepts
|
|
1232
|
+
*/
|
|
1233
|
+
strict?: boolean;
|
|
911
1234
|
}
|
|
912
1235
|
|
|
913
1236
|
/**
|
|
@@ -961,6 +1284,17 @@ export interface SPFOptions {
|
|
|
961
1284
|
* Custom DNS resolver function
|
|
962
1285
|
*/
|
|
963
1286
|
resolver?: DNSResolver;
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Follow RFC 7208 exactly instead of the lenient default (default: false)
|
|
1290
|
+
*/
|
|
1291
|
+
strict?: boolean;
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* Maximum time in milliseconds for the whole evaluation, after which the result is
|
|
1295
|
+
* temperror (RFC 7208 section 4.6.4). No limit by default
|
|
1296
|
+
*/
|
|
1297
|
+
maxElapsedTime?: number;
|
|
964
1298
|
}
|
|
965
1299
|
|
|
966
1300
|
/**
|
|
@@ -984,6 +1318,12 @@ export interface DMARCOptions {
|
|
|
984
1318
|
*/
|
|
985
1319
|
headerFrom: string | string[];
|
|
986
1320
|
|
|
1321
|
+
/**
|
|
1322
|
+
* Number of From header fields the addresses came from. With more than one, DMARC
|
|
1323
|
+
* validation is not possible (reason "multiple-from-fields")
|
|
1324
|
+
*/
|
|
1325
|
+
fromFields?: number;
|
|
1326
|
+
|
|
987
1327
|
/**
|
|
988
1328
|
* Domains that passed SPF
|
|
989
1329
|
*/
|
|
@@ -1008,6 +1348,11 @@ export interface DMARCOptions {
|
|
|
1008
1348
|
* Custom DNS resolver function
|
|
1009
1349
|
*/
|
|
1010
1350
|
resolver?: DNSResolver;
|
|
1351
|
+
|
|
1352
|
+
/**
|
|
1353
|
+
* Follow RFC 9989 exactly instead of the lenient default (default: false)
|
|
1354
|
+
*/
|
|
1355
|
+
strict?: boolean;
|
|
1011
1356
|
}
|
|
1012
1357
|
|
|
1013
1358
|
/**
|
|
@@ -1042,6 +1387,24 @@ export interface ARCData {
|
|
|
1042
1387
|
error?: Error;
|
|
1043
1388
|
}
|
|
1044
1389
|
|
|
1390
|
+
/**
|
|
1391
|
+
* An error from ARC sealing
|
|
1392
|
+
*/
|
|
1393
|
+
export interface ARCSealError {
|
|
1394
|
+
/**
|
|
1395
|
+
* The error. Codes include EINVALIDCV (a missing or invalid cv), EINVALIDINSTANCE (an
|
|
1396
|
+
* instance out of 1-50, a chain that already has 50 sets, one that already exists, or in
|
|
1397
|
+
* strict mode one that leaves a gap),
|
|
1398
|
+
* EARCCHAINFAILED (the newest ARC-Seal already has cv=fail, RFC 8617 section 5.1 step 2),
|
|
1399
|
+
* EINVALIDALGO and EINVALIDTYPE (an algorithm that is not supported or does not match the key)
|
|
1400
|
+
*/
|
|
1401
|
+
err: Error & { code?: string };
|
|
1402
|
+
type?: string;
|
|
1403
|
+
selector?: string;
|
|
1404
|
+
signingDomain?: string;
|
|
1405
|
+
[key: string]: any;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1045
1408
|
/**
|
|
1046
1409
|
* ARC verification options
|
|
1047
1410
|
*/
|
|
@@ -1055,6 +1418,47 @@ export interface ARCOptions {
|
|
|
1055
1418
|
* Minimal allowed public key length in bits (default: 1024)
|
|
1056
1419
|
*/
|
|
1057
1420
|
minBitLength?: number;
|
|
1421
|
+
|
|
1422
|
+
/**
|
|
1423
|
+
* Follow RFC 8617 exactly instead of the lenient default (default: false). In strict mode
|
|
1424
|
+
* ARC-Seal and ARC-Message-Signature tag lists must be valid RFC 6376 section 3.2 syntax
|
|
1425
|
+
* with lower case tag names and every required tag, instance values must be 1*2DIGIT from
|
|
1426
|
+
* 1 to 50 (and come first in ARC-Authentication-Results), ARC header fields with an
|
|
1427
|
+
* invalid instance fail the chain instead of being ignored, an ARC-Message-Signature
|
|
1428
|
+
* without c= is only verified as simple/simple, and the arc= result also reports
|
|
1429
|
+
* arc=none and smtp.remote-ip
|
|
1430
|
+
*/
|
|
1431
|
+
strict?: boolean;
|
|
1432
|
+
|
|
1433
|
+
/**
|
|
1434
|
+
* Client IP address, reported as smtp.remote-ip in strict mode (RFC 8617 section 6)
|
|
1435
|
+
*/
|
|
1436
|
+
ip?: string;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Seal creation data for createSeal()
|
|
1441
|
+
*/
|
|
1442
|
+
export interface ARCCreateSealData {
|
|
1443
|
+
/**
|
|
1444
|
+
* Parsed message headers, when `input` is false
|
|
1445
|
+
*/
|
|
1446
|
+
headers?: any;
|
|
1447
|
+
|
|
1448
|
+
/**
|
|
1449
|
+
* ARC chain data of the message, when `input` is false
|
|
1450
|
+
*/
|
|
1451
|
+
arc?: ARCData | ARCSigningData;
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* Sealing options, with `bodyHash` (the relaxed/relaxed sha256 body hash) when `input` is false
|
|
1455
|
+
*/
|
|
1456
|
+
seal: ARCSealOptions & { bodyHash?: string };
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* Follow the RFCs exactly (default: false, or `seal.strict`)
|
|
1460
|
+
*/
|
|
1461
|
+
strict?: boolean;
|
|
1058
1462
|
}
|
|
1059
1463
|
|
|
1060
1464
|
/**
|
|
@@ -1076,30 +1480,34 @@ export function arc(data: ARCData, opts?: ARCOptions): Promise<ARCResult>;
|
|
|
1076
1480
|
export function sealMessage(input: MessageInput, seal: ARCSealOptions): Promise<Buffer>;
|
|
1077
1481
|
|
|
1078
1482
|
/**
|
|
1079
|
-
* Gets ARC chain from parsed headers
|
|
1483
|
+
* Gets ARC chain from parsed headers and validates its structure. Throws for a chain that is not valid
|
|
1080
1484
|
*
|
|
1081
1485
|
* @param headers - Parsed message headers
|
|
1486
|
+
* @param opts - `strict` applies the RFC 8617 syntax rules, lenient acceptances are added to `warnings`
|
|
1082
1487
|
* @returns ARC chain or false if no chain found
|
|
1083
1488
|
*/
|
|
1084
|
-
export function getARChain(headers: any): ARCChainEntry[] | false;
|
|
1489
|
+
export function getARChain(headers: any, opts?: { strict?: boolean; warnings?: string[] }): ARCChainEntry[] | false;
|
|
1085
1490
|
|
|
1086
1491
|
/**
|
|
1087
|
-
* Verifies ARC seal chain
|
|
1492
|
+
* Verifies ARC seal chain. Throws if any seal of the chain is not valid, including a chain whose
|
|
1493
|
+
* newest seal has cv=fail
|
|
1088
1494
|
*
|
|
1089
1495
|
* @param data - ARC chain data
|
|
1090
1496
|
* @param opts - ARC verification options
|
|
1091
|
-
* @returns true if chain is valid
|
|
1497
|
+
* @returns true if chain is valid, false if there is no chain
|
|
1092
1498
|
*/
|
|
1093
|
-
export function verifyASChain(data: ARCData, opts: ARCOptions): Promise<boolean>;
|
|
1499
|
+
export function verifyASChain(data: ARCData, opts: ARCOptions & { warnings?: string[] }): Promise<boolean>;
|
|
1094
1500
|
|
|
1095
1501
|
/**
|
|
1096
1502
|
* Creates ARC seal headers
|
|
1097
1503
|
*
|
|
1098
1504
|
* @param input - RFC822 formatted message or false for pre-calculated data
|
|
1099
1505
|
* @param data - Seal creation data
|
|
1100
|
-
* @returns Seal headers
|
|
1506
|
+
* @returns Seal headers (empty when no set was created), the errors that say why, and what the
|
|
1507
|
+
* default mode sealed that strict mode would have refused ('arc-instance-gap',
|
|
1508
|
+
* 'arc-cv-instance', and the DKIM signing warnings)
|
|
1101
1509
|
*/
|
|
1102
|
-
export function createSeal(input: MessageInput | false, data:
|
|
1510
|
+
export function createSeal(input: MessageInput | false, data: ARCCreateSealData): Promise<{ headers: string[]; errors: ARCSealError[]; warnings: string[] }>;
|
|
1103
1511
|
|
|
1104
1512
|
// ============================================================================
|
|
1105
1513
|
// BIMI
|
|
@@ -1128,6 +1536,12 @@ export interface BIMIOptions {
|
|
|
1128
1536
|
* Custom DNS resolver function
|
|
1129
1537
|
*/
|
|
1130
1538
|
resolver?: DNSResolver;
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Format the Authentication-Results entry in the RFC 8601 section 2.2 form (default: false).
|
|
1542
|
+
* BIMI has no other strict-only rules, the draft checks apply in both modes
|
|
1543
|
+
*/
|
|
1544
|
+
strict?: boolean;
|
|
1131
1545
|
}
|
|
1132
1546
|
|
|
1133
1547
|
/**
|
|
@@ -1338,10 +1752,33 @@ export interface MTASTSPolicyResult {
|
|
|
1338
1752
|
|
|
1339
1753
|
/**
|
|
1340
1754
|
* Status of the fetch operation
|
|
1755
|
+
*
|
|
1756
|
+
* - `found`: a new or changed policy was fetched
|
|
1757
|
+
* - `renewed`: the cached policy has the same id and has not expired, it is returned without a fetch
|
|
1758
|
+
* - `not_found`: the domain has no MTA-STS policy
|
|
1759
|
+
* - `errored`: the policy could not be fetched. With a valid cached policy, that policy is
|
|
1760
|
+
* returned with `error` set
|
|
1341
1761
|
*/
|
|
1342
1762
|
status: 'found' | 'renewed' | 'not_found' | 'errored';
|
|
1763
|
+
|
|
1764
|
+
/**
|
|
1765
|
+
* Lax acceptance markers, only present when the default lax mode accepted something strict mode would reject
|
|
1766
|
+
*/
|
|
1767
|
+
warnings?: MTASTSWarning[];
|
|
1343
1768
|
}
|
|
1344
1769
|
|
|
1770
|
+
/**
|
|
1771
|
+
* Markers for input that the default lax mode accepted but `strict: true` would reject
|
|
1772
|
+
*
|
|
1773
|
+
* - `txt-syntax`: the TXT record does not match the RFC 8461 3.1 syntax
|
|
1774
|
+
* - `http-status`: the policy was served with a 2xx status other than 200
|
|
1775
|
+
* - `content-type`: the policy was not served as `text/plain`
|
|
1776
|
+
* - `policy-syntax`: the policy file does not match the RFC 8461 3.2 syntax
|
|
1777
|
+
* - `cert-identity`: the Policy Host certificate matched only through the CN or a partial-label wildcard
|
|
1778
|
+
* - `expired-cache`: an expired cached policy was returned because the policy could not be fetched
|
|
1779
|
+
*/
|
|
1780
|
+
export type MTASTSWarning = 'txt-syntax' | 'http-status' | 'content-type' | 'policy-syntax' | 'cert-identity' | 'expired-cache';
|
|
1781
|
+
|
|
1345
1782
|
/**
|
|
1346
1783
|
* MTA-STS MX validation result
|
|
1347
1784
|
*/
|
|
@@ -1375,6 +1812,21 @@ export interface MTASTSOptions {
|
|
|
1375
1812
|
* Custom DNS resolver function
|
|
1376
1813
|
*/
|
|
1377
1814
|
resolver?: DNSResolver;
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* Apply the RFC 8461 rules exactly (default false)
|
|
1818
|
+
*/
|
|
1819
|
+
strict?: boolean;
|
|
1820
|
+
|
|
1821
|
+
/**
|
|
1822
|
+
* Overall time limit for the HTTPS policy request in milliseconds (default 60000)
|
|
1823
|
+
*/
|
|
1824
|
+
timeout?: number;
|
|
1825
|
+
|
|
1826
|
+
/**
|
|
1827
|
+
* Maximum size of the policy file in bytes (default 65536)
|
|
1828
|
+
*/
|
|
1829
|
+
maxPolicySize?: number;
|
|
1378
1830
|
}
|
|
1379
1831
|
|
|
1380
1832
|
// Note: MTA-STS functions (resolvePolicy, fetchPolicy, parsePolicy, validateMx, getPolicy)
|