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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b12f70edd52fc81916790101bddabe8e450b1d97683bc115be5737e5a4d3562a
4
- data.tar.gz: cf42cbb5eb83750ad066b4762e3b42fead8c02ea3f7c18b6494c0bef1b0c6a48
3
+ metadata.gz: 531133646bfb1df1b2b61c250179cd46de9e7986bc05075e509bededcd24bfbc
4
+ data.tar.gz: 4971a237b63f18b5f50d95751fd18ed51f4aa863fcd89da97be46c77176b31e8
5
5
  SHA512:
6
- metadata.gz: 6839912471ef9957061996a9376119f907cf6d99f5d6d92a48c08d57a0c7f106f1d3234b9be431ad6b53b99d6e026bbc614d4fe47398738b48cf935b507b8616
7
- data.tar.gz: eca82696939268ef17064912d3d072686ebd01eac5d1a7aa32e35ca2f72c8d7073176c58cc9010a4d308b2113273c43b9f9b1af8a56b5ad559cbc837b3b3092c
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.3"
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,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
- @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
- return '' if value.nil?
48
+ return "" if value.nil?
31
49
 
32
- value&.dup&.force_encoding('UTF-8')
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 '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,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
- begin
168
- headers = {'Content-Type' => 'application/json'}
156
+ headers = { "Content-Type" => "application/json" }
169
157
 
170
- if Rails && Rails.application
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
- body = {
177
- meta: {
178
- version: Wafris::VERSION,
179
- client: 'wafris-rb',
180
- framework: framework
181
- },
182
- batch: requests_array
183
- }.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
184
168
 
185
- url_and_api_key = @configuration.upsync_url + '/' + @configuration.api_key
169
+ url_and_api_key = @configuration.upsync_url + "/" + @configuration.api_key
186
170
 
187
- response = HTTParty.post(url_and_api_key,
188
- :body => body,
189
- :headers => headers,
190
- :timeout => 300)
171
+ response = HTTParty.post(
172
+ url_and_api_key,
173
+ body: body,
174
+ headers: headers,
175
+ timeout: 10
176
+ )
191
177
 
192
- if response.code == 200
193
- @configuration.upsync_status = 'Complete'
194
- else
195
- LogSuppressor.puts_log("Upsync Error. HTTP Response: #{response.code}")
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
- return true
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(ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp)
215
- if @configuration.upsync_status != 'Disabled' || @configuration.upsync_status != 'Uploading'
216
- @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"
217
200
 
218
201
  # Add request to the queue
219
- request = [ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp]
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 = 'Enabled'
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
- return treatment
221
+ treatment
240
222
  else
241
- @configuration.upsync_status = 'Enabled'
242
- return "Passed"
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
- if Rails && Rails.application
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: 'wafris-rb',
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 = 'Disabled'
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 = 'Enabled'
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 = 'Enabled'
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['content-disposition']
318
- filename = content_disposition.split('filename=')[1].strip
319
-
320
- # Save the body of the response to a new SQLite file
321
- File.open(@configuration.db_file_path + "/" + filename, 'wb') { |file| file.write(response.body) }
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.open("#{@configuration.db_file_path}/#{db_rule_category}.modfile", 'w') { |file| file.write(filename) }
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
- # Remove the old database file
331
- if old_file_name
332
- if File.exist?(@configuration.db_file_path + "/" + old_file_name)
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", 'w')
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
- return filename
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
- if db_rule_category == 'custom_rules'
377
- interval = @configuration.downsync_custom_rules_interval
378
- else
379
- interval = @configuration.downsync_data_subscriptions_interval
380
- end
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
- return returned_db
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
- return ''
386
+ if returned_db == ""
387
+ ""
417
388
  else
418
- return returned_db
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
- return nil
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
- return nil
441
- else
442
- return returned_db
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(ip, user_agent, path, parameters, host, method, _headers, _body, request_id, request_timestamp)
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
- if rules_db_filename.to_s.strip != '' && data_subscriptions_db_filename.strip.to_s.strip != ''
426
+ rules_db_filename = current_db("custom_rules")
427
+ data_subscriptions_db_filename = current_db("data_subscriptions")
463
428
 
464
- rules_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{rules_db_filename}"
465
- data_subscriptions_db =
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
- # Allowed IPs
469
- if exact_match(ip, 'allowed_ips', rules_db)
470
- return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ai',
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
- # Allowed CIDR Ranges
475
- if ip_in_cidr_range(ip, 'allowed_cidr_ranges', rules_db)
476
- return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ac', ip, request_id, request_timestamp)
477
- end
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
- # Blocked IPs
480
- if exact_match(ip, 'blocked_ips', rules_db)
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
- # Blocked CIDR Ranges
485
- if ip_in_cidr_range(ip, 'blocked_cidr_ranges', rules_db)
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
- # Blocked Country Codes
490
- country_code = get_country_code(ip, data_subscriptions_db)
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
- # Blocked Reputation IP Ranges
496
- if ip_in_cidr_range(ip, 'reputation_ip_ranges', data_subscriptions_db)
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
- # Blocked User Agents
501
- user_agent_match = substring_match(user_agent, 'blocked_user_agents', rules_db)
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
- # Blocked Paths
507
- path_match = substring_match(path, 'blocked_paths', rules_db)
508
- if path_match
509
- return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bp', path_match, request_id, request_timestamp)
510
- end
511
-
512
- # Blocked Parameters
513
- parameters_match = substring_match(parameters, 'blocked_parameters', rules_db)
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
- end # end api_key.nil?
540
- end # end evaluate
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
- if ENV['WAFRIS_API_KEY']
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['WAFRIS_API_KEY']}"
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.open(settings.db_file_path + "/" + "custom_rules.modfile", 'r').read}"
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.open(settings.db_file_path + "/" + "data_subscriptions.modfile", 'r').read}"
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.3
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-02 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