haraka-plugin-karma 1.0.13 → 1.0.14

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.
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **System Info:**
11
+
12
+ Please report your OS, Node version, and Haraka version by running this shell script on your Haraka server and replacing this section with the output.
13
+
14
+ echo "Haraka | $(haraka -v)"; echo " --- | :--- "; echo "Node | $(node -v)"; echo "OS | $(uname -a)"; echo "openssl | $(openssl version)"
15
+
16
+ **Describe the bug**
17
+ A clear and concise description of what the bug is.
18
+
19
+ **Expected behavior**
20
+ A clear and concise description of what you expected to happen.
21
+
22
+ **Observed behavior**
23
+
24
+
25
+ **Steps To Reproduce**
26
+
27
+
28
+ **Additional context**
29
+ Add any other context about the problem here.
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: Custom issue template
3
+ about: Issues that aren't bug reports or feature requests
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,38 @@
1
+ name: Tests - Windows
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ # no docker/images support on Windows (currently), so run w/o Redis
6
+ # also, stack run commands so test doesn't begin before install completes
7
+
8
+ jobs:
9
+
10
+ ci-test-win:
11
+
12
+ runs-on: ${{ matrix.os }}
13
+
14
+ strategy:
15
+ matrix:
16
+ os: [ windows-latest ]
17
+ node-version: [ 14.x, 16.x ]
18
+ fail-fast: false
19
+
20
+ steps:
21
+ - uses: actions/checkout@v2
22
+ name: Checkout
23
+ with:
24
+ fetch-depth: 1
25
+
26
+ - uses: actions/setup-node@v2
27
+ name: Use Node.js ${{ matrix.node-version }}
28
+ with:
29
+ node-version: ${{ matrix.node-version }}
30
+
31
+ - name: Install
32
+ run: npm install
33
+
34
+ - name: Test
35
+ run: npm run test
36
+
37
+ env:
38
+ CI: true
@@ -0,0 +1,41 @@
1
+ name: Tests
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+
7
+ ci-test:
8
+
9
+ runs-on: ${{ matrix.os }}
10
+
11
+ strategy:
12
+ matrix:
13
+ os: [ ubuntu-latest ]
14
+ node-version: [ 14.x, 16.x ]
15
+ fail-fast: false
16
+
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ name: Checkout
20
+ with:
21
+ fetch-depth: 1
22
+
23
+ - uses: actions/setup-node@v2
24
+ name: Use Node.js ${{ matrix.node-version }}
25
+ with:
26
+ node-version: ${{ matrix.node-version }}
27
+
28
+ - name: Install
29
+ run: npm install
30
+
31
+ - name: Test
32
+ run: npm run test
33
+
34
+ env:
35
+ CI: true
36
+
37
+ services:
38
+ redis:
39
+ image: redis
40
+ ports:
41
+ - 6379/tcp
@@ -0,0 +1,33 @@
1
+ name: Haraka Lint
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+
7
+ lint:
8
+
9
+ runs-on: ubuntu-latest
10
+
11
+ strategy:
12
+ matrix:
13
+ node-version: [ 14.x ]
14
+
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ name: Checkout
18
+ with:
19
+ fetch-depth: 1
20
+
21
+ - uses: actions/setup-node@v2
22
+ name: Use Node.js ${{ matrix.node-version }}
23
+ with:
24
+ node-version: ${{ matrix.node-version }}
25
+
26
+ - name: Install
27
+ run: npm install
28
+
29
+ - name: Lint
30
+ run: npm run lint
31
+
32
+ env:
33
+ CI: true
package/.travis.yml CHANGED
@@ -1,8 +1,7 @@
1
1
  language: node_js
2
2
  node_js:
3
- - "10"
4
- - "12"
5
3
  - "14"
4
+ - "16"
6
5
 
7
6
  before_install:
8
7
  - npm install
package/Changes.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 1.0.14 - 2022-02-14
2
+
3
+ - try to unsubscribe in case connection is marked to skip during transaction
1
4
 
