sqreen 1.20.4 → 1.21.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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