wafris 1.1.11 → 2.0.0

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.
data/lib/wafris.rb CHANGED
@@ -1,61 +1,605 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'connection_pool'
3
+
4
4
  require 'rails'
5
- require 'redis'
5
+ require 'sqlite3'
6
+ require 'ipaddr'
7
+ require 'httparty'
8
+ require 'awesome_print'
6
9
 
7
10
  require 'wafris/configuration'
8
11
  require 'wafris/middleware'
9
12
  require 'wafris/log_suppressor'
10
13
 
11
14
  require 'wafris/railtie' if defined?(Rails::Railtie)
15
+ ActiveSupport::Deprecation.behavior = :silence
12
16
 
13
17
  module Wafris
14
18
  class << self
15
19
  attr_accessor :configuration
16
20
 
17
21
  def configure
18
- raise ArgumentError unless block_given?
19
-
20
- self.configuration ||= Wafris::Configuration.new
21
- yield(configuration)
22
- LogSuppressor.puts_log(
23
- "[Wafris] attempting firewall connection via Wafris.configure initializer."
24
- ) unless configuration.quiet_mode
25
- configuration.create_settings
26
- rescue ArgumentError
27
- LogSuppressor.puts_log(
28
- "[Wafris] block is required to configure Wafris. More info can be found at: https://github.com/Wafris/wafris-rb"
29
- )
30
- rescue StandardError => e
31
- LogSuppressor.puts_log(
32
- "[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"
33
- )
34
- end
35
-
36
- def allow_request?(request)
37
- configuration.connection_pool.with do |conn|
38
- time = Time.now.utc.to_i * 1000
39
- status = conn.evalsha(
40
- configuration.core_sha,
41
- argv: [
42
- request.ip,
43
- IPAddr.new(request.ip).to_i,
44
- time,
45
- request.user_agent,
46
- request.path,
47
- request.query_string,
48
- request.host,
49
- request.request_method
50
- ]
51
- )
22
+ begin
23
+ self.configuration ||= Wafris::Configuration.new
24
+ yield(configuration)
25
+
26
+ LogSuppressor.puts_log("[Wafris] Configuration settings created.")
27
+ configuration.create_settings
28
+
29
+ rescue StandardError => e
30
+ 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"
31
+ end
52
32
 
