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 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