sqreen 1.20.1-java → 1.22.0-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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/lib/sqreen/actions/block_user.rb +1 -1
  4. data/lib/sqreen/actions/redirect_ip.rb +1 -1
  5. data/lib/sqreen/actions/redirect_user.rb +1 -1
  6. data/lib/sqreen/attack_detected.html +1 -2
  7. data/lib/sqreen/condition_evaluator.rb +8 -2
  8. data/lib/sqreen/configuration.rb +1 -1
  9. data/lib/sqreen/deferred_logger.rb +50 -14
  10. data/lib/sqreen/deliveries/batch.rb +8 -1
  11. data/lib/sqreen/dependency/detector.rb +11 -3
  12. data/lib/sqreen/dependency/new_relic.rb +10 -1
  13. data/lib/sqreen/deprecation.rb +38 -0
  14. data/lib/sqreen/ecosystem.rb +123 -0
  15. data/lib/sqreen/ecosystem/databases/database_connection_data.rb +23 -0
  16. data/lib/sqreen/ecosystem/databases/mongo.rb +39 -0
  17. data/lib/sqreen/ecosystem/databases/mysql.rb +54 -0
  18. data/lib/sqreen/ecosystem/databases/postgres.rb +51 -0
  19. data/lib/sqreen/ecosystem/databases/redis.rb +36 -0
  20. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  21. data/lib/sqreen/ecosystem/exception_reporting.rb +28 -0
  22. data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
  23. data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
  24. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  25. data/lib/sqreen/ecosystem/messaging/bunny.rb +61 -0
  26. data/lib/sqreen/ecosystem/messaging/kafka.rb +70 -0
  27. data/lib/sqreen/ecosystem/messaging/kinesis.rb +66 -0
  28. data/lib/sqreen/ecosystem/messaging/sqs.rb +68 -0
  29. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  30. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  31. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  32. data/lib/sqreen/ecosystem/module_api/message_producer.rb +57 -0
  33. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
  34. data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
  35. data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
  36. data/lib/sqreen/ecosystem/module_api/tracing/consumer_data.rb +13 -0
  37. data/lib/sqreen/ecosystem/module_api/tracing/messaging_data.rb +35 -0
  38. data/lib/sqreen/ecosystem/module_api/tracing/producer_data.rb +13 -0
  39. data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
  40. data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
  41. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  42. data/lib/sqreen/ecosystem/module_registry.rb +48 -0
  43. data/lib/sqreen/ecosystem/tracing/modules/client.rb +35 -0
  44. data/lib/sqreen/ecosystem/tracing/modules/consumer.rb +35 -0
  45. data/lib/sqreen/ecosystem/tracing/modules/determine_ip.rb +28 -0
  46. data/lib/sqreen/ecosystem/tracing/modules/producer.rb +35 -0
  47. data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
  48. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  49. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  50. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  51. data/lib/sqreen/ecosystem/tracing/signals/tracing_consumer.rb +56 -0
  52. data/lib/sqreen/ecosystem/tracing/signals/tracing_producer.rb +56 -0
  53. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  54. data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
  55. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  56. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  57. data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
  58. data/lib/sqreen/ecosystem_integration.rb +81 -0
  59. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +89 -0
  60. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +38 -0
  61. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
  62. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  63. data/lib/sqreen/events/request_record.rb +0 -1
  64. data/lib/sqreen/frameworks/generic.rb +36 -1
  65. data/lib/sqreen/frameworks/rails.rb +0 -7
  66. data/lib/sqreen/frameworks/request_recorder.rb +2 -0
  67. data/lib/sqreen/graft/call.rb +85 -18
  68. data/lib/sqreen/graft/callback.rb +1 -1
  69. data/lib/sqreen/graft/hook.rb +192 -88
  70. data/lib/sqreen/graft/hook_point.rb +18 -11
  71. data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +2 -0
  72. data/lib/sqreen/legacy/instrumentation.rb +22 -10
  73. data/lib/sqreen/legacy/old_event_submission_strategy.rb +9 -2
  74. data/lib/sqreen/log.rb +3 -2
  75. data/lib/sqreen/log/loggable.rb +1 -0
  76. data/lib/sqreen/logger.rb +24 -0
  77. data/lib/sqreen/metrics_store.rb +11 -0
  78. data/lib/sqreen/null_logger.rb +22 -0
  79. data/lib/sqreen/remote_command.rb +4 -0
  80. data/lib/sqreen/rules.rb +8 -4
  81. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  82. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  83. data/lib/sqreen/rules/rule_cb.rb +2 -0
  84. data/lib/sqreen/rules/waf_cb.rb +3 -3
  85. data/lib/sqreen/runner.rb +47 -7
  86. data/lib/sqreen/session.rb +2 -0
  87. data/lib/sqreen/signals/conversions.rb +6 -1
  88. data/lib/sqreen/version.rb +1 -1
  89. data/lib/sqreen/weave/budget.rb +46 -0
  90. data/lib/sqreen/weave/legacy/instrumentation.rb +252 -109
  91. data/lib/sqreen/worker.rb +6 -2
  92. metadata +60 -11
  93. data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -0,0 +1,34 @@
