sqreen 1.17.0-java → 1.17.2.beta1-java

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.
@@ -0,0 +1,17 @@
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
+ module Dependency
6
+ module NewRelic
7
+ module_function
8
+
9
+ def ignore_sqreen_exceptions
10
+ return unless defined?(NewRelic::Agent::Agent)
11
+ NewRelic::Agent::Agent.instance.error_collector.ignore(['Sqreen::AttackBlocked'])
12
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
13
+ Sqreen.log.warn "Failed ignoring AttackBlocked on NewRelic: #{e.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
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
+ module Dependency
6
+ module Rack
7
+ module_function
8
+
9
+ def find_handler(&block)
10
+ Sqreen::Dependency::Hook.add('Rack::Server#server') do
11
+ after do |callback, _, server, _|
12
+ block.call(server)
13
+ callback.disable # do this once, :server is a lazy init accessor
14
+ end
15
+ end
16
+ Sqreen::Dependency::Hook['Rack::Server#server'].install
17
+ end
18
+
19
+ def on_run(handler, &block)
20
+ Sqreen.log.debug "[#{Process.pid}] #{handler.inspect}"
21
+ hookpoint_name = "#{handler.name}.run"
22
+
23
+ Sqreen::Dependency::Hook.add(hookpoint_name) do
24
+ before { block.call(handler) }
25
+ end
26
+ Sqreen::Dependency::Hook[hookpoint_name].install
27
+ end
28
+
29
+ def rackup?
30
+ return false if Sqreen::Dependency::Rails.server?
31
+
32
+ Sqreen::Dependency.const_exist?('Rack::Server') && ObjectSpace.each_object(::Rack::Server).count > 0
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
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
+ module Dependency
6
+ module Rails
7
+ module_function
8
+
9
+ def required?
10
+ Sqreen::Dependency.const_exist?('Rails::Application')
11
+ end
12
+
13
+ def server?
14
+ Sqreen::Dependency.const_exist?('Rails::Server') && ObjectSpace.each_object(::Rails::Server).count > 0
15
+ end
16
+
17
+ def inspect_middlewares
18
+ Sqreen.log.debug { "Middlewares: " << ::Rails.application.middleware.map(&:inspect).inspect }
19
+ end
20
+
21
+ def insert_sqreen_middlewares
22
+ Sqreen.log.debug { 'Inserting Sqreen middlewares for Rails' }
23
+ app = ::Rails.application
24
+ app.middleware.insert_after(::Rack::Runtime, Sqreen::Middleware)
25
+ app.middleware.insert_after(::ActionDispatch::DebugExceptions, Sqreen::RailsMiddleware)
26
+ app.middleware.insert_after(::ActionDispatch::DebugExceptions, Sqreen::ErrorHandlingMiddleware)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
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
+ module Dependency
6
+ module Sentry
7
+ module_function
8
+
9
+ def ignore_sqreen_exceptions
10
+ return unless defined?(Raven) && Raven.respond_to?(:configuration)
11
+ Raven.configuration.excluded_exceptions += ['Sqreen::AttackBlocked']
12
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
13
+ Sqreen.log.warn "Failed setting Sentry's excluded_exceptions: #{e.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -40,4 +40,7 @@ module Sqreen
40
40
 
41
41
  class InvalidSignatureException < Exception
42
42
  end
43
+
44
+ class Unauthorized < Exception
45
+ end
43
46
  end
@@ -1,5 +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 'ipaddr'
4
5
  require 'set'
5
6
 
@@ -17,11 +18,6 @@ module Sqreen
17
18
  attr_accessor :sqreen_configuration
18
19
 
19
20
  def initialize
20
- if defined?(Rack::Builder)
21
- hook_rack_builder
22
- else
23
- to_app_done(Process.pid)
24
- end
25
21
  clean_request_record
26
22
  end
27
23
 
@@ -205,12 +201,6 @@ module Sqreen
205
201
  nil
206
202
  end
207
203
 
