sqreen 1.20.4 → 1.21.0.beta1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -25
  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/condition_evaluator.rb +2 -8
  7. data/lib/sqreen/configuration.rb +1 -1
  8. data/lib/sqreen/deferred_logger.rb +14 -50
  9. data/lib/sqreen/deliveries/batch.rb +8 -1
  10. data/lib/sqreen/ecosystem.rb +80 -0
  11. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  12. data/lib/sqreen/ecosystem/http/net_http.rb +51 -0
  13. data/lib/sqreen/ecosystem/http/rack_request.rb +38 -0
  14. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  15. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  16. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  17. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  18. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +26 -0
  19. data/lib/sqreen/ecosystem/module_api/tracing_push_down.rb +34 -0
  20. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  21. data/lib/sqreen/ecosystem/module_registry.rb +39 -0
  22. data/lib/sqreen/ecosystem/redis/redis_connection.rb +35 -0
  23. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  24. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  25. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  26. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  27. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  28. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  29. data/lib/sqreen/ecosystem_integration.rb +70 -0
  30. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +89 -0
  31. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +38 -0
  32. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +56 -0
  33. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  34. data/lib/sqreen/encoding_sanitizer.rb +27 -0
  35. data/lib/sqreen/events/request_record.rb +1 -0
  36. data/lib/sqreen/frameworks/generic.rb +15 -10
  37. data/lib/sqreen/frameworks/rails.rb +7 -0
  38. data/lib/sqreen/frameworks/request_recorder.rb +0 -2
  39. data/lib/sqreen/graft/call.rb +23 -72
  40. data/lib/sqreen/graft/callback.rb +1 -1
  41. data/lib/sqreen/graft/hook.rb +85 -187
  42. data/lib/sqreen/graft/hook_point.rb +1 -1
  43. data/lib/sqreen/legacy/instrumentation.rb +10 -22
  44. data/lib/sqreen/legacy/old_event_submission_strategy.rb +8 -3
  45. data/lib/sqreen/log.rb +2 -3
  46. data/lib/sqreen/log/loggable.rb +0 -1
  47. data/lib/sqreen/logger.rb +0 -24
  48. data/lib/sqreen/metrics_store.rb +0 -11
  49. data/lib/sqreen/null_logger.rb +0 -22
  50. data/lib/sqreen/remote_command.rb +3 -1
  51. data/lib/sqreen/rules.rb +4 -8
  52. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  53. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  54. data/lib/sqreen/rules/rule_cb.rb +0 -2
  55. data/lib/sqreen/rules/waf_cb.rb +3 -3
  56. data/lib/sqreen/runner.rb +21 -33
  57. data/lib/sqreen/session.rb +2 -0
  58. data/lib/sqreen/signals/conversions.rb +6 -1
  59. data/lib/sqreen/version.rb +1 -1
  60. data/lib/sqreen/weave/legacy/instrumentation.rb +103 -194
  61. data/lib/sqreen/worker.rb +2 -6
  62. metadata +35 -10
  63. data/lib/sqreen/deprecation.rb +0 -38
  64. data/lib/sqreen/weave/budget.rb +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7324df5cecbb626299a4e048550c6f7c5a3e15bef2eecc8d89011d0342914aa
4
- data.tar.gz: eabb5203769dcf898d8a4e373c6cfee7e5b0eac8bbfc3977fb8924a54504b126
3
+ metadata.gz: 49a6c95ab34d19ae0f769e475ae67c2c3e4d19b17582ee09d8a6d231ac3d9150
4
+ data.tar.gz: ad1ea7a80f3582ba90ffc6aa1cef7040459c56f65ea4712a023923b8bc7801e3
5
5
  SHA512:
6
- metadata.gz: 5e7f4bb53e01c0f5328a5190b189c82b596bcf28eada2104b1491c5601b1275866ef8950ae9ad92c91303011636a3c11ebcb3c3dbccd75280d6b89c1c69d301a
7
- data.tar.gz: 77770ff7b00b9d07ea7f4137eadce1e840bc1ad94e885b42623354707306ebd42425187833e0403e33d02bea7471010bf934bda9010c5fe1d604715c5daf88c1
6
+ metadata.gz: 0da6438f20a00a84914db2bb06c24feab53e164feae1cb7d2af0b53afe24c5d5ea54a0cb68a8872a49aadedae4450de57e63da6a4f4a2ec21ac85eaa84c13acf
7
+ data.tar.gz: 3e75918e810529674e10d693fd2a62c0eeeeecb18bac06a25fdd686d29c45d55333abb0feea790ec1d89e29ce71097bd1de16c2d31d98a6219e985d806793008
@@ -1,28 +1,3 @@
1
- ## 1.20.4
2
-
3
- * Fix missing budget check
4
- * Improve performance
5
- * Align internal setting name for WAF
6
- * Include response information in all payloads
7
- * Improve robustness against invalid Unicode
8
- * Prevent rule execution to pursue in early block cases
9
-
10
- ## 1.20.4.beta1
11
-
12
- * Add optional dynamic time budget
13
- * Add advanced per request metrics
14
- * Improve robustness against exception in instrumentation
15
- * Improve metric engine thread safety
16
- * Restrict deferred logger to final logger severity on agent boot
17
-
18
- ## 1.20.3
19
-
20
- * Fix signature check
21
-
22
- ## 1.20.2
23
-
24
- * Fix performance regression in instrumentation engine
25
-
26
1
  ## 1.20.1
