@bobfrankston/mailx-types 0.1.71 → 0.1.73

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 (3) hide show
  1. package/package.json +1 -1
  2. package/trust.d.ts +11 -0
  3. package/trust.js +18 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.71",
3
+ "version": "0.1.73",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/trust.d.ts CHANGED
@@ -56,6 +56,14 @@ export interface TrustInput {
56
56
  ownAddresses: string[];
57
57
  /** Sanitized HTML body, for the link-construct checks. */
58
58
  bodyHtml: string;
59
+ /** The reader has approved this sender or its domain in allowlist.jsonc —
60
+ * "Always: *@icecer.com" in the remote-content banner, or "Trust all
61
+ * senders at ‹domain›" in the address menu. It answers the only question
62
+ * a spam score cannot: is this mail WANTED. It never silences forgery
63
+ * evidence, and it counts only on a message whose sender was actually
64
+ * proved — otherwise the list would be a switch a forger could flip by
65
+ * typing a trusted address into From. */
66
+ senderTrusted?: boolean;
59
67
  /** Plain-text body, for the character-level checks. Separate from the HTML
60
68
  * because markup legitimately contains things prose does not, and the
61
69
  * zero-width test would trip over an entity or an attribute value. */
@@ -105,6 +113,9 @@ export interface SpamScore {
105
113
  * listed in the Spamhaus SBL blocklist)". A number alone is unactionable;
106
114
  * this can be judged in a second. */
107
115
  reasons: string[];
116
+ /** The reader approved this sender AND the sender is proved: the score is
117
+ * still true, and no longer worth showing. The viewer hides the chip. */
118
+ trusted: boolean;
108
119
  }
109
120
  export declare function spamScoreOf(input: TrustInput): SpamScore;
110
121
  //# sourceMappingURL=trust.d.ts.map
package/trust.js CHANGED
@@ -354,6 +354,16 @@ function serverSpamVerdict(input) {
354
354
  const a = analyzeServerSpam(input);
355
355
  if (!a || !a.flagged)
356
356
  return null;
357
+ // Mail from a sender the reader has approved, whose identity is proved,
358
+ // and which nothing accuses of forgery: say nothing at all. The server's
359
+ // score answers "is this bulk", the reader has already answered "is it
360
+ // wanted", and repeating the first after the second is how a banner turns
361
+ // into furniture. (Bob 2026-08-28, having just clicked "Always:
362
+ // *@icecer.com" on a conference announcement: "I accepted the server so
363
+ // shouldn't this be considered safe?")
364
+ const trustedAndProved = input.senderTrusted && a.proved;
365
+ if (trustedAndProved && a.kind !== "forgery" && !a.authFailures.length)
366
+ return null;
357
367
  const numbers = Number.isFinite(a.score) && Number.isFinite(a.threshold)
358
368
  ? `SpamAssassin score ${a.score} of ${a.threshold}`
359
369
  : "flagged by SpamAssassin";
@@ -385,6 +395,13 @@ function serverSpamVerdict(input) {
385
395
  detail: `${numbers}${why}${bayes}`,
386
396
  };
387
397
  const proof = a.proved ? `${provedNote(a)} ` : "";
398
+ if (input.senderTrusted && !a.proved)
399
+ return {
400
+ id: "server-spam-verdict",
401
+ severity: "caution",
402
+ text: "This claims to be a sender you trust, but nothing proves it is.",
403
+ detail: `No DMARC pass, no valid signature from the sending domain, and it passed through hosts your server does not trust. ${numbers}${why}`,
404
+ };
388
405
  return {
389
406
  id: "server-spam-verdict",
390
407
  severity: "caution",
@@ -658,6 +675,7 @@ export function spamScoreOf(input) {
658
675
  kind: a.kind,
659
676
  proved: a.proved,
660
677
  provedBy: a.provedBy,
678
+ trusted: !!input.senderTrusted && a.proved && a.kind !== "forgery" && !a.authFailures.length,
661
679
  reasons: a.reasons,
662
680
  };
663
681
  }