sqreen-alt 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +22 -0
  3. data/README.md +77 -0
  4. data/Rakefile +20 -0
  5. data/lib/sqreen-alt.rb +1 -0
  6. data/lib/sqreen.rb +68 -0
  7. data/lib/sqreen/attack_detected.html +2 -0
  8. data/lib/sqreen/binding_accessor.rb +288 -0
  9. data/lib/sqreen/ca.crt +72 -0
  10. data/lib/sqreen/call_countable.rb +67 -0
  11. data/lib/sqreen/callback_tree.rb +78 -0
  12. data/lib/sqreen/callbacks.rb +100 -0
  13. data/lib/sqreen/capped_queue.rb +23 -0
  14. data/lib/sqreen/condition_evaluator.rb +235 -0
  15. data/lib/sqreen/conditionable.rb +50 -0
  16. data/lib/sqreen/configuration.rb +168 -0
  17. data/lib/sqreen/context.rb +26 -0
  18. data/lib/sqreen/deliveries/batch.rb +84 -0
  19. data/lib/sqreen/deliveries/simple.rb +39 -0
  20. data/lib/sqreen/event.rb +16 -0
  21. data/lib/sqreen/events/attack.rb +61 -0
  22. data/lib/sqreen/events/remote_exception.rb +54 -0
  23. data/lib/sqreen/events/request_record.rb +62 -0
  24. data/lib/sqreen/exception.rb +34 -0
  25. data/lib/sqreen/frameworks.rb +40 -0
  26. data/lib/sqreen/frameworks/generic.rb +446 -0
  27. data/lib/sqreen/frameworks/rails.rb +148 -0
  28. data/lib/sqreen/frameworks/rails3.rb +36 -0
  29. data/lib/sqreen/frameworks/request_recorder.rb +69 -0
  30. data/lib/sqreen/frameworks/sinatra.rb +57 -0
  31. data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
  32. data/lib/sqreen/instrumentation.rb +542 -0
  33. data/lib/sqreen/log.rb +119 -0
  34. data/lib/sqreen/metrics.rb +6 -0
  35. data/lib/sqreen/metrics/average.rb +39 -0
  36. data/lib/sqreen/metrics/base.rb +45 -0
  37. data/lib/sqreen/metrics/collect.rb +22 -0
  38. data/lib/sqreen/metrics/sum.rb +20 -0
  39. data/lib/sqreen/metrics_store.rb +96 -0
  40. data/lib/sqreen/middleware.rb +34 -0
  41. data/lib/sqreen/payload_creator.rb +137 -0
  42. data/lib/sqreen/performance_notifications.rb +86 -0
  43. data/lib/sqreen/performance_notifications/log.rb +36 -0
  44. data/lib/sqreen/performance_notifications/metrics.rb +36 -0
  45. data/lib/sqreen/performance_notifications/newrelic.rb +36 -0
  46. data/lib/sqreen/remote_command.rb +93 -0
  47. data/lib/sqreen/rule_attributes.rb +26 -0
  48. data/lib/sqreen/rule_callback.rb +108 -0
  49. data/lib/sqreen/rules.rb +126 -0
  50. data/lib/sqreen/rules_callbacks.rb +29 -0
  51. data/lib/sqreen/rules_callbacks/binding_accessor_matcher.rb +77 -0
  52. data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
  53. data/lib/sqreen/rules_callbacks/blacklist_ips.rb +44 -0
  54. data/lib/sqreen/rules_callbacks/count_http_codes.rb +40 -0
  55. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
  56. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +24 -0
  57. data/lib/sqreen/rules_callbacks/custom_error.rb +64 -0
  58. data/lib/sqreen/rules_callbacks/execjs.rb +241 -0
  59. data/lib/sqreen/rules_callbacks/headers_insert.rb +22 -0
  60. data/lib/sqreen/rules_callbacks/inspect_rule.rb +25 -0
  61. data/lib/sqreen/rules_callbacks/matcher_rule.rb +138 -0
  62. data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
  63. data/lib/sqreen/rules_callbacks/record_request_context.rb +39 -0
  64. data/lib/sqreen/rules_callbacks/reflected_xss.rb +254 -0
  65. data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
  66. data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
  67. data/lib/sqreen/rules_callbacks/url_matches.rb +25 -0
  68. data/lib/sqreen/rules_callbacks/user_agent_matches.rb +22 -0
  69. data/lib/sqreen/rules_signature.rb +151 -0
  70. data/lib/sqreen/runner.rb +365 -0
  71. data/lib/sqreen/runtime_infos.rb +138 -0
  72. data/lib/sqreen/safe_json.rb +60 -0
  73. data/lib/sqreen/sdk.rb +22 -0
  74. data/lib/sqreen/serializer.rb +46 -0
  75. data/lib/sqreen/session.rb +317 -0
  76. data/lib/sqreen/shared_storage.rb +31 -0
  77. data/lib/sqreen/stats.rb +18 -0
  78. data/lib/sqreen/version.rb +5 -0
  79. metadata +148 -0
