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
@@ -0,0 +1,17 @@
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
+ cs__scoped_require 'contrast/utils/object_share'
5
+
6
+ # Some developers override various methods on Module, which can often involve
7
+ # changing expected method parity/behavior which in turn prevents us from being
8
+ # able to reliably use affected methods. Let's alias these method so that we
9
+ # always have access to them.
10
+ class Module
11
+ alias_method :cs__name, :name
12
+ alias_method :cs__constants, :constants
13
+ alias_method :cs__const_defined?, :const_defined?
14
+ alias_method :cs__const_get, :const_get
15
+ alias_method :cs__const_set, :const_set
16
+ alias_method :cs__autoload?, :autoload?
17
+ end
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  cs__scoped_require 'contrast/components/interface'
5
- cs__scoped_require 'contrast/core_extensions/protect/applies_deserialization_rule'
5
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_deserialization_rule'
6
6
 
7
7
  module Contrast
8
8
  module CoreExtensions
@@ -13,7 +13,7 @@ module Contrast
13
13
  # rule should be invoked.
14
14
  # In addition, b/c of the nature of Deserialization's sand boxing
15
15
  # function, this Module's apply methods call through to the
16
- # {#cs__patched_apply_deserialization_command_check} method of the
16
+ # {#apply_deserialization_command_check} method of the
17
17
  # Deserialization applicator.
18
18
  module AppliesCommandInjectionRule
19
19
  CS__SEMICOLON = '; '
@@ -22,12 +22,12 @@ module Contrast
22
22
  include Contrast::Components::Interface
23
23
  access_component :logging, :analysis
24
24
 
25
- def cs__patched_apply_command_injection_rule method, _exception, _properties, object, args
25
+ def apply_command_injection_rule method, _exception, _properties, object, args
26
26
  return unless valid_command?(args)
27
27
 
28
28
  command = build_command(args)
29
- Contrast::CoreExtensions::Protect::AppliesDeserializationRule.cs__patched_apply_deserialization_command_check(command)
30
- return if cs__skip_analysis?
29
+ Contrast::CoreExtensions::Protect::AppliesDeserializationRule.apply_deserialization_command_check(command)
30
+ return if skip_analysis?
31
31
 
32
32
  begin
33
33
  clazz = object.is_a?(Module) ? object : object.cs__class
@@ -41,11 +41,13 @@ module Contrast
41
41
  logger.error(e, msg)
42
42
  end
43
43
 
44
+ private
45
+
44
46
  def rule
45
47
  PROTECT.rule Contrast::Agent::Protect::Rule::CmdInjection::NAME
46
48
  end
47
49
 
48
- def cs__skip_analysis?
50
+ def skip_analysis?
49
51
  context = Contrast::Agent::REQUEST_TRACKER.current
50
52
  return true unless context&.app_loaded?
51
53
  return true unless rule&.enabled?
@@ -15,9 +15,9 @@ module Contrast
15
15
  access_component :logging, :analysis
16
16
 
17
17
  class << self
18
- def cs__patched_apply_deserialization_rule method, _exception, _properties, object, args
18
+ def apply_deserialization_rule method, _exception, _properties, object, args
19
19
  return unless valid_input?(args)
20
- return if cs__skip_analysis?
20
+ return if skip_analysis?
21
21
 
22
22
  rule.infilter(Contrast::Agent::REQUEST_TRACKER.current, args[0])
23
23
  rescue Contrast::SecurityException => e
@@ -26,13 +26,15 @@ module Contrast
26
26
  logger.error(e, "Error running untrusted-deserialization rule in #{ object }.#{ method }")
27
27
  end
28
28
 
29
- def cs__patched_apply_deserialization_command_check command
29
+ def apply_deserialization_command_check command
30
30
  return unless command
31
- return if cs__skip_analysis?
31
+ return if skip_analysis?
32
32
 
33
33
  rule.check_command_scope(command)
34
34
  end
35
35
 
36
+ private
37
+
36
38
  def rule
