sqreen 1.6.5 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cc4c4bcf86488934c2048d33804dd07398484e09
4
- data.tar.gz: 4d4bb6a78d00ef04d6d824f01f1194a156cb019b
3
+ metadata.gz: dc11a3a48f65cd5aaa1b4e0444b122cf12ae2c21
4
+ data.tar.gz: 85c6e6f370a1bb862f554679063057774f96e7ab
5
5
  SHA512:
6
- metadata.gz: 1ab651db3fb58f44551e4c04885da9bf0c7543a51c479565e8baf23cb7739d83b0b14196ac6958515462b34103542916875da9516cac8517b0f54a74cfa437ee
7
- data.tar.gz: b6886860a4d6a9d118bc675c4ea6b1e70fe6093c9cbda35691f89a9058e845b1270f75b67c9b2bbacd4f4e3a40b013fc5ba733c9cc8d269e5b2806a510c4b699
6
+ metadata.gz: b0d671840b6c605359512b50e23efef360f29d58944031ce0589683c64254a9d7205e91e7ecb6c17797a05e42c09b0d649a36c50de78468280fd4720eefe50b6
7
+ data.tar.gz: 7664d0468f7fd8e0ee71e03b325f02f5b0c76f53379046d2013232592dda05db0f3c9970db1ac046944ada579a42e87175077955485d649fdb207fdbe0e91589
@@ -58,6 +58,8 @@ module Sqreen
58
58
  # - { :status => :reraise }: reraise
59
59
  # - { :status => :override }: eat exception
60
60
  # - { :retry => :retry }: try the block again
61
+ #
62
+ # CB can also declare that they are whitelisted and should not be run at the moment.
61
63
 
62
64
  attr_reader :klass, :method
63
65
 
@@ -72,6 +74,10 @@ module Sqreen
72
74
  raise(Sqreen::Exception, 'No callback provided') unless @has_pre || @has_post || @has_failing
73
75
  end
74
76
 
77
+ def whitelisted?
78
+ false
79
+ end
80
+
75
81
  def pre?
76
82
  @has_pre
77
83
  end
@@ -45,7 +45,6 @@ module Sqreen
45
45
  res = {}
46
46
  rule_p = payload['rule']
47
47
  request_p = payload['request']
48
- whitelisted = request_p.delete('whitelisted') if request_p
49
48
  res[:rule_name] = rule_p['name'] if rule_p && rule_p['name']
50
49
  res[:rulespack_id] = rule_p['rulespack_id'] if rule_p && rule_p['rulespack_id']
51
50
  res[:test] = rule_p['test'] if rule_p && rule_p['test']
@@ -56,7 +55,6 @@ module Sqreen
56
55
  res[:params] = payload['params'] if payload['params']
57
56
  res[:context] = payload['context'] if payload['context']
58
57
  res[:headers] = payload['headers'] if payload['headers']
59
- res[:whitelist_match] = whitelisted if whitelisted
60
58
  res
61
59
  end
62
60
  end
@@ -291,8 +291,52 @@ module Sqreen
291
291
  Dir.getwd
292
292
  end
293
293
 
294
+ WHITELIST_KEY = 'sqreen.whitelisted_request'.freeze
295
+
296
+ # Return the current item that whitelist this request
297
+ # returns nil if request is not whitelisted
298
+ def whitelisted_match
299
+ return nil unless request
300
+ return request.env[WHITELIST_KEY] if request.env.key?(WHITELIST_KEY)
301
+ request.env[WHITELIST_KEY] = whitelisted_ip || whitelisted_path
302
+ end
303
+
304
+ # Returns the current path that whitelist the request
305
+ def whitelisted_path
306
+ path = request_path
307
+ return nil unless path
308
+ find_whitelisted_path(path)
309
+ end
310
+
311
+ # Returns the current path that whitelist the request
312
+ def whitelisted_ip
313
+ ip = client_ip
314
+ return nil unless ip
315
+ find_whitelisted_ip(ip)
316
+ rescue
317
+ nil
318
+ end
319
+
294
320
  protected
295
321
 
