contrast-agent 3.8.5 → 3.9.0

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