37
39
  PROTECT.rule Contrast::Agent::Protect::Rule::Deserialization::NAME
38
40
  end
@@ -44,7 +46,7 @@ module Contrast
44
46
  input.is_a?(String)
45
47
  end
46
48
 
47
- def cs__skip_analysis?
49
+ def skip_analysis?
48
50
  context = Contrast::Agent::REQUEST_TRACKER.current
49
51
  return true unless context&.app_loaded?
50
52
  return true unless rule&.enabled?
@@ -21,9 +21,9 @@ module Contrast
21
21
  include Contrast::Components::Interface
22
22
  access_component :logging, :analysis
23
23
 
24
- def cs__patched_apply_nosql_rule method, _exception, properties, _object, args
24
+ def apply_nosql_rule method, _exception, properties, _object, args
25
25
  return unless valid_input?(args)
26
- return if cs__skip_analysis?
26
+ return if skip_analysis?
27
27
 
28
28
  database = properties['database']
29
29
  operations = args[0]
@@ -41,6 +41,8 @@ module Contrast
41
41
  logger.error(e, "Error running NoSQLi rule in #{ properties['database'] }")
42
42
  end
43
43
 
44
+ private
45
+
44
46
  def rule
45
47
  PROTECT.rule Contrast::Agent::Protect::Rule::NoSqli::NAME
46
48
  end
@@ -51,7 +53,7 @@ module Contrast
51
53
  args[0]
52
54
  end
53
55
 
54
- def cs__skip_analysis?
56
+ def skip_analysis?
55
57
  context = Contrast::Agent::REQUEST_TRACKER.current
56
58
  return true unless context&.app_loaded?
57
59
  return true unless rule&.enabled?
@@ -17,32 +17,17 @@ module Contrast
17
17
  access_component :logging, :analysis
18
18
 
19
19
  class << self
20
- def cs__possible_write input
21
- input.cs__respond_to?(:to_s) &&
22
- input.to_s.include?(Contrast::Utils::ObjectShare::WRITE_FLAG)
23
- end
24
-
25
- def rule
26
- PROTECT.rule Contrast::Agent::Protect::Rule::PathTraversal::NAME
27
- end
28
-
29
- def cs__skip_analysis?
30
- context = Contrast::Agent::REQUEST_TRACKER.current
31
- return true unless context&.app_loaded?
32
- return true unless rule&.enabled?
33
- end
34
-
35
- def cs__patched_apply_path_traversal_rule method, _exception, properties, _object, args
20
+ def apply_path_traversal_rule method, _exception, properties, _object, args
36
21
  return unless args&.any?
37
22
 
38
23
  path = args[0]
39
24
  return unless path.is_a?(String)
40
- return if cs__skip_analysis?
25
+ return if skip_analysis?
41
26
 
42
27
  action = properties['action']
43
28
  write_marker = write?(action, *args)
44
- possible_write = write_marker && cs__possible_write(write_marker)
45
- cs__patched_path_traversal_rule(path, possible_write, method)
29
+ possible_write = write_marker && possible_write(write_marker)
30
+ path_traversal_rule(path, possible_write, method)
46
31
 
47
32
  # If the action was copy, we need to handle the write half of it.
48
33
  # We handled read in line above.
@@ -52,7 +37,26 @@ module Contrast
52
37
  dst = args[1]
53
38
  return unless dst.is_a?(String)
54
39
 
55
- cs__patched_path_traversal_rule(dst, true, method)
40
+ path_traversal_rule(dst, true, method)
41
+ end
42
+
43
+ private
44
+
45
+ def rule
46
+ PROTECT.rule Contrast::Agent::Protect::Rule::PathTraversal::NAME
47
+ end
48
+
49
+ def possible_write input
50
+ input.cs__respond_to?(:to_s) &&
51
+ input.to_s.include?(Contrast::Utils::ObjectShare::WRITE_FLAG)
52
+ end
53
+
54
+ def skip_analysis?
55
+ context = Contrast::Agent::REQUEST_TRACKER.current
56
+ return true unless context&.app_loaded?
57
+ return true unless rule&.enabled?
58
+
59
+ false
56
60
  end