53
- if status.eql? 'Blocked'
33
+ end
34
+
35
+ def zero_pad(number, length)
36
+ number.to_s.rjust(length, "0")
37
+ end
38
+
39
+ def ip_to_decimal_lexical_string(ip)
40
+ num = 0
41
+
42
+ if ip.include?(":")
43
+ ip = IPAddr.new(ip).to_string
44
+ hex = ip.delete(":")
45
+ (0...hex.length).step(4) do |i|
46
+ chunk = hex[i, 4].to_i(16)
47
+ num = num * (2**16) + chunk
48
+ end
49
+ elsif ip.include?(".")
50
+ ip.split(".").each do |chunk|
51
+ num = num * 256 + chunk.to_i
52
+ end
53
+ end
54
+
55
+ str = num.to_s
56
+ zero_pad(str, 39)
57
+ end
58
+
59
+ def ip_in_cidr_range(ip_address, table_name, db_connection)
60
+ lexical_address = ip_to_decimal_lexical_string(ip_address)
61
+ higher_value = db_connection.get_first_value("SELECT * FROM #{table_name} WHERE member > ? ORDER BY member ASC", [lexical_address])
62
+ lower_value = db_connection.get_first_value("SELECT * FROM #{table_name} WHERE member < ? ORDER BY member DESC", [lexical_address])
63
+
64
+ if higher_value.nil? || lower_value.nil?
65
+ return nil
66
+ else
67
+ higher_compare = higher_value.split("-").last
68
+ lower_compare = lower_value.split("-").last
69
+
70
+ if higher_compare == lower_compare
71
+ return lower_compare
72
+ else
73
+ return nil
74
+ end
75
+ end
76
+ end
77
+
78
+ def get_country_code(ip, db_connection)
79
+ country_code = ip_in_cidr_range(ip, 'country_ip_ranges', db_connection)
80
+
81
+ if country_code
82
+ country_code = country_code.split("_").first.split("G").last
83
+ return country_code
84
+ else
85
+ return "ZZ"
86
+ end
87
+ end
88
+
89
+ def substring_match(request_property, table_name, db_connection)
90
+ result = db_connection.execute("SELECT entries FROM #{table_name}")
91
+ result.flatten.each do |entry|
92
+ if request_property.include?(entry)
93
+ return entry
94
+ end
95
+ end
96
+ return false
97
+ end
98
+
99
+ def exact_match(request_property, table_name, db_connection)
100
+ result = db_connection.execute("SELECT entries FROM #{table_name} WHERE entries = ?", [request_property])
101
+ return result.any?
102
+ end
103
+
104
+ def check_rate_limit(ip, path, method, db_connection)
105
+
106
+ # Correctly format the SQL query with placeholders
107
+ limiters = db_connection.execute("SELECT * FROM blocked_rate_limits WHERE path = ? AND method = ?", [path, method])
108
+
109
+ # If no rate limiters are matched
110
+ if limiters.empty?
111
+ return false
112
+ end
113
+
114
+ current_timestamp = Time.now.to_i
115
+
116
+ # If any rate limiters are matched
117
+ # This implementation will block the request on any of the rate limiters
118
+ limiters.each do |limiter|
119
+
120
+ # Limiter array mapping
121
+ # 0: id
122
+ # 1: path
123
+ # 2: method
124
+ # 3: interval
125
+ # 4: max_count
126
+ # 5: rule_id
127
+
128
+ interval = limiter[3]
129
+ max_count = limiter[4]
130
+ rule_id = limiter[5]
131
+
132
+ # Expire old timestamps
133
+ @configuration.rate_limiters.each do |ip, timestamps|
134
+ # Removes timestamps older than the interval
135
+
136
+ @configuration.rate_limiters[ip] = timestamps.select { |timestamp| timestamp > current_timestamp - interval }
137
+
138
+ # Remove the IP if there are no more timestamps for the IP
139
+ @configuration.rate_limiters.delete(ip) if @configuration.rate_limiters[ip].empty?
140
+ end
141
+
142
+ # Check if the IP+Method is rate limited
143
+
144
+ if @configuration.rate_limiters[ip] && @configuration.rate_limiters[ip].length >= max_count
145
+ # Request is rate limited
146
+
147
+
148
+ return rule_id
149
+
150
+ else
151
+ # Request is not rate limited, so add the current timestamp
152
+ if @configuration.rate_limiters[ip]
153
+ @configuration.rate_limiters[ip] << current_timestamp
154
+ else
155
+ @configuration.rate_limiters[ip] = [current_timestamp]
156
+ end
157
+
54
158
  return false
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+
165
+ def send_upsync_requests(requests_array)
166
+
167
+ begin
168
+
169
+ headers = {'Content-Type' => 'application/json'}
170
+
171
+ if Rails && Rails.application
172
+ framework = "Rails v#{Rails::VERSION::STRING}"
173
+ else
174
+ framework = "Rack v#{Rack::VERSION::STRING}"
175
+ end
176
+
177
+ body = {
178
+ meta: {
179
+ version: Wafris::VERSION,
180
+ client: 'wafris-rb',
181
+ framework: framework
182
+ },
183
+ batch: requests_array
184
+ }.to_json
185
+
186
+ url_and_api_key = @configuration.upsync_url + '/' + @configuration.api_key
187
+
188
+ response = HTTParty.post(url_and_api_key,
189
+ :body => body,
190
+ :headers => headers,
191
+ :timeout => 300)
192
+
193
+ if response.code == 200
194
+ @configuration.upsync_status = 'Complete'
195
+ else
196
+ LogSuppressor.puts_log("Upsync Error. HTTP Response: #{response.code}")
197
+ end
198
+ rescue HTTParty::Error => e
199
+ LogSuppressor.puts_log("Upsync Error. Failed to send upsync requests: #{e.message}")
200
+ end
201
+ return true
202
+ end
203
+
204
+ # This method is used to queue upsync requests. It takes in several parameters including
205
+ # ip, user_agent, path, parameters, host, method, treatment, category, and rule.
206
+ #
207
+ # The 'treatment' parameter represents the action taken on the request, which can be
208
+ # 'Allowed', 'Blocked', or 'Passed'.
209
+ #
210
+ # The 'category' parameter represents the category of the rule that was matched, such as
211
+ # 'blocked_ip', 'allowed_cidr', etc.
212
+ #
213
+ # The 'rule' parameter represents the specific rule that was matched within the category
214
+ # ex: '192.23.5.4', 'SemRush', etc.
215
+ def queue_upsync_request(ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp)
216
+
217
+ if @configuration.upsync_status != 'Disabled' || @configuration.upsync_status != 'Uploading'
218
+ @configuration.upsync_status = 'Uploading'
219
+
220
+ # Add request to the queue
221
+ request = [ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp]
222
+ @configuration.upsync_queue << request
223
+
224
+ # If the queue is full, send the requests to the upsync server
225
+ if @configuration.upsync_queue.length >= @configuration.upsync_queue_limit || (Time.now.to_i - @configuration.last_upsync_timestamp) >= @configuration.upsync_interval
226
+ requests_array = @configuration.upsync_queue
227
+ @configuration.upsync_queue = []
228
+ @configuration.last_upsync_timestamp = Time.now.to_i
229
+
230
+ send_upsync_requests(requests_array)
231
+ end
232
+
233
+ @configuration.upsync_status = 'Enabled'
234
+ # Return the treatment - used to return 403 or 200
235
+
236
+ message = "Request #{treatment}"
237
+ message += " | Category: #{category}" unless category.blank?
238
+ message += " | Rule: #{rule}" unless rule.blank?
239
+ LogSuppressor.puts_log(message)
240
+
241
+ return treatment
242
+ else
243
+ @configuration.upsync_status = 'Enabled'
244
+ return "Passed"
245
+ end
246
+
247
+ end
248
+
249
+ # Pulls the latest rules from the server
250
+ def downsync_db(db_rule_category, current_filename = nil)
251
+
252
+ lockfile_path = "#{@configuration.db_file_path}/#{db_rule_category}.lockfile"
253
+
254
+ # Ensure the directory exists before attempting to open the lockfile
255
+ FileUtils.mkdir_p(@configuration.db_file_path) unless Dir.exist?(@configuration.db_file_path)
256
+
257
+ # Attempt to create a lockfile with exclusive access; skip if it exists
258
+ begin
259
+ lockfile = File.open(lockfile_path, File::RDWR|File::CREAT|File::EXCL)
260
+ rescue Errno::EEXIST
261
+ LogSuppressor.puts_log("[Wafris][Downsync] Lockfile already exists, skipping downsync.")
262
+ return
263
+ rescue Exception => e
264
+ LogSuppressor.puts_log("[Wafris][Downsync] Error creating lockfile: #{e.message}")
265
+ end
266
+
267
+ begin
268
+ # Actual Downsync operations
269
+ filename = ""
270
+
271
+ if Rails && Rails.application
272
+ framework = "Rails v#{Rails::VERSION::STRING}"
55
273
  else
