contrast-agent 4.12.0 → 4.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +1 -0
  3. data/ext/cs__assess_module/cs__assess_module.c +48 -0
  4. data/ext/cs__assess_module/cs__assess_module.h +7 -0
  5. data/ext/cs__common/cs__common.c +5 -0
  6. data/ext/cs__common/cs__common.h +8 -0
  7. data/ext/cs__contrast_patch/cs__contrast_patch.c +16 -1
  8. data/ext/cs__os_information/cs__os_information.c +31 -0
  9. data/ext/cs__os_information/cs__os_information.h +7 -0
  10. data/ext/cs__os_information/extconf.rb +5 -0
  11. data/lib/contrast/agent/assess/policy/policy_node.rb +6 -6
  12. data/lib/contrast/agent/assess/policy/policy_scanner.rb +5 -0
  13. data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
  14. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  15. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -1
  16. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +2 -154
  17. data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
  18. data/lib/contrast/agent/assess/policy/trigger_method.rb +45 -110
  19. data/lib/contrast/agent/assess/policy/trigger_node.rb +14 -6
  20. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +1 -1
  21. data/lib/contrast/agent/assess/property/tagged.rb +53 -185
  22. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +40 -6
  23. data/lib/contrast/agent/deadzone/policy/policy.rb +1 -1
  24. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -0
  25. data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
  26. data/lib/contrast/agent/middleware.rb +14 -62
  27. data/lib/contrast/agent/patching/policy/method_policy.rb +3 -89
  28. data/lib/contrast/agent/patching/policy/method_policy_extend.rb +111 -0
  29. data/lib/contrast/agent/patching/policy/patch.rb +28 -235
  30. data/lib/contrast/agent/patching/policy/patcher.rb +14 -49
  31. data/lib/contrast/agent/reporting/report.rb +21 -0
  32. data/lib/contrast/agent/reporting/reporter.rb +142 -0
  33. data/lib/contrast/agent/reporting/reporting_events/finding.rb +90 -0
  34. data/lib/contrast/agent/reporting/reporting_events/preflight.rb +25 -0
  35. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +56 -0
  36. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +37 -0
  37. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +127 -0
  38. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +168 -0
  39. data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +66 -0
  40. data/lib/contrast/agent/request.rb +2 -81
  41. data/lib/contrast/agent/request_context.rb +4 -128
  42. data/lib/contrast/agent/request_context_extend.rb +138 -0
  43. data/lib/contrast/agent/request_handler.rb +7 -3
  44. data/lib/contrast/agent/response.rb +2 -73
  45. data/lib/contrast/agent/startup_metrics_telemetry_event.rb +94 -0
  46. data/lib/contrast/agent/static_analysis.rb +5 -3
  47. data/lib/contrast/agent/telemetry.rb +137 -0
  48. data/lib/contrast/agent/telemetry_event.rb +33 -0
  49. data/lib/contrast/agent/thread_watcher.rb +66 -11
  50. data/lib/contrast/agent/version.rb +1 -1
  51. data/lib/contrast/agent.rb +21 -0
  52. data/lib/contrast/api/communication/connection_status.rb +10 -7
  53. data/lib/contrast/api/communication/messaging_queue.rb +37 -3
  54. data/lib/contrast/api/communication/response_processor.rb +15 -8
  55. data/lib/contrast/api/communication/service_lifecycle.rb +13 -3
  56. data/lib/contrast/api/communication/socket.rb +6 -8
  57. data/lib/contrast/api/communication/socket_client.rb +29 -12
  58. data/lib/contrast/api/communication/speedracer.rb +37 -1
  59. data/lib/contrast/api/communication/tcp_socket.rb +4 -3
  60. data/lib/contrast/api/communication/unix_socket.rb +1 -0
  61. data/lib/contrast/api/decorators/finding.rb +45 -0
  62. data/lib/contrast/components/api.rb +90 -0
  63. data/lib/contrast/components/app_context.rb +10 -41
  64. data/lib/contrast/components/app_context_extend.rb +78 -0
  65. data/lib/contrast/components/base.rb +23 -0
  66. data/lib/contrast/components/config.rb +92 -13
  67. data/lib/contrast/components/contrast_service.rb +11 -0
  68. data/lib/contrast/components/sampling.rb +2 -2
  69. data/lib/contrast/config/agent_configuration.rb +1 -1
  70. data/lib/contrast/config/api_configuration.rb +27 -0
  71. data/lib/contrast/config/api_proxy_configuration.rb +14 -0
  72. data/lib/contrast/config/application_configuration.rb +2 -3
  73. data/lib/contrast/config/assess_configuration.rb +3 -3
  74. data/lib/contrast/config/base_configuration.rb +17 -28
  75. data/lib/contrast/config/certification_configuration.rb +15 -0
  76. data/lib/contrast/config/env_variables.rb +18 -0
  77. data/lib/contrast/config/heap_dump_configuration.rb +6 -6
  78. data/lib/contrast/config/inventory_configuration.rb +1 -5
  79. data/lib/contrast/config/protect_rule_configuration.rb +1 -1
  80. data/lib/contrast/config/request_audit_configuration.rb +18 -0
  81. data/lib/contrast/config/root_configuration.rb +1 -0
  82. data/lib/contrast/config/ruby_configuration.rb +6 -6
  83. data/lib/contrast/config/service_configuration.rb +2 -2
  84. data/lib/contrast/config.rb +1 -1
  85. data/lib/contrast/configuration.rb +4 -2
  86. data/lib/contrast/extension/assess/array.rb +5 -7
  87. data/lib/contrast/extension/thread.rb +31 -12
  88. data/lib/contrast/framework/manager.rb +22 -44
  89. data/lib/contrast/framework/manager_extend.rb +50 -0
  90. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
  91. data/lib/contrast/framework/rails/patch/support.rb +31 -29
  92. data/lib/contrast/framework/rails/railtie.rb +1 -1
  93. data/lib/contrast/framework/sinatra/support.rb +2 -1
  94. data/lib/contrast/logger/application.rb +4 -0
  95. data/lib/contrast/logger/log.rb +8 -103
  96. data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
  97. data/lib/contrast/utils/assess/property/tagged_utils.rb +165 -0
  98. data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
  99. data/lib/contrast/utils/assess/tracking_util.rb +20 -15
  100. data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
  101. data/lib/contrast/utils/class_util.rb +18 -14
  102. data/lib/contrast/utils/exclude_key.rb +20 -0
  103. data/lib/contrast/utils/findings.rb +62 -0
  104. data/lib/contrast/utils/hash_digest.rb +10 -73
  105. data/lib/contrast/utils/hash_digest_extend.rb +86 -0
  106. data/lib/contrast/utils/head_dump_utils_extend.rb +74 -0
  107. data/lib/contrast/utils/heap_dump_util.rb +2 -65
  108. data/lib/contrast/utils/invalid_configuration_util.rb +29 -0
  109. data/lib/contrast/utils/io_util.rb +1 -1
  110. data/lib/contrast/utils/log_utils.rb +108 -0
  111. data/lib/contrast/utils/metrics_hash.rb +59 -0
  112. data/lib/contrast/utils/middleware_utils.rb +87 -0
  113. data/lib/contrast/utils/net_http_base.rb +158 -0
  114. data/lib/contrast/utils/object_share.rb +1 -0
  115. data/lib/contrast/utils/os.rb +23 -0
  116. data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
  117. data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
  118. data/lib/contrast/utils/request_utils.rb +88 -0
  119. data/lib/contrast/utils/response_utils.rb +97 -0
  120. data/lib/contrast/utils/substitution_utils.rb +167 -0
  121. data/lib/contrast/utils/tag_util.rb +9 -9
  122. data/lib/contrast/utils/telemetry.rb +79 -0
  123. data/lib/contrast/utils/telemetry_client.rb +90 -0
  124. data/lib/contrast/utils/telemetry_identifier.rb +130 -0
  125. data/lib/contrast.rb +18 -0
  126. data/ruby-agent.gemspec +7 -6
  127. data/service_executables/VERSION +1 -1
  128. data/service_executables/linux/contrast-service +0 -0
  129. data/service_executables/mac/contrast-service +0 -0
  130. metadata +69 -22
  131. data/lib/contrast/config/default_value.rb +0 -17
