contrast-agent 3.8.5 → 3.9.0

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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_array/cs__assess_array.c +1 -1
  3. data/ext/cs__assess_module/cs__assess_module.c +0 -1
  4. data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
  5. data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
  6. data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
  7. data/ext/cs__common/cs__common.c +6 -6
  8. data/ext/cs__common/cs__common.h +3 -1
  9. data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
  10. data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
  11. data/funchook/autom4te.cache/requests +48 -48
  12. data/funchook/config.log +2 -2
  13. data/lib/contrast/agent.rb +15 -5
  14. data/lib/contrast/agent/assess.rb +0 -1
  15. data/lib/contrast/agent/assess/contrast_event.rb +9 -8
  16. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
  17. data/lib/contrast/agent/assess/policy/policy.rb +0 -14
  18. data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
  19. data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
  20. data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
  21. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  22. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
  23. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
  24. data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
  25. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
  26. data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
  27. data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
  28. data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
  29. data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
  30. data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
  31. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
  32. data/lib/contrast/agent/assess/properties.rb +29 -0
  33. data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
  34. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
  35. data/lib/contrast/agent/class_reopener.rb +98 -55
  36. data/lib/contrast/agent/feature_state.rb +1 -1
  37. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  38. data/lib/contrast/agent/logger_manager.rb +2 -2
  39. data/lib/contrast/agent/middleware.rb +1 -3
  40. data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
  41. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
  42. data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
  43. data/lib/contrast/agent/patching/policy/patch.rb +54 -23
  44. data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
  45. data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
  46. data/lib/contrast/agent/patching/policy/policy.rb +4 -0
  47. data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
  48. data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
  49. data/lib/contrast/agent/protect/policy/policy.rb +6 -6
  50. data/lib/contrast/agent/protect/rule/base.rb +1 -1
  51. data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
  52. data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
  53. data/lib/contrast/agent/railtie.rb +11 -5
  54. data/lib/contrast/agent/request.rb +1 -19
  55. data/lib/contrast/agent/request_context.rb +1 -1
  56. data/lib/contrast/agent/rewriter.rb +4 -3
  57. data/lib/contrast/agent/scope.rb +116 -19
  58. data/lib/contrast/agent/service_heartbeat.rb +5 -2
  59. data/lib/contrast/agent/settings_state.rb +12 -8
  60. data/lib/contrast/agent/version.rb +1 -1
  61. data/lib/contrast/api.rb +1 -0
  62. data/lib/contrast/api/speedracer.rb +2 -2
  63. data/lib/contrast/components/agent.rb +26 -7
  64. data/lib/contrast/components/app_context.rb +8 -45
  65. data/lib/contrast/components/contrast_service.rb +3 -4
  66. data/lib/contrast/components/interface.rb +1 -1
  67. data/lib/contrast/components/scope.rb +56 -26
  68. data/lib/contrast/config/ruby_configuration.rb +8 -3
  69. data/lib/contrast/delegators.rb +9 -0
  70. data/lib/contrast/delegators/application_update.rb +32 -0
  71. data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
  72. data/lib/contrast/extensions/framework/rack/request.rb +24 -0
  73. data/lib/contrast/extensions/framework/rack/response.rb +23 -0
  74. data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
  75. data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
  76. data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
  77. data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
  78. data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
  79. data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
  80. data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
  81. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
  82. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
  83. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
  84. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
  85. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
  86. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
  87. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
  88. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
  89. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
  90. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
  91. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
  92. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
  93. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
  94. data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
  95. data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
  96. data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
  97. data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
  98. data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
  99. data/lib/contrast/extensions/ruby_core/module.rb +17 -0
  100. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
  101. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
  102. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
  103. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
  104. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
  105. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
  106. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
  107. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
  108. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
  109. data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
  110. data/lib/contrast/framework/base_support.rb +63 -0
  111. data/lib/contrast/framework/manager.rb +109 -0
  112. data/lib/contrast/framework/platform_version.rb +21 -0
  113. data/lib/contrast/framework/rails_support.rb +88 -0
  114. data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
  115. data/lib/contrast/framework/sinatra_support.rb +94 -0
  116. data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
  117. data/lib/contrast/utils/assess/tracking_util.rb +2 -4
  118. data/lib/contrast/utils/class_util.rb +92 -37
  119. data/lib/contrast/utils/duck_utils.rb +59 -39
  120. data/lib/contrast/utils/environment_util.rb +5 -75
  121. data/lib/contrast/utils/freeze_util.rb +3 -7
  122. data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
  123. data/lib/contrast/utils/job_servers_running.rb +39 -0
  124. data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
  125. data/lib/contrast/utils/service_response_util.rb +0 -6
  126. data/lib/contrast/utils/sinatra_helper.rb +6 -0
  127. data/lib/contrast/utils/stack_trace_utils.rb +1 -1
  128. data/resources/assess/policy.json +74 -23
  129. data/resources/inventory/policy.json +1 -1
  130. data/resources/protect/policy.json +11 -9
  131. data/resources/rubocops/object/frozen_cop.rb +1 -1
  132. data/ruby-agent.gemspec +2 -0
  133. data/service_executables/VERSION +1 -1
  134. data/service_executables/linux/contrast-service +0 -0
  135. data/service_executables/mac/contrast-service +0 -0
  136. metadata +94 -57
  137. data/ext/cs__scope/cs__scope.c +0 -96
  138. data/ext/cs__scope/cs__scope.h +0 -33
  139. data/lib/contrast/agent/assess/class_reverter.rb +0 -82
  140. data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
  141. data/lib/contrast/core_extensions/module.rb +0 -42
  142. data/lib/contrast/core_extensions/object.rb +0 -27
  143. data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
  144. data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
  145. data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
  146. data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
  147. data/lib/contrast/rails_extensions/buffer.rb +0 -30
  148. data/lib/contrast/rails_extensions/rack.rb +0 -45
  149. data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
  150. data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
  151. data/lib/contrast/utils/operating_environment.rb +0 -38
  152. data/lib/contrast/utils/path_util.rb +0 -151
  153. data/lib/contrast/utils/scope_util.rb +0 -99