57
61
 
58
62
  READ = 'read'
@@ -64,11 +68,11 @@ module Contrast
64
68
  return true if action == WRITE
65
69
 
66
70
  write_marker = args.length > 1 ? args[1] : nil
67
- write_marker && cs__possible_write(write_marker)
71
+ write_marker && possible_write(write_marker)
68
72
  end
69
73
 
70
- def cs__patched_path_traversal_rule path, possible_write, method
71
- return unless cs__patched_applies_to?(path, possible_write)
74
+ def path_traversal_rule path, possible_write, method
75
+ return unless applies_to?(path, possible_write)
72
76
 
73
77
  logger.debug(nil, "checking path traversal: write=true path=#{ path }")
74
78
  rule.infilter(Contrast::Agent::REQUEST_TRACKER.current, method, path)
@@ -79,8 +83,8 @@ module Contrast
79
83
  end
80
84
 
81
85
  CS__SAFER_REL_PATHS = %w[public app log tmp].cs__freeze
82
- def cs__patched_safer_abs_paths
83
- @_cs__patched_safer_abs_paths ||= begin
86
+ def safer_abs_paths
87
+ @_safer_abs_paths ||= begin
84
88
  pwd = ENV['PWD']
85
89
  if pwd
86
90
  tmp = CS__SAFER_REL_PATHS.map { |r| "#{ pwd }/#{ r }" }
@@ -93,7 +97,7 @@ module Contrast
93
97
  end
94
98
  end
95
99
 
96
- def cs__patched_applies_to? path, possible_write = false
100
+ def applies_to? path, possible_write = false
97
101
  # any possible write is a potential risk
98
102
  return true if possible_write
99
103
 
@@ -102,7 +106,7 @@ module Contrast
102
106
 
103
107
  path = path.downcase
104
108
  if path.start_with?(Contrast::Utils::ObjectShare::SLASH)
105
- cs__patched_safer_abs_paths.each do |prefix|
109
+ safer_abs_paths.each do |prefix|
106
110
  return false if path.start_with?(prefix)
107
111
  end
108
112
  else
@@ -21,13 +21,13 @@ module Contrast
21
21
 
22
22
  access_component :logging, :analysis
23
23
 
24
- def cs__patched_apply_sql_rule method, _exception, properties, object, args
24
+ def apply_sql_rule method, _exception, properties, object, args
25
25
  database = properties['database']
26
26
  return unless database
27
27
 
28
28
  index = properties[Contrast::Utils::ObjectShare::INDEX]
29
29
  return unless valid_input?(index, args)
30
- return if cs__skip_analysis?
30
+ return if skip_analysis?
31
31
 
32
32
  sql = args[index]
33
33
  logger.debug(nil, "applying sqli rule on #{ object } for #{ method }")
@@ -38,6 +38,8 @@ module Contrast
38
38
  logger.error(e, "Error running SQLi rule in #{ object }")
39
39
  end
40
40
 
41
+ private
42
+
41
43
  def rule
42
44
  PROTECT.rule Contrast::Agent::Protect::Rule::Sqli::NAME
43
45
  end
@@ -49,7 +51,7 @@ module Contrast
49
51
  sql && !sql.empty?
50
52
  end
51
53
 
52
- def cs__skip_analysis?
54
+ def skip_analysis?
53
55
  context = Contrast::Agent::REQUEST_TRACKER.current
54
56
  return true unless context&.app_loaded?
55
57
  return true unless rule&.enabled?
@@ -16,9 +16,9 @@ module Contrast
16
16
  access_component :logging, :analysis
17
17
 
18
18
  class << self
19
- def cs__patched_apply_xxe_rule _method, _exception, _properties, object, args
19
+ def apply_xxe_rule _method, _exception, _properties, object, args
20
20
  return unless valid_input?(args)
21
- return if cs__skip_analysis?
21
+ return if skip_analysis?
22
22
 
