wafris 1.1.11 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
metadata CHANGED
@@ -1,58 +1,86 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wafris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.11
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Micahel Buckbee
7
+ - Michael Buckbee
8
8
  - Ryan Castillo
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-05-04 00:00:00.000000000 Z
12
+ date: 2024-07-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: connection_pool
15
+ name: rack
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '2.3'
20
+ version: '2.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '2.3'
27
+ version: '2.0'
28
28
  - !ruby/object:Gem::Dependency
29
- name: rack
29
+ name: sqlite3
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: '2.0'
34
+ version: '0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: '2.0'
41
+ version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
- name: redis
43
+ name: ipaddr
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: 4.8.0
48
+ version: '0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
- version: 4.8.0
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: httparty
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: awesome_print
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
56
84
  - !ruby/object:Gem::Dependency
57
85
  name: minitest
58
86
  requirement: !ruby/object:Gem::Requirement
@@ -107,14 +135,14 @@ dependencies:
107
135
  requirements:
108
136
  - - ">="
109
137
  - !ruby/object:Gem::Version
110
- version: '5.0'
138
+ version: '6.0'
111
139
  type: :development
112
140
  prerelease: false
113
141
  version_requirements: !ruby/object:Gem::Requirement
114
142
  requirements:
115
143
  - - ">="
116
144
  - !ruby/object:Gem::Version
117
- version: '5.0'
145
+ version: '6.0'
118
146
  - !ruby/object:Gem::Dependency
119
147
  name: railties
120
148
  requirement: !ruby/object:Gem::Requirement
@@ -149,7 +177,6 @@ executables: []
149
177
  extensions: []
150
178
  extra_rdoc_files: []
151
179
  files:
152
- - lib/lua/dist/wafris_core.lua
153
180
  - lib/wafris.rb
154
181
  - lib/wafris/configuration.rb
155
182
  - lib/wafris/log_suppressor.rb
@@ -161,11 +188,9 @@ licenses:
161
188
  - Elastic-2.0
162
189
  metadata: {}
163
190
  post_install_message: |2+
164
- Thank you for installing the wafris gem.
165
-
166
- If you haven't already, please sign up for Wafris Hub at:
191
+ Thank you for installing the Wafris gem.
167
192
 
168
- https://github.com/Wafris/wafris-rb
193
+ Get your API key and set firewall rules at https://hub.wafris.org
169
194
 
170
195
  rdoc_options: []
171
196
  require_paths:
