wafris 2.0.2 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fab800ba280f295d31d26326cd24af8b30585db667f0cd26cb0b40e78e23dc34
4
- data.tar.gz: 134f0b73d8cabfa0cd889974301d843e8c4341dc18ee76c23401af0adf313bae
3
+ metadata.gz: 531133646bfb1df1b2b61c250179cd46de9e7986bc05075e509bededcd24bfbc
4
+ data.tar.gz: 4971a237b63f18b5f50d95751fd18ed51f4aa863fcd89da97be46c77176b31e8
5
5
  SHA512:
6
- metadata.gz: 617ee93c06d6b634a78a8a2f03e02d9de5df6916ace984a2efaf791b6635e2e3b4095acc9fc67d3b40200acfb0948502f22ecb1a4f55fcfbd2c26fcb2b2e7565
7
- data.tar.gz: af26e184475c1d8fb2683cbb4f02dc1b3f08a8f7984c7ac62609cf34cc0721d1894fd82efd49df9a9dd6b3ff08f6a43035ac566d4ad0f13df776c9f1f0e6ec45
6
+ metadata.gz: 835a0b2ff984d50e6de308db53dacfba7490a7d41a5ae426b26347a23b862e05ea5f0a0250e31d66a0a952335e083ce16843476c1e98004f62131a4c7d52349c
7
+ data.tar.gz: 2e9df9bdf15d055730171a1da8126e59adb754ba1d033949739fc2a3e5b001c5e58144d4453a84c98922bb93faac59fc911c49f430aa95f582d9511958b02f3d
@@ -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['WAFRIS_API_KEY']
27
- @api_key = ENV['WAFRIS_API_KEY']
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
- if ENV['WAFRIS_DB_FILE_PATH']
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
- if ENV['WAFRIS_DB_FILE_NAME']
50
- @db_file_name = ENV['WAFRIS_DB_FILE_NAME']
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
- if ENV['WAFRIS_DOWNSYNC_CUSTOM_RULES_INTERVAL']
58
- @downsync_custom_rules_interval = ENV['WAFRIS_DOWNSYNC_CUSTOM_RULES_INTERVAL'].to_i
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
- if ENV['WAFRIS_DOWNSYNC_DATA_SUBSCRIPTIONS_INTERVAL']
65
- @downsync_data_subscriptions_interval = ENV['WAFRIS_DOWNSYNC_DATA_SUBSCRIPTIONS_INTERVAL'].to_i
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
- if ENV['WAFRIS_DOWNSYNC_URL']
73
- @downsync_url = ENV['WAFRIS_DOWNSYNC_URL']
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
- if ENV['WAFRIS_UPSYNC_URL']
81
- @upsync_url = ENV['WAFRIS_UPSYNC_URL']
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
- if ENV['WAFRIS_UPSYNC_INTERVAL']
88
- @upsync_interval = ENV['WAFRIS_UPSYNC_INTERVAL'].to_i
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
- if ENV['WAFRIS_UPSYNC_QUEUE_LIMIT']
95
- @upsync_queue_limit = ENV['WAFRIS_UPSYNC_QUEUE_LIMIT'].to_i
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['WAFRIS_MAX_BODY_SIZE_MB'] && ENV['WAFRIS_MAX_BODY_SIZE_MB'].to_i > 0
102
- @max_body_size_mb = ENV['WAFRIS_MAX_BODY_SIZE_MB'].to_i
103
- else
104
- @max_body_size_mb = 10
105
- end
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 = 'Disabled'
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
- return output
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
- (ENV['WAFRIS_LOG_LEVEL'] && ENV['WAFRIS_LOG_LEVEL'] == 'silent')
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
- ['test'] + (ENV['CI'] ? ['CI'] : [])
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['RACK_ENV'] || 'development'
22
+ ENV["RACK_ENV"] || "development"
23
23
  end
24
24
  end
25
25
  end
@@ -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
- wafris_request.ip,
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 == 'Allowed' || treatment == 'Passed'
18
+ if treatment == "Allowed" || treatment == "Passed"
29
19
  @app.call(env)
30
- elsif treatment == 'Blocked'
31
- [403, { 'content-type' => 'text/plain' }, ['Blocked']]
20
+ elsif treatment == "Blocked"
21
+ [403, {"content-type" => "text/plain"}, ["Blocked"]]
32
22
  else
33
- #ap request
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)
@@ -3,7 +3,7 @@
3
3
  module Wafris
4
4
  module ProxyFilter
5
5
  def self.set_filter
6
- user_defined_proxies = ENV['TRUSTED_PROXY_RANGES'].split(',') if ENV['TRUSTED_PROXY_RANGES']
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Wafris
4
4
  class Railtie < ::Rails::Railtie
5
- initializer 'wafris.middleware' do |app|
5
+ initializer "wafris.middleware" do |app|
6
6
  app.middleware.use(Wafris::Middleware)