23
23
  xml = args.first
24
24
  parser = determine_parser(object)
@@ -34,10 +34,10 @@ module Contrast
34
34
 
35
35
  # IO is tricky. If we can't rewind it, we can't fix it back to the
36
36
  # original state. To be safe, we'll skip non-rewindable IO objects.
37
- def cs__patched_apply_xxe_rule__io _method, _exception, _properties, object, args
37
+ def apply_xxe_rule__io _method, _exception, _properties, object, args
38
38
  need_rewind = false
39
39
  return unless valid_io_input?(args)
40
- return if cs__skip_analysis?
40
+ return if skip_analysis?
41
41
 
42
42
  need_rewind = true
43
43
  xml = args.first.read
@@ -56,9 +56,9 @@ module Contrast
56
56
 
57
57
  # Oga's Lexer is a special case b/c the information we need is on the
58
58
  # object itself -- specifically in the @data instance variable
59
- def cs__patched_apply_xxe_rule__lexer _method, _exception, _properties, object, _args
59
+ def apply_xxe_rule__lexer _method, _exception, _properties, object, _args
60
60
  return unless valid_data_input?(object)
61
- return if cs__skip_analysis?
61
+ return if skip_analysis?
62
62
 
63
63
  parser = determine_parser(object)
64
64
  return unless parser
@@ -85,6 +85,8 @@ module Contrast
85
85
  logger.error(e, "Error running XXE rule in #{ parser }")
86
86
  end
87
87
 
88
+ private
89
+
88
90
  def rule
89
91
  PROTECT.rule Contrast::Agent::Protect::Rule::Xxe::NAME
90
92
  end
@@ -108,7 +110,7 @@ module Contrast
108
110
  object.instance_variable_get(DATA_KEY)
109
111
  end
110
112
 
111
- def cs__skip_analysis?
113
+ def skip_analysis?
112
114
  context = Contrast::Agent::REQUEST_TRACKER.current
113
115
  return true unless context&.app_loaded?
114
116
  return true unless rule&.enabled?
@@ -2,6 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  if defined?(Psych)
5
- cs__scoped_require 'contrast/core_extensions/protect/applies_deserialization_rule'
5
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_deserialization_rule'
6
6
  cs__scoped_require 'cs__patched_psych/cs__patched_psych'
7
7
  end