@@ -3,55 +3,75 @@
3
3
 
4
4
  module Contrast
5
5
  module Utils
6
- # Utility methods for identifying instances that can be used interchangably
6
+ # Utility methods for identifying instances that can be used interchangeably
7
7
  class DuckUtils
8
- def self.quacks_like_tracked_hash? obj
9
- return false unless obj
10
- return false if quacks_like_cs_patched?(obj)
8
+ class << self
9
+ # Determine if the given object, or the object to which it delegates,
10
+ # responds to the given method.
11
+ #
12
+ # @param object [Object]
13
+ # @param method [Symbol]
14
+ # @return [Boolean]
15
+ def quacks_to? object, method
16
+ return true if object.cs__respond_to?(method)
17
+ return false unless object.is_a?(Delegator)
11
18
 
12
- obj.is_a?(Hash)
13
- end
19
+ object.cs__delegator_respond_to?(method)
20
+ end
14
21
 
15
- def self.quacks_like_tracked_enumerable? obj
16
- return false unless obj
17
- return false if obj.is_a?(IO)
18
- # TODO: RUBY-606: temporary work around to avoid the agent treating OpenSSL::Buffering as a source,
19
- # see https://contrast.atlassian.net/browse/RUBY-588
20
- return false if defined?(OpenSSL::Buffering) && obj.is_a?(OpenSSL::Buffering)
21
- return false if quacks_like_cs_patched?(obj)
22
+ # Most things that are closable IO's will in fact be of the IO type. That
23
+ # being said, some will also be extensions of DelegateClass with IO type,
24
+ # like Tempfile. We need to handle both cases.
25
+ #
26
+ # @param object [Object]
27
+ # @return [Boolean]
28
+ def closable_io? object
29
+ return false unless Contrast::Utils::IOUtil.io?(object)
22
30
 
23
- obj.is_a?(Enumerable)
24
- end
31
+ quacks_to?(object, :closed?)
32
+ end
25
33
 
26
- # This method will return true if the object being checked has been patched
27
- # to have the cs__properties field. We check the cs__tracked? method since it
28
- # is side effect free (ie doesn't cause the properties object to be created).
29
- def self.quacks_like_cs_patched? obj
30
- obj && quacks_to?(obj, :cs__tracked?)
31
- end
34
+ # Determine if the given Object is a Hash, or similar enough to a hash
35
+ # for us to iterate on it using the #each_pair method
36
+ #
37
+ # @param object [Object]
38
+ # @return [Boolean]
39
+ def iterable_hash? object
40
+ # do iterate on things believed to safely implement #each_pair
41
+ return true if object.cs__is_a?(Hash)
32
42
 
