wafris 2.0.3 → 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: 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