sqreen 1.20.2 → 1.21.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -3
  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 +9 -2
  7. data/lib/sqreen/conditionable.rb +24 -6
  8. data/lib/sqreen/configuration.rb +1 -1
  9. data/lib/sqreen/deferred_logger.rb +46 -14
  10. data/lib/sqreen/deliveries/batch.rb +8 -1
  11. data/lib/sqreen/deprecation.rb +38 -0
  12. data/lib/sqreen/ecosystem.rb +96 -0
  13. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  14. data/lib/sqreen/ecosystem/exception_reporting.rb +26 -0
  15. data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
  16. data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
  17. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  18. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  19. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  20. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  21. data/lib/sqreen/ecosystem/module_api/message_producer.rb +51 -0
  22. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
  23. data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
  24. data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
  25. data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
  26. data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
  27. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  28. data/lib/sqreen/ecosystem/module_registry.rb +44 -0
  29. data/lib/sqreen/ecosystem/redis/redis_connection.rb +43 -0
  30. data/lib/sqreen/ecosystem/tracing/modules/client.rb +31 -0
  31. data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
  32. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  33. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  34. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  35. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  36. data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
  37. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  38. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  39. data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
  40. data/lib/sqreen/ecosystem_integration.rb +87 -0
  41. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +99 -0
  42. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +42 -0
  43. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
  44. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  45. data/lib/sqreen/events/request_record.rb +0 -1
  46. data/lib/sqreen/frameworks/generic.rb +24 -1
  47. data/lib/sqreen/frameworks/rails.rb +0 -7
  48. data/lib/sqreen/frameworks/request_recorder.rb +2 -0
  49. data/lib/sqreen/graft/call.rb +89 -15
  50. data/lib/sqreen/graft/hook.rb +231 -100
  51. data/lib/sqreen/graft/hook_point.rb +17 -10
  52. data/lib/sqreen/legacy/instrumentation.rb +12 -0
  53. data/lib/sqreen/legacy/old_event_submission_strategy.rb +9 -2
  54. data/lib/sqreen/log.rb +3 -2
  55. data/lib/sqreen/logger.rb +20 -0
  56. data/lib/sqreen/metrics.rb +1 -0
  57. data/lib/sqreen/metrics/req_detailed.rb +41 -0
  58. data/lib/sqreen/metrics_store.rb +11 -0
  59. data/lib/sqreen/null_logger.rb +22 -0
  60. data/lib/sqreen/remote_command.rb +4 -0
  61. data/lib/sqreen/rules.rb +8 -4
  62. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  63. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  64. data/lib/sqreen/rules/rule_cb.rb +4 -2
  65. data/lib/sqreen/rules/waf_cb.rb +3 -3
  66. data/lib/sqreen/runner.rb +63 -8
  67. data/lib/sqreen/session.rb +2 -0
  68. data/lib/sqreen/signals/conversions.rb +6 -1
  69. data/lib/sqreen/version.rb +1 -1
  70. data/lib/sqreen/weave/budget.rb +35 -0
  71. data/lib/sqreen/weave/legacy/instrumentation.rb +237 -98
  72. data/lib/sqreen/worker.rb +6 -2
  73. metadata +60 -9
  74. data/lib/sqreen/encoding_sanitizer.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20f83c83a19022c3bfba53af705716e2d4b4d791912bf32c6e7e2853496a4b05
4
- data.tar.gz: 6592f5a6c88895016924c4a342d6c1f48f885527c18fc0b182495d2905cef926
3
+ metadata.gz: 88934827e2aa5f84ae21f96fb33afdada009fb118d94e8940fe61973812da4d4
4
+ data.tar.gz: b30da2199b4dc96d0193c671872ac192396c988ef67845c26fc1ed36a7704bfd
5
5
  SHA512:
6
- metadata.gz: 349fecb7524cb1740965162e596708fede99c5ab12bd5ed0a009c324e360585a15f2237c78614b666e77d5bbbf5b044a9a1ee7be8d3ce791b377cbc1ecbe15aa
7
- data.tar.gz: ad4b8725a6531e80a4e47aac8c820f88284391a8f674ae59e8908846714c8b1c76be7fd95034cdf4885588f39e413308f39cc5688f6ed207558b48d0b14def5f
6
+ metadata.gz: 19942da7597a18aee5b46aa4ef664c5083eadf3a878c5f431f9dde83196fc15f3263509c6cda6537a5c823de310f117dbb7c600130478a0828d2dbc73f0bf53c
7
+ data.tar.gz: c8b1293e8480043d3b494d6335725f649107c56e7bf282580592de7d680eaeb9475bd9091ea280ad2d2e473b100789a9b80c3dac93b9ddebbddb2f8ec4f3ced6
@@ -1,6 +1,18 @@
1
- ## 1.20.2
2
-
3
- * Fix performance regression in instrumentation engine
1
+ ## 1.21.0.beta3
2
+
3
+ * Avoid fd leak in `custom_error_cb` (ARB-109)
4
+ * Support `skip_rem_cbs` (ARB-107)
5
+ * Fix instrumentation in Ruby 2.0
6
+ * Fix encoding exception on `hash_key_include` (ARB-53)
7
+ * Fix erroneous start in non-Rails context (SQREEN-880)
8
+ * Make metrics thread-safe
9
+ * Add restart command
10
+ * Fix `overtime_cb`
11
+ * Add `perf_level` 2
12
+ * Several performance optimizations
13
+ * WAF: rename `budget_in_ms` to `max_budget_ms`
14
+ * Transport/Tracing with http module
15
+ * Update the blocking page
4
16
 
5
17
  ## 1.20.1
6
18
 
@@ -22,7 +22,7 @@ module Sqreen
22
22
  end
23
23
 
24
24
  def do_run(identity_params)