@@ -12,17 +12,18 @@ module Contrast
12
12
  class BaseConfiguration
13
13
  extend Forwardable
14
14
 
15
- STRING_BOOLEANS = %w[false true].cs__freeze
15
+ attr_reader :configuration_map
16
16
 
17
- attr_reader :map
18
-
19
- alias_method :to_hash, :map
20
- def_delegators :@map, :empty?, :key?, :delete, :fetch, :[], :[]=, :each, :each_pair, :each_key, :each_value
17
+ alias_method :to_hash, :configuration_map
18
+ def_delegators :@configuration_map, :empty?, :key?, :delete, :fetch,
19
+ :[], :[]=, :each, :each_pair, :each_key, :each_value
21
20
 
22
21
  EMPTY_VALUE = :EMPTY_VALUE
23
22
 
24
23
  def initialize hsh = {}, keys = {}
25
- @map = {}
24
+ # holds configuration key value pairs
25
+ # each configuration class can contain nested BaseConfigurations
26
+ @configuration_map = {}
26
27
  traverse_config(hsh, keys)
27
28
  end
28
29
 
@@ -39,7 +40,7 @@ module Contrast
39
40
  end
40
41
 
41
42
  def nil?
42
- @map.empty?
43
+ @configuration_map.empty?
43
44
  end