33
- # All Objects respond to dup. Not all Objects respond nicely. Nil, True,
34
- # False and Number types respond with TypeError. This check should handle
35
- # those cases.
36
- # https://stackoverflow.com/questions/20954789/how-do-i-check-if-a-variable-really-responds-to-dup
37
- def self.quacks_like_duplicable? obj
38
- obj.cs__class.methods.include? :new
39
- end
43
+ # otherwise, don't risk it
44
+ false
45
+ end
40
46
 
41
- # Most things that are closable IO's will in fact be of the IO type. That
42
- # being said, some will also be extensions of DelegateClass with IO type,
43
- # like Tempfile. We need to handle both cases.
44
- def self.quacks_like_closable_io object
45
- return false unless Contrast::Utils::IOUtil.io?(object)
47
+ # Determine if the given Object is a concrete implementation of
48
+ # Enumerable known to be safe to call #each on.
49
+ #
50
+ # @param object [Object]
51
+ # @return [Boolean]
52
+ def iterable_enumerable? object
53
+ # do iterate on things believed to safely implement #each. We're
54
+ # purposefully skipping Hash and Hash-like things here as
55
+ # #iterable_hash? should handle those.
56
+ return true if object.cs__is_a?(Array)
57
+ return true if object.cs__is_a?(Enumerator)
58
+ return true if object.cs__is_a?(Hash)
59
+ return true if object.cs__is_a?(Range)
60
+ return true if object.cs__is_a?(Set)
46
61
 
47
- quacks_to?(object, :closed?)
48
- end
62
+ # otherwise, don't risk it
63
+ false
64
+ end
49
65
 
50
- def self.quacks_to? object, method
51
- return true if object.cs__respond_to?(method)
52
- return false unless object.is_a?(Delegator)
66
+ # This method will return true if the object being checked has been patched
67
+ # to have the cs__properties field. We check the cs__tracked? method since it
68
+ # is side effect free (ie doesn't cause the properties object to be created).
69
+ def trackable? object
70
+ return true if object.cs__respond_to?(:cs__tracked?)
71
+ return false unless object.is_a?(Delegator)
53
72
 
54
- object.cs__delegator_respond_to?(method)
73
+ object.cs__delegator_respond_to?(:cs__tracked?)
74
+ end
55
75
  end
56
76
  end
57
77
  end
@@ -2,7 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  cs__scoped_require 'socket'
5
- cs__scoped_require 'contrast/core_extensions/module'
5
+
6
+ cs__scoped_require 'contrast/extensions/ruby_core/module'
6
7
  cs__scoped_require 'contrast/components/interface'
7
8
  cs__scoped_require 'contrast/utils/object_share'
8
9
  cs__scoped_require 'contrast/utils/string_utils'
@@ -12,16 +13,10 @@ module Contrast
12
13
  # Gather information about the application and server environment
13
14
  # that the agent is running in.
14
15
  class EnvironmentUtil
15
- include Contrast::Components::Interface
16
-
17
- access_component :analysis
18
-
19
- DEFAULT_APP_NAME = 'rack'
20
- DEFAULT_SERVER_NAME = 'localhost'
21
-
22
16
  CS_VERSION = 'VERSION'
23
17
 
24
18
  # Possible names of constants that would hold version info
19
+ # TODO - RUBY-770
25
20
  VERSION_CONSTANT_CANDIDATES = %w[
26
21
  VERSION
27
22
  ::VERSION
@@ -30,16 +25,6 @@ module Contrast
30
25
  ::Application::VERSION
31
26
  ].cs__freeze
32
27
 
33
- # Common places where view-layer files can be found in a Rails application
34
- RAILS_VIEWS = [
35
- ['app/assets', '*.coffee', %w[CoffeeScript]],
36
- ['app/assets', '*.scss', %w[SASS]],
37
- ['app/views', '*.html', %w[HTML5]],
38
- ['app/views', '*.html.erb', %w[HTML5 ERB]],
39
- ['app/views', '*.html.haml', %w[HTML5 HAML]],
40
- ['public', '*.html', %w[HTML5]]
41
- ].cs__freeze
42
-
43
28
  class << self
44
29
  ######### These are helpers ####################
45
30
 
@@ -50,6 +35,7 @@ module Contrast
50
35
  ######### Actually env/framework dependent ####################
51
36
 
