sqreen 1.20.4 → 1.21.1

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/lib/sqreen/deliveries/batch.rb +8 -1
  4. data/lib/sqreen/dependency/detector.rb +11 -3
  5. data/lib/sqreen/dependency/new_relic.rb +10 -1
  6. data/lib/sqreen/ecosystem.rb +123 -0
  7. data/lib/sqreen/ecosystem/databases/database_connection_data.rb +23 -0
  8. data/lib/sqreen/ecosystem/databases/mongo.rb +39 -0
  9. data/lib/sqreen/ecosystem/databases/mysql.rb +54 -0
  10. data/lib/sqreen/ecosystem/databases/postgres.rb +51 -0
  11. data/lib/sqreen/ecosystem/databases/redis.rb +36 -0
  12. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  13. data/lib/sqreen/ecosystem/exception_reporting.rb +28 -0
  14. data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
  15. data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
  16. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  17. data/lib/sqreen/ecosystem/messaging/bunny.rb +61 -0
  18. data/lib/sqreen/ecosystem/messaging/kafka.rb +70 -0
  19. data/lib/sqreen/ecosystem/messaging/kinesis.rb +66 -0
  20. data/lib/sqreen/ecosystem/messaging/sqs.rb +68 -0
  21. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  22. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  23. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  24. data/lib/sqreen/ecosystem/module_api/message_producer.rb +57 -0
  25. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
  26. data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
  27. data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
  28. data/lib/sqreen/ecosystem/module_api/tracing/consumer_data.rb +13 -0
  29. data/lib/sqreen/ecosystem/module_api/tracing/messaging_data.rb +35 -0
  30. data/lib/sqreen/ecosystem/module_api/tracing/producer_data.rb +13 -0
  31. data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
  32. data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
  33. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  34. data/lib/sqreen/ecosystem/module_registry.rb +48 -0
  35. data/lib/sqreen/ecosystem/tracing/modules/client.rb +35 -0
  36. data/lib/sqreen/ecosystem/tracing/modules/consumer.rb +35 -0
  37. data/lib/sqreen/ecosystem/tracing/modules/determine_ip.rb +28 -0
  38. data/lib/sqreen/ecosystem/tracing/modules/producer.rb +35 -0
  39. data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
  40. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  41. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  42. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  43. data/lib/sqreen/ecosystem/tracing/signals/tracing_consumer.rb +56 -0
  44. data/lib/sqreen/ecosystem/tracing/signals/tracing_producer.rb +56 -0
  45. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  46. data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
  47. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  48. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  49. data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
  50. data/lib/sqreen/ecosystem_integration.rb +81 -0
  51. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +89 -0
  52. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +38 -0
  53. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
  54. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  55. data/lib/sqreen/frameworks/generic.rb +15 -1
  56. data/lib/sqreen/graft/call.rb +9 -0
  57. data/lib/sqreen/graft/hook.rb +5 -3
  58. data/lib/sqreen/graft/hook_point.rb +17 -10
  59. data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +2 -0
  60. data/lib/sqreen/legacy/old_event_submission_strategy.rb +7 -1
  61. data/lib/sqreen/remote_command.rb +3 -0
  62. data/lib/sqreen/rules/custom_error_cb.rb +1 -1
  63. data/lib/sqreen/runner.rb +19 -5
  64. data/lib/sqreen/session.rb +2 -0
  65. data/lib/sqreen/signals/conversions.rb +6 -1
  66. data/lib/sqreen/version.rb +1 -1
  67. metadata +51 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7324df5cecbb626299a4e048550c6f7c5a3e15bef2eecc8d89011d0342914aa
4
- data.tar.gz: eabb5203769dcf898d8a4e373c6cfee7e5b0eac8bbfc3977fb8924a54504b126
3
+ metadata.gz: 6611caf7add24e05a248fce4026be48a4c47612335c5fc9a26c64c33cd0409e9
4
+ data.tar.gz: 8381eba611e37df32a8744848661bcf144eaffe00c818e306355508fe0267394
5
5
  SHA512:
