mailauth 7.1.1 → 7.3.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.
Files changed (58) hide show
  1. package/.c8rc.json +11 -0
  2. package/.prettierignore +3 -0
  3. package/.release-please-manifest.json +1 -1
  4. package/CHANGELOG.md +68 -0
  5. package/README.md +131 -31
  6. package/bin/mailauth.js +62 -0
  7. package/cli.md +150 -8
  8. package/docs/arc.md +62 -54
  9. package/docs/bimi.md +153 -54
  10. package/docs/dkim.md +37 -36
  11. package/docs/dkim2.md +154 -0
  12. package/docs/dmarc.md +4 -1
  13. package/docs/mta-sts.md +12 -9
  14. package/docs/spf.md +14 -13
  15. package/index.d.ts +583 -42
  16. package/lib/arc/index.d.ts +6 -2
  17. package/lib/arc/index.js +45 -15
  18. package/lib/bimi/index.js +362 -102
  19. package/lib/bimi/validate-svg.js +493 -60
  20. package/lib/commands/dkim2-hash.js +25 -0
  21. package/lib/commands/dkim2-sign.js +77 -0
  22. package/lib/commands/dkim2-verify.js +59 -0
  23. package/lib/commands/report.js +10 -35
  24. package/lib/commands/resolver.js +51 -0
  25. package/lib/commands/seal.js +10 -38
  26. package/lib/commands/spf.js +4 -35
  27. package/lib/dkim/dkim-signer.js +50 -30
  28. package/lib/dkim/dkim-verifier.js +79 -27
  29. package/lib/dkim/header/simple.js +2 -2
  30. package/lib/dkim/sign.js +7 -2
  31. package/lib/dkim/syntax.js +11 -0
  32. package/lib/dkim/verify.js +1 -0
  33. package/lib/dkim2/dkim2-parser.js +52 -0
  34. package/lib/dkim2/dkim2-signer.js +334 -0
  35. package/lib/dkim2/dkim2-verifier.js +572 -0
  36. package/lib/dkim2/fields.js +570 -0
  37. package/lib/dkim2/hash.d.ts +3 -0
  38. package/lib/dkim2/hash.js +51 -0
  39. package/lib/dkim2/key.js +203 -0
  40. package/lib/dkim2/message-state.js +181 -0
  41. package/lib/dkim2/recipe.js +134 -0
  42. package/lib/dkim2/sign.d.ts +3 -0
  43. package/lib/dkim2/sign.js +38 -0
  44. package/lib/dkim2/verify.d.ts +3 -0
  45. package/lib/dkim2/verify.js +19 -0
  46. package/lib/dmarc/author-domain.js +270 -5
  47. package/lib/dmarc/get-dmarc-record.js +24 -11
  48. package/lib/dmarc/verify.js +7 -3
  49. package/lib/mailauth.js +57 -14
  50. package/lib/mta-sts.js +37 -11
  51. package/lib/parse-received.js +214 -25
  52. package/lib/spf/index.js +21 -4
  53. package/lib/spf/macro.js +5 -3
  54. package/lib/spf/spf-verify.js +14 -5
  55. package/lib/spf/syntax.js +5 -0
  56. package/lib/tools.js +160 -21
  57. package/package.json +15 -6
  58. package/stryker.config.json +16 -0
