@bobfrankston/mailx-types 0.1.65 → 0.1.69
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/package.json +1 -1
- package/trust.d.ts +7 -1
- package/trust.js +44 -4
package/package.json
CHANGED
package/trust.d.ts
CHANGED
|
@@ -93,8 +93,14 @@ export interface SpamScore {
|
|
|
93
93
|
* the chip stays visible and says so, rather than going red at a number
|
|
94
94
|
* and teaching the reader to ignore it. (Claude Code 2026-08-27) */
|
|
95
95
|
kind: "forgery" | "association" | "bulk" | "unclassified";
|
|
96
|
-
/** The From
|
|
96
|
+
/** The From line is proved — provedBy says by what. */
|
|
97
97
|
proved: boolean;
|
|
98
|
+
/** Which evidence: the server's DMARC verdict, a valid signature from the
|
|
99
|
+
* author's own domain, or never having crossed an untrusted host. The
|
|
100
|
+
* chip's tooltip has to say which one, or it asserts a DMARC pass that
|
|
101
|
+
* may not exist (Bob's own mail carries no Authentication-Results at
|
|
102
|
+
* all). */
|
|
103
|
+
provedBy: "dmarc" | "dkim" | "trusted" | "";
|
|
98
104
|
/** Top scoring rules, named — "6.0 URIBL_SBL (Contains an URL's NS IP
|
|
99
105
|
* listed in the Spamhaus SBL blocklist)". A number alone is unactionable;
|
|
100
106
|
* this can be judged in a second. */
|
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:
|
|
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:
|
|
308
|
-
|
|
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
|
|
356
|
+
const proof = a.proved ? `${provedNote(a)} ` : "";
|
|
318
357
|
return {
|
|
319
358
|
id: "server-spam-verdict",
|
|
320
359
|
severity: "caution",
|
|
@@ -587,6 +626,7 @@ export function spamScoreOf(input) {
|
|
|
587
626
|
flagged: a.flagged,
|
|
588
627
|
kind: a.kind,
|
|
589
628
|
proved: a.proved,
|
|
629
|
+
provedBy: a.provedBy,
|
|
590
630
|
reasons: a.reasons,
|
|
591
631
|
};
|
|
592
632
|
}
|