208
- # Main entry point for sqreen.
209
- # launch whenever we are ready
210
- def on_start
211
- yield self
212
- end
213
-
214
204
  # Should the agent not be starting up?
215
205
  def prevent_startup
216
206
  return :irb if $0 == 'irb'
@@ -221,23 +211,7 @@ module Sqreen
221
211
 
222
212
  # Instrument with our rules when the framework as finished loading
223
213
  def instrument_when_ready!(instrumentor, rules)
224
- wait_for_to_app do
225
- instrumentor.instrument!(rules, self)
226
- end
227
- end
228
-
229
- def to_app_done(val)
230
- return if @to_app_done
231
- @to_app_done = val
232
- return unless @wait
233
- @wait.each(&:call)
234
- @wait.clear
235
- end
236
-
237
- def wait_for_to_app(&block)
238
- yield && return if @to_app_done
239
- @wait ||= []
240
- @wait << block
214
+ instrumentor.instrument!(rules, self)
241
215
  end
242
216
 
243
217
  # Does the parameters value include this value
@@ -456,15 +430,6 @@ module Sqreen
456
430
  end
457
431
  end
458
432
 
459
- def on_pre_fork_preload?
460
- # unlike with worker_fork_detection, we can't be instrument Puma::Cluster
461
- # in time for intercepting run
462
- stack = Kernel.caller_locations
463
-
464
- puma_preload?(stack) || unicorn_preload?(stack)
465
- end
466
-
467
-
468
433
  protected
469
434
 
470
435
  # Is this a whitelisted path?
@@ -485,22 +450,6 @@ module Sqreen
485
450
  ret.first
486
451
  end
487
452
 
488
- def hook_rack_request(klass)
489
- @calling_pid = Process.pid
490
- klass.class_eval do
491
- define_method(:call_with_sqreen) do |*args, &block|
492
- rv = call_without_sqreen(*args, &block)
493
- if Sqreen.framework.instance_variable_get('@calling_pid') != Process.pid
494
- Sqreen.framework.instance_variable_set('@calling_pid', Process.pid)
495
- yield Sqreen.framework
496
- end
497
- rv
498
- end
499
- alias_method :call_without_sqreen, :call
500
- alias_method :call, :call_with_sqreen
501
- end
502
- end
503
-
504
453
  def hook_rack_builder
505
454
  Rack::Builder.class_eval do
506
455
  define_method(:to_app_with_sqreen) do |*args, &block|
@@ -547,52 +496,8 @@ module Sqreen
547
496
  false
548
497
  end
549
498
 
550
- def sentry_ignore_exceptions
551
- return unless defined?(Raven) && Raven.respond_to?(:configuration)
552
- Raven.configuration.excluded_exceptions += ['Sqreen::AttackBlocked']
553
- rescue
554
- Sqreen.log.warn "Failed setting Sentry's excluded_exceptions: #{e.inspect}"
555
- end
556
-
557
- def newrelic_ignore_errors
558
- return unless defined?(NewRelic::Agent::Agent)
559
- NewRelic::Agent::Agent.instance.
560
- error_collector.ignore(['Sqreen::AttackBlocked'])
561
- rescue
562
- Sqreen.log.warn "Failed ignoring AttackBlocked on NewRelic: #{e.inspect}"
563
- end
564
-
565
- # if it doesn't detect the fork it's not a problem
566
- def worker_fork_detection
567
- # only Puma currently supported
568
- return unless defined?(Puma::Cluster) && Puma::Cluster.instance_methods.include?(:worker)
569
- cur_worker_meth = Puma::Cluster.instance_method(:worker)
570
- Puma::Cluster.class_eval do
571
- define_method(:worker) do |*args|
572
- Sqreen.on_forked_worker = true
573
- cur_worker_meth.bind(self)[*args]
574
- end
575
- end
576
- end
577
-
578
499
  private
579
500
 
