local rspamd_text = require "rspamd_text"
local rspamd_http = require "rspamd_http"
local rspamd_util = require "rspamd_util"
+local PluginSchema = require "lua_shape.plugin_schema"
local settings = {
s3_bucket = nil,
inline_content_limit = T.number():optional():doc({ summary = "Max inline content size before external ref" }),
}):doc({ summary = "AWS S3 plugin configuration" })
+PluginSchema.register("plugins.aws_s3", settings_schema)
+
local function raw_data(task, nonce, queue_id)
local ext, content, content_type
local rspamd_logger = require "rspamd_logger"
local T = require "lua_shape.core"
local lua_redis = require "lua_redis"
+local PluginSchema = require "lua_shape.plugin_schema"
local ucl = require "ucl"
local lua_mime = require "lua_mime"
local rspamd_http = require "rspamd_http"
}):optional():doc({ summary = "Helper read timeout (seconds)" }),
})
+PluginSchema.register("plugins.bimi", settings_schema)
+
local function check_dmarc_policy(task)
local dmarc_sym = task:get_symbol('DMARC_POLICY_ALLOW')
limitations under the License.
]]--
-if confighelp then
- return
-end
-
-- Plugin for finding patterns in email flows
local N = 'clustering'
local lua_redis = require "lua_redis"
local lua_selectors = require "lua_selectors"
local T = require "lua_shape.core"
+local PluginSchema = require "lua_shape.plugin_schema"
local redis_params
prefix = T.string():optional():doc({ summary = "Redis key prefix" }),
}):doc({ summary = "Clustering rule configuration" })
+local config_schema = lua_redis.enrich_schema({
+ enabled = T.boolean():optional():doc({ summary = "Enable the plugin" }),
+ rules = T.table({}, {
+ open = true,
+ extra = rule_schema
+ }):doc({ summary = "Clustering rules keyed by name" })
+}):doc({ summary = "Clustering plugin configuration" })
+
+PluginSchema.register("plugins.clustering", config_schema)
+
+if confighelp then
+ return
+end
+
-- Redis scripts
-- Queries for a cluster's data
)
end
-- Init part
-redis_params = lua_redis.parse_redis_server('clustering')
local opts = rspamd_config:get_all_opt("clustering")
-- Initialization part
return
end
+local cfg, cfg_err = config_schema:transform(opts)
+if not cfg then
+ rspamd_logger.errx(rspamd_config, 'invalid clustering config: %s', cfg_err)
+ lua_util.disable_module(N, "config")
+ return
+end
+
+opts = cfg
+
+redis_params = lua_redis.parse_redis_server('clustering', opts)
+
if not redis_params then
lua_util.disable_module(N, "redis")
return
local E = {}
local N = 'contextal'
-if confighelp then
- return
-end
-
-local opts = rspamd_config:get_all_opt(N)
-if not opts then
- return
-end
-
local lua_redis = require "lua_redis"
local lua_util = require "lua_util"
local redis_cache = require "lua_cache"
local rspamd_util = require "rspamd_util"
local T = require "lua_shape.core"
local ucl = require "ucl"
+local PluginSchema = require "lua_shape.plugin_schema"
local cache_context, redis_params
read_timeout = T.number():optional():doc({ summary = "Read timeout (seconds)" }),
}
+PluginSchema.register("plugins.contextal", config_schema)
+
+if confighelp then
+ return
+end
+
+local opts = rspamd_config:get_all_opt(N)
+if not opts then
+ return
+end
+
local settings = {
action_symbol_prefix = 'CONTEXTAL_ACTION',
base_url = 'http://localhost:8080',
external_relay plugin - sets IP/hostname from Received headers
]]--
-if confighelp then
- return
-end
-
local lua_maps = require "lua_maps"
local lua_util = require "lua_util"
local rspamd_logger = require "rspamd_logger"
local T = require "lua_shape.core"
+local PluginSchema = require "lua_shape.plugin_schema"
local E = {}
local N = "external_relay"
}):doc({ summary = "External relay rules keyed by name" }),
}):doc({ summary = "External relay plugin configuration" })
+PluginSchema.register("plugins.external_relay", config_schema)
+
+if confighelp then
+ return
+end
+
local function set_from_rcvd(task, rcvd)
local rcvd_ip = rcvd.real_ip
if not (rcvd_ip and rcvd_ip:is_valid()) then
limitations under the License.
]]--
-if confighelp then
- rspamd_config:add_example(nil, 'history_redis',
- "Store history of checks for WebUI using Redis",
- [[
-redis_history {
- # History key name
- key_prefix = 'rs_history{{HOSTNAME}}{{COMPRESS}}';
- # Expire in seconds for inactive keys, default to 5 days
- expire = 432000;
- # History rows limit
- nrows = 200;
- # Use zstd compression when storing data in redis
- compress = true;
- # Obfuscate subjects for privacy
- subject_privacy = false;
- # Default hash-algorithm to obfuscate subject
- subject_privacy_alg = 'blake2';
- # Prefix to show it's obfuscated
- subject_privacy_prefix = 'obf';
- # Cut the length of the hash if desired
- subject_privacy_length = 16;
-}
- ]])
- return
-end
-
local rspamd_logger = require "rspamd_logger"
local rspamd_util = require "rspamd_util"
local lua_util = require "lua_util"
local fun = require "fun"
local ucl = require "ucl"
local T = require "lua_shape.core"
+local PluginSchema = require "lua_shape.plugin_schema"
local E = {}
local N = "history_redis"
subject_privacy_length = T.number():optional():doc({ summary = "Hash length for obfuscated subjects" }),
})
+PluginSchema.register("plugins.history_redis", settings_schema)
+
+if confighelp then
+ rspamd_config:add_example(nil, 'history_redis',
+ "Store history of checks for WebUI using Redis",
+ [[
+redis_history {
+ # History key name
+ key_prefix = 'rs_history{{HOSTNAME}}{{COMPRESS}}';
+ # Expire in seconds for inactive keys, default to 5 days
+ expire = 432000;
+ # History rows limit
+ nrows = 200;
+ # Use zstd compression when storing data in redis
+ compress = true;
+ # Obfuscate subjects for privacy
+ subject_privacy = false;
+ # Default hash-algorithm to obfuscate subject
+ subject_privacy_alg = 'blake2';
+ # Prefix to show it's obfuscated
+ subject_privacy_prefix = 'obf';
+ # Cut the length of the hash if desired
+ subject_privacy_length = 16;
+}
+ ]])
+ return
+end
+
local function process_addr(addr)
if addr then
return addr.addr
local lua_redis = require "lua_redis"
local lua_maps = require "lua_maps"
local rspamd_cryptobox_hash = require "rspamd_cryptobox_hash"
-
-if confighelp then
- rspamd_config:add_example(nil, 'known_senders',
- "Maintain a list of known senders using Redis",
- [[
-known_senders {
- # Domains to track senders
- domains = "https://maps.rspamd.com/freemail/free.txt.zst";
- # Maximum number of elements
- max_senders = 100000;
- # Maximum time to live (when not using bloom filters)
- max_ttl = 30d;
- # Use bloom filters (must be enabled in Redis as a plugin)
- use_bloom = false;
- # Insert symbol for new senders from the specific domains
- symbol_unknown = 'UNKNOWN_SENDER';
-}
- ]])
- return
-end
+local T = require "lua_shape.core"
+local PluginSchema = require "lua_shape.plugin_schema"
local redis_params
local settings = {
- domains = {},
+ domains = nil,
max_senders = 100000,
max_ttl = 30 * 86400,
use_bloom = false,
reply_sender_privacy_length = 16,
}
---[[
-XXX: please fix tableshape one day
local settings_schema = lua_redis.enrich_schema({
- domains = lua_maps.map_schema:is_optional(),
- enabled = ts.boolean:is_optional(),
- max_senders = (ts.integer + ts.string / tonumber):is_optional(),
- max_ttl = (ts.integer + ts.string / tonumber):is_optional(),
- use_bloom = ts.boolean:is_optional(),
- redis_key = ts.string:is_optional(),
- symbol = ts.string:is_optional(),
- symbol_unknown = ts.string:is_optional(),
- max_recipients = ts.integer:is_optional(),
- sender_prefix = ts.string:is_optional(),
- sender_key_global = ts.string:is_optional(),
- sender_key_size = ts.integer:is_optional(),
- reply_sender_privacy = ts.boolean:is_optional(),
- reply_sender_privacy_alg = ts.string:is_optional(),
- reply_sender_privacy_prefix = ts.string:is_optional(),
- reply_sender_privacy_length = ts.integer:is_optional(),
-})
-]]--
+ domains = lua_maps.map_schema:optional():doc({ summary = "Map of domains to track senders" }),
+ enabled = T.boolean():optional():doc({ summary = "Enable the plugin" }),
+ max_senders = T.one_of({
+ T.number(),
+ T.transform(T.string(), tonumber)
+ }):optional():doc({ summary = "Maximum number of senders to keep" }),
+ max_ttl = T.one_of({
+ T.number(),
+ T.transform(T.string(), lua_util.parse_time_interval)
+ }):optional():doc({ summary = "Maximum sender lifetime (seconds)" }),
+ use_bloom = T.boolean():optional():doc({ summary = "Use Redis bloom filters" }),
+ redis_key = T.string():optional():doc({ summary = "Redis key for known senders" }),
+ symbol = T.string():optional():doc({ summary = "Symbol for known senders" }),
+ symbol_unknown = T.string():optional():doc({ summary = "Symbol for unknown senders" }),
+ symbol_check_mail_global = T.string():optional():doc({ summary = "Symbol to check global replies" }),
+ symbol_check_mail_local = T.string():optional():doc({ summary = "Symbol to check local replies" }),
+ max_recipients = T.number():optional():doc({ summary = "Maximum recipients to inspect" }),
+ sender_prefix = T.string():optional():doc({ summary = "Redis prefix for sender reply sets" }),
+ sender_key_global = T.string():optional():doc({ summary = "Redis key for global replies" }),
+ sender_key_size = T.number():optional():doc({ summary = "Length of hashed sender keys" }),
+ reply_sender_privacy = T.boolean():optional():doc({ summary = "Enable reply sender privacy mode" }),
+ reply_sender_privacy_alg = T.string():optional():doc({ summary = "Hash algorithm for sender privacy" }),
+ reply_sender_privacy_prefix = T.string():optional():doc({ summary = "Prefix for hashed sender keys" }),
+ reply_sender_privacy_length = T.number():optional():doc({ summary = "Length of hashed sender output" }),
+}):doc({ summary = "Known senders plugin configuration" })
+
+PluginSchema.register("plugins.known_senders", settings_schema)
+
+if confighelp then
+ rspamd_config:add_example(nil, 'known_senders',
+ "Maintain a list of known senders using Redis",
+ [[
+known_senders {
+ # Domains to track senders
+ domains = "https://maps.rspamd.com/freemail/free.txt.zst";
+ # Maximum number of elements
+ max_senders = 100000;
+ # Maximum time to live (when not using bloom filters)
+ max_ttl = 30d;
+ # Use bloom filters (must be enabled in Redis as a plugin)
+ use_bloom = false;
+ # Insert symbol for new senders from the specific domains
+ symbol_unknown = 'UNKNOWN_SENDER';
+}
+ ]])
+ return
+end
local function make_key(input)
local hash = rspamd_cryptobox_hash.create_specific('md5')
local opts = rspamd_config:get_all_opt('known_senders')
if opts then
settings = lua_util.override_defaults(settings, opts)
+ local res, err = settings_schema:transform(settings)
+ if not res then
+ rspamd_logger.errx(rspamd_config, 'invalid %s config: %s', N, err)
+ lua_util.disable_module(N, "config")
+ return
+ end
+ settings = res
+
+ if not settings.domains then
+ rspamd_logger.errx(rspamd_config, '%s requires "domains" map to be defined', N)
+ lua_util.disable_module(N, "config")
+ return
+ end
+
redis_params = lua_redis.parse_redis_server(N, opts)
if redis_params then
limitations under the License.
]] --
-if confighelp then
- return
-end
-
-- A plugin that provides common header manipulations
local logger = require "rspamd_logger"
local lua_maps = require "lua_maps"
local lua_mime = require "lua_mime"
local T = require "lua_shape.core"
+local PluginSchema = require "lua_shape.plugin_schema"
local E = {}
local HOSTNAME = rspamd_util.get_hostname()
open = true
}):doc({ summary = "Milter headers plugin configuration" })
+PluginSchema.register("plugins.milter_headers", config_schema)
+
+if confighelp then
+ return
+end
+
local opts = rspamd_config:get_all_opt(N) or
rspamd_config:get_all_opt('rmilter_headers')
]] --
-if confighelp then
- return
-end
-
local fun = require "fun"
local lua_redis = require "lua_redis"
local lua_util = require "lua_util"
local rspamd_text = require "rspamd_text"
local rspamd_util = require "rspamd_util"
local T = require "lua_shape.core"
+local PluginSchema = require "lua_shape.plugin_schema"
-- Load providers
pcall(require, "plugins/neural/providers/llm")
pcall(require, "plugins/neural/providers/symbols")
providers_digest = T.string():optional():doc({ summary = "Providers digest" }),
}):doc({ summary = "Neural network profile schema" })
+PluginSchema.register("plugins.neural.profile", redis_profile_schema)
+
+if confighelp then
+ return
+end
+
local has_blas = rspamd_tensor.has_blas()
local text_cookie = rspamd_text.cookie
limitations under the License.
]]--
-if confighelp then
- return
-end
-
-- A generic plugin for reputation handling
local E = {}
local fun = require "fun"
local lua_selectors = require "lua_selectors"
local T = require "lua_shape.core"
+local PluginSchema = require "lua_shape.plugin_schema"
local redis_params = nil
local default_expiry = 864000 -- 10 day by default
idempotent = generic_reputation_idempotent -- used to set scores
}
+PluginSchema.register("plugins.reputation.selector.generic", generic_selector.schema)
+
local selectors = {
ip = ip_selector,
sender = ip_selector, -- Better name
}
}
+PluginSchema.register("plugins.reputation.backend.redis", backends.redis.schema)
+PluginSchema.register("plugins.reputation.backend.dns", backends.dns.schema)
+
+if confighelp then
+ return
+end
+
local function is_rule_applicable(task, rule)
local ip = task:get_from_ip()
if not (rule.selector.config.outbound and rule.selector.config.inbound) then