44
45
 
45
46
  private
@@ -69,25 +70,13 @@ module Contrast
69
70
  end
70
71
 
71
72
  def assign_config_value str_key, spec_value, user_provided_value
72
- @map[str_key] = if spec_value.is_a?(Class) && spec_value <= Contrast::Config::BaseConfiguration
73
- spec_value.new(user_provided_value)
74
- elsif spec_value.is_a?(Contrast::Config::DefaultValue) && user_provided_value == EMPTY_VALUE
75
- spec_value.value
76
- elsif user_provided_value.cs__is_a?(String)
77
- value = user_provided_value.downcase
78
- # converts string values to 'true' => true or 'false' => false
79
- case value
80
- when STRING_BOOLEANS[1]
81
- true
82
- when STRING_BOOLEANS[0]
83
- false
84
- else
85
- # returns non boolean string values
86
- user_provided_value
87
- end
88
- else
89
- user_provided_value
90
- end
73
+ @configuration_map[str_key] = if spec_value.is_a?(Class) && spec_value <= Contrast::Config::BaseConfiguration
74
+ spec_value.new(user_provided_value)
75
+ elsif user_provided_value == EMPTY_VALUE
76
+ spec_value
77
+ else
78
+ user_provided_value
79
+ end
91
80
  end
92
81
 
93
82
  def value_from_key_config key, config_hash
@@ -99,13 +88,13 @@ module Contrast
99
88
 
100
89
  def define_getter str_key
101
90
  define_singleton_method str_key.to_sym do
102
- @map[str_key] == EMPTY_VALUE ? nil : @map[str_key]
91
+ @configuration_map[str_key] == EMPTY_VALUE ? nil : @configuration_map[str_key]
103
92
  end
104
93
  end
105
94
 
106
95
  def define_setter str_key
107
96
  define_singleton_method "#{ str_key }=".to_sym do |new_value|
108
- @map[str_key] = new_value
97
+ @configuration_map[str_key] = new_value
109
98
  end
110
99
  end
111
100
  end
@@ -0,0 +1,15 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Config
6
+ # Certificate Configuration
7
+ class CertificationConfiguration < BaseConfiguration
8
+ KEYS = { enable: false, ca_file: EMPTY_VALUE, cert_file: EMPTY_VALUE, key_file: EMPTY_VALUE }.cs__freeze
9
+
10
+ def initialize hsh
11
+ super(hsh, KEYS)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Config
6
+ # This module is holding all the Env Variables that we could use through the agent lifecycle
7
+ module EnvVariables
8
+ ENV_VARIABLES = { telemetry_opt_outs: ENV['CONTRAST_AGENT_TELEMETRY_OPTOUT'].to_s || false }.cs__freeze
9
+
10
+ def return_value key
11
+ return unless ENV_VARIABLES.key?(key.to_sym)
12
+
13
+ sym_key = key.downcase.to_sym
14
+ ENV_VARIABLES[sym_key]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -8,17 +8,17 @@ module Contrast
8
8
  class HeapDumpConfiguration < BaseConfiguration
9
9
  KEYS = {
10
10
  enable: # should dumps be taken
11
- Contrast::Config::DefaultValue.new(Contrast::Utils::ObjectShare::FALSE),
11
+ Contrast::Utils::ObjectShare::FALSE,
12
12
  path: # dir to which dumps should be
13
- Contrast::Config::DefaultValue.new('contrast_heap_dumps'), # saved
13
+ 'contrast_heap_dumps', # saved
14
14
  delay_ms: # time, in ms, after initialization
15
- Contrast::Config::DefaultValue.new(10_000), # to delay before taking dump
15
+ 10_000, # to delay before taking dump
16
16
  window_ms: # ms between each dump
17
- Contrast::Config::DefaultValue.new(10_000), #
17
+ 10_000, #
18
18
  count: # number of dumps to take
19
- Contrast::Config::DefaultValue.new(5), #
19
+ 5, #
20
20
  clean: # remove temporary objects or not
21
- Contrast::Config::DefaultValue.new(Contrast::Utils::ObjectShare::FALSE) #
21
+ Contrast::Utils::ObjectShare::FALSE #
22
22
  }.cs__freeze
23
23
 