@@ -1,305 +0,0 @@
1
-
2
-
3
- local USE_TIMESTAMPS_AS_REQUEST_IDS = false
4
- local EXPIRATION_IN_SECONDS = tonumber(redis.call("HGET", "waf-settings", "expiration-time")) or 86400
5
- local EXPIRATION_OFFSET_IN_SECONDS = 3600
6
-
7
-
8
- local function get_timebucket(timestamp_in_seconds)
9
- local startOfHourTimestamp = math.floor(timestamp_in_seconds / 3600) * 3600
10
- return tostring(startOfHourTimestamp)
11
- end
12
-
13
- local function set_property_value_id_lookups(property_abbreviation, property_value)
14
-
15
- local value_key = property_abbreviation .. "V" .. property_value
16
- local property_id = redis.call("GET", value_key)
17
-
18
- if property_id == false then
19
- property_id = redis.call("INCR", property_abbreviation .. "-id-counter")
20
- redis.call("SET", value_key, property_id)
21
- redis.call("SET", property_abbreviation .. "I" .. property_id, property_value)
22
- end
23
-
24
- redis.call("EXPIRE", value_key, EXPIRATION_IN_SECONDS + EXPIRATION_OFFSET_IN_SECONDS)
25
- redis.call("EXPIRE", property_abbreviation .. "I" .. property_id, EXPIRATION_IN_SECONDS + EXPIRATION_OFFSET_IN_SECONDS)
26
-
27
- return property_id
28
- end
29
-
30
- local function increment_leaderboard_for(property_abbreviation, property_id, timebucket)
31
-
32
- local key = property_abbreviation .. "L" .. timebucket
33
- redis.call("ZINCRBY", key, 1, property_id)
34
- redis.call("EXPIRE", key, EXPIRATION_IN_SECONDS)
35
- end
36
-
37
- local function set_property_to_requests_list(property_abbreviation, property_id, request_id, timebucket)
38
-
39
- local key = property_abbreviation .. "R" .. property_id .. "-" .. timebucket
40
- redis.call("LPUSH", key, request_id)
41
-
42
- redis.call("EXPIRE", key, EXPIRATION_IN_SECONDS + EXPIRATION_OFFSET_IN_SECONDS)
43
- end
44
-
45
-
46
- local function ip_in_hash(hash_name, ip_address)
47
- local found_ip = redis.call('HEXISTS', hash_name, ip_address)
48
-
49
- if found_ip == 1 then
50
- return ip_address
51
- else
52
- return false
53
- end
54
- end
55
-
56
- local function ip_in_cidr_range(cidr_set, ip_decimal_lexical)
57
-
58
- local higher_value = redis.call('ZRANGEBYLEX', cidr_set, '['..ip_decimal_lexical, '+', 'LIMIT', 0, 1)[1]
59
-
60
- local lower_value = redis.call('ZREVRANGEBYLEX', cidr_set, '['..ip_decimal_lexical, '-', 'LIMIT', 0, 1)[1]
61
-
62
- if not (higher_value and lower_value) then
63
- return false
64
- end
65
-
66
- local higher_compare = higher_value:match('([^%-]+)$')
67
- local lower_compare = lower_value:match('([^%-]+)$')
68
-
69
- if higher_compare == lower_compare then
70
- return lower_compare
71
- else
72
- return false
73
- end
74
- end
75
-
76
- local function escapePattern(s)
77
- local patternSpecials = "[%^%$%(%)%%%.%[%]%*%+%-%?]"
78
- return s:gsub(patternSpecials, "%%%1")
79
- end
80
-
81
- local function match_by_pattern(property_abbreviation, property_value)
82
- local hash_name = "rules-blocked-" .. property_abbreviation
83
-
84
- local patterns = redis.call('HKEYS', hash_name)
85
-
86
- for _, pattern in ipairs(patterns) do
87
- if string.find(string.lower(property_value), string.lower(escapePattern(pattern))) then
88
- return pattern
89
- end
90
- end
91
-
92
- return false
93
- end
94
-
95
- local function blocked_by_rate_limit(request_properties)
96
-
97
- local rate_limiting_rules_values = redis.call('HKEYS', 'rules-blocked-rate-limits')
98
-
99
- for i, rule_name in ipairs(rate_limiting_rules_values) do
100
-
101
- local conditions_hash = redis.call('HGETALL', rule_name .. "-conditions")
102
-
103
- local all_conditions_match = true
104
-
105
- for j = 1, #conditions_hash, 2 do
106
- local condition_key = conditions_hash[j]
107
- local condition_value = conditions_hash[j + 1]
108
-
109
- if request_properties[condition_key] ~= condition_value then
110
- all_conditions_match = false
111
- break
112
- end
113
- end
114
-
115
- if all_conditions_match then
116
-
117
- local rule_settings_key = rule_name .. "-settings"
118
-
119
- local limit, time_period, limited_by, rule_id = unpack(redis.call('HMGET', rule_settings_key, 'limit', 'time-period', 'limited-by', 'rule-id'))
120
-
121
- local throttle_key = rule_name .. ":" .. limit .. "V" .. request_properties.ip
122
-
123
- local new_value = redis.call('INCR', throttle_key)
124
-
125
- if new_value == 1 then
126
- redis.call('EXPIRE', throttle_key, tonumber(time_period))
127
- end
128
-
129
- if tonumber(new_value) >= tonumber(limit) then
130
- return rule_id
131
- else
132
- return false
133
- end
134
- end
135
- end
136
- end
137
-
138
- local function check_rules(functions_to_check)
139
- for _, check in ipairs(functions_to_check) do
140
-
141
- local rule = check.func(unpack(check.args))
142
- local category = check.category
143
-
144
- if type(rule) == "string" then
145
- return rule, category
146
- end
147
- end
148
-
149
- return false, false
150
- end
151
-
152
- local function check_blocks(request)
153
- local rule_categories = {
154
- { category = "bi", func = ip_in_hash, args = { "rules-blocked-i", request.ip } },
155
- { category = "bc", func = ip_in_cidr_range, args = { "rules-blocked-cidrs-set", request.ip_decimal_lexical } },
156
- { category = "bs", func = ip_in_cidr_range, args = { "rules-blocked-cidrs-subscriptions-set", request.ip_decimal_lexical } },
157
- { category = "bu", func = match_by_pattern, args = { "u", request.user_agent } },
158
- { category = "bp", func = match_by_pattern, args = { "p", request.path } },
159
- { category = "ba", func = match_by_pattern, args = { "a", request.parameters } },
160
- { category = "bh", func = match_by_pattern, args = { "h", request.host } },
161
- { category = "bm", func = match_by_pattern, args = { "m", request.method } },
162
- { category = "bd", func = match_by_pattern, args = { "rh", request.headers } },
163
- { category = "bpb", func = match_by_pattern, args = { "pb", request.post_body } },
164
- { category = "brl", func = blocked_by_rate_limit, args = { request } }
165
- }
166
-
167
- return check_rules(rule_categories)
168
- end
169
-
170
- local function check_allowed(request)
171
- local rule_categories = {
172
- { category = "ai", func = ip_in_hash, args = { "rules-allowed-i", request.ip } },
173
- { category = "ac", func = ip_in_cidr_range, args = { "rules-allowed-cidrs-set", request.ip_decimal_lexical } }
174
- }
175
-
176
- return check_rules(rule_categories)
177
- end
178
-
179
- local request = {
180
- ["ip"] = ARGV[1],
181
- ["ip_decimal_lexical"] = string.rep("0", 39 - #ARGV[2]) .. ARGV[2],
182
- ["ts_in_milliseconds"] = ARGV[3],
183
- ["ts_in_seconds"] = ARGV[3] / 1000,
184
- ["user_agent"] = ARGV[4],
185
- ["path"] = ARGV[5],
186
- ["parameters"] = ARGV[6],
187
- ["host"] = ARGV[7],
188
- ["method"] = ARGV[8],
189
- ["headers"] = ARGV[9],
190
- ["post_body"] = ARGV[10],
191
- ["ip_id"] = set_property_value_id_lookups("i", ARGV[1]),
192
- ["user_agent_id"] = set_property_value_id_lookups("u", ARGV[4]),
193
- ["path_id"] = set_property_value_id_lookups("p", ARGV[5]),
194
- ["parameters_id"] = set_property_value_id_lookups("a", ARGV[6]),
195
- ["host_id"] = set_property_value_id_lookups("h", ARGV[7]),
196
- ["method_id"] = set_property_value_id_lookups("m", ARGV[8])
197
- }
198
-
199
-
200
-
201
- local current_timebucket = get_timebucket(request.ts_in_seconds)
202
-
203
- local blocked_rule = false
204
- local blocked_category = nil
205
- local treatment = "p"
206
-
207
- local stream_id
208
-
209
- if USE_TIMESTAMPS_AS_REQUEST_IDS == true then
210
- stream_id = request.ts_in_milliseconds
211
- else
212
- stream_id = "*"
213
- end
214
-
215
- local stream_args = {
216
- "XADD",
217
- "rStream",
218
- "MINID",
219
- tostring((current_timebucket - EXPIRATION_IN_SECONDS) * 1000 ),
220
- stream_id,
221
- "i", request.ip_id,
222
- "u", request.user_agent_id,
223
- "p", request.path_id,
224
- "h", request.host_id,
225
- "m", request.method_id,
226
- "a", request.parameters_id,
227
- }
228
-
229
- local allowed_rule, allowed_category = check_allowed(request)
230
-
231
- if allowed_rule then
232
- table.insert(stream_args, "t")
233
- table.insert(stream_args, "a")
234
-
235
- treatment = "a"
236
-
237
- table.insert(stream_args, "ac")
238
- table.insert(stream_args, allowed_category)
239
-
240
- table.insert(stream_args, "ar")
241
- table.insert(stream_args, allowed_rule)
242
-
243
- else
244
- blocked_rule, blocked_category = check_blocks(request)
245
- end
246
-
247
- if blocked_rule then
248
- table.insert(stream_args, "t")
249
- table.insert(stream_args, "b")
250
-
251
- treatment = "b"
252
-
253
- table.insert(stream_args, "bc")
254
- table.insert(stream_args, blocked_category)
255
-
256
- table.insert(stream_args, "br")
257
- table.insert(stream_args, blocked_rule)
258
- end
259
-
260
- if blocked_rule == false and allowed_rule == false then
261
- table.insert(stream_args, "t")
262
- table.insert(stream_args, "p")
263
- end
264
-
265
- local request_id = redis.call(unpack(stream_args))
266
-
267
- increment_leaderboard_for("i", request.ip_id, current_timebucket)
268
- increment_leaderboard_for("u", request.user_agent_id, current_timebucket)
269
- increment_leaderboard_for("p", request.path_id, current_timebucket)
270
- increment_leaderboard_for("a", request.parameters_id, current_timebucket)
271
- increment_leaderboard_for("h", request.host_id, current_timebucket)
272
- increment_leaderboard_for("m", request.method_id, current_timebucket)
273
- increment_leaderboard_for("t", treatment, current_timebucket)
274
-
275
- set_property_to_requests_list("i", request.ip_id, request_id, current_timebucket)
276
- set_property_to_requests_list("u", request.user_agent_id, request_id, current_timebucket)
277
- set_property_to_requests_list("p", request.path_id, request_id, current_timebucket)
278
- set_property_to_requests_list("a", request.parameters_id, request_id, current_timebucket)
279
- set_property_to_requests_list("h", request.host_id, request_id, current_timebucket)
280
- set_property_to_requests_list("m", request.method_id, request_id, current_timebucket)
281
- set_property_to_requests_list("t", treatment, request_id, current_timebucket)
282
-
283
- if blocked_rule ~= false then
284
- increment_leaderboard_for("bc", blocked_category, current_timebucket)
285
- set_property_to_requests_list("bc", blocked_category, request_id, current_timebucket)
286
-
287
- increment_leaderboard_for("br", blocked_rule, current_timebucket)
288
- set_property_to_requests_list("br", blocked_rule, request_id, current_timebucket)
289
- end
290
-
291
- if allowed_rule ~= false then
292
- increment_leaderboard_for("ac", allowed_category, current_timebucket)
293
- set_property_to_requests_list("ac", allowed_category, request_id, current_timebucket)
294
-
295
- increment_leaderboard_for("ar", allowed_rule, current_timebucket)
296
- set_property_to_requests_list("ar", allowed_rule, request_id, current_timebucket)
297
- end
298
-
299
- if blocked_rule ~= false then
300
- return "Blocked"
301
- elseif allowed_rule ~= false then
302
- return "Allowed"
303
- else
304
- return "Passed"
305
- end