GitHub RadarBlue team tool
Spam filter for Apple's Mail.app on macOS Primary language: AppleScript. 34 stars.
Project links:Open GitHub projectBack to radar
This spamfilter lets you easily define keyword-based filter rules as well as authentication policies for the senders you trust. These rules can be applied for each of your email accounts individually and cause spam messages to be marked as Junk and moved to the trash folder. This filter makes use of Mail.app's scripting interface for Applescript and JXA.
There are two invokation modes for the spamfilter script that impose different installation tasks. The first mode relies on Mail.app's rule infrastructure to automate handling of new incoming messages dedicated to default inboxes:
spamfilter.zip from Releasescd path/to/spamfiltersh install.sh and answer the questionsAs rule invokation is restricted to default inboxes it might be desirable to also enable filtering on other mailboxes. This second invocation mode checks all mailboxes in a 15 minutes interval by setting up a launch agent for launchd daemon: Perform the steps stated above, except step 3: Run sh install.sh -launchagent to set up the default launch agent or sh install.sh -launchagent 600 to configure your own interval in seconds, e.g., 600 for 10 minutes.
The configuration of the script as well as your custom rules are stored in ~/Library/Application Scripts/com.apple.mail/spamfilter-rules.json. Edit this file using the text editor of your choice. A simple sample (without sender authentication policies) might look like this:
{
"shouldAlertMatchDetails": false,
"shouldLogActivity": false,
"rulesList": [
{
"email": "me@example.com",
"fromWhitelist": {"shouldTest": false, "list": []},
"senderBlacklist": {"similarityList": [{"selector": "GitHub", "similarityMode": "dl:2"}],
"list": ["John Doe", "@evil.org", "GitHub"]
},
"subjectBlacklist": {"list": ["50% off", "Account suspended"]},
"contentBlacklist": {"list": ["Dear customer"]},
"headerBlacklist": [{"name": "received", "list": ["from mta.eval.to"]},
{"name": "content-type", "list": ["application/"]}
]
},
{
"email": "foo@bar.com",
"fromWhitelist": {"shouldTest": true, "list": ["GitHub"]},
"senderBlacklist": {"list": ["Jane Doe", "support@evil.org"]},
"subjectBlacklist": {"list": []},
"contentBlacklist": {"list": ["Dear customer"]},
"mailboxList": [{"name": "Another Mailbox",
"fromWhitelist": {"shouldTest": false, "list": []},
"senderBlacklist": {"list": []},
"subjectBlacklist": {"list": ["50% off"]},
"contentBlacklist": {"list": []}
}]
}
]
}Apart from your account-specific rules the JSON object above comprises general settings like shouldAlertMatchDetails that always affect the filtering regardless of your rules. shouldAlertMatchDetails set to true (boolean) helps debugging false-positives by telling you which rule has matched. shouldLogActivity logs more information about each message in the file spamfilter.log next to your spamfilter-rules.json.
The rulesList property contains the array of mail accounts for which you want to enable filtering. Accounts not listed there, as well as already read messages, are not filtered at all. Here's a description of an account rule for its default INBOX:
email indicates the mail address of your account.name [optional] indicates the usually unique account name as displayed by Mail.app in its account overview, e.g., Example.com or iCloud. This provides an alternative way to find your rules if Mail.app doesn't store any addresses for the account or has forgotten them due to bugs.trustList [optional] defines authentication policies for user-defined trusted senders identified by selector text strings that are compared to the "From" header of the message. If a selector has matched, one of its specified policies must be fulfilled, otherwise the message is classified as spam. A full description of this advanced feature is stated in the section below.fromWhitelist controls the test whether the sender contains a full name (first name and last name) or just a single word. If shouldTest is true, one-word names are considered as spam matches. Whitelist exceptions are likely to be necessary then, e.g., for GitHub in the foo@bar.com rule above (case-sensitive). A whitelist match doesn't stop proceeding with remaining tests.senderBlacklist searches the "From" header of the message for all items in the corresponding blacklist. Those items can be names, email addresses or only string components of them. There are two sub-blacklists:similarityList [optional] list of similarity objects: compares the "From" header with the selector string property through applying the algorithm given by property similarityMode. The available modes canonical, dl and jw are explained in the trustList section below. As this is a blacklist (in opposition to the trustList), a selector match leads to a spam classification here.list literal, case-sensitive searchsubjectBlacklist searches the "Subject" header of the message for all items in the blacklist (case-sensitive).contentBlacklist searches the message content for all items in the blacklist (case-sensitive). Only text content is tested; binary data is skipped.headerBlacklist [optional] searches further arbitrary message headers defined by the name property (lower-case) for all items in the corresponding blacklist (case-sensitive).mailboxList [optional] a list of rule objects for additional mailboxes different from the default INBOX. Those rules work analogously to the ones above (from to ), but only for a specific mailbox. Mailbox names are case-sensitive.The list items from trustList to headerBlacklist are always processed in the order as defined above.
These policies help to detect phishing and spoofing through the definition of constraints that certain user-defined trusted senders must comply with. Senders are identified by selector strings searched in the whole "From" header of the message (human-readable display name + email address). Corresponding policies are based upon a combination of DKIM (RFC6376, wiki), SPF (RFC7208, wiki), DMARC (RFC7489, wiki) and Authentication-Results (RFC8601, wiki) IETF standards to verify the original sender domain found within the "From" header as well as a configurable list of allowed sender addresses for each selector. Violation of the so found policy, i.e., an invalid sender address for the given selector or invalid headers, causes a spam match per default.
An extended sample might look like this:
{
"shouldAlertMatchDetails": false,
"shouldLogActivity": false,
"rulesList": [
{
"email": "me@example.com",
"name": "Example.com",
"trustList": {"authResultsIssuers": "example.com", "selectorMode": "relaxed", "list": [
{"selector": ["Alice Bobman", "Bobman, Alice"], "policyList": [
{"addresses": "alice.bobman@examplemail.com", "dkim": "self", "spf": "strict", "authResults": {"methods": "dkim,spf,dmarc"}}
]},
{"selector": "Example", "policyList": [
{"addresses": "*@example.com", "dkim": "self", "spf": "strict", "authResults": {"methods": "dkim,spf,dmarc"}},
{"addresses": "*@examplemail.com", "onRemedy": "skip", "dkim": "self", "spf": "strict", "authResults": {"methods": "dkim,spf,dmarc"}, "proceedTests": true}
]}
]},
"fromWhitelist": {"shouldTest": false, "list": []},
"senderBlacklist": {"similarityList": [{"selector": "GitHub", "similarityMode": "dl:2"}],
"list": ["John Doe", "@evil.org"]},
"subjectBlacklist": {"list": ["50% off", "Account suspended"]},
"contentBlacklist": {"list": ["Dear customer"]},
"headerBlacklist": [{"name": "received", "list": ["from mta.eval.to"]},
{"name": "content-type", "list": ["application/"]}
]
},
{
"email": "foo@bar.com",
"trustList": {"authResultsIssuers": "bar.com", "onViolation": "trash", "list": [
{"selector": "GitHub", "policyList": [
{"addresses": "*@github.com", "blueprint": "default-full", "proceedTests": true},
{"addresses": "josh@macgithubintosh.cc, *.macgithubintosh.cc", "dmarc": "s,r;macgithubintosh.cc", "headerList": [
{"return-path": "@bounce.macgithubintosh.cc>", "to": "myalias@bar.com"}], "proceedTests": true}
]},
{"selector": ["Microsoft", "office365support"], "similarityMode": "jw:0.64", "shouldAlertSimilarity": true, "policyList": [
{"addresses": "*@microsoft.com, *.microsoft.com, *@office365support.com", "dmarc": "s,s", "authResults": {"methods": "dkim,spf,dmarc", "borderlineMode": "strict:softfail"}}
]},
{"selector": "Jeanne Doe", "policyList": [
{"addresses": "jeanne.doe@foo.com", "blueprint": "foo.com"}
]},
{"selector": "Joe Doe", "policyList": [
{"addresses": "joe.doe@foo.com", "blueprint": "foo.com"},
{"addresses": "joe@doe.net", "blueprint": "default-full", "onViolation": "flag"}
]}
], "policyBlueprints": {
"default-full": {"dkim": "self", "spf": "strict", "dmarc": "s,s", "authResults": {"methods": "dkim,spf,dmarc"}, "onViolation": "trash"},
"foo.com": {"dkim": "self", "authResults": {"methods": "dkim,spf,dmarc"}, "onViolation": "flag"}
}},
"fromWhitelist": {"shouldTest": true, "list": ["GitHub"]},
"senderBlacklist": {"list": ["Jane Doe", "support@evil.org"]},
"subjectBlacklist": {"list": []},
"contentBlacklist": {"list": ["Dear customer"]},
"mailboxList": [{"name": "Another Mailbox",
"fromWhitelist": {"shouldTest": false, "list": []},
"senderBlacklist": {"list": []},
"subjectBlacklist": {"list": ["50% off"]},
"contentBlacklist": {"list": []}
}]
}
]
}The next sections cover a full formal description of the trustList object and a straightforward explanation of the example trust lists from above.
trustList objectselectorMode [optional] one of the following options:strict (default) a policy of first matched selector object must be fulfilledrelaxed also search remaining selector objects for a match to redeem violation of the first selector's policy. In this mode, a policy violation triggers the harshest user-defined onViolation action of all matched selectors and involved policies.similarityMode [optional] one of the following options to compare selector and "From" header:canonical (default) searches the complete "From" header of the message for the selector string in a canonical way, i.e., replace most non-alphanumeric characters with spaces and convert to lower-case (Git_Hub => git hub).dl:x (Damerau-Levenshtein distance with integtrustListheaderBlacklist