1
+ require 'sqreen/ecosystem/module_registry'
2
+ require 'sqreen/ecosystem/transaction_storage'
3
+ require 'sqreen/ecosystem/module_api/signal_producer'
4
+
5
+ module Sqreen
6
+ module Ecosystem
7
+ class TracingIdSetup
8
+ # @param [Array<Sqreen::Ecosystem::ModuleApi::SignalProducer>] signal_producer_modules
9
+ def initialize(signal_producer_modules)
10
+ @modules = signal_producer_modules
11
+ @tracing_id_prefix = nil
12
+ end
13
+
14
+ def setup_modules
15
+ inject_out_of_tx_tracing_id_gen
16
+ end
17
+
18
+ attr_writer :tracing_id_prefix
19
+
20
+ private
21
+
22
+ def inject_out_of_tx_tracing_id_gen
23
+ @modules.each do |mod|
24
+ mod.tracing_id_producer = method(:generate_tracing_id)
25
+ end
26
+ end
27
+
28
+ def generate_tracing_id
29
+ return nil unless @tracing_id_prefix
30
+ "#{@tracing_id_prefix}.#{SecureRandom.uuid}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,64 @@
1
+ require 'sqreen/ecosystem/loggable'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ # The transaction storage is a mechanism for the modules to share
6
+ # request-scoped data with each other or to keep request-scoped objects
7
+ # themselves, although, for this last use case to be effective,
8
+ # propagation of request start/end events to the modules will likely be
9
+ # needed. A more generic notification of data availability to modules
10
+ # also may be needed in the future.
11
+ #
12
+ # This is not be used to share data with the Ecosystem client
13
+ #
14
+ # This class is not thread safe because it can call the lazy getter
15
+ # twice if [] is called concurrently.
16
+ class TransactionStorage
17
+ include Loggable
18
+
19
+ class << self
20
+ # @return [Sqreen::Ecosystem::TransactionStorage]
21
+ def create_thread_local
22
+ Thread.current[:tx_storage] = new
23
+ end
24
+
25
+ # @return [Sqreen::Ecosystem::TransactionStorage]
26
+ def fetch_thread_local
27
+ Thread.current[:tx_storage]
28
+ end
29
+
30
+ def destroy_thread_local
31
+ Thread.current[:tx_storage] = nil
32
+ end
33
+ end
34
+
35
+ def initialize
36
+ @values = {}
37
+ @values_lazy = {}
38
+ end
39
+
40
+ def []=(key, value)
41
+ @values[key] = value
42
+ end
43
+
44
+ def set_lazy(key, &block)
45
+ @values_lazy[key] = block
46
+ end
47
+
48
+ def [](key)
49
+ v = @values[key]
50
+ return v unless v.nil?
51
+
52
+ v = @values_lazy[key]
53
+ return nil if v.nil?
54
+
55
+ begin
56
+ @values[key] = v.call
57
+ rescue StandardError => e
58
+ logger.warn { "Error resolving key #{e} with lazy value: #{e.message}" }
59
+ raise
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,13 @@
1
+ module Sqreen
2
+ module Ecosystem
3
+ module Util
4
+ module CallWritersFromInit
5
+ def initialize(values = {})
6
+ values.each do |attr, val|
7
+ public_send("#{attr}=", val)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,81 @@
1
+ require 'sqreen/log/loggable'
2
+ require 'sqreen/ecosystem'
3
+ require 'sqreen/ecosystem/dispatch_table'
4
+ require 'sqreen/ecosystem_integration/instrumentation_service'
5
+ require 'sqreen/ecosystem_integration/request_lifecycle_tracking'
6
+ require 'sqreen/ecosystem_integration/signal_consumption'
7
+
8
+ module Sqreen
9
+ # This class is the interface through which the agent interacts
10
+ # with the ecosystem.
11
+ #
12
+ # Other classes in the EcosystemIntegration module implement the
13
+ # functionality that the ecosystem requires in order to deliver
14
+ # data to the agent and to be informed by the agent of certain
15
+ # key events (see Sqreen::Ecosystem::DispatchTable).
16
+ class EcosystemIntegration
17
+ include Sqreen::Log::Loggable
18
+
19
+ # @param [Sqreen::Framework] framework
20
+ def initialize(framework, queue)
21
+ @framework = framework
22
+ @queue = queue
23
+ @request_lifecycle = RequestLifecycleTracking.new
24
+ @online = false
25
+ end
26
+
27
+ def init
28
+ raise 'already initialized' if @online
29
+
30
+ setup_dispatch_table
31
+ Ecosystem.init
32
+ logger.info 'Ecosystem successfully initialized'
33
+ @online = true
34
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
35
+ logger.warn { "Error initializing Ecosystem: #{e.message}" }
36
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
37
+ Sqreen::RemoteException.record(e)
38
+ end
39
+
40
+ def disable
41
+ raise NotImplementedYet
42
+ end
43
+
44
+ def request_start(rack_request)
45
+ return unless @online
46
+
47
+ Ecosystem.start_transaction
48
+ @request_lifecycle.notify_request_start(rack_request)
49
+ end
50
+
51
+ def request_end
52
+ return unless @online
53
+
54
+ Ecosystem.end_transaction
55
+ end
56
+
57
+ def handle_tracing_command(trace_id_prefix, scopes_config)
58
+ return unless @online
59
+
60
+ Ecosystem.configure_sampling(trace_id_prefix, scopes_config)
61
+ end
62
+
63
+ private
64
+
65
+ def setup_dispatch_table
66
+ Ecosystem::DispatchTable.consume_signal =
67
+ create_signal_consumption.method(:consume_signal)
68
+
69
+ Ecosystem::DispatchTable.add_request_start_listener =
70
+ @request_lifecycle.method(:add_start_observer)
71
+
72
+ Ecosystem::DispatchTable.fetch_logger = lambda { logger }
73
+
74
+ Ecosystem::DispatchTable.instrument = InstrumentationService.method(:instrument)
75
+ end
76
+
77
+ def create_signal_consumption
78
+ SignalConsumption.new(@framework, @request_lifecycle, @queue)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,89 @@
1
+ require 'sqreen/log/loggable'
2
+ require 'sqreen/events/remote_exception'
3
+ require 'sqreen/mono_time'
4
+
5
+ module Sqreen
6
+ class EcosystemIntegration
7
+ module AroundCallbacks
8
+ class << self
9
+ include Log::Loggable::ClassMethods
10
+
11
+ # for instrumentation hooks
12
+ # instrumentation hooks already handle budgets, so nothing
13
+ # to do in that respect
14
+ def wrap_instrumentation_hook(module_name, action, callable)
15
+ perf_notif_name = "ecosystem_#{module_name}"
16
+
17
+ Proc.new do |*args|
18
+ begin
19
+ start = Sqreen.time
20
+ callable.call(*args)
21
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
22
+ # 2) rescue exceptions
23
+ logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
24
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
25
+ Sqreen::RemoteException.record(e)
26
+ ensure
27
+ # 3) contribute to performance metrics
28
+ stop = Sqreen.time
29
+ Sqreen::PerformanceNotifications.notify(perf_notif_name, action, start, stop)
30
+ end # end proc
31
+ end # end begin
32
+ end
33
+
34
+ # XXX: not used yet
35
+ def wrap_generic_callback(module_name, action, callable)
36
+ timer_name = "ecosystem:#{module_name}@#{action}"
37
+ perf_notif_name = "ecosystem_#{module_name}"
38
+
39
+ Proc.new do |*args|
40
+ begin
41
+ req_storage = Thread.current[:sqreen_http_request]
42
+
43
+ timer = Graft::Timer.new(timer_name) do |t|
44
+ # this is an epilogue to measure()
45
+ req_storage && req_storage[:timed_hooks] << t
46
+ end
47
+
48
+ req_timer = nil
49
+ timer.measure do
50
+ # not in a request, no budget; call cb
51
+ next callable.call(*args) unless req_storage
52
+
53
+ # 1) budget enforcement
54
+ # skip callback if budget already expended
55
+ next if req_storage[:time_budget_expended]
56
+
57
+ budget = req_storage[:time_budget]
58
+ if budget
59
+ req_timer = req_storage[:timer]
60
+ remaining = budget - req_timer.elapsed
61
+ unless remaining > 0
62
+ req_storage[:time_budget_expended] = true
63
+ next # skip callback
64
+ end
65
+ end
66
+
67
+ callable.call(*args)
68
+ end
69
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
70
+ # 2) rescue exceptions
71
+ logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
72
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
73
+ Sqreen::RemoteException.record(e)
74
+ ensure
75
+ # 3) contribute to performance metrics
76
+ if timer
77
+ req_timer.include_measurements(timer) if req_timer
78
+
79
+ Sqreen::PerformanceNotifications.notify(
80
+ perf_notif_name, action, *timer.start_and_end
81
+ )
82
+ end
83
+ end # end begin
84
+ end # end proc
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,38 @@
1
+ require 'sqreen/graft/hook'
2
+ require 'sqreen/ecosystem_integration/around_callbacks'
3
+
4
+ module Sqreen
5
+ class EcosystemIntegration
6
+ module InstrumentationService
7
+ class << self
8
+ # @param [String] module_name
9
+ # @param [String] method in form A::B#c or A::B.c
10
+ # @param [Hash{Symbol=>Proc}] spec
11
+ def instrument(module_name, method, spec)
12
+ hook = Sqreen::Graft::Hook[method].add do
13
+ if spec[:before]
14
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'pre', spec[:before])
15
+ before(nil, flow: true, &cb)
16
+ end
17
+
18
+ if spec[:after]
19
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'post', spec[:after])
20
+ after(nil, flow: true, &cb)
21
+ end
22
+
23
+ if spec[:raised]
24
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'failing', spec[:raised])
25
+ raised(nil, flow: true, &cb)
26
+ end
27
+
28
+ if spec[:ensured]
29
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'finally', spec[:ensured])
30
+ ensured(nil, flow: true, &cb)
31
+ end
32
+ end
33
+ hook.install
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,58 @@
1
+ require 'sqreen/events/remote_exception'
2
+ require 'sqreen/log/loggable'
3
+
4
+ module Sqreen
5
+ class EcosystemIntegration
6
+ # This class gets notified of request start/end and
7
+ # 1) distributes such events to listeners (typically ecosystem modules;
8
+ # the method add_start_observer is exposed to ecosystem modules through
9
+ # +Sqreen::Ecosystem::ModuleApi::EventListener+ and the dispatch table).
10
+ # 2) keeps track of whether a request is active on this thread. This is
11
+ # so that users of this class can have this information without needing
12
+ # to subscribe to request start/events and keeping thread local state
13
+ # themselves.
14
+ # XXX: Since the Ecosystem is also notified of request start/end, it could
15
+ # notify its modules of request start without going through the dispatch
16
+ # table and call add_start_observer. We need to think if we want to keep
17
+ # the transaction / request distinction or if they should just be
18
+ # assumed to be the same, though.
19
+ class RequestLifecycleTracking
20
+ include Sqreen::Log::Loggable
21
+
22
+ def initialize
23
+ @start_observers = []
24
+ @tl_key = "#{object_id}_req_in_flight"
25
+ end
26
+
27
+ # API for classes needing to know the request state
28
+
29
+ # @param cb A callback taking a Rack::Request
30
+ def add_start_observer(cb)
31
+ @start_observers << cb
32
+ end
33
+
34
+ def in_request?
35
+ Thread.current[@tl_key] ? true : false
36
+ end
37
+
38
+ # API for classes notifying this one of request events
39
+
40
+ def notify_request_start(rack_req)
41
+ Thread.current[@tl_key] = true
42
+ return if @start_observers.empty?
43
+ @start_observers.each do |cb|
44
+ begin
45
+ cb.call(rack_req)
46
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
47
+ logger.warn { "Error calling #{cb} on request start: #{e.message}" }
48
+ Sqreen::RemoteException.record(e)
49
+ end
50
+ end
51
+ end
52
+
53
+ def notify_request_end
54
+ Thread.current[@tl_key] = false
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ require 'sqreen/log/loggable'
2
+
3
+ module Sqreen
4
+ class EcosystemIntegration
5
+ class SignalConsumption
6
+ include Sqreen::Log::Loggable
7
+
8
+ PAYLOAD_CREATOR_SECTIONS = %w[request response params headers].freeze
9
+
10
+ # @param [Sqreen::Frameworks::GenericFramework] framework
11
+ # @param [Sqreen::EcosystemIntegration::RequestLifecycleTracking]
12
+ # @param [Sqreen::CappedQueue]
13
+ def initialize(framework, req_lifecycle, queue)
14
+ @framework = framework
15
+ @req_lifecycle = req_lifecycle
16
+ @queue = queue
17
+ end
18
+
19
+ def consume_signal(signal)
20
+ # transitional
21
+ unless Sqreen.features.fetch('use_signals', DEFAULT_USE_SIGNALS)
22
+ logger.debug { "Discarding signal #{signal} (signals disabled)" }
23
+ return
24
+ end
25
+
26
+ if @req_lifecycle.in_request?
27
+ # add it to the request record
28
+ @framework.observe(:signals, signal, PAYLOAD_CREATOR_SECTIONS, true)
29
+ else
30
+ @queue.push signal
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -8,7 +8,6 @@
8
8
  require 'json'