322
+ # Is this a whitelisted path?
323
+ # return the path witelisted prefix that match path
324
+ def find_whitelisted_path(rpath)
325
+ (Sqreen.whitelisted_paths || []).find do |path|
326
+ rpath.start_with?(path)
327
+ end
328
+ end
329
+
330
+ # Is this a whitelisted ip?
331
+ # return the ip witelisted range that match ip
332
+ def find_whitelisted_ip(rip)
333
+ ret = (Sqreen.whitelisted_ips || {}).find do |_, ip|
334
+ ip.include?(rip)
335
+ end
336
+ return nil unless ret
337
+ ret.first
338
+ end
339
+
296
340
  def hook_rack_request(klass)
297
341
  @calling_pid = Process.pid
298
342
  klass.class_eval do
@@ -33,6 +33,7 @@ require 'set'
33
33
 
34
34
  module Sqreen
35
35
  class Instrumentation
36
+ WHITELISTED_METRIC='whitelisted'.freeze
36
37
  @@override_semaphore = Mutex.new
37
38
 
38
39
  ## Overriden methods and callbacks globals
@@ -54,6 +55,7 @@ module Sqreen
54
55
  def self.callback_wrapper_pre(klass, method, instance, *args, &block)
55
56
  Instrumentation.guard_call(method, []) do
56
57
  callbacks = @@registered_callbacks.get(klass, method, :pre)
58
+ callbacks.reject!(&:whitelisted?)
57
59
 
58
60
  returns = []
59
61
  callbacks.each do |cb|
@@ -90,6 +92,7 @@ module Sqreen
90
92
  def self.callback_wrapper_post(klass, method, return_val, instance, *args, &block)
91
93
  Instrumentation.guard_call(method, []) do
92
94
  callbacks = @@registered_callbacks.get(klass, method, :post)
95
+ callbacks.reject!(&:whitelisted?)
93
96
 
94
97
  returns = []
95
98
  callbacks.reverse_each do |cb|
@@ -126,6 +129,7 @@ module Sqreen
126
129
  def self.callback_wrapper_failing(exception, klass, method, instance, *args, &block)
127
130
  Instrumentation.guard_call(method, []) do
128
131
  callbacks = @@registered_callbacks.get(klass, method, :failing)
132
+ callbacks.reject!(&:whitelisted?)
129
133
 
130
134
  returns = []
131
135
  callbacks.each do |cb|
@@ -506,11 +510,13 @@ module Sqreen
506
510
 
507
511
  def initialize(metrics_engine = nil)
508
512
  self.metrics_engine = metrics_engine
509
- unless metrics_engine.nil?
510
- metrics_engine.create_metric('name' => CallCountable::COUNT_CALLS,
511
- 'period' => 60,
512
- 'kind' => 'Sum')
513
- end
513
+ return if metrics_engine.nil?
514
+ metrics_engine.create_metric('name' => CallCountable::COUNT_CALLS,
515
+ 'period' => 60,
516
+ 'kind' => 'Sum')
517
+ metrics_engine.create_metric('name' => WHITELISTED_METRIC,
518
+ 'period' => 60,
519
+ 'kind' => 'Sum')
514
520
  end
515
521
  end
516
522
  end
@@ -1,6 +1,7 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
- require "sqreen/log"
3
+ require 'sqreen/log'
4
+ require 'sqreen/events/remote_exception'
4
5
 
5
6
  module Sqreen
6
7
  # Execute and sanitize remote commands
@@ -13,6 +14,7 @@ module Sqreen
13
14
  :features_change => :change_features,
14
15
  :force_logout => :shutdown,
15
16
  :paths_whitelist => :change_whitelisted_paths,
17
+ :ips_whitelist => :change_whitelisted_ips,
16
18
  }.freeze
17
19
 
18
20
  attr_reader :uuid
@@ -27,7 +29,12 @@ module Sqreen
27
29
  failing = validate_command(runner)
28
30
  return failing if failing
29
31
  Sqreen.log.debug format('processing command %s', @name)
