sqreen 1.20.1-java → 1.21.0.beta3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +16 -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 +9 -2
  8. data/lib/sqreen/conditionable.rb +24 -6
  9. data/lib/sqreen/configuration.rb +1 -1
  10. data/lib/sqreen/deferred_logger.rb +50 -14
  11. data/lib/sqreen/deliveries/batch.rb +8 -1
  12. data/lib/sqreen/deprecation.rb +38 -0
  13. data/lib/sqreen/ecosystem.rb +96 -0
  14. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  15. data/lib/sqreen/ecosystem/exception_reporting.rb +26 -0
  16. data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
  17. data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
  18. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  19. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  20. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  21. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  22. data/lib/sqreen/ecosystem/module_api/message_producer.rb +51 -0
  23. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
  24. data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
  25. data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
  26. data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
  27. data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
  28. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  29. data/lib/sqreen/ecosystem/module_registry.rb +44 -0
  30. data/lib/sqreen/ecosystem/redis/redis_connection.rb +43 -0
  31. data/lib/sqreen/ecosystem/tracing/modules/client.rb +31 -0
  32. data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
  33. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  34. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  35. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  36. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  37. data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
  38. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  39. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  40. data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
  41. data/lib/sqreen/ecosystem_integration.rb +87 -0
  42. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +99 -0
  43. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +42 -0
  44. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
  45. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  46. data/lib/sqreen/events/request_record.rb +0 -1
  47. data/lib/sqreen/frameworks/generic.rb +24 -1
  48. data/lib/sqreen/frameworks/rails.rb +0 -7
  49. data/lib/sqreen/frameworks/request_recorder.rb +2 -0
  50. data/lib/sqreen/graft/call.rb +106 -19
  51. data/lib/sqreen/graft/callback.rb +1 -1
  52. data/lib/sqreen/graft/hook.rb +212 -100
  53. data/lib/sqreen/graft/hook_point.rb +18 -11
  54. data/lib/sqreen/legacy/instrumentation.rb +22 -10
  55. data/lib/sqreen/legacy/old_event_submission_strategy.rb +9 -2
  56. data/lib/sqreen/log.rb +3 -2
  57. data/lib/sqreen/log/loggable.rb +1 -0
  58. data/lib/sqreen/logger.rb +24 -0
  59. data/lib/sqreen/metrics.rb +1 -0
  60. data/lib/sqreen/metrics/req_detailed.rb +41 -0
  61. data/lib/sqreen/metrics_store.rb +11 -0
  62. data/lib/sqreen/null_logger.rb +22 -0
  63. data/lib/sqreen/remote_command.rb +4 -0
  64. data/lib/sqreen/rules.rb +8 -4
  65. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  66. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  67. data/lib/sqreen/rules/rule_cb.rb +4 -2
  68. data/lib/sqreen/rules/waf_cb.rb +3 -3
  69. data/lib/sqreen/runner.rb +63 -8
  70. data/lib/sqreen/session.rb +2 -0
  71. data/lib/sqreen/signals/conversions.rb +6 -1
  72. data/lib/sqreen/version.rb +1 -1
  73. data/lib/sqreen/weave/budget.rb +35 -0
  74. data/lib/sqreen/weave/legacy/instrumentation.rb +274 -132
  75. data/lib/sqreen/worker.rb +6 -2
  76. metadata +46 -9
  77. data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -0,0 +1,53 @@
