haraka-plugin-rspamd
Advanced tools
Comparing version 1.2.0 to 1.3.0
### Unreleased | ||
### [1.3.0] - 2023-02-23 | ||
- add: defer options, similar to spamassassin.js #32 | ||
- es6: replace Object.keys().forEach with for...of | ||
- fix: wrap milter header adds in try/catch, fixes #28 | ||
### [1.2.0] - 2022-10-14 | ||
@@ -68,1 +75,3 @@ | ||
[1.1.9]: https://github.com/haraka/haraka-plugin-rspamd/releases/tag/1.1.9 | ||
[1.2.0]: https://github.com/haraka/haraka-plugin-rspamd/releases/tag/1.2.0 | ||
[1.3.0]: https://github.com/haraka/haraka-plugin-rspamd/releases/tag/1.3.0 |
188
index.js
@@ -29,2 +29,4 @@ 'use strict'; | ||
'+smtp_message.enabled', | ||
'-defer.error', | ||
'-defer.timeout', | ||
], | ||
@@ -35,28 +37,28 @@ }, () => { | ||
if (!plugin.cfg.reject.message) { | ||
plugin.cfg.reject.message = 'Detected as spam'; | ||
if (!this.cfg.reject.message) { | ||
this.cfg.reject.message = 'Detected as spam'; | ||
} | ||
if (!plugin.cfg.soft_reject.message) { | ||
plugin.cfg.soft_reject.message = 'Deferred by policy'; | ||
if (!this.cfg.soft_reject.message) { | ||
this.cfg.soft_reject.message = 'Deferred by policy'; | ||
} | ||
if (!plugin.cfg.spambar) { | ||
plugin.cfg.spambar = { positive: '+', negative: '-', neutral: '/' }; | ||
if (!this.cfg.spambar) { | ||
this.cfg.spambar = { positive: '+', negative: '-', neutral: '/' }; | ||
} | ||
if (!plugin.cfg.main.port) plugin.cfg.main.port = 11333; | ||
if (!plugin.cfg.main.host) plugin.cfg.main.host = 'localhost'; | ||
if (!this.cfg.main.port) this.cfg.main.port = 11333; | ||
if (!this.cfg.main.host) this.cfg.main.host = 'localhost'; | ||
if (!plugin.cfg.main.add_headers) { | ||
if (plugin.cfg.main.always_add_headers === true) { | ||
plugin.cfg.main.add_headers = 'always'; | ||
if (!this.cfg.main.add_headers) { | ||
if (this.cfg.main.always_add_headers === true) { | ||
this.cfg.main.add_headers = 'always'; | ||
} | ||
else { | ||
plugin.cfg.main.add_headers = 'sometimes'; | ||
this.cfg.main.add_headers = 'sometimes'; | ||
} | ||
} | ||
if (!plugin.cfg.subject) { | ||
plugin.cfg.subject = "[SPAM] %s"; | ||
if (!this.cfg.subject) { | ||
this.cfg.subject = "[SPAM] %s"; | ||
} | ||
@@ -66,3 +68,2 @@ } | ||
exports.get_options = function (connection) { | ||
const plugin = this; | ||
@@ -77,8 +78,8 @@ // https://rspamd.com/doc/architecture/protocol.html | ||
if (plugin.cfg.main.unix_socket) { | ||
options.socketPath = plugin.cfg.main.unix_socket; | ||
if (this.cfg.main.unix_socket) { | ||
options.socketPath = this.cfg.main.unix_socket; | ||
} | ||
else { | ||
options.port = plugin.cfg.main.port; | ||
options.host = plugin.cfg.main.host; | ||
options.port = this.cfg.main.port; | ||
options.host = this.cfg.main.host; | ||
} | ||
@@ -125,4 +126,4 @@ | ||
options.headers.Rcpt = []; | ||
for (let i=0; i < rcpts.length; i++) { | ||
options.headers.Rcpt.push(rcpts[i].address()); | ||
for (const rcpt of rcpts) { | ||
options.headers.Rcpt.push(rcpt.address()); | ||
} | ||
@@ -148,5 +149,4 @@ | ||
exports.get_smtp_message = function (r) { | ||
const plugin = this; | ||
if (!plugin.cfg.smtp_message.enabled || !r.data.messages) return; | ||
if (!this.cfg.smtp_message.enabled || !r.data.messages) return; | ||
if (typeof(r.data.messages) !== 'object') return; | ||
@@ -159,8 +159,7 @@ if (!r.data.messages.smtp_message) return; | ||
exports.do_rewrite = function (connection, data) { | ||
const plugin = this; | ||
if (!plugin.cfg.rewrite_subject.enabled) return false; | ||
if (!this.cfg.rewrite_subject.enabled) return false; | ||
if (data.action !== 'rewrite subject') return false; | ||
const rspamd_subject = data.subject || plugin.cfg.subject; | ||
const rspamd_subject = data.subject || this.cfg.subject; | ||
const old_subject = connection.transaction.header.get('Subject') || ''; | ||
@@ -174,5 +173,4 @@ const new_subject = rspamd_subject.replace('%s', old_subject); | ||
exports.add_dkim_header = function (connection, data) { | ||
const plugin = this; | ||
if (!plugin.cfg.dkim.enabled) return; | ||
if (!this.cfg.dkim.enabled) return; | ||
if (!data['dkim-signature']) return; | ||
@@ -184,36 +182,40 @@ | ||
exports.do_milter_headers = function (connection, data) { | ||
const plugin = this; | ||
if (!plugin.cfg.rmilter_headers.enabled) return; | ||
if (!this.cfg.rmilter_headers.enabled) return; | ||
if (!data.milter) return; | ||
if (data.milter.remove_headers) { | ||
Object.keys(data.milter.remove_headers).forEach((key) => { | ||
for (const key of Object.keys(data.milter.remove_headers)) { | ||
connection.transaction.remove_header(key); | ||
}) | ||
} | ||
} | ||
if (data.milter.add_headers) { | ||
connection.logdebug(`milter.add_headers: ${JSON.stringify(data.milter.add_headers)}`, plugin); | ||
Object.keys(data.milter.add_headers).forEach((key) => { | ||
const header_values = data.milter.add_headers[key]; | ||
if (!header_values) return; | ||
try { | ||
connection.logdebug(this, `milter.add_headers: ${JSON.stringify(data.milter.add_headers)}`); | ||
for (const key of Object.keys(data.milter.add_headers)) { | ||
const header_values = data.milter.add_headers[key]; | ||
if (!header_values) return; | ||
if (Object.prototype.toString.call(header_values) == '[object Array]') { | ||
header_values.forEach(function (header_value, header_index) { | ||
if (typeof header_value === 'object') { | ||
connection.transaction.add_header(key, header_value.value); | ||
} | ||
else { | ||
connection.transaction.add_header(key, header_value); | ||
} | ||
}); | ||
if (Object.prototype.toString.call(header_values) == '[object Array]') { | ||
header_values.forEach(function (header_value, header_index) { | ||
if (typeof header_value === 'object') { | ||
connection.transaction.add_header(key, header_value.value); | ||
} | ||
else { | ||
connection.transaction.add_header(key, header_value); | ||
} | ||
}); | ||
} | ||
else if (typeof header_values === 'object') { | ||
connection.transaction.add_header(key, header_values.value); | ||
} | ||
else { | ||
connection.transaction.add_header(key, header_values); | ||
} | ||
} | ||
else if (typeof header_values === 'object') { | ||
connection.transaction.add_header(key, header_values.value); | ||
} | ||
else { | ||
connection.transaction.add_header(key, header_values); | ||
} | ||
}) | ||
} | ||
catch (err) { | ||
connection.errorlog(this, `milter.addheaders error: ${err}`) | ||
} | ||
} | ||
@@ -236,2 +238,3 @@ } | ||
calledNext=true; | ||
if (!connection?.transaction) return; | ||
next(code, msg); | ||
@@ -241,5 +244,5 @@ } | ||
timer = setTimeout(() => { | ||
if (!connection) return; | ||
if (!connection.transaction) return; | ||
if (!connection?.transaction) return; | ||
connection.transaction.results.add(plugin, {err: 'timeout'}); | ||
if (plugin.cfg.defer.timeout) return nextOnce(DENYSOFT, 'Rspamd scan timeout'); | ||
nextOnce(); | ||
@@ -256,4 +259,9 @@ }, timeout * 1000); | ||
res.on('end', () => { | ||
if (!connection.transaction) return nextOnce(); //client gone | ||
const r = plugin.parse_response(rawData, connection); | ||
if (!r || !r.data || !r.log) return nextOnce(); | ||
if (!r || !r.data || !r.log) { | ||
if (plugin.cfg.defer.error) return nextOnce(DENYSOFT, 'Rspamd scan error'); | ||
return nextOnce(); | ||
} | ||
@@ -263,4 +271,2 @@ r.log.emit = true; // spit out a log entry | ||
if (!connection.transaction) return nextOnce(); | ||
connection.transaction.results.add(plugin, r.log); | ||
@@ -290,4 +296,5 @@ if (r.data.symbols) connection.transaction.results.add(plugin, { symbols: r.data.symbols }); | ||
req.on('error', (err) => { | ||
if (!connection || !connection.transaction) return; | ||
if (!connection?.transaction) return nextOnce(); // client gone | ||
connection.transaction.results.add(plugin, { err: err.message}); | ||
if (plugin.cfg.defer.error) return nextOnce(DENYSOFT, 'Rspamd scan error'); | ||
nextOnce(); | ||
@@ -301,27 +308,26 @@ }); | ||
exports.should_check = function (connection) { | ||
const plugin = this; | ||
let result = true; // default | ||
if (plugin.cfg.check.authenticated == false && connection.notes.auth_user) { | ||
connection.transaction.results.add(plugin, { skip: 'authed'}); | ||
if (this.cfg.check.authenticated == false && connection.notes.auth_user) { | ||
connection.transaction.results.add(this, { skip: 'authed'}); | ||
result = false; | ||
} | ||
if (plugin.cfg.check.relay == false && connection.relaying) { | ||
connection.transaction.results.add(plugin, { skip: 'relay'}); | ||
if (this.cfg.check.relay == false && connection.relaying) { | ||
connection.transaction.results.add(this, { skip: 'relay'}); | ||
result = false; | ||
} | ||
if (plugin.cfg.check.local_ip == false && connection.remote.is_local) { | ||
connection.transaction.results.add(plugin, { skip: 'local_ip'}); | ||
if (this.cfg.check.local_ip == false && connection.remote.is_local) { | ||
connection.transaction.results.add(this, { skip: 'local_ip'}); | ||
result = false; | ||
} | ||
if (plugin.cfg.check.private_ip == false && connection.remote.is_private) { | ||
if (plugin.cfg.check.local_ip == true && connection.remote.is_local) { | ||
if (this.cfg.check.private_ip == false && connection.remote.is_private) { | ||
if (this.cfg.check.local_ip == true && connection.remote.is_local) { | ||
// local IPs are included in private IPs | ||
} | ||
else { | ||
connection.transaction.results.add(plugin, { skip: 'private_ip'}); | ||
connection.transaction.results.add(this, { skip: 'private_ip'}); | ||
result = false; | ||
@@ -335,3 +341,2 @@ } | ||
exports.wants_reject = function (connection, data) { | ||
const plugin = this; | ||
@@ -341,6 +346,6 @@ if (data.action !== 'reject') return false; | ||
if (connection.notes.auth_user) { | ||
if (plugin.cfg.reject.authenticated == false) return false; | ||
if (this.cfg.reject.authenticated == false) return false; | ||
} | ||
else { | ||
if (plugin.cfg.reject.spam == false) return false; | ||
if (this.cfg.reject.spam == false) return false; | ||
} | ||
@@ -352,6 +357,5 @@ | ||
exports.wants_headers_added = function (rspamd_data) { | ||
const plugin = this; | ||
if (plugin.cfg.main.add_headers === 'never') return false; | ||
if (plugin.cfg.main.add_headers === 'always') return true; | ||
if (this.cfg.main.add_headers === 'never') return false; | ||
if (this.cfg.main.add_headers === 'always') return true; | ||
@@ -364,3 +368,2 @@ // implicit add_headers=sometimes, based on rspamd response | ||
exports.get_clean = function (data, connection) { | ||
const plugin = this; | ||
const clean = { symbols: {} }; | ||
@@ -377,3 +380,3 @@ | ||
// unhandled type | ||
connection.logerror(plugin, a); | ||
connection.logerror(this, a); | ||
}) | ||
@@ -383,3 +386,4 @@ } | ||
// objects that may exist | ||
['action', 'is_skipped', 'required_score', 'score'].forEach((key) => { | ||
const skip_keys = ['action', 'is_skipped', 'required_score', 'score']; | ||
for (const key of skip_keys) { | ||
switch (typeof data[key]) { | ||
@@ -392,8 +396,9 @@ case 'boolean': | ||
default: | ||
connection.loginfo(plugin, `skipping unhandled: ${ typeof data[key]}`); | ||
connection.loginfo(this, `skipping unhandled: ${typeof data[key]}`); | ||
} | ||
}); | ||
} | ||
// arrays which might be present | ||
['urls', 'emails', 'messages'].forEach(b => { | ||
const arrays = ['urls', 'emails', 'messages']; | ||
for (const b of arrays) { | ||
// collapse to comma separated string, so values get logged | ||
@@ -413,3 +418,3 @@ if (!data[b]) return; | ||
} | ||
}); | ||
} | ||
@@ -420,4 +425,2 @@ return clean; | ||
exports.parse_response = function (rawData, connection) { | ||
const plugin = this; | ||
if (!rawData) return; | ||
@@ -430,4 +433,4 @@ | ||
catch (err) { | ||
connection.transaction.results.add(plugin, { | ||
err: `parse failure: ${ err.message}` | ||
connection.transaction.results.add(this, { | ||
err: `parse failure: ${err.message}` | ||
}); | ||
@@ -440,5 +443,3 @@ return; | ||
if (Object.keys(data).length === 1 && data.error) { | ||
connection.transaction.results.add(plugin, { | ||
err: data.error | ||
}); | ||
connection.transaction.results.add(this, { err: data.error }); | ||
return; | ||
@@ -449,3 +450,3 @@ } | ||
data, | ||
'log' : plugin.get_clean(data, connection), | ||
'log' : this.get_clean(data, connection), | ||
}; | ||
@@ -455,6 +456,5 @@ } | ||
exports.add_headers = function (connection, data) { | ||
const plugin = this; | ||
const cfg = plugin.cfg; | ||
const cfg = this.cfg; | ||
if (!plugin.wants_headers_added(data)) return; | ||
if (!this.wants_headers_added(data)) return; | ||
@@ -484,3 +484,3 @@ if (cfg.header && cfg.header.bar) { | ||
if (data.symbols[k].score) { | ||
prettySymbols.push(`${data.symbols[k].name }(${ data.symbols[k].score })`); | ||
prettySymbols.push(`${data.symbols[k].name}(${data.symbols[k].score})`); | ||
} | ||
@@ -495,4 +495,4 @@ } | ||
connection.transaction.remove_header(cfg.header.score); | ||
connection.transaction.add_header(cfg.header.score, `${ data.score}`); | ||
connection.transaction.add_header(cfg.header.score, `${data.score}`); | ||
} | ||
} |
{ | ||
"name": "haraka-plugin-rspamd", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Haraka plugin for rspamd", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Sorry, the diff of this file is not supported yet
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
30574
587