56
- return true
274
+ framework = "Rack v#{Rack::VERSION::STRING}"
57
275
  end
276
+
277
+ data = {
278
+ client_db: current_filename,
279
+ process_id: Process.pid,
280
+ hostname: Socket.gethostname,
281
+ version: Wafris::VERSION,
282
+ client: 'wafris-rb',
283
+ framework: framework
284
+ }
285
+
286
+ # Check server for new rules including process id
287
+ #puts "Downloading from #{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?current_version=#{current_filename}&process_id=#{Process.pid}"
288
+ uri = "#{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?#{data.to_query}"
289
+
290
+ response = HTTParty.get(
291
+ uri,
292
+ follow_redirects: true, # Enable following redirects
293
+ max_redirects: 2 # Maximum number of redirects to follow
294
+ )
295
+
296
+ # TODO: What to do if timeout
297
+ # TODO: What to do if error
298
+
299
+ if response.code == 401
300
+ @configuration.upsync_status = 'Disabled'
301
+ LogSuppressor.puts_log("[Wafris][Downsync] Unauthorized: Bad or missing API key")
302
+ LogSuppressor.puts_log("[Wafris][Downsync] API Key: #{@configuration.api_key}")
303
+ filename = current_filename
304
+
305
+ elsif response.code == 304
306
+ @configuration.upsync_status = 'Enabled'
307
+ LogSuppressor.puts_log("[Wafris][Downsync] No new rules to download")
308
+
309
+ filename = current_filename
310
+
311
+ elsif response.code == 200
312
+ @configuration.upsync_status = 'Enabled'
313
+
314
+ if current_filename
315
+ old_file_name = current_filename
316
+ end
317
+
318
+ # Extract the filename from the response
319
+ content_disposition = response.headers['content-disposition']
320
+ filename = content_disposition.split('filename=')[1].strip
321
+
322
+ # Save the body of the response to a new SQLite file
323
+ File.open(@configuration.db_file_path + "/" + filename, 'wb') { |file| file.write(response.body) }
324
+
325
+ # Write the filename into the db_category.modfile
326
+ File.open("#{@configuration.db_file_path}/#{db_rule_category}.modfile", 'w') { |file| file.write(filename) }
327
+
328
+ # Sanity check that the downloaded db file has tables
329
+ # not empty or corrupted
330
+ db = SQLite3::Database.new @configuration.db_file_path + "/" + filename
331
+ if db.execute("SELECT name FROM sqlite_master WHERE type='table';").any?
332
+ # Remove the old database file
333
+ if old_file_name
334
+ if File.exist?(@configuration.db_file_path + "/" + old_file_name)
335
+ File.delete(@configuration.db_file_path + "/" + old_file_name)
336
+ end
337
+ end
338
+
339
+ # DB file is bad or empty so keep using whatever we have now
340
+ else
341
+ filename = old_file_name
342
+ LogSuppressor.puts_log("[Wafris][Downsync] DB Error - No tables exist in the db file #{@configuration.db_file_path}/#{filename}")
343
+ end
344
+
345
+
346
+ end
347
+
348
+ rescue Exception => e
349
+ LogSuppressor.puts_log("[Wafris][Downsync] Error downloading rules: #{e.message}")
350
+
351
+ # This gets set even if the API key is bad or other issues
352
+ # to prevent hammering the distribution server on every request
353
+ ensure
354
+
355
+ # Reset the modified time of the modfile
356
+ unless File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
357
+ File.new("#{@configuration.db_file_path}/#{db_rule_category}.modfile", 'w')
358
+ end
359
+
360
+ # Set the modified time of the modfile to the current time
361
+ File.utime(Time.now, Time.now, "#{@configuration.db_file_path}/#{db_rule_category}.modfile")
362
+
363
+ # Ensure the lockfile is removed after operations
364
+ lockfile.close
365
+ File.delete(lockfile_path)
366
+ end
367
+
368
+ return filename
369
+
370
+ end
371
+
372
+ # Returns the current database file,
373
+ # if the file is older than the interval, it will download the latest db
374
+ # if the file doesn't exist, it will download the latest db
375
+ # if the lockfile exists, it will return the current db
376
+ def current_db(db_rule_category)
377
+
378
+ if db_rule_category == 'custom_rules'
379
+ interval = @configuration.downsync_custom_rules_interval
380
+ else
381
+ interval = @configuration.downsync_data_subscriptions_interval
382
+ end
383
+
384
+ # Checks for existing current modfile, which contains the current db filename
385
+ if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
386
+
387
+ LogSuppressor.puts_log("[Wafris][Downsync] Modfile exists, skipping downsync")
388
+
389
+ # Get last Modified Time and current database file name
390
+ last_db_synctime = File.mtime("#{@configuration.db_file_path}/#{db_rule_category}.modfile").to_i
391
+ returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
392
+
393
+ LogSuppressor.puts_log("[Wafris][Downsync] Modfile Last Modified Time: #{last_db_synctime}")
394
+ LogSuppressor.puts_log("[Wafris][Downsync] DB in Modfile: #{returned_db}")
395
+
396
+ # Check if the db file is older than the interval
397
+ if (Time.now.to_i - last_db_synctime) > interval
398
+
399
+ LogSuppressor.puts_log("[Wafris][Downsync] DB is older than the interval")
400
+
401
+ # Make sure that another process isn't already downloading the rules
402
+ if !File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
403
+ returned_db = downsync_db(db_rule_category, returned_db)
404
+ end
405
+
406
+ return returned_db
407
+
408
+ # Current db is up to date
409
+ else
410
+
411
+ LogSuppressor.puts_log("[Wafris][Downsync] DB is up to date")
412
+
413
+ returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
414
+
415
+ # If the modfile is empty (no db file name), return nil
416
+ # this can happen if the the api key is bad
417
+ if returned_db == ''
418
+ return ''
419
+ else
420
+ return returned_db
421
+ end
422
+
423
+ end
424
+
425
+ # No modfile exists, so download the latest db
426
+ else
427
+
428
+ LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
429
+
430
+ # Make sure that another process isn't already downloading the rules
431
+ if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
432
+ LogSuppressor.puts_log("[Wafris][Downsync] Lockfile exists, skipping downsync")
433
+ # Lockfile exists, but no modfile with a db filename
434
+ return nil
435
+ else
436
+
437
+ LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
438
+ # No modfile exists, so download the latest db
439
+ returned_db = downsync_db(db_rule_category, nil)
440
+
441
+ if returned_db.nil?
442
+ return nil
443
+ else
444
+ return returned_db
445
+ end
446
+
447
+ end
448
+
449
+ end
450
+
451
+ end
452
+
453
+ # This is the main loop that evaluates the request
454
+ # as well as sorts out when downsync and upsync should be called
455
+ def evaluate(ip, user_agent, path, parameters, host, method, headers, body, request_id, request_timestamp)
456
+ @configuration ||= Wafris::Configuration.new
457
+
458
+ if @configuration.api_key.nil?
459
+ return "Passed"
460
+ else
461
+
462
+ rules_db_filename = current_db('custom_rules')
463
+ data_subscriptions_db_filename = current_db('data_subscriptions')
464
+
465
+ if rules_db_filename.to_s.strip != '' && data_subscriptions_db_filename.strip.to_s.strip != ''
466
+
467
+ rules_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{rules_db_filename}"
468
+ data_subscriptions_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{data_subscriptions_db_filename}"
469
+
470
+ # Allowed IPs
471
+ if exact_match(ip, 'allowed_ips', rules_db)
472
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ai', ip, request_id, request_timestamp)
473
+ end
474
+
475
+ # Allowed CIDR Ranges
476
+ if ip_in_cidr_range(ip, 'allowed_cidr_ranges', rules_db)
477
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ac', ip, request_id, request_timestamp)
478
+ end
479
+
480
+ # Blocked IPs
481
+ if exact_match(ip, 'blocked_ips', rules_db)
482
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bi', ip, request_id, request_timestamp)
483
+ end
484
+
485
+ # Blocked CIDR Ranges
486
+ if ip_in_cidr_range(ip, 'blocked_cidr_ranges', rules_db)
487
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bc', ip, request_id, request_timestamp)
488
+ end
489
+
490
+ # Blocked Country Codes
491
+ country_code = get_country_code(ip, data_subscriptions_db)
492
+ if exact_match(country_code, 'blocked_country_codes', rules_db)
493
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "G_#{country_code}", request_id, request_timestamp)
494
+ end
495
+
496
+ # Blocked Reputation IP Ranges
497
+ if ip_in_cidr_range(ip, 'reputation_ip_ranges', data_subscriptions_db)
498
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "R", request_id, request_timestamp)
499
+ end
500
+
501
+ # Blocked User Agents
502
+ user_agent_match = substring_match(user_agent, 'blocked_user_agents', rules_db)
503
+ if user_agent_match
504
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bu', user_agent_match, request_id, request_timestamp)
505
+ end
506
+
507
+ # Blocked Paths
508
+ path_match = substring_match(path, 'blocked_paths', rules_db)
509
+ if path_match
510
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bp', path_match, request_id, request_timestamp)
511
+ end
512
+
513
+ # Blocked Parameters
514
+ parameters_match = substring_match(parameters, 'blocked_parameters', rules_db)
515
+ if parameters_match
516
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'ba', parameters_match, request_id, request_timestamp)
517
+ end
518
+
519
+ # Blocked Hosts
520
+ if exact_match(host, 'blocked_hosts', rules_db)
521
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bh', host, request_id, request_timestamp)
522
+ end
523
+
524
+ # Blocked Methods
525
+ if exact_match(method, 'blocked_methods', rules_db)
526
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bm', method, request_id, request_timestamp)
527
+ end
528
+
529
+ # Rate Limiting
530
+ rule_id = check_rate_limit(ip, path, method, rules_db)
531
+ if rule_id
532
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'brl', rule_id, request_id, request_timestamp)
533
+ end
534
+
535
+ end
536
+
537
+ # Passed if no allow or block rules matched
538
+ return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Passed', 'passed', '-', request_id, request_timestamp)
539
+
540
+ end # end api_key.nil?
541
+ end # end evaluate
542
+
543
+ def debug(api_key)
544
+
545
+ if ENV['WAFRIS_API_KEY']
546
+ puts "Wafris API Key environment variable is set."
547
+ puts " - API Key: #{ENV['WAFRIS_API_KEY']}"
548
+ else
549
+ puts "Wafris API Key environment variable is not set."
58
550
  end