@@ -0,0 +1,63 @@
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 Framework
6
+ # The API for all subclasses to implement to correctly support a given framework
7
+ class BaseSupport
8
+ class << self
9
+ # The top level module name used by the framework
10
+ def detection_class
11
+ raise NoMethodError('Subclasses of BaseSupport should implement this method')
12
+ end
13
+
14
+ def version
15
+ raise NoMethodError('Subclasses of BaseSupport should implement this method')
16
+ end
17
+
18
+ def application_name
19
+ raise NoMethodError, 'Subclasses of BaseSupport should implement this method'
20
+ end
21
+
22
+ def server_type
23
+ raise NoMethodError, 'Subclasses of BaseSupport should implement this method'
24
+ end
25
+
26
+ # Iterate through known locations, looking for files
27
+ # that represent view or template files. If found, for each file in the directory
28
+ # append the technology and the view object to the application update instance
29
+ def scan_views
30
+ raise NoMethodError, 'Subclasses of BaseSupport should implement this method'
31
+ end
32
+
33
+ # Find all the predefined routes for this application and append them to the
34
+ # provided inventory message
35
+ # msg should be a Contrast::Api::Dtm::ApplicationUpdate or some other msg
36
+ # that has a routes array consisting of Contrast::Api::Dtm::RouteCoverage
37
+ def collect_routes
38
+ raise NoMethodError, 'Subclasses of BaseSupport should implement this method'
39
+ end
40
+
41
+ def current_route
42
+ raise NoMethodError, 'Subclasses of BaseSupport should implement this method'
43
+ end
44
+
45
+ protected
46
+
47
+ def source_or_string obj
48
+ if obj.cs__is_a?(Regexp)
49
+ obj.source
50
+ elsif obj.cs__respond_to?(:safe_string)
51
+ obj.safe_string
52
+ else
53
+ obj.to_s
54
+ end
55
+ end
56
+
57
+ def scan_view_directories view_technology_descriptors
58
+ view_technology_descriptors.reject(&:empty?)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,109 @@
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
+ cs__scoped_require 'contrast/framework/view_technologies_descriptor'
5
+ cs__scoped_require 'contrast/framework/platform_version'
6
+ cs__scoped_require 'contrast/framework/base_support'
7
+ cs__scoped_require 'contrast/framework/rails_support'
8
+ cs__scoped_require 'contrast/framework/sinatra_support'
9
+ cs__scoped_require 'contrast/components/interface'
10
+ cs__scoped_require 'contrast/utils/class_util'
11
+
12
+ module Contrast
13
+ module Framework
14
+ # Allows access to framework specific information
15
+ class Manager
16
+ include Contrast::Components::Interface
17
+ access_component :logging, :analysis
18
+
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
22
+ SUPPORTED_FRAMEWORKS = [
23
+ Contrast::Framework::RailsSupport,
24
+ Contrast::Framework::SinatraSupport
25
+ ].cs__freeze
26
+
27
+ def initialize
28
+ @_frameworks = SUPPORTED_FRAMEWORKS.map do |framework_klass|
29
+ next unless enable_framework_support?(framework_klass.detection_class)
30
+
31
+ logger.debug("Detected framework #{ framework_klass.detection_class } - enabling support")
32
+ framework_klass
33
+ end
34
+ @_frameworks.compact!
35
+ end
36
+
37
+ def find_applicable_view_technologies
38
+ scan_views_for_all_frameworks
39
+ end
40
+
41
+ def find_route_discovery_data
42
+ routes_for_all_frameworks
43
+ end
44
+
45
+ def platform_version
46
+ framework_version = first_framework_result :version, ''
47
+
48
+ Contrast::Framework::PlatformVersion.from_string(framework_version)
49
+ end
50
+
51
+ def server_type
52
+ first_framework_result :server_type, 'rack'
53
+ end
54
+
55
+ def app_name
56
+ first_framework_result :application_name, 'root'
57
+ end
58
+
59
+ def get_route_dtm request
60
+ result = nil
61
+ @_frameworks.find do |framework_klass|
62
+ # TODO: RUBY-763 Sinatra::Base#call patch adds the Route report
63
+ next if framework_klass == Contrast::Framework::SinatraSupport
64
+
65
+ result = framework_klass.current_route(request)
66
+ end
67
+ result
68
+ end
69
+
70
+ private
71
+
72
+ def enable_framework_support? klass
73
+ Contrast::Utils::ClassUtil.truly_defined?(klass)
74
+ end
75
+
76
+ def scan_views_for_all_frameworks
77
+ data_for_all_frameworks :scan_views
78
+ end
79
+
80
+ def routes_for_all_frameworks
81
+ data_for_all_frameworks :collect_routes
82
+ end
83
+
84
+ # This returns an array of all data from each framework in a flat, no-nil values array
85
+ #
86
+ # @param method_name [Symbol] the method to call on each FrameworkSupport class
87
+ # @return [Array]
88
+ def data_for_all_frameworks method_name
89
+ data = @_frameworks.flat_map do |framework|
90
+ framework.send(method_name)
91
+ end.compact
92
+ data
93
+ end
94
+
95
+ # This returns a single object from the first framework to successfully respond
96
+ #
97
+ # @param method_name [Symbol] the method to call on each FrameworkSupport class
98
+ # @return [Object] - Determined by method to be invoked
99
+ def first_framework_result method_name, default_value
100
+ result = nil
101
+ @_frameworks.each do |framework|
102
+ result = framework.send(method_name)
103
+ break if result
104
+ end
105
+ result || default_value
106
+ end
107
+ end
108
+ end
109
+ end