30
- output = runner.send(KNOWN_COMMANDS[@name], *@params, context_infos)
32
+ begin
33
+ output = runner.send(KNOWN_COMMANDS[@name], *@params, context_infos)
34
+ rescue => e
35
+ Sqreen::RemoteException.record(e)
36
+ return { :status => false, :reason => "error: #{e.inspect}" }
37
+ end
31
38
  format_output(output)
32
39
  end
33
40
 
@@ -46,39 +46,24 @@ module Sqreen
46
46
  @rule[Attrs::RULESPACK_ID]
47
47
  end
48
48
 
49
+ def whitelisted?
50
+ framework && !framework.whitelisted_match.nil?
51
+ end
52
+
49
53
  # Recommend taking an action (optionnally adding more data/context)
50
54
  #
51
55
  # This will format the requested action and optionnally
52
56
  # override it if it should not be taken (should not block for example)
53
57
  def advise_action(action, additional_data = {})
54
58
  return if action.nil? && additional_data.empty?
55
- if %w(override raise).include?(action.to_s) && framework
56
- prefix = find_whitelisted_path(framework.request_path.to_s)
57
- if prefix
58
- Sqreen.log.debug { "Trying to block on whitelisted path #{prefix}" }
59
- action = nil
60
- end
61
- end
62
59
  additional_data.merge(:status => action)
63
60
  end
64
61
 
65
- # Is this a whitelisted path?
66
- # return the path witelisted prefix that match path
67
- def find_whitelisted_path(rpath)
68
- (Sqreen.whitelisted_paths || []).find do |path|
69
- rpath.start_with?(path)
70
- end
71
- end
72
-
73
62
  # Record an attack event into Sqreen system
74
63
  # @param infos [Hash] Additional information about request
75
64
  def record_event(infos)
76
65
  payload = @payload_generator.payload(framework, @rule)
77
66
  payload['infos'] = infos
78
- if framework && payload['request']
79
- prefix = find_whitelisted_path(framework.request_path.to_s)
80
- payload['request']['whitelisted'] = prefix
81
- end
82
67
  Attack.record(payload)
83
68
  end
84
69
 
@@ -8,6 +8,7 @@ require 'sqreen/rules_callbacks/record_request_context'
8
8
  require 'sqreen/rules_callbacks/rails_parameters'
9
9
 
10
10
  require 'sqreen/rules_callbacks/headers_insert'
11
+ require 'sqreen/rules_callbacks/blacklist_ips'
11
12
 
12
13
  require 'sqreen/rules_callbacks/inspect_rule'
13
14
 
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+
4
+ require 'ipaddr'
5
+
6
+ require 'sqreen/rule_callback'
7
+
8
+ module Sqreen
9
+ module Rules
10
+ # Looks for a blacklisted ip and block
11
+ class BlacklistIPsCB < RuleCB
12
+ def initialize(klass, method, rule_hash)
13
+ super(klass, method, rule_hash)
14
+ @ips = Hash[@data['values'].map { |v| [v, IPAddr.new(v)] }]
15
+ raise ArgumentError.new("no ips given") if @ips.empty?
16
+ end
17
+
18
+ def pre(_inst, *_args, &_block)
19
+ return unless framework
20
+ ip = framework.client_ip
21
+ return unless ip
22
+ found = find_blacklisted_ip(ip)
23
+ return unless found
24
+ Sqreen.log.debug { "Found blacklisted IP #{ip} - found: #{found}" }
25
+ record_observation('blacklisted', found, 1)
26
+ advise_action(:raise)
27
+ end
28
+
29
+ protected
30
+
31
+ # Is this a blacklisted ip?
32
+ # return the ip blacklisted range that match ip
33
+ def find_blacklisted_ip(rip)
34
+ ret = (@ips || {}).find do |_, ip|
35
+ ip.include?(rip)
36
+ end
37
+ return nil unless ret
38
+ ret.first
39
+ rescue
40
+ nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'sqreen/rule_attributes'
5
5
  require 'sqreen/rule_callback'
6
+ require 'sqreen/safe_json'
6
7
 
7
8
  module Sqreen
8
9
  module Rules
@@ -31,7 +32,7 @@ module Sqreen
31
32
  key = @accessors.map do |accessor|