9
9
  require 'sqreen/log'
10
10
  require 'sqreen/event'
11
- require 'sqreen/encoding_sanitizer'
12
11
  require 'sqreen/sensitive_data_redactor'
13
12
 
14
13
  module Sqreen
@@ -22,8 +22,17 @@ module Sqreen
22
22
  include RequestRecorder
23
23
  attr_accessor :sqreen_configuration
24
24
 
25
+ attr_writer :req_start_cb, :req_end_cb
26
+
25
27
  def initialize
26
28
  clean_request_record
29
+
30
+ # for notifying the ecosystem of request boundaries
31
+ # XXX: this should be refactored. It shouldn't be
32
+ # the framework doing these notifications to the ecosystem
33
+ # Probably the rule callback should do it itself
34
+ @req_start_cb = Proc.new {}
35
+ @req_end_cb = Proc.new {}
27
36
  end
28
37
 
29
38
  # What kind of database is this
@@ -209,7 +218,16 @@ module Sqreen
209
218
 
210
219
  # Should the agent not be starting up?
211
220
  def prevent_startup
221
+ # SQREEN-880 - prevent Sqreen startup on Sidekiq workers
222
+ return :sidekiq_cli if defined?(Sidekiq::CLI)
223
+ return :delayed_job if defined?(Delayed::Command)
224
+
225
+ # Prevent Sqreen startup on rake tasks - unless this is a Sqreen test
226
+ run_in_test = sqreen_configuration.get(:run_in_test)
227
+ return :rake if !run_in_test && $0.end_with?('rake')
228
+
212
229
  return :irb if $0 == 'irb'