24
24
  def initialize hsh
@@ -6,11 +6,7 @@ module Contrast
6
6
  # Common Configuration settings. Those in this section pertain to the
7
7
  # inventory functionality of the Agent.
8
8
  class InventoryConfiguration < BaseConfiguration
9
- KEYS = {
10
- enable: Contrast::Config::DefaultValue.new(true),
11
- analyze_libraries: Contrast::Config::DefaultValue.new(true),
12
- tags: EMPTY_VALUE
13
- }.cs__freeze
9
+ KEYS = { enable: true, analyze_libraries: true, tags: EMPTY_VALUE }.cs__freeze
14
10
 
15
11
  def initialize hsh
16
12
  super(hsh, KEYS)
@@ -12,7 +12,7 @@ module Contrast
12
12
  enable: EMPTY_VALUE,
13
13
  mode: EMPTY_VALUE,
14
14
  disable_system_commands: EMPTY_VALUE,
15
- detect_custom_code_accessing_system_files: Contrast::Config::DefaultValue.new('true')
15
+ detect_custom_code_accessing_system_files: true
16
16
  }.cs__freeze
17
17
 
18
18
  def initialize hsh
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Config
6
+ # This class holds the Common Settings for the
7
+ # hidden functionality of the TS
8
+ class RequestAuditConfiguration < BaseConfiguration
9
+ DEFAULT_PATH = './messages'
10
+
11
+ KEYS = { enable: false, requests: false, responses: false, path: DEFAULT_PATH }.cs__freeze
12
+
13
+ def initialize hsh
14
+ super(hsh, KEYS)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -6,6 +6,7 @@ module Contrast
6
6
  # The base of the Common Configuration settings.
7
7
  class RootConfiguration < BaseConfiguration
8
8
  KEYS = {
9
+ api: Contrast::Config::ApiConfiguration,
9
10
  enable: BaseConfiguration::EMPTY_VALUE,
10
11
  agent: Contrast::Config::AgentConfiguration,
11
12
  application: Contrast::Config::ApplicationConfiguration,
@@ -23,17 +23,17 @@ module Contrast
23
23
  DEFAULT_UNINSTRUMENTED_NAMESPACES = %w[FactoryGirl FactoryBot].cs__freeze
24
24
 
25
25
  KEYS = {
26
- disabled_agent_rake_tasks: Contrast::Config::DefaultValue.new(DISABLED_RAKE_TASK_LIST),
26
+ disabled_agent_rake_tasks: DISABLED_RAKE_TASK_LIST,
27
27
  exceptions: Contrast::Config::ExceptionConfiguration,
28
28
  # controls whether or not we patch interpolation, either by rewrite or by funchook
29
- interpolate: Contrast::Config::DefaultValue.new(Contrast::Utils::ObjectShare::TRUE),
29
+ interpolate: Contrast::Utils::ObjectShare::TRUE,
30
30
  # controls whether or not we patch the rb_yield block to track split propagation
31
- propagate_yield: Contrast::Config::DefaultValue.new(Contrast::Utils::ObjectShare::TRUE),
31
+ propagate_yield: Contrast::Utils::ObjectShare::TRUE,
32
32
  # control whether or not we run file scanning rules on require
33
- require_scan: Contrast::Config::DefaultValue.new(Contrast::Utils::ObjectShare::TRUE),
33
+ require_scan: Contrast::Utils::ObjectShare::TRUE,
34
34
  # controls whether or not we track frozen Strings by replacing them
35
- track_frozen_sources: Contrast::Config::DefaultValue.new(Contrast::Utils::ObjectShare::TRUE),
36
- uninstrument_namespace: Contrast::Config::DefaultValue.new(DEFAULT_UNINSTRUMENTED_NAMESPACES)
35
+ track_frozen_sources: Contrast::Utils::ObjectShare::TRUE,
36
+ uninstrument_namespace: DEFAULT_UNINSTRUMENTED_NAMESPACES
37
37
  }.cs__freeze
38
38
 
39
39
  def initialize hsh
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'contrast/config/default_value'
5
4
  require 'contrast/config/logger_configuration'
6
5
 
7
6
  module Contrast
@@ -19,7 +18,8 @@ module Contrast
19
18
  host: EMPTY_VALUE,
20
19
  port: EMPTY_VALUE,
21
20
  socket: EMPTY_VALUE,
22
- logger: Contrast::Config::LoggerConfiguration
21
+ logger: Contrast::Config::LoggerConfiguration,
22
+ bypass: false
23
23
  }.cs__freeze
24
24
 
25
25
  def initialize hsh
@@ -11,7 +11,6 @@ module Contrast
11
11
  end