551
+
552
+ puts "\n"
553
+ puts "Wafris Configuration:"
554
+
555
+ Wafris.configure do |config|
556
+ config.api_key = api_key
557
+ end
558
+
559
+ settings = Wafris.configuration
560
+
561
+ settings.instance_variables.each do |ivar|
562
+ puts " - #{ivar}: #{Wafris.configuration.instance_variable_get(ivar)}"
563
+ end
564
+
565
+ puts "\n"
566
+ if File.exist?(settings.db_file_path + "/" + "custom_rules.lockfile")
567
+ puts "Custom Rules Lockfile: #{settings.db_file_path}/custom_rules.lockfile exists"
568
+ puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "custom_rules.lockfile")}"
569
+ else
570
+ puts "Custom Rules Lockfile: #{settings.db_file_path}/custom_rules.lockfile does not exist."
571
+ end
572
+
573
+ puts "\n"
574
+ if File.exist?(settings.db_file_path + "/" + "custom_rules.modfile")
575
+ puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile exists"
576
+ puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "custom_rules.modfile")}"
577
+ puts " - Contents: #{File.open(settings.db_file_path + "/" + "custom_rules.modfile", 'r').read}"
578
+ else
579
+ puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile does not exist."
580
+ end
581
+
582
+ puts "\n"
583
+ if File.exist?(settings.db_file_path + "/" + "data_subscriptions.lockfile")
584
+ puts "Data Subscriptions Lockfile: #{settings.db_file_path}/data_subscriptions.lockfile exists"
585
+ puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "data_subscriptions.lockfile")}"
586
+ else
587
+ puts "Data Subscriptions Lockfile: #{settings.db_file_path}/data_subscriptions.lockfile does not exist."
588
+ end
589
+
590
+ puts "\n"
591
+ if File.exist?(settings.db_file_path + "/" + "data_subscriptions.modfile")
592
+ puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile exists"
593
+ puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "data_subscriptions.modfile")}"
594
+ puts " - Contents: #{File.open(settings.db_file_path + "/" + "data_subscriptions.modfile", 'r').read}"
595
+ else
596
+ puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile does not exist."
597
+ end
598
+
599
+
600
+
601
+ return true
59
602
  end
603
+
60
604
  end
61
605
  end