2
5
  ## 1.0.13 - 2019-04-23
3
6
 
package/index.js CHANGED
@@ -15,9 +15,9 @@ exports.register = function () {
15
15
 
16
16
  // set up defaults
17
17
  plugin.deny_hooks = utils.to_object(
18
- ['unrecognized_command','helo','data','data_post','queue']
18
+ ['unrecognized_command','helo','data','data_post','queue','queue_outbound']
19
19
  );
20
- plugin.deny_exclude_hooks = utils.to_object('rcpt_to, queue');
20
+ plugin.deny_exclude_hooks = utils.to_object('rcpt_to queue queue_outbound');
21
21
  plugin.deny_exclude_plugins = utils.to_object([
22
22
  'access', 'helo.checks', 'data.headers', 'spamassassin',
23
23
  'mail_from.is_resolvable', 'clamd', 'tls'
@@ -210,7 +210,7 @@ exports.result_as_array = function (result) {
210
210
  });
211
211
  return array;
212
212
  }
213
- this.loginfo('what format is result: ' + result);
213
+ this.loginfo(`what format is result: ${result}`);
214
214
  return result;
215
215
  }
216
216
 
@@ -286,10 +286,7 @@ exports.check_result_length = function (thisResult, thisAward, conn) {
286
286
  const plugin = this;
287
287
 
288
288
  for (let j=0; j < thisResult.length; j++) {
289
- // let [operator, qty] = thisAward.value.split(/\s+/); // requires node 6
290
- const matches = thisAward.value.split(/\s+/);
291
- const operator = matches[0];
292
- const qty = matches[1];
289
+ const [operator, qty] = thisAward.value.split(/\s+/); // requires node 6+
293
290
 
294
291
  switch (operator) {
295
292
  case 'eq':
@@ -304,12 +301,12 @@ exports.check_result_length = function (thisResult, thisAward, conn) {
304
301
  if (parseInt(thisResult[j], 10) >= parseInt(qty, 10)) continue;
305
302
  break;
306
303
  default:
307
- conn.results.add(plugin, { err: 'invalid operator:' + operator });
304
+ conn.results.add(plugin, { err: `invalid operator: ${operator}` });
308
305
  continue;
309
306
  }
310
307
 
311
- conn.results.incr(plugin, {score: thisAward.award});
312
- conn.results.push(plugin, {awards: thisAward.id});
308
+ conn.results.incr(plugin, {score: thisAward.award});
309
+ conn.results.push(plugin, {awards: thisAward.id });
313
310
  }
314
311
  }
315
312
 
@@ -351,9 +348,9 @@ exports.apply_tarpit = function (connection, hook, score, next) {
351
348
  const delay = plugin.tarpit_delay(score, connection, hook, k);
352
349
  if (!delay) return next();
353
350
 
354
- connection.logdebug(plugin, 'tarpitting '+hook+' for ' + delay + 's');
351
+ connection.logdebug(plugin, `tarpitting ${hook} for ${delay}s`);
355
352
  setTimeout(() => {
356
- connection.logdebug(plugin, 'tarpit '+hook+' end');
353
+ connection.logdebug(plugin, `tarpit ${hook} end`);
357
354
  next();
358
355
  }, delay * 1000);
359
356
  }
@@ -376,7 +373,7 @@ exports.tarpit_delay = function (score, connection, hook, k) {
376
373
 
377
374
  const max = plugin.cfg.tarpit.max || 5;
378
375
  if (delay > max) {
379
- connection.logdebug(plugin, 'tarpit capped to: ' + max);
376
+ connection.logdebug(plugin, `tarpit capped to: ${max}`);
380
377
  return max;
381
378
  }
382
379
 
@@ -393,20 +390,20 @@ exports.tarpit_delay_msa = function (connection, delay, k) {
393
390
  const history = ((k.good || 0) - (k.bad || 0));
394
391
  if (history > 0) {
395
392
  delay = delay - 2;
396
- connection.logdebug(plugin, trg + ' history: ' + delay);
393
+ connection.logdebug(plugin, `${trg} history: ${delay}`);
397
394
  }
398
395
 
399
396
  // Reduce delay for good ASN history
400
397
  let asn = connection.results.get('asn');
401
398
  if (!asn) { asn = connection.results.get('geoip'); }
402
399
  if (asn && asn.asn && k.neighbors > 0) {
403
- connection.logdebug(plugin, trg + ' neighbors: ' + delay);
400
+ connection.logdebug(plugin, `${trg} neighbors: ${delay}`);
404
401
  delay = delay - 2;
405
402
  }
406
403
 
407
404
  const max = plugin.cfg.tarpit.max_msa || 2;
408
405
  if (delay > max) {
409
- connection.logdebug(plugin, 'tarpit capped at: ' + delay);
406
+ connection.logdebug(plugin, `tarpit capped at: ${delay}`);
410
407
  delay = max;
411
408
  }
412
409
 
@@ -423,7 +420,7 @@ exports.should_we_deny = function (next, connection, hook) {
423
420
  const plugin = this;
424
421
 
425
422
  const r = connection.results.get('karma');
426
- if (!r) { return next(); }
423
+ if (!r) return next();
427
424
 
428
425
  plugin.check_awards(connection); // update awards first
429
426
 
@@ -487,7 +484,7 @@ exports.hook_deny = function (next, connection, params) {
487
484
  }
488
485
 
489
486
  // intercept any other denials
490
- connection.results.add(plugin, { msg: 'deny:' + pi_name });
487
+ connection.results.add(plugin, { msg: `deny: ${pi_name}` });
491
488
  connection.results.incr(plugin, { score: -2 });
492
489
 
493
490
  next(constants.OK); // resume the connection
@@ -585,7 +582,7 @@ exports.ip_history_from_redis = function (next, connection) {
585
582
  if (plugin.should_we_skip(connection)) return next();
586
583
 
587
584
  const expire = (plugin.cfg.redis.expire_days || 60) * 86400; // to days
588
- const dbkey = 'karma|' + connection.remote.ip;
585
+ const dbkey = `karma|${connection.remote.ip}`;
589
586
 
590
587
  // redis plugin is emitting errors, no need to here
591
588
  if (!plugin.db) return next();
@@ -641,7 +638,7 @@ exports.hook_mail = function (next, connection, params) {
641
638
  // look for invalid (RFC 5321,(2)821) space in envelope from
642
639
  const full_from = connection.current_line;
643
640
  if (full_from.toUpperCase().substring(0,11) !== 'MAIL FROM:<') {
644
- connection.loginfo(plugin, 'RFC ignorant env addr format: ' + full_from);
641
+ connection.loginfo(plugin, `RFC ignorant env addr format: ${full_from}`);
645
642
  connection.results.add(plugin, {fail: 'rfc5321.MailFrom'});
646
643
  }
647
644
 
@@ -706,7 +703,7 @@ exports.hook_data_post = function (next, connection) {
706
703
  plugin.check_awards(connection); // update awards
707
704
 
708
705
  const results = connection.results.collate(plugin);
709
- connection.logdebug(plugin, 'adding header: ' + results);
706
+ connection.logdebug(plugin, `adding header: ${results}`);
710
707
  connection.transaction.remove_header('X-Haraka-Karma');
711
708
  connection.transaction.add_header('X-Haraka-Karma', results);
712
709
 
@@ -717,7 +714,7 @@ exports.increment = function (connection, key, val) {
717
714
  const plugin = this;
718
715
  if (!plugin.db) return;
719
716
 
720
- plugin.db.hincrby('karma|' + connection.remote.ip, key, 1);
717
+ plugin.db.hincrby(`karma|${connection.remote.ip}`, key, 1);
721
718
 
722
719
  const asnkey = plugin.get_asn_key(connection);
723
720
  if (asnkey) plugin.db.hincrby(asnkey, key, 1);
@@ -726,10 +723,10 @@ exports.increment = function (connection, key, val) {
726
723
  exports.hook_disconnect = function (next, connection) {
727
724
  const plugin = this;
728
725
 
729
- if (plugin.should_we_skip(connection)) return next();
730
-
731
726
  plugin.redis_unsubscribe(connection);
732
727
 
728
+ if (plugin.should_we_skip(connection)) return next();
729
+
733
730
  const k = connection.results.get('karma');
734
731
  if (!k || k.score === undefined) {
735
732
  connection.results.add(plugin, {err: 'karma results missing'});
@@ -761,11 +758,11 @@ exports.get_award_loc_from_note = function (connection, award) {
761
758
  if (obj) { return obj; }
762
759
  }
763
760
 
764
- // connection.logdebug(plugin, 'no txn note: ' + award);
761
+ // connection.logdebug(plugin, `no txn note: ${award}`);
765
762
  const obj = plugin.assemble_note_obj(connection, award);
766
763
  if (obj) return obj;
767
764
 
768
- // connection.logdebug(plugin, 'no conn note: ' + award);
765
+ // connection.logdebug(plugin, `no conn note: ${award}`);
769
766
  return;
770
767
  }
771
768
 
@@ -775,7 +772,7 @@ exports.get_award_loc_from_results = function (connection, loc_bits) {
775
772
  let notekey = loc_bits[2];
776
773
 
777
774
  if (phase_prefixes[pi_name]) {
778
- pi_name = loc_bits[1] + '.' + loc_bits[2];
775
+ pi_name = `${loc_bits[1]}.${loc_bits[2]}`;
779
776
  notekey = loc_bits[3];
780
777
  }
781
778
 
@@ -784,17 +781,16 @@ exports.get_award_loc_from_results = function (connection, loc_bits) {
784
781
  obj = connection.transaction.results.get(pi_name);
785
782
  }
786
783
  if (!obj) {
787
- // connection.logdebug(plugin, 'no txn results: ' + pi_name);
784
+ // connection.logdebug(plugin, `no txn results: ${pi_name}`);
788
785
  obj = connection.results.get(pi_name);
789
786
  }
790
787
  if (!obj) {
791
- // connection.logdebug(plugin, 'no conn results: ' + pi_name);
788
+ // connection.logdebug(plugin, `no conn results: ${pi_name}`);
792
789
  return;
793
790
  }
794
791
 
795
- // connection.logdebug(plugin, 'found results for ' + pi_name +
796
- // ', ' + notekey);
797
- if (notekey) { return obj[notekey]; }
792
+ // connection.logdebug(plugin, `found results for ${pi_name}, ${notekey}`);
793
+ if (notekey) return obj[notekey];
798
794
  return obj;
799
795
  }
800
796
 
@@ -823,7 +819,7 @@ exports.get_award_location = function (connection, award_key) {
823
819
  return plugin.get_award_loc_from_results(connection.transaction, loc_bits);
824
820
  }
825
821
 
826
- connection.logdebug(plugin, 'unknown location for ' + award_key);
822
+ connection.logdebug(plugin, `unknown location for ${award_key}`);
827
823
  }
828
824
 
829
825
  exports.get_award_condition = function (note_key, note_val) {
@@ -871,8 +867,7 @@ exports.check_awards = function (connection) {
871
867
  if (note !== wants) { continue; } // didn't match
872
868
  }
873
869
 
874
- // connection.loginfo(plugin, 'check_awards, case matching for: ' +
875
- // wants);
870
+ // connection.loginfo(plugin, `check_awards, case matching for: ${wants}`
876
871
 
877
872
  // the matching logic here is inverted, weeding out misses (continue)
878
873
  // Matches fall through (break) to the apply_award below.
@@ -908,8 +903,7 @@ exports.check_awards = function (connection) {
908
903
  if (note.length !== parseFloat(wants)) { continue; }
909
904
  break;
910
905
  default:
911
- connection.logerror(plugin, 'length operator "' +
912
- operator + '" not supported.');
906
+ connection.logerror(plugin, `length operator "${operator}" not supported.`);
913
907
  continue;
914
908
  }
915
909
  break;
@@ -933,15 +927,14 @@ exports.apply_award = function (connection, nl, award) {
933
927
  const plugin = this;
934
928
  if (!award) { return; }
935
929
  if (isNaN(award)) { // garbage in config
936
- connection.logerror(plugin, 'non-numeric award from: ' + nl + ':' +
937
- award);
930
+ connection.logerror(plugin, `non-numeric award from: ${nl}:${award}`);
938
931
  return;
939
932
  }
940
933
 
941
934
  const bits = nl.split('@'); nl = bits[0]; // strip off @... if present
942
935
 
943
936
  connection.results.incr(plugin, {score: award});
944
- connection.logdebug(plugin, 'applied ' + nl + ':' + award);
937
+ connection.logdebug(plugin, `applied ${nl}:${award}`);
945
938
 
946
939
  let trimmed = nl.substring(0, 5) === 'notes' ? nl.substring(6) :
947
940
  nl.substring(0, 7) === 'results' ? nl.substring(8) :
@@ -963,7 +956,7 @@ exports.check_spammy_tld = function (mail_from, connection) {
963
956
  if (mail_from.isNull()) return; // null sender (bounce)
964
957
 
965
958
  const from_tld = mail_from.host.split('.').pop();
966
- // connection.logdebug(plugin, 'from_tld: ' + from_tld);
959
+ // connection.logdebug(plugin, `from_tld: ${from_tld}`);
967
960
 
968
961
  const tld_penalty = parseFloat(plugin.cfg.spammy_tlds[from_tld] || 0);
969
962
  if (tld_penalty === 0) return;
@@ -979,8 +972,7 @@ exports.check_syntax_RcptTo = function (connection) {
979
972
  const full_rcpt = connection.current_line;
980
973
  if (full_rcpt.toUpperCase().substring(0,9) === 'RCPT TO:<') { return; }
981
974
 
982
- connection.loginfo(plugin, 'illegal envelope address format: ' +
983
- full_rcpt );
975
+ connection.loginfo(plugin, `illegal envelope address format: ${full_rcpt}`);
984
976
  connection.results.add(plugin, {fail: 'rfc5321.RcptTo'});
985
977
  }
986
978
 
@@ -990,7 +982,7 @@ exports.assemble_note_obj = function (prefix, key) {
990
982
  while (parts.length > 0) {
991
983
  let next = parts.shift();
992
984
  if (phase_prefixes[next]) {
993
- next = next + '.' + parts.shift();
985
+ next = `${next}.${parts.shift()}`;
994
986
  }
995
987
  note = note[next];
996
988
  if (note === null || note === undefined) { break; }
@@ -1067,7 +1059,7 @@ exports.get_asn_key = function (connection) {
1067
1059
  asn = connection.results.get('geoip');
1068
1060
  }
1069
1061
  if (!asn || !asn.asn || isNaN(asn.asn)) { return; }
1070
- return 'as' + asn.asn;
1062
+ return `as${asn.asn}`;
1071
1063
  }
1072
1064
 
1073
1065
  exports.init_asn = function (asnkey, expire) {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "haraka-plugin-karma",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "A heuristics scoring and reputation engine for SMTP connections",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "npx mocha",
8
- "lint": "npx eslint *.js test/*.js",
9
- "lintfix": "npx eslint --fix *.js test/*.js",
8
+ "lint": "npx eslint index.js test/*.js",
9
+ "lintfix": "npx eslint --fix index.js test/*.js",
10
10
  "cover": "npx istanbul cover npx mocha"
11
11
  },
12
12
  "repository": {