haraka-plugin-karma
Advanced tools
Comparing version 2.1.3 to 2.1.4
403
index.js
@@ -5,21 +5,34 @@ 'use strict' | ||
const constants = require('haraka-constants') | ||
const redis = require('redis') | ||
const utils = require('haraka-utils') | ||
const redis = require('redis') | ||
const utils = require('haraka-utils') | ||
const phase_prefixes = utils.to_object([ | ||
'connect','helo','mail_from','rcpt_to','data' | ||
'connect', | ||
'helo', | ||
'mail_from', | ||
'rcpt_to', | ||
'data', | ||
]) | ||
exports.register = function () { | ||
this.inherits('haraka-plugin-redis') | ||
// set up defaults | ||
this.deny_hooks = utils.to_object( | ||
['unrecognized_command','helo','data','data_post','queue','queue_outbound'] | ||
) | ||
this.deny_hooks = utils.to_object([ | ||
'unrecognized_command', | ||
'helo', | ||
'data', | ||
'data_post', | ||
'queue', | ||
'queue_outbound', | ||
]) | ||
this.deny_exclude_hooks = utils.to_object('rcpt_to queue queue_outbound') | ||
this.deny_exclude_plugins = utils.to_object([ | ||
'access', 'helo.checks', 'data.headers', 'spamassassin', | ||
'mail_from.is_resolvable', 'clamd', 'tls' | ||
'access', | ||
'helo.checks', | ||
'data.headers', | ||
'spamassassin', | ||
'mail_from.is_resolvable', | ||
'clamd', | ||
'tls', | ||
]) | ||
@@ -29,4 +42,4 @@ | ||
this.register_hook('init_master', 'init_redis_plugin') | ||
this.register_hook('init_child', 'init_redis_plugin') | ||
this.register_hook('init_master', 'init_redis_plugin') | ||
this.register_hook('init_child', 'init_redis_plugin') | ||
@@ -40,9 +53,11 @@ this.register_hook('connect_init', 'results_init') | ||
plugin.cfg = plugin.config.get('karma.ini', { | ||
booleans: [ | ||
'+asn.enable', | ||
], | ||
}, function () { | ||
plugin.load_karma_ini() | ||
}) | ||
plugin.cfg = plugin.config.get( | ||
'karma.ini', | ||
{ | ||
booleans: ['+asn.enable'], | ||
}, | ||
function () { | ||
plugin.load_karma_ini() | ||
}, | ||
) | ||
@@ -79,3 +94,2 @@ plugin.merge_redis_ini() | ||
exports.results_init = async function (next, connection) { | ||
if (this.should_we_skip(connection)) { | ||
@@ -88,3 +102,3 @@ connection.logdebug(this, 'skipping') | ||
connection.logerror(this, 'this should never happen') | ||
return next() // init once per connection | ||
return next() // init once per connection | ||
} | ||
@@ -100,7 +114,6 @@ | ||
} | ||
connection.results.add(this, { score:0, todo }) | ||
connection.results.add(this, { score: 0, todo }) | ||
} else { | ||
connection.results.add(this, { score: 0 }) | ||
} | ||
else { | ||
connection.results.add(this, { score:0 }) | ||
} | ||
@@ -112,3 +125,3 @@ if (!connection.server.notes.redis) { | ||
if (!this.result_awards) return next() // not configured | ||
if (!this.result_awards) return next() // not configured | ||
@@ -138,6 +151,5 @@ if (connection.notes.redis) { | ||
for (const anum of Object.keys(cra)) { | ||
const [pi_name, prop, operator, value, award, reason, resolv] = | ||
cra[anum].split(/(?:\s*\|\s*)/) | ||
const [pi_name, prop, operator, value, award, reason, resolv] | ||
= cra[anum].split(/(?:\s*\|\s*)/) | ||
const ra = this.result_awards | ||
@@ -154,3 +166,2 @@ | ||
exports.check_result = function (connection, message) { | ||
// connection.loginfo(this, message); | ||
@@ -164,9 +175,10 @@ // {"plugin":"karma","result":{"fail":"spamassassin.hits"}} | ||
} | ||
if (!this.result_awards[m.plugin]) return // no awards for plugin | ||
if (!this.result_awards[m.plugin]) return // no awards for plugin | ||
for (const r of Object.keys(m.result)) { // each result in mess | ||
if (r === 'emit') continue // r: pass, fail, skip, err, ... | ||
for (const r of Object.keys(m.result)) { | ||
// each result in mess | ||
if (r === 'emit') continue // r: pass, fail, skip, err, ... | ||
const pi_prop = this.result_awards[m.plugin][r] | ||
if (!pi_prop) continue // no award for this plugin property | ||
if (!pi_prop) continue // no award for this plugin property | ||
@@ -182,3 +194,4 @@ const thisResult = m.result[r] | ||
// do any award conditions match this result? | ||
for (const thisAward of pi_prop) { // each award... | ||
for (const thisAward of pi_prop) { | ||
// each award... | ||
// { id: '011', operator: 'equals', value: 'all_bad', award: '-2'} | ||
@@ -210,3 +223,2 @@ const thisResArr = this.result_as_array(thisResult) | ||
exports.result_as_array = function (result) { | ||
if (typeof result === 'string') return [result] | ||
@@ -218,3 +230,3 @@ if (typeof result === 'number') return [result] | ||
const array = [] | ||
Object.keys(result).forEach(tr => { | ||
Object.keys(result).forEach((tr) => { | ||
array.push(result[tr]) | ||
@@ -232,8 +244,7 @@ }) | ||
conn.results.incr(this, {score: this.cfg.asn_awards[asn]}) | ||
conn.results.push(this, {fail: 'asn_awards'}) | ||
conn.results.incr(this, { score: this.cfg.asn_awards[asn] }) | ||
conn.results.push(this, { fail: 'asn_awards' }) | ||
} | ||
exports.check_result_lt = function (thisResult, thisAward, conn) { | ||
for (const element of thisResult) { | ||
@@ -244,4 +255,4 @@ const tr = parseFloat(element) | ||
conn.results.incr(this, {score: thisAward.award}) | ||
conn.results.push(this, {awards: thisAward.id}) | ||
conn.results.incr(this, { score: thisAward.award }) | ||
conn.results.push(this, { awards: thisAward.id }) | ||
} | ||
@@ -251,3 +262,2 @@ } | ||
exports.check_result_gt = function (thisResult, thisAward, conn) { | ||
for (const element of thisResult) { | ||
@@ -258,4 +268,4 @@ const tr = parseFloat(element) | ||
conn.results.incr(this, {score: thisAward.award}) | ||
conn.results.push(this, {awards: thisAward.id}) | ||
conn.results.incr(this, { score: thisAward.award }) | ||
conn.results.push(this, { awards: thisAward.id }) | ||
} | ||
@@ -265,8 +275,6 @@ } | ||
exports.check_result_equal = function (thisResult, thisAward, conn) { | ||
for (const element of thisResult) { | ||
if (thisAward.value === 'true') { | ||
if (!element) continue | ||
} | ||
else { | ||
} else { | ||
if (element != thisAward.value) continue | ||
@@ -279,4 +287,4 @@ } | ||
conn.results.incr(this, {score: thisAward.award}) | ||
conn.results.push(this, {awards: thisAward.id}) | ||
conn.results.incr(this, { score: thisAward.award }) | ||
conn.results.push(this, { awards: thisAward.id }) | ||
} | ||
@@ -292,4 +300,4 @@ } | ||
conn.results.incr(this, {score: thisAward.award}) | ||
conn.results.push(this, {awards: thisAward.id}) | ||
conn.results.incr(this, { score: thisAward.award }) | ||
conn.results.push(this, { awards: thisAward.id }) | ||
} | ||
@@ -299,3 +307,2 @@ } | ||
exports.check_result_length = function (thisResult, thisAward, conn) { | ||
for (const element of thisResult) { | ||
@@ -321,4 +328,4 @@ const [operator, qty] = thisAward.value.split(/\s+/) // requires node 6+ | ||
conn.results.incr(this, {score: thisAward.award}) | ||
conn.results.push(this, {awards: thisAward.id }) | ||
conn.results.incr(this, { score: thisAward.award }) | ||
conn.results.push(this, { awards: thisAward.id }) | ||
} | ||
@@ -328,3 +335,2 @@ } | ||
exports.check_result_exists = function (thisResult, thisAward, conn) { | ||
/* eslint-disable no-unused-vars */ | ||
@@ -343,4 +349,4 @@ for (const r of thisResult) { | ||
conn.results.incr(this, {score: thisAward.award}) | ||
conn.results.push(this, {awards: thisAward.id}) | ||
conn.results.incr(this, { score: thisAward.award }) | ||
conn.results.push(this, { awards: thisAward.id }) | ||
} | ||
@@ -350,3 +356,2 @@ } | ||
exports.apply_tarpit = function (connection, hook, score, next) { | ||
if (!this.cfg.tarpit) return next() // tarpit disabled in config | ||
@@ -356,3 +361,3 @@ | ||
// wait. Then bad things happen, like a Haraka crash. | ||
if (utils.in_array(hook, ['reset_transaction','queue'])) return next() | ||
if (utils.in_array(hook, ['reset_transaction', 'queue'])) return next() | ||
@@ -376,3 +381,2 @@ // no delay for senders with good karma | ||
exports.tarpit_delay = function (score, connection, hook, k) { | ||
if (this.cfg.tarpit.delay && parseFloat(this.cfg.tarpit.delay)) { | ||
@@ -383,7 +387,9 @@ connection.logdebug(this, 'static tarpit') | ||
const delay = score * -1 // progressive tarpit | ||
const delay = score * -1 // progressive tarpit | ||
// detect roaming users based on MSA ports that require auth | ||
if (utils.in_array(connection.local.port, [587,465]) && | ||
utils.in_array(hook, ['ehlo','connect'])) { | ||
if ( | ||
utils.in_array(connection.local.port, [587, 465]) && | ||
utils.in_array(hook, ['ehlo', 'connect']) | ||
) { | ||
return this.tarpit_delay_msa(connection, delay, k) | ||
@@ -407,3 +413,3 @@ } | ||
// Reduce delay for good history | ||
const history = ((k.good || 0) - (k.bad || 0)) | ||
const history = (k.good || 0) - (k.bad || 0) | ||
if (history > 0) { | ||
@@ -441,8 +447,8 @@ delay = delay - 2 | ||
this.check_awards(connection) // update awards first | ||
this.check_awards(connection) // update awards first | ||
const score = parseFloat(r.score) | ||
if (isNaN(score)) { | ||
if (isNaN(score)) { | ||
connection.logerror(this, 'score is NaN') | ||
connection.results.add(this, {score: 0}) | ||
connection.results.add(this, { score: 0 }) | ||
return next() | ||
@@ -483,6 +489,6 @@ } | ||
// let pi_message = params[1]; | ||
const pi_name = params[2] | ||
const pi_name = params[2] | ||
// let pi_function = params[3]; | ||
// let pi_params = params[4]; | ||
const pi_hook = params[5] | ||
const pi_hook = params[5] | ||
@@ -502,3 +508,3 @@ // exceptions, whose 'DENY' should not be captured | ||
next(constants.OK) // resume the connection | ||
next(constants.OK) // resume the connection | ||
} | ||
@@ -561,3 +567,3 @@ | ||
connection.results.add(this, {emit: true}) | ||
connection.results.add(this, { emit: true }) | ||
this.should_we_deny(next, connection, 'reset_transaction') | ||
@@ -567,3 +573,2 @@ } | ||
exports.hook_unrecognized_command = function (next, connection, params) { | ||
if (this.should_we_skip(connection)) return next() | ||
@@ -577,4 +582,4 @@ | ||
connection.results.incr(this, {score: -1}) | ||
connection.results.add(this, {fail: `cmd:(${params})`}) | ||
connection.results.incr(this, { score: -1 }) | ||
connection.results.add(this, { fail: `cmd:(${params})` }) | ||
@@ -590,3 +595,3 @@ return this.should_we_deny(next, connection, 'unrecognized_command') | ||
const expire = (this.cfg.redis.expire_days || 60) * 86400 // to days | ||
const dbkey = `karma|${connection.remote.ip}` | ||
const dbkey = `karma|${connection.remote.ip}` | ||
@@ -596,38 +601,41 @@ // redis plugin is emitting errors, no need to here | ||
this.db.hGetAll(dbkey).then(dbr => { | ||
if (dbr === null) { | ||
plugin.init_ip(dbkey, connection.remote.ip, expire) | ||
return next() | ||
} | ||
this.db | ||
.hGetAll(dbkey) | ||
.then((dbr) => { | ||
if (dbr === null) { | ||
plugin.init_ip(dbkey, connection.remote.ip, expire) | ||
return next() | ||
} | ||
plugin.db.multi() | ||
.hIncrBy(dbkey, 'connections', 1) // increment total conn | ||
.expire(dbkey, expire) // extend expiration | ||
.exec() | ||
.catch(err => { | ||
connection.results.add(plugin, { err }) | ||
}) | ||
plugin.db | ||
.multi() | ||
.hIncrBy(dbkey, 'connections', 1) // increment total conn | ||
.expire(dbkey, expire) // extend expiration | ||
.exec() | ||
.catch((err) => { | ||
connection.results.add(plugin, { err }) | ||
}) | ||
const results = { | ||
good: dbr.good, | ||
bad: dbr.bad, | ||
connections: dbr.connections, | ||
history: parseInt((dbr.good || 0) - (dbr.bad || 0)), | ||
emit: true, | ||
} | ||
const results = { | ||
good: dbr.good, | ||
bad: dbr.bad, | ||
connections: dbr.connections, | ||
history: parseInt((dbr.good || 0) - (dbr.bad || 0)), | ||
emit: true, | ||
} | ||
// Careful: don't become self-fulfilling prophecy. | ||
if (parseInt(dbr.good) > 5 && parseInt(dbr.bad) === 0) { | ||
results.pass = 'all_good' | ||
} | ||
if (parseInt(dbr.bad) > 5 && parseInt(dbr.good) === 0) { | ||
results.fail = 'all_bad' | ||
} | ||
// Careful: don't become self-fulfilling prophecy. | ||
if (parseInt(dbr.good) > 5 && parseInt(dbr.bad) === 0) { | ||
results.pass = 'all_good' | ||
} | ||
if (parseInt(dbr.bad) > 5 && parseInt(dbr.good) === 0) { | ||
results.fail = 'all_bad' | ||
} | ||
connection.results.add(plugin, results) | ||
connection.results.add(plugin, results) | ||
plugin.check_awards(connection) | ||
next() | ||
}) | ||
.catch(err => { | ||
plugin.check_awards(connection) | ||
next() | ||
}) | ||
.catch((err) => { | ||
connection.results.add(plugin, { err }) | ||
@@ -639,3 +647,2 @@ next() | ||
exports.hook_mail = function (next, connection, params) { | ||
if (this.should_we_skip(connection)) return next() | ||
@@ -647,5 +654,5 @@ | ||
const full_from = connection.current_line | ||
if (full_from.toUpperCase().substring(0,11) !== 'MAIL FROM:<') { | ||
if (full_from.toUpperCase().substring(0, 11) !== 'MAIL FROM:<') { | ||
connection.loginfo(this, `RFC ignorant env addr format: ${full_from}`) | ||
connection.results.add(this, {fail: 'rfc5321.MailFrom'}) | ||
connection.results.add(this, { fail: 'rfc5321.MailFrom' }) | ||
} | ||
@@ -656,6 +663,6 @@ | ||
if (this.cfg.tls.set && connection.tls.enabled) { | ||
connection.results.incr(this, {score: this.cfg.tls.set}) | ||
connection.results.incr(this, { score: this.cfg.tls.set }) | ||
} | ||
if (this.cfg.tls.unset && !connection.tls.enabled) { | ||
connection.results.incr(this, {score: this.cfg.tls.unset}) | ||
connection.results.incr(this, { score: this.cfg.tls.unset }) | ||
} | ||
@@ -668,3 +675,2 @@ } | ||
exports.hook_rcpt = function (next, connection, params) { | ||
if (this.should_we_skip(connection)) return next() | ||
@@ -680,3 +686,3 @@ | ||
if (connection?.transaction?.mail_from?.user === rcpt.user) { | ||
connection.results.add(this, {fail: 'env_user_match'}) | ||
connection.results.add(this, { fail: 'env_user_match' }) | ||
} | ||
@@ -686,3 +692,3 @@ | ||
connection.results.add(this, {fail: 'rcpt_to'}) | ||
connection.results.add(this, { fail: 'rcpt_to' }) | ||
@@ -693,3 +699,2 @@ return this.should_we_deny(next, connection, 'rcpt') | ||
exports.hook_rcpt_ok = function (next, connection, rcpt) { | ||
if (this.should_we_skip(connection)) return next() | ||
@@ -699,3 +704,3 @@ | ||
if (txn && txn.mail_from && txn.mail_from.user === rcpt.user) { | ||
connection.results.add(this, {fail: 'env_user_match'}) | ||
connection.results.add(this, { fail: 'env_user_match' }) | ||
} | ||
@@ -713,3 +718,3 @@ | ||
this.check_awards(connection) // update awards | ||
this.check_awards(connection) // update awards | ||
@@ -740,3 +745,3 @@ const results = connection.results.collate(this) | ||
if (!k || k.score === undefined) { | ||
connection.results.add(this, {err: 'karma results missing'}) | ||
connection.results.add(this, { err: 'karma results missing' }) | ||
return next() | ||
@@ -747,3 +752,3 @@ } | ||
this.check_awards(connection) | ||
connection.results.add(this, {msg: 'no action', emit: true }) | ||
connection.results.add(this, { msg: 'no action', emit: true }) | ||
return next() | ||
@@ -759,3 +764,3 @@ } | ||
connection.results.add(this, {emit: true }) | ||
connection.results.add(this, { emit: true }) | ||
next() | ||
@@ -765,3 +770,2 @@ } | ||
exports.get_award_loc_from_note = function (connection, award) { | ||
if (connection.transaction) { | ||
@@ -781,3 +785,2 @@ const obj = this.assemble_note_obj(connection.transaction, award) | ||
exports.get_award_loc_from_results = function (connection, loc_bits) { | ||
let pi_name = loc_bits[1] | ||
@@ -809,7 +812,9 @@ let notekey = loc_bits[2] | ||
if (loc_bits[0] === 'notes') { // ex: notes.spf_mail_helo | ||
if (loc_bits[0] === 'notes') { | ||
// ex: notes.spf_mail_helo | ||
return this.get_award_loc_from_note(connection, bits[0]) | ||
} | ||
if (loc_bits[0] === 'results') { // ex: results.geoip.distance | ||
if (loc_bits[0] === 'results') { | ||
// ex: results.geoip.distance | ||
return this.get_award_loc_from_results(connection, loc_bits) | ||
@@ -819,3 +824,7 @@ } | ||
// ex: transaction.results.spf | ||
if (connection.transaction && loc_bits[0] === 'transaction' && loc_bits[1] === 'results') { | ||
if ( | ||
connection.transaction && | ||
loc_bits[0] === 'transaction' && | ||
loc_bits[1] === 'results' | ||
) { | ||
loc_bits.shift() | ||
@@ -831,7 +840,9 @@ return this.get_award_loc_from_results(connection.transaction, loc_bits) | ||
const keybits = note_key.split('@') | ||
if (keybits[1]) { wants = keybits[1] } | ||
if (keybits[1]) { | ||
wants = keybits[1] | ||
} | ||
const valbits = note_val.split(/\s+/) | ||
if (!valbits[1]) return wants | ||
if (valbits[1] !== 'if') return wants // no if condition | ||
if (valbits[1] !== 'if') return wants // no if condition | ||
@@ -862,5 +873,7 @@ if (valbits[2].match(/^(equals|gt|lt|match)$/)) { | ||
const award = parseFloat(bits[0]) | ||
if (!bits[1] || bits[1] !== 'if') { // no if conditions | ||
if (!note) continue // failed truth test | ||
if (!wants) { // no wants, truth matches | ||
if (!bits[1] || bits[1] !== 'if') { | ||
// no if conditions | ||
if (!note) continue // failed truth test | ||
if (!wants) { | ||
// no wants, truth matches | ||
this.apply_award(connection, key, award) | ||
@@ -870,3 +883,3 @@ delete karma.todo[key] | ||
} | ||
if (note !== wants) continue // didn't match | ||
if (note !== wants) continue // didn't match | ||
} | ||
@@ -898,3 +911,5 @@ | ||
const operator = bits[3] | ||
if (bits[4]) { wants = bits[4] } | ||
if (bits[4]) { | ||
wants = bits[4] | ||
} | ||
switch (operator) { | ||
@@ -911,3 +926,6 @@ case 'gt': | ||
default: | ||
connection.logerror(this, `length operator "${operator}" not supported.`) | ||
connection.logerror( | ||
this, | ||
`length operator "${operator}" not supported.`, | ||
) | ||
continue | ||
@@ -917,5 +935,7 @@ } | ||
} | ||
case 'in': // if in pass whitelisted | ||
case 'in': // if in pass whitelisted | ||
// let list = bits[3]; | ||
if (bits[4]) { wants = bits[4] } | ||
if (bits[4]) { | ||
wants = bits[4] | ||
} | ||
if (!Array.isArray(note)) continue | ||
@@ -935,3 +955,4 @@ if (!wants) continue | ||
if (!award) return | ||
if (isNaN(award)) { // garbage in config | ||
if (isNaN(award)) { | ||
// garbage in config | ||
connection.logerror(this, `non-numeric award from: ${nl}:${award}`) | ||
@@ -941,16 +962,21 @@ return | ||
const bits = nl.split('@'); nl = bits[0] // strip off @... if present | ||
const bits = nl.split('@') | ||
nl = bits[0] // strip off @... if present | ||
connection.results.incr(this, {score: award}) | ||
connection.results.incr(this, { score: award }) | ||
connection.logdebug(this, `applied ${nl}:${award}`) | ||
let trimmed = nl.substring(0, 5) === 'notes' ? nl.substring(6) : | ||
nl.substring(0, 7) === 'results' ? nl.substring(8) : | ||
nl.substring(0,19) === 'transaction.results' ? | ||
nl.substring(20) : nl | ||
let trimmed = | ||
nl.substring(0, 5) === 'notes' | ||
? nl.substring(6) | ||
: nl.substring(0, 7) === 'results' | ||
? nl.substring(8) | ||
: nl.substring(0, 19) === 'transaction.results' | ||
? nl.substring(20) | ||
: nl | ||
if (trimmed.substring(0,7) === 'rcpt_to') trimmed = trimmed.substring(8) | ||
if (trimmed.substring(0,7) === 'mail_from') trimmed = trimmed.substring(10) | ||
if (trimmed.substring(0,7) === 'connect') trimmed = trimmed.substring(8) | ||
if (trimmed.substring(0,4) === 'data') trimmed = trimmed.substring(5) | ||
if (trimmed.substring(0, 7) === 'rcpt_to') trimmed = trimmed.substring(8) | ||
if (trimmed.substring(0, 7) === 'mail_from') trimmed = trimmed.substring(10) | ||
if (trimmed.substring(0, 7) === 'connect') trimmed = trimmed.substring(8) | ||
if (trimmed.substring(0, 4) === 'data') trimmed = trimmed.substring(5) | ||
@@ -963,3 +989,3 @@ if (award > 0) connection.results.add(this, { pass: trimmed }) | ||
if (!this.cfg.spammy_tlds) return | ||
if (mail_from.isNull()) return // null sender (bounce) | ||
if (mail_from.isNull()) return // null sender (bounce) | ||
@@ -972,4 +998,4 @@ const from_tld = mail_from.host.split('.').pop() | ||
connection.results.incr(this, {score: tld_penalty}) | ||
connection.results.add(this, {fail: 'spammy.TLD'}) | ||
connection.results.incr(this, { score: tld_penalty }) | ||
connection.results.add(this, { fail: 'spammy.TLD' }) | ||
} | ||
@@ -980,6 +1006,6 @@ | ||
const full_rcpt = connection.current_line | ||
if (full_rcpt.toUpperCase().substring(0,9) === 'RCPT TO:<') return | ||
if (full_rcpt.toUpperCase().substring(0, 9) === 'RCPT TO:<') return | ||
connection.loginfo(this, `illegal envelope address format: ${full_rcpt}`) | ||
connection.results.add(this, {fail: 'rfc5321.RcptTo'}) | ||
connection.results.add(this, { fail: 'rfc5321.RcptTo' }) | ||
} | ||
@@ -1008,38 +1034,39 @@ | ||
this.db.hGetAll(asnkey).then(res => { | ||
if (res === null) { | ||
const expire = (this.cfg.redis.expire_days || 60) * 86400 // days | ||
this.init_asn(asnkey, expire) | ||
return | ||
} | ||
this.db | ||
.hGetAll(asnkey) | ||
.then((res) => { | ||
if (res === null) { | ||
const expire = (this.cfg.redis.expire_days || 60) * 86400 // days | ||
this.init_asn(asnkey, expire) | ||
return | ||
} | ||
this.db.hIncrBy(asnkey, 'connections', 1) | ||
const asn_score = parseInt(res.good || 0) - (res.bad || 0) | ||
const asn_results = { | ||
asn_score, | ||
asn_connections: res.connections, | ||
asn_good: res.good, | ||
asn_bad: res.bad, | ||
emit: true, | ||
} | ||
this.db.hIncrBy(asnkey, 'connections', 1) | ||
const asn_score = parseInt(res.good || 0) - (res.bad || 0) | ||
const asn_results = { | ||
asn_score, | ||
asn_connections: res.connections, | ||
asn_good: res.good, | ||
asn_bad: res.bad, | ||
emit: true, | ||
} | ||
if (asn_score) { | ||
if (asn_score < -5) { | ||
asn_results.fail = 'asn:history' | ||
if (asn_score) { | ||
if (asn_score < -5) { | ||
asn_results.fail = 'asn:history' | ||
} else if (asn_score > 5) { | ||
asn_results.pass = 'asn:history' | ||
} | ||
} | ||
else if (asn_score > 5) { | ||
asn_results.pass = 'asn:history' | ||
if (parseInt(res.bad) > 5 && parseInt(res.good) === 0) { | ||
asn_results.fail = 'asn:all_bad' | ||
} | ||
} | ||
if (parseInt(res.good) > 5 && parseInt(res.bad) === 0) { | ||
asn_results.pass = 'asn:all_good' | ||
} | ||
if (parseInt(res.bad) > 5 && parseInt(res.good) === 0) { | ||
asn_results.fail = 'asn:all_bad' | ||
} | ||
if (parseInt(res.good) > 5 && parseInt(res.bad) === 0) { | ||
asn_results.pass = 'asn:all_good' | ||
} | ||
connection.results.add(report_as, asn_results) | ||
}) | ||
.catch(err => { | ||
connection.results.add(report_as, asn_results) | ||
}) | ||
.catch((err) => { | ||
connection.results.add(this, { err }) | ||
@@ -1051,4 +1078,5 @@ }) | ||
if (!this.db) return | ||
await this.db.multi() | ||
.hmSet(dbkey, {'bad': 0, 'good': 0, 'connections': 1}) | ||
await this.db | ||
.multi() | ||
.hmSet(dbkey, { bad: 0, good: 0, connections: 1 }) | ||
.expire(dbkey, expire) | ||
@@ -1068,6 +1096,7 @@ .exec() | ||
if (!this.db) return | ||
this.db.multi() | ||
.hmSet(asnkey, {'bad': 0, 'good': 0, 'connections': 1}) | ||
.expire(asnkey, expire * 2) // keep ASN longer | ||
this.db | ||
.multi() | ||
.hmSet(asnkey, { bad: 0, good: 0, connections: 1 }) | ||
.expire(asnkey, expire * 2) // keep ASN longer | ||
.exec() | ||
} |
{ | ||
"name": "haraka-plugin-karma", | ||
"version": "2.1.3", | ||
"version": "2.1.4", | ||
"description": "A heuristics scoring and reputation engine for SMTP connections", | ||
"main": "index.js", | ||
"files": [ | ||
"CHANGELOG.md", | ||
"config", | ||
"test" | ||
], | ||
"scripts": { | ||
"test": "npx mocha", | ||
"lint": "npx eslint index.js test", | ||
"lintfix": "npx eslint --fix index.js test", | ||
"versions": "npx dependency-version-checker check" | ||
"format": "npm run prettier:fix && npm run lint:fix", | ||
"lint": "npx eslint@^8 *.js test", | ||
"lint:fix": "npx eslint@^8 *.js test --fix", | ||
"prettier": "npx prettier . --check", | ||
"prettier:fix": "npx prettier . --write --log-level=warn", | ||
"test": "npx mocha@10", | ||
"versions": "npx @msimerson/dependency-version-checker check", | ||
"versions:fix": "npx @msimerson/dependency-version-checker update" | ||
}, | ||
@@ -26,14 +35,12 @@ "repository": { | ||
"dependencies": { | ||
"address-rfc2821": "^2.1.1", | ||
"haraka-constants": "^1.0.2", | ||
"haraka-utils": "^1.0.3", | ||
"address-rfc2821": "^2.1.2", | ||
"haraka-constants": "^1.0.6", | ||
"haraka-utils": "^1.1.1", | ||
"haraka-plugin-redis": "^2.0.6", | ||
"redis": "^4.6.11" | ||
"redis": "^4.6.13" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^8.55.0", | ||
"eslint-plugin-haraka": "*", | ||
"haraka-test-fixtures": "*", | ||
"mocha": "^10.2.0" | ||
"@haraka/eslint-config": "^1.1.2", | ||
"haraka-test-fixtures": "^1.3.4" | ||
} | ||
} |
@@ -21,3 +21,2 @@ [![Build Status][ci-img]][ci-url] | ||
## How Karma Works | ||
@@ -36,5 +35,4 @@ | ||
Karma is not a replacement for content filters. Karma focuses on the quality of the **connection**. Content filters (bayes\*) focus on the content of the **message**. Karma works best *with* content filters. | ||
Karma is not a replacement for content filters. Karma focuses on the quality of the **connection**. Content filters (bayes\*) focus on the content of the **message**. Karma works best _with_ content filters. | ||
# CONFIG | ||
@@ -44,3 +42,2 @@ | ||
## <a name="awards"></a>AWARDS | ||
@@ -56,3 +53,2 @@ | ||
## <a name="penalties"></a>Penalties | ||
@@ -68,3 +64,3 @@ | ||
When each connection ends, *karma* records the result. When a sufficient history has been built for an IP or ASN, future connections from that address(es) will get a positive or negative karma award. | ||
When each connection ends, _karma_ records the result. When a sufficient history has been built for an IP or ASN, future connections from that address(es) will get a positive or negative karma award. | ||
@@ -106,9 +102,9 @@ The reward is purposefully small, to permit good senders in bad neighborhoods to still send. | ||
* [IP Reputation](#IP_Reputation) | ||
* [ASN reputation](#Neighbor_Reputation) | ||
* DENY events by other plugins | ||
* envelope sender from a spammy TLD | ||
* [malformed envelope addresses](#malformed_env) | ||
* [unrecognized SMTP commands](#unrecognized) | ||
* matching *env from* and *env to* name (rare in ham, frequent in spam) | ||
- [IP Reputation](#IP_Reputation) | ||
- [ASN reputation](#Neighbor_Reputation) | ||
- DENY events by other plugins | ||
- envelope sender from a spammy TLD | ||
- [malformed envelope addresses](#malformed_env) | ||
- [unrecognized SMTP commands](#unrecognized) | ||
- matching _env from_ and _env to_ name (rare in ham, frequent in spam) | ||
@@ -118,6 +114,5 @@ The data from these tests are helpful but the real power of karma is [scoring | ||
### <a name="IP_Reputation"></a>IP Reputation | ||
Karma records the number of good, bad, and total connections. The results | ||
Karma records the number of good, bad, and total connections. The results | ||
are accessible to other plugins as well. | ||
@@ -152,3 +147,3 @@ | ||
#### report\_as | ||
#### report_as | ||
@@ -160,3 +155,3 @@ Store the ASN results as another plugin. Example: I set `report_as=asn`, so that karma history for an ASN is reported with the ASN plugin data. A practical consequence of changing report_as is that the award location in karma.ini would need to change from: | ||
to: | ||
to: | ||
@@ -170,3 +165,2 @@ NNN asn | pass | equals | asn_all_good | 2 | ||
### <a name="unrecognized"></a>Unrecognized SMTP verbs/commands | ||
@@ -203,6 +197,4 @@ | ||
Spam delivered by servers with good reputations normally pass karma's checks. | ||
Expect to use karma *with* content filters. | ||
Expect to use karma _with_ content filters. | ||
[p0f-url]: /manual/plugins/connect.p0f.html | ||
@@ -209,0 +201,0 @@ [geoip-url]: https://github.com/haraka/haraka-plugin-geoip |
'use strict' | ||
const assert = require('assert') | ||
const assert = require('assert') | ||
const Address = require('address-rfc2821').Address | ||
const fixtures = require('haraka-test-fixtures') | ||
const constants = require('haraka-constants') | ||
const Address = require('address-rfc2821').Address | ||
const fixtures = require('haraka-test-fixtures') | ||
const constants = require('haraka-constants') | ||
const stub = fixtures.stub.stub | ||
const stub = fixtures.stub.stub | ||
function _set_up (done) { | ||
function _set_up(done) { | ||
this.plugin = new fixtures.plugin('karma') | ||
this.plugin.cfg = { main: {} } | ||
this.plugin.deny_hooks = {'connect': true} | ||
this.plugin.deny_hooks = { connect: true } | ||
this.plugin.tarpit_hooks = ['connect'] | ||
@@ -25,3 +24,2 @@ | ||
describe('karma_init', function () { | ||
@@ -88,3 +86,6 @@ beforeEach(function (done) { | ||
it('no auth fails', function (done) { | ||
const obj = this.plugin.assemble_note_obj(this.connection, 'notes.auth_fails') | ||
const obj = this.plugin.assemble_note_obj( | ||
this.connection, | ||
'notes.auth_fails', | ||
) | ||
assert.equal(undefined, obj) | ||
@@ -95,5 +96,8 @@ done() | ||
it('has auth fails', function (done) { | ||
this.connection.notes.auth_fails=[1,2] | ||
const obj = this.plugin.assemble_note_obj(this.connection, 'notes.auth_fails') | ||
assert.deepEqual([1,2], obj) | ||
this.connection.notes.auth_fails = [1, 2] | ||
const obj = this.plugin.assemble_note_obj( | ||
this.connection, | ||
'notes.auth_fails', | ||
) | ||
assert.deepEqual([1, 2], obj) | ||
done() | ||
@@ -111,3 +115,3 @@ }) | ||
} | ||
this.plugin.hook_deny(next, this.connection, ['','','','']) | ||
this.plugin.hook_deny(next, this.connection, ['', '', '', '']) | ||
}) | ||
@@ -120,3 +124,3 @@ | ||
} | ||
this.plugin.hook_deny(next, this.connection, ['','','karma','']) | ||
this.plugin.hook_deny(next, this.connection, ['', '', 'karma', '']) | ||
}) | ||
@@ -130,3 +134,3 @@ | ||
this.plugin.deny_exclude_plugins = { access: true } | ||
this.plugin.hook_deny(next, this.connection, ['','','access','']) | ||
this.plugin.hook_deny(next, this.connection, ['', '', 'access', '']) | ||
}) | ||
@@ -140,4 +144,10 @@ | ||
this.plugin.deny_exclude_hooks = { rcpt_to: true } | ||
this.plugin.hook_deny(next, this.connection, | ||
['','','','','','rcpt_to']) | ||
this.plugin.hook_deny(next, this.connection, [ | ||
'', | ||
'', | ||
'', | ||
'', | ||
'', | ||
'rcpt_to', | ||
]) | ||
}) | ||
@@ -151,3 +161,3 @@ | ||
this.plugin.deny_exclude_hooks = { queue: true } | ||
this.plugin.hook_deny(next, this.connection, ['','','','','','queue']) | ||
this.plugin.hook_deny(next, this.connection, ['', '', '', '', '', 'queue']) | ||
}) | ||
@@ -160,3 +170,10 @@ | ||
} | ||
this.plugin.hook_deny(next, this.connection, [constants.DENYSOFT,'','','','','']) | ||
this.plugin.hook_deny(next, this.connection, [ | ||
constants.DENYSOFT, | ||
'', | ||
'', | ||
'', | ||
'', | ||
'', | ||
]) | ||
}) | ||
@@ -169,3 +186,3 @@ }) | ||
it('relaying=false', function (done) { | ||
this.connection.relaying=false | ||
this.connection.relaying = false | ||
const r = this.plugin.get_award_location(this.connection, 'relaying') | ||
@@ -177,3 +194,3 @@ assert.equal(false, r) | ||
it('relaying=true', function (done) { | ||
this.connection.relaying=true | ||
this.connection.relaying = true | ||
const r = this.plugin.get_award_location(this.connection, 'relaying') | ||
@@ -225,3 +242,6 @@ assert.equal(true, r) | ||
this.connection.results.add('karma', { score: -1 }) | ||
const r = this.plugin.get_award_location(this.connection, 'transaction.results.karma') | ||
const r = this.plugin.get_award_location( | ||
this.connection, | ||
'transaction.results.karma', | ||
) | ||
// console.log(r); | ||
@@ -234,3 +254,6 @@ assert.equal(undefined, r) | ||
this.connection.results.add('auth/auth_base', { fail: 'PLAIN' }) | ||
const r = this.plugin.get_award_location(this.connection, 'results.auth/auth_base') | ||
const r = this.plugin.get_award_location( | ||
this.connection, | ||
'results.auth/auth_base', | ||
) | ||
assert.equal('PLAIN', r.fail[0]) | ||
@@ -244,8 +267,16 @@ done() | ||
it('geoip.distance', function (done) { | ||
assert.equal(4000, this.plugin.get_award_condition( | ||
'results.geoip.distance@4000', '-1 if gt' | ||
)) | ||
assert.equal(4000, this.plugin.get_award_condition( | ||
'results.geoip.distance@uniq', '-1 if gt 4000' | ||
)) | ||
assert.equal( | ||
4000, | ||
this.plugin.get_award_condition( | ||
'results.geoip.distance@4000', | ||
'-1 if gt', | ||
), | ||
) | ||
assert.equal( | ||
4000, | ||
this.plugin.get_award_condition( | ||
'results.geoip.distance@uniq', | ||
'-1 if gt 4000', | ||
), | ||
) | ||
done() | ||
@@ -255,5 +286,9 @@ }) | ||
it('auth/auth_base', function (done) { | ||
assert.equal('plain', this.plugin.get_award_condition( | ||
'results.auth/auth_base.fail@plain', '-1 if in' | ||
)) | ||
assert.equal( | ||
'plain', | ||
this.plugin.get_award_condition( | ||
'results.auth/auth_base.fail@plain', | ||
'-1 if in', | ||
), | ||
) | ||
done() | ||
@@ -273,3 +308,3 @@ }) | ||
it('no todo', function (done) { | ||
this.connection.results.add('karma', { todo: { } }) | ||
this.connection.results.add('karma', { todo: {} }) | ||
const r = this.plugin.check_awards(this.connection) | ||
@@ -281,6 +316,5 @@ assert.equal(undefined, r) | ||
it('geoip gt', function (done) { | ||
// populate the karma result with a todo item | ||
this.connection.results.add('karma', { | ||
todo: { 'results.geoip.distance@4000': '-1 if gt 4000' } | ||
todo: { 'results.geoip.distance@4000': '-1 if gt 4000' }, | ||
}) | ||
@@ -305,9 +339,11 @@ // test a non-matching criteria | ||
this.connection.results.add('karma', { | ||
todo: { 'results.auth/auth_base.fail@PLAIN': '-1 if in' } | ||
todo: { 'results.auth/auth_base.fail@PLAIN': '-1 if in' }, | ||
}) | ||
this.connection.results.add('auth/auth_base', | ||
{fail: 'PLAIN'}) | ||
this.connection.results.add('auth/auth_base', { fail: 'PLAIN' }) | ||
const r = this.plugin.check_awards(this.connection) | ||
assert.equal(undefined, r) | ||
assert.equal('auth/auth_base.fail', this.connection.results.get('karma').fail[0]) | ||
assert.equal( | ||
'auth/auth_base.fail', | ||
this.connection.results.get('karma').fail[0], | ||
) | ||
done() | ||
@@ -318,5 +354,5 @@ }) | ||
this.connection.results.add('karma', { | ||
todo: { 'results.rcpt_to.qmd.pass@exist': '1 if in' } | ||
todo: { 'results.rcpt_to.qmd.pass@exist': '1 if in' }, | ||
}) | ||
this.connection.results.add('rcpt_to.qmd', {pass: 'exist'}) | ||
this.connection.results.add('rcpt_to.qmd', { pass: 'exist' }) | ||
const r = this.plugin.check_awards(this.connection) | ||
@@ -449,3 +485,3 @@ assert.equal(undefined, r) | ||
this.plugin.cfg.tarpit = { max: 1, delay: 0 } | ||
this.plugin.deny_hooks = { connect: true} | ||
this.plugin.deny_hooks = { connect: true } | ||
this.connection.results.add(this.plugin, { score: -6 }) | ||
@@ -473,5 +509,8 @@ this.plugin.should_we_deny(next, this.connection, 'connect') | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'equals', value : 'clean', | ||
reason : 'testing', resolution : 'never', | ||
id: 1, | ||
award: 2, | ||
operator: 'equals', | ||
value: 'clean', | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
@@ -486,5 +525,8 @@ this.plugin.check_result_equal(['clean'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'equals', value : 'dirty', | ||
reason : 'testing', resolution : 'never', | ||
id: 1, | ||
award: 2, | ||
operator: 'equals', | ||
value: 'dirty', | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
@@ -502,5 +544,8 @@ this.plugin.check_result_equal(['clean'], award, this.connection) | ||
const award = { | ||
id : 5, award : 3, | ||
operator : 'gt', value : 3, | ||
reason : 'testing', resolution : 'never', | ||
id: 5, | ||
award: 3, | ||
operator: 'gt', | ||
value: 3, | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
@@ -520,5 +565,8 @@ this.plugin.check_result_gt([4], award, this.connection) | ||
const award = { | ||
id : 2, award : 3, | ||
operator : 'lt', value : 5, | ||
reason : 'testing', resolution : 'never', | ||
id: 2, | ||
award: 3, | ||
operator: 'lt', | ||
value: 5, | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
@@ -534,5 +582,8 @@ this.plugin.check_result_lt([4], award, this.connection) | ||
const award = { | ||
id : 3, award : 3, | ||
operator : 'lt', value : 3, | ||
reason : 'testing', resolution : 'never', | ||
id: 3, | ||
award: 3, | ||
operator: 'lt', | ||
value: 3, | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
@@ -551,5 +602,8 @@ this.plugin.check_result_lt([4], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'match', value : 'phish', | ||
reason : 'testing', resolution : 'never', | ||
id: 1, | ||
award: 2, | ||
operator: 'match', | ||
value: 'phish', | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
@@ -565,5 +619,8 @@ this.plugin.check_result_match(['isphishing'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'match', value : 'dirty', | ||
reason : 'testing', resolution : 'never', | ||
id: 1, | ||
award: 2, | ||
operator: 'match', | ||
value: 'dirty', | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
@@ -578,7 +635,14 @@ this.plugin.check_result_match(['clean'], award, this.connection) | ||
const award = { | ||
id : 89, award : 2, | ||
operator : 'match', value : 'google.com', | ||
reason : 'testing', resolution : 'never', | ||
id: 89, | ||
award: 2, | ||
operator: 'match', | ||
value: 'google.com', | ||
reason: 'testing', | ||
resolution: 'never', | ||
} | ||
this.plugin.check_result_match(['mail-yk0-f182.google.com'], award, this.connection) | ||
this.plugin.check_result_match( | ||
['mail-yk0-f182.google.com'], | ||
award, | ||
this.connection, | ||
) | ||
// console.log(this.connection.results.store); | ||
@@ -595,5 +659,8 @@ assert.equal(this.connection.results.store.karma.score, 2) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'length', value : 'eq 3', | ||
reason : 'testing', resolution : 'hah', | ||
id: 1, | ||
award: 2, | ||
operator: 'length', | ||
value: 'eq 3', | ||
reason: 'testing', | ||
resolution: 'hah', | ||
} | ||
@@ -609,5 +676,8 @@ this.plugin.check_result_length(['3'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'length', value : 'eq 3', | ||
reason : 'testing', resolution : 'hah', | ||
id: 1, | ||
award: 2, | ||
operator: 'length', | ||
value: 'eq 3', | ||
reason: 'testing', | ||
resolution: 'hah', | ||
} | ||
@@ -622,5 +692,8 @@ this.plugin.check_result_length(['4'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'length', value : 'gt 3', | ||
reason : 'testing', resolution : 'hah', | ||
id: 1, | ||
award: 2, | ||
operator: 'length', | ||
value: 'gt 3', | ||
reason: 'testing', | ||
resolution: 'hah', | ||
} | ||
@@ -636,5 +709,8 @@ this.plugin.check_result_length(['5'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'length', value : 'gt 3', | ||
reason : 'testing', resolution : 'hah', | ||
id: 1, | ||
award: 2, | ||
operator: 'length', | ||
value: 'gt 3', | ||
reason: 'testing', | ||
resolution: 'hah', | ||
} | ||
@@ -649,5 +725,8 @@ this.plugin.check_result_length(['3'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'length', value : 'lt 3', | ||
reason : 'testing', resolution : 'hah', | ||
id: 1, | ||
award: 2, | ||
operator: 'length', | ||
value: 'lt 3', | ||
reason: 'testing', | ||
resolution: 'hah', | ||
} | ||
@@ -663,5 +742,8 @@ this.plugin.check_result_length(['2'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'length', value : 'lt 3', | ||
reason : 'testing', resolution : 'hah', | ||
id: 1, | ||
award: 2, | ||
operator: 'length', | ||
value: 'lt 3', | ||
reason: 'testing', | ||
resolution: 'hah', | ||
} | ||
@@ -680,5 +762,8 @@ this.plugin.check_result_length(['3'], award, this.connection) | ||
const award = { | ||
id : 1, award : 2, | ||
operator : 'exists', value : 'any', | ||
reason : 'testing', resolution : 'high five', | ||
id: 1, | ||
award: 2, | ||
operator: 'exists', | ||
value: 'any', | ||
reason: 'testing', | ||
resolution: 'high five', | ||
} | ||
@@ -694,5 +779,8 @@ this.plugin.check_result_exists(['3'], award, this.connection) | ||
const award = { | ||
id : 1, award : 3, | ||
operator : 'exists', value : '', | ||
reason : 'testing', resolution : 'misses', | ||
id: 1, | ||
award: 3, | ||
operator: 'exists', | ||
value: '', | ||
reason: 'testing', | ||
resolution: 'misses', | ||
} | ||
@@ -715,5 +803,7 @@ this.plugin.check_result_exists([], award, this.connection) | ||
this.plugin.preparse_result_awards() | ||
this.connection.results.add({name: 'geoip'}, {country: 'CN'}) | ||
this.plugin.check_result(this.connection, | ||
'{"plugin":"geoip","result":{"country":"CN"}}') | ||
this.connection.results.add({ name: 'geoip' }, { country: 'CN' }) | ||
this.plugin.check_result( | ||
this.connection, | ||
'{"plugin":"geoip","result":{"country":"CN"}}', | ||
) | ||
// console.log(this.connection.results.store); | ||
@@ -730,5 +820,7 @@ assert.equal(this.connection.results.store.karma.score, 2) | ||
this.plugin.preparse_result_awards() | ||
this.connection.results.add({name: 'dnsbl'}, {fail: 'dnsbl.sorbs.net'}) | ||
this.plugin.check_result(this.connection, | ||
'{"plugin":"dnsbl","result":{"fail":"dnsbl.sorbs.net"}}') | ||
this.connection.results.add({ name: 'dnsbl' }, { fail: 'dnsbl.sorbs.net' }) | ||
this.plugin.check_result( | ||
this.connection, | ||
'{"plugin":"dnsbl","result":{"fail":"dnsbl.sorbs.net"}}', | ||
) | ||
// console.log(this.connection.results.store); | ||
@@ -769,9 +861,13 @@ assert.equal(this.connection.results.store.karma.score, -5) | ||
it('unconfigured TLS does nothing', function (done) { | ||
this.connection.tls.enabled=true | ||
this.connection.tls.enabled = true | ||
const mfrom = new Address('spamy@er7diogt.rrnsale.top') | ||
this.connection.current_line="MAIL FROM:<foo@test.com>" | ||
this.plugin.hook_mail(() => { | ||
assert.equal(this.connection.results.store.karma, undefined) | ||
done() | ||
}, this.connection, [mfrom]) | ||
this.connection.current_line = 'MAIL FROM:<foo@test.com>' | ||
this.plugin.hook_mail( | ||
() => { | ||
assert.equal(this.connection.results.store.karma, undefined) | ||
done() | ||
}, | ||
this.connection, | ||
[mfrom], | ||
) | ||
}) | ||
@@ -781,10 +877,14 @@ | ||
this.plugin.cfg.tls = { set: 2, unset: -4 } | ||
this.connection.tls.enabled=true | ||
this.connection.tls.enabled = true | ||
const mfrom = new Address('spamy@er7diogt.rrnsale.top') | ||
this.connection.current_line="MAIL FROM:<foo@test.com>" | ||
this.plugin.hook_mail(() => { | ||
// console.log(this.connection.results.store); | ||
assert.equal(this.connection.results.store.karma.score, 2) | ||
done() | ||
}, this.connection, [mfrom]) | ||
this.connection.current_line = 'MAIL FROM:<foo@test.com>' | ||
this.plugin.hook_mail( | ||
() => { | ||
// console.log(this.connection.results.store); | ||
assert.equal(this.connection.results.store.karma.score, 2) | ||
done() | ||
}, | ||
this.connection, | ||
[mfrom], | ||
) | ||
}) | ||
@@ -794,10 +894,14 @@ | ||
this.plugin.cfg.tls = { set: 2, unset: -4 } | ||
this.connection.tls.enabled=false | ||
this.connection.tls.enabled = false | ||
const mfrom = new Address('spamy@er7diogt.rrnsale.top') | ||
this.connection.current_line="MAIL FROM:<foo@test.com>" | ||
this.plugin.hook_mail(() => { | ||
// console.log(this.connection.results.store); | ||
assert.equal(this.connection.results.store.karma.score, -4) | ||
done() | ||
}, this.connection, [mfrom]) | ||
this.connection.current_line = 'MAIL FROM:<foo@test.com>' | ||
this.plugin.hook_mail( | ||
() => { | ||
// console.log(this.connection.results.store); | ||
assert.equal(this.connection.results.store.karma.score, -4) | ||
done() | ||
}, | ||
this.connection, | ||
[mfrom], | ||
) | ||
}) | ||
@@ -810,6 +914,6 @@ }) | ||
it('notes.disable_karma', function (done) { | ||
function next (rc) { | ||
function next(rc) { | ||
assert.equal(undefined, rc) | ||
} | ||
function last (rc) { | ||
function last(rc) { | ||
assert.equal(undefined, rc) | ||
@@ -832,6 +936,6 @@ done() | ||
it('private skip', function (done) { | ||
function next (rc) { | ||
function next(rc) { | ||
assert.equal(undefined, rc) | ||
} | ||
function last (rc) { | ||
function last(rc) { | ||
assert.equal(undefined, rc) | ||
@@ -838,0 +942,0 @@ done() |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2
1659
88341
7
208
Updatedaddress-rfc2821@^2.1.2
Updatedharaka-constants@^1.0.6
Updatedharaka-utils@^1.1.1
Updatedredis@^4.6.13