12
12
 
13
13
  require 'contrast/config/base_configuration'
14
- require 'contrast/config/default_value'
15
14
 
16
15
  require 'contrast/config/logger_configuration'
17
16
 
@@ -24,6 +23,7 @@ require 'contrast/config/protect_rules_configuration'
24
23
  require 'contrast/config/sampling_configuration'
25
24
 
26
25
  require 'contrast/config/ruby_configuration'
26
+ require 'contrast/config/api_configuration'
27
27
  require 'contrast/config/agent_configuration'
28
28
  require 'contrast/config/application_configuration'
29
29
  require 'contrast/config/server_configuration'
@@ -7,6 +7,7 @@ require 'fileutils'
7
7
  require 'contrast/config'
8
8
  require 'contrast/utils/object_share'
9
9
  require 'contrast/components/scope'
10
+ require 'contrast/utils/exclude_key'
10
11
 
11
12
  module Contrast
12
13
  # This is how we read in the local settings for the Agent, both ENV/ CMD line
@@ -86,7 +87,7 @@ module Contrast
86
87
  def yaml_to_hash path
87
88
  if path && File.readable?(path)
88
89
  begin
89
- yaml = IO.read(path)
90
+ yaml = File.read(path)
90
91
  yaml = ERB.new(yaml).result if defined?(ERB)
91
92
  return YAML.safe_load(yaml)
92
93
  rescue Psych::Exception => e
@@ -204,7 +205,6 @@ module Contrast
204
205
  # in the thing to convert and setting them in the given hash. For now, this
205
206
  # logs every possible key, whether set or not. If we want to change that
206
207
  # behavior, we can skip adding keys to the hash if the value is nil, blank,
207
- # or Contrast::Config::DefaultValue depending on desired behavior
208
208
  #
209
209
  # @param hash [Hash] the hash to populate
210
210
  # @param convert [Contrast::Config::BaseConfiguration, Object] the level of
@@ -218,6 +218,8 @@ module Contrast
218
218
  case convert
219
219
  when Contrast::Config::BaseConfiguration
220
220
  convert.cs__class::KEYS.each_key do |key|
221
+ next if Contrast::Utils::ExcludeKey.excludable? key.to_s
222
+
221
223
  hash[key] = convert_to_hash(convert.send(key), {})
222
224
  end
223
225
  hash
@@ -42,13 +42,11 @@ module Contrast
42
42
  shift = 0
43
43
  separator_length = separator.nil? ? 0 : separator.to_s.length
44
44
  parent_events = []
45
- ary.each do |obj|
46
- if obj # skip nil here
47
- properties.copy_from(obj, ret, shift)
48
- shift += obj.to_s.length
49
- parent_event = Contrast::Agent::Assess::Tracker.properties(obj)&.event
50
- parent_events << parent_event if parent_event
51
- end
45
+ ary.compact.each do |obj|
46
+ properties.copy_from(obj, ret, shift)
47
+ shift += obj.to_s.length
48
+ parent_event = Contrast::Agent::Assess::Tracker.properties(obj)&.event
49
+ parent_events << parent_event if parent_event
52
50
  shift += separator_length
53
51
  end
54
52
  return ret unless Contrast::Agent::Assess::Tracker.tracked?(ret)
@@ -10,22 +10,41 @@
10
10
  # exists mostly for reference.
11
11
  #
12
12
  # Scope HAS to exist per-fiber.
13
+ # Check to see if Thread#initialize has been prepended. If it hasn't, then we should use the alias approach, so that
14
+ # others can continue to alias it. If it has been, then we must use prepend ourselves.
15
+ if Thread.instance_method(:initialize).owner == Thread
16
+ # Alias-chaining Thread#initialize.
17
+ class Thread
18
+ alias_method :cs__initialize, :initialize
13
19
 
14
- # Monkey-patching Thread#initialize.
15
- class Thread
16
- alias_method :cs__initialize, :initialize
20
+ def initialize *args, &block
21
+ # Thread.current still references the original(callee) thread that is instantiating this new fiber during
22
+ # initialization.
23
+ Contrast::Components::Scope::MONITOR.synchronize do
24
+ if (current_context = Thread.current[:current_context])
25
+ self[:current_context] = current_context.dup
26
+ end
17
27
 
18
- def initialize *args, &block
19
- # Thread.current still references the original(callee) thread that is
20
- # instantiating this new fiber during initialization
21
- Contrast::Components::Scope::MONITOR.synchronize do
22
- if (current_context = Thread.current[:current_context])
23
- self[:current_context] = current_context.dup
28
+ Contrast::Components::Scope.sweep_dead_ecs
24
29
  end
