wafris 2.0.3 → 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 +25 -7
- data/lib/wafris.rb +213 -287
- 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,25 +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
|
-
return
|
48
|
+
return "" if value.nil?
|
31
49
|
|
32
|
-
value&.dup&.force_encoding(
|
50
|
+
value&.dup&.force_encoding("UTF-8")
|
33
51
|
end
|
34
52
|
end
|
35
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,99 +117,89 @@ 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
|
-
headers = {'Content-Type' => 'application/json'}
|
156
|
+
headers = { "Content-Type" => "application/json" }
|
169
157
|
|
170
|
-
|
171
|
-
framework = "Rails v#{Rails::VERSION::STRING}"
|
172
|
-
else
|
173
|
-
framework = "Rack v#{Rack::VERSION::STRING}"
|
174
|
-
end
|
158
|
+
framework = defined?(Rails) ? "Rails v#{Rails::VERSION::STRING}" : "Rack v#{Rack.release}"
|
175
159
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
160
|
+
body = {
|
161
|
+
meta: {
|
162
|
+
version: Wafris::VERSION,
|
163
|
+
client: "wafris-rb",
|
164
|
+
framework: framework
|
165
|
+
},
|
166
|
+
batch: requests_array
|
167
|
+
}.to_json
|
184
168
|
|
185
|
-
|
169
|
+
url_and_api_key = @configuration.upsync_url + "/" + @configuration.api_key
|
186
170
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
171
|
+
response = HTTParty.post(
|
172
|
+
url_and_api_key,
|
173
|
+
body: body,
|
174
|
+
headers: headers,
|
175
|
+
timeout: 10
|
176
|
+
)
|
191
177
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
197
|
-
rescue HTTParty::Error => e
|
198
|
-
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}")
|
199
182
|
end
|
200
|
-
|
183
|
+
rescue HTTParty::Error => e
|
184
|
+
LogSuppressor.puts_log("Upsync Error. Failed to send upsync requests: #{e.message}")
|
201
185
|
end
|
202
|
-
|
203
|
-
# This method is used to queue upsync requests. It takes in several parameters including
|
204
|
-
# 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:
|
205
188
|
#
|
206
|
-
# 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
|
207
190
|
# 'Allowed', 'Blocked', or 'Passed'.
|
208
191
|
#
|
209
|
-
# 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
|
210
193
|
# 'blocked_ip', 'allowed_cidr', etc.
|
211
194
|
#
|
212
|
-
# 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
|
213
196
|
# ex: '192.23.5.4', 'SemRush', etc.
|
214
|
-
def queue_upsync_request(
|
215
|
-
if @configuration.upsync_status !=
|
216
|
-
@configuration.upsync_status =
|
197
|
+
def queue_upsync_request(request, treatment, category, rule)
|
198
|
+
if @configuration.upsync_status != "Disabled" || @configuration.upsync_status != "Uploading"
|
199
|
+
@configuration.upsync_status = "Uploading"
|
217
200
|
|
218
201
|
# Add request to the queue
|
219
|
-
|
220
|
-
@configuration.upsync_queue << request
|
202
|
+
@configuration.upsync_queue << request.data(treatment: treatment, category: category, rule: rule)
|
221
203
|
|
222
204
|
# If the queue is full, send the requests to the upsync server
|
223
205
|
if @configuration.upsync_queue.length >= @configuration.upsync_queue_limit || (Time.now.to_i - @configuration.last_upsync_timestamp) >= @configuration.upsync_interval
|
@@ -228,7 +210,7 @@ module Wafris
|
|
228
210
|
send_upsync_requests(requests_array)
|
229
211
|
end
|
230
212
|
|
231
|
-
@configuration.upsync_status =
|
213
|
+
@configuration.upsync_status = "Enabled"
|
232
214
|
# Return the treatment - used to return 403 or 200
|
233
215
|
|
234
216
|
message = "Request #{treatment}"
|
@@ -236,103 +218,97 @@ module Wafris
|
|
236
218
|
message += " | Rule: #{rule}" unless rule.blank?
|
237
219
|
LogSuppressor.puts_log(message)
|
238
220
|
|
239
|
-
|
221
|
+
treatment
|
240
222
|
else
|
241
|
-
@configuration.upsync_status =
|
242
|
-
|
223
|
+
@configuration.upsync_status = "Enabled"
|
224
|
+
"Passed"
|
243
225
|
end
|
244
|
-
|
245
226
|
end
|
246
|
-
|
227
|
+
|
247
228
|
# Pulls the latest rules from the server
|
248
229
|
def downsync_db(db_rule_category, current_filename = nil)
|
249
|
-
|
250
230
|
lockfile_path = "#{@configuration.db_file_path}/#{db_rule_category}.lockfile"
|
251
|
-
|
231
|
+
|
252
232
|
# Ensure the directory exists before attempting to open the lockfile
|
253
233
|
FileUtils.mkdir_p(@configuration.db_file_path) unless Dir.exist?(@configuration.db_file_path)
|
254
234
|
|
255
235
|
# Attempt to create a lockfile with exclusive access; skip if it exists
|
256
236
|
begin
|
257
|
-
lockfile = File.open(lockfile_path, File::RDWR|File::CREAT|File::EXCL)
|
237
|
+
lockfile = File.open(lockfile_path, File::RDWR | File::CREAT | File::EXCL)
|
258
238
|
rescue Errno::EEXIST
|
259
239
|
LogSuppressor.puts_log("[Wafris][Downsync] Lockfile already exists, skipping downsync.")
|
260
240
|
return
|
261
241
|
rescue Exception => e
|
262
242
|
LogSuppressor.puts_log("[Wafris][Downsync] Error creating lockfile: #{e.message}")
|
263
243
|
end
|
264
|
-
|
244
|
+
|
265
245
|
begin
|
266
246
|
# Actual Downsync operations
|
267
247
|
filename = ""
|
268
|
-
|
269
|
-
|
270
|
-
framework = "Rails v#{Rails::VERSION::STRING}"
|
271
|
-
else
|
272
|
-
framework = "Rack v#{Rack::VERSION::STRING}"
|
273
|
-
end
|
248
|
+
|
249
|
+
framework = defined?(Rails) ? "Rails v#{Rails::VERSION::STRING}" : "Rack v#{Rack.release}"
|
274
250
|
|
275
251
|
data = {
|
276
252
|
client_db: current_filename,
|
277
253
|
process_id: Process.pid,
|
278
254
|
hostname: Socket.gethostname,
|
279
255
|
version: Wafris::VERSION,
|
280
|
-
client:
|
256
|
+
client: "wafris-rb",
|
281
257
|
framework: framework
|
282
258
|
}
|
283
259
|
|
284
260
|
# Check server for new rules including process id
|
285
|
-
#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}"
|
286
262
|
uri = "#{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?#{data.to_query}"
|
287
|
-
|
263
|
+
|
288
264
|
response = HTTParty.get(
|
289
265
|
uri,
|
290
266
|
follow_redirects: true, # Enable following redirects
|
291
267
|
max_redirects: 2 # Maximum number of redirects to follow
|
292
268
|
)
|
293
|
-
|
269
|
+
|
294
270
|
# TODO: What to do if timeout
|
295
|
-
# TODO: What to do if error
|
271
|
+
# TODO: What to do if error
|
296
272
|
|
297
273
|
if response.code == 401
|
298
|
-
@configuration.upsync_status =
|
274
|
+
@configuration.upsync_status = "Disabled"
|
299
275
|
LogSuppressor.puts_log("[Wafris][Downsync] Unauthorized: Bad or missing API key")
|
300
276
|
LogSuppressor.puts_log("[Wafris][Downsync] API Key: #{@configuration.api_key}")
|
301
277
|
filename = current_filename
|
302
|
-
|
278
|
+
|
303
279
|
elsif response.code == 304
|
304
|
-
@configuration.upsync_status =
|
280
|
+
@configuration.upsync_status = "Enabled"
|
305
281
|
LogSuppressor.puts_log("[Wafris][Downsync] No new rules to download")
|
306
|
-
|
282
|
+
|
307
283
|
filename = current_filename
|
308
|
-
|
284
|
+
|
309
285
|
elsif response.code == 200
|
310
|
-
@configuration.upsync_status =
|
311
|
-
|
286
|
+
@configuration.upsync_status = "Enabled"
|
287
|
+
|
312
288
|
if current_filename
|
313
289
|
old_file_name = current_filename
|
314
290
|
end
|
315
|
-
|
316
|
-
# Extract the filename from the response
|
317
|
-
content_disposition = response.headers[
|
318
|
-
filename = content_disposition.split(
|
319
|
-
|
320
|
-
# Save the body of the response to a new SQLite file
|
321
|
-
File.
|
322
|
-
|
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
|
+
|
323
299
|
# Write the filename into the db_category.modfile
|
324
|
-
File.
|
325
|
-
|
300
|
+
File.write("#{@configuration.db_file_path}/#{db_rule_category}.modfile", filename)
|
301
|
+
|
326
302
|
# Sanity check that the downloaded db file has tables
|
327
303
|
# not empty or corrupted
|
328
|
-
db = SQLite3::Database.new @configuration.db_file_path + "/" + filename
|
304
|
+
db = SQLite3::Database.new @configuration.db_file_path + "/" + filename
|
329
305
|
if db.execute("SELECT name FROM sqlite_master WHERE type='table';").any?
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
File.delete(@configuration.db_file_path + "/" + old_file_name)
|
334
|
-
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)
|
335
310
|
end
|
311
|
+
end
|
336
312
|
|
337
313
|
# DB file is bad or empty so keep using whatever we have now
|
338
314
|
else
|
@@ -340,210 +316,163 @@ module Wafris
|
|
340
316
|
LogSuppressor.puts_log("[Wafris][Downsync] DB Error - No tables exist in the db file #{@configuration.db_file_path}/#{filename}")
|
341
317
|
end
|
342
318
|
|
343
|
-
|
344
319
|
end
|
345
|
-
|
346
|
-
rescue Exception => e
|
320
|
+
rescue => e
|
347
321
|
LogSuppressor.puts_log("[Wafris][Downsync] Error downloading rules: #{e.message}")
|
348
|
-
|
322
|
+
|
349
323
|
# This gets set even if the API key is bad or other issues
|
350
324
|
# to prevent hammering the distribution server on every request
|
351
325
|
ensure
|
352
|
-
|
353
326
|
# Reset the modified time of the modfile
|
354
327
|
unless File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
355
|
-
File.new("#{@configuration.db_file_path}/#{db_rule_category}.modfile",
|
328
|
+
File.new("#{@configuration.db_file_path}/#{db_rule_category}.modfile", "w")
|
356
329
|
end
|
357
|
-
|
330
|
+
|
358
331
|
# Set the modified time of the modfile to the current time
|
359
332
|
File.utime(Time.now, Time.now, "#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
360
|
-
|
333
|
+
|
361
334
|
# Ensure the lockfile is removed after operations
|
362
335
|
lockfile.close
|
363
336
|
File.delete(lockfile_path)
|
364
337
|
end
|
365
|
-
|
366
|
-
|
367
|
-
|
338
|
+
|
339
|
+
filename
|
368
340
|
end
|
369
|
-
|
370
|
-
# Returns the current database file,
|
341
|
+
|
342
|
+
# Returns the current database file,
|
371
343
|
# if the file is older than the interval, it will download the latest db
|
372
344
|
# if the file doesn't exist, it will download the latest db
|
373
345
|
# if the lockfile exists, it will return the current db
|
374
346
|
def current_db(db_rule_category)
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
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
|
+
|
382
353
|
# Checks for existing current modfile, which contains the current db filename
|
383
354
|
if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
384
|
-
|
355
|
+
|
385
356
|
LogSuppressor.puts_log("[Wafris][Downsync] Modfile exists, skipping downsync")
|
386
357
|
|
387
358
|
# Get last Modified Time and current database file name
|
388
359
|
last_db_synctime = File.mtime("#{@configuration.db_file_path}/#{db_rule_category}.modfile").to_i
|
389
360
|
returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
|
390
|
-
|
361
|
+
|
391
362
|
LogSuppressor.puts_log("[Wafris][Downsync] Modfile Last Modified Time: #{last_db_synctime}")
|
392
|
-
LogSuppressor.puts_log("[Wafris][Downsync] DB in Modfile: #{returned_db}")
|
363
|
+
LogSuppressor.puts_log("[Wafris][Downsync] DB in Modfile: #{returned_db}")
|
393
364
|
|
394
|
-
# Check if the db file is older than the interval
|
365
|
+
# Check if the db file is older than the interval
|
395
366
|
if (Time.now.to_i - last_db_synctime) > interval
|
396
|
-
|
367
|
+
|
397
368
|
LogSuppressor.puts_log("[Wafris][Downsync] DB is older than the interval")
|
398
369
|
|
399
370
|
# Make sure that another process isn't already downloading the rules
|
400
371
|
if !File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
|
401
|
-
returned_db = downsync_db(db_rule_category, returned_db)
|
372
|
+
returned_db = downsync_db(db_rule_category, returned_db)
|
402
373
|
end
|
403
|
-
|
404
|
-
|
405
|
-
|
374
|
+
|
375
|
+
returned_db
|
376
|
+
|
406
377
|
# Current db is up to date
|
407
|
-
else
|
408
|
-
|
378
|
+
else
|
379
|
+
|
409
380
|
LogSuppressor.puts_log("[Wafris][Downsync] DB is up to date")
|
410
381
|
|
411
382
|
returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
|
412
|
-
|
383
|
+
|
413
384
|
# If the modfile is empty (no db file name), return nil
|
414
385
|
# this can happen if the the api key is bad
|
415
|
-
if returned_db ==
|
416
|
-
|
386
|
+
if returned_db == ""
|
387
|
+
""
|
417
388
|
else
|
418
|
-
|
389
|
+
returned_db
|
419
390
|
end
|
420
|
-
|
391
|
+
|
421
392
|
end
|
422
|
-
|
393
|
+
|
423
394
|
# No modfile exists, so download the latest db
|
424
|
-
else
|
425
|
-
|
395
|
+
else
|
396
|
+
|
426
397
|
LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
|
427
398
|
|
428
399
|
# Make sure that another process isn't already downloading the rules
|
429
400
|
if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
|
430
401
|
LogSuppressor.puts_log("[Wafris][Downsync] Lockfile exists, skipping downsync")
|
431
402
|
# Lockfile exists, but no modfile with a db filename
|
432
|
-
|
403
|
+
nil
|
433
404
|
else
|
434
|
-
|
405
|
+
|
435
406
|
LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
|
436
407
|
# No modfile exists, so download the latest db
|
437
408
|
returned_db = downsync_db(db_rule_category, nil)
|
438
|
-
|
409
|
+
|
439
410
|
if returned_db.nil?
|
440
|
-
|
441
|
-
else
|
442
|
-
|
411
|
+
nil
|
412
|
+
else
|
413
|
+
returned_db
|
443
414
|
end
|
444
|
-
|
445
415
|
end
|
446
|
-
|
447
|
-
end
|
448
|
-
|
416
|
+
end
|
449
417
|
end
|
450
|
-
|
418
|
+
|
451
419
|
# This is the main loop that evaluates the request
|
452
420
|
# as well as sorts out when downsync and upsync should be called
|
453
|
-
def evaluate(
|
421
|
+
def evaluate(request)
|
454
422
|
@configuration ||= Wafris::Configuration.new
|
455
423
|
|
456
|
-
if @configuration.api_key.nil?
|
457
|
-
return "Passed"
|
458
|
-
else
|
459
|
-
rules_db_filename = current_db('custom_rules')
|
460
|
-
data_subscriptions_db_filename = current_db('data_subscriptions')
|
424
|
+
return "Passed" if @configuration.api_key.nil?
|
461
425
|
|
462
|
-
|
426
|
+
rules_db_filename = current_db("custom_rules")
|
427
|
+
data_subscriptions_db_filename = current_db("data_subscriptions")
|
463
428
|
|
464
|
-
|
465
|
-
|
466
|
-
SQLite3::Database.new "#{@configuration.db_file_path}/#{data_subscriptions_db_filename}"
|
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 != ""
|
467
431
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
ip, request_id, request_timestamp)
|
472
|
-
end
|
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}"
|
473
435
|
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
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)
|
478
441
|
|
479
|
-
|
480
|
-
|
481
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bi', ip, request_id, request_timestamp)
|
482
|
-
end
|
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)
|
483
444
|
|
484
|
-
|
485
|
-
|
486
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bc', ip, request_id, request_timestamp)
|
487
|
-
end
|
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)
|
488
447
|
|
489
|
-
|
490
|
-
|
491
|
-
if exact_match(country_code, 'blocked_country_codes', rules_db)
|
492
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "G_#{country_code}", request_id, request_timestamp)
|
493
|
-
end
|
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
|
494
450
|
|
495
|
-
|
496
|
-
|
497
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "R", request_id, request_timestamp)
|
498
|
-
end
|
451
|
+
path_match = substring_match(request.path, "blocked_paths", rules_db)
|
452
|
+
return queue_upsync_request(request, "Blocked", "bp", path_match) if path_match
|
499
453
|
|
500
|
-
|
501
|
-
|
502
|
-
if user_agent_match
|
503
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bu', user_agent_match, request_id, request_timestamp)
|
504
|
-
end
|
454
|
+
parameters_match = substring_match(request.parameters, "blocked_parameters", rules_db)
|
455
|
+
return queue_upsync_request(request, "Blocked", "ba", parameters_match) if parameters_match
|
505
456
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
if parameters_match
|
515
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'ba', parameters_match, request_id, request_timestamp)
|
516
|
-
end
|
517
|
-
|
518
|
-
# Blocked Hosts
|
519
|
-
if exact_match(host, 'blocked_hosts', rules_db)
|
520
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bh', host, request_id, request_timestamp)
|
521
|
-
end
|
522
|
-
|
523
|
-
# Blocked Methods
|
524
|
-
if exact_match(method, 'blocked_methods', rules_db)
|
525
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bm', method, request_id, request_timestamp)
|
526
|
-
end
|
527
|
-
|
528
|
-
# Rate Limiting
|
529
|
-
rule_id = check_rate_limit(ip, path, method, rules_db)
|
530
|
-
if rule_id
|
531
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'brl', rule_id, request_id, request_timestamp)
|
532
|
-
end
|
533
|
-
|
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)
|
534
465
|
end
|
535
|
-
|
536
|
-
# Passed if no allow or block rules matched
|
537
|
-
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Passed', 'passed', '-', request_id, request_timestamp)
|
466
|
+
end
|
538
467
|
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
def debug(api_key)
|
468
|
+
# Passed if no allow or block rules matched
|
469
|
+
queue_upsync_request(request, "Passed", "passed", "-")
|
470
|
+
end
|
543
471
|
|
544
|
-
|
472
|
+
def debug(api_key)
|
473
|
+
if ENV["WAFRIS_API_KEY"]
|
545
474
|
puts "Wafris API Key environment variable is set."
|
546
|
-
puts " - API Key: #{ENV[
|
475
|
+
puts " - API Key: #{ENV["WAFRIS_API_KEY"]}"
|
547
476
|
else
|
548
477
|
puts "Wafris API Key environment variable is not set."
|
549
478
|
end
|
@@ -573,7 +502,7 @@ module Wafris
|
|
573
502
|
if File.exist?(settings.db_file_path + "/" + "custom_rules.modfile")
|
574
503
|
puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile exists"
|
575
504
|
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "custom_rules.modfile")}"
|
576
|
-
puts " - Contents: #{File.
|
505
|
+
puts " - Contents: #{File.read(settings.db_file_path + "/" + "custom_rules.modfile")}"
|
577
506
|
else
|
578
507
|
puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile does not exist."
|
579
508
|
end
|
@@ -590,15 +519,12 @@ module Wafris
|
|
590
519
|
if File.exist?(settings.db_file_path + "/" + "data_subscriptions.modfile")
|
591
520
|
puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile exists"
|
592
521
|
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "data_subscriptions.modfile")}"
|
593
|
-
puts " - Contents: #{File.
|
522
|
+
puts " - Contents: #{File.read(settings.db_file_path + "/" + "data_subscriptions.modfile")}"
|
594
523
|
else
|
595
524
|
puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile does not exist."
|
596
525
|
end
|
597
526
|
|
598
|
-
|
599
|
-
|
600
|
-
return true
|
527
|
+
true
|
601
528
|
end
|
602
|
-
|
603
529
|
end
|
604
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-10-
|
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
|