32
33
  accessor.resolve(binding, framework, inst, args, @data, rv)
33
34
  end
34
- record_observation(@metric_category, JSON.dump(key), 1)
35
+ record_observation(@metric_category, SafeJSON.dump(key), 1)
35
36
  advise_action(nil)
36
37
  end
37
38
  end
@@ -73,6 +73,7 @@ module Sqreen
73
73
 
74
74
  def match(str)
75
75
  return if str.nil? || str.empty?
76
+ str = enforce_encoding(str)
76
77
  istr = str.downcase
77
78
 
78
79
  @string.each do |type, cases|
@@ -92,11 +93,35 @@ module Sqreen
92
93
  end
93
94
  end
94
95
 
95
- @regexp_patterns.each do |p|
96
- return p if p.match(str)
96
+ if defined?(Encoding)
97
+ @regexp_patterns.each do |p|
98
+ next unless Encoding.compatible?(p, str)
99
+ return p if p.match(str)
100
+ end
101
+ else
102
+ @regexp_patterns.each do |p|
103
+ warn(Encoding.compatible?(p, str).inspect)
104
+ return p if p.match(str)
105
+ end
97
106
  end
98
107
  nil
99
108
  end
109
+
110
+ private
111
+
112
+ def enforce_encoding(str)
113
+ return str unless str.is_a?(String)
114
+ return str if str.ascii_only?
115
+ encoded8bit = str.encoding.name == 'ASCII-8BIT'
116
+ return str if !encoded8bit && str.valid_encoding?
117
+ str.chars.map do |v|
118
+ if !v.valid_encoding? || (encoded8bit && !v.ascii_only?)
119
+ ''
120
+ else
121
+ v
122
+ end
123
+ end.join
124
+ end
100
125
  end
101
126
 
102
127
  # A configurable matcher rule
@@ -2,13 +2,26 @@
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
4
  require 'sqreen/rule_callback'
5
+ require 'sqreen/instrumentation'
5
6
 
6
7
  module Sqreen
7
8
  module Rules
8
9
  # Save request context for handling further down
9
10
  class RecordRequestContext < RuleCB
11
+ def whitelisted?
12
+ false
13
+ end
14
+
10
15
  def pre(_inst, *args, &_block)
11
16
  framework.store_request(args[0])
17
+ wh = framework.whitelisted_match
18
+ if wh
19
+ unless Sqreen.features.key?('whitelisted_metric') &&
20
+ !Sqreen.features['whitelisted_metric']
21
+ record_observation(Instrumentation::WHITELISTED_METRIC, wh, 1)
22
+ end
23
+ Sqreen.log.debug { "Request was whitelisted because of #{wh}" }
24
+ end
12
25
  advise_action(nil)
13
26
  end
14
27
 
@@ -47,8 +47,7 @@ module Sqreen
47
47
  return unless report_dangerous_xss?(saved_value)
48
48
 
49
49
  # potential XSS! let's escape
50
- if block &&
51
- (!framework || !find_whitelisted_path(framework.request_path.to_s))
50
+ if block
52
51
  args[0].replace(CGI.escape_html(value))
53
52
  end
54
53
 
@@ -150,9 +149,6 @@ module Sqreen
150
149
 
151
150
  return if !found_xss || !block
152
151
  # potential XSS! let's escape
153
- if framework && find_whitelisted_path(framework.request_path.to_s)
154
- return nil
155
- end
156
152
  args[-1] = new_attrs
157
153
  r = inst.send(method, *args)
158
154
  { :status => :skip, :new_return_value => r }
@@ -213,8 +209,7 @@ module Sqreen
213
209
 
214
210
  return unless block
215
211
  # potential XSS! let's escape
216
- if block &&
217
- (!framework || !find_whitelisted_path(framework.request_path.to_s))
212
+ if block
218
213
  args[0] = CGI.escape_html(value)
219
214
  r = inst.send(method, *args)
220
215
  return { :status => :skip, :new_return_value => r }
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
- require 'sqreen/rules_callbacks'
5
4
  require 'sqreen/exception'