25
30
 
26
- Contrast::Components::Scope.sweep_dead_ecs
31
+ cs__initialize(*args, &block)
32
+ end
33
+ end
34
+ else
35
+ # Prepending Thread#initialize.
36
+ module ContrastThread
37
+ def initialize *args, &block
38
+ # Thread.current still references the original(callee) thread that is instantiating this new fiber during
39
+ # initialization.
40
+ Contrast::Components::Scope::MONITOR.synchronize do
41
+ if (current_context = Thread.current[:current_context])
42
+ self[:current_context] = current_context.dup
43
+ end
44
+ Contrast::Components::Scope.sweep_dead_ecs
45
+ end
46
+ super
27
47
  end
28
-
29
- cs__initialize(*args, &block)
30
48
  end
49
+ Thread.prepend(ContrastThread)
31
50
  end
@@ -9,16 +9,18 @@ require 'contrast/framework/rails/support'
9
9
  require 'contrast/framework/grape/support'
10
10
  require 'contrast/framework/sinatra/support'
11
11
  require 'contrast/utils/class_util'
12
+ require 'contrast/framework/manager_extend'
12
13
 
13
14
  module Contrast
14
15
  module Framework
15
16
  # Allows access to framework specific information
16
17
  class Manager
17
18
  include Contrast::Components::Logger::InstanceMethods
19
+ include Contrast::Framework::ManagerExtend
18
20
 