52
37
  # See CONTRAST-16380 for more details
38
+ # TODO RUBY-770
53
39
  def determine_application_version
54
40
  @_determine_application_version ||= begin
55
41
  candidates = VERSION_CONSTANT_CANDIDATES.map do |name|
@@ -80,20 +66,9 @@ module Contrast
80
66
  end
81
67
  end
82
68
 
83
- def platform
84
- version = Rails.version rescue Sinatra::VERSION rescue Rack.version rescue '' # rubocop:disable Style/RescueModifier
85
- major, minor, patch = *version.split(Contrast::Utils::ObjectShare::PERIOD)
86
- major ||= ''
87
- minor ||= ''
88
- patch ||= ''
89
-
90
- [major, minor, patch]
91
- rescue StandardError
92
- Contrast::Utils::ObjectShare::EMPTY_TRIPLE
93
- end
94
-
95
69
  # Read libraries from the gemfile and append to the given ApplicationUpdate message
96
70
  # or other class that has a libraries map association
71
+ # TODO - RUBY-770
97
72
  def add_library_to_app_update app_update_pb, library_tags = ''
98
73
  libraries = Contrast::Utils::GemfileReader.instance.library_pb_list
99
74
  libraries.each do |n|
@@ -101,51 +76,6 @@ module Contrast
101
76
  app_update_pb.libraries[n.hash_code] = n
102
77
  end
103
78
  end
104
-
105
- # Iterate through known locations, looking for files
106
- # that represent view or template files. If found, for each file in the directory
107
- # append the technology and the view object to the application update instance
108
- def scan_views app_update_msg
109
- return unless INVENTORY.enabled?
110
-
111
- scan_rails_views(app_update_msg)
112
- scan_sinatra_views(app_update_msg)
113
- end
114
-
115
- # Find all the predefined routes for this application and append them to the
116
- # provided inventory message
117
- # msg should be a Contrast::Api::Dtm::ApplicationUpdate or some other msg
118
- # that has a routes array consisting of Contrast::Api::Dtm::RouteCoverage
119
- def scan_routes msg
120
- return unless INVENTORY.enabled?
121
-
122
- Contrast::Utils::PathUtil.find_routes.each do |route|
123
- msg.routes << route
124
- end
125
- end
126
-
127
- private
128
-
129
- def scan_view_directories app_update_msg, view_location_data
130
- view_location_data.each do |path, extension, tech_names|
131
- next if Dir["#{ path }/**/*#{ extension }"].empty?
132
-
133
- tech_names.each do |tech|
134
- app_update_msg.technologies[tech] = true
135
- end
136
- end
137
- end
138
-
139
- def scan_rails_views app_update_msg
140
- scan_view_directories(app_update_msg, RAILS_VIEWS)
141
- end
142
-
143
- def scan_sinatra_views app_update_msg
144
- sinatra_app = Contrast::Utils::SinatraHelper.app_class
145
- return unless sinatra_app
146
-
147
- scan_view_directories(app_update_msg, Contrast::Utils::SinatraHelper.scannable_view_dirs)
148
- end
149
79
  end
150
80
  end
151
81
  end
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- cs__scoped_require 'contrast/core_extensions/object'
5
4
  cs__scoped_require 'contrast/utils/duck_utils'
6
5
 
7
6
  module Contrast
@@ -11,21 +10,18 @@ module Contrast
11
10
  class FreezeUtil
12
11
  class << self
13
12
  # Make every attempt to duplicate the frozen object so that it can
14
- # be tracked. Some things, like Nil, True, and False, respond to dup
15
- # but throw a TypeError. This catches that case and returns the
16
- # original. We luck out as these three cases do not get propagated to
13
+ # be tracked.
17
14
  #
18
15
  # @param original [Object] something frozen, usually a String
19
16
  # @return [Object] the original or an unfrozen copy
20
17
  def unfreeze_dup original
21
18
  return original unless original.cs__frozen?
22
- return original unless Contrast::Utils::DuckUtils.quacks_like_duplicable?(original)
23
19
 
24
20
  copy = original.dup
25
- if Contrast::Utils::DuckUtils.quacks_like_tracked_hash?(copy)
21
+ if Contrast::Utils::DuckUtils.iterable_hash?(copy)
26
22
  copy.each_key do |key|
27
23
  value = original[key]