6
5
 
7
6
  require 'set'
data/lib/sqreen/runner.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
+ require 'ipaddr'
4
5
  require 'timeout'
5
6
  require 'json'
6
7
 
@@ -53,6 +54,11 @@ module Sqreen
53
54
  def update_whitelisted_paths(paths)
54
55
  @whitelisted_paths = paths.freeze
55
56
  end
57
+
58
+ attr_reader :whitelisted_ips
59
+ def update_whitelisted_ips(paths)
60
+ @whitelisted_ips = Hash[paths.map { |v| [v, IPAddr.new(v)] }].freeze
61
+ end
56
62
  end
57
63
 
58
64
  # Main running job class for the agent
@@ -87,6 +93,7 @@ module Sqreen
87
93
  @token = @configuration.get(:token)
88
94
  @url = @configuration.get(:url)
89
95
  Sqreen.update_whitelisted_paths([])
96
+ Sqreen.update_whitelisted_ips({})
90
97
  raise(Sqreen::Exception, 'no url found') unless @url
91
98
  raise(Sqreen::TokenNotFoundException, 'no token found') unless @token
92
99
 
@@ -244,6 +251,12 @@ module Sqreen
244
251
  true
245
252
  end
246
253
 
254
+ def change_whitelisted_ips(ips, _context_infos = {})
255
+ return false unless ips.respond_to?(:each)
256
+ Sqreen.update_whitelisted_ips(ips)
257
+ true
258
+ end
259
+
247
260
  def change_features(new_features, _context_infos = {})
248
261
  old = features
249
262
  self.features = new_features
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+
4
+ require 'json'
5
+
6
+ require 'sqreen/log'
7
+
8
+ module Sqreen
9
+ # Safely dump datastructure in json (more resilient to encoding errors)
10
+ class SafeJSON
11
+ def self.dump(data)
12
+ JSON.generate(data)
13
+ rescue JSON::GeneratorError, Encoding::UndefinedConversionError
14
+ Sqreen.log.debug('Payload could not be encoded enforcing recode')
15
+ JSON.generate(rencode_payload(data))
16
+ end
17
+
18
+ def self.rencode_payload(obj, max_depth = 20)
19
+ max_depth -= 1
20
+ return obj if max_depth < 0
21
+ return rencode_array(obj, max_depth) if obj.is_a?(Array)
22
+ return enforce_encoding(obj) unless obj.is_a?(Hash)
23
+ nobj = {}
24
+ obj.each do |k, v|
25
+ safe_k = rencode_payload(k, max_depth)
26
+ nobj[safe_k] = case v
27
+ when Array
28
+ rencode_array(v, max_depth)
29
+ when Hash
30
+ rencode_payload(v, max_depth)
31
+ when String
32
+ enforce_encoding(v)
33
+ else # for example integers
34
+ v
35
+ end
36
+ end
37
+ nobj
38
+ end
39
+
40
+ def self.rencode_array(array, max_depth)
41
+ array.map! { |e| rencode_payload(e, max_depth - 1) }
42
+ array
43
+ end
44
+
45
+ def self.enforce_encoding(str)
46
+ return str unless str.is_a?(String)
47
+ return str if str.ascii_only?
48
+ encoded8bit = str.encoding.name == 'ASCII-8BIT'
49
+ return str if !encoded8bit && str.valid_encoding?
50
+ r = str.chars.map do |v|
51
+ if !v.valid_encoding? || (encoded8bit && !v.ascii_only?)
52
+ v.bytes.map { |c| "\\x#{c.to_s(16).upcase}" }.join
53
+ else
54
+ v
55
+ end
56
+ end.join
57
+ "SqBytes[#{r}]"
58
+ end
59
+ end
60
+ end
@@ -6,9 +6,9 @@ require 'sqreen/serializer'
6
6
  require 'sqreen/runtime_infos'
7
7
  require 'sqreen/events/remote_exception'
8
8
  require 'sqreen/exception'
9
+ require 'sqreen/safe_json'
9
10
 
10
11
  require 'net/https'
11
- require 'json'
12
12
  require 'uri'