6
- metadata.gz: 5e7f4bb53e01c0f5328a5190b189c82b596bcf28eada2104b1491c5601b1275866ef8950ae9ad92c91303011636a3c11ebcb3c3dbccd75280d6b89c1c69d301a
7
- data.tar.gz: 77770ff7b00b9d07ea7f4137eadce1e840bc1ad94e885b42623354707306ebd42425187833e0403e33d02bea7471010bf934bda9010c5fe1d604715c5daf88c1
6
+ metadata.gz: 65c820c306961ed812360c2c73a1f37396433238cff8fd79e2676132d7842eaa86d3555bf860d7c2f6fd465783f8c1aeb59100c94349b8c53588d83513265d2e
7
+ data.tar.gz: 38cda532df6f1037ac243c8cd5ccaecd708b880ff377d3fcbcecad329be184d40ec9f27deb61b69124be3d37ff74a16f88a7ab1032a6e6a0f6b5387645181027
@@ -1,3 +1,11 @@
1
+ ## 1.21.1
2
+
3
+ * Work around NewRelic initialisation (see https://github.com/newrelic/newrelic-ruby-agent/issues/461)
4
+
5
+ ## 1.21.0
6
+
7
+ * Add support for transport and tracing facilities
8
+
1
9
  ## 1.20.4
2
10
 
3
11
  * Fix missing budget check
@@ -13,6 +13,8 @@ require 'sqreen/events/attack'
13
13
  require 'sqreen/events/remote_exception'
14
14
  require 'sqreen/mono_time'
15
15
  require 'sqreen/deliveries/simple'
16
+ require 'sqreen/kit/signals/signal'
17
+ require 'sqreen/kit/signals/trace'
16
18
 
17
19
  module Sqreen
18
20
  module Deliveries
@@ -58,7 +60,7 @@ module Sqreen
58
60
  def post_batch_needed?(event)
59
61
  now = Sqreen.time
60
62
  # do not use any? {} due to side effects inside block
61
- event_keys(event).map do |key|
63
+ event_keys(event).uniq.map do |key|
62
64
  was = @first_seen[key]
63
65
  @first_seen[key] ||= now
64
66
  was.nil? || current_batch.size > max_batch || now > (was + max_staleness)
@@ -86,6 +88,7 @@ module Sqreen
86
88
  res += event.observed.fetch(:sdk, []).select { |e|
87
89
  e[0] == :track
88
90
  }.map { |e| "sdk-track".freeze }
91
+ res += event.observed.fetch(:signals, []).map { "signal".freeze }
89
92
  return res
90
93
  end
91
94
 
@@ -97,6 +100,10 @@ module Sqreen
97
100
  "rex-#{event.klass}"
98
101
  when Sqreen::AggregatedMetric
99
102
  "agg-metric"
103
+ when Sqreen::Kit::Signals::Signal
104
+ "signal"
105
+ when Sqreen::Kit::Signals::Trace
106
+ "signal"
100
107
  end
101
108
  end
102
109
  end
@@ -25,6 +25,14 @@ module Sqreen
25
25
  end
26
26
  end
27
27
 
28
+ def to_app_hook_strategy
29
+ if Sqreen::Dependency::NewRelic.bundled? || Sqreen::Dependency::NewRelic.required?
30
+ :chain
31
+ else
32
+ :prepend
33
+ end
34
+ end
35
+
28
36
  def hook(&block)
29
37
  Sqreen.log.debug "[#{Process.pid}] Startup command: #{$0}"
30
38
 
@@ -34,7 +42,7 @@ module Sqreen
34
42
  Sqreen::Dependency::Rails.insert_sqreen_middlewares
35
43
  end if Sqreen::Dependency::Rails.required?
36
44
 
37
- Sqreen::Graft::Hook.add('Rack::Builder#to_app') do
45
+ Sqreen::Graft::Hook.add('Rack::Builder#to_app', to_app_hook_strategy) do
38
46
  after do
39
47
  Sqreen::Dependency::Rails.inspect_middlewares
40
48
  end
@@ -48,7 +56,7 @@ module Sqreen
48
56
  end
49
57
  end.install if Sqreen::Dependency::Sinatra.required?
50
58
 
51
- Sqreen::Graft::Hook.add('Rack::Builder#to_app') do
59
+ Sqreen::Graft::Hook.add('Rack::Builder#to_app', to_app_hook_strategy) do
52
60
  after do |call|
53
61
  builder = call.instance
54
62
 
@@ -58,7 +66,7 @@ module Sqreen
58
66
 
59
67
  # ensure startup of thread in request handling processes
60
68
 
61
- Sqreen::Graft::Hook.add('Rack::Builder#to_app') do
69
+ Sqreen::Graft::Hook.add('Rack::Builder#to_app', to_app_hook_strategy) do
62
70
  after do |call|
63
71
  callback = call.callback
64
72
 
@@ -8,8 +8,17 @@ module Sqreen
8
8
  module NewRelic
9
9
  module_function
10
10
 
11
+ def bundled?
12
+ defined?(Gem) && Gem.respond_to?(:loaded_specs) && !Gem.loaded_specs['newrelic_rpm'].nil?
13
+ end
14
+
15
+ def required?
16
+ Sqreen::Dependency.const_exist?('NewRelic::Agent::Agent')
17
+ end
18
+
11
19
  def ignore_sqreen_exceptions
12
- return unless defined?(NewRelic::Agent::Agent)
20
+ return unless required?
21
+
13
22
  NewRelic::Agent::Agent.instance.error_collector.ignore(['Sqreen::AttackBlocked'])
14
23
  rescue ::Exception => e # rubocop:disable Lint/RescueException
15
24
  Sqreen.log.warn "Failed ignoring AttackBlocked on NewRelic: #{e.inspect}"
@@ -0,0 +1,123 @@
1
+ require 'securerandom'
2
+ require 'sqreen/ecosystem/module_registry'
3
+ require 'sqreen/ecosystem/tracing/sampling_configuration'
4
+ require 'sqreen/ecosystem/transaction_storage'
5
+ require 'sqreen/ecosystem/tracing_broker'
6
+ require 'sqreen/ecosystem/tracing_id_setup'
7
+ require 'sqreen/ecosystem/module_api/message_producer'
8
+ require 'sqreen/ecosystem/module_api/tracing_id_generation'
9
+ require 'sqreen/ecosystem/module_api/tracing'
10
+
11
+ module Sqreen
12
+ # The API for the ecosystem client (together with the dispatch table)
13
+ module Ecosystem
14
+ class << self
15
+ def init(opts = {})
16
+ @registry = ModuleRegistry.new
17
+ register_modules(opts[:modules])
18
+ @registry.init_all
19
+
20
+ # setup tracing generation
21
+ tracing_id_mods = @registry.module_subset(ModuleApi::TracingIdGeneration)
22
+ @tracing_id_setup = TracingIdSetup.new(tracing_id_mods)
23
+ @tracing_id_setup.setup_modules
24
+
25
+ # configure tracing broker with the consumers (tracing modules)
26
+ tracing_modules = @registry.module_subset(ModuleApi::Tracing)
27
+ @tracing_broker = TracingBroker.new(tracing_modules)
28
+
29
+ # inject tracing broker in message producers
30
+ @registry.each_module(ModuleApi::MessageProducer) do |mod|
31
+ mod.tracing_broker = @tracing_broker
32
+ end
33
+ rescue ::Exception # rubocop:disable Lint/RescueException
34
+ # TODO: modules must be disabled at this point
35
+ raise
36
+ end
37
+
38
+ def reset
39
+ instance_variables.each do |ia|
40
+ instance_variable_set(ia, nil)
41
+ end
42
+ end
43
+
44
+ # To be called by the Ecosystem client when a new transaction
45
+ # (generally: request) is started
46
+ # In the future, it's intended that request end/start detection be handled
47
+ # by the Ecosystem itself, so control will flow in the other direction,
48
+ # from the ecosystem to its client
49
+ def start_transaction
50
+ TransactionStorage.create_thread_local
51
+ end
52
+
53
+ def end_transaction
54
+ TransactionStorage.destroy_thread_local
55
+ end
56
+
57
+ # @param [String] tracing_id_prefix
58
+ # @param [Array<Hash{String=>Object}>] sampling_config
59
+ def configure_sampling(tracing_id_prefix, sampling_config)
60
+ @tracing_id_setup.tracing_id_prefix = tracing_id_prefix
61
+ built_samp_cfg = Tracing::SamplingConfiguration.new(sampling_config)
62
+ @tracing_broker.sampling_configuration = built_samp_cfg
63
+ end
64
+
65
+ private
66
+
67
+ def register_modules(modules)
68
+ return register_all_modules unless modules
69
+
70
+ modules.each { |mod| register mod }
71
+ end
72
+
73
+ def register_all_modules
74
+ # replace with something more magical?
75
+ require_relative 'ecosystem/http/rack_request'
76
+ register Http::RackRequest.new
77
+
78
+ require_relative 'ecosystem/http/net_http'
79
+ register Http::NetHttp.new
80
+
81
+ require_relative 'ecosystem/databases/postgres'
82
+ register Databases::Postgres.new
83
+
84
+ require_relative 'ecosystem/databases/mysql'
85
+ register Databases::Mysql.new
86
+
87
+ require_relative 'ecosystem/databases/mongo'
88
+ register Databases::Mongo.new
89
+
90
+ require_relative 'ecosystem/databases/redis'
91
+ register Databases::Redis.new
92
+
93
+ require_relative 'ecosystem/messaging/sqs'
94
+ register Messaging::Sqs.new
95
+
96
+ require_relative 'ecosystem/messaging/kinesis'
97
+ register Messaging::Kinesis.new
98
+
99
+ require_relative 'ecosystem/messaging/bunny'
100
+ register Messaging::Bunny.new
101
+
102
+ require_relative 'ecosystem/messaging/kafka'
103
+ register Messaging::Kafka.new
104
+
105
+ require_relative 'ecosystem/tracing/modules/client'
106
+ register Tracing::Modules::Client.new
107
+
108
+ require_relative 'ecosystem/tracing/modules/server'
109
+ register Tracing::Modules::Server.new
110
+
111
+ require_relative 'ecosystem/tracing/modules/producer'
112
+ register Tracing::Modules::Producer.new
113
+
114
+ require_relative 'ecosystem/tracing/modules/consumer'
115
+ register Tracing::Modules::Consumer.new
116
+ end
117
+
118
+ def register(mod)
119
+ @registry.register mod
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,23 @@
1
+ require 'sqreen/ecosystem/module_api/tracing/client_data'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ module Databases
6
+ class DatabaseConnectionData
7
+ include ModuleApi::Tracing::ClientData
8
+
9
+ # @return [Integer]
10
+ attr_accessor :port
11
+
12
+ # @return [String]
13
+ attr_accessor :unix_socket
14
+
15
+ # @return [String]
16
+ attr_accessor :username
17
+
18
+ # @return [String]
19
+ attr_accessor :db
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/instrumentation'
3
+ require 'sqreen/ecosystem/module_api/message_producer'
4
+ require 'sqreen/ecosystem/databases/database_connection_data'
5
+
6
+ module Sqreen
7
+ module Ecosystem
8
+ module Databases
9
+ class Mongo
10
+ include ModuleApi::Instrumentation
11
+ include ModuleApi::MessageProducer
12
+
13
+ def setup
14
+ advice = wrap_for_interest(DatabaseConnectionData, &method(:after_advice))
15
+ instrument 'Mongo::Client#initialize', after: advice
16
+ end
17
+
18
+ private
19
+
20
+ # @param [Sqreen::Graft::CallbackCall] call
21
+ def after_advice(call, _ball)
22
+ return if call.raised
23
+
24
+ client = call.instance
25
+ server_addrs = client.cluster.servers.map(&:address)
26
+
27
+ server_addrs.map do |addr|
28
+ DatabaseConnectionData.new(
29
+ transport: :mongo,
30
+ host: addr.host,
31
+ port: addr.port,
32
+ db: client.database.name,
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,54 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/instrumentation'
3
+ require 'sqreen/ecosystem/module_api/message_producer'
4
+ require 'sqreen/ecosystem/module_api/tracing_id_generation'
5
+ require 'sqreen/ecosystem/module_api/tracing/client_data'
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Databases
10
+ class Mysql
11
+ include ModuleApi::Instrumentation
12
+ include ModuleApi::MessageProducer
13
+
14
+ def setup
15
+ advice = wrap_for_interest(DatabaseConnectionData, &method(:after_advice))
16
+ instrument 'Mysql2::Client#connect', after: advice
17
+ end
18
+
19
+ private
20
+
21
+ # instance is of type +Mysql2::Client+
22
+ # VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database,
23
+ # VALUE socket, VALUE flags, VALUE conn_attrs) {
24
+ # @param [Sqreen::Graft::CallbackCall]
25
+ def after_advice(call, _ball)
26
+ args = call.args
27
+
28
+ # build & submit signal
29
+ signal = DatabaseConnectionData.new(transport: :mysql)
30
+
31
+ user = args[0]
32
+ host = args[2]
33
+ port = args[3]
34
+ db = args[4]
35
+ socket = args[5]
36
+
37
+ if socket && !socket.empty?
38
+ signal.unix_socket = socket
39
+ signal.host = 'localhost'
40
+ signal.ip = '::1'
41
+ else
42
+ signal.host = host
43
+ end
44
+
45
+ signal.port = port if port != 0
46
+ signal.username = user
47
+ signal.db = db
48
+
49
+ signal
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,51 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/instrumentation'
3
+ require 'sqreen/ecosystem/module_api/message_producer'
4
+ require 'sqreen/ecosystem/databases/database_connection_data'
5
+
6
+ module Sqreen
7
+ module Ecosystem
8
+ module Databases
9
+ class Postgres
10
+ include ModuleApi::Instrumentation
11
+ include ModuleApi::MessageProducer
12
+
13
+ def setup
14
+ advice = wrap_for_interest(DatabaseConnectionData, &method(:after_advice))
15
+ instrument 'PG::Connection#initialize', after: advice
16
+ end
17
+
18
+ private
19
+
20
+ # instance is of type +PG::Connection+
21
+ # > c = PG::Connection.new(host: '172.17.0.2', password: 'mysecretpassword', user: 'postgres')
22
+ # => #<PG::Connection:0x000055b44d077d10>
23
+ # > %i{host port user db}.map { |m| c.send m }
24
+ # => ["172.17.0.2", 5432, "postgres", "postgres"]
25
+ def after_advice(call, _ball)
26
+ conn = call.instance
27
+
28
+ # build & submit signal
29
+ signal = DatabaseConnectionData.new(transport: :postgres)
30
+
31
+ host = conn.host
32
+ if host
33
+ if host.include?('/')
34
+ signal.unix_socket = host
35
+ signal.host = 'localhost'
36
+ signal.ip = '::1'
37
+ else
38
+ signal.host = host
39
+ end
40
+ end
41
+
42
+ signal.port = conn.port
43
+ signal.username = conn.user
44
+ signal.db = conn.db
45
+
46
+ signal
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,36 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/instrumentation'
3
+ require 'sqreen/ecosystem/module_api/message_producer'
4
+ require 'sqreen/ecosystem/databases/database_connection_data'
5
+
6
+ module Sqreen
7
+ module Ecosystem
8
+ module Databases
9
+ class Redis
10
+ include ModuleApi::Instrumentation
11
+ include ModuleApi::MessageProducer
12
+
13
+ def setup
14
+ advice = wrap_for_interest(DatabaseConnectionData, &method(:after_advice))
15
+ instrument 'Redis#initialize', after: advice
16
+ end
17
+
18
+ private
19
+
20
+ # @param [Sqreen::Graft::CallbackCall] call
21
+ def after_advice(call, _ball)
22
+ return if call.raised
23
+
24
+ conn = call.instance.connection
25
+
26
+ DatabaseConnectionData.new(
27
+ transport: :redis,
28
+ host: conn[:host],
29
+ port: conn[:port],
30
+ db: conn[:db].to_s,
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end