19
- # Order here does matter as the first framework listed will be the first one we pull information from
20
- # Rack will be a special case that may involve updating some logic to handle only applying Rack if Rails/Sinatra
21
- # do not exist
21
+ # Order here does matter as the first framework listed will be the first one we pull information from Rack will
22
+ # be a special case that may involve updating some logic to handle only applying Rack if Rails/Sinatra do not
23
+ # exist
22
24
  SUPPORTED_FRAMEWORKS = [
23
25
  Contrast::Framework::Rails::Support, Contrast::Framework::Sinatra::Support,
24
26
  Contrast::Framework::Grape::Support, Contrast::Framework::Rack::Support
@@ -34,9 +36,8 @@ module Contrast
34
36
  @_frameworks.compact!
35
37
  end
36
38
 
37
- # Patches that have to be applied as early as possible to catch calls
38
- # that happen prior to the first Request, typically those around
39
- # configuration.
39
+ # Patches that have to be applied as early as possible to catch calls that happen prior to the first Request,
40
+ # typically those around configuration.
40
41
  def before_load_patches!
41
42
  @_before_load_patches ||= begin
42
43
  SUPPORTED_FRAMEWORKS.each(&:before_load_patches!)
@@ -66,6 +67,10 @@ module Contrast
66
67
  Contrast::Framework::PlatformVersion.from_string(framework_version)
67
68
  end
68
69
 
70
+ def platform_version_string
71
+ first_framework_result :version, ''
72
+ end
73
+
69
74
  def server_type
70
75
  first_framework_result :server_type, 'rack'
71
76
  end
@@ -81,8 +86,8 @@ module Contrast
81
86
 
82
87
  # Build a request from the provided env, based on the framework(s) we're currently supporting.
83
88
  #
84
- # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values
85
- # of this particular Request
89
+ # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values of
90
+ # this particular Request
86
91
  # @return [::Rack::Request] either a rack request or subclass thereof.
87
92
  def retrieve_request env
88
93
  # If we're mounted on Rails, use Rails.
@@ -99,8 +104,8 @@ module Contrast
99
104
  logger.warn('Unable to retrieve_request', e)
100
105
  end
101
106
 
102
- # @param env [Hash] the various variables stored by this and other Middlewares to know the state
103
- # and values of this particular Request
107
+ # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values of
108
+ # this particular Request
104
109
  # @return [Boolean] true if at least one framework is streaming the response; false if none are streaming
105
110
  def streaming? env
106
111
  result = false
@@ -111,13 +116,14 @@ module Contrast
111
116
  result
112
117
  end
113
118
 
114
- # Iterate through current frameworks and return the current request's route. This will be the first
115
- # non-nil result.
119
+ # Iterate through current frameworks and return the current request's route. This will be the first non-nil
120
+ # result.
116
121
  #
117
122
  # @param request [Contrast::Agent::Request] the current request.
118
123
  # @return [Contrast::Api::Dtm::RouteCoverage] the current route as a Dtm.
119
124
  def get_route_dtm request
120
- @_frameworks.lazy.map { |framework_support| framework_support.current_route(request) }.reject(&:nil?).first
125
+ @_frameworks.lazy.map { |framework_support| framework_support.current_route(request) }.
126
+ reject(&:nil?).first
121
127
  end
122
128
 
123
129
  # Sometimes the framework we want to instrument is loaded after our agent code. To catch that case, we'll detect
@@ -125,6 +131,9 @@ module Contrast
125
131
  # have support enabled, we'll enable it now. We'll also need to catch up on any other startup actions that we've
126
132
  # missed. Most likely, this is only necessary for those applications which have applications mounted on them.
127
133
  #
134
+ # TODO: RUBY-1354
135
+ # TODO: RUBY-1356
136
+ #
128
137
  # @param mod [Module] the module or class that was just loaded
129
138
  def register_late_framework mod
130
139
  return unless mod
@@ -147,37 +156,6 @@ module Contrast
147
156
  rescue StandardError => e
148
157
  logger.warn('Unable to register a late framework', e, module: mod.cs__name)
149
158
  end
150
-
151
- private
152
-
153
- def enable_framework_support? klass
154
- Contrast::Utils::ClassUtil.truly_defined?(klass)
155
- end
156
-
157
- def routes_for_all_frameworks
158
- data_for_all_frameworks :collect_routes
159
- end
160
-
161
- # This returns an array of all data from each framework in a flat, no-nil values array
162
- #
163
- # @param method_name [Symbol] the method to call on each FrameworkSupport class
164
- # @return [Array]
165
- def data_for_all_frameworks method_name
166
- @_frameworks.flat_map { |framework| framework.send(method_name) }.compact
167
- end
168
-
169
- # This returns a single object from the first framework to successfully respond
170
- #
171
- # @param method_name [Symbol] the method to call on each FrameworkSupport class
172
- # @return [Object] - Determined by method to be invoked
173
- def first_framework_result method_name, default_value
174
- result = nil
175
- @_frameworks.each do |framework|
176
- result = framework.send(method_name)
177
- break if result
178
- end
179
- result || default_value
180
- end
181
159
  end
182
160
  end
183
161
  end
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/components/logger'
5
+ require 'contrast/extension/module'
6
+ require 'contrast/framework/platform_version'
7
+ require 'contrast/framework/rack/support'
8
+ require 'contrast/framework/rails/support'
9
+ require 'contrast/framework/grape/support'
10
+ require 'contrast/framework/sinatra/support'
11
+ require 'contrast/utils/class_util'
12
+
13
+ module Contrast
14
+ module Framework
15
+ # Allows access to framework specific information
16
+ module ManagerExtend
17
+ private
18
+
19
+ def enable_framework_support? klass
20
+ Contrast::Utils::ClassUtil.truly_defined?(klass)
21
+ end
22
+
23
+ def routes_for_all_frameworks
24
+ data_for_all_frameworks :collect_routes
25
+ end
26
+
27
+ # This returns an array of all data from each framework in a flat, no-nil values array
28
+ #
29
+ # @param method_name [Symbol] the method to call on each FrameworkSupport class
30
+ # @return [Array]
31
+ def data_for_all_frameworks method_name
32
+ @_frameworks.flat_map { |framework| framework.send(method_name) }.
33
+ compact
34
+ end
35
+
36
+ # This returns a single object from the first framework to successfully respond
37
+ #
38
+ # @param method_name [Symbol] the method to call on each FrameworkSupport class
39
+ # @return [Object] - Determined by method to be invoked
40
+ def first_framework_result method_name, default_value
41
+ result = nil
42
+ @_frameworks.each do |framework|
43
+ result = framework.send(method_name)
44
+ break if result
45
+ end
46
+ result || default_value
47
+ end
48
+ end
49
+ end
50
+ end
@@ -5,10 +5,14 @@ module Contrast
5
5
  module Framework
6
6
  module Rails
7
7
  module Patch
8
- # This class acts as our patch into the ActionController::Live::Buffer
9
- # class, allowing us to track the close event on streamed responses.
8
+ # This class acts as our patch into the ActionController::Live::Buffer class, allowing us to track the close
9
+ # event on streamed responses.
10
10
  module ActionControllerLiveBuffer
11
11
  class << self
12
+ # TODO: RUBY-1353
13
+ # TODO: RUBY-1355
14
+ # TODO: RUBY-1357
15
+ # TODO: RUBY-1357
12
16
  def send_messages
13
17
  return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
14
18
 
@@ -20,10 +24,9 @@ module Contrast
20
24
  def instrument
21
25
  @_instrument ||= begin
22
26
  ::ActionController::Live::Buffer.class_eval do
23
- # normally pre->in->post filters are applied however, in a streamed response
24
- # we can run into a case where it's pre -> in -> post -> more infilters
25
- # in order to submit anything found during the infilters after the response has
26
- # been written we need to explicitly send them
27
+ # normally pre->in->post filters are applied however, in a streamed response we can run into a case
28
+ # where it's pre -> in -> post -> more infilters in order to submit anything found during the
29
+ # infilters after the response has been written we need to explicitly send them
27
30
  alias_method :cs__close, :close
28
31
  def close
29
32
  Contrast::Framework::Rails::Patch::ActionControllerLiveBuffer.send_messages
@@ -38,37 +38,39 @@ module Contrast
38
38
  instrumenting_module:
39
39
  'Contrast::Framework::Rails::Patch::RailsApplicationConfiguration')