28
- copy[key] = Contrast::Utils::DuckUtils.quacks_like_duplicable?(original) ? value.dup : value
24
+ copy[key] = value.dup
29
25
  end
30
26
  end
31
27
  copy
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: false
2
+ # frozen_string_literal: true
3
3
 
4
4
  cs__scoped_require 'contrast/components/interface'
5
5
 
@@ -11,9 +11,9 @@ module Contrast
11
11
  include Contrast::Components::Interface
12
12
  access_component :agent, :analysis, :contrast_service, :logging
13
13
 
14
- CS__PATH = 'path'.cs__freeze
15
- CS__SESSION_ID = 'sessionId'.cs__freeze
16
- CS__SNIPPET = 'snippet'.cs__freeze
14
+ CS__PATH = 'path'
15
+ CS__SESSION_ID = 'sessionId'
16
+ CS__SNIPPET = 'snippet'
17
17
 
18
18
  # Build and report a finding for the given rule
19
19
  #
@@ -71,7 +71,7 @@ module Contrast
71
71
  idx = call_location&.lineno
72
72
  if file_path && idx && File.exist?(file_path)
73
73
  idx = idx > 5 ? idx - 5 : 0
74
- snippet = ''
74
+ snippet = +''
75
75
  File.foreach(file_path).with_index do |line, line_num|
76
76
  next unless line_num >= idx
77
77
  break if line_num > idx + 10
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2020 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 Utils
6
+ # A module that detects whether any job servers attached to
7
+ # the application are running
8
+ module JobServersRunning
9
+ include Contrast::Components::Interface
10
+ access_component :logging
11
+
12
+ class << self
13
+ def job_servers_running?
14
+ sidekiq_running? || rake_running?
15
+ end
16
+
17
+ def sidekiq_running?
18
+ return unless defined?(Sidekiq) && Sidekiq.cs__respond_to?(:server?) && Sidekiq.server?
19
+
20
+ logger.debug(nil, 'Detected the spawn of a Sidekiq process')
21
+ true
22
+ end
23
+
24
+ def rake_running?
25
+ return unless defined?(Rake) &&
26
+ Rake.cs__respond_to?(:application) &&
27
+ Rake.application.cs__respond_to?(:top_level_tasks)
28
+
29
+ disabled_rake_tasks = Contrast::Agent::FeatureState.instance.disabled_agent_rake_tasks
30
+ has_disabled_task = Rake.application.top_level_tasks.any? { |top_level_task| disabled_rake_tasks.include?(top_level_task) }
31
+ return false unless has_disabled_task
32
+
33
+ logger.debug(nil, 'Detected startup within Rake task')
34
+ true
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: false
2
+ # frozen_string_literal: true
3
3
 
4
4
  cs__scoped_require 'parser/current'
5
5
 
@@ -32,7 +32,7 @@ module Contrast
32
32
  def on_dstr node
33
33
  return if node.children.all? { |child_node| child_node.type == :str }
34
34
 
35
- new_content = '('
35
+ new_content = +'('
36
36
  node.children.each_with_index do |child_node, index|
37
37
  # A begin node looks like #{some_code} in ruby, we do a substring
38
38
  # from [2...-1] to get rid of the #{ & trailing }.
@@ -100,12 +100,6 @@ module Contrast
100
100
  end
101
101
  end
102
102
 
103
- if (dynamic_sources_map_pb = server_features&.assess&.dynamic_sources_map)
104
- dynamic_sources_map = dynamic_sources_map_pb.keys.inject({}) do |hsh, key|
105
- hsh[key] = dynamic_sources_map_pb[key]
106
- end
107
- Contrast::Agent::Assess::Policy::Policy.instance.update_dynamic_sources dynamic_sources_map
108
- end
109
103
  # This is for assess
110
104
  # PERF, we could avoid here with dirty-tracking.
111
105
  Contrast::Utils::Assess::SamplingUtil.instance.update
@@ -33,6 +33,12 @@ module Contrast
33
33
  end
34
34
  end
35
35
 
36
+ def self.settings_instance
37
+ @_settings_instance ||= begin
38
+ app_class&.settings
39
+ end
40
+ end
41
+
36
42
  def self.view_directory
37
43
  @_view_directory ||= begin
38
44
  app_class&.settings&.views
@@ -8,7 +8,7 @@ module Contrast
8
8
  module Utils
9
9
  # Utilities for converting ruby stack trace into DTMs
10
10
  class StackTraceUtils
