@bobfrankston/rmfmail 1.2.282 → 1.2.283
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/.commitmsg +17 -62
- package/client/app.bundle.js +3 -3
- package/client/app.bundle.js.map +2 -2
- package/client/components/message-viewer.js +15 -14
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +15 -14
- package/client/styles/components.css +6 -5
- package/npmchanges.md +65 -0
- package/package.json +5 -5
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-56556 → node_modules.npmglobalize-stash-23452}/.package-lock.json +0 -0
|
@@ -1652,26 +1652,27 @@ export async function showMessage(accountId: string, uid: number, folderId?: num
|
|
|
1652
1652
|
// it can be checked rather than believed. (Claude Code 2026-08-25)
|
|
1653
1653
|
bodyEl.innerHTML = "";
|
|
1654
1654
|
|
|
1655
|
-
// The server's spam score
|
|
1656
|
-
// (Bob 2026-08-25: "
|
|
1657
|
-
//
|
|
1658
|
-
//
|
|
1659
|
-
//
|
|
1660
|
-
// the reader deserves the number rather than a silent pass.
|
|
1655
|
+
// The server's spam score — shown ONLY when it is high enough to mean
|
|
1656
|
+
// something (Bob 2026-08-25: "only show the spam score if it is high
|
|
1657
|
+
// and likely"). The first cut put a chip on every message that carried
|
|
1658
|
+
// a score, which is nearly all of them: a badge that is always present
|
|
1659
|
+
// carries no information and becomes furniture the eye stops reading.
|
|
1661
1660
|
//
|
|
1662
|
-
// The
|
|
1663
|
-
//
|
|
1664
|
-
//
|
|
1661
|
+
// The floor is halfway to the SERVER'S OWN threshold, so it is a
|
|
1662
|
+
// proportion rather than a constant and follows if that threshold ever
|
|
1663
|
+
// changes. Halfway is where "did not quite trip the wire" starts being
|
|
1664
|
+
// a different statement from "clean" — the sextortion mail that
|
|
1665
|
+
// prompted all of this scored 2.7 against 4.5 and was delivered
|
|
1666
|
+
// unflagged, so showing only the flagged ones would miss exactly the
|
|
1667
|
+
// case worth showing.
|
|
1665
1668
|
const spam = (msg as any).spamScore as { score: number; threshold: number; flagged: boolean };
|
|
1666
|
-
if (spam && Number.isFinite(spam.score)) {
|
|
1667
|
-
const level = spam.flagged ? "high" :
|
|
1669
|
+
if (spam && Number.isFinite(spam.score) && (spam.flagged || spam.score >= spam.threshold / 2)) {
|
|
1670
|
+
const level = spam.flagged ? "high" : "mid";
|
|
1668
1671
|
const chip = document.createElement("div");
|
|
1669
1672
|
chip.className = `mv-spamscore mv-spamscore-${level}`;
|
|
1670
1673
|
chip.title = level === "high"
|
|
1671
1674
|
? "Your mail server scored this at or above its spam threshold and flagged it."
|
|
1672
|
-
:
|
|
1673
|
-
? "Below your server's spam threshold, but more than halfway to it."
|
|
1674
|
-
: "Well below your mail server's spam threshold.";
|
|
1675
|
+
: "Below your server's spam threshold, but more than halfway to it.";
|
|
1675
1676
|
chip.textContent = `spam score ${spam.score} of ${spam.threshold}`;
|
|
1676
1677
|
bodyEl.appendChild(chip);
|
|
1677
1678
|
}
|
|
@@ -2977,10 +2977,12 @@ body.calendar-sidebar-on .calendar-sidebar { display: flex; }
|
|
|
2977
2977
|
opacity: 0.9;
|
|
2978
2978
|
}
|
|
2979
2979
|
|
|
2980
|
-
/* Receiving server's spam score
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2980
|
+
/* Receiving server's spam score. Rendered only when the score is at least
|
|
2981
|
+
halfway to the server's own threshold — a chip on every message is furniture
|
|
2982
|
+
the eye stops reading (Bob 2026-08-25: "only show the spam score if it is
|
|
2983
|
+
high and likely"). Two levels only, because anything quieter never renders
|
|
2984
|
+
at all. The cutoff is a proportion, computed in message-viewer, which is why
|
|
2985
|
+
no number appears here. (Claude Code 2026-08-25) */
|
|
2984
2986
|
.mv-spamscore {
|
|
2985
2987
|
display: inline-block;
|
|
2986
2988
|
margin: 0 0 var(--gap-sm) 0;
|
|
@@ -2990,6 +2992,5 @@ body.calendar-sidebar-on .calendar-sidebar { display: flex; }
|
|
|
2990
2992
|
font-family: var(--font-mono, ui-monospace, monospace);
|
|
2991
2993
|
letter-spacing: 0.01em;
|
|
2992
2994
|
}
|
|
2993
|
-
.mv-spamscore-low { background: var(--color-bg-alt, #eee); color: var(--color-text-muted, #666); }
|
|
2994
2995
|
.mv-spamscore-mid { background: oklch(0.55 0.18 50); color: white; font-weight: 600; }
|
|
2995
2996
|
.mv-spamscore-high { background: oklch(0.42 0.20 25); color: white; font-weight: 700; }
|
package/npmchanges.md
CHANGED
|
@@ -703,3 +703,68 @@ tests/trust.test.ts pins both phishes and, as importantly, the negative cases:
|
|
|
703
703
|
a self-sent note with no auth headers, SPF failure on mail not claiming to be
|
|
704
704
|
you, a modest image map, a same-site return-path parameter, and ordinary mail.
|
|
705
705
|
|
|
706
|
+
## v1.2.282 — 2026-08-25
|
|
707
|
+
|
|
708
|
+
Catch the spam that scores under the threshold
|
|
709
|
+
|
|
710
|
+
A sextortion mail arrived that none of yesterday's checks caught ("yet this
|
|
711
|
+
spam got through"). SpamAssassin scored it 2.7 against a 4.5 threshold and
|
|
712
|
+
delivered it, so server-spam-verdict correctly stayed silent — the server said
|
|
713
|
+
no. Three independent proofs of forgery were sitting in the message anyway.
|
|
714
|
+
|
|
715
|
+
spf=none now counts as "not authenticated" for self-claimed mail, not just
|
|
716
|
+
fail. bobf.frankston.com publishes no SPF record, so nothing sent as that
|
|
717
|
+
domain can ever FAIL — only come back none. Treating none as neutral left the
|
|
718
|
+
least-protected domain the least defended, which is backwards. Checked against
|
|
719
|
+
the real store before changing it, 50,000 messages scanned:
|
|
720
|
+
|
|
721
|
+
From at one of Bob's domains 10,338
|
|
722
|
+
no Authentication-Results 10,034 <- his own mail, still ignored
|
|
723
|
+
spf=pass 243 <- still ignored
|
|
724
|
+
spf=fail / softfail 33
|
|
725
|
+
spf=none 18 <- nearly all spam
|
|
726
|
+
|
|
727
|
+
The 18 were "Settle your debt", "AAA Final notice", "McAfee Protection Plan
|
|
728
|
+
Ended" and similar. Legitimate self-mail sits in the no-header group: it never
|
|
729
|
+
crossed a trust boundary, so there is no Authentication-Results to read, and
|
|
730
|
+
that case is untouched. An explicit spf/dkim/dmarc=pass still silences the
|
|
731
|
+
check.
|
|
732
|
+
|
|
733
|
+
relay-auth-mismatch — Bob's own catch: "note that the origin is different than
|
|
734
|
+
the from domain in antiabuse and in source-auth".
|
|
735
|
+
|
|
736
|
+
From: universe@bobf.frankston.com
|
|
737
|
+
X-Source-Auth: admin@calebjross.com
|
|
738
|
+
X-Source-Cap: (base64) calebjro;calebjro;box2202.bluehost.com
|
|
739
|
+
Message-ID: <...@calebjross.com>
|
|
740
|
+
|
|
741
|
+
A Bluehost cPanel account belonging to an unrelated domain authenticated and
|
|
742
|
+
sent mail wearing his address. That stamp is written by the RELAY, downstream
|
|
743
|
+
of whoever submitted the message, so a forger cannot remove or edit it — which
|
|
744
|
+
makes the disagreement about as close to proof as mail headers get. Compared at
|
|
745
|
+
the registrable-domain level, since a host legitimately sending for a domain
|
|
746
|
+
authenticates as some account AT that domain and the local part varies for
|
|
747
|
+
ordinary reasons. Covers the cPanel/Exim, Postfix and Communigate spellings.
|
|
748
|
+
|
|
749
|
+
zero-width-obfuscation — the body had ~1380 zero-width characters stuffed
|
|
750
|
+
between letters, so "i regret to inform you" reached every word-matching filter
|
|
751
|
+
as gibberish while rendering as clean prose. SpamAssassin saw it
|
|
752
|
+
(UNICODE_OBFU_ZW_MANY) and still totalled only 2.7. Counted only BETWEEN TWO
|
|
753
|
+
LATIN LETTERS, which is what obfuscation looks like and what the legitimate
|
|
754
|
+
uses are not: an emoji ZWJ joins pictographs, Arabic and Indic ZWNJ sit between
|
|
755
|
+
their own scripts' letters. The occurrence count guards against a stray
|
|
756
|
+
character surviving a copy-paste; it is a sanity bound, not a sensitivity dial.
|
|
757
|
+
|
|
758
|
+
The score itself is now always visible — a chip above the body reading "spam
|
|
759
|
+
score 2.7 of 4.5" ("should we have a flag ... with the spam assassin number and
|
|
760
|
+
a color code?"). Separate from the findings on purpose: a finding is an
|
|
761
|
+
accusation and fires only on evidence, this is a measurement. Colour is a
|
|
762
|
+
PROPORTION of the server's own threshold — red once flagged, amber past halfway
|
|
763
|
+
to its line, quiet below — so it follows if the server's threshold changes
|
|
764
|
+
rather than hardcoding a number. Viewer only for now; the message-list row
|
|
765
|
+
would need the score stored at sync time.
|
|
766
|
+
|
|
767
|
+
tests/trust.test.ts covers all three, and the negative cases with them: spf=pass
|
|
768
|
+
and no-header both silent, a relay identity at the same organisation, an emoji
|
|
769
|
+
ZWJ family sequence, and a stray zero-width character from a paste.
|
|
770
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/rmfmail",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.283",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@bobfrankston/iflow-direct": "^0.1.65",
|
|
36
36
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
37
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
38
|
-
"@bobfrankston/mailx-store-web": "^0.1.
|
|
37
|
+
"@bobfrankston/mailx-imap": "^0.1.160",
|
|
38
|
+
"@bobfrankston/mailx-store-web": "^0.1.88",
|
|
39
39
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
40
40
|
"@bobfrankston/miscinfo": "^1.0.20",
|
|
41
41
|
"@bobfrankston/msger": "^0.1.429",
|
|
@@ -116,8 +116,8 @@
|
|
|
116
116
|
"dependencies": {
|
|
117
117
|
"@bobfrankston/iflow-direct": "^0.1.65",
|
|
118
118
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
119
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
120
|
-
"@bobfrankston/mailx-store-web": "^0.1.
|
|
119
|
+
"@bobfrankston/mailx-imap": "^0.1.160",
|
|
120
|
+
"@bobfrankston/mailx-store-web": "^0.1.88",
|
|
121
121
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
122
122
|
"@bobfrankston/miscinfo": "^1.0.20",
|
|
123
123
|
"@bobfrankston/msger": "^0.1.429",
|