1
+ require 'sqreen/kit/configuration'
2
+ require 'sqreen/kit/signals/point'
3
+ require 'sqreen/kit/signals/dto_helper'
4
+
5
+ # reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/tracing/client-2020-04-21/schema.cue
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Tracing
10
+ module Signals
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class Sqreen::Ecosystem::Tracing::Signals::TracingClient < Sqreen::Kit::Signals::Point
17
+ readonly_attrs :payload_schema, :source, :signal_name
18
+
19
+ def initialize(values = {})
20
+ self.payload_schema = Payload::SCHEMA_VERSION
21
+ self.source = Sqreen::Kit::Configuration.default_source
22
+ self.signal_name = 'tracing.client'
23
+ self.time = values[:time] || Time.now
24
+ super
25
+ end
26
+
27
+ def payload=(payload)
28
+ unless payload.is_a?(Payload)
29
+ raise ArgumentError, "Payload should be a #{Payload}"
30
+ end
31
+ super
32
+ end
33
+
34
+ class Payload
35
+ include Sqreen::Kit::Signals::DtoHelper
36
+
37
+ add_mandatory_attrs :transport, :host
38
+
39
+ SCHEMA_VERSION = 'tracing/client-2020-04-21'.freeze
40
+
41
+ # @return [Symbol]
42
+ attr_accessor :transport
43
+
44
+ # @return [String]
45
+ attr_accessor :host
46
+
47
+ # @return [String]
48
+ attr_accessor :ip
49
+
50
+ # @return [String]
51
+ attr_accessor :tracing_identifier
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ require 'sqreen/kit/configuration'
2
+ require 'sqreen/kit/signals/point'
3
+ require 'sqreen/kit/signals/dto_helper'
4
+
5
+ # reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/tracing/server-2020-04-21/schema.cue
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Tracing
10
+ module Signals
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class Sqreen::Ecosystem::Tracing::Signals::TracingServer < Sqreen::Kit::Signals::Point
17
+ readonly_attrs :payload_schema, :source, :signal_name
18
+
19
+ def initialize(values = {})
20
+ self.payload_schema = Payload::SCHEMA_VERSION
21
+ self.source = Sqreen::Kit::Configuration.default_source
22
+ self.signal_name = 'tracing.server'
23
+ self.time = values[:time] || Time.now
24
+ super
25
+ end
26
+
27
+ def payload=(payload)
28
+ unless payload.is_a?(Payload)
29
+ raise ArgumentError, "Payload should be a #{Payload}"
30
+ end
31
+ super
32
+ end
33
+
34
+ class Payload
35
+ include Sqreen::Kit::Signals::DtoHelper
36
+
37
+ add_mandatory_attrs :transport, :client_ip
38
+
39
+ SCHEMA_VERSION = 'tracing/server-2020-04-21'.freeze
40
+
41
+ # @return [Symbol]
42
+ attr_accessor :transport
43
+
44
+ # @return [String]
45
+ attr_accessor :client_ip
46
+
47
+ # @return [Array<String>]
48
+ attr_accessor :previous_hops
49
+
50
+ # @return [String]
51
+ attr_accessor :tracing_identifier
52
+ end
53
+ end
@@ -0,0 +1,101 @@
1
+ require 'sqreen/ecosystem/loggable'
2
+ require 'sqreen/ecosystem/exception_reporting'
3
+
4
+ module Sqreen
5
+ module Ecosystem
6
+ class TracingBroker
7
+ include Loggable
8
+ include ExceptionReporting
9
+
10
+ # Stores a lookup resolution so that lookup (incl. sampling) is done
11
+ # only once, not in the beginning of the producer code (when it asks
12
+ # whether it should proceed) and again once it delivers the data
13
+ ObserverLookup = Struct.new(:modules)
14
+
15
+ # @return [Sqreen::Ecosystem::Tracing::SamplingConfiguration]
16
+ attr_writer :sampling_configuration
17
+
18
+ # @param [Array<Sqreen::Ecosystem::ModuleApi::Tracing>] tracing_modules
19
+ def initialize(tracing_modules)
20
+ @sampling_configuration = nil
21
+ @type_to_subscribers = {}
22
+ tracing_modules.each do |mod|
23
+ consumed_type = mod.consumed_type
24
+ @type_to_subscribers[consumed_type] ||= []
25
+ @type_to_subscribers[consumed_type] << mod
26
+ end
27
+ end
28
+
29
+ # @param [Object] data
30
+ # @param [Sqreen::Ecosystem::TracingBroker::ObserverLookup] prev_lookup
31
+ def publish(data, prev_lookup)
32
+ prev_lookup.modules.each do |mod|
33
+ mod_process_data mod, data
34
+ end
35
+ end
36
+
37
+ # @param [Module] data_type
38
+ # @param [Hash] _hints reserved for future use, e.g. virtual scopes
39
+ # @return [Sqreen::Ecosystem::TracingBroker::ObserverLookup]
40
+ def interested_consumers(data_type, _hints = {})
41
+ unless @sampling_configuration
42
+ logger.debug do
43
+ "Declaring no one is interested in #{data_type} " \
44
+ "because tracing hasn't been enabled yet"
45
+ end
46
+ return false
47
+ end
48
+
49
+ # if we have several modules with the same scope, we
50
+ # should ask whether we should sample only once
51
+ scope_to_should_sample = Hash.new do |hash, scope|
52
+ result = @sampling_configuration.should_sample?(scope)
53
+ if result
54
+ logger.debug { "Will sample scope #{scope}. Sampling line: #{result}" }
55
+ else
56
+ logger.debug { "Will NOT sample scope #{scope}" }
57
+ end
58
+
59
+ hash[scope] = result
60
+ end
61
+
62
+ res = subscribers(data_type).select do |mod|
63
+ scope_to_should_sample[mod.scope]
64
+ end
65
+
66
+ res.empty? ? false : ObserverLookup.new(res)
67
+ end
68
+
69
+ private
70
+
71
+ # @param [Sqreen::Ecosystem::ModuleApi::Tracing] mod
72
+ def mod_process_data(mod, data)
73
+ mod.receive(data)
74
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
75
+ report_exception("Error invoking tracing module #{mod}", e)
76
+ end
77
+
78
+ # @param [Module] data_type
79
+ # @return Array<Sqreen::Ecosystem::ModuleApi::Tracing>
80
+ def subscribers(data_type)
81
+ subscribers = @type_to_subscribers[data_type]
82
+
83
+ # None of the modules subscribes to data_type directly,
84
+ # but maybe they subscribe to one of the ancestors
85
+ # Cache this lookup
86
+ unless subscribers
87
+ subscribers = parents(data_type).inject([]) do |accum, type|
88
+ accum + (@type_to_subscribers[type] || [])
89
+ end
90
+ @type_to_subscribers[data_type] = subscribers
91
+ end
92
+
93
+ subscribers
94
+ end
95
+
96
+ def parents(type)
97
+ type.ancestors - Object.ancestors - [type]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -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,87 @@
1
+ require 'sqreen/log/loggable'
2
+ require 'sqreen/metrics_store'
3
+ require 'sqreen/ecosystem'
4
+ require 'sqreen/ecosystem/dispatch_table'
5
+ require 'sqreen/ecosystem_integration/around_callbacks'
6
+ require 'sqreen/ecosystem_integration/instrumentation_service'
7
+ require 'sqreen/ecosystem_integration/request_lifecycle_tracking'
8
+ require 'sqreen/ecosystem_integration/signal_consumption'
9
+
10
+ module Sqreen
11
+ # This class is the interface through which the agent interacts
12
+ # with the ecosystem.
13
+ #
14
+ # Other classes in the EcosystemIntegration module implement the
15
+ # functionality that the ecosystem requires in order to deliver
16
+ # data to the agent and to be informed by the agent of certain
17
+ # key events (see Sqreen::Ecosystem::DispatchTable).
18
+ class EcosystemIntegration
19
+ include Sqreen::Log::Loggable
20
+
21
+ # @param [Sqreen::Framework] framework
22
+ # @param [Proc] create_binning_metric
23
+ def initialize(framework, queue, create_binning_metric)
24
+ @framework = framework
25
+ @queue = queue
26
+ # XXX: created metrics are insulated from feature upgrades
27
+ @create_binning_metric = create_binning_metric
28
+ @request_lifecycle = RequestLifecycleTracking.new
29
+ @online = false
30
+ end
31
+
32
+ def init
33
+ raise 'already initialized' if @online
34
+
35
+ setup_dispatch_table
36
+ AroundCallbacks.create_metric = @create_binning_metric
37
+ Ecosystem.init
38
+ logger.info 'Ecosystem successfully initialized'
39
+ @online = true
40
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
41
+ logger.warn { "Error initializing Ecosystem: #{e.message}" }
42
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
43
+ Sqreen::RemoteException.record(e)
44
+ end
45
+
46
+ def disable
47
+ raise NotImplementedYet
48
+ end
49
+
50
+ def request_start(rack_request)
51
+ return unless @online
52
+
53
+ Ecosystem.start_transaction
54
+ @request_lifecycle.notify_request_start(rack_request)
55
+ end
56
+
57
+ def request_end
58
+ return unless @online
59
+
60
+ Ecosystem.end_transaction
61
+ end
62
+
63
+ def handle_tracing_command(trace_id_prefix, scopes_config)
64
+ return unless @online
65
+
66
+ Ecosystem.configure_sampling(trace_id_prefix, scopes_config)
67
+ end
68
+
69
+ private
70
+
71
+ def setup_dispatch_table
72
+ Ecosystem::DispatchTable.consume_signal =
73
+ create_signal_consumption.method(:consume_signal)
74
+
75
+ Ecosystem::DispatchTable.add_request_start_listener =
76
+ @request_lifecycle.method(:add_start_observer)
77
+
78
+ Ecosystem::DispatchTable.fetch_logger = lambda { logger }
79
+
80
+ Ecosystem::DispatchTable.instrument = InstrumentationService.method(:instrument)
81
+ end
82
+
83
+ def create_signal_consumption
84
+ SignalConsumption.new(@framework, @request_lifecycle, @queue)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,99 @@
1
+ require 'sqreen/metrics_store'
2
+ require 'sqreen/log/loggable'
3
+ require 'sqreen/events/remote_exception'
4
+ require 'sqreen/mono_time'
5
+ require 'sqreen/graft/call'
6
+
7
+ module Sqreen
8
+ class EcosystemIntegration
9
+ module AroundCallbacks
10
+ class << self
11
+ include Log::Loggable::ClassMethods
12
+
13
+ # @return [Proc]
14
+ attr_accessor :create_metric
15
+
16
+ # for instrumentation hooks
17
+ # instrumentation hooks already handle budgets/metrics
18
+ def wrap_instrumentation_hook(module_name, action, callable)
19
+ # make tag similar to that of rules
20
+ # TODO: move to structured tags to avoid silly string manips
21
+ action_for_metric = if %w[pre post failing finally].include?(action)
22
+ action
23
+ else
24
+ 'pre'
25
+ end
26
+ metric_name = "sq.ecosystem_#{module_name}.#{action_for_metric}"
27
+ create_metric.call(metric_name)
28
+
29
+ Proc.new do |*args|
30
+ begin
31
+ callable.call(*args)
32
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
33
+ # 2) rescue exceptions
34
+ logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
35
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
36
+ Sqreen::RemoteException.record(e)
37
+ end # end begin
38
+ end # end proc
39
+ end
40
+
41
+ # XXX: not used yet
42
+ def wrap_generic_callback(module_name, action, callable)
43
+ timer_name = "ecosystem:#{module_name}@#{action}"
44
+ perf_notif_name = "ecosystem_#{module_name}"
45
+
46
+ # XXX: register metric
47
+
48
+ Proc.new do |*args|
49
+ begin
50
+ req_storage = Thread.current[:sqreen_http_request]
51
+
52
+ timer = Graft::Timer.new(timer_name) do |t|
53
+ # this is an epilogue to measure()
54
+ req_storage && req_storage[:timed_hooks] << t
55
+ end
56
+
57
+ req_timer = nil
58
+ timer.measure do
59
+ # not in a request, no budget; call cb
60
+ next callable.call(*args) unless req_storage
61
+
62
+ # 1) budget enforcement
63
+ # skip callback if budget already expended
64
+ next if req_storage[:time_budget_expended]
65
+
66
+ budget = req_storage[:time_budget]
67
+ if budget
68
+ req_timer = req_storage[:timer]
69
+ remaining = budget - req_timer.elapsed
70
+ unless remaining > 0
71
+ req_storage[:time_budget_expended] = true
72
+ next # skip callback
73
+ end
74
+ end
75
+
76
+ callable.call(*args)
77
+ end
78
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
79
+ # 2) rescue exceptions
80
+ logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
81
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
82
+ Sqreen::RemoteException.record(e)
83
+ ensure
84
+ # 3) contribute to performance metrics
85
+ if timer
86
+ req_timer.include_measurements(timer) if req_timer
87
+
88
+ # XXX: PerformanceNotifications is used no more
89
+ Sqreen::PerformanceNotifications.notify(
90
+ perf_notif_name, action, *timer.start_and_end
91
+ )
92
+ end
93
+ end # end begin
94
+ end # end proc
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end