11
- CONTRAST_MARKER = 'contrast/core_extensions'
11
+ CONTRAST_MARKER = 'contrast/extensions/ruby_core'
12
12
  MONKEYPATCH_MARKER = 'cs__'
13
13
 
14
14
  # TODO: RUBY-532
@@ -23,15 +23,17 @@
23
23
  "method_visibility": "public",
24
24
  "method_name":"body",
25
25
  "target":"R",
26
- "type":"BODY"
26
+ "type":"BODY",
27
+ "tags":["CROSS_SITE"]
27
28
  }, {
28
29
  "class_name":"Rack::Request::Env",
29
30
  "instance_method": true,
30
31
  "method_visibility": "public",
31
32
  "method_name":"get_header",
33
+ "source": "P0",
32
34
  "target":"R",
33
35
  "type":"HEADER",
34
- "tags":["NO_NEWLINES"]
36
+ "tags":["NO_NEWLINES", "CROSS_SITE"]
35
37
  }, {
36
38
  "class_name":"ActionDispatch::Request",
37
39
  "instance_method": true,
@@ -39,28 +41,30 @@
39
41
  "method_name": "raw_post",
40
42
  "target": "R",
41
43
  "type": "BODY",
42
- "tags":["NO_NEWLINES"]
44
+ "tags":["NO_NEWLINES", "CROSS_SITE"]
43
45
  }, {
44
46
  "class_name":"Rack::Request::Helpers",
45
47
  "instance_method": true,
46
48
  "method_visibility": "public",
47
49
  "method_name":"POST",
48
50
  "target":"R",
49
- "type":"PARAMETER"
51
+ "type":"PARAMETER",
52
+ "tags":["CROSS_SITE"]
50
53
  }, {
51
54
  "class_name":"Rack::Request::Helpers",
52
55
  "instance_method": true,
53
56
  "method_visibility": "public",
54
57
  "method_name":"GET",
55
58
  "target":"R",
56
- "type":"PARAMETER"
59
+ "type":"PARAMETER",
60
+ "tags":["CROSS_SITE"]
57
61
  }, {
58
62
  "class_name":"Rack::Request::Helpers",
59
63
  "instance_method": true,
60
64
  "method_visibility": "public",
61
65
  "method_name":"cookies",
62
66
  "target":"R",
63
- "type":"PARAMETER",
67
+ "type":"COOKIE",
64
68
  "tags":["NO_NEWLINES"]
65
69
  }, {
66
70
  "class_name":"Rack::Request::Helpers",
@@ -68,71 +72,72 @@
68
72
  "method_visibility": "public",
69
73
  "method_name":"url",
70
74
  "target":"R",
71
- "type":"PARAMETER"
75
+ "type":"PARAMETER",
76
+ "tags":["CROSS_SITE"]
72
77
  }, {
73
78
  "class_name":"Rack::Request::Helpers",
74
79
  "instance_method": true,
75
80
  "method_visibility": "public",
76
81
  "method_name":"query_string",
77
82
  "target":"R",
78
- "type":"PARAMETER"
83
+ "type":"BODY",
84
+ "tags":["CROSS_SITE"]
79
85
  }, {
80
86
  "class_name":"Rack::Request",
81
87
  "instance_method": true,
82
88
  "method_visibility": "public",
83
89
  "method_name":"body",
84
90
  "target":"R",
85
- "type":"BODY"
91
+ "type":"BODY",
92
+ "tags":["CROSS_SITE"]
86
93
  }, {
87
94
  "class_name":"Rack::Request",
88
95
  "instance_method": true,
89
96
  "method_visibility": "public",
90
97
  "method_name":"query_string",
91
98
  "target":"R",
92
- "type":"BODY"
99
+ "type":"BODY",
100
+ "tags":["CROSS_SITE"]
93
101
  }, {
94
102
  "class_name":"Rack::Request",
95
103
  "instance_method": true,
96
104
  "method_visibility": "public",
97
105
  "method_name":"GET",
98
106
  "target":"R",
99
- "type":"PARAMETER"
107
+ "type":"PARAMETER",
108
+ "tags":["CROSS_SITE"]
100
109
  }, {
101
110
  "class_name":"Rack::Request",
102
111
  "instance_method": true,
103
112
  "method_visibility": "public",
104
113
  "method_name":"POST",
105
114
  "target":"R",
106
- "type":"PARAMETER"
115
+ "type":"PARAMETER",
116
+ "tags":["CROSS_SITE"]
107
117
  }, {
108
118
  "class_name":"Rack::Request",
109
119
  "instance_method": true,
110
120
  "method_visibility": "public",
111
121
  "method_name":"cookies",
112
122
  "target":"R",
113
- "type":"PARAMETER",
123
+ "type":"COOKIE",
114
124
  "tags":["NO_NEWLINES"]
115
- }, {
116
- "class_name":"Rack::Request",
117
- "instance_method": true,
118
- "method_visibility": "public",
119
- "method_name":"url",
120
- "target":"R",
121
- "type":"BODY"
122
125
  }, {
123
126
  "class_name":"ActionController::Metal",
124
127
  "instance_method": true,
125
128
  "method_visibility": "public",
126
129
  "method_name":"params",
127
130
  "target":"R",
128
- "type":"PARAMETER"
131
+ "type":"PARAMETER",
132
+ "tags":["CROSS_SITE"]
129
133
  }, {
130
134
  "class_name":"ActionController::StrongParameters",
131
135
  "instance_method": true,
132
136
  "method_visibility": "public",
133
137
  "method_name":"params",
134
138
  "target":"R",
135
- "type":"PARAMETER"
139
+ "type":"PARAMETER",
140
+ "tags":["CROSS_SITE"]
136
141
  }
