Skip to content

Commit e8041fc

Browse files
committed
hew better to style of the plugin
1 parent be78032 commit e8041fc

File tree

5 files changed

+149
-227
lines changed

5 files changed

+149
-227
lines changed

.github/workflows/publish.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ on:
77
paths:
88
- package.json
99

10+
permissions:
11+
id-token: write
12+
contents: write
13+
packages: write
14+
1015
env:
1116
CI: true
1217

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
Increase the reputation of domains you exchange email with by sending them email.
77

8+
Reduce the reputation of emails purporting to be from well-known entities that bear no common forms of domain authentication.
9+
810
## Synopsis
911

10-
Known Senders is based on the premise that domains users send email to are domains they also want to receive email from. By maintaining lists of domains that local users send email to, a weak but helpful form of trust is obtained.
12+
Known Senders is based on the premise that domains users send email to are domains they also want to receive email from. By maintaining lists of domains that local users send email to, a basis of trust is obtained.
1113

1214
## How it works
1315

@@ -71,11 +73,13 @@ Multiple variations (like "c0stc0" with zeros instead of letters) can map to the
7173
### Examples
7274

7375
**Rejected:**
76+
7477
- From: "Costco Support" \<spam@spammer.com\>
7578
- Subject: "Your Costco Order"
7679
- Result: Rejected because "costco" appears in the name/subject but the domain is not costco.com
7780

7881
**Allowed:**
82+
7983
- From: "Costco Support" \<noreply@costco.com\>
8084
- Subject: "Your Costco Order"
8185
- Result: Allowed because the domain matches costco.com

index.js

Lines changed: 33 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict'
22

33
const tlds = require('haraka-tld')
4-
const constants = require('haraka-constants')
54