25
- Sqreen.log.info(
25
+ Sqreen.log.debug(
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.info "Will request redirect for client with IP #{client_ip} " \
28
+ Sqreen.log.debug "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.info 'Will request redirect for user with identity ' \
27
+ Sqreen.log.debug '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
- ConditionEvaluator.hash_key_include?(values, v, min_value_size, rem - 1)
70
+ hash_key_include?(values, v, min_value_size, rem - 1)
71
71
  end
72
72
  end
73
73
 
@@ -81,7 +81,14 @@ module Sqreen
81
81
  if hkey.respond_to?(:empty?) && hkey.empty?
82
82
  false
83
83
  else
84
- values.include?(hkey.to_s) || ConditionEvaluator.hash_key_include?(values, hval, min_value_size, rem - 1)
84
+ key_incl =
85
+ if values.is_a?(String)
86
+ str_include? values, hkey.to_s
87
+ else
88
+ values.include?(hkey.to_s)
89
+ end
90
+
91
+ key_incl || hash_key_include?(values, hval, min_value_size, rem - 1)
85
92
  end
86
93
  end
87
94
  end
@@ -28,21 +28,39 @@ module Sqreen
28
28
  end
29
29
 
30
30
  def pre_with_conditions(inst, args, budget = nil, &block)
31
+ return pre_without_conditions(inst, args, budget, &block) if pre_conditions.nil?
32
+
31
33
  eargs = [nil, framework, inst, args, @data, nil]
32
- return nil if !pre_conditions.nil? && !pre_conditions.evaluate(*eargs)
33
- pre_without_conditions(inst, args, budget, &block)
34
+ return nil unless pre_conditions.evaluate(*eargs)
35
+
36
+ res = pre_without_conditions(inst, args, budget, &block)
37
+ return { passed_conditions: true } unless res.is_a?(Hash)
38
+ res[:passed_conditions] = true
39
+ res
34
40
  end
35
41
 
36
42
  def post_with_conditions(rv, inst, args, budget = nil, &block)
43
+ return post_without_conditions(rv, inst, args, budget, &block) if post_conditions.nil?
44
+
37
45
  eargs = [nil, framework, inst, args, @data, rv]
38
- return nil if !post_conditions.nil? && !post_conditions.evaluate(*eargs)
39
- post_without_conditions(rv, inst, args, budget, &block)
46
+ return nil unless post_conditions.evaluate(*eargs)
47
+
48
+ res = post_without_conditions(rv, inst, args, budget, &block)
49
+ return { passed_conditions: true } if res.nil?
50
+ res[:passed_conditions] = true
51
+ res
40
52
  end
41
53
 
42
54
  def failing_with_conditions(rv, inst, args, budget = nil, &block)
55
+ return failing_without_conditions(rv, inst, args, budget, &block) if failing_conditions.nil?
56
+
43
57
  eargs = [nil, framework, inst, args, @data, rv]
44
- return nil if !failing_conditions.nil? && !failing_conditions.evaluate(*eargs)
45
- failing_without_conditions(rv, inst, args, budget, &block)
58
+ return nil unless failing_conditions.evaluate(*eargs)
59
+
60
+ res = failing_without_conditions(rv, inst, args, budget, &block)
61
+ return { passed_conditions: true } if res.nil?
62
+ res[:passed_conditions] = true
63
+ res
46
64
  end
47
65
 
48
66
  protected
@@ -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 => 'WARN', :choice => %w[UNKNOWN FATAL ERROR WARN INFO DEBUG] },
60
+ :default => 'INFO', :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,39 +9,70 @@ require 'sqreen/logger'
9
9
 
10
10
  module Sqreen
11
11
  class DeferredLogger
12
- include Singleton
12
+ MAX_ENTRIES = 1000
13
+
14
+ Entry = Struct.new(:severity, :message)
13
15
 
14
16
  def initialize
15
17
  @buffer = StringIO.new
16
18
  @logger = ::Logger.new(@buffer)
19
+ @entries = []
20
+ @mutex = Mutex.new
17
21
  end
18
22
 
19
23
  def debug?
20
24
  true
21
25
  end
22
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
+ end
42
+
23
43
  def debug(msg = nil, &block)
24
- @logger.debug(msg, &block)
44
+ add(::Logger::DEBUG, msg, &block)
25
45
  end
26
46
 
27
47
  def info(msg = nil, &block)
28
- @logger.info(msg, &block)
48
+ add(::Logger::INFO, msg, &block)
29
49
  end
30
50
 
31
51
  def warn(msg = nil, &block)
32
- @logger.warn(msg, &block)
52
+ add(::Logger::WARN, msg, &block)
33
53
  end
34
54
 
35
55
  def error(msg = nil, &block)
36
- @logger.error(msg, &block)
56
+ add(::Logger::ERROR, msg, &block)
37
57
  end
38
58
 
39
59
  def fatal(msg = nil, &block)
40
- @logger.error(msg, &block)
60
+ add(::Logger::FATAL, msg, &block)
61
+ end
62
+
63
+ def unknown(msg = nil, &block)
64
+ add(::Logger::UNKNOWN, msg, &block)
41
65
  end
42
66
 
43
67
  def add(severity, msg = nil, &block)
44
- send(Sqreen::Logger::SEVERITY_TO_METHOD[severity], msg, &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
45
76
  end
46
77
 
47
78
  def formatter=(value)
@@ -49,21 +80,22 @@ module Sqreen
49
80
  end
50
81
 
51
82
  def flush_to(logger)
52
- logger.instance_eval { @logdev }.write(read).tap { reset }
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
53
90
  end
54
91
 
55
92
  private
56
93
 
57
- def read
58
- @buffer.rewind
59
- @buffer.read
60
- end
61
-
62
94
  def reset
63
95
  buffer = StringIO.new
64
96
  logger = ::Logger.new(buffer)
65
97
  logger.formatter = @logger.formatter
66
- @buffer, @logger = buffer, logger
98
+ @buffer, @logger, @entries = buffer, logger, []
67
99
  end
68
100
  end
69
101
  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,38 @@
1
+ # typed: strong
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ require 'sqreen/log/loggable'
7
+
8
+ module Sqreen
9
+ module Deprecation
10
+ include Sqreen::Log::Loggable
11
+
12
+ module_function
13
+
14
+ def deprecate(method)
15
+ return unless ENV['SQREEN_DEBUG_DEPRECATION']
16
+
17
+ owner = method.owner
18
+ deprecated = :"_deprecated_#{method.name}"
19
+ klass = owner.is_a?(Module)
20
+ target = klass ? owner.to_s : owner.class.to_s
21
+
22
+ method.owner.instance_eval do
23
+ alias_method deprecated, method.name
24
+
25
+ define_method(method.name) do |*args, &block|
26
+ msg = [
27
+ "deprecation",
28
+ "target:#{target}",
29
+ "method:#{method.name}",
30
+ "caller:#{Kernel.caller_locations[0]}",
31
+ ].join(' ')
32
+ Sqreen::Deprecation.logger.info(msg)
33
+ send(deprecated, *args, &block)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,96 @@
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/redis/redis_connection'
82
+ register Redis::RedisConnection.new
83
+
84
+ require_relative 'ecosystem/tracing/modules/client'
85
+ register Tracing::Modules::Client.new
86
+
87
+ require_relative 'ecosystem/tracing/modules/server'
88
+ register Tracing::Modules::Server.new
89
+ end
90
+
91
+ def register(mod)
92
+ @registry.register mod
93
+ end
94
+ end
95
+ end
96
+ 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