haraka-plugin-rspamd
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -0,4 +1,9 @@ | ||
# 1.1.0 - 2018-01-12 | ||
- use /checkv2 endpoint (requires rspamd 1.6+) | ||
- support setting SMTP message from rspamd | ||
- support 'rewrite subject' action | ||
# 1.0.0 - 201_-__-__ | ||
- initial release |
107
index.js
@@ -23,4 +23,6 @@ 'use strict'; | ||
'-reject.authenticated', | ||
'+rewrite_subject.enabled', | ||
'+rmilter_headers.enabled', | ||
'+soft_reject.enabled', | ||
'+smtp_message.enabled', | ||
], | ||
@@ -53,2 +55,6 @@ }, function () { | ||
} | ||
if (!plugin.cfg.subject) { | ||
plugin.cfg.subject = "[SPAM] %s"; | ||
} | ||
}; | ||
@@ -65,3 +71,3 @@ | ||
host: plugin.cfg.main.host, | ||
path: '/check', | ||
path: '/checkv2', | ||
method: 'POST', | ||
@@ -76,3 +82,3 @@ }; | ||
const fcrdns = connection.results.get('connect.fcrdns'); | ||
const fcrdns = connection.results.get('fcrdns'); | ||
if (fcrdns && fcrdns.fcrdns && fcrdns.fcrdns[0]) { | ||
@@ -168,3 +174,2 @@ options.headers.Hostname = fcrdns.fcrdns[0]; | ||
if (!r.data) return callNext(); | ||
if (!r.data.default) return callNext(); | ||
if (!r.log) return callNext(); | ||
@@ -178,2 +183,8 @@ | ||
let smtp_message; | ||
if (cfg.smtp_message.enabled && r.data.messages && | ||
typeof(r.data.messages) == 'object' && r.data.messages.smtp_message) { | ||
smtp_message = r.data.messages.smtp_message; | ||
} | ||
function no_reject () { | ||
@@ -183,17 +194,14 @@ if (cfg.dkim.enabled && r.data['dkim-signature']) { | ||
} | ||
if (cfg.rmilter_headers.enabled && r.rmilter) { | ||
if (r.rmilter.remove_headers) { | ||
Object.keys(r.rmilter.remove_headers).forEach(function (key) { | ||
if (cfg.rmilter_headers.enabled && r.data.milter) { | ||
if (r.data.milter.remove_headers) { | ||
Object.keys(r.data.milter.remove_headers).forEach(function (key) { | ||
connection.transaction.remove_header(key); | ||
}) | ||
} | ||
if (r.rmilter.add_headers) { | ||
Object.keys(r.rmilter.add_headers).forEach(function (key) { | ||
connection.transaction.add_header(key, r.rmilter.add_headers[key]); | ||
if (r.data.milter.add_headers) { | ||
Object.keys(r.data.milter.add_headers).forEach(function (key) { | ||
connection.transaction.add_header(key, r.data.milter.add_headers[key]); | ||
}) | ||
} | ||
} | ||
if (cfg.soft_reject.enabled && r.data.default.action === 'soft reject') { | ||
return callNext(DENYSOFT, DSN.sec_unauthorized(cfg.soft_reject.message, 451)); | ||
} | ||
if (plugin.wants_headers_added(r.data)) { | ||
@@ -205,7 +213,19 @@ plugin.add_headers(connection, r.data); | ||
if (r.data.default.action !== 'reject') return no_reject(); | ||
if (cfg.rewrite_subject.enabled && r.data.action === 'rewrite subject') { | ||
const rspamd_subject = r.data.subject || cfg.subject; | ||
const old_subject = connection.transaction.header.get('Subject') || ''; | ||
const new_subject = rspamd_subject.replace('%s', old_subject); | ||
connection.transaction.remove_header('Subject'); | ||
connection.transaction.add_header('Subject', new_subject); | ||
} | ||
if (cfg.soft_reject.enabled && r.data.action === 'soft reject') { | ||
return callNext(DENYSOFT, DSN.sec_unauthorized(smtp_message || cfg.soft_reject.message, 451)); | ||
} | ||
if (r.data.action !== 'reject') return no_reject(); | ||
if (!authed && !cfg.reject.spam) return no_reject(); | ||
if (authed && !cfg.reject.authenticated) return no_reject(); | ||
return callNext(DENY, cfg.reject.message); | ||
return callNext(DENY, smtp_message || cfg.reject.message); | ||
}); | ||
@@ -229,3 +249,3 @@ }) | ||
// implicit add_headers=sometimes, based on rspamd response | ||
if (rspamd_data.default.action === 'add header') return true; | ||
if (rspamd_data.action === 'add header') return true; | ||
return false; | ||
@@ -255,16 +275,18 @@ }; | ||
// copy those nested objects into a higher level object | ||
const dataClean = {}; | ||
Object.keys(data.default).forEach(function (key) { | ||
const a = data.default[key]; | ||
// make cleaned data for logs | ||
const dataClean = {symbols: {}}; | ||
Object.keys(data.symbols).forEach(function (key) { | ||
const a = data.symbols[key]; | ||
// transform { name: KEY, score: VAL } -> { KEY: VAL } | ||
if (a.name && a.score !== undefined) { | ||
dataClean.symbols[ a.name ] = a.score; | ||
} else { | ||
// unhandled type | ||
connection.logerror(plugin, a); | ||
} | ||
}); | ||
const wantKeys = ["action", "is_skipped", "required_score", "score"]; | ||
wantKeys.forEach(function (key) { | ||
const a = data[key]; | ||
switch (typeof a) { | ||
case 'object': | ||
// transform { name: KEY, score: VAL } -> { KEY: VAL } | ||
if (a.name && a.score !== undefined) { | ||
dataClean[ a.name ] = a.score; | ||
break; | ||
} | ||
// unhandled type | ||
connection.logerror(plugin, a); | ||
break; | ||
case 'boolean': | ||
@@ -283,3 +305,12 @@ case 'number': | ||
// collapse to comma separated string, so values get logged | ||
if (data[b] && data[b].length) dataClean[b] = data[b].join(','); | ||
if (data[b]) { | ||
if (data[b].length) { | ||
dataClean[b] = data[b].join(','); | ||
} else if (typeof(data[b]) == 'object') { | ||
// 'messages' is probably a dictionary | ||
Object.keys(data[b]).map(function (k) { | ||
return k + " : " + data[b][k]; | ||
}).join(','); | ||
} | ||
} | ||
}); | ||
@@ -301,8 +332,8 @@ | ||
let spamBarChar = cfg.spambar.neutral || '/'; | ||
if (data.default.score >= 1) { | ||
spamBarScore = Math.floor(data.default.score); | ||
if (data.score >= 1) { | ||
spamBarScore = Math.floor(data.score); | ||
spamBarChar = cfg.spambar.positive || '+'; | ||
} | ||
else if (data.default.score <= -1) { | ||
spamBarScore = Math.floor(data.default.score * -1); | ||
else if (data.score <= -1) { | ||
spamBarScore = Math.floor(data.score * -1); | ||
spamBarChar = cfg.spambar.negative || '-'; | ||
@@ -319,6 +350,6 @@ } | ||
const prettySymbols = []; | ||
for (const k in data.default) { | ||
if (data.default[k].score) { | ||
prettySymbols.push(data.default[k].name + | ||
'(' + data.default[k].score + ')'); | ||
for (const k in data.symbols) { | ||
if (data.symbols[k].score) { | ||
prettySymbols.push(data.symbols[k].name + | ||
'(' + data.symbols[k].score + ')'); | ||
} | ||
@@ -333,4 +364,4 @@ } | ||
connection.transaction.remove_header(cfg.header.score); | ||
connection.transaction.add_header(cfg.header.score, '' + data.default.score); | ||
connection.transaction.add_header(cfg.header.score, '' + data.score); | ||
} | ||
}; |
{ | ||
"name": "haraka-plugin-rspamd", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Haraka plugin for rspamd", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -34,3 +34,3 @@ [![Build Status][ci-img]][ci-url] | ||
"always" - always add headers | ||
"never" - never add headers (unless provided by rspamd - see rmilter\_headers) | ||
"never" - never add headers (unless provided by rspamd - see rmilter_headers) | ||
"sometimes" - add headers when rspamd recommends `add header` action | ||
@@ -97,8 +97,20 @@ | ||
- rmilter_headers.enabled | ||
- rewrite\_subject.enabled | ||
Default: true | ||
If set to true, allow rspamd to add/remove headers to messages via [task:rmilter_set_reply()](https://rspamd.com/doc/lua/task.html#me7351). | ||
If set to true, "rewrite subject" action is honored. | ||
- rmilter\_headers.enabled | ||
Default: true | ||
If set to true, allow rspamd to add/remove headers to messages via [task:set_milter_reply()](https://rspamd.com/doc/lua/task.html#m70081). | ||
- smtp\_message.enabled | ||
Default: true | ||
If set to true, "smtp_message" provided by Rspamd is used in response for "reject" & "soft reject" actions. | ||
- soft\_reject.enabled | ||
@@ -134,2 +146,8 @@ | ||
- subject | ||
Default: [SPAM] %s | ||
Subject to use for `rewrite subject` action if Rspamd does not provide one. | ||
- timeout (in seconds) | ||
@@ -136,0 +154,0 @@ |
@@ -57,4 +57,4 @@ 'use strict'; | ||
const test_data = { | ||
default: { | ||
score: 1.1, | ||
score: 1.1, | ||
symbols: { | ||
FOO: { | ||
@@ -82,5 +82,3 @@ name: 'FOO', | ||
const test_data = { | ||
default: { | ||
score: -1, | ||
} | ||
score: -1 | ||
}; | ||
@@ -101,3 +99,3 @@ this.plugin.add_headers(this.connection, test_data); | ||
test.equal( | ||
this.plugin.wants_headers_added({ default: { action: 'add header' }}), | ||
this.plugin.wants_headers_added({ action: 'add header' }), | ||
false | ||
@@ -111,3 +109,3 @@ ); | ||
test.equal( | ||
this.plugin.wants_headers_added({ default: { action: 'beat it' }}), | ||
this.plugin.wants_headers_added({ action: 'beat it' }), | ||
true | ||
@@ -121,7 +119,7 @@ ); | ||
test.equal( | ||
this.plugin.wants_headers_added({ default: { action: 'add header' }}), | ||
this.plugin.wants_headers_added({ action: 'add header' }), | ||
true | ||
); | ||
test.equal( | ||
this.plugin.wants_headers_added({ default: { action: 'brownlist' }}), | ||
this.plugin.wants_headers_added({ action: 'brownlist' }), | ||
false | ||
@@ -128,0 +126,0 @@ ); |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
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
24993
455
170
2