changeset 2545:9b46d24edf0d

mod_firewall: Add and document COUNT condition
author Matthew Wild <mwild1@gmail.com>
date Tue, 21 Feb 2017 22:41:58 +0000
parents 223eea31588d
children 6e4494772328
files mod_firewall/README.markdown mod_firewall/conditions.lib.lua
diffstat 2 files changed, 34 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mod_firewall/README.markdown	Tue Feb 21 22:41:40 2017 +0000
+++ b/mod_firewall/README.markdown	Tue Feb 21 22:41:58 2017 +0000
@@ -157,6 +157,21 @@
     SCAN: body for word in badwords
     BOUNCE=policy-violation (This word is not allowed!)
 
+#### COUNT
+
+COUNT is similar to SCAN, in that it uses a defined SEARCH and breaks it up according to a PATTERN. Then it
+counts the number of results.
+
+For example, to block every message with more than one URL:
+
+    # Define a search location called 'body' which fetches the text of the 'body' element
+    %SEARCH body: body#
+    # Define a pattern called 'url' which matches HTTP links
+    %PATTERN url: https?://%S+
+    
+    COUNT: url in body > 1
+    BOUNCE=policy-violation (Up to one HTTP URL is allowed in messages)
+
 ### Stanza matching
 
   Condition   Matches
--- a/mod_firewall/conditions.lib.lua	Tue Feb 21 22:41:40 2017 +0000
+++ b/mod_firewall/conditions.lib.lua	Tue Feb 21 22:41:58 2017 +0000
@@ -279,4 +279,23 @@
 	return ("scan_list(list_%s, %s)"):format(list_name, "tokens_"..search_name.."_"..pattern_name), { "scan_list", "tokens:"..search_name.."_"..pattern_name, "list:"..list_name };
 end
 
+local valid_comp_ops = { [">"] = ">", ["<"] = "<", ["="] = "==", ["=="] = "==", ["<="] = "<=", [">="] = ">=" };
+function condition_handlers.COUNT(count_expression)
+	local pattern_name, search_name, comparator_expression = count_expression:match("(%S+) in (%S+) (.+)$");
+	if not (pattern_name) then
+		error("Error parsing COUNT expression, syntax: PATTERN in SEARCH COMPARATOR");
+	end
+	local value;
+	comparator_expression = comparator_expression:gsub("%d+", function (value_string)
+		value = tonumber(value_string);
+		return "";
+	end);
+	if not value then
+		error("Error parsing COUNT expression, expected value");
+	end
+	local comp_op = comparator_expression:gsub("%s+", "");
+	assert(valid_comp_ops[comp_op], "Error parsing COUNT expression, unknown comparison operator: "..comp_op);
+	return ("it_count(search_%s:gmatch(pattern_%s)) %s %d"):format(search_name, pattern_name, comp_op, value), { "it_count", "search:"..search_name, "pattern:"..pattern_name };
+end
+
 return condition_handlers;