wafris 2.0.2 → 2.0.4
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.
- checksums.yaml +4 -4
- data/lib/wafris/configuration.rb +29 -71
- data/lib/wafris/log_suppressor.rb +4 -4
- data/lib/wafris/middleware.rb +6 -18
- data/lib/wafris/proxy_filter.rb +2 -2
- data/lib/wafris/railtie.rb +1 -1
- data/lib/wafris/version.rb +1 -1
- data/lib/wafris/wafris_request.rb +26 -6
- data/lib/wafris.rb +227 -303
- metadata +20 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 531133646bfb1df1b2b61c250179cd46de9e7986bc05075e509bededcd24bfbc
|
4
|
+
data.tar.gz: 4971a237b63f18b5f50d95751fd18ed51f4aa863fcd89da97be46c77176b31e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 835a0b2ff984d50e6de308db53dacfba7490a7d41a5ae426b26347a23b862e05ea5f0a0250e31d66a0a952335e083ce16843476c1e98004f62131a4c7d52349c
|
7
|
+
data.tar.gz: 2e9df9bdf15d055730171a1da8126e59adb754ba1d033949739fc2a3e5b001c5e58144d4453a84c98922bb93faac59fc911c49f430aa95f582d9511958b02f3d
|
data/lib/wafris/configuration.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
|
2
|
-
require_relative 'version'
|
1
|
+
require_relative "version"
|
3
2
|
|
4
3
|
module Wafris
|
5
4
|
class Configuration
|
6
|
-
|
7
5
|
attr_accessor :api_key
|
8
6
|
attr_accessor :db_file_path
|
9
7
|
attr_accessor :db_file_name
|
@@ -21,23 +19,17 @@ module Wafris
|
|
21
19
|
attr_accessor :rate_limiters
|
22
20
|
|
23
21
|
def initialize
|
24
|
-
|
25
22
|
# API Key - Required
|
26
|
-
if ENV[
|
27
|
-
@api_key = ENV[
|
23
|
+
if ENV["WAFRIS_API_KEY"]
|
24
|
+
@api_key = ENV["WAFRIS_API_KEY"]
|
28
25
|
else
|
29
|
-
unless @api_key
|
30
|
-
LogSuppressor.puts_log("Firewall disabled as neither local only or API key set")
|
26
|
+
unless @api_key
|
27
|
+
LogSuppressor.puts_log("Firewall disabled as neither local only or API key set")
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
34
31
|
# DB FILE PATH LOCATION - Optional
|
35
|
-
|
36
|
-
@db_file_path = ENV['WAFRIS_DB_FILE_PATH']
|
37
|
-
else
|
38
|
-
#@db_file_path = Rails.root.join('tmp', 'wafris').to_s
|
39
|
-
@db_file_path = './tmp/wafris'
|
40
|
-
end
|
32
|
+
@db_file_path = ENV["WAFRIS_DB_FILE_PATH"] || "./tmp/wafris"
|
41
33
|
|
42
34
|
# Ensure that the db_file_path exists
|
43
35
|
unless File.directory?(@db_file_path)
|
@@ -46,95 +38,61 @@ module Wafris
|
|
46
38
|
end
|
47
39
|
|
48
40
|
# DB FILE NAME - For local
|
49
|
-
|
50
|
-
|
51
|
-
else
|
52
|
-
@db_file_name = 'wafris.db'
|
53
|
-
end
|
54
|
-
|
41
|
+
@db_file_name = ENV["WAFRIS_DB_FILE_NAME"] || "wafris.db"
|
42
|
+
|
55
43
|
# DOWNSYNC
|
56
44
|
# Custom Rules are checked often (default 1 minute) - Optional
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
@downsync_custom_rules_interval = 60
|
61
|
-
end
|
62
|
-
|
45
|
+
@downsync_custom_rules_interval = ENV["WAFRIS_DOWNSYNC_CUSTOM_RULES_INTERVAL"]&.to_i || 60
|
46
|
+
|
63
47
|
# Data Subscriptions are checked rarely (default 1 day) - Optional
|
64
|
-
|
65
|
-
|
66
|
-
else
|
67
|
-
@downsync_data_subscriptions_interval = 60
|
68
|
-
end
|
69
|
-
|
48
|
+
@downsync_data_subscriptions_interval = ENV["WAFRIS_DOWNSYNC_DATA_SUBSCRIPTIONS_INTERVAL"]&.to_i || 60
|
49
|
+
|
70
50
|
# Set Downsync URL - Optional
|
71
51
|
# Used for both DataSubscription and CustomRules
|
72
|
-
|
73
|
-
|
74
|
-
else
|
75
|
-
@downsync_url = 'https://distributor.wafris.org/v2/downsync'
|
76
|
-
end
|
77
|
-
|
52
|
+
@downsync_url = ENV["WAFRIS_DOWNSYNC_URL"] || "https://distributor.wafris.org/v2/downsync"
|
53
|
+
|
78
54
|
# UPSYNC - Optional
|
79
55
|
# Set Upsync URL
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
@upsync_url = 'https://collector.wafris.org/v2/upsync'
|
84
|
-
end
|
85
|
-
|
56
|
+
@upsync_url = ENV["WAFRIS_UPSYNC_URL"] || "https://collector.wafris.org/v2/upsync"
|
57
|
+
|
86
58
|
# Set Upsync Interval - Optional
|
87
|
-
|
88
|
-
|
89
|
-
else
|
90
|
-
@upsync_interval = 10
|
91
|
-
end
|
92
|
-
|
59
|
+
@upsync_interval = ENV["WAFRIS_UPSYNC_INTERVAL"]&.to_i || 10
|
60
|
+
|
93
61
|
# Set Upsync Queued Request Limit - Optional
|
94
|
-
|
95
|
-
|
96
|
-
else
|
97
|
-
@upsync_queue_limit = 250
|
98
|
-
end
|
99
|
-
|
62
|
+
@upsync_queue_limit = ENV["WAFRIS_UPSYNC_QUEUE_LIMIT"]&.to_i || 250
|
63
|
+
|
100
64
|
# Set Maximium Body Size for Requests - Optional (in Megabytes)
|
101
|
-
if ENV[
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
65
|
+
@max_body_size_mb = if ENV["WAFRIS_MAX_BODY_SIZE_MB"] && ENV["WAFRIS_MAX_BODY_SIZE_MB"].to_i > 0
|
66
|
+
ENV["WAFRIS_MAX_BODY_SIZE_MB"].to_i
|
67
|
+
else
|
68
|
+
10
|
69
|
+
end
|
106
70
|
|
107
71
|
# Upsync Queue Defaults
|
108
72
|
@upsync_queue = []
|
109
73
|
@last_upsync_timestamp = Time.now.to_i
|
110
|
-
|
74
|
+
|
111
75
|
# Memory structure for rate limiting
|
112
76
|
@rate_limiters = {}
|
113
|
-
|
77
|
+
|
114
78
|
# Disable Upsync if Downsync API Key is invalid
|
115
79
|
# This prevents the client from sending upsync requests
|
116
80
|
# if the API key is known bad
|
117
|
-
@upsync_status =
|
118
|
-
|
119
|
-
return true
|
120
|
-
|
81
|
+
@upsync_status = "Disabled"
|
121
82
|
end
|
122
83
|
|
123
84
|
def current_config
|
124
|
-
|
125
85
|
output = {}
|
126
86
|
|
127
87
|
instance_variables.each do |var|
|
128
88
|
output[var.to_s] = instance_variable_get(var)
|
129
89
|
end
|
130
90
|
|
131
|
-
|
132
|
-
|
91
|
+
output
|
133
92
|
end
|
134
93
|
|
135
94
|
def create_settings
|
136
95
|
@version = Wafris::VERSION
|
137
96
|
end
|
138
|
-
|
139
97
|
end
|
140
98
|
end
|
@@ -7,19 +7,19 @@ module Wafris
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.suppress_logs?
|
10
|
-
suppressed_environments.include?(current_environment) ||
|
11
|
-
|
10
|
+
suppressed_environments.include?(current_environment) ||
|
11
|
+
(ENV["WAFRIS_LOG_LEVEL"] && ENV["WAFRIS_LOG_LEVEL"] == "silent")
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.suppressed_environments
|
15
|
-
[
|
15
|
+
["test"] + (ENV["CI"] ? ["CI"] : [])
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.current_environment
|
19
19
|
if defined?(Rails)
|
20
20
|
Rails.env
|
21
21
|
else
|
22
|
-
ENV[
|
22
|
+
ENV["RACK_ENV"] || "development"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
data/lib/wafris/middleware.rb
CHANGED
@@ -9,32 +9,20 @@ module Wafris
|
|
9
9
|
|
10
10
|
def call(env)
|
11
11
|
request = Rack::Request.new(env)
|
12
|
-
wafris_request = WafrisRequest.new(request, env)
|
13
12
|
|
14
13
|
treatment = Wafris.evaluate(
|
15
|
-
|
16
|
-
wafris_request.user_agent,
|
17
|
-
wafris_request.path,
|
18
|
-
wafris_request.parameters,
|
19
|
-
wafris_request.host,
|
20
|
-
wafris_request.request_method,
|
21
|
-
wafris_request.headers,
|
22
|
-
wafris_request.body,
|
23
|
-
wafris_request.request_id,
|
24
|
-
wafris_request.request_timestamp
|
14
|
+
WafrisRequest.new(request, env)
|
25
15
|
)
|
26
16
|
|
27
17
|
# These values match what the client tests expect (200, 404, 403, 500)
|
28
|
-
if treatment ==
|
18
|
+
if treatment == "Allowed" || treatment == "Passed"
|
29
19
|
@app.call(env)
|
30
|
-
elsif treatment ==
|
31
|
-
[403, {
|
20
|
+
elsif treatment == "Blocked"
|
21
|
+
[403, {"content-type" => "text/plain"}, ["Blocked"]]
|
32
22
|
else
|
33
|
-
|
34
|
-
[500, { 'content-type' => 'text/plain' }, ['Error']]
|
23
|
+
[500, {"content-type" => "text/plain"}, ["Error"]]
|
35
24
|
end
|
36
|
-
|
37
|
-
rescue StandardError => e
|
25
|
+
rescue => e
|
38
26
|
LogSuppressor.puts_log "[Wafris] Detailed Error: #{e.class} - #{e.message}"
|
39
27
|
LogSuppressor.puts_log "[Wafris] Backtrace: #{e.backtrace.join("\n")}"
|
40
28
|
@app.call(env)
|
data/lib/wafris/proxy_filter.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Wafris
|
4
4
|
module ProxyFilter
|
5
5
|
def self.set_filter
|
6
|
-
user_defined_proxies = ENV[
|
6
|
+
user_defined_proxies = ENV["TRUSTED_PROXY_RANGES"].split(",") if ENV["TRUSTED_PROXY_RANGES"]
|
7
7
|
|
8
8
|
valid_ipv4_octet = /\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/
|
9
9
|
|
@@ -21,4 +21,4 @@ module Wafris
|
|
21
21
|
Rack::Request.ip_filter = lambda { |ip| trusted_proxies.match?(ip) }
|
22
22
|
end
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
data/lib/wafris/railtie.rb
CHANGED
data/lib/wafris/version.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Wafris
|
4
4
|
class WafrisRequest
|
5
|
-
attr_reader :ip, :user_agent, :path, :parameters, :host, :
|
5
|
+
attr_reader :ip, :user_agent, :path, :parameters, :host, :method,
|
6
6
|
:headers, :body, :request_id, :request_timestamp
|
7
7
|
|
8
8
|
def initialize(request, env)
|
@@ -11,23 +11,43 @@ module Wafris
|
|
11
11
|
@path = encode_to_utf8(request.path)
|
12
12
|
@parameters = encode_to_utf8(Rack::Utils.build_query(request.params))
|
13
13
|
@host = encode_to_utf8(request.host.to_s)
|
14
|
-
@
|
14
|
+
@method = encode_to_utf8(request.request_method)
|
15
15
|
@headers = extract_headers(env)
|
16
|
-
@
|
17
|
-
@request_id = env.fetch('action_dispatch.request_id', SecureRandom.uuid.to_s)
|
16
|
+
@request_id = env.fetch("action_dispatch.request_id", SecureRandom.uuid.to_s)
|
18
17
|
@request_timestamp = Time.now.utc.to_i
|
18
|
+
|
19
|
+
@body = encode_to_utf8(request.body&.read)
|
20
|
+
request.body&.rewind
|
21
|
+
end
|
22
|
+
|
23
|
+
def data(treatment:, category:, rule:)
|
24
|
+
{
|
25
|
+
ip: @ip,
|
26
|
+
user_agent: @user_agent,
|
27
|
+
path: @path,
|
28
|
+
parameter: @parameters,
|
29
|
+
host: @host,
|
30
|
+
method: @method,
|
31
|
+
request_id: @request_id,
|
32
|
+
timestamp: @request_timestamp,
|
33
|
+
treatment: treatment,
|
34
|
+
category: category,
|
35
|
+
rule: rule
|
36
|
+
}
|
19
37
|
end
|
20
38
|
|
21
39
|
private
|
22
40
|
|
23
41
|
def extract_headers(env)
|
24
42
|
env.each_with_object({}) do |(k, v), h|
|
25
|
-
h[k] = encode_to_utf8(v) if k.start_with?(
|
43
|
+
h[k] = encode_to_utf8(v) if k.start_with?("HTTP_")
|
26
44
|
end
|
27
45
|
end
|
28
46
|
|
29
47
|
def encode_to_utf8(value)
|
30
|
-
value
|
48
|
+
return "" if value.nil?
|
49
|
+
|
50
|
+
value&.dup&.force_encoding("UTF-8")
|
31
51
|
end
|
32
52
|
end
|
33
53
|
end
|
data/lib/wafris.rb
CHANGED
@@ -1,45 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "rails"
|
4
|
+
require "sqlite3"
|
5
|
+
require "ipaddr"
|
6
|
+
require "httparty"
|
7
|
+
require "awesome_print"
|
8
8
|
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
9
|
+
require "wafris/configuration"
|
10
|
+
require "wafris/middleware"
|
11
|
+
require "wafris/log_suppressor"
|
12
|
+
require "wafris/proxy_filter"
|
13
|
+
require "wafris/ip_resolver"
|
14
|
+
require "wafris/wafris_request"
|
15
15
|
|
16
|
-
require
|
16
|
+
require "wafris/railtie" if defined?(Rails::Railtie)
|
17
17
|
|
18
18
|
module Wafris
|
19
19
|
class << self
|
20
20
|
attr_accessor :configuration
|
21
21
|
|
22
22
|
def configure
|
23
|
-
|
24
|
-
|
25
|
-
yield(configuration)
|
26
|
-
|
27
|
-
LogSuppressor.puts_log("[Wafris] Configuration settings created.")
|
28
|
-
configuration.create_settings
|
29
|
-
|
30
|
-
rescue StandardError => e
|
31
|
-
puts "[Wafris] firewall disabled due to: #{e.message}. Cannot connect via Wafris.configure. Please check your configuration settings. More info can be found at: https://github.com/Wafris/wafris-rb"
|
32
|
-
end
|
23
|
+
self.configuration ||= Wafris::Configuration.new
|
24
|
+
yield(configuration)
|
33
25
|
|
26
|
+
LogSuppressor.puts_log("[Wafris] Configuration settings created.")
|
27
|
+
configuration.create_settings
|
28
|
+
rescue => e
|
29
|
+
puts "[Wafris] firewall disabled due to: #{e.message}. Cannot connect via Wafris.configure. Please check your configuration settings. More info can be found at: https://github.com/Wafris/wafris-rb"
|
34
30
|
end
|
35
31
|
|
36
32
|
def zero_pad(number, length)
|
37
33
|
number.to_s.rjust(length, "0")
|
38
34
|
end
|
39
|
-
|
35
|
+
|
40
36
|
def ip_to_decimal_lexical_string(ip)
|
41
37
|
num = 0
|
42
|
-
|
38
|
+
|
43
39
|
if ip.include?(":")
|
44
40
|
ip = IPAddr.new(ip).to_string
|
45
41
|
hex = ip.delete(":")
|
@@ -52,41 +48,39 @@ module Wafris
|
|
52
48
|
num = num * 256 + chunk.to_i
|
53
49
|
end
|
54
50
|
end
|
55
|
-
|
51
|
+
|
56
52
|
str = num.to_s
|
57
53
|
zero_pad(str, 39)
|
58
54
|
end
|
59
|
-
|
55
|
+
|
60
56
|
def ip_in_cidr_range(ip_address, table_name, db_connection)
|
61
57
|
lexical_address = ip_to_decimal_lexical_string(ip_address)
|
62
58
|
higher_value = db_connection.get_first_value("SELECT * FROM #{table_name} WHERE member > ? ORDER BY member ASC", [lexical_address])
|
63
59
|
lower_value = db_connection.get_first_value("SELECT * FROM #{table_name} WHERE member < ? ORDER BY member DESC", [lexical_address])
|
64
|
-
|
60
|
+
|
65
61
|
if higher_value.nil? || lower_value.nil?
|
66
|
-
|
67
|
-
else
|
62
|
+
nil
|
63
|
+
else
|
68
64
|
higher_compare = higher_value.split("-").last
|
69
65
|
lower_compare = lower_value.split("-").last
|
70
|
-
|
66
|
+
|
71
67
|
if higher_compare == lower_compare
|
72
|
-
|
73
|
-
else
|
74
|
-
return nil
|
68
|
+
lower_compare
|
75
69
|
end
|
76
70
|
end
|
77
71
|
end
|
78
|
-
|
72
|
+
|
79
73
|
def get_country_code(ip, db_connection)
|
80
|
-
country_code = ip_in_cidr_range(ip,
|
81
|
-
|
74
|
+
country_code = ip_in_cidr_range(ip, "country_ip_ranges", db_connection)
|
75
|
+
|
82
76
|
if country_code
|
83
|
-
country_code
|
84
|
-
|
85
|
-
else
|
86
|
-
|
87
|
-
end
|
77
|
+
country_code.split("_").first.split("G").last
|
78
|
+
|
79
|
+
else
|
80
|
+
"ZZ"
|
81
|
+
end
|
88
82
|
end
|
89
|
-
|
83
|
+
|
90
84
|
def substring_match(request_property, table_name, db_connection)
|
91
85
|
result = db_connection.execute("SELECT entries FROM #{table_name}")
|
92
86
|
result.flatten.each do |entry|
|
@@ -94,30 +88,28 @@ module Wafris
|
|
94
88
|
return entry
|
95
89
|
end
|
96
90
|
end
|
97
|
-
|
91
|
+
false
|
98
92
|
end
|
99
|
-
|
93
|
+
|
100
94
|
def exact_match(request_property, table_name, db_connection)
|
101
95
|
result = db_connection.execute("SELECT entries FROM #{table_name} WHERE entries = ?", [request_property])
|
102
|
-
|
96
|
+
result.any?
|
103
97
|
end
|
104
|
-
|
98
|
+
|
105
99
|
def check_rate_limit(ip, path, method, db_connection)
|
106
|
-
|
107
100
|
# Correctly format the SQL query with placeholders
|
108
101
|
limiters = db_connection.execute("SELECT * FROM blocked_rate_limits WHERE path = ? AND method = ?", [path, method])
|
109
|
-
|
102
|
+
|
110
103
|
# If no rate limiters are matched
|
111
|
-
if limiters.empty?
|
104
|
+
if limiters.empty?
|
112
105
|
return false
|
113
|
-
end
|
114
|
-
|
106
|
+
end
|
107
|
+
|
115
108
|
current_timestamp = Time.now.to_i
|
116
|
-
|
109
|
+
|
117
110
|
# If any rate limiters are matched
|
118
111
|
# This implementation will block the request on any of the rate limiters
|
119
112
|
limiters.each do |limiter|
|
120
|
-
|
121
113
|
# Limiter array mapping
|
122
114
|
# 0: id
|
123
115
|
# 1: path
|
@@ -125,113 +117,100 @@ module Wafris
|
|
125
117
|
# 3: interval
|
126
118
|
# 4: max_count
|
127
119
|
# 5: rule_id
|
128
|
-
|
120
|
+
|
129
121
|
interval = limiter[3]
|
130
|
-
max_count = limiter[4]
|
122
|
+
max_count = limiter[4]
|
131
123
|
rule_id = limiter[5]
|
132
|
-
|
124
|
+
|
133
125
|
# Expire old timestamps
|
134
126
|
@configuration.rate_limiters.each do |ip, timestamps|
|
135
127
|
# Removes timestamps older than the interval
|
136
|
-
|
128
|
+
|
137
129
|
@configuration.rate_limiters[ip] = timestamps.select { |timestamp| timestamp > current_timestamp - interval }
|
138
|
-
|
130
|
+
|
139
131
|
# Remove the IP if there are no more timestamps for the IP
|
140
132
|
@configuration.rate_limiters.delete(ip) if @configuration.rate_limiters[ip].empty?
|
141
133
|
end
|
142
|
-
|
143
|
-
# Check if the IP+Method is rate limited
|
144
|
-
|
134
|
+
|
135
|
+
# Check if the IP+Method is rate limited
|
136
|
+
|
145
137
|
if @configuration.rate_limiters[ip] && @configuration.rate_limiters[ip].length >= max_count
|
146
138
|
# Request is rate limited
|
147
|
-
|
148
|
-
|
139
|
+
|
149
140
|
return rule_id
|
150
|
-
|
141
|
+
|
151
142
|
else
|
152
143
|
# Request is not rate limited, so add the current timestamp
|
153
144
|
if @configuration.rate_limiters[ip]
|
154
|
-
@configuration.rate_limiters[ip] << current_timestamp
|
155
|
-
else
|
145
|
+
@configuration.rate_limiters[ip] << current_timestamp
|
146
|
+
else
|
156
147
|
@configuration.rate_limiters[ip] = [current_timestamp]
|
157
148
|
end
|
158
|
-
|
149
|
+
|
159
150
|
return false
|
160
151
|
end
|
161
|
-
|
162
152
|
end
|
163
|
-
|
164
153
|
end
|
165
|
-
|
154
|
+
|
166
155
|
def send_upsync_requests(requests_array)
|
167
|
-
|
168
|
-
begin
|
169
|
-
|
170
|
-
headers = {'Content-Type' => 'application/json'}
|
156
|
+
headers = { "Content-Type" => "application/json" }
|
171
157
|
|
172
|
-
|
173
|
-
framework = "Rails v#{Rails::VERSION::STRING}"
|
174
|
-
else
|
175
|
-
framework = "Rack v#{Rack::VERSION::STRING}"
|
176
|
-
end
|
158
|
+
framework = defined?(Rails) ? "Rails v#{Rails::VERSION::STRING}" : "Rack v#{Rack.release}"
|
177
159
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
160
|
+
body = {
|
161
|
+
meta: {
|
162
|
+
version: Wafris::VERSION,
|
163
|
+
client: "wafris-rb",
|
164
|
+
framework: framework
|
165
|
+
},
|
166
|
+
batch: requests_array
|
167
|
+
}.to_json
|
186
168
|
|
187
|
-
|
169
|
+
url_and_api_key = @configuration.upsync_url + "/" + @configuration.api_key
|
188
170
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
171
|
+
response = HTTParty.post(
|
172
|
+
url_and_api_key,
|
173
|
+
body: body,
|
174
|
+
headers: headers,
|
175
|
+
timeout: 10
|
176
|
+
)
|
193
177
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
end
|
199
|
-
rescue HTTParty::Error => e
|
200
|
-
LogSuppressor.puts_log("Upsync Error. Failed to send upsync requests: #{e.message}")
|
178
|
+
if response.code == 200
|
179
|
+
@configuration.upsync_status = "Complete"
|
180
|
+
else
|
181
|
+
LogSuppressor.puts_log("Upsync Error. HTTP Response: #{response.code}")
|
201
182
|
end
|
202
|
-
|
183
|
+
rescue HTTParty::Error => e
|
184
|
+
LogSuppressor.puts_log("Upsync Error. Failed to send upsync requests: #{e.message}")
|
203
185
|
end
|
204
|
-
|
205
|
-
# This method is used to queue upsync requests. It takes in several parameters including
|
206
|
-
# ip, user_agent, path, parameters, host, method, treatment, category, and rule.
|
186
|
+
|
187
|
+
# This method is used to queue upsync requests. It takes in several parameters including:
|
207
188
|
#
|
208
|
-
# The 'treatment' parameter represents the action taken on the request, which can be
|
189
|
+
# The 'treatment' parameter represents the action taken on the request, which can be
|
209
190
|
# 'Allowed', 'Blocked', or 'Passed'.
|
210
191
|
#
|
211
|
-
# The 'category' parameter represents the category of the rule that was matched, such as
|
192
|
+
# The 'category' parameter represents the category of the rule that was matched, such as
|
212
193
|
# 'blocked_ip', 'allowed_cidr', etc.
|
213
194
|
#
|
214
|
-
# The 'rule' parameter represents the specific rule that was matched within the category
|
195
|
+
# The 'rule' parameter represents the specific rule that was matched within the category
|
215
196
|
# ex: '192.23.5.4', 'SemRush', etc.
|
216
|
-
def queue_upsync_request(
|
217
|
-
|
218
|
-
|
219
|
-
@configuration.upsync_status = 'Uploading'
|
197
|
+
def queue_upsync_request(request, treatment, category, rule)
|
198
|
+
if @configuration.upsync_status != "Disabled" || @configuration.upsync_status != "Uploading"
|
199
|
+
@configuration.upsync_status = "Uploading"
|
220
200
|
|
221
201
|
# Add request to the queue
|
222
|
-
|
223
|
-
|
224
|
-
|
202
|
+
@configuration.upsync_queue << request.data(treatment: treatment, category: category, rule: rule)
|
203
|
+
|
225
204
|
# If the queue is full, send the requests to the upsync server
|
226
205
|
if @configuration.upsync_queue.length >= @configuration.upsync_queue_limit || (Time.now.to_i - @configuration.last_upsync_timestamp) >= @configuration.upsync_interval
|
227
206
|
requests_array = @configuration.upsync_queue
|
228
207
|
@configuration.upsync_queue = []
|
229
208
|
@configuration.last_upsync_timestamp = Time.now.to_i
|
230
|
-
|
209
|
+
|
231
210
|
send_upsync_requests(requests_array)
|
232
211
|
end
|
233
|
-
|
234
|
-
@configuration.upsync_status =
|
212
|
+
|
213
|
+
@configuration.upsync_status = "Enabled"
|
235
214
|
# Return the treatment - used to return 403 or 200
|
236
215
|
|
237
216
|
message = "Request #{treatment}"
|
@@ -239,103 +218,97 @@ module Wafris
|
|
239
218
|
message += " | Rule: #{rule}" unless rule.blank?
|
240
219
|
LogSuppressor.puts_log(message)
|
241
220
|
|
242
|
-
|
221
|
+
treatment
|
243
222
|
else
|
244
|
-
@configuration.upsync_status =
|
245
|
-
|
223
|
+
@configuration.upsync_status = "Enabled"
|
224
|
+
"Passed"
|
246
225
|
end
|
247
|
-
|
248
226
|
end
|
249
|
-
|
227
|
+
|
250
228
|
# Pulls the latest rules from the server
|
251
229
|
def downsync_db(db_rule_category, current_filename = nil)
|
252
|
-
|
253
230
|
lockfile_path = "#{@configuration.db_file_path}/#{db_rule_category}.lockfile"
|
254
|
-
|
231
|
+
|
255
232
|
# Ensure the directory exists before attempting to open the lockfile
|
256
233
|
FileUtils.mkdir_p(@configuration.db_file_path) unless Dir.exist?(@configuration.db_file_path)
|
257
234
|
|
258
235
|
# Attempt to create a lockfile with exclusive access; skip if it exists
|
259
236
|
begin
|
260
|
-
lockfile = File.open(lockfile_path, File::RDWR|File::CREAT|File::EXCL)
|
237
|
+
lockfile = File.open(lockfile_path, File::RDWR | File::CREAT | File::EXCL)
|
261
238
|
rescue Errno::EEXIST
|
262
239
|
LogSuppressor.puts_log("[Wafris][Downsync] Lockfile already exists, skipping downsync.")
|
263
240
|
return
|
264
241
|
rescue Exception => e
|
265
242
|
LogSuppressor.puts_log("[Wafris][Downsync] Error creating lockfile: #{e.message}")
|
266
243
|
end
|
267
|
-
|
244
|
+
|
268
245
|
begin
|
269
246
|
# Actual Downsync operations
|
270
247
|
filename = ""
|
271
|
-
|
272
|
-
|
273
|
-
framework = "Rails v#{Rails::VERSION::STRING}"
|
274
|
-
else
|
275
|
-
framework = "Rack v#{Rack::VERSION::STRING}"
|
276
|
-
end
|
248
|
+
|
249
|
+
framework = defined?(Rails) ? "Rails v#{Rails::VERSION::STRING}" : "Rack v#{Rack.release}"
|
277
250
|
|
278
251
|
data = {
|
279
252
|
client_db: current_filename,
|
280
253
|
process_id: Process.pid,
|
281
254
|
hostname: Socket.gethostname,
|
282
255
|
version: Wafris::VERSION,
|
283
|
-
client:
|
256
|
+
client: "wafris-rb",
|
284
257
|
framework: framework
|
285
258
|
}
|
286
259
|
|
287
260
|
# Check server for new rules including process id
|
288
|
-
#puts "Downloading from #{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?current_version=#{current_filename}&process_id=#{Process.pid}"
|
261
|
+
# puts "Downloading from #{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?current_version=#{current_filename}&process_id=#{Process.pid}"
|
289
262
|
uri = "#{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?#{data.to_query}"
|
290
|
-
|
263
|
+
|
291
264
|
response = HTTParty.get(
|
292
265
|
uri,
|
293
266
|
follow_redirects: true, # Enable following redirects
|
294
267
|
max_redirects: 2 # Maximum number of redirects to follow
|
295
268
|
)
|
296
|
-
|
269
|
+
|
297
270
|
# TODO: What to do if timeout
|
298
|
-
# TODO: What to do if error
|
271
|
+
# TODO: What to do if error
|
299
272
|
|
300
273
|
if response.code == 401
|
301
|
-
@configuration.upsync_status =
|
274
|
+
@configuration.upsync_status = "Disabled"
|
302
275
|
LogSuppressor.puts_log("[Wafris][Downsync] Unauthorized: Bad or missing API key")
|
303
276
|
LogSuppressor.puts_log("[Wafris][Downsync] API Key: #{@configuration.api_key}")
|
304
277
|
filename = current_filename
|
305
|
-
|
278
|
+
|
306
279
|
elsif response.code == 304
|
307
|
-
@configuration.upsync_status =
|
280
|
+
@configuration.upsync_status = "Enabled"
|
308
281
|
LogSuppressor.puts_log("[Wafris][Downsync] No new rules to download")
|
309
|
-
|
282
|
+
|
310
283
|
filename = current_filename
|
311
|
-
|
284
|
+
|
312
285
|
elsif response.code == 200
|
313
|
-
@configuration.upsync_status =
|
314
|
-
|
286
|
+
@configuration.upsync_status = "Enabled"
|
287
|
+
|
315
288
|
if current_filename
|
316
289
|
old_file_name = current_filename
|
317
290
|
end
|
318
|
-
|
319
|
-
# Extract the filename from the response
|
320
|
-
content_disposition = response.headers[
|
321
|
-
filename = content_disposition.split(
|
322
|
-
|
323
|
-
# Save the body of the response to a new SQLite file
|
324
|
-
File.
|
325
|
-
|
291
|
+
|
292
|
+
# Extract the filename from the response
|
293
|
+
content_disposition = response.headers["content-disposition"]
|
294
|
+
filename = content_disposition.split("filename=")[1].strip
|
295
|
+
|
296
|
+
# Save the body of the response to a new SQLite file
|
297
|
+
File.binwrite(@configuration.db_file_path + "/" + filename, response.body)
|
298
|
+
|
326
299
|
# Write the filename into the db_category.modfile
|
327
|
-
File.
|
328
|
-
|
300
|
+
File.write("#{@configuration.db_file_path}/#{db_rule_category}.modfile", filename)
|
301
|
+
|
329
302
|
# Sanity check that the downloaded db file has tables
|
330
303
|
# not empty or corrupted
|
331
|
-
db = SQLite3::Database.new @configuration.db_file_path + "/" + filename
|
304
|
+
db = SQLite3::Database.new @configuration.db_file_path + "/" + filename
|
332
305
|
if db.execute("SELECT name FROM sqlite_master WHERE type='table';").any?
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
File.delete(@configuration.db_file_path + "/" + old_file_name)
|
337
|
-
end
|
306
|
+
# Remove the old database file
|
307
|
+
if old_file_name
|
308
|
+
if File.exist?(@configuration.db_file_path + "/" + old_file_name)
|
309
|
+
File.delete(@configuration.db_file_path + "/" + old_file_name)
|
338
310
|
end
|
311
|
+
end
|
339
312
|
|
340
313
|
# DB file is bad or empty so keep using whatever we have now
|
341
314
|
else
|
@@ -343,209 +316,163 @@ module Wafris
|
|
343
316
|
LogSuppressor.puts_log("[Wafris][Downsync] DB Error - No tables exist in the db file #{@configuration.db_file_path}/#{filename}")
|
344
317
|
end
|
345
318
|
|
346
|
-
|
347
319
|
end
|
348
|
-
|
349
|
-
rescue Exception => e
|
320
|
+
rescue => e
|
350
321
|
LogSuppressor.puts_log("[Wafris][Downsync] Error downloading rules: #{e.message}")
|
351
|
-
|
322
|
+
|
352
323
|
# This gets set even if the API key is bad or other issues
|
353
324
|
# to prevent hammering the distribution server on every request
|
354
325
|
ensure
|
355
|
-
|
356
326
|
# Reset the modified time of the modfile
|
357
327
|
unless File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
358
|
-
File.new("#{@configuration.db_file_path}/#{db_rule_category}.modfile",
|
328
|
+
File.new("#{@configuration.db_file_path}/#{db_rule_category}.modfile", "w")
|
359
329
|
end
|
360
|
-
|
330
|
+
|
361
331
|
# Set the modified time of the modfile to the current time
|
362
332
|
File.utime(Time.now, Time.now, "#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
363
|
-
|
333
|
+
|
364
334
|
# Ensure the lockfile is removed after operations
|
365
335
|
lockfile.close
|
366
336
|
File.delete(lockfile_path)
|
367
337
|
end
|
368
|
-
|
369
|
-
|
370
|
-
|
338
|
+
|
339
|
+
filename
|
371
340
|
end
|
372
|
-
|
373
|
-
# Returns the current database file,
|
341
|
+
|
342
|
+
# Returns the current database file,
|
374
343
|
# if the file is older than the interval, it will download the latest db
|
375
344
|
# if the file doesn't exist, it will download the latest db
|
376
345
|
# if the lockfile exists, it will return the current db
|
377
346
|
def current_db(db_rule_category)
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
347
|
+
interval = if db_rule_category == "custom_rules"
|
348
|
+
@configuration.downsync_custom_rules_interval
|
349
|
+
else
|
350
|
+
@configuration.downsync_data_subscriptions_interval
|
351
|
+
end
|
352
|
+
|
385
353
|
# Checks for existing current modfile, which contains the current db filename
|
386
354
|
if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
387
|
-
|
355
|
+
|
388
356
|
LogSuppressor.puts_log("[Wafris][Downsync] Modfile exists, skipping downsync")
|
389
357
|
|
390
358
|
# Get last Modified Time and current database file name
|
391
359
|
last_db_synctime = File.mtime("#{@configuration.db_file_path}/#{db_rule_category}.modfile").to_i
|
392
360
|
returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
|
393
|
-
|
361
|
+
|
394
362
|
LogSuppressor.puts_log("[Wafris][Downsync] Modfile Last Modified Time: #{last_db_synctime}")
|
395
|
-
LogSuppressor.puts_log("[Wafris][Downsync] DB in Modfile: #{returned_db}")
|
363
|
+
LogSuppressor.puts_log("[Wafris][Downsync] DB in Modfile: #{returned_db}")
|
396
364
|
|
397
|
-
# Check if the db file is older than the interval
|
365
|
+
# Check if the db file is older than the interval
|
398
366
|
if (Time.now.to_i - last_db_synctime) > interval
|
399
|
-
|
367
|
+
|
400
368
|
LogSuppressor.puts_log("[Wafris][Downsync] DB is older than the interval")
|
401
369
|
|
402
370
|
# Make sure that another process isn't already downloading the rules
|
403
371
|
if !File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
|
404
|
-
returned_db = downsync_db(db_rule_category, returned_db)
|
372
|
+
returned_db = downsync_db(db_rule_category, returned_db)
|
405
373
|
end
|
406
|
-
|
407
|
-
|
408
|
-
|
374
|
+
|
375
|
+
returned_db
|
376
|
+
|
409
377
|
# Current db is up to date
|
410
|
-
else
|
411
|
-
|
378
|
+
else
|
379
|
+
|
412
380
|
LogSuppressor.puts_log("[Wafris][Downsync] DB is up to date")
|
413
381
|
|
414
382
|
returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
|
415
|
-
|
383
|
+
|
416
384
|
# If the modfile is empty (no db file name), return nil
|
417
385
|
# this can happen if the the api key is bad
|
418
|
-
if returned_db ==
|
419
|
-
|
386
|
+
if returned_db == ""
|
387
|
+
""
|
420
388
|
else
|
421
|
-
|
389
|
+
returned_db
|
422
390
|
end
|
423
|
-
|
391
|
+
|
424
392
|
end
|
425
|
-
|
393
|
+
|
426
394
|
# No modfile exists, so download the latest db
|
427
|
-
else
|
428
|
-
|
395
|
+
else
|
396
|
+
|
429
397
|
LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
|
430
398
|
|
431
399
|
# Make sure that another process isn't already downloading the rules
|
432
400
|
if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
|
433
401
|
LogSuppressor.puts_log("[Wafris][Downsync] Lockfile exists, skipping downsync")
|
434
402
|
# Lockfile exists, but no modfile with a db filename
|
435
|
-
|
403
|
+
nil
|
436
404
|
else
|
437
|
-
|
405
|
+
|
438
406
|
LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
|
439
407
|
# No modfile exists, so download the latest db
|
440
408
|
returned_db = downsync_db(db_rule_category, nil)
|
441
|
-
|
409
|
+
|
442
410
|
if returned_db.nil?
|
443
|
-
|
444
|
-
else
|
445
|
-
|
411
|
+
nil
|
412
|
+
else
|
413
|
+
returned_db
|
446
414
|
end
|
447
|
-
|
448
415
|
end
|
449
|
-
|
450
|
-
end
|
451
|
-
|
416
|
+
end
|
452
417
|
end
|
453
|
-
|
418
|
+
|
454
419
|
# This is the main loop that evaluates the request
|
455
420
|
# as well as sorts out when downsync and upsync should be called
|
456
|
-
def evaluate(
|
457
|
-
|
421
|
+
def evaluate(request)
|
422
|
+
@configuration ||= Wafris::Configuration.new
|
458
423
|
|
459
|
-
|
460
|
-
return "Passed"
|
461
|
-
else
|
424
|
+
return "Passed" if @configuration.api_key.nil?
|
462
425
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
if rules_db_filename.to_s.strip != '' && data_subscriptions_db_filename.strip.to_s.strip != ''
|
467
|
-
|
468
|
-
rules_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{rules_db_filename}"
|
469
|
-
data_subscriptions_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{data_subscriptions_db_filename}"
|
470
|
-
|
471
|
-
# Allowed IPs
|
472
|
-
if exact_match(ip, 'allowed_ips', rules_db)
|
473
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ai', ip, request_id, request_timestamp)
|
474
|
-
end
|
475
|
-
|
476
|
-
# Allowed CIDR Ranges
|
477
|
-
if ip_in_cidr_range(ip, 'allowed_cidr_ranges', rules_db)
|
478
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ac', ip, request_id, request_timestamp)
|
479
|
-
end
|
480
|
-
|
481
|
-
# Blocked IPs
|
482
|
-
if exact_match(ip, 'blocked_ips', rules_db)
|
483
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bi', ip, request_id, request_timestamp)
|
484
|
-
end
|
485
|
-
|
486
|
-
# Blocked CIDR Ranges
|
487
|
-
if ip_in_cidr_range(ip, 'blocked_cidr_ranges', rules_db)
|
488
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bc', ip, request_id, request_timestamp)
|
489
|
-
end
|
490
|
-
|
491
|
-
# Blocked Country Codes
|
492
|
-
country_code = get_country_code(ip, data_subscriptions_db)
|
493
|
-
if exact_match(country_code, 'blocked_country_codes', rules_db)
|
494
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "G_#{country_code}", request_id, request_timestamp)
|
495
|
-
end
|
496
|
-
|
497
|
-
# Blocked Reputation IP Ranges
|
498
|
-
if ip_in_cidr_range(ip, 'reputation_ip_ranges', data_subscriptions_db)
|
499
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "R", request_id, request_timestamp)
|
500
|
-
end
|
501
|
-
|
502
|
-
# Blocked User Agents
|
503
|
-
user_agent_match = substring_match(user_agent, 'blocked_user_agents', rules_db)
|
504
|
-
if user_agent_match
|
505
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bu', user_agent_match, request_id, request_timestamp)
|
506
|
-
end
|
507
|
-
|
508
|
-
# Blocked Paths
|
509
|
-
path_match = substring_match(path, 'blocked_paths', rules_db)
|
510
|
-
if path_match
|
511
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bp', path_match, request_id, request_timestamp)
|
512
|
-
end
|
513
|
-
|
514
|
-
# Blocked Parameters
|
515
|
-
parameters_match = substring_match(parameters, 'blocked_parameters', rules_db)
|
516
|
-
if parameters_match
|
517
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'ba', parameters_match, request_id, request_timestamp)
|
518
|
-
end
|
519
|
-
|
520
|
-
# Blocked Hosts
|
521
|
-
if exact_match(host, 'blocked_hosts', rules_db)
|
522
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bh', host, request_id, request_timestamp)
|
523
|
-
end
|
524
|
-
|
525
|
-
# Blocked Methods
|
526
|
-
if exact_match(method, 'blocked_methods', rules_db)
|
527
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bm', method, request_id, request_timestamp)
|
528
|
-
end
|
529
|
-
|
530
|
-
# Rate Limiting
|
531
|
-
rule_id = check_rate_limit(ip, path, method, rules_db)
|
532
|
-
if rule_id
|
533
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'brl', rule_id, request_id, request_timestamp)
|
534
|
-
end
|
535
|
-
|
536
|
-
end
|
537
|
-
|
538
|
-
# Passed if no allow or block rules matched
|
539
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Passed', 'passed', '-', request_id, request_timestamp)
|
426
|
+
rules_db_filename = current_db("custom_rules")
|
427
|
+
data_subscriptions_db_filename = current_db("data_subscriptions")
|
540
428
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
429
|
+
# Checks to see if the filenames are present before loading the db
|
430
|
+
if rules_db_filename.to_s.strip != "" && data_subscriptions_db_filename.strip.to_s.strip != ""
|
431
|
+
|
432
|
+
rules_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{rules_db_filename}"
|
433
|
+
data_subscriptions_db =
|
434
|
+
SQLite3::Database.new "#{@configuration.db_file_path}/#{data_subscriptions_db_filename}"
|
435
|
+
|
436
|
+
ip = request.ip
|
437
|
+
return queue_upsync_request(request, "Allowed", "ai", ip) if exact_match(ip, "allowed_ips", rules_db)
|
438
|
+
return queue_upsync_request(request, "Allowed", "ac", ip) if ip_in_cidr_range(ip, "allowed_cidr_ranges", rules_db)
|
439
|
+
return queue_upsync_request(request, "Blocked", "bi", ip) if exact_match(ip, "blocked_ips", rules_db)
|
440
|
+
return queue_upsync_request(request, "Blocked", "bc", ip) if ip_in_cidr_range(ip, "blocked_cidr_ranges", rules_db)
|
441
|
+
|
442
|
+
country_code = get_country_code(ip, data_subscriptions_db)
|
443
|
+
return queue_upsync_request(request, "Blocked", "bs", "G_#{country_code}") if exact_match(country_code, "blocked_country_codes", rules_db)
|
545
444
|
|
546
|
-
|
445
|
+
# Blocked Reputation IP Ranges
|
446
|
+
return queue_upsync_request(request, "Blocked", "bs", "R") if ip_in_cidr_range(ip, "reputation_ip_ranges", data_subscriptions_db)
|
447
|
+
|
448
|
+
user_agent_match = substring_match(request.user_agent, "blocked_user_agents", rules_db)
|
449
|
+
return queue_upsync_request(request, "Blocked", "bu", user_agent_match) if user_agent_match
|
450
|
+
|
451
|
+
path_match = substring_match(request.path, "blocked_paths", rules_db)
|
452
|
+
return queue_upsync_request(request, "Blocked", "bp", path_match) if path_match
|
453
|
+
|
454
|
+
parameters_match = substring_match(request.parameters, "blocked_parameters", rules_db)
|
455
|
+
return queue_upsync_request(request, "Blocked", "ba", parameters_match) if parameters_match
|
456
|
+
|
457
|
+
return queue_upsync_request(request, "Blocked", "bh", request.host) if exact_match(request.host, "blocked_hosts", rules_db)
|
458
|
+
|
459
|
+
return queue_upsync_request(request, "Blocked", "bm", request.method) if exact_match(request.method, "blocked_methods", rules_db)
|
460
|
+
|
461
|
+
# Rate Limiting
|
462
|
+
rule_id = check_rate_limit(ip, request.path, request.method, rules_db)
|
463
|
+
if rule_id
|
464
|
+
return queue_upsync_request(request, "Blocked", "brl", rule_id)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
# Passed if no allow or block rules matched
|
469
|
+
queue_upsync_request(request, "Passed", "passed", "-")
|
470
|
+
end
|
471
|
+
|
472
|
+
def debug(api_key)
|
473
|
+
if ENV["WAFRIS_API_KEY"]
|
547
474
|
puts "Wafris API Key environment variable is set."
|
548
|
-
puts " - API Key: #{ENV[
|
475
|
+
puts " - API Key: #{ENV["WAFRIS_API_KEY"]}"
|
549
476
|
else
|
550
477
|
puts "Wafris API Key environment variable is not set."
|
551
478
|
end
|
@@ -575,7 +502,7 @@ module Wafris
|
|
575
502
|
if File.exist?(settings.db_file_path + "/" + "custom_rules.modfile")
|
576
503
|
puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile exists"
|
577
504
|
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "custom_rules.modfile")}"
|
578
|
-
puts " - Contents: #{File.
|
505
|
+
puts " - Contents: #{File.read(settings.db_file_path + "/" + "custom_rules.modfile")}"
|
579
506
|
else
|
580
507
|
puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile does not exist."
|
581
508
|
end
|
@@ -592,15 +519,12 @@ module Wafris
|
|
592
519
|
if File.exist?(settings.db_file_path + "/" + "data_subscriptions.modfile")
|
593
520
|
puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile exists"
|
594
521
|
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "data_subscriptions.modfile")}"
|
595
|
-
puts " - Contents: #{File.
|
522
|
+
puts " - Contents: #{File.read(settings.db_file_path + "/" + "data_subscriptions.modfile")}"
|
596
523
|
else
|
597
524
|
puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile does not exist."
|
598
525
|
end
|
599
526
|
|
600
|
-
|
601
|
-
|
602
|
-
return true
|
527
|
+
true
|
603
528
|
end
|
604
|
-
|
605
529
|
end
|
606
530
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wafris
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Buckbee
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-10-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '2.0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
29
|
+
name: awesome_print
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - ">="
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
43
|
+
name: httparty
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - ">="
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
57
|
+
name: ipaddr
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
60
|
- - ">="
|
@@ -68,7 +68,7 @@ dependencies:
|
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
71
|
+
name: sqlite3
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
74
|
- - ">="
|
@@ -95,6 +95,20 @@ dependencies:
|
|
95
95
|
- - "~>"
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: '5.1'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: mocha
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 2.4.5
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 2.4.5
|
98
112
|
- !ruby/object:Gem::Dependency
|
99
113
|
name: pry
|
100
114
|
requirement: !ruby/object:Gem::Requirement
|