package/docs/mta-sts.md CHANGED
@@ -48,15 +48,17 @@ const { policy, status, warnings } = await getPolicy('example.com', knownPolicy,
48
48
 
49
49
  ### Policy Object Fields
50
50
 
51
- | Field | Type | Presence | Description |
52
- | --------- | --------------- | ---------------- | ------------------------------------------------------------------------- |
53
- | `id` | `string\|false` | Always | Policy ID from DNS TXT record, or `false` if not found |
54
- | `version` | `string` | Policy found | Always `"STSv1"` for valid policies |
55
- | `mode` | `string` | Always | Policy mode (see below) |
56
- | `mx` | `string[]` | Listed in policy | Array of allowed MX hostnames (may include wildcards) |
57
- | `maxAge` | `number` | Policy found | Policy validity period in seconds |
58
- | `expires` | `string` | Policy found | ISO 8601 expiration timestamp |
59
- | `error` | `Error` | Status errored | Error object explaining why no live policy could be discovered or fetched |
51
+ | Field | Type | Presence | Description |
52
+ | ------------ | --------------- | ---------------- | ------------------------------------------------------------------------- |
53
+ | `id` | `string\|false` | Always | Policy ID from DNS TXT record, or `false` if not found |
54
+ | `version` | `string` | Policy found | Always `"STSv1"` for valid policies |
55
+ | `mode` | `string` | Always | Policy mode (see below) |
56
+ | `mx` | `string[]` | Listed in policy | Array of allowed MX hostnames (may include wildcards) |
57
+ | `maxAge` | `number` | Policy found | Policy validity period in seconds |
58
+ | `expires` | `string` | Policy found | ISO 8601 expiration timestamp |
59
+ | `error` | `Error` | Status errored | Error object explaining why no live policy could be discovered or fetched |
60
+ | `retryId` | `string` | Fetch failed | New policy ID that could not be fetched while the cached policy is kept |
61
+ | `retryAfter` | `string` | Fetch failed | ISO 8601 timestamp before which `retryId` is not fetched again |
60
62
 
61
63
  ### Mode Values
62
64
 
@@ -79,6 +81,7 @@ await cache.set(domain, policy); // keep the entry at least until policy.expires
79
81
  - **Valid cached policy, same ID in DNS:** the cached policy is returned as `renewed` without an HTTPS request. Its `expires` value is not extended, so the policy file is fetched again once it expires.
80
82
  - **Valid cached policy, no live policy:** if the TXT lookup fails, the TXT record is missing, unusable or duplicated, the policy host has no address, or the new policy can not be fetched or is invalid, the cached policy is returned with `status: "errored"` and the reason in `policy.error`. A valid cached policy is never replaced by `mode: "none"` because of a discovery failure. An exception is a cached policy that already has mode `none`, which may be replaced by the retry placeholder described below.
81
83
  - **Expired cached policy:** the policy file is fetched again. If the fetch fails, the default lax mode keeps returning the expired `enforce` or `testing` policy (with the `expired-cache` warning), while strict mode treats the domain as having no policy.
84
+ - **Cached policy kept after a failed fetch:** when the policy for a new ID can not be fetched and the cached policy is returned instead, it gets `retryId` (the new ID) and `retryAfter` (one hour ahead). Storing it makes the next calls return it as `errored` without HTTPS requests while the DNS record has the same ID, so the policy host is not retried for an hour (RFC 8461 3.3). A different policy ID is fetched right away, and the fields are dropped once a policy is fetched.
82
85
  - **No usable cached policy and the fetch fails:** the result is a placeholder with the new policy ID, mode `none`, and `expires` set one hour ahead. Storing it makes the next calls return it as `renewed` without HTTPS requests, so the policy host is not retried for an hour. A changed policy ID is fetched right away.
83
86
 
84
87
  ## Strict Mode
package/docs/spf.md CHANGED
@@ -19,17 +19,17 @@ const result = await spf({
19
19
 
20
20
  ## Options
21
21
 
22
- | Option | Type | Default | Description |
23
- | ----------------- | ---------- | ----------------- | -------------------------------------------------------------------------------------------------------------- |
24
- | `sender` | `string` | `postmaster@helo` | MAIL FROM address. An empty value means a null reverse-path, so the HELO identity is checked |
25
- | `ip` | `string` | Required | SMTP client IP address. IPv4-mapped IPv6 addresses (`::ffff:192.0.2.1`, `::ffff:c000:201`) are checked as IPv4 |
26
- | `helo` | `string` | | EHLO/HELO hostname |
27
- | `mta` | `string` | `os.hostname()` | Hostname of the MTA performing the check |
28
- | `resolver` | `function` | `dns.resolve` | Custom DNS resolver |
29
- | `maxResolveCount` | `number` | `10` | Maximum DNS lookups allowed |
30
- | `maxVoidCount` | `number` | `2` | Maximum void (empty) DNS lookups allowed |
31
- | `maxElapsedTime` | `number` | No limit | Maximum time in milliseconds for the whole evaluation. If exceeded, the result is `temperror` (RFC 7208 4.6.4) |
32
- | `strict` | `boolean` | `false` | Follow RFC 7208 exactly instead of the lenient default, see [Strict Mode](#strict-mode) |
22
+ | Option | Type | Default | Description |
23
+ | ----------------- | ---------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
24
+ | `sender` | `string` | `postmaster@helo` | MAIL FROM address. An empty value means a null reverse-path, so the HELO identity is checked |
25
+ | `ip` | `string` | Required | SMTP client IP address. IPv4-mapped IPv6 addresses (`::ffff:192.0.2.1`, `::ffff:c000:201`) are checked as IPv4 |
26
+ | `helo` | `string` | | EHLO/HELO hostname |
27
+ | `mta` | `string` | `os.hostname()` | Hostname of the MTA performing the check |
28
+ | `resolver` | `function` | `dns.resolve` | Custom DNS resolver |
29
+ | `maxResolveCount` | `number` | `10` | Maximum DNS lookups allowed |
30
+ | `maxVoidCount` | `number` | `2` | Maximum void (empty) DNS lookups allowed. `0` is a valid limit, a value that is not a non-negative number uses the default |
31
+ | `maxElapsedTime` | `number` | No limit | Maximum time in milliseconds for the whole evaluation. If exceeded, the result is `temperror` (RFC 7208 4.6.4) |
32
+ | `strict` | `boolean` | `false` | Follow RFC 7208 exactly instead of the lenient default, see [Strict Mode](#strict-mode) |
33
33
 
34
34
  A missing or invalid `ip` gives a `temperror` result with the comment `missing or invalid client IP address`.
35
35
 
@@ -68,7 +68,7 @@ In strict mode `status.smtp` holds only the identity that was checked (RFC 8601
68
68
 
69
69
  ## explanation
70
70
 
71
- When the result is `fail` because of a mechanism match and the record has an `exp` modifier, the explanation string is fetched and expanded as described in RFC 7208 section 6.2. Any problem with it (DNS error, no or several TXT records, syntax error, non-ASCII text) means that there is no explanation. The explanation lookup does not count toward the DNS lookup limits. The explanation of an included record is never used, and after a redirect only the explanation of the redirect target is used.
71
+ When the result is `fail` because of a mechanism match and the record has an `exp` modifier, the explanation string is fetched and expanded as described in RFC 7208 section 6.2. Any problem with it (DNS error, no or several TXT records, syntax error, text that is not printable US-ASCII before or after macro expansion) means that there is no explanation. The explanation lookup does not count toward the DNS lookup limits. The explanation of an included record is never used, and after a redirect only the explanation of the redirect target is used.
72
72
 
73
73
  The explanation is text from the domain owner. It is not added to the generated headers. If you show it to an SMTP client, make clear that it comes from a third party, for example by prepending `"<domain> explains: "`.
74
74
 
@@ -144,7 +144,8 @@ These rules apply in both modes:
144
144
  - `%{p}` expands to a validated PTR name or `unknown`.
145
145
  - Trailing dots in domain-specs are accepted, names longer than 253 characters are truncated from the left, and uppercase macros are URL escaped.
146
146
  - An internationalized MAIL FROM domain is converted to A-labels.
147
- - Address lookups of MX hosts are not counted as void lookups.
147
+ - A single trailing dot is removed from the MAIL FROM domain and the HELO name (RFC 7208 section 4.3). A zero-length label anywhere else gives `none`.
148
+ - Address lookups of MX hosts are not counted as void lookups. The PTR lookup of a `%{p}` expansion is not counted as a void lookup either, the query of the term itself decides if the term was void.
148
149
 
149
150
  ## Example Output
150
151