@bobfrankston/mailx-types 0.1.65 → 0.1.67

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 (2) hide show
  1. package/package.json +1 -1
  2. package/trust.js +43 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/trust.js CHANGED
@@ -242,6 +242,28 @@ function analyzeServerSpam(input) {
242
242
  : present("other") ? "unclassified"
243
243
  : rules.length ? "bulk"
244
244
  : "unclassified";
245
+ // Two proofs SpamAssassin states in its own rule list, which mail that
246
+ // never crossed a trust boundary carries INSTEAD of an Authentication-Results
247
+ // header — Bob's own message to John Levine about spam handling, 2026-08-27,
248
+ // had neither AR nor DMARC and got a caution banner reading "links and
249
+ // attachments here should not be opened" on prose he had just written:
250
+ //
251
+ // ALL_TRUSTED Passed through trusted hosts only via SMTP
252
+ // DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain
253
+ //
254
+ // DKIM_VALID_AU is the same evidence DMARC alignment rests on — the author
255
+ // domain signed it and the signature verified — so it proves the From line on
256
+ // its own. ALL_TRUSTED says the message reached the mailbox without passing
257
+ // through a host the server distrusts, which is Bob's own authenticated
258
+ // submission; nothing could have injected it in transit. (What flagged his
259
+ // message was URIBL_DBL_SPAM 8.0 on blueorchestra.org — the domain he was
260
+ // quoting IN a discussion about spam. Guilt by association at its purest.)
261
+ const has = (name) => rules.some(r => r.name === name);
262
+ const provedBy = dmarc.pass ? "dmarc"
263
+ : has("DKIM_VALID_AU") ? "dkim"
264
+ : has("ALL_TRUSTED") ? "trusted"
265
+ : "";
266
+ const fromDomain = (bare(input.fromAddress).split("@")[1] || "").toLowerCase();
245
267
  const reasons = rules
246
268
  .filter(r => r.cls !== "neutral" && scored(r))
247
269
  .sort((a, b) => (b.score || 0) - (a.score || 0))
@@ -252,7 +274,8 @@ function analyzeServerSpam(input) {
252
274
  });
253
275
  return {
254
276
  score, threshold, flagged, rules, kind,
255
- proved: dmarc.pass, provedDomain: dmarc.domain, provedPolicy: dmarc.policy,
277
+ proved: !!provedBy, provedBy,
278
+ provedDomain: dmarc.domain || fromDomain, provedPolicy: dmarc.policy,
256
279
  bayesHam: rules.some(r => r.name === "BAYES_00" || r.name === "BAYES_01"),
257
280
  authFailures: dmarc.authFailures,
258
281
  reasons,
@@ -282,6 +305,20 @@ function analyzeServerSpam(input) {
282
305
  * cannot classify, which is never a
283
306
  * reason to go quiet.
284
307
  */
308
+ /** How the From line was proved, in the reader's terms — named so it can be
309
+ * checked against the headers rather than believed. */
310
+ function provedNote(a) {
311
+ switch (a.provedBy) {
312
+ case "dmarc":
313
+ return `DMARC pass, aligned${a.provedPolicy ? `, policy ${a.provedPolicy}` : ""}.`;
314
+ case "dkim":
315
+ return `Valid signature from ${a.provedDomain}, the sending domain itself (DKIM_VALID_AU).`;
316
+ case "trusted":
317
+ return "It passed through trusted hosts only (ALL_TRUSTED) — nothing untrusted handled it.";
318
+ default:
319
+ return "";
320
+ }
321
+ }
285
322
  function serverSpamVerdict(input) {
286
323
  const a = analyzeServerSpam(input);
287
324
  if (!a || !a.flagged)
@@ -304,8 +341,10 @@ function serverSpamVerdict(input) {
304
341
  return {
305
342
  id: "server-spam-verdict",
306
343
  severity: "info",
307
- text: `Your mail server scored this as spam, but ${a.provedDomain} proved it sent this.`,
308
- detail: `DMARC pass, aligned${a.provedPolicy ? `, policy ${a.provedPolicy}` : ""}. ${numbers}${why}${bayes}`,
344
+ text: a.provedBy === "trusted"
345
+ ? "Your mail server scored this as spam, but it reached you without leaving your own systems."
346
+ : `Your mail server scored this as spam, but ${a.provedDomain} proved it sent this.`,
347
+ detail: `${provedNote(a)} ${numbers}${why}${bayes}`,
309
348
  };
310
349
  if (a.kind === "bulk")
311
350
  return {
@@ -314,7 +353,7 @@ function serverSpamVerdict(input) {
314
353
  text: "Your mail server rates this bulk mail — nothing that scored tests who sent it.",
315
354
  detail: `${numbers}${why}${bayes}`,
316
355
  };
317
- const proof = a.proved ? `${a.provedDomain} passed DMARC, so the From line is genuine. ` : "";
356
+ const proof = a.proved ? `${provedNote(a)} ` : "";
318
357
  return {
319
358
  id: "server-spam-verdict",
320
359
  severity: "caution",