haraka-plugin-karma 2.1.3 → 2.1.4

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/index.js CHANGED
@@ -2,31 +2,44 @@
2
2
  // karma - reward good and penalize bad mail senders
3
3
 
4
4
  const constants = require('haraka-constants')
5
- const redis = require('redis')
6
- const utils = require('haraka-utils')
5
+ const redis = require('redis')
6
+ const utils = require('haraka-utils')
7
7
 
8
8
  const phase_prefixes = utils.to_object([
9
- 'connect','helo','mail_from','rcpt_to','data'
9
+ 'connect',
10
+ 'helo',
11
+ 'mail_from',
12
+ 'rcpt_to',
13
+ 'data',
10
14
  ])
11
15
 
12
16
  exports.register = function () {
13
-
14
17
  this.inherits('haraka-plugin-redis')
15
18
 
16
19
  // set up defaults
17
- this.deny_hooks = utils.to_object(
18
- ['unrecognized_command','helo','data','data_post','queue','queue_outbound']
19
- )
20
+ this.deny_hooks = utils.to_object([
21
+ 'unrecognized_command',
22
+ 'helo',
23
+ 'data',
24
+ 'data_post',
25
+ 'queue',
26
+ 'queue_outbound',
27
+ ])
20
28
  this.deny_exclude_hooks = utils.to_object('rcpt_to queue queue_outbound')
21
29
  this.deny_exclude_plugins = utils.to_object([
22
- 'access', 'helo.checks', 'data.headers', 'spamassassin',
23
- 'mail_from.is_resolvable', 'clamd', 'tls'
30
+ 'access',
31
+ 'helo.checks',
32
+ 'data.headers',
33
+ 'spamassassin',
34
+ 'mail_from.is_resolvable',
35
+ 'clamd',
36
+ 'tls',
24
37
  ])
25
38
 
26
39
  this.load_karma_ini()
27
40
 
28
- this.register_hook('init_master', 'init_redis_plugin')
29
- this.register_hook('init_child', 'init_redis_plugin')
41
+ this.register_hook('init_master', 'init_redis_plugin')
42
+ this.register_hook('init_child', 'init_redis_plugin')
30
43
 
31
44
  this.register_hook('connect_init', 'results_init')
32
45
  this.register_hook('connect_init', 'ip_history_from_redis')
@@ -35,13 +48,15 @@ exports.register = function () {
35
48
  exports.load_karma_ini = function () {
36
49
  const plugin = this
37
50
 
38
- plugin.cfg = plugin.config.get('karma.ini', {
39
- booleans: [
40
- '+asn.enable',
41
- ],
42
- }, function () {
43
- plugin.load_karma_ini()
44
- })
51
+ plugin.cfg = plugin.config.get(
52
+ 'karma.ini',
53
+ {
54
+ booleans: ['+asn.enable'],
55
+ },
56
+ function () {
57
+ plugin.load_karma_ini()
58
+ },
59
+ )
45
60
 
46
61
  plugin.merge_redis_ini()
47
62
 
@@ -73,7 +88,6 @@ exports.load_karma_ini = function () {
73
88
  }
74
89
 
75
90
  exports.results_init = async function (next, connection) {
76
-
77
91
  if (this.should_we_skip(connection)) {
78
92
  connection.logdebug(this, 'skipping')
79
93
  return next()
@@ -81,7 +95,7 @@ exports.results_init = async function (next, connection) {
81
95
 
82
96
  if (connection.results.get('karma')) {
83
97
  connection.logerror(this, 'this should never happen')
84
- return next() // init once per connection
98
+ return next() // init once per connection
85
99
  }
86
100
 
87
101
  if (this.cfg.awards) {
@@ -92,10 +106,9 @@ exports.results_init = async function (next, connection) {
92
106
  const award = this.cfg.awards[key].toString()
93
107
  todo[key] = award
94
108
  }
95
- connection.results.add(this, { score:0, todo })
96
- }
97
- else {
98
- connection.results.add(this, { score:0 })
109
+ connection.results.add(this, { score: 0, todo })
110
+ } else {
111
+ connection.results.add(this, { score: 0 })
99
112
  }
100
113
 
101
114
  if (!connection.server.notes.redis) {
@@ -103,7 +116,7 @@ exports.results_init = async function (next, connection) {
103
116
  return next()
104
117
  }
105
118
 
106
- if (!this.result_awards) return next() // not configured
119
+ if (!this.result_awards) return next() // not configured
107
120
 
108
121
  if (connection.notes.redis) {
109
122
  connection.logdebug(this, `redis already subscribed`)
@@ -128,9 +141,8 @@ exports.preparse_result_awards = function () {
128
141
  // arrange results for rapid traversal by check_result() :
129
142
  // ex: karma.result_awards.clamd.fail = { .... }
130
143
  for (const anum of Object.keys(cra)) {
131
-
132
- const [pi_name, prop, operator, value, award, reason, resolv]
133
- = cra[anum].split(/(?:\s*\|\s*)/)
144
+ const [pi_name, prop, operator, value, award, reason, resolv] =
145
+ cra[anum].split(/(?:\s*\|\s*)/)
134
146
 
135
147
  const ra = this.result_awards
136
148
 
@@ -143,7 +155,6 @@ exports.preparse_result_awards = function () {
143
155
  }
144
156
 
145
157
  exports.check_result = function (connection, message) {
146
-
147
158
  // connection.loginfo(this, message);
148
159
  // {"plugin":"karma","result":{"fail":"spamassassin.hits"}}
149
160
  // {"plugin":"geoip","result":{"country":"CN"}}
@@ -152,13 +163,14 @@ exports.check_result = function (connection, message) {
152
163
  if (m && m.result && m.result.asn) {
153
164
  this.check_result_asn(m.result.asn, connection)
154
165
  }
155
- if (!this.result_awards[m.plugin]) return // no awards for plugin
166
+ if (!this.result_awards[m.plugin]) return // no awards for plugin
156
167
 
157
- for (const r of Object.keys(m.result)) { // each result in mess
158
- if (r === 'emit') continue // r: pass, fail, skip, err, ...
168
+ for (const r of Object.keys(m.result)) {
169
+ // each result in mess
170
+ if (r === 'emit') continue // r: pass, fail, skip, err, ...
159
171
 
160
172
  const pi_prop = this.result_awards[m.plugin][r]
161
- if (!pi_prop) continue // no award for this plugin property
173
+ if (!pi_prop) continue // no award for this plugin property
162
174
 
163
175
  const thisResult = m.result[r]
164
176
  // ignore empty arrays, objects, and strings
@@ -169,7 +181,8 @@ exports.check_result = function (connection, message) {
169
181
  if (typeof thisResult === 'string' && !thisResult) continue // empty
170
182
 
171
183
  // do any award conditions match this result?
172
- for (const thisAward of pi_prop) { // each award...
184
+ for (const thisAward of pi_prop) {
185
+ // each award...
173
186
  // { id: '011', operator: 'equals', value: 'all_bad', award: '-2'}
174
187
  const thisResArr = this.result_as_array(thisResult)
175
188
  switch (thisAward.operator) {
@@ -196,14 +209,13 @@ exports.check_result = function (connection, message) {
196
209
  }
197
210
 
198
211
  exports.result_as_array = function (result) {
199
-
200
212
  if (typeof result === 'string') return [result]
201
213
  if (typeof result === 'number') return [result]
202
214
  if (typeof result === 'boolean') return [result]
203
215
  if (Array.isArray(result)) return result
204
216
  if (typeof result === 'object') {
205
217
  const array = []
206
- Object.keys(result).forEach(tr => {
218
+ Object.keys(result).forEach((tr) => {
207
219
  array.push(result[tr])
208
220
  })
209
221
  return array
@@ -216,41 +228,37 @@ exports.check_result_asn = function (asn, conn) {
216
228
  if (!this.cfg.asn_awards) return
217
229
  if (!this.cfg.asn_awards[asn]) return
218
230
 
219
- conn.results.incr(this, {score: this.cfg.asn_awards[asn]})
220
- conn.results.push(this, {fail: 'asn_awards'})
231
+ conn.results.incr(this, { score: this.cfg.asn_awards[asn] })
232
+ conn.results.push(this, { fail: 'asn_awards' })
221
233
  }
222
234
 
223
235
  exports.check_result_lt = function (thisResult, thisAward, conn) {
224
-
225
236
  for (const element of thisResult) {
226
237
  const tr = parseFloat(element)
227
238
  if (tr >= parseFloat(thisAward.value)) continue
228
239
  if (conn.results.has('karma', 'awards', thisAward.id)) continue
229
240
 
230
- conn.results.incr(this, {score: thisAward.award})
231
- conn.results.push(this, {awards: thisAward.id})
241
+ conn.results.incr(this, { score: thisAward.award })
242
+ conn.results.push(this, { awards: thisAward.id })
232
243
  }
233
244
  }
234
245
 
235
246
  exports.check_result_gt = function (thisResult, thisAward, conn) {
236
-
237
247
  for (const element of thisResult) {
238
248
  const tr = parseFloat(element)
239
249
  if (tr <= parseFloat(thisAward.value)) continue
240
250
  if (conn.results.has('karma', 'awards', thisAward.id)) continue
241
251
 
242
- conn.results.incr(this, {score: thisAward.award})
243
- conn.results.push(this, {awards: thisAward.id})
252
+ conn.results.incr(this, { score: thisAward.award })
253
+ conn.results.push(this, { awards: thisAward.id })
244
254
  }
245
255
  }
246
256
 
247
257
  exports.check_result_equal = function (thisResult, thisAward, conn) {
248
-
249
258
  for (const element of thisResult) {
250
259
  if (thisAward.value === 'true') {
251
260
  if (!element) continue
252
- }
253
- else {
261
+ } else {
254
262
  if (element != thisAward.value) continue
255
263
  }
256
264
  if (!/auth/.test(thisAward.plugin)) {
@@ -258,8 +266,8 @@ exports.check_result_equal = function (thisResult, thisAward, conn) {
258
266
  if (conn.results.has('karma', 'awards', thisAward.id)) continue
259
267
  }
260
268
 
261
- conn.results.incr(this, {score: thisAward.award})
262
- conn.results.push(this, {awards: thisAward.id})
269
+ conn.results.incr(this, { score: thisAward.award })
270
+ conn.results.push(this, { awards: thisAward.id })
263
271
  }
264
272
  }
265
273
 
@@ -270,13 +278,12 @@ exports.check_result_match = function (thisResult, thisAward, conn) {
270
278
  if (!re.test(element)) continue
271
279
  if (conn.results.has('karma', 'awards', thisAward.id)) continue
272
280
 
273
- conn.results.incr(this, {score: thisAward.award})
274
- conn.results.push(this, {awards: thisAward.id})
281
+ conn.results.incr(this, { score: thisAward.award })
282
+ conn.results.push(this, { awards: thisAward.id })
275
283
  }
276
284
  }
277
285
 
278
286
  exports.check_result_length = function (thisResult, thisAward, conn) {
279
-
280
287
  for (const element of thisResult) {
281
288
  const [operator, qty] = thisAward.value.split(/\s+/) // requires node 6+
282
289
 
@@ -297,13 +304,12 @@ exports.check_result_length = function (thisResult, thisAward, conn) {
297
304
  continue
298
305
  }
299
306
 
300
- conn.results.incr(this, {score: thisAward.award})
301
- conn.results.push(this, {awards: thisAward.id })
307
+ conn.results.incr(this, { score: thisAward.award })
308
+ conn.results.push(this, { awards: thisAward.id })
302
309
  }
303
310
  }
304
311
 
305
312
  exports.check_result_exists = function (thisResult, thisAward, conn) {
306
-
307
313
  /* eslint-disable no-unused-vars */
308
314
  for (const r of thisResult) {
309
315
  const [operator, qty] = thisAward.value.split(/\s+/)
@@ -317,18 +323,17 @@ exports.check_result_exists = function (thisResult, thisAward, conn) {
317
323
  continue
318
324
  }
319
325
 
320
- conn.results.incr(this, {score: thisAward.award})
321
- conn.results.push(this, {awards: thisAward.id})
326
+ conn.results.incr(this, { score: thisAward.award })
327
+ conn.results.push(this, { awards: thisAward.id })
322
328
  }
323
329
  }
324
330
 
325
331
  exports.apply_tarpit = function (connection, hook, score, next) {
326
-
327
332
  if (!this.cfg.tarpit) return next() // tarpit disabled in config
328
333
 
329
334
  // If tarpit is enabled on the reset_transaction hook, Haraka doesn't
330
335
  // wait. Then bad things happen, like a Haraka crash.
331
- if (utils.in_array(hook, ['reset_transaction','queue'])) return next()
336
+ if (utils.in_array(hook, ['reset_transaction', 'queue'])) return next()
332
337
 
333
338
  // no delay for senders with good karma
334
339
  const k = connection.results.get('karma')
@@ -347,17 +352,18 @@ exports.apply_tarpit = function (connection, hook, score, next) {
347
352
  }
348
353
 
349
354
  exports.tarpit_delay = function (score, connection, hook, k) {
350
-
351
355
  if (this.cfg.tarpit.delay && parseFloat(this.cfg.tarpit.delay)) {
352
356
  connection.logdebug(this, 'static tarpit')
353
357
  return parseFloat(this.cfg.tarpit.delay)
354
358
  }
355
359
 
356
- const delay = score * -1 // progressive tarpit
360
+ const delay = score * -1 // progressive tarpit
357
361
 
358
362
  // detect roaming users based on MSA ports that require auth
359
- if (utils.in_array(connection.local.port, [587,465]) &&
360
- utils.in_array(hook, ['ehlo','connect'])) {
363
+ if (
364
+ utils.in_array(connection.local.port, [587, 465]) &&
365
+ utils.in_array(hook, ['ehlo', 'connect'])
366
+ ) {
361
367
  return this.tarpit_delay_msa(connection, delay, k)
362
368
  }
363
369
 
@@ -376,7 +382,7 @@ exports.tarpit_delay_msa = function (connection, delay, k) {
376
382
  delay = parseFloat(delay)
377
383
 
378
384
  // Reduce delay for good history
379
- const history = ((k.good || 0) - (k.bad || 0))
385
+ const history = (k.good || 0) - (k.bad || 0)
380
386
  if (history > 0) {
381
387
  delay = delay - 2
382
388
  connection.logdebug(this, `${trg} history: ${delay}`)
@@ -409,12 +415,12 @@ exports.should_we_deny = function (next, connection, hook) {
409
415
  const r = connection.results.get('karma')
410
416
  if (!r) return next()
411
417
 
412
- this.check_awards(connection) // update awards first
418
+ this.check_awards(connection) // update awards first
413
419
 
414
420
  const score = parseFloat(r.score)
415
- if (isNaN(score)) {
421
+ if (isNaN(score)) {
416
422
  connection.logerror(this, 'score is NaN')
417
- connection.results.add(this, {score: 0})
423
+ connection.results.add(this, { score: 0 })
418
424
  return next()
419
425
  }
420
426
 
@@ -450,10 +456,10 @@ exports.hook_deny = function (next, connection, params) {
450
456
 
451
457
  // let pi_deny = params[0]; // (constants.deny, denysoft, ok)
452
458
  // let pi_message = params[1];
453
- const pi_name = params[2]
459
+ const pi_name = params[2]
454
460
  // let pi_function = params[3];
455
461
  // let pi_params = params[4];
456
- const pi_hook = params[5]
462
+ const pi_hook = params[5]
457
463
 
458
464
  // exceptions, whose 'DENY' should not be captured
459
465
  if (pi_name) {
@@ -468,7 +474,7 @@ exports.hook_deny = function (next, connection, params) {
468
474
  connection.results.add(this, { msg: `deny: ${pi_name}` })
469
475
  connection.results.incr(this, { score: -2 })
470
476
 
471
- next(constants.OK) // resume the connection
477
+ next(constants.OK) // resume the connection
472
478
  }
473
479
 
474
480
  exports.hook_connect = function (next, connection) {
@@ -526,12 +532,11 @@ exports.hook_queue_outbound = function (next, connection) {
526
532
  exports.hook_reset_transaction = function (next, connection) {
527
533
  if (this.should_we_skip(connection)) return next()
528
534
 
529
- connection.results.add(this, {emit: true})
535
+ connection.results.add(this, { emit: true })
530
536
  this.should_we_deny(next, connection, 'reset_transaction')
531
537
  }
532
538
 
533
539
  exports.hook_unrecognized_command = function (next, connection, params) {
534
-
535
540
  if (this.should_we_skip(connection)) return next()
536
541
 
537
542
  // in case karma is in config/plugins before tls
@@ -540,8 +545,8 @@ exports.hook_unrecognized_command = function (next, connection, params) {
540
545
  // in case karma is in config/plugins before AUTH plugin(s)
541
546
  if (connection.notes.authenticating) return next()
542
547
 
543
- connection.results.incr(this, {score: -1})
544
- connection.results.add(this, {fail: `cmd:(${params})`})
548
+ connection.results.incr(this, { score: -1 })
549
+ connection.results.add(this, { fail: `cmd:(${params})` })
545
550
 
546
551
  return this.should_we_deny(next, connection, 'unrecognized_command')
547
552
  }
@@ -552,72 +557,74 @@ exports.ip_history_from_redis = function (next, connection) {
552
557
  if (this.should_we_skip(connection)) return next()
553
558
 
554
559
  const expire = (this.cfg.redis.expire_days || 60) * 86400 // to days
555
- const dbkey = `karma|${connection.remote.ip}`
560
+ const dbkey = `karma|${connection.remote.ip}`
556
561
 
557
562
  // redis plugin is emitting errors, no need to here
558
563
  if (!this.db) return next()
559
564
 
560
- this.db.hGetAll(dbkey).then(dbr => {
561
- if (dbr === null) {
562
- plugin.init_ip(dbkey, connection.remote.ip, expire)
563
- return next()
564
- }
565
+ this.db
566
+ .hGetAll(dbkey)
567
+ .then((dbr) => {
568
+ if (dbr === null) {
569
+ plugin.init_ip(dbkey, connection.remote.ip, expire)
570
+ return next()
571
+ }
565
572
 
566
- plugin.db.multi()
567
- .hIncrBy(dbkey, 'connections', 1) // increment total conn
568
- .expire(dbkey, expire) // extend expiration
569
- .exec()
570
- .catch(err => {
571
- connection.results.add(plugin, { err })
572
- })
573
-
574
- const results = {
575
- good: dbr.good,
576
- bad: dbr.bad,
577
- connections: dbr.connections,
578
- history: parseInt((dbr.good || 0) - (dbr.bad || 0)),
579
- emit: true,
580
- }
573
+ plugin.db
574
+ .multi()
575
+ .hIncrBy(dbkey, 'connections', 1) // increment total conn
576
+ .expire(dbkey, expire) // extend expiration
577
+ .exec()
578
+ .catch((err) => {
579
+ connection.results.add(plugin, { err })
580
+ })
581
+
582
+ const results = {
583
+ good: dbr.good,
584
+ bad: dbr.bad,
585
+ connections: dbr.connections,
586
+ history: parseInt((dbr.good || 0) - (dbr.bad || 0)),
587
+ emit: true,
588
+ }
581
589
 
582
- // Careful: don't become self-fulfilling prophecy.
583
- if (parseInt(dbr.good) > 5 && parseInt(dbr.bad) === 0) {
584
- results.pass = 'all_good'
585
- }
586
- if (parseInt(dbr.bad) > 5 && parseInt(dbr.good) === 0) {
587
- results.fail = 'all_bad'
588
- }
590
+ // Careful: don't become self-fulfilling prophecy.
591
+ if (parseInt(dbr.good) > 5 && parseInt(dbr.bad) === 0) {
592
+ results.pass = 'all_good'
593
+ }
594
+ if (parseInt(dbr.bad) > 5 && parseInt(dbr.good) === 0) {
595
+ results.fail = 'all_bad'
596
+ }
589
597
 
590
- connection.results.add(plugin, results)
598
+ connection.results.add(plugin, results)
591
599
 
592
- plugin.check_awards(connection)
593
- next()
594
- })
595
- .catch(err => {
600
+ plugin.check_awards(connection)
601
+ next()
602
+ })
603
+ .catch((err) => {
596
604
  connection.results.add(plugin, { err })
597
605
  next()
598
606
  })
599
607
  }
600
608
 
601
609
  exports.hook_mail = function (next, connection, params) {
602
-
603
610
  if (this.should_we_skip(connection)) return next()
604
611
 
605
612
  this.check_spammy_tld(params[0], connection)
606
613
 
607
614
  // look for invalid (RFC 5321,(2)821) space in envelope from
608
615
  const full_from = connection.current_line
609
- if (full_from.toUpperCase().substring(0,11) !== 'MAIL FROM:<') {
616
+ if (full_from.toUpperCase().substring(0, 11) !== 'MAIL FROM:<') {
610
617
  connection.loginfo(this, `RFC ignorant env addr format: ${full_from}`)
611
- connection.results.add(this, {fail: 'rfc5321.MailFrom'})
618
+ connection.results.add(this, { fail: 'rfc5321.MailFrom' })
612
619
  }
613
620
 
614
621
  // apply TLS awards (if defined)
615
622
  if (this.cfg.tls !== undefined) {
616
623
  if (this.cfg.tls.set && connection.tls.enabled) {
617
- connection.results.incr(this, {score: this.cfg.tls.set})
624
+ connection.results.incr(this, { score: this.cfg.tls.set })
618
625
  }
619
626
  if (this.cfg.tls.unset && !connection.tls.enabled) {
620
- connection.results.incr(this, {score: this.cfg.tls.unset})
627
+ connection.results.incr(this, { score: this.cfg.tls.unset })
621
628
  }
622
629
  }
623
630
 
@@ -625,7 +632,6 @@ exports.hook_mail = function (next, connection, params) {
625
632
  }
626
633
 
627
634
  exports.hook_rcpt = function (next, connection, params) {
628
-
629
635
  if (this.should_we_skip(connection)) return next()
630
636
 
631
637
  const rcpt = params[0]
@@ -636,23 +642,22 @@ exports.hook_rcpt = function (next, connection, params) {
636
642
  // odds of from_user=rcpt_user in ham: < 1%, in spam > 40%
637
643
  // 2015-05 30-day sample: 84% spam correlation
638
644
  if (connection?.transaction?.mail_from?.user === rcpt.user) {
639
- connection.results.add(this, {fail: 'env_user_match'})
645
+ connection.results.add(this, { fail: 'env_user_match' })
640
646
  }
641
647
 
642
648
  this.check_syntax_RcptTo(connection)
643
649
 
644
- connection.results.add(this, {fail: 'rcpt_to'})
650
+ connection.results.add(this, { fail: 'rcpt_to' })
645
651
 
646
652
  return this.should_we_deny(next, connection, 'rcpt')
647
653
  }
648
654
 
649
655
  exports.hook_rcpt_ok = function (next, connection, rcpt) {
650
-
651
656
  if (this.should_we_skip(connection)) return next()
652
657
 
653
658
  const txn = connection.transaction
654
659
  if (txn && txn.mail_from && txn.mail_from.user === rcpt.user) {
655
- connection.results.add(this, {fail: 'env_user_match'})
660
+ connection.results.add(this, { fail: 'env_user_match' })
656
661
  }
657
662
 
658
663
  this.check_syntax_RcptTo(connection)
@@ -665,7 +670,7 @@ exports.hook_data_post = function (next, connection) {
665
670
 
666
671
  if (this.should_we_skip(connection)) return next()
667
672
 
668
- this.check_awards(connection) // update awards
673
+ this.check_awards(connection) // update awards
669
674
 
670
675
  const results = connection.results.collate(this)
671
676
  connection.logdebug(this, `adding header: ${results}`)
@@ -691,13 +696,13 @@ exports.hook_disconnect = function (next, connection) {
691
696
 
692
697
  const k = connection.results.get('karma')
693
698
  if (!k || k.score === undefined) {
694
- connection.results.add(this, {err: 'karma results missing'})
699
+ connection.results.add(this, { err: 'karma results missing' })
695
700
  return next()
696
701
  }
697
702
 
698
703
  if (!this.cfg.thresholds) {
699
704
  this.check_awards(connection)
700
- connection.results.add(this, {msg: 'no action', emit: true })
705
+ connection.results.add(this, { msg: 'no action', emit: true })
701
706
  return next()
702
707
  }
703
708
 
@@ -708,12 +713,11 @@ exports.hook_disconnect = function (next, connection) {
708
713
  this.increment(connection, 'bad', 1)
709
714
  }
710
715
 
711
- connection.results.add(this, {emit: true })
716
+ connection.results.add(this, { emit: true })
712
717
  next()
713
718
  }
714
719
 
715
720
  exports.get_award_loc_from_note = function (connection, award) {
716
-
717
721
  if (connection.transaction) {
718
722
  const obj = this.assemble_note_obj(connection.transaction, award)
719
723
  if (obj) return obj
@@ -728,7 +732,6 @@ exports.get_award_loc_from_note = function (connection, award) {
728
732
  }
729
733
 
730
734
  exports.get_award_loc_from_results = function (connection, loc_bits) {
731
-
732
735
  let pi_name = loc_bits[1]
733
736
  let notekey = loc_bits[2]
734
737
 
@@ -755,16 +758,22 @@ exports.get_award_location = function (connection, award_key) {
755
758
  const loc_bits = bits[0].split('.')
756
759
  if (loc_bits.length === 1) return connection[bits[0]] // ex: relaying
757
760
 
758
- if (loc_bits[0] === 'notes') { // ex: notes.spf_mail_helo
761
+ if (loc_bits[0] === 'notes') {
762
+ // ex: notes.spf_mail_helo
759
763
  return this.get_award_loc_from_note(connection, bits[0])
760
764
  }
761
765
 
762
- if (loc_bits[0] === 'results') { // ex: results.geoip.distance
766
+ if (loc_bits[0] === 'results') {
767
+ // ex: results.geoip.distance
763
768
  return this.get_award_loc_from_results(connection, loc_bits)
764
769
  }
765
770
 
766
771
  // ex: transaction.results.spf
767
- if (connection.transaction && loc_bits[0] === 'transaction' && loc_bits[1] === 'results') {
772
+ if (
773
+ connection.transaction &&
774
+ loc_bits[0] === 'transaction' &&
775
+ loc_bits[1] === 'results'
776
+ ) {
768
777
  loc_bits.shift()
769
778
  return this.get_award_loc_from_results(connection.transaction, loc_bits)
770
779
  }
@@ -775,11 +784,13 @@ exports.get_award_location = function (connection, award_key) {
775
784
  exports.get_award_condition = function (note_key, note_val) {
776
785
  let wants
777
786
  const keybits = note_key.split('@')
778
- if (keybits[1]) { wants = keybits[1] }
787
+ if (keybits[1]) {
788
+ wants = keybits[1]
789
+ }
779
790
 
780
791
  const valbits = note_val.split(/\s+/)
781
792
  if (!valbits[1]) return wants
782
- if (valbits[1] !== 'if') return wants // no if condition
793
+ if (valbits[1] !== 'if') return wants // no if condition
783
794
 
784
795
  if (valbits[2].match(/^(equals|gt|lt|match)$/)) {
785
796
  if (valbits[3]) wants = valbits[3]
@@ -805,14 +816,16 @@ exports.check_awards = function (connection) {
805
816
  // test the desired condition
806
817
  const bits = award_terms.split(/\s+/)
807
818
  const award = parseFloat(bits[0])
808
- if (!bits[1] || bits[1] !== 'if') { // no if conditions
809
- if (!note) continue // failed truth test
810
- if (!wants) { // no wants, truth matches
819
+ if (!bits[1] || bits[1] !== 'if') {
820
+ // no if conditions
821
+ if (!note) continue // failed truth test
822
+ if (!wants) {
823
+ // no wants, truth matches
811
824
  this.apply_award(connection, key, award)
812
825
  delete karma.todo[key]
813
826
  continue
814
827
  }
815
- if (note !== wants) continue // didn't match
828
+ if (note !== wants) continue // didn't match
816
829
  }
817
830
 
818
831
  // connection.loginfo(this, `check_awards, case matching for: ${wants}`
@@ -839,7 +852,9 @@ exports.check_awards = function (connection) {
839
852
  continue
840
853
  case 'length': {
841
854
  const operator = bits[3]
842
- if (bits[4]) { wants = bits[4] }
855
+ if (bits[4]) {
856
+ wants = bits[4]
857
+ }
843
858
  switch (operator) {
844
859
  case 'gt':
845
860
  if (note.length <= parseFloat(wants)) continue
@@ -851,14 +866,19 @@ exports.check_awards = function (connection) {
851
866
  if (note.length !== parseFloat(wants)) continue
852
867
  break
853
868
  default:
854
- connection.logerror(this, `length operator "${operator}" not supported.`)
869
+ connection.logerror(
870
+ this,
871
+ `length operator "${operator}" not supported.`,
872
+ )
855
873
  continue
856
874
  }
857
875
  break
858
876
  }
859
- case 'in': // if in pass whitelisted
877
+ case 'in': // if in pass whitelisted
860
878
  // let list = bits[3];
861
- if (bits[4]) { wants = bits[4] }
879
+ if (bits[4]) {
880
+ wants = bits[4]
881
+ }
862
882
  if (!Array.isArray(note)) continue
863
883
  if (!wants) continue
864
884
  if (note.indexOf(wants) !== -1) break // found!
@@ -873,25 +893,31 @@ exports.check_awards = function (connection) {
873
893
 
874
894
  exports.apply_award = function (connection, nl, award) {
875
895
  if (!award) return
876
- if (isNaN(award)) { // garbage in config
896
+ if (isNaN(award)) {
897
+ // garbage in config
877
898
  connection.logerror(this, `non-numeric award from: ${nl}:${award}`)
878
899
  return
879
900
  }
880
901
 
881
- const bits = nl.split('@'); nl = bits[0] // strip off @... if present
902
+ const bits = nl.split('@')
903
+ nl = bits[0] // strip off @... if present
882
904
 
883
- connection.results.incr(this, {score: award})
905
+ connection.results.incr(this, { score: award })
884
906
  connection.logdebug(this, `applied ${nl}:${award}`)
885
907
 
886
- let trimmed = nl.substring(0, 5) === 'notes' ? nl.substring(6) :
887
- nl.substring(0, 7) === 'results' ? nl.substring(8) :
888
- nl.substring(0,19) === 'transaction.results' ?
889
- nl.substring(20) : nl
908
+ let trimmed =
909
+ nl.substring(0, 5) === 'notes'
910
+ ? nl.substring(6)
911
+ : nl.substring(0, 7) === 'results'
912
+ ? nl.substring(8)
913
+ : nl.substring(0, 19) === 'transaction.results'
914
+ ? nl.substring(20)
915
+ : nl
890
916
 
891
- if (trimmed.substring(0,7) === 'rcpt_to') trimmed = trimmed.substring(8)
892
- if (trimmed.substring(0,7) === 'mail_from') trimmed = trimmed.substring(10)
893
- if (trimmed.substring(0,7) === 'connect') trimmed = trimmed.substring(8)
894
- if (trimmed.substring(0,4) === 'data') trimmed = trimmed.substring(5)
917
+ if (trimmed.substring(0, 7) === 'rcpt_to') trimmed = trimmed.substring(8)
918
+ if (trimmed.substring(0, 7) === 'mail_from') trimmed = trimmed.substring(10)
919
+ if (trimmed.substring(0, 7) === 'connect') trimmed = trimmed.substring(8)
920
+ if (trimmed.substring(0, 4) === 'data') trimmed = trimmed.substring(5)
895
921
 
896
922
  if (award > 0) connection.results.add(this, { pass: trimmed })
897
923
  if (award < 0) connection.results.add(this, { fail: trimmed })
@@ -899,7 +925,7 @@ exports.apply_award = function (connection, nl, award) {
899
925
 
900
926
  exports.check_spammy_tld = function (mail_from, connection) {
901
927
  if (!this.cfg.spammy_tlds) return
902
- if (mail_from.isNull()) return // null sender (bounce)
928
+ if (mail_from.isNull()) return // null sender (bounce)
903
929
 
904
930
  const from_tld = mail_from.host.split('.').pop()
905
931
  // connection.logdebug(this, `from_tld: ${from_tld}`);
@@ -907,17 +933,17 @@ exports.check_spammy_tld = function (mail_from, connection) {
907
933
  const tld_penalty = parseFloat(this.cfg.spammy_tlds[from_tld] || 0)
908
934
  if (tld_penalty === 0) return
909
935
 
910
- connection.results.incr(this, {score: tld_penalty})
911
- connection.results.add(this, {fail: 'spammy.TLD'})
936
+ connection.results.incr(this, { score: tld_penalty })
937
+ connection.results.add(this, { fail: 'spammy.TLD' })
912
938
  }
913
939
 
914
940
  exports.check_syntax_RcptTo = function (connection) {
915
941
  // look for an illegal (RFC 5321,(2)821) space in envelope recipient
916
942
  const full_rcpt = connection.current_line
917
- if (full_rcpt.toUpperCase().substring(0,9) === 'RCPT TO:<') return
943
+ if (full_rcpt.toUpperCase().substring(0, 9) === 'RCPT TO:<') return
918
944
 
919
945
  connection.loginfo(this, `illegal envelope address format: ${full_rcpt}`)
920
- connection.results.add(this, {fail: 'rfc5321.RcptTo'})
946
+ connection.results.add(this, { fail: 'rfc5321.RcptTo' })
921
947
  }
922
948
 
923
949
  exports.assemble_note_obj = function (prefix, key) {
@@ -941,50 +967,52 @@ exports.check_asn = function (connection, asnkey) {
941
967
 
942
968
  if (this.cfg.asn.report_as) report_as.name = this.cfg.asn.report_as
943
969
 
944
- this.db.hGetAll(asnkey).then(res => {
945
- if (res === null) {
946
- const expire = (this.cfg.redis.expire_days || 60) * 86400 // days
947
- this.init_asn(asnkey, expire)
948
- return
949
- }
950
-
951
- this.db.hIncrBy(asnkey, 'connections', 1)
952
- const asn_score = parseInt(res.good || 0) - (res.bad || 0)
953
- const asn_results = {
954
- asn_score,
955
- asn_connections: res.connections,
956
- asn_good: res.good,
957
- asn_bad: res.bad,
958
- emit: true,
959
- }
970
+ this.db
971
+ .hGetAll(asnkey)
972
+ .then((res) => {
973
+ if (res === null) {
974
+ const expire = (this.cfg.redis.expire_days || 60) * 86400 // days
975
+ this.init_asn(asnkey, expire)
976
+ return
977
+ }
960
978
 
961
- if (asn_score) {
962
- if (asn_score < -5) {
963
- asn_results.fail = 'asn:history'
979
+ this.db.hIncrBy(asnkey, 'connections', 1)
980
+ const asn_score = parseInt(res.good || 0) - (res.bad || 0)
981
+ const asn_results = {
982
+ asn_score,
983
+ asn_connections: res.connections,
984
+ asn_good: res.good,
985
+ asn_bad: res.bad,
986
+ emit: true,
964
987
  }
965
- else if (asn_score > 5) {
966
- asn_results.pass = 'asn:history'
988
+
989
+ if (asn_score) {
990
+ if (asn_score < -5) {
991
+ asn_results.fail = 'asn:history'
992
+ } else if (asn_score > 5) {
993
+ asn_results.pass = 'asn:history'
994
+ }
967
995
  }
968
- }
969
996
 
970
- if (parseInt(res.bad) > 5 && parseInt(res.good) === 0) {
971
- asn_results.fail = 'asn:all_bad'
972
- }
973
- if (parseInt(res.good) > 5 && parseInt(res.bad) === 0) {
974
- asn_results.pass = 'asn:all_good'
975
- }
997
+ if (parseInt(res.bad) > 5 && parseInt(res.good) === 0) {
998
+ asn_results.fail = 'asn:all_bad'
999
+ }
1000
+ if (parseInt(res.good) > 5 && parseInt(res.bad) === 0) {
1001
+ asn_results.pass = 'asn:all_good'
1002
+ }
976
1003
 
977
- connection.results.add(report_as, asn_results)
978
- })
979
- .catch(err => {
1004
+ connection.results.add(report_as, asn_results)
1005
+ })
1006
+ .catch((err) => {
980
1007
  connection.results.add(this, { err })
981
1008
  })
982
1009
  }
983
1010
 
984
1011
  exports.init_ip = async function (dbkey, rip, expire) {
985
1012
  if (!this.db) return
986
- await this.db.multi()
987
- .hmSet(dbkey, {'bad': 0, 'good': 0, 'connections': 1})
1013
+ await this.db
1014
+ .multi()
1015
+ .hmSet(dbkey, { bad: 0, good: 0, connections: 1 })
988
1016
  .expire(dbkey, expire)
989
1017
  .exec()
990
1018
  }
@@ -999,8 +1027,9 @@ exports.get_asn_key = function (connection) {
999
1027
 
1000
1028
  exports.init_asn = function (asnkey, expire) {
1001
1029
  if (!this.db) return
1002
- this.db.multi()
1003
- .hmSet(asnkey, {'bad': 0, 'good': 0, 'connections': 1})
1004
- .expire(asnkey, expire * 2) // keep ASN longer
1030
+ this.db
1031
+ .multi()
1032
+ .hmSet(asnkey, { bad: 0, good: 0, connections: 1 })
1033
+ .expire(asnkey, expire * 2) // keep ASN longer
1005
1034
  .exec()
1006
1035
  }