230
+
213
231
  return if sqreen_configuration.nil?
214
232
  disable = sqreen_configuration.get(:disable)
215
233
  return :config_disable if disable == true || disable.to_s.to_i == 1
@@ -251,8 +269,12 @@ module Sqreen
251
269
  # Nota: cleanup should be performed at end of request (see clean_request)
252
270
  def store_request(object)
253
271
  return unless ensure_rack_loaded
272
+
273
+ rack_req = Rack::Request.new(object)
274
+ @req_start_cb.call(rack_req)
275
+
254
276
  self.remaining_perf_budget = Sqreen.performance_budget
255
- SharedStorage.set(:request, Rack::Request.new(object))
277
+ SharedStorage.set(:request, rack_req)
256
278
  SharedStorage.set(:xss_params, nil)
257
279
  SharedStorage.set(:whitelisted, nil)
258
280
  SharedStorage.set(:request_overtime, nil)
@@ -281,6 +303,7 @@ module Sqreen
281
303
  SharedStorage.set(:xss_params, nil)
282
304
  SharedStorage.set(:whitelisted, nil)
283
305
  SharedStorage.set(:request_overtime, nil)
306
+ @req_end_cb.call
284
307
  end
285
308
 
286
309
  def remaining_perf_budget
@@ -377,6 +400,18 @@ module Sqreen
377
400
  r
378
401
  end
379
402
 
403
+ def body
404
+ return nil unless request.respond_to?(:body)
405
+ return nil unless request.body.respond_to?(:read)
406
+ return nil unless request.body.respond_to?(:rewind)
407
+
408
+ body_io = request.body
409
+ body = body_io.read(4096)
410
+ body_io.rewind
411
+
412
+ body
413
+ end
414
+
380
415
  # Expose current working directory
381
416
  def cwd
382
417
  Dir.getwd