@@ -0,0 +1,54 @@
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
+ require 'sqreen/event'
6
+
7
+ module Sqreen
8
+ # When an exception arise it is automatically pushed to the event queue
9
+ class RemoteException < Sqreen::Event
10
+ def self.record(payload_or_exception)
11
+ exception = RemoteException.new(payload_or_exception)
12
+ exception.enqueue
13
+ end
14
+
15
+ def initialize(payload_or_exception)
16
+ payload = if payload_or_exception.is_a?(Hash)
17
+ payload_or_exception
18
+ else
19
+ { 'exception' => payload_or_exception }
20
+ end
21
+ super(payload)
22
+ end
23
+
24
+ def enqueue
25
+ Sqreen.queue.push(self)
26
+ end
27
+
28
+ def klass
29
+ payload['exception'].class.name
30
+ end
31
+
32
+ def to_hash
33
+ exception = payload['exception']
34
+ ev = {
35
+ :klass => exception.class.name,
36
+ :message => exception.message,
37
+ :params => payload['request_params'],
38
+ :time => payload['time'],
39
+ :infos => {
40
+ :client_ip => payload['client_ip'],
41
+ },
42
+ :request => payload['request_infos'],
43
+ :headers => payload['headers'],
44
+ :rule_name => payload['rule_name'],
45
+ :rulespack_id => payload['rulespack_id'],
46
+ }
47
+
48
+ ev[:infos].merge!(payload['infos']) if payload['infos']
49
+ return ev unless exception.backtrace
50
+ ev[:context] = { :backtrace => exception.backtrace.map(&:to_s) }
51
+ ev
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,62 @@
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
+ require 'sqreen/event'
6
+
7
+ module Sqreen
8
+ # When a request is deeemed worthy of being sent to the backend
9
+ class RequestRecord < Sqreen::Event
10
+ def observed
11
+ (payload && payload[:observed]) || {}
12
+ end
13
+
14
+ def to_hash
15
+ res = { :version => '20171208' }
16
+ if payload[:observed]
17
+ res[:observed] = payload[:observed].dup
18
+ rulespack = nil
19
+ if observed[:attacks]
20
+ res[:observed][:attacks] = observed[:attacks].map do |att|
21
+ natt = att.dup
22
+ rulespack = natt.delete(:rulespack_id) || rulespack
23
+ natt
24
+ end
25
+ end
26
+ if observed[:sqreen_exceptions]
27
+ res[:observed][:sqreen_exceptions] = observed[:sqreen_exceptions].map do |exc|
28
+ nex = exc.dup
29
+ excp = nex.delete(:exception)
30
+ if excp
31
+ nex[:message] = excp.message
32
+ nex[:klass] = excp.class.name
33
+ end
34
+ rulespack = nex.delete(:rulespack_id) || rulespack
35
+ nex
36
+ end
37
+ end
38
+ res[:rulespack_id] = rulespack unless rulespack.nil?
39
+ if observed[:observations]
40
+ res[:observed][:observations] = observed[:observations].map do |cat, key, value, time|
41
+ { :category => cat, :key => key, :value => value, :time => time }
42
+ end
43
+ end
44
+ if observed[:sdk]
45
+ res[:observed][:sdk] = observed[:sdk].map do |meth, time, *args|
46
+ { :name => meth, :time => time, :args => args }
47
+ end
48
+ end
49
+ end
50
+ res[:local] = payload['local'] if payload['local']
51
+ if payload['request']
52
+ res[:request] = payload['request'].dup
53
+ res[:client_ip] = res[:request].delete(:client_ip) if res[:request][:client_ip]
54
+ else
55
+ res[:request] = {}
56
+ end
57
+ res[:request][:parameters] = payload['params'] if payload['params']
58
+ res[:request][:headers] = payload['headers'] if payload['headers']
59
+ res
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,34 @@
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 'sqreen/log'
5
+
6
+ module Sqreen
7
+ # Base exeception class for sqreen
8
+ class Exception < ::StandardError
9
+ def initialize(msg = nil, *args)
10
+ super(msg, *args)
11
+ Sqreen.log.error msg if msg
12
+ end
13
+ end
14
+
15
+ # When the token is not found
16
+ class TokenNotFoundException < Exception
17
+ end
18
+
19
+ # When the token is invalid
20
+ class TokenInvalidException < Exception
21
+ end
22
+
23
+ # This exception name is particularly important since it is often seen by
24
+ # Sqreen users when watching their logs. It should not raise any concern to
25
+ # them.
26
+ class AttackBlocked < Exception
27
+ end
28
+
29
+ class NotImplementedYet < Exception
30
+ end
31
+
32
+ class InvalidSignatureException < Exception
33
+ end
34
+ end
@@ -0,0 +1,40 @@
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
+ module Sqreen
5
+ @@framework = nil
6
+
7
+ def self::set_framework(fwk)
8
+ @@framework = fwk
9
+ end
10
+
11
+ def self::framework
12
+ return @@framework if @@framework
13
+ klass = case
14
+ when defined?(::Rails) && defined?(::Rails::VERSION)
15
+ case Rails::VERSION::MAJOR.to_i
16
+ when 4, 5
17
+ require 'sqreen/frameworks/rails'
18
+ Sqreen::Frameworks::RailsFramework
19
+ when 3
20
+ require 'sqreen/frameworks/rails3'
21
+ Sqreen::Frameworks::Rails3Framework
22
+ else
23
+ raise "Rails version #{Rails.version} not supported"
24
+ end
25
+ when defined?(::Sinatra)
26
+ require 'sqreen/frameworks/sinatra'
27
+ Sqreen::Frameworks::SinatraFramework
28
+ when defined?(::SqreenTest)
29
+ require 'sqreen/frameworks/sqreen_test'
30
+ Sqreen::Frameworks::SqreenTestFramework
31
+ else
32
+ # FIXME: use sqreen logger before configuration?
33
+ STDERR.puts "Error: cannot find any framework\n"
34
+ require 'sqreen/frameworks/generic'
35
+ Sqreen::Frameworks::GenericFramework
36
+ end
37
+ fwk = klass.new
38
+ Sqreen.set_framework(fwk)
39
+ end
40
+ end
@@ -0,0 +1,446 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+ require 'ipaddr'
4
+
5
+ require 'sqreen/events/remote_exception'
6
+ require 'sqreen/callbacks'
7
+ require 'sqreen/exception'
8
+ require 'sqreen/log'
9
+ require 'sqreen/frameworks/request_recorder'
10
+
11
+ module Sqreen
12
+ module Frameworks
13
+ # This is the base class for framework specific code
14
+ class GenericFramework
15
+ include RequestRecorder
16
+ attr_accessor :sqreen_configuration
17
+
18
+ def initialize
19
+ if defined?(Rack::Builder)
20
+ hook_rack_builder
21
+ else
22
+ to_app_done(Process.pid)
23
+ end
24
+ clean_request_record
25
+ end
26
+
27
+ # What kind of database is this
28
+ def db_settings(_options = {})
29
+ raise Sqreen::NotImplementedYet
30
+ end
31
+
32
+ # More information about the current framework
33
+ def framework_infos
34
+ raise Sqreen::NotImplementedYet unless ensure_rack_loaded
35
+ {
36
+ :framework_type => 'Rack',
37
+ :framework_version => Rack.version,
38
+ :environment => ENV['RACK_ENV'],
39
+ }
40
+ end
41
+
42
+ def development?
43
+ ENV['RACK_ENV'] == 'development'
44
+ end
45
+
46
+ PREFFERED_IP_HEADERS = %w(HTTP_X_FORWARDED_FOR HTTP_X_REAL_IP
47
+ HTTP_CLIENT_IP HTTP_X_FORWARDED
48
+ HTTP_X_CLUSTER_CLIENT_IP HTTP_FORWARDED_FOR
49
+ HTTP_FORWARDED HTTP_VIA).freeze
50
+
51
+ def ip_headers
52
+ req = request
53
+ return [] unless req
54
+ ips = []
55
+ (PREFFERED_IP_HEADERS + ['REMOTE_ADDR']).each do |header|
56
+ v = req.env[header]
57
+ ips << [header, v] unless v.nil?
58
+ end
59
+ ips << ['rack.ip', req.ip] if req.respond_to?(:ip)
60
+ ips
61
+ end
62
+
63
+ # What is the current client IP as seen by rack
64
+ def rack_client_ip
65
+ req = request
66
+ return nil unless req
67
+ return req.ip if req.respond_to?(:ip)
68
+ req.env['REMOTE_ADDR']
69
+ end
70
+
71
+ # Sourced from rack:Request#trusted_proxy?
72
+ TRUSTED_PROXIES = /\A127\.0\.0\.1\Z|\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|\A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/i
73
+ LOCALHOST = /\A127\.0\.0\.1\Z|\A::1\Z|\Alocalhost\Z|\Aunix\Z|\Aunix:/i
74
+
75
+ # What is the current client IP
76
+ def client_ip
77
+ req = request
78
+ return nil unless req
79
+ # Look for an external address being forwarded
80
+ split_ips = []
81
+ PREFFERED_IP_HEADERS.each do |header_name|
82
+ forwarded = req.env[header_name]
83
+ ips = split_ip_addresses(forwarded)
84
+ lip = ips.find { |ip| (ip !~ TRUSTED_PROXIES) && valid_ip?(ip) }
85
+ split_ips << ips unless ips.empty?
86
+ return lip unless lip.nil?
87
+ end
88
+ # Else fall back to declared remote addr
89
+ r = req.env['REMOTE_ADDR']
90
+ # If this is localhost get the last hop before
91
+ if r.nil? || r =~ LOCALHOST
92
+ split_ips.each do |ips|
93
+ lip = ips.find { |ip| (ip !~ LOCALHOST) && valid_ip?(ip) }
94
+ return lip unless lip.nil?
95
+ end
96
+ end
97
+ r
98
+ end
99
+
100
+ # Get a header by name
101
+ def header(name)
102
+ req = request
103
+ return nil unless req
104
+ req.env[name]
105
+ end
106
+
107
+ def http_headers
108
+ req = request
109
+ return nil unless req
110
+ req.env.select { |k, _| k.to_s.start_with?('HTTP_') }
111
+ end
112
+
113
+ def hostname
114
+ req = request
115
+ return nil unless req
116
+ http_host = req.env['HTTP_HOST']
117
+ return http_host if http_host && !http_host.empty?
118
+ req.env['SERVER_NAME']
119
+ end
120
+
121
+ def request_id
122
+ req = request
123
+ return nil unless req
124
+ req.env['HTTP_X_REQUEST_ID']
125
+ end
126
+
127
+ # Summary of known request infos
128
+ def request_infos
129
+ req = request
130
+ return {} unless req
131
+ # FIXME: Use frozen string keys
132
+ {
133
+ :rid => request_id,
134
+ :user_agent => client_user_agent,
135
+ :scheme => req.scheme,
136
+ :verb => req.env['REQUEST_METHOD'],
137
+ :host => hostname,
138
+ :port => req.env['SERVER_PORT'],
139
+ :referer => req.env['HTTP_REFERER'],
140
+ :path => request_path,
141
+ :remote_port => req.env['REMOTE_PORT'],
142
+ :remote_ip => remote_addr,
143
+ :client_ip => client_ip,
144
+ }
145
+ end
146
+
147
+ # Request URL path
148
+ def request_path
149
+ req = request
150
+ return nil unless req
151
+ req.script_name + req.path_info
152
+ end
153
+
154
+ # request user agent
155
+ def client_user_agent
156
+ req = request
157
+ return nil unless req
158
+ req.env['HTTP_USER_AGENT']
159
+ end
160
+
161
+ # Application root
162
+ def root
163
+ nil
164
+ end
165
+
166
+ # Main entry point for sqreen.
167
+ # launch whenever we are ready
168
+ def on_start
169
+ yield self
170
+ end
171
+
172
+ # Should the agent not be starting up?
173
+ def prevent_startup
174
+ return :irb if $0 == 'irb'
175
+ return if sqreen_configuration.nil?
176
+ disable = sqreen_configuration.get(:disable)
177
+ return :config_disable if disable == true || disable.to_s.to_i == 1
178
+ end
179
+
180
+ # Instrument with our rules when the framework as finished loading
181
+ def instrument_when_ready!(instrumentor, rules)
182
+ wait_for_to_app do
183
+ instrumentor.instrument!(rules, self)
184
+ end
185
+ end
186
+
187
+ def to_app_done(val)
188
+ return if @to_app_done
189
+ @to_app_done = val
190
+ return unless @wait
191
+ @wait.each(&:call)
192
+ @wait.clear
193
+ end
194
+
195
+ def wait_for_to_app(&block)
196
+ yield && return if @to_app_done
197
+ @wait ||= []
198
+ @wait << block
199
+ end
200
+
201
+ # Does the parameters value include this value
202
+ def params_include?(value)
203
+ params = request_params
204
+ return false if params.nil?
205
+ each_value_for_hash(params) do |param|
206
+ return true if param == value
207
+ end
208
+ false
209
+ end
210
+
211
+ # Does the parameters key/value include this value
212
+ def full_params_include?(value)
213
+ params = request_params
214
+ return false if params.nil?
215
+ each_key_value_for_hash(params) do |param|
216
+ return true if param == value
217
+ end
218
+ false
219
+ end
220
+
221
+ # Fetch and store the current request object
222
+ # Nota: cleanup should be performed at end of request (see clean_request)
223
+ def store_request(object)
224
+ return unless ensure_rack_loaded
225
+ SharedStorage.set(:request, Rack::Request.new(object))
226
+ SharedStorage.inc(:stored_requests)
227
+ end
228
+
229
+ # Get the currently stored request
230
+ def request
231
+ SharedStorage.get(:request)
232
+ end
233
+
234
+ # Cleanup request context
235
+ def clean_request
236
+ return unless SharedStorage.dec(:stored_requests) <= 0
237
+ payload_creator = Sqreen::PayloadCreator.new(self)
238
+ close_request_record(Sqreen.queue, Sqreen.observations_queue, payload_creator)
239
+ SharedStorage.set(:request, nil)
240
+ end
241
+
242
+ def request_params
243
+ self.class.parameters_from_request(request)
244
+ end
245
+
246
+ def filtered_request_params
247
+ params = request_params
248
+ params.delete('cookies')
249
+ params
250
+ end
251
+
252
+ %w(form query cookies).each do |section|
253
+ define_method("#{section}_params") do
254
+ self.class.send("#{section}_params", request)
255
+ end
256
+ end
257
+
258
+ P_FORM = 'form'.freeze
259
+ P_QUERY = 'query'.freeze
260
+ P_COOKIE = 'cookies'.freeze
261
+ P_GRAPE = 'grape_params'.freeze
262
+ P_RACK_ROUTING = 'rack_routing'.freeze
263
+
264
+ def self.form_params(request)
265
+ return nil unless request
266
+ begin
267
+ request.POST
268
+ rescue => e
269
+ Sqreen.log.debug("POST Parameters are invalid #{e.inspect}")
270
+ nil
271
+ end
272
+ end
273
+
274
+ def self.cookies_params(request)
275
+ return nil unless request
276
+ begin
277
+ request.cookies
278
+ rescue => e
279
+ Sqreen.log.debug("cookies are invalid #{e.inspect}")
280
+ nil
281
+ end
282
+ end
283
+
284
+ def self.query_params(request)
285
+ return nil unless request
286
+ begin
287
+ request.GET
288
+ rescue => e
289
+ Sqreen.log.debug("GET Parameters are invalid #{e.inspect}")
290
+ nil
291
+ end
292
+ end
293
+
294
+ def self.parameters_from_request(request)
295
+ return {} unless request
296
+
297
+ r = {
298
+ P_FORM => form_params(request),
299
+ P_QUERY => query_params(request),
300
+ P_COOKIE => cookies_params(request),
301
+ }
302
+ # Add grape parameters if seen
303
+ p = request.env['grape.request.params']
304
+ r[P_GRAPE] = p if p
305
+ p = request.env['rack.routing_args']
306
+ if p
307
+ r[P_RACK_ROUTING] = p.dup
308
+ r[P_RACK_ROUTING].delete :route_info
309
+ r[P_RACK_ROUTING].delete :version
310
+ end
311
+ r
312
+ end
313
+
314
+ # Expose current working directory
315
+ def cwd
316
+ Dir.getwd
317
+ end
318
+
319
+ WHITELIST_KEY = 'sqreen.whitelisted_request'.freeze
320
+
321
+ # Return the current item that whitelist this request
322
+ # returns nil if request is not whitelisted
323
+ def whitelisted_match
324
+ return nil unless request
325
+ return request.env[WHITELIST_KEY] if request.env.key?(WHITELIST_KEY)
326
+ request.env[WHITELIST_KEY] = whitelisted_ip || whitelisted_path
327
+ end
328
+
329
+ # Returns the current path that whitelist the request
330
+ def whitelisted_path
331
+ path = request_path
332
+ return nil unless path
333
+ find_whitelisted_path(path)
334
+ end
335
+
336
+ # Returns the current path that whitelist the request
337
+ def whitelisted_ip
338
+ ip = client_ip
339
+ return nil unless ip
340
+ find_whitelisted_ip(ip)
341
+ rescue
342
+ nil
343
+ end
344
+
345
+ def remote_addr
346
+ return nil unless request
347
+ request.env['REMOTE_ADDR']
348
+ end
349
+
350
+ protected
351
+
352
+ # Is this a whitelisted path?
353
+ # return the path witelisted prefix that match path
354
+ def find_whitelisted_path(rpath)
355
+ (Sqreen.whitelisted_paths || []).find do |path|
356
+ rpath.start_with?(path)
357
+ end
358
+ end
359
+
360
+ # Is this a whitelisted ip?
361
+ # return the ip witelisted range that match ip
362
+ def find_whitelisted_ip(rip)
363
+ ret = (Sqreen.whitelisted_ips || {}).find do |_, ip|
364
+ ip.include?(rip)
365
+ end
366
+ return nil unless ret
367
+ ret.first
368
+ end
369
+
370
+ def hook_rack_request(klass)
371
+ @calling_pid = Process.pid
372
+ klass.class_eval do
373
+ define_method(:call_with_sqreen) do |*args, &block|
374
+ rv = call_without_sqreen(*args, &block)
375
+ if Sqreen.framework.instance_variable_get('@calling_pid') != Process.pid
376
+ Sqreen.framework.instance_variable_set('@calling_pid', Process.pid)
377
+ yield Sqreen.framework
378
+ end
379
+ rv
380
+ end
381
+ alias_method :call_without_sqreen, :call
382
+ alias_method :call, :call_with_sqreen
383
+ end
384
+ end
385
+
386
+ def hook_rack_builder
387
+ Rack::Builder.class_eval do
388
+ define_method(:to_app_with_sqreen) do |*args, &block|
389
+ Sqreen.framework.to_app_done(Process.pid)
390
+ to_app_without_sqreen(*args, &block)
391
+ end
392
+ alias_method :to_app_without_sqreen, :to_app
393
+ alias_method :to_app, :to_app_with_sqreen
394
+ end
395
+ end
396
+
397
+ # FIXME: Extract to another object (utils?)
398
+ # FIXME: protect against cycles ?
399
+ def each_value_for_hash(params, &block)
400
+ case params
401
+ when Hash then params.each { |_k, v| each_value_for_hash(v, &block) }
402
+ when Array then params.each { |v| each_value_for_hash(v, &block) }
403
+ else
404
+ yield params
405
+ end
406
+ end
407
+
408
+ def each_key_value_for_hash(params, &block)
409
+ case params
410
+ when Hash then params.each do |k, v|
411
+ yield k
412
+ each_key_value_for_hash(v, &block)
413
+ end
414
+ when Array then params.each { |v| each_key_value_for_hash(v, &block) }
415
+ else
416
+ yield params
417
+ end
418
+ end
419
+
420
+ def ensure_rack_loaded
421
+ @cannot_load_rack ||= false
422
+ return false if @cannot_load_rack
423
+ require 'rack' unless defined?(Rack)
424
+ true
425
+ rescue LoadError => e
426
+ # FIXME: find a nice way to test this branch
427
+ Sqreen::RemoteException.record(e)
428
+ @cannot_load_rack = true
429
+ false
430
+ end
431
+
432
+ private
433
+
434
+ def split_ip_addresses(ip_addresses)
435
+ ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
436
+ end
437
+
438
+ def valid_ip?(ip)
439
+ IPAddr.new(ip)
440
+ true
441
+ rescue
442
+ false
443
+ end
444
+ end
445
+ end
446
+ end