27
2
 
28
3
  * Add fallback mechanisms when connecting to new Sqreen backend API domains
@@ -22,7 +22,7 @@ module Sqreen
22
22
  end
23
23
 
24
24
  def do_run(identity_params)
25
- Sqreen.log.debug(
25
+ Sqreen.log.info(
26
26
  "Will raise due to user being blocked by action #{id}. " \
27
27
  "Blocked user identity: #{identity_params}"
28
28
  )
@@ -25,7 +25,7 @@ module Sqreen
25
25
  end
26
26
 
27
27
  def do_run(client_ip)
28
- Sqreen.log.debug "Will request redirect for client with IP #{client_ip} " \
28
+ Sqreen.log.info "Will request redirect for client with IP #{client_ip} " \
29
29
  "(action: #{id})."
30
30
  {
31
31
  :status => :skip,
@@ -24,7 +24,7 @@ module Sqreen
24
24
  end
25
25
 
26
26
  def do_run(identity_params)
27
- Sqreen.log.debug 'Will request redirect for user with identity ' \
27
+ Sqreen.log.info 'Will request redirect for user with identity ' \
28
28
  "#{identity_params} (action: #{id})."
29
29
 
30
30
  e = Sqreen::AttackBlocked.new(
@@ -67,7 +67,7 @@ module Sqreen
67
67
  return true if rem <= 0
68
68
  if hash.is_a?(Array)
69
69
  return hash.any? do |v|
70
- hash_key_include?(values, v, min_value_size, rem - 1)
70
+ ConditionEvaluator.hash_key_include?(values, v, min_value_size, rem - 1)
71
71
  end
72
72
  end
73
73
 
@@ -81,13 +81,7 @@ module Sqreen
81
81
  if hkey.respond_to?(:empty?) && hkey.empty?
82
82
  false
83
83
  else
84
- key_incl = if values.is_a?(String)
85
- str_include?(values, hkey.to_s)
86
- else
87
- values.include?(hkey.to_s)
88
- end
89
-
90
- key_incl || hash_key_include?(values, hval, min_value_size, rem - 1)
84
+ values.include?(hkey.to_s) || ConditionEvaluator.hash_key_include?(values, hval, min_value_size, rem - 1)
91
85
  end
92
86
  end
93
87
  end
@@ -57,7 +57,7 @@ module Sqreen
57
57
  { :env => :SQREEN_RULES_SIGNATURE, :name => :rules_verify_signature,
58
58
  :default => true },
59
59
  { :env => :SQREEN_LOG_LEVEL, :name => :log_level,
60
- :default => 'INFO', :choice => %w[UNKNOWN FATAL ERROR WARN INFO DEBUG] },
60
+ :default => 'WARN', :choice => %w[UNKNOWN FATAL ERROR WARN INFO DEBUG] },
61
61
  { :env => :SQREEN_LOG_LOCATION, :name => :log_location,
62
62
  :default => 'log/sqreen.log' },
63
63
  { :env => :SQREEN_RUN_IN_TEST, :name => :run_in_test,
@@ -9,70 +9,35 @@ require 'sqreen/logger'
9
9
 
10
10
  module Sqreen
11
11
  class DeferredLogger
12
- MAX_ENTRIES = 1000
13
-
14
- Entry = Struct.new(:severity, :message)
12
+ include Singleton
15
13
 
16
14
  def initialize
17
15
  @buffer = StringIO.new
18
16
  @logger = ::Logger.new(@buffer)
19
- @entries = []
20
- @mutex = Mutex.new
21
- end
22
-
23
- def debug?
24
- true
25
- end
26
-
27
- def info?
28
- true
29
- end
30
-
31
- def warn?
32
- true
33
- end
34
-
35
- def error?
36
- true
37
- end
38
-
39
- def fatal?
40
- true
41
17
  end
42
18
 
43
19
  def debug(msg = nil, &block)
44
- add(::Logger::DEBUG, msg, &block)
20
+ @logger.debug(msg, &block)
45
21
  end
46
22
 
47
23
  def info(msg = nil, &block)
48
- add(::Logger::INFO, msg, &block)
24
+ @logger.info(msg, &block)
49
25
  end
50
26
 
51
27
  def warn(msg = nil, &block)
52
- add(::Logger::WARN, msg, &block)
28
+ @logger.warn(msg, &block)
53
29
  end
54
30
 
55
31
  def error(msg = nil, &block)
56
- add(::Logger::ERROR, msg, &block)
32
+ @logger.error(msg, &block)
57
33
  end
58
34
 
59
35
  def fatal(msg = nil, &block)
60
- add(::Logger::FATAL, msg, &block)
61
- end
62
-
63
- def unknown(msg = nil, &block)
64
- add(::Logger::UNKNOWN, msg, &block)
36
+ @logger.error(msg, &block)
65
37
  end
66
38
 
67
39
  def add(severity, msg = nil, &block)
68
- @mutex.synchronize do
69
- @entries.shift if @entries.count >= MAX_ENTRIES
70
- mark = @buffer.pos
71
- @logger.add(severity, msg, &block)
72
- @buffer.seek(mark)
73
- @entries << Entry.new(severity, @buffer.read)
74
- @buffer.truncate(0)
75
- end
40
+ send(Sqreen::Logger::SEVERITY_TO_METHOD[severity], msg, &block)
76
41
  end
77
42
 
78
43
  def formatter=(value)
@@ -80,22 +45,21 @@ module Sqreen
80
45
  end
81
46
 
82
47
  def flush_to(logger)
83
- @mutex.synchronize do
84
- @entries.each do |entry|
85
- next if entry.severity < logger.level
86
- logger.instance_eval { @logdev }.write(entry.message)
87
- end
88
- reset
89
- end
48
+ logger.instance_eval { @logdev }.write(read).tap { reset }
90
49
  end
91
50
 
92
51
  private
93
52
 
53
+ def read
54
+ @buffer.rewind
55
+ @buffer.read
56
+ end
57
+
94
58
  def reset
95
59
  buffer = StringIO.new
96
60
  logger = ::Logger.new(buffer)
97
61
  logger.formatter = @logger.formatter
98
- @buffer, @logger, @entries = buffer, logger, []
62
+ @buffer, @logger = buffer, logger
99
63
  end
100
64
  end
101
65
  end
@@ -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
@@ -0,0 +1,80 @@
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_id_setup'
6
+ require 'sqreen/ecosystem/module_api/tracing_push_down'
7
+
8
+ module Sqreen
9
+ # The API for the ecosystem client (together with the dispatch table)
10
+ module Ecosystem
11
+ class << self
12
+ def init(opts = {})
13
+ @registry = ModuleRegistry.new
14
+ register_modules(opts[:modules])
15
+ @registry.init_all
16
+
17
+ @tracing_id_setup = TracingIdSetup.new(@registry)
18
+ @tracing_id_setup.setup_modules
19
+ end
20
+
21
+ def reset
22
+ instance_variables.each do |ia|
23
+ instance_variable_set(ia, nil)
24
+ end
25
+ end
26
+
27
+ # To be called by the Ecosystem client when a new transaction
28
+ # (generally: request) is started
29
+ # In the future, it's intended that request end/start detection be handled
30
+ # by the Ecosystem itself, so control will flow in the other direction,
31
+ # from the ecosystem to its client
32
+ def start_transaction
33
+ TransactionStorage.create_thread_local
34
+ end
35
+
36
+ def end_transaction
37
+ TransactionStorage.destroy_thread_local
38
+ end
39
+
40
+ # @param [String] tracing_id_prefix
41
+ # @param [Array<Hash{String=>Object}>] sampling_config
42
+ def configure_sampling(tracing_id_prefix, sampling_config)
43
+ @tracing_id_setup.tracing_id_prefix = tracing_id_prefix
44
+ built_samp_cfg = Tracing::SamplingConfiguration.new(sampling_config)
45
+ inject_sampling_config(built_samp_cfg)
46
+ end
47
+
48
+ private
49
+
50
+ def register_modules(modules)
51
+ return register_all_modules unless modules
52
+
53
+ modules.each { |mod| register mod }
54
+ end
55
+
56
+ def register_all_modules
57
+ # replace with something more magical?
58
+ require_relative 'ecosystem/http/rack_request'
59
+ register Http::RackRequest.new
60
+
61
+ require_relative 'ecosystem/http/net_http'
62
+ register Http::NetHttp.new
63
+
64
+ require_relative 'ecosystem/redis/redis_connection'
65
+ register Redis::RedisConnection.new
66
+ end
67
+
68
+ def register(mod)
69
+ @registry.register mod
70
+ end
71
+
72
+ # @param [Sqreen::Ecosystem::SamplingConfiguration] config
73
+ def inject_sampling_config(config)
74
+ @registry.each_module(Sqreen::Ecosystem::ModuleApi::TracingPushDown) do |mod|
75
+ mod.sampling_config = config
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ require 'logger'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ # Configured by the ecosystem client
6
+ module DispatchTable
7
+ class << self
8
+ # data consumption
9
+ # argument: +Sqreen::Kit::Signals::Signal+
10
+ # see +Sqreen::EcosystemIntegration::SignalConsumption#consume_signal+
11
+ attr_accessor :consume_signal
12
+
13
+ # argument: block taking a Rack::Request
14
+ # see +Sqreen::EcosystemIntegration::RequestLifecycleTracking#add_start_observer+
15
+ attr_accessor :add_request_start_listener
16
+
17
+ attr_accessor :fetch_logger
18
+
19
+ # argument: callback taking:
20
+ # * the method to instrument
21
+ # * A Hash{Symbol=>Proc} with the advice. The proc takes the
22
+ # arguments and the ball, so these details of the instrumentation
23
+ # implementation leak through the abstraction
24
+ # see +Sqreen::EcosystemIntegration::InstrumentationService+
25
+ attr_accessor :instrument
26
+
27
+ def reset
28
+ instance_variables.each do |ia|
29
+ instance_variable_set(ia, nil)
30
+ end
31
+
32
+ # set default logger
33
+ logger = ::Logger.new(STDERR)
34
+ logger.level = ::Logger::WARN
35
+ logger.progname = 'sqreen-ecosystem'
36
+ self.fetch_logger = proc { logger }
37
+ end
38
+ end
39
+
40
+ reset
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/instrumentation'
3
+ require 'sqreen/ecosystem/module_api/tracing_push_down'
4
+ require 'sqreen/ecosystem/module_api/signal_producer'
5
+ require 'sqreen/ecosystem/module_api/transaction_storage'
6
+ require 'sqreen/ecosystem/tracing/signals/tracing_client'
7
+
8
+ module Sqreen
9
+ module Ecosystem
10
+ module Http
11
+ class NetHttp
12
+ include ModuleApi::Instrumentation
13
+ include ModuleApi::SignalProducer
14
+ include ModuleApi::TracingPushDown
15
+
16
+ def setup
17
+ instrument 'Net::HTTP#request', before: method(:before_advice)
18
+ end
19
+
20
+ private
21
+
22
+ # instr. def request(req, body = nil, &block) # :yield: +response+
23
+ # req is of type +Net::HTTPGenericRequest+
24
+ def before_advice(call, _ball)
25
+ return unless should_sample?('client')
26
+
27
+ tracing_id = create_tracing_id
28
+
29
+ # build & submit signal
30
+ host = call.instance.address
31
+ port = call.instance.port
32
+
33
+ host += ':' + port.to_s if port != 80 && port != 443
34
+
35
+ signal = Tracing::Signals::TracingClient.new
36
+ signal.payload = Tracing::Signals::TracingClient::Payload.new(
37
+ transport: 'http',
38
+ host: host,
39
+ tracing_identifier: tracing_id
40
+ )
41
+
42
+ submit_signal signal
43
+
44
+ # add tracing header, if available
45
+ req = call.args[0]
46
+ req[ModuleApi::TRACE_ID_HEADER] = tracing_id
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/event_listener'
3
+ require 'sqreen/ecosystem/module_api/signal_producer'
4
+ require 'sqreen/ecosystem/module_api/tracing_push_down'
5
+ require 'sqreen/ecosystem/tracing/signals/tracing_server'
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Http
10
+ class RackRequest
11
+ include ModuleApi::EventListener
12
+ include ModuleApi::TracingPushDown
13
+ include ModuleApi::SignalProducer
14
+
15
+ def setup
16
+ on_request_start(&method(:handle_request))
17
+ end
18
+
19
+ private
20
+
21
+ def handle_request(rack_request)
22
+ return unless should_sample?('server')
23
+
24
+ trace_id = rack_request.env[ModuleApi::TRACE_ID_ENV_KEY]
25
+
26
+ signal = Tracing::Signals::TracingServer.new
27
+ signal.payload = Tracing::Signals::TracingServer::Payload.new(
28
+ transport: 'http',
29
+ client_ip: rack_request.ip,
30
+ tracing_identifier: trace_id
31
+ )
32
+
33
+ submit_signal signal
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end