65
exports.register = function () {
76
this.inherits('haraka-plugin-redis')
@@ -73,8 +72,7 @@ exports.update_sender = async function (next, connection, params) {
7372

7473
sender_od = this.get_sender_domain_by_txn(txn)
7574
if (!sender_od) return errNext('no sender domain')
76-
if (sender_od in plugin.cfg.ignored_ods)
77-
return errNext(`ignored(${sender_od})`)
75+
if (sender_od in plugin.cfg.ignored_ods) return errNext(`ignored(${sender_od})`)
7876

7977
rcpt_domains = this.get_recipient_domains_by_txn(txn)
8078
if (rcpt_domains.length === 0) {
@@ -91,10 +89,7 @@ exports.update_sender = async function (next, connection, params) {
9189

9290
const replies = await multi.exec()
9391
for (let i = 0; i < rcpt_domains.length; i++) {
94-
connection.loginfo(
95-
this,
96-
`saved ${sender_od} : ${rcpt_domains[i]} : ${replies[i]}`,
97-
)
92+
connection.loginfo(this, `saved ${sender_od} : ${rcpt_domains[i]} : ${replies[i]}`)
9893
}
9994
next(null, null, sender_od, rcpt_domains)
10095
} catch (err) {
@@ -113,16 +108,14 @@ exports.get_sender_domain_by_txn = function (txn) {
113108
}
114109

115110
exports.get_recipient_domains_by_txn = function (txn) {
116-
const plugin = this
117-
118111
const rcpt_domains = []
119112
if (!txn.rcpt_to) return rcpt_domains
120113

121114
for (const element of txn.rcpt_to) {
122115
if (!element.host) continue
123116
const rcpt_od = tlds.get_organizational_domain(element.host)
124117
if (element.host !== rcpt_od) {
125-
plugin.loginfo(`rcpt: ${element.host} -> ${rcpt_od}`)
118+
this.loginfo(`rcpt: ${element.host} -> ${rcpt_od}`)
126119
}
127120
if (rcpt_domains.indexOf(rcpt_od) === -1) {
128121
// not a duplicate, add to the list
@@ -251,8 +244,7 @@ exports.is_dkim_authenticated = async function (next, connection) {
251244

252245
const sender_od = this.get_validated_sender_od(connection)
253246
if (!sender_od) return errNext('no sender_od')
254-
if (sender_od in this.cfg.ignored_ods)
255-
return infoNext(`ignored(${sender_od})`)
247+
if (sender_od in this.cfg.ignored_ods) return infoNext(`ignored(${sender_od})`)
256248

257249
rcpt_ods = this.get_rcpt_ods(connection)
258250
if (!rcpt_ods || !rcpt_ods.length) return errNext('no rcpt_ods')
@@ -349,13 +341,10 @@ exports.has_spf_match = function (sender_od, connection) {
349341
*/
350342

351343
exports.check_abused_names = function (next, connection) {
352-
const plugin = this
344+
if (connection.relaying) return next() // inbound only
353345

354-
// Only check inbound messages
355-
if (connection.relaying) return next()
356-
357-
// Skip if no commonly abused names configured
358-
if (!plugin.cfg.commonly_abused_patterns || Object.keys(plugin.cfg.commonly_abused_patterns).length === 0) {
346+
// Skip when no commonly abused names configured
347+
if (!this.cfg.commonly_abused || Object.keys(this.cfg.commonly_abused).length === 0) {
359348
return next()
360349
}
361350

@@ -364,9 +353,8 @@ exports.check_abused_names = function (next, connection) {
364353

365354
try {
366355
// Get envelope from domain
367-
const envelope_from_domain = txn.mail_from && txn.mail_from.host
368-
? tlds.get_organizational_domain(txn.mail_from.host)
369-
: null
356+
const envelope_from_domain =
357+
txn.mail_from && txn.mail_from.host ? tlds.get_organizational_domain(txn.mail_from.host) : null
370358

371359
// Get header from
372360
const header_from = txn.header.get('from')
@@ -388,50 +376,45 @@ exports.check_abused_names = function (next, connection) {
388376
const subject_text = subject ? subject.toLowerCase() : ''
389377

390378
// Get envelope from text (local part and any display name)
391-
const envelope_from_text = txn.mail_from && txn.mail_from.user
392-
? txn.mail_from.user.toLowerCase()
393-
: ''
394-
395-
// Check each commonly abused name using pre-compiled patterns
396-
for (const [abused_name, pattern_info] of Object.entries(plugin.cfg.commonly_abused_patterns)) {
397-
const { pattern, legitimate_domain } = pattern_info
398-
399-
// Check if the abused name appears in from or subject
400-
const found_in_header_from = pattern.test(header_from_text)
401-
const found_in_subject = pattern.test(subject_text)
402-
const found_in_envelope_from = pattern.test(envelope_from_text)
403-
404-
if (found_in_header_from || found_in_subject || found_in_envelope_from) {
379+
const envelope_from_text = txn.mail_from && txn.mail_from.user ? txn.mail_from.user.toLowerCase() : ''
380+
381+
for (const [abused_name, legitimate_domain] of Object.entries(this.cfg.commonly_abused)) {
382+
const name_lower = abused_name.toLowerCase()
383+
384+
// is the abused name in from or subject?
385+
const in_header_from = header_from_text.includes(name_lower)
386+
const in_subject = subject_text.includes(name_lower)
387+
const in_envelope_from = envelope_from_text.includes(name_lower)
388+
389+
if (in_header_from || in_subject || in_envelope_from) {
405390
// Get the legitimate OD for comparison
406391
const legitimate_od = tlds.get_organizational_domain(legitimate_domain)
407-
408-
// Check if the actual sending domains match the legitimate domain
392+
393+
// Check if the sending domains matches the legitimate domain
409394
const envelope_matches = envelope_from_domain === legitimate_od
410395
const header_matches = header_from_domain === legitimate_od
411396

412397
if (!envelope_matches && !header_matches) {
413398
// The abused name was found but neither domain matches - REJECT
414399
const locations = []
415-
if (found_in_envelope_from) locations.push('envelope from')
416-
if (found_in_header_from) locations.push('header from')
417-
if (found_in_subject) locations.push('subject')
418-
419-
connection.loginfo(plugin,
420-
`rejecting: abused name '${abused_name}' found in ${locations.join(', ')} ` +
421-
`but domain is not ${legitimate_domain} ` +
422-
`(envelope: ${envelope_from_domain || 'none'}, header: ${header_from_domain || 'none'})`
423-
)
424-
425-
return next(constants.DENY,
426-
`This message appears to impersonate ${legitimate_domain} and has been rejected`
400+
if (in_envelope_from) locations.push('envelope from')
401+
if (in_header_from) locations.push('header from')
402+
if (in_subject) locations.push('subject')
403+
404+
connection.loginfo(
405+
this,
406+
`'${abused_name}' found in ${locations.join(', ')}, domain is not ${legitimate_domain} ` +
407+
`(envelope: ${envelope_from_domain || 'none'}, header: ${header_from_domain || 'none'})`,
427408
)
409+
410+
return next(DENY, `This message appears to impersonate ${legitimate_domain}`)
428411
}
429412
}
430413
}
431414

432415
next()
433416
} catch (err) {
434-
connection.logerror(plugin, `check_abused_names error: ${err}`)
417+
connection.logerror(this, `check_abused_names error: ${err}`)
435418
next()
436419
}
437420
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"lint:fix": "npx eslint --fix *.js test/*.js",
1515
"prettier": "npx prettier . --check",
1616
"prettier:fix": "npx prettier . --write --log-level=warn",
17-
"test": "npx mocha@^11 --exit",
17+
"test": "npx mocha --exit",
1818
"versions": "npx dependency-version-checker check",
1919
"versions:fix": "npx dependency-version-checker update"
2020
},
@@ -46,6 +46,7 @@
4646
"haraka-tld": "^1.2.4"
4747
},
4848
"prettier": {
49+
"printWidth": 120,
4950
"singleQuote": true,
5051
"semi": false
5152
}

0 commit comments

Comments
 (0)