haraka-plugin-karma 2.1.6 → 2.1.8

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/CHANGELOG.md CHANGED
@@ -4,9 +4,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
4
4
 
5
5
  ### Unreleased
6
6
 
7
+ ### [2.1.8] - 2025-10-27
8
+
9
+ - fix: use optional chaining in should_we_skip, fixes #63
10
+ - reduce ASN details saved to results store
11
+ - config: update plugin names
12
+
13
+ ### [2.1.7] - 2025-01-31
14
+
15
+ - replace utils.in_array with [].includes
16
+ - dep(eslint): upgrade to v9
17
+ - doc(CONTRIBUTORS): updated
18
+
7
19
  ### [2.1.6] - 2024-11-08
8
20
 
9
- - fix missing error handler on redis client [#45](https://github.com/haraka/haraka-plugin-redis/issues/45)
21
+ - fix missing error handler on redis client #61
10
22
 
11
23
  ### [2.1.5] - 2024-04-23
12
24
 
@@ -134,3 +146,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
134
146
  [2.1.3]: https://github.com/haraka/haraka-plugin-karma/releases/tag/v2.1.3
135
147
  [2.1.4]: https://github.com/haraka/haraka-plugin-karma/releases/tag/v2.1.4
136
148
  [2.1.5]: https://github.com/haraka/haraka-plugin-karma/releases/tag/v2.1.5
149
+ [2.1.6]: https://github.com/haraka/haraka-plugin-karma/releases/tag/v2.1.6
150
+ [2.1.7]: https://github.com/haraka/haraka-plugin-karma/releases/tag/v2.1.7
151
+ [2.1.8]: https://github.com/haraka/haraka-plugin-karma/releases/tag/v2.1.8
package/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  [![Build Status][ci-img]][ci-url]
2
2
  [![Code Climate][clim-img]][clim-url]
3
- [![NPM][npm-img]][npm-url]
4
3
 
5
4
  # Karma - A heuristics based reputation engine for the Haraka MTA
6
5
 
@@ -200,5 +199,3 @@ Expect to use karma _with_ content filters.
200
199
  [cov-url]: https://codecov.io/github/haraka/haraka-plugin-karma
201
200
  [clim-img]: https://codeclimate.com/github/haraka/haraka-plugin-karma/badges/gpa.svg
202
201
  [clim-url]: https://codeclimate.com/github/haraka/haraka-plugin-karma
203
- [npm-img]: https://nodei.co/npm/haraka-plugin-karma.png
204
- [npm-url]: https://www.npmjs.com/package/haraka-plugin-karma
package/config/karma.ini CHANGED
@@ -57,7 +57,7 @@ hooks=unrecognized_command,data,data_post,queue,queue_outbound
57
57
  ; karma captures and scores deny requests from other plugins, permitting finer
58
58
  ; control over connection handling. For plugins that should be able to reject
59
59
  ; the connection, add their name to the plugin list:
60
- plugins=send_email, tls, access, helo.checks, data.headers, rspamd, spamassassin, clamd, attachment, limit
60
+ plugins=send_email, tls, access, helo.checks, headers, rspamd, spamassassin, clamd, attachment, limit
61
61
 
62
62
  ; hooks whose DENY rejections should be not be captured.
63
63
  hooks=rcpt, queue, queue_outbound
@@ -227,16 +227,16 @@ early_talker = -3
227
227
  104 = access | pass | equals | rcpt_to.access.whitelist | 8
228
228
 
229
229
  ; Scores for specific DNSBLs
230
- 111 = dnsbl | fail | equals | b.barracudacentral.org | -7 | DNS Blacklist | Disinfect your host/network
231
- 112 = dnsbl | fail | equals | truncate.gbudb.net | -5 | DNS Blacklist | Disinfect your host/network
232
- 113 = dnsbl | fail | equals | psbl.surriel.com | -6 | DNS Blacklist | Disinfect your host/network
233
- 114 = dnsbl | fail | equals | bl.spamcop.net | -3 | DNS Blacklist | Disinfect your host/network
234
- 115 = dnsbl | fail | equals | dnsbl-1.uceprotect.net | -3 | DNS Blacklist | Disinfect your host/network
235
- 116 = dnsbl | fail | equals | zen.spamhaus.org | -5 | DNS Blacklist | Disinfect your host/network
236
- 117 = dnsbl | fail | equals | xbl.spamhaus.org | -6 | DNS Blacklist | Disinfect your host/network
237
- 118 = dnsbl | fail | equals | cbl.abuseat.org | -5 | DNS Blacklist | Disinfect your host/network
238
- 119 = dnsbl | fail | equals | dnsbl.justspam.org | -1 | DNS Blacklist | Disinfect your host/network
239
- 120 = dnsbl | fail | equals | dnsbl.sorbs.net | -2 | DNS Blacklist | Clean up DNSBL listing
230
+ 111 = dns-list | fail | equals | b.barracudacentral.org | -7 | DNS Blacklist | Disinfect your host/network
231
+ 112 = dns-list | fail | equals | truncate.gbudb.net | -5 | DNS Blacklist | Disinfect your host/network
232
+ 113 = dns-list | fail | equals | psbl.surriel.com | -6 | DNS Blacklist | Disinfect your host/network
233
+ 114 = dns-list | fail | equals | bl.spamcop.net | -3 | DNS Blacklist | Disinfect your host/network
234
+ 115 = dns-list | fail | equals | dnsbl-1.uceprotect.net | -3 | DNS Blacklist | Disinfect your host/network
235
+ 116 = dns-list | fail | equals | zen.spamhaus.org | -5 | DNS Blacklist | Disinfect your host/network
236
+ 117 = dns-list | fail | equals | xbl.spamhaus.org | -6 | DNS Blacklist | Disinfect your host/network
237
+ 118 = dns-list | fail | equals | cbl.abuseat.org | -5 | DNS Blacklist | Disinfect your host/network
238
+ 119 = dns-list | fail | equals | dnsbl.justspam.org | -1 | DNS Blacklist | Disinfect your host/network
239
+ 120 = dns-list | fail | equals | dnsbl.sorbs.net | -2 | DNS Blacklist | Clean up DNSBL listing
240
240
 
241
241
  130 = helo.checks | fail | match | valid_hostname | -1 | HELO host invalid | Use valid HELO hostname
242
242
  131 = helo.checks | pass | match | forward_dns | 1 | HELO host has forward DNS
package/index.js CHANGED
@@ -364,8 +364,8 @@ exports.tarpit_delay = function (score, connection, hook, k) {
364
364
 
365
365
  // detect roaming users based on MSA ports that require auth
366
366
  if (
367
- utils.in_array(connection.local.port, [587, 465]) &&
368
- utils.in_array(hook, ['ehlo', 'connect'])
367
+ [587, 465].includes(connection.local.port) &&
368
+ ['ehlo', 'connect'].includes(hook)
369
369
  ) {
370
370
  return this.tarpit_delay_msa(connection, delay, k)
371
371
  }
@@ -394,7 +394,7 @@ exports.tarpit_delay_msa = function (connection, delay, k) {
394
394
  // Reduce delay for good ASN history
395
395
  let asn = connection.results.get('asn')
396
396
  if (!asn) asn = connection.results.get('geoip')
397
- if (asn && asn.asn && k.neighbors > 0) {
397
+ if (asn && asn.asn && asn.asn_score > 0) {
398
398
  connection.logdebug(this, `${trg} neighbors: ${delay}`)
399
399
  delay = delay - 2
400
400
  }
@@ -409,8 +409,8 @@ exports.tarpit_delay_msa = function (connection, delay, k) {
409
409
  }
410
410
 
411
411
  exports.should_we_skip = function (connection) {
412
- if (connection.remote.is_private) return true
413
- if (connection.notes.disable_karma) return true
412
+ if (connection.remote?.is_private) return true
413
+ if (connection.notes?.disable_karma) return true
414
414
  return false
415
415
  }
416
416
 
@@ -673,6 +673,19 @@ exports.hook_data_post = function (next, connection) {
673
673
 
674
674
  if (this.should_we_skip(connection)) return next()
675
675
 
676
+ /*
677
+ This should not be a default due to highly probability of false positives,
678
+ but I've found it extremely effective against a recent (most of 2024) spam
679
+ campaign that Gmail apparently has no interest in stopping.
680
+ if (connection.transaction.header.get_decoded('subject').match(/\p{Emoji}/gu)) {
681
+ connection.results.add(this, { msg: 'subject_contains_emoji' })
682
+ if (connection.transaction.mail_from.host === 'gmail.com') {
683
+ connection.results.incr(this, { score: -10 })
684
+ connection.results.add(this, { msg: 'emoji_from_gmail' })
685
+ }
686
+ }
687
+ */
688
+
676
689
  this.check_awards(connection) // update awards
677
690
 
678
691
  const results = connection.results.collate(this)
@@ -967,7 +980,6 @@ exports.check_asn = function (connection, asnkey) {
967
980
  if (!this.db) return
968
981
 
969
982
  const report_as = { name: this.name }
970
-
971
983
  if (this.cfg.asn.report_as) report_as.name = this.cfg.asn.report_as
972
984
 
973
985
  this.db
@@ -981,30 +993,24 @@ exports.check_asn = function (connection, asnkey) {
981
993
 
982
994
  this.db.hIncrBy(asnkey, 'connections', 1)
983
995
  const asn_score = parseInt(res.good || 0) - (res.bad || 0)
984
- const asn_results = {
985
- asn_score,
986
- asn_connections: res.connections,
987
- asn_good: res.good,
988
- asn_bad: res.bad,
989
- emit: true,
990
- }
991
996
 
992
997
  if (asn_score) {
998
+ connection.results.add(report_as, { asn_score: asn_score })
993
999
  if (asn_score < -5) {
994
- asn_results.fail = 'asn:history'
1000
+ connection.results.add(report_as, { fail: 'asn:history' })
995
1001
  } else if (asn_score > 5) {
996
- asn_results.pass = 'asn:history'
1002
+ connection.results.add(report_as, { pass: 'asn:history' })
997
1003
  }
998
1004
  }
999
1005
 
1000
1006
  if (parseInt(res.bad) > 5 && parseInt(res.good) === 0) {
1001
- asn_results.fail = 'asn:all_bad'
1007
+ connection.results.add(report_as, { fail: 'asn:all_bad' })
1002
1008
  }
1003
1009
  if (parseInt(res.good) > 5 && parseInt(res.bad) === 0) {
1004
- asn_results.pass = 'asn:all_good'
1010
+ connection.results.add(report_as, { pass: 'asn:all_good' })
1005
1011
  }
1006
1012
 
1007
- connection.results.add(report_as, asn_results)
1013
+ connection.results.add(report_as, { emit: true })
1008
1014
  })
1009
1015
  .catch((err) => {
1010
1016
  connection.results.add(this, { err })
package/package.json CHANGED
@@ -1,20 +1,19 @@
1
1
  {
2
2
  "name": "haraka-plugin-karma",
3
- "version": "2.1.6",
3
+ "version": "2.1.8",
4
4
  "description": "A heuristics scoring and reputation engine for SMTP connections",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "CHANGELOG.md",
8
- "config",
9
- "test"
8
+ "config"
10
9
  ],
11
10
  "scripts": {
12
11
  "format": "npm run prettier:fix && npm run lint:fix",
13
- "lint": "npx eslint@^8 *.js test",
14
- "lint:fix": "npx eslint@^8 *.js test --fix",
12
+ "lint": "npx eslint@^9 *.js test",
13
+ "lint:fix": "npx eslint@^9 *.js test --fix",
15
14
  "prettier": "npx prettier . --check",
16
15
  "prettier:fix": "npx prettier . --write --log-level=warn",
17
- "test": "npx mocha@^10",
16
+ "test": "npx mocha@^11",
18
17
  "versions": "npx dependency-version-checker check",
19
18
  "versions:fix": "npx dependency-version-checker update"
20
19
  },
@@ -39,7 +38,11 @@
39
38
  "redis": "^4.6.13"
40
39
  },
41
40
  "devDependencies": {
42
- "@haraka/eslint-config": "^1.1.2",
41
+ "@haraka/eslint-config": "^2.0.2",
43
42
  "haraka-test-fixtures": "^1.3.4"
43
+ },
44
+ "prettier": {
45
+ "singleQuote": true,
46
+ "semi": false
44
47
  }
45
48
  }
package/test/karma.js DELETED
@@ -1,913 +0,0 @@
1
- 'use strict'
2
-
3
- const assert = require('assert')
4
-
5
- const Address = require('address-rfc2821').Address
6
- const fixtures = require('haraka-test-fixtures')
7
- const constants = require('haraka-constants')
8
-
9
- const stub = fixtures.stub.stub
10
-
11
- function _set_up(done) {
12
- this.plugin = new fixtures.plugin('karma')
13
-
14
- this.plugin.cfg = { main: {} }
15
- this.plugin.deny_hooks = { connect: true }
16
- this.plugin.tarpit_hooks = ['connect']
17
-
18
- this.connection = fixtures.connection.createConnection({}, { notes: {} })
19
- this.connection.init_transaction()
20
-
21
- done()
22
- }
23
-
24
- describe('karma_init', function () {
25
- beforeEach(function (done) {
26
- this.plugin = new fixtures.plugin('karma')
27
- done()
28
- })
29
-
30
- it('load_karma_ini', function (done) {
31
- this.plugin.inherits('haraka-plugin-redis')
32
- this.plugin.load_karma_ini()
33
- assert.ok(this.plugin.cfg.asn)
34
- assert.ok(this.plugin.deny_hooks)
35
- done()
36
- })
37
- })
38
-
39
- describe('results_init', function () {
40
- beforeEach(_set_up)
41
-
42
- it('init, pre', function (done) {
43
- const r = this.connection.results.get('karma')
44
- assert.equal(undefined, r)
45
- done()
46
- })
47
-
48
- it('init, empty cfg', function (done) {
49
- this.plugin.results_init(stub, this.connection)
50
- const r = this.connection.results.get('karma')
51
- assert.ok(r)
52
- done()
53
- })
54
-
55
- it('init, cfg', function (done) {
56
- this.plugin.cfg.awards = { test: 1 }
57
- this.plugin.results_init(stub, this.connection)
58
- const r = this.connection.results.get('karma')
59
- assert.ok(r)
60
- assert.ok(r.todo)
61
- done()
62
- })
63
-
64
- it('init, skip', function (done) {
65
- this.connection.remote.is_private = true
66
- this.plugin.results_init(stub, this.connection)
67
- const r = this.connection.results.get('karma')
68
- assert.equal(undefined, r)
69
- done()
70
- })
71
-
72
- it('init, private skip', function (done) {
73
- this.connection.notes.disable_karma = true
74
- this.plugin.results_init(stub, this.connection)
75
- const r = this.connection.results.get('karma')
76
- assert.equal(undefined, r)
77
- done()
78
- })
79
- })
80
-
81
- describe('assemble_note_obj', function () {
82
- beforeEach(_set_up)
83
-
84
- it('no auth fails', function (done) {
85
- const obj = this.plugin.assemble_note_obj(
86
- this.connection,
87
- 'notes.auth_fails',
88
- )
89
- assert.equal(undefined, obj)
90
- done()
91
- })
92
-
93
- it('has auth fails', function (done) {
94
- this.connection.notes.auth_fails = [1, 2]
95
- const obj = this.plugin.assemble_note_obj(
96
- this.connection,
97
- 'notes.auth_fails',
98
- )
99
- assert.deepEqual([1, 2], obj)
100
- done()
101
- })
102
- })
103
-
104
- describe('hook_deny', function () {
105
- beforeEach(_set_up)
106
-
107
- it('no params', function (done) {
108
- const next = function (rc) {
109
- assert.equal(constants.OK, rc, rc)
110
- done()
111
- }
112
- this.plugin.hook_deny(next, this.connection, ['', '', '', ''])
113
- })
114
-
115
- it('pi_name=karma', function (done) {
116
- const next = function (rc) {
117
- assert.equal(undefined, rc)
118
- done()
119
- }
120
- this.plugin.hook_deny(next, this.connection, ['', '', 'karma', ''])
121
- })
122
-
123
- it('pi_name=access', function (done) {
124
- const next = function (rc) {
125
- assert.equal(undefined, rc)
126
- done()
127
- }
128
- this.plugin.deny_exclude_plugins = { access: true }
129
- this.plugin.hook_deny(next, this.connection, ['', '', 'access', ''])
130
- })
131
-
132
- it('pi_hook=rcpt_to', function (done) {
133
- const next = function (rc) {
134
- assert.equal(undefined, rc)
135
- done()
136
- }
137
- this.plugin.deny_exclude_hooks = { rcpt_to: true }
138
- this.plugin.hook_deny(next, this.connection, [
139
- '',
140
- '',
141
- '',
142
- '',
143
- '',
144
- 'rcpt_to',
145
- ])
146
- })
147
-
148
- it('pi_hook=queue', function (done) {
149
- const next = function (rc) {
150
- assert.equal(undefined, rc)
151
- done()
152
- }
153
- this.plugin.deny_exclude_hooks = { queue: true }
154
- this.plugin.hook_deny(next, this.connection, ['', '', '', '', '', 'queue'])
155
- })
156
-
157
- it('denysoft', function (done) {
158
- const next = function (rc) {
159
- assert.equal(constants.OK, rc)
160
- done()
161
- }
162
- this.plugin.hook_deny(next, this.connection, [
163
- constants.DENYSOFT,
164
- '',
165
- '',
166
- '',
167
- '',
168
- '',
169
- ])
170
- })
171
- })
172
-
173
- describe('get_award_location', function () {
174
- beforeEach(_set_up)
175
-
176
- it('relaying=false', function (done) {
177
- this.connection.relaying = false
178
- const r = this.plugin.get_award_location(this.connection, 'relaying')
179
- assert.equal(false, r)
180
- done()
181
- })
182
-
183
- it('relaying=true', function (done) {
184
- this.connection.relaying = true
185
- const r = this.plugin.get_award_location(this.connection, 'relaying')
186
- assert.equal(true, r)
187
- done()
188
- })
189
-
190
- it('notes.undef=2', function (done) {
191
- const r = this.plugin.get_award_location(this.connection, 'notes.undef')
192
- assert.equal(undefined, r)
193
- done()
194
- })
195
-
196
- it('notes.tarpit=2', function (done) {
197
- this.connection.notes = { tarpit: 2 }
198
- const r = this.plugin.get_award_location(this.connection, 'notes.tarpit')
199
- assert.equal(2, r)
200
- done()
201
- })
202
-
203
- it('results.geoip', function (done) {
204
- this.connection.results.add('geoip', { country: 'US' })
205
- const r = this.plugin.get_award_location(this.connection, 'results.geoip')
206
- // console.log(r);
207
- assert.equal('US', r.country)
208
- done()
209
- })
210
-
211
- it('results.karma', function (done) {
212
- this.connection.results.add('karma', { score: -1 })
213
- const r = this.plugin.get_award_location(this.connection, 'results.karma')
214
- // console.log(r);
215
- assert.equal(-1, r.score)
216
- done()
217
- })
218
-
219
- it('results.karma, txn', function (done) {
220
- // results should be found in conn or txn
221
- this.connection.transaction.results.add('karma', { score: -1 })
222
- const r = this.plugin.get_award_location(this.connection, 'results.karma')
223
- // console.log(r);
224
- assert.equal(-1, r.score)
225
- done()
226
- })
227
-
228
- it('txn.results.karma', function (done) {
229
- // these results shouldn't be found, b/c txn specified
230
- this.connection.results.add('karma', { score: -1 })
231
- const r = this.plugin.get_award_location(
232
- this.connection,
233
- 'transaction.results.karma',
234
- )
235
- // console.log(r);
236
- assert.equal(undefined, r)
237
- done()
238
- })
239
-
240
- it('results.auth/auth_base', function (done) {
241
- this.connection.results.add('auth/auth_base', { fail: 'PLAIN' })
242
- const r = this.plugin.get_award_location(
243
- this.connection,
244
- 'results.auth/auth_base',
245
- )
246
- assert.equal('PLAIN', r.fail[0])
247
- done()
248
- })
249
- })
250
-
251
- describe('get_award_condition', function () {
252
- beforeEach(_set_up)
253
- it('geoip.distance', function (done) {
254
- assert.equal(
255
- 4000,
256
- this.plugin.get_award_condition(
257
- 'results.geoip.distance@4000',
258
- '-1 if gt',
259
- ),
260
- )
261
- assert.equal(
262
- 4000,
263
- this.plugin.get_award_condition(
264
- 'results.geoip.distance@uniq',
265
- '-1 if gt 4000',
266
- ),
267
- )
268
- done()
269
- })
270
-
271
- it('auth/auth_base', function (done) {
272
- assert.equal(
273
- 'plain',
274
- this.plugin.get_award_condition(
275
- 'results.auth/auth_base.fail@plain',
276
- '-1 if in',
277
- ),
278
- )
279
- done()
280
- })
281
- })
282
-
283
- describe('check_awards', function () {
284
- beforeEach(_set_up)
285
-
286
- it('no results', function (done) {
287
- const r = this.plugin.check_awards(this.connection)
288
- assert.equal(undefined, r)
289
- done()
290
- })
291
-
292
- it('no todo', function (done) {
293
- this.connection.results.add('karma', { todo: {} })
294
- const r = this.plugin.check_awards(this.connection)
295
- assert.equal(undefined, r)
296
- done()
297
- })
298
-
299
- it('geoip gt', function (done) {
300
- // populate the karma result with a todo item
301
- this.connection.results.add('karma', {
302
- todo: { 'results.geoip.distance@4000': '-1 if gt 4000' },
303
- })
304
- // test a non-matching criteria
305
- this.connection.results.add('geoip', { distance: 4000 })
306
- // check awards
307
- this.plugin.check_awards(this.connection)
308
- assert.equal(undefined, this.connection.results.get('karma').fail[0])
309
-
310
- // test a matching criteria
311
- this.connection.results.add('geoip', { distance: 4001 })
312
- // check awards
313
- this.plugin.check_awards(this.connection)
314
- // test that the award was applied
315
- assert.equal('geoip.distance', this.connection.results.get('karma').fail[0])
316
-
317
- done()
318
- })
319
-
320
- it('auth failure', function (done) {
321
- this.connection.results.add('karma', {
322
- todo: { 'results.auth/auth_base.fail@PLAIN': '-1 if in' },
323
- })
324
- this.connection.results.add('auth/auth_base', { fail: 'PLAIN' })
325
- const r = this.plugin.check_awards(this.connection)
326
- assert.equal(undefined, r)
327
- assert.equal(
328
- 'auth/auth_base.fail',
329
- this.connection.results.get('karma').fail[0],
330
- )
331
- done()
332
- })
333
-
334
- it('valid recipient', function (done) {
335
- this.connection.results.add('karma', {
336
- todo: { 'results.rcpt_to.qmd.pass@exist': '1 if in' },
337
- })
338
- this.connection.results.add('rcpt_to.qmd', { pass: 'exist' })
339
- const r = this.plugin.check_awards(this.connection)
340
- assert.equal(undefined, r)
341
- assert.equal('qmd.pass', this.connection.results.get('karma').pass[0])
342
- done()
343
- })
344
- })
345
-
346
- describe('apply_tarpit', function () {
347
- beforeEach(_set_up)
348
-
349
- it('tarpit=false', function (done) {
350
- const next = function (rc, msg) {
351
- assert.equal(undefined, rc)
352
- assert.equal(undefined, msg)
353
- done()
354
- }
355
- this.plugin.apply_tarpit(this.connection, 'connect', 0, next)
356
- })
357
-
358
- it('tarpit=true, score=0', function (done) {
359
- const next = function (rc, msg) {
360
- assert.equal(undefined, rc)
361
- assert.equal(undefined, msg)
362
- done()
363
- }
364
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
365
- this.plugin.apply_tarpit(this.connection, 'connect', 0, next)
366
- })
367
-
368
- it('tarpit=true, score=1', function (done) {
369
- const next = function (rc, msg) {
370
- assert.equal(undefined, rc)
371
- assert.equal(undefined, msg)
372
- done()
373
- }
374
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
375
- this.plugin.apply_tarpit(this.connection, 'connect', 1, next)
376
- })
377
-
378
- it('tarpit=true, score=-1', function (done) {
379
- const before = Date.now()
380
- const next = function (rc, msg) {
381
- assert.ok(Date.now() >= before + 1)
382
- assert.equal(undefined, rc)
383
- assert.equal(undefined, msg)
384
- done()
385
- }
386
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
387
- this.plugin.apply_tarpit(this.connection, 'connect', -1, next)
388
- })
389
-
390
- it('tarpit=true, score=-2, max=1', function (done) {
391
- const before = Date.now()
392
- const next = function (rc, msg) {
393
- assert.ok(Date.now() >= before + 1)
394
- assert.equal(undefined, rc)
395
- assert.equal(undefined, msg)
396
- done()
397
- }
398
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
399
- this.plugin.apply_tarpit(this.connection, 'connect', -2, next)
400
- })
401
-
402
- it('tarpit=true, score=connect, max=1', function (done) {
403
- const before = Date.now()
404
- const next = function (rc, msg) {
405
- assert.ok(Date.now() >= before + 1)
406
- assert.equal(undefined, rc)
407
- assert.equal(undefined, msg)
408
- done()
409
- }
410
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
411
- this.connection.results.add(this.plugin, { score: -2 })
412
- this.plugin.apply_tarpit(this.connection, 'connect', -2, next)
413
- })
414
- })
415
-
416
- describe('should_we_deny', function () {
417
- beforeEach(_set_up)
418
-
419
- it('no results', function (done) {
420
- const next = function (rc, msg) {
421
- assert.equal(undefined, rc)
422
- assert.equal(undefined, msg)
423
- done()
424
- }
425
- this.plugin.should_we_deny(next, this.connection, 'connect')
426
- })
427
-
428
- it('no score', function (done) {
429
- const next = function (rc, msg) {
430
- assert.equal(undefined, rc)
431
- assert.equal(undefined, msg)
432
- done()
433
- }
434
- this.connection.results.add(this.plugin, { test: 'blah' })
435
- this.plugin.should_we_deny(next, this.connection, 'connect')
436
- })
437
-
438
- it('invalid score', function (done) {
439
- const next = function (rc, msg) {
440
- assert.equal(undefined, rc)
441
- assert.equal(undefined, msg)
442
- done()
443
- }
444
- this.connection.results.add(this.plugin, { score: 'blah' })
445
- this.plugin.should_we_deny(next, this.connection, 'connect')
446
- })
447
-
448
- it('valid score, okay', function (done) {
449
- const next = function (rc, msg) {
450
- assert.equal(undefined, rc)
451
- assert.equal(undefined, msg)
452
- done()
453
- }.bind(this)
454
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
455
- this.connection.results.add(this.plugin, { score: -1 })
456
- this.plugin.should_we_deny(next, this.connection, 'connect')
457
- })
458
-
459
- it('valid score, -6, deny_hook', function (done) {
460
- const next = function (rc, msg) {
461
- assert.equal(constants.DENY, rc)
462
- assert.ok(msg)
463
- done()
464
- }.bind(this)
465
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
466
- this.plugin.deny_hooks = { connect: true }
467
- this.connection.results.add(this.plugin, { score: -6 })
468
- this.plugin.should_we_deny(next, this.connection, 'connect')
469
- })
470
-
471
- it('valid score, -6, pass_hook', function (done) {
472
- const next = function (rc, msg) {
473
- assert.equal(undefined, rc)
474
- assert.equal(undefined, msg)
475
- done()
476
- }.bind(this)
477
- this.plugin.cfg.tarpit = { max: 1, delay: 0 }
478
- this.plugin.deny_hooks = { helo: true }
479
- this.connection.results.add(this.plugin, { score: -6 })
480
- this.plugin.should_we_deny(next, this.connection, 'connect')
481
- })
482
- })
483
-
484
- describe('check_result_equal', function () {
485
- beforeEach(_set_up)
486
-
487
- it('equal match is scored', function (done) {
488
- const award = {
489
- id: 1,
490
- award: 2,
491
- operator: 'equals',
492
- value: 'clean',
493
- reason: 'testing',
494
- resolution: 'never',
495
- }
496
- this.plugin.check_result_equal(['clean'], award, this.connection)
497
- assert.equal(this.connection.results.store.karma.score, 2)
498
- assert.equal(this.connection.results.store.karma.awards[0], 1)
499
- done()
500
- })
501
-
502
- it('not equal match is not scored', function (done) {
503
- const award = {
504
- id: 1,
505
- award: 2,
506
- operator: 'equals',
507
- value: 'dirty',
508
- reason: 'testing',
509
- resolution: 'never',
510
- }
511
- this.plugin.check_result_equal(['clean'], award, this.connection)
512
- assert.equal(this.connection.results.store.karma, undefined)
513
- done()
514
- })
515
- })
516
-
517
- describe('check_result_gt', function () {
518
- beforeEach(_set_up)
519
-
520
- it('gt match is scored', function (done) {
521
- const award = {
522
- id: 5,
523
- award: 3,
524
- operator: 'gt',
525
- value: 3,
526
- reason: 'testing',
527
- resolution: 'never',
528
- }
529
- this.plugin.check_result_gt([4], award, this.connection)
530
- // console.log(this.connection.results.store);
531
- assert.equal(this.connection.results.store.karma.score, 3)
532
- assert.equal(this.connection.results.store.karma.awards[0], 5)
533
- done()
534
- })
535
- })
536
-
537
- describe('check_result_lt', function () {
538
- beforeEach(_set_up)
539
-
540
- it('lt match is scored', function (done) {
541
- const award = {
542
- id: 2,
543
- award: 3,
544
- operator: 'lt',
545
- value: 5,
546
- reason: 'testing',
547
- resolution: 'never',
548
- }
549
- this.plugin.check_result_lt([4], award, this.connection)
550
- // console.log(this.connection.results.store);
551
- assert.equal(this.connection.results.store.karma.score, 3)
552
- assert.equal(this.connection.results.store.karma.awards[0], 2)
553
- done()
554
- })
555
-
556
- it('lt match not scored', function (done) {
557
- const award = {
558
- id: 3,
559
- award: 3,
560
- operator: 'lt',
561
- value: 3,
562
- reason: 'testing',
563
- resolution: 'never',
564
- }
565
- this.plugin.check_result_lt([4], award, this.connection)
566
- // console.log(this.connection.results.store);
567
- assert.equal(this.connection.results.store.karma, undefined)
568
- done()
569
- })
570
- })
571
-
572
- describe('check_result_match', function () {
573
- beforeEach(_set_up)
574
-
575
- it('match pattern is scored', function (done) {
576
- const award = {
577
- id: 1,
578
- award: 2,
579
- operator: 'match',
580
- value: 'phish',
581
- reason: 'testing',
582
- resolution: 'never',
583
- }
584
- this.plugin.check_result_match(['isphishing'], award, this.connection)
585
- // console.log(this.connection.results.store);
586
- assert.equal(this.connection.results.store.karma.score, 2)
587
- assert.equal(this.connection.results.store.karma.awards[0], 1)
588
- done()
589
- })
590
-
591
- it('mismatch is not scored', function (done) {
592
- const award = {
593
- id: 1,
594
- award: 2,
595
- operator: 'match',
596
- value: 'dirty',
597
- reason: 'testing',
598
- resolution: 'never',
599
- }
600
- this.plugin.check_result_match(['clean'], award, this.connection)
601
- // console.log(this.connection.results.store);
602
- assert.equal(this.connection.results.store.karma, undefined)
603
- done()
604
- })
605
-
606
- it('FCrDNS match is scored', function (done) {
607
- const award = {
608
- id: 89,
609
- award: 2,
610
- operator: 'match',
611
- value: 'google.com',
612
- reason: 'testing',
613
- resolution: 'never',
614
- }
615
- this.plugin.check_result_match(
616
- ['mail-yk0-f182.google.com'],
617
- award,
618
- this.connection,
619
- )
620
- // console.log(this.connection.results.store);
621
- assert.equal(this.connection.results.store.karma.score, 2)
622
- assert.equal(this.connection.results.store.karma.awards[0], 89)
623
- done()
624
- })
625
- })
626
-
627
- describe('check_result_length', function () {
628
- beforeEach(_set_up)
629
- it('eq pattern is scored', function (done) {
630
- const award = {
631
- id: 1,
632
- award: 2,
633
- operator: 'length',
634
- value: 'eq 3',
635
- reason: 'testing',
636
- resolution: 'hah',
637
- }
638
- this.plugin.check_result_length(['3'], award, this.connection)
639
- // console.log(this.connection.results.store);
640
- assert.equal(this.connection.results.store.karma.score, 2)
641
- assert.equal(this.connection.results.store.karma.awards[0], 1)
642
- done()
643
- })
644
-
645
- it('eq pattern is not scored', function (done) {
646
- const award = {
647
- id: 1,
648
- award: 2,
649
- operator: 'length',
650
- value: 'eq 3',
651
- reason: 'testing',
652
- resolution: 'hah',
653
- }
654
- this.plugin.check_result_length(['4'], award, this.connection)
655
- // console.log(this.connection.results.store.karma);
656
- assert.deepEqual(this.connection.results.store.karma, undefined)
657
- done()
658
- })
659
-
660
- it('gt pattern is scored', function (done) {
661
- const award = {
662
- id: 1,
663
- award: 2,
664
- operator: 'length',
665
- value: 'gt 3',
666
- reason: 'testing',
667
- resolution: 'hah',
668
- }
669
- this.plugin.check_result_length(['5'], award, this.connection)
670
- // console.log(this.connection.results.store);
671
- assert.equal(this.connection.results.store.karma.score, 2)
672
- assert.equal(this.connection.results.store.karma.awards[0], 1)
673
- done()
674
- })
675
-
676
- it('gt pattern is not scored', function (done) {
677
- const award = {
678
- id: 1,
679
- award: 2,
680
- operator: 'length',
681
- value: 'gt 3',
682
- reason: 'testing',
683
- resolution: 'hah',
684
- }
685
- this.plugin.check_result_length(['3'], award, this.connection)
686
- // console.log(this.connection.results.store.karma);
687
- assert.deepEqual(this.connection.results.store.karma, undefined)
688
- done()
689
- })
690
-
691
- it('lt pattern is scored', function (done) {
692
- const award = {
693
- id: 1,
694
- award: 2,
695
- operator: 'length',
696
- value: 'lt 3',
697
- reason: 'testing',
698
- resolution: 'hah',
699
- }
700
- this.plugin.check_result_length(['2'], award, this.connection)
701
- // console.log(this.connection.results.store);
702
- assert.equal(this.connection.results.store.karma.score, 2)
703
- assert.equal(this.connection.results.store.karma.awards[0], 1)
704
- done()
705
- })
706
-
707
- it('lt pattern is not scored', function (done) {
708
- const award = {
709
- id: 1,
710
- award: 2,
711
- operator: 'length',
712
- value: 'lt 3',
713
- reason: 'testing',
714
- resolution: 'hah',
715
- }
716
- this.plugin.check_result_length(['3'], award, this.connection)
717
- // console.log(this.connection.results.store.karma);
718
- assert.deepEqual(this.connection.results.store.karma, undefined)
719
- done()
720
- })
721
- })
722
-
723
- describe('check_result_exists', function () {
724
- beforeEach(_set_up)
725
-
726
- it('exists pattern is scored', function (done) {
727
- const award = {
728
- id: 1,
729
- award: 2,
730
- operator: 'exists',
731
- value: 'any',
732
- reason: 'testing',
733
- resolution: 'high five',
734
- }
735
- this.plugin.check_result_exists(['3'], award, this.connection)
736
- // console.log(this.connection.results.store);
737
- assert.equal(this.connection.results.store.karma.score, 2)
738
- assert.equal(this.connection.results.store.karma.awards[0], 1)
739
- done()
740
- })
741
-
742
- it('not exists pattern is not scored', function (done) {
743
- const award = {
744
- id: 1,
745
- award: 3,
746
- operator: 'exists',
747
- value: '',
748
- reason: 'testing',
749
- resolution: 'misses',
750
- }
751
- this.plugin.check_result_exists([], award, this.connection)
752
- // console.log(this.connection.results.store);
753
- assert.equal(this.connection.results.store.karma, undefined)
754
- assert.equal(this.connection.results.store.karma, undefined)
755
- done()
756
- })
757
- })
758
-
759
- describe('check_result', function () {
760
- beforeEach(_set_up)
761
-
762
- it('geoip country is scored', function (done) {
763
- this.plugin.cfg.result_awards = {
764
- 1: 'geoip | country | equals | CN | 2',
765
- }
766
- this.plugin.preparse_result_awards()
767
- this.connection.results.add({ name: 'geoip' }, { country: 'CN' })
768
- this.plugin.check_result(
769
- this.connection,
770
- '{"plugin":"geoip","result":{"country":"CN"}}',
771
- )
772
- // console.log(this.connection.results.store);
773
- assert.equal(this.connection.results.store.karma.score, 2)
774
- assert.equal(this.connection.results.store.karma.awards[0], 1)
775
- done()
776
- })
777
-
778
- it('dnsbl listing is scored', function (done) {
779
- this.plugin.cfg.result_awards = {
780
- 2: 'dnsbl | fail | equals | dnsbl.sorbs.net | -5',
781
- }
782
- this.plugin.preparse_result_awards()
783
- this.connection.results.add({ name: 'dnsbl' }, { fail: 'dnsbl.sorbs.net' })
784
- this.plugin.check_result(
785
- this.connection,
786
- '{"plugin":"dnsbl","result":{"fail":"dnsbl.sorbs.net"}}',
787
- )
788
- // console.log(this.connection.results.store);
789
- assert.equal(this.connection.results.store.karma.score, -5)
790
- assert.equal(this.connection.results.store.karma.awards[0], 2)
791
- done()
792
- })
793
- })
794
-
795
- describe('check_spammy_tld', function () {
796
- beforeEach(_set_up)
797
-
798
- it('spammy TLD is scored: top', function (done) {
799
- this.plugin.cfg.spammy_tlds = { top: -3 }
800
- const mfrom = new Address('spamy@er7diogt.rrnsale.top')
801
- this.plugin.check_spammy_tld(mfrom, this.connection)
802
- // console.log(this.connection.results.store);
803
- assert.equal(this.connection.results.store.karma.score, -3)
804
- assert.equal(this.connection.results.store.karma.fail[0], 'spammy.TLD')
805
- done()
806
- })
807
-
808
- it('spammy TLD is scored: rocks', function (done) {
809
- this.plugin.cfg.spammy_tlds = { rocks: '-2' }
810
- const mfrom = new Address('spamy@foo.rocks')
811
- this.plugin.check_spammy_tld(mfrom, this.connection)
812
- // console.log(this.connection.results.store);
813
- assert.equal(this.connection.results.store.karma.score, -2)
814
- assert.equal(this.connection.results.store.karma.fail[0], 'spammy.TLD')
815
- done()
816
- })
817
- })
818
-
819
- describe('tls', function () {
820
- beforeEach(_set_up)
821
-
822
- it('unconfigured TLS does nothing', function (done) {
823
- this.connection.tls.enabled = true
824
- const mfrom = new Address('spamy@er7diogt.rrnsale.top')
825
- this.connection.current_line = 'MAIL FROM:<foo@test.com>'
826
- this.plugin.hook_mail(
827
- () => {
828
- assert.equal(this.connection.results.store.karma, undefined)
829
- done()
830
- },
831
- this.connection,
832
- [mfrom],
833
- )
834
- })
835
-
836
- it('TLS is scored', function (done) {
837
- this.plugin.cfg.tls = { set: 2, unset: -4 }
838
- this.connection.tls.enabled = true
839
- const mfrom = new Address('spamy@er7diogt.rrnsale.top')
840
- this.connection.current_line = 'MAIL FROM:<foo@test.com>'
841
- this.plugin.hook_mail(
842
- () => {
843
- // console.log(this.connection.results.store);
844
- assert.equal(this.connection.results.store.karma.score, 2)
845
- done()
846
- },
847
- this.connection,
848
- [mfrom],
849
- )
850
- })
851
-
852
- it('no TLS is scored', function (done) {
853
- this.plugin.cfg.tls = { set: 2, unset: -4 }
854
- this.connection.tls.enabled = false
855
- const mfrom = new Address('spamy@er7diogt.rrnsale.top')
856
- this.connection.current_line = 'MAIL FROM:<foo@test.com>'
857
- this.plugin.hook_mail(
858
- () => {
859
- // console.log(this.connection.results.store);
860
- assert.equal(this.connection.results.store.karma.score, -4)
861
- done()
862
- },
863
- this.connection,
864
- [mfrom],
865
- )
866
- })
867
- })
868
-
869
- describe('skipping hooks', function () {
870
- beforeEach(_set_up)
871
-
872
- it('notes.disable_karma', function (done) {
873
- function next(rc) {
874
- assert.equal(undefined, rc)
875
- }
876
- function last(rc) {
877
- assert.equal(undefined, rc)
878
- done()
879
- }
880
- this.connection.notes.disable_karma = true
881
-
882
- this.plugin.hook_deny(next, this.connection)
883
- this.plugin.hook_connect(next, this.connection)
884
- this.plugin.hook_ehlo(next, this.connection)
885
- this.plugin.hook_vrfy(next, this.connection)
886
- this.plugin.hook_noop(next, this.connection)
887
- this.plugin.hook_data(next, this.connection)
888
- this.plugin.hook_queue(next, this.connection)
889
- this.plugin.hook_reset_transaction(next, this.connection)
890
- this.plugin.hook_unrecognized_command(last, this.connection)
891
- })
892
-
893
- it('private skip', function (done) {
894
- function next(rc) {
895
- assert.equal(undefined, rc)
896
- }
897
- function last(rc) {
898
- assert.equal(undefined, rc)
899
- done()
900
- }
901
- this.connection.remote.is_private = true
902
-
903
- this.plugin.hook_deny(next, this.connection)
904
- this.plugin.hook_connect(next, this.connection)
905
- this.plugin.hook_ehlo(next, this.connection)
906
- this.plugin.hook_vrfy(next, this.connection)
907
- this.plugin.hook_noop(next, this.connection)
908
- this.plugin.hook_data(next, this.connection)
909
- this.plugin.hook_queue(next, this.connection)
910
- this.plugin.hook_reset_transaction(next, this.connection)
911
- this.plugin.hook_unrecognized_command(last, this.connection)
912
- })
913
- })