40
40
  ])
41
- if RUBY_VERSION < '2.6.0'
42
- patches.merge([
43
- # TODO: RUBY-714 remove w/ EOL of 2.5
44
- #
45
- # @deprecated Everything past here is used for Rewriting and can
46
- # be removed once we no longer support 2.5.
47
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
48
- 'ActionController::Railties::Helper::ClassMethods',
49
- 'contrast/framework/rails/rewrite/action_controller_railties_helper_inherited',
50
- method_to_instrument: :inherited,
51
- instrumenting_module:
52
- 'Contrast::Framework::Rails::Rewrite::ActionControllerRailtiesHelperInherited'),
53
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
54
- 'ActiveRecord::AttributeMethods::Read::ClassMethods',
55
- 'contrast/framework/rails/rewrite/active_record_attribute_methods_read',
56
- instrumenting_module:
57
- 'Contrast::Framework::Rails::Rewrite::ActiveRecordAttributeMethodsRead'),
58
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
59
- 'ActiveRecord::Scoping::Named::ClassMethods',
60
- 'contrast/framework/rails/rewrite/active_record_named',
61
- instrumenting_module: 'Contrast::Framework::Rails::Rewrite::ActiveRecordNamed'),
62
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
63
- 'ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods',
64
- 'contrast/framework/rails/rewrite/active_record_time_zone_inherited',
65
- method_to_instrument: :inherited,
66
- instrumenting_module:
67
- 'Contrast::Framework::Rails::Rewrite::ActiveRecordTimeZoneInherited')
68
- ])
69
- end
41
+ patches.merge(special_after_load_patches) if RUBY_VERSION < '2.6.0'
70
42
  patches
71
43
  end
44
+
45
+ def special_after_load_patches
46
+ [
47
+ # TODO: RUBY-714 remove w/ EOL of 2.5
48
+ #
49
+ # @deprecated Everything past here is used for Rewriting and can
50
+ # be removed once we no longer support 2.5.
51
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
52
+ 'ActionController::Railties::Helper::ClassMethods',
53
+ 'contrast/framework/rails/rewrite/action_controller_railties_helper_inherited',
54
+ method_to_instrument: :inherited,
55
+ instrumenting_module:
56
+ 'Contrast::Framework::Rails::Rewrite::ActionControllerRailtiesHelperInherited'),
57
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
58
+ 'ActiveRecord::AttributeMethods::Read::ClassMethods',
59
+ 'contrast/framework/rails/rewrite/active_record_attribute_methods_read',
60
+ instrumenting_module:
61
+ 'Contrast::Framework::Rails::Rewrite::ActiveRecordAttributeMethodsRead'),
62
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
63
+ 'ActiveRecord::Scoping::Named::ClassMethods',
64
+ 'contrast/framework/rails/rewrite/active_record_named',
65
+ instrumenting_module: 'Contrast::Framework::Rails::Rewrite::ActiveRecordNamed'),
66
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
67
+ 'ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods',
68
+ 'contrast/framework/rails/rewrite/active_record_time_zone_inherited',
69
+ method_to_instrument: :inherited,
70
+ instrumenting_module:
71
+ 'Contrast::Framework::Rails::Rewrite::ActiveRecordTimeZoneInherited')
72
+ ]
73
+ end
72
74
  end
73
75
  end
74
76
  end
@@ -14,7 +14,7 @@ module Contrast
14
14
  initializer 'Contrast Ruby Agent Initializer' do |app|
15
15
  log_rails = defined?(Rails) && defined?(Rails.logger)
16
16
 
17
- Rails.logger.debug("In railtie ::#{ app.middleware.inspect }") if log_rails
17
+ Rails.logger.debug { "In railtie ::#{ app.middleware.inspect }" } if log_rails
18
18
 
19
19
  if ::Contrast::APP_CONTEXT.instrument_middleware_stack?
20
20
  ::Contrast::AGENT.insert_middleware(app)