137
142
  ],
138
143
  "propagators":[
@@ -1213,6 +1218,7 @@
1213
1218
  ]
1214
1219
  }, {
1215
1220
  "name":"reflected-xss",
1221
+ "required_tags": ["CROSS_SITE"],
1216
1222
  "disallowed_tags":["BASE64_ENCODED", "CSS_ENCODED", "CSV_ENCODED", "HTML_ENCODED", "JAVASCRIPT_ENCODED", "JAVA_ENCODED", "LDAP_ENCODED", "OS_ENCODED", "SQL_ENCODED", "URL_ENCODED", "VBSCRIPT_ENCODED", "XML_ENCODED", "XPATH_ENCODED"],
1217
1223
  "triggers":[
1218
1224
  {
@@ -1565,7 +1571,6 @@
1565
1571
  },
1566
1572
  {
1567
1573
  "name": "xxe",
1568
- "dataflow": "true",
1569
1574
  "triggers": [
1570
1575
  {
1571
1576
  "class_name": "Ox",
@@ -1668,6 +1673,52 @@
1668
1673
  "source": "P0"
1669
1674
  }
1670
1675
  ]
1676
+ }, {
1677
+ "name": "xpath-injection",
1678
+ "disallowed_tags":["XPATH_ENCODED"],
1679
+ "triggers": [
1680
+ {
1681
+ "class_name": "XPath::Expression",
1682
+ "instance_method": true,
1683
+ "method_visibility": "private",
1684
+ "method_name": "initialize",
1685
+ "source": "P1",
1686
+ "trigger_class": "XPathLibraryTrigger",
1687
+ "trigger_method": "xpath_trigger_check"
1688
+ }, {
1689
+ "class_name": "Oga::XML::CharacterNode",
1690
+ "instance_method": true,
1691
+ "method_visibility": "public",
1692
+ "method_name": "initialize",
1693
+ "source": "P0",
1694
+ "trigger_class": "XPathLibraryTrigger",
1695
+ "trigger_method": "xpath_trigger_check"
1696
+ }, {
1697
+ "class_name": "XPather",
1698
+ "instance_method": true,
1699
+ "method_visibility": "public",
1700
+ "method_name": "get",
1701
+ "source": "P0"
1702
+ }, {
1703
+ "class_name": "XPather",
1704
+ "instance_method": true,
1705
+ "method_visibility": "public",
1706
+ "method_name": "search",
1707
+ "source": "P0"
1708
+ }, {
1709
+ "class_name": "Ox::HasAttrs",
1710
+ "instance_method": true,
1711
+ "method_visibility": "public",
1712
+ "method_name": "[]=",
1713
+ "source": "P1"
1714
+ }, {
1715
+ "class_name": "Nokogiri::XML::Node",
1716
+ "instance_method": true,
1717
+ "method_visibility": "public",
1718
+ "method_name": "[]=",
1719
+ "source": "P1"
1720
+ }
1721
+ ]
1671
1722
  }
1672
1723
  ]
1673
1724
  }