7
7
  end
8
8
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wafris
4
- VERSION = "2.0.2"
4
+ VERSION = "2.0.4"
5
5
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Wafris
4
4
  class WafrisRequest
5
- attr_reader :ip, :user_agent, :path, :parameters, :host, :request_method,
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
- @request_method = encode_to_utf8(request.request_method)
14
+ @method = encode_to_utf8(request.request_method)
15
15
  @headers = extract_headers(env)
16
- @body = encode_to_utf8(request.body&.string)
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?('HTTP_')
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&.dup&.force_encoding('UTF-8')
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 'rails'
4
- require 'sqlite3'
5
- require 'ipaddr'
6
- require 'httparty'
7
- require 'awesome_print'
3
+ require "rails"
4
+ require "sqlite3"
5
+ require "ipaddr"
6
+ require "httparty"
7
+ require "awesome_print"
8
8
 
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'
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 'wafris/railtie' if defined?(Rails::Railtie)
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
- begin
24
- self.configuration ||= Wafris::Configuration.new
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
- return nil
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
- return lower_compare
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, 'country_ip_ranges', db_connection)
81
-
74
+ country_code = ip_in_cidr_range(ip, "country_ip_ranges", db_connection)
75
+
82
76
  if country_code
83
- country_code = country_code.split("_").first.split("G").last
84
- return country_code
85
- else
86
- return "ZZ"
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
- return false
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
- return result.any?
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
- if Rails && Rails.application
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
- body = {
179
- meta: {
180
- version: Wafris::VERSION,
181
- client: 'wafris-rb',
182
- framework: framework
183
- },
184
- batch: requests_array
185
- }.to_json
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
- url_and_api_key = @configuration.upsync_url + '/' + @configuration.api_key
169
+ url_and_api_key = @configuration.upsync_url + "/" + @configuration.api_key
188
170
 
189
- response = HTTParty.post(url_and_api_key,
190
- :body => body,
191
- :headers => headers,
192
- :timeout => 300)
171
+ response = HTTParty.post(
172
+ url_and_api_key,
173
+ body: body,
174
+ headers: headers,
175
+ timeout: 10
176
+ )
193
177
 
194
- if response.code == 200
195
- @configuration.upsync_status = 'Complete'
196
- else
197
- LogSuppressor.puts_log("Upsync Error. HTTP Response: #{response.code}")
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
- return true
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(ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp)
217
-
218
- if @configuration.upsync_status != 'Disabled' || @configuration.upsync_status != 'Uploading'
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
- request = [ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp]
223
- @configuration.upsync_queue << request
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 = 'Enabled'
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
- return treatment
221
+ treatment
243
222
  else
244
- @configuration.upsync_status = 'Enabled'
245
- return "Passed"
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
- if Rails && Rails.application
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: 'wafris-rb',
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 = 'Disabled'
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 = 'Enabled'
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 = 'Enabled'
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['content-disposition']
321
- filename = content_disposition.split('filename=')[1].strip
322
-
323
- # Save the body of the response to a new SQLite file
324
- File.open(@configuration.db_file_path + "/" + filename, 'wb') { |file| file.write(response.body) }
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.open("#{@configuration.db_file_path}/#{db_rule_category}.modfile", 'w') { |file| file.write(filename) }
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
- # Remove the old database file
334
- if old_file_name
335
- if File.exist?(@configuration.db_file_path + "/" + old_file_name)
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", 'w')
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
- return filename
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
- if db_rule_category == 'custom_rules'
380
- interval = @configuration.downsync_custom_rules_interval
381
- else
382
- interval = @configuration.downsync_data_subscriptions_interval
383
- end
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
- return returned_db
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
- return ''
386
+ if returned_db == ""
387
+ ""
420
388
  else
421
- return returned_db
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
- return nil
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
- return nil
444
- else
445
- return returned_db
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(ip, user_agent, path, parameters, host, method, headers, body, request_id, request_timestamp)
457
- @configuration ||= Wafris::Configuration.new
421
+ def evaluate(request)
422
+ @configuration ||= Wafris::Configuration.new
458
423
 
459
- if @configuration.api_key.nil?
460
- return "Passed"
461
- else
424
+ return "Passed" if @configuration.api_key.nil?
462
425
 
463
- rules_db_filename = current_db('custom_rules')
464
- data_subscriptions_db_filename = current_db('data_subscriptions')
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
- end # end api_key.nil?
542
- end # end evaluate
543
-
544
- def debug(api_key)
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
- if ENV['WAFRIS_API_KEY']
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['WAFRIS_API_KEY']}"
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.open(settings.db_file_path + "/" + "custom_rules.modfile", 'r').read}"
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.open(settings.db_file_path + "/" + "data_subscriptions.modfile", 'r').read}"
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.2
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-09-21 00:00:00.000000000 Z
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: sqlite3
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: ipaddr
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: httparty
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: awesome_print
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