580
- def puma_preload?(stack)
581
- cluster_run = stack.each_with_index.find { |b, _i| b.path =~ /puma\/cluster\.rb\z/ && b.label == 'run' }
582
- return false unless cluster_run
583
- frame_atop = stack[cluster_run[1] - 1]
584
- frame_atop.label == 'load_and_bind'
585
- end
586
-
587
- def unicorn_preload?(stack)
588
- build_app = stack.each_with_index.find do |b, _i|
589
- b.path =~ /unicorn\/http_server\.rb\z/ && b.label == 'build_app!'
590
- end
591
- return false unless build_app
592
- frame_below = stack[build_app[1] + 1]
593
- frame_below.label == 'start'
594
- end
595
-
596
501
  def split_ip_addresses(ip_addresses)
597
502
  return [] unless ip_addresses
598
503
 
@@ -15,6 +15,10 @@ module Sqreen
15
15
  'Mysql2' => :mysql,
16
16
  }.freeze
17
17
 
18
+ def initialize
19
+ super
20
+ end
21
+
18
22
  def framework_infos
19
23
  {
20
24
  :framework_type => 'Rails',
@@ -89,19 +93,6 @@ module Sqreen
89
93
  end
90
94
  end
91
95
 
92
- def on_start(&block)
93
- @calling_pid = Process.pid
94
- Init.startup do |app|
95
- worker_fork_detection
96
- sentry_ignore_exceptions
97
- newrelic_ignore_errors
98
- hook_rack_request(app.class, &block)
99
- app.config.after_initialize do
100
- yield self
101
- end
102
- end
103
- end
104
-
105
96
  def prevent_startup
106
97
  res = super
107
98
  return res if res
@@ -15,15 +15,6 @@ module Sqreen
15
15
  h
16
16
  end
17
17
 
18
- def on_start(&block)
19
- worker_fork_detection
20
- sentry_ignore_exceptions
21
- newrelic_ignore_errors
22
- hook_app_build(Sinatra::Base)
23
- hook_rack_request(Sinatra::Application, &block)
24
- yield self
25
- end
26
-
27
18
  def db_settings(options = {})
28
19
  adapter = options[:connection_adapter]
29
20
  return nil unless adapter
@@ -39,22 +30,6 @@ module Sqreen
39
30
  db_infos = { :name => adapter_name }
40
31
  [db_type, db_infos]
41
32
  end
42
-
43
- def hook_app_build(klass)
44
- klass.singleton_class.class_eval do
45
- define_method(:setup_default_middleware_with_sqreen) do |builder|
46
- ret = setup_default_middleware_without_sqreen(builder)
47
- builder.instance_variable_get('@use').insert(2, proc do |app|
48
- # Inject error middle just before sinatra one
49
- Sqreen::ErrorHandlingMiddleware.new(app)
50
- end)
51
- ret
52
- end
53
-
54
- alias_method :setup_default_middleware_without_sqreen, :setup_default_middleware
55
- alias_method :setup_default_middleware, :setup_default_middleware_with_sqreen
56
- end
57
- end
58
33
  end
59
34
  end
60
35
  end
@@ -43,7 +43,6 @@ module Sqreen
43
43
  POST_CB = 'post'.freeze
44
44
  FAILING_CB = 'failing'.freeze
45
45
 
46
- MGMT_COST = 0.000025
47
46
  @@override_semaphore = Mutex.new
48
47
  @@overriden_singleton_methods = false
49
48
 
@@ -115,7 +114,7 @@ module Sqreen
115
114
  Sqreen::PerformanceNotifications.notify(rule || cb.class.name, PRE_CB, start, stop)
116
115
  end
117
116
  all_stop = Sqreen.time
118
- framework.remaining_perf_budget = budget - (all_stop - all_start) - MGMT_COST * callbacks.size if framework && budget
117
+ framework.remaining_perf_budget = budget - (all_stop - all_start) if framework && budget
119
118
  Sqreen::PerformanceNotifications.notify('hooks_pre', PRE_CB, all_start, all_stop)
120
119
  returns
121
120
  #end
@@ -168,7 +167,7 @@ module Sqreen
168
167
  end
169
168
  all_stop = Sqreen.time
170
169
  if framework && budget && framework.remaining_perf_budget
171
- framework.remaining_perf_budget = budget - (all_stop - all_start) - MGMT_COST * callbacks.size
170
+ framework.remaining_perf_budget = budget - (all_stop - all_start)
172
171
  end
173
172
  Sqreen::PerformanceNotifications.notify('hooks_post', POST_CB, all_start, all_stop)
174
173
  returns
@@ -222,7 +221,7 @@ module Sqreen
222
221
  end
223
222
  all_stop = Sqreen.time
224
223
  if framework && budget && framework.remaining_perf_budget
225
- framework.remaining_perf_budget = budget - (all_stop - all_start) - MGMT_COST * callbacks.size
224
+ framework.remaining_perf_budget = budget - (all_stop - all_start)
226
225
  end
227
226
  Sqreen::PerformanceNotifications.notify('hooks_failing', FAILING_CB, all_start, all_stop)
228
227
  returns
@@ -254,6 +253,7 @@ module Sqreen
254
253
  @sqreen_multi_instr ||= nil
255
254
 
256
255
  proc do |*args, &block|
256
+ Sqreen.log.debug { "Calling instrumented #{klass_name} #{original_meth} => #{meth}" }
257
257
  budget = nil
258
258
  skip_call = Thread.current[:sqreen_in_use]
259
259
  begin
@@ -626,6 +626,7 @@ module Sqreen
626
626
  end
627
627
  end
628
628
 
629
+ Sqreen.log.debug "Adding callback #{cb} for #{klass} #{method}"
629
630
  @@registered_callbacks.add(cb)
630
631
  @@unovertimable_hookpoints << key unless cb.overtimeable
631
632
  @@instrumented_pid = Process.pid
@@ -50,18 +50,21 @@ module Sqreen
50
50
  end
51
51
 
52
52
  def pre(inst, args, budget = nil, &_block)
53
+ Sqreen.log.debug { "#{self.class} pre args: #{args.inspect}" }
53
54
  return unless pre?
54
55
 
55
56
  call_callback('pre', budget, inst, @cb_bas['pre'], args)
56
57
  end
57
58
 
58
59
  def post(rv, inst, args, budget = nil, &_block)
60
+ Sqreen.log.debug { "#{self.class} post args: #{args.inspect}" }
59
61
  return unless post?
60
62
 
61
63
  call_callback('post', budget, inst, @cb_bas['post'], args, rv)
62
64
  end
63
65
 
64
66
  def failing(rv, inst, args, budget = nil, &_block)
67
+ Sqreen.log.debug { "#{self.class} failing args: #{args.inspect}" }
65
68
  return unless failing?
66
69
 
67
70
  call_callback('failing', budget, inst, @cb_bas['failing'], args, rv)
@@ -18,6 +18,7 @@ module Sqreen
18
18
  end
19
19
 
20
20
  def pre(_inst, args, _budget = nil, &_block)
21
+ Sqreen.log.debug { "RecordRequestContext pre args: #{args.inspect}" }
21
22
  framework.store_request(args[0])
22
23
  wh = framework.whitelisted_match
23
24
  if wh
@@ -31,12 +32,14 @@ module Sqreen
31
32
  end
32
33
 
33
34
  def post(rv, _inst, args, _budget = nil, &_block)
35
+ Sqreen.log.debug { "RecordRequestContext post args: #{args.inspect}" }
34
36
  framework.store_response(rv, args[0])
35
37
  framework.clean_request
36
38
  advise_action(nil)
37
39
  end
38
40
 
39
- def failing(_exception, _inst, _args, _budget = nil, &_block)
41
+ def failing(_exception, _inst, args, _budget = nil, &_block)
42
+ Sqreen.log.debug { "RecordRequestContext failing args: #{args.inspect}" }
40
43
  framework.clean_request
41
44
  advise_action(nil)
42
45
  end
@@ -58,9 +58,6 @@ module Sqreen
58
58
  attr_accessor :logged_in
59
59
  alias logged_in? logged_in
60
60
 
61
- attr_accessor :on_forked_worker
62
- alias on_forked_worker? on_forked_worker
63
-
64
61
  attr_reader :whitelisted_paths
65
62
  def update_whitelisted_paths(paths)
66
63
  @whitelisted_paths = paths.freeze
@@ -33,9 +33,10 @@ module Sqreen
33
33
  RETRY_CONNECT_SECONDS = 10
34
34
  RETRY_REQUEST_SECONDS = 10
35
35
 
36
- MAX_DELAY = 60 * 30
36
+ MAX_DELAY = 300
37
37
 
38
- RETRY_LONG = 128
38
+ RETRY_FOREVER = :forever
39
+ RETRY_MANY = 301
39
40
 
40
41
  MUTEX = Mutex.new
41
42
  METRICS_KEY = 'metrics'.freeze
@@ -108,14 +109,6 @@ module Sqreen
108
109
  end
109
110
  end
110
111
 
111
- def resilient_post(path, data, headers = {})
112
- post(path, data, headers, RETRY_LONG)
113
- end
114
-
115
- def resilient_get(path, headers = {})
116
- get(path, headers, RETRY_LONG)
117
- end
118
-
119
112
  def post(path, data, headers = {}, max_retry = 2)
120
113
  do_http_request(:POST, path, data, headers, max_retry)
121
114
  end
@@ -133,7 +126,7 @@ module Sqreen
133
126
 
134
127
  current_retry += 1
135
128
 
136
- raise e if current_retry >= max_retry || e.is_a?(Sqreen::NotImplementedYet)
129
+ raise e if max_retry != RETRY_FOREVER && current_retry >= max_retry || e.is_a?(Sqreen::NotImplementedYet) || e.is_a?(Sqreen::Unauthorized)
137
130
 
138
131
  sleep_delay = [MAX_DELAY, retry_request_seconds * current_retry].min
139
132
  Sqreen.log.debug format("Sleeping %ds before retry #{current_retry}/#{max_retry}", sleep_delay)
@@ -173,41 +166,44 @@ module Sqreen
173
166
  path = prefix_path(path)
174
167
  Sqreen.log.debug format('%s %s (%s)', method, path, @token)
175
168
 
176
- res = {}
169
+ payload = {}
177
170
  resiliently(RETRY_REQUEST_SECONDS, max_retry) do
178
- json = nil
171
+ res = nil
179
172
  MUTEX.synchronize do
180
- json = case method.upcase
181
- when :GET
182
- @con.get(path, headers)
183
- when :POST
184
- json_data = nil
185
- unless data.nil?
186
- serialized = Serializer.serialize(data)
187
- json_data = compress(SafeJSON.dump(serialized))
188
- end
189
- @con.post(path, json_data, headers)
190
- else
191
- Sqreen.log.debug format('unknown method %s', method)
192
- raise Sqreen::NotImplementedYet
193
- end
173
+ res = case method.upcase
174
+ when :GET
175
+ @con.get(path, headers)
176
+ when :POST
177
+ json_data = nil
178
+ unless data.nil?
179
+ serialized = Serializer.serialize(data)
180
+ json_data = compress(SafeJSON.dump(serialized))
181
+ end
182
+ @con.post(path, json_data, headers)
183
+ else
184
+ Sqreen.log.debug format('unknown method %s', method)
185
+ raise Sqreen::NotImplementedYet
186
+ end
187
+ end
188
+ if res && res.code == '401'
189
+ raise Sqreen::Unauthorized, 'HTTP 401: shall relogin'
194
190
  end
195
- if json && json.body
196
- if json['Content-Type'] && json['Content-Type'].start_with?('application/json')
197
- res = JSON.parse(json.body)
198
- unless res['status']
199
- Sqreen.log.debug(format('Cannot %s %s. Parsed response body was: %s', method, path, res.inspect))
191
+ if res && res.body
192
+ if res['Content-Type'] && res['Content-Type'].start_with?('application/json')
193
+ payload = JSON.parse(res.body)
194
+ unless payload['status']
195
+ Sqreen.log.debug(format('Cannot %s %s. Parsed response body was: %s', method, path, payload.inspect))
200
196
  end
201
197
  else
202
- Sqreen.log.debug "Unexpected response Content-Type: #{json['Content-Type']}"
203
- Sqreen.log.debug "Unexpected response body: #{json.body.inspect}"
198
+ Sqreen.log.debug "Unexpected response Content-Type: #{res['Content-Type']}"
199
+ Sqreen.log.debug "Unexpected response body: #{res.body.inspect}"
204
200
  end
205
201
  else
206
202
  Sqreen.log.debug 'warning: empty return value'
207
203
  end
208
204
  end
209
205
  Sqreen.log.debug format('%s %s (DONE in %f ms)', method, path, (Time.now.utc - now) * 1000)
210
- res
206
+ payload
211
207
  end
212
208
 
213
209
  def compress(data)
@@ -227,7 +223,7 @@ module Sqreen
227
223
 
228
224
  Sqreen.log.warn "Using app name: #{headers['x-app-name']}"
229
225
 
230
- res = resilient_post('app-login', RuntimeInfos.all(framework), headers)
226
+ res = post('app-login', RuntimeInfos.all(framework), headers, RETRY_FOREVER)
231
227
 
232
228
  if !res || !res['status']
233
229
  public_error = format('Cannot login. Token may be invalid: %s', @token)
@@ -243,7 +239,7 @@ module Sqreen
243
239
  end
244
240
 
245
241
  def rules
246
- resilient_get('rulespack')
242
+ get('rulespack', {}, RETRY_MANY)
247
243
  end
248
244
 
249
245
  def heartbeat(cmd_res = {}, metrics = [])
@@ -251,30 +247,29 @@ module Sqreen
251
247
  payload['metrics'] = metrics unless metrics.nil? || metrics.empty?
252
248
  payload['command_results'] = cmd_res unless cmd_res.nil? || cmd_res.empty?
253
249
 
254
- post('app-beat', payload.empty? ? nil : payload, {}, 5)
250
+ post('app-beat', payload.empty? ? nil : payload, {}, RETRY_MANY)
255
251
  end
256
252
 
257
253
  def post_metrics(metrics)
258
254
  return if metrics.nil? || metrics.empty?
259
255
  payload = { METRICS_KEY => metrics }
260
- resilient_post(METRICS_KEY, payload)
256
+ post(METRICS_KEY, payload, {}, RETRY_MANY)
261
257
  end
262
258
 
263
259
  def post_attack(attack)
264
- resilient_post('attack', attack.to_hash)
260
+ post('attack', attack.to_hash, {}, RETRY_MANY)
265
261
  end
266
262
 
267
263
  def post_bundle(bundle_sig, dependencies)
268
- resilient_post('bundle', 'bundle_signature' => bundle_sig,
269
- 'dependencies' => dependencies)
264
+ post('bundle', { 'bundle_signature' => bundle_sig, 'dependencies' => dependencies }, {}, RETRY_MANY)
270
265
  end
271
266
 
272
267
  def get_actionspack
273
- resilient_get('actionspack')
268
+ get('actionspack', {}, RETRY_MANY)
274
269
  end
275
270
 
276
271
  def post_request_record(request_record)
277
- resilient_post('request_record', request_record.to_hash)
272
+ post('request_record', request_record.to_hash, {}, RETRY_MANY)
278
273
  end
279
274
 
280
275
  # Post an exception to Sqreen for analysis
@@ -300,7 +295,7 @@ module Sqreen
300
295
  tally = Hash[events.group_by(&:class).map{ |k,v| [k, v.count] }]
301
296
  "Doing batch with the following tally of event types: #{tally}"
302
297
  end
303
- resilient_post(BATCH_KEY, BATCH_KEY => batch)
298
+ post(BATCH_KEY, { BATCH_KEY => batch }, {}, RETRY_MANY)
304
299
  end
305
300
 
306
301
  # Perform agent logout