13
13
  require 'openssl'
14
14
  require 'zlib'
@@ -187,7 +187,7 @@ module Sqreen
187
187
  json_data = nil
188
188
  unless data.nil?
189
189
  serialized = Serializer.serialize(data)
190
- json_data = compress(self.class.encode_payload(serialized))
190
+ json_data = compress(SafeJSON.dump(serialized))
191
191
  end
192
192
  @con.post(path, json_data, headers)
193
193
  else
@@ -209,13 +209,6 @@ module Sqreen
209
209
  res
210
210
  end
211
211
 
212
- def self.encode_payload(data)
213
- JSON.generate(data)
214
- rescue JSON::GeneratorError, Encoding::UndefinedConversionError
215
- Sqreen.log.debug('Payload could not be encoded enforcing recode')
216
- JSON.generate(rencode_payload(data))
217
- end
218
-
219
212
  def compress(data)
220
213
  return data unless request_compression
221
214
  out = StringIO.new
@@ -225,47 +218,6 @@ module Sqreen
225
218
  out.string
226
219
  end
227
220
 
228
- def self.rencode_payload(obj, max_depth = 20)
229
- max_depth -= 1
230
- return obj if max_depth < 0
231
- return rencode_array(obj, max_depth) if obj.is_a?(Array)
232
- return enforce_encoding(obj) unless obj.is_a?(Hash)
233
- nobj = {}
234
- obj.each do |k, v|
235
- safe_k = rencode_payload(k, max_depth)
236
- nobj[safe_k] = case v
237
- when Array
238
- rencode_array(v, max_depth)
239
- when Hash
240
- rencode_payload(v, max_depth)
241
- when String
242
- enforce_encoding(v)
243
- else # for example integers
244
- v
245
- end
246
- end
247
- nobj
248
- end
249
-
250
- def self.rencode_array(array, max_depth)
251
- array.map! { |e| rencode_payload(e, max_depth - 1) }
252
- array
253
- end
254
-
255
- def self.enforce_encoding(str)
256
- return str unless str.is_a?(String)
257
- return str if str.ascii_only?
258
- encoded8bit = str.encoding.name == 'ASCII-8BIT'
259
- return str if !encoded8bit && str.valid_encoding?
260
- str.chars.map do |v|
261
- if !v.valid_encoding? || (encoded8bit && !v.ascii_only?)
262
- v.bytes.map { |c| "\\x#{c.to_s(16).upcase}" }.join
263
- else
264
- v
265
- end
266
- end.join
267
- end
268
-
269
221
  def login(framework)
270
222
  headers = { 'x-api-key' => @token }
271
223
 
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
  module Sqreen
4
- VERSION = '1.6.5'.freeze
4
+ VERSION = '1.7.0'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqreen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.5
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sqreen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-09 00:00:00.000000000 Z
11
+ date: 2017-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs
@@ -93,6 +93,7 @@ files:
93
93
  - lib/sqreen/rules_callbacks.rb
94
94
  - lib/sqreen/rules_callbacks/binding_accessor_matcher.rb
95
95
  - lib/sqreen/rules_callbacks/binding_accessor_metrics.rb
96
+ - lib/sqreen/rules_callbacks/blacklist_ips.rb
96
97
  - lib/sqreen/rules_callbacks/count_http_codes.rb
97
98
  - lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb
98
99
  - lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb
@@ -111,6 +112,7 @@ files:
111
112
  - lib/sqreen/rules_signature.rb
112
113
  - lib/sqreen/runner.rb
113
114
  - lib/sqreen/runtime_infos.rb
115
+ - lib/sqreen/safe_json.rb
114
116
  - lib/sqreen/sdk.rb
115
117
  - lib/sqreen/serializer.rb
116
118
  - lib/sqreen/session.rb
@@ -135,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
137
  version: '0'
136
138
  requirements: []
137
139
  rubyforge_project:
138
- rubygems_version: 2.6.11
140
+ rubygems_version: 2.6.12
139
141
  signing_key:
140
142
  specification_version: 4
141
143
  summary: Sqreen Ruby agent