sqreen-alt 1.10.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.
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