contrast-agent 3.11.0 → 3.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (217) hide show
  1. checksums.yaml +4 -4
  2. data/.flayignore +1 -0
  3. data/ext/cs__assess_active_record_named/cs__active_record_named.c +7 -2
  4. data/ext/cs__assess_active_record_named/cs__active_record_named.h +1 -0
  5. data/ext/cs__assess_array/cs__assess_array.c +2 -1
  6. data/ext/cs__assess_array/cs__assess_array.h +1 -0
  7. data/ext/cs__assess_basic_object/cs__assess_basic_object.c +3 -7
  8. data/ext/cs__assess_basic_object/cs__assess_basic_object.h +2 -1
  9. data/ext/cs__assess_kernel/cs__assess_kernel.c +1 -1
  10. data/ext/cs__assess_module/cs__assess_module.c +5 -7
  11. data/ext/cs__assess_module/cs__assess_module.h +3 -0
  12. data/ext/cs__common/cs__common.c +1 -1
  13. data/ext/cs__protect_kernel/cs__protect_kernel.c +4 -2
  14. data/ext/cs__protect_kernel/cs__protect_kernel.h +1 -0
  15. data/funchook/autom4te.cache/output.0 +13 -1
  16. data/funchook/autom4te.cache/requests +49 -48
  17. data/funchook/autom4te.cache/traces.0 +3 -0
  18. data/funchook/config.log +217 -378
  19. data/funchook/config.status +24 -23
  20. data/funchook/configure +13 -1
  21. data/funchook/src/Makefile +7 -7
  22. data/funchook/src/config.h +2 -2
  23. data/funchook/src/decoder.o +0 -0
  24. data/funchook/src/distorm.o +0 -0
  25. data/funchook/src/funchook.o +0 -0
  26. data/funchook/src/funchook_io.o +0 -0
  27. data/funchook/src/funchook_syscall.o +0 -0
  28. data/funchook/src/funchook_unix.o +0 -0
  29. data/funchook/src/funchook_x86.o +0 -0
  30. data/funchook/src/instructions.o +0 -0
  31. data/funchook/src/insts.o +0 -0
  32. data/funchook/src/libfunchook.so +0 -0
  33. data/funchook/src/mnemonics.o +0 -0
  34. data/funchook/src/operands.o +0 -0
  35. data/funchook/src/os_func.o +0 -0
  36. data/funchook/src/os_func_unix.o +0 -0
  37. data/funchook/src/prefix.o +0 -0
  38. data/funchook/src/printf_base.o +0 -0
  39. data/funchook/src/textdefs.o +0 -0
  40. data/funchook/src/wstring.o +0 -0
  41. data/funchook/test/Makefile +2 -2
  42. data/funchook/test/funchook_test +0 -0
  43. data/funchook/test/libfunchook_test.so +0 -0
  44. data/funchook/test/test_main.o +0 -0
  45. data/funchook/test/x86_64_test.o +0 -0
  46. data/lib/contrast.rb +0 -1
  47. data/lib/contrast/agent.rb +19 -22
  48. data/lib/contrast/agent/assess.rb +0 -9
  49. data/lib/contrast/agent/assess/policy/patcher.rb +1 -0
  50. data/lib/contrast/agent/assess/policy/policy_node.rb +1 -1
  51. data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
  52. data/lib/contrast/agent/assess/policy/propagation_method.rb +3 -0
  53. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  54. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -3
  55. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +90 -0
  56. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +57 -0
  57. data/lib/contrast/agent/assess/policy/trigger_method.rb +3 -7
  58. data/lib/contrast/agent/assess/policy/trigger_node.rb +4 -1
  59. data/lib/contrast/agent/assess/rule/base.rb +0 -15
  60. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +22 -5
  61. data/lib/contrast/agent/assess/rule/redos.rb +0 -1
  62. data/lib/contrast/agent/at_exit_hook.rb +2 -2
  63. data/lib/contrast/agent/class_reopener.rb +9 -4
  64. data/lib/contrast/agent/exclusion_matcher.rb +0 -1
  65. data/lib/contrast/agent/inventory/policy/datastores.rb +54 -0
  66. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  67. data/lib/contrast/agent/middleware.rb +38 -34
  68. data/lib/contrast/agent/patching/policy/after_load_patch.rb +11 -2
  69. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +51 -56
  70. data/lib/contrast/agent/patching/policy/patch.rb +2 -1
  71. data/lib/contrast/agent/patching/policy/patcher.rb +10 -12
  72. data/lib/contrast/agent/patching/policy/policy_node.rb +1 -1
  73. data/lib/contrast/agent/patching/policy/trigger_node.rb +1 -1
  74. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +63 -0
  75. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +52 -0
  76. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +68 -0
  77. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +117 -0
  78. data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +54 -0
  79. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +129 -0
  80. data/lib/contrast/agent/protect/policy/policy.rb +6 -6
  81. data/lib/contrast/agent/protect/policy/rule_applicator.rb +51 -0
  82. data/lib/contrast/agent/protect/rule.rb +0 -5
  83. data/lib/contrast/agent/protect/rule/base.rb +6 -5
  84. data/lib/contrast/agent/protect/rule/cmd_injection.rb +3 -3
  85. data/lib/contrast/agent/protect/rule/path_traversal.rb +2 -7
  86. data/lib/contrast/agent/protect/rule/sqli.rb +4 -4
  87. data/lib/contrast/agent/railtie.rb +1 -0
  88. data/lib/contrast/agent/request.rb +2 -6
  89. data/lib/contrast/agent/request_context.rb +5 -6
  90. data/lib/contrast/agent/request_handler.rb +2 -2
  91. data/lib/contrast/agent/response.rb +0 -69
  92. data/lib/contrast/agent/service_heartbeat.rb +2 -2
  93. data/lib/contrast/agent/socket_client.rb +8 -8
  94. data/lib/contrast/agent/static_analysis.rb +2 -3
  95. data/lib/contrast/agent/version.rb +1 -1
  96. data/lib/contrast/api/decorators/application_settings.rb +1 -1
  97. data/lib/contrast/api/speedracer.rb +1 -1
  98. data/lib/contrast/components/agent.rb +17 -12
  99. data/lib/contrast/components/app_context.rb +33 -1
  100. data/lib/contrast/components/assess.rb +25 -15
  101. data/lib/contrast/components/contrast_service.rb +23 -67
  102. data/lib/contrast/components/interface.rb +4 -12
  103. data/lib/contrast/components/inventory.rb +5 -1
  104. data/lib/contrast/components/logger.rb +2 -2
  105. data/lib/contrast/components/protect.rb +40 -4
  106. data/lib/contrast/components/scope.rb +2 -52
  107. data/lib/contrast/components/settings.rb +24 -18
  108. data/lib/contrast/config/protect_rules_configuration.rb +0 -1
  109. data/lib/contrast/{extensions/ruby_core → extension}/assess.rb +12 -14
  110. data/lib/contrast/extension/assess/array.rb +77 -0
  111. data/lib/contrast/{extensions/ruby_core → extension}/assess/assess_extension.rb +2 -2
  112. data/lib/contrast/{extensions/ruby_core → extension}/assess/erb.rb +0 -0
  113. data/lib/contrast/extension/assess/eval_trigger.rb +78 -0
  114. data/lib/contrast/{extensions/ruby_core → extension}/assess/exec_trigger.rb +1 -1
  115. data/lib/contrast/{extensions/ruby_core → extension}/assess/fiber.rb +6 -5
  116. data/lib/contrast/{extensions/ruby_core → extension}/assess/hash.rb +2 -2
  117. data/lib/contrast/extension/assess/kernel.rb +110 -0
  118. data/lib/contrast/{extensions/ruby_core → extension}/assess/regexp.rb +4 -4
  119. data/lib/contrast/{extensions/ruby_core → extension}/assess/string.rb +5 -5
  120. data/lib/contrast/{extensions/ruby_core → extension}/delegator.rb +0 -0
  121. data/lib/contrast/{extensions/ruby_core → extension}/inventory.rb +2 -2
  122. data/lib/contrast/extension/kernel.rb +54 -0
  123. data/lib/contrast/{extensions/ruby_core → extension}/module.rb +0 -0
  124. data/lib/contrast/{extensions/ruby_core → extension}/protect.rb +2 -2
  125. data/lib/contrast/extension/protect/kernel.rb +44 -0
  126. data/lib/contrast/{extensions/ruby_core → extension}/protect/psych.rb +1 -1
  127. data/lib/contrast/{extensions/ruby_core → extension}/thread.rb +0 -0
  128. data/lib/contrast/framework/base_support.rb +22 -0
  129. data/lib/contrast/framework/manager.rb +33 -8
  130. data/lib/contrast/framework/rack/patch/session_cookie.rb +126 -0
  131. data/lib/contrast/framework/rack/patch/support.rb +24 -0
  132. data/lib/contrast/framework/rack/support.rb +22 -0
  133. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +43 -0
  134. data/lib/contrast/framework/rails/patch/assess_configuration.rb +103 -0
  135. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +31 -0
  136. data/lib/contrast/framework/rails/patch/support.rb +67 -0
  137. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +34 -0
  138. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +39 -0
  139. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +73 -0
  140. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +33 -0
  141. data/lib/contrast/framework/rails/support.rb +115 -0
  142. data/lib/contrast/framework/sinatra/application_helper.rb +51 -0
  143. data/lib/contrast/framework/sinatra/patch/base.rb +83 -0
  144. data/lib/contrast/framework/sinatra/patch/support.rb +27 -0
  145. data/lib/contrast/framework/sinatra/support.rb +109 -0
  146. data/lib/contrast/logger/application.rb +80 -0
  147. data/lib/contrast/{agent/logger.rb → logger/log.rb} +23 -54
  148. data/lib/contrast/logger/time.rb +50 -0
  149. data/lib/contrast/tasks/config.rb +54 -0
  150. data/lib/contrast/tasks/service.rb +1 -5
  151. data/lib/contrast/utils/class_util.rb +1 -1
  152. data/lib/contrast/utils/gemfile_reader.rb +2 -2
  153. data/lib/contrast/utils/hash_digest.rb +2 -7
  154. data/lib/contrast/utils/invalid_configuration_util.rb +3 -3
  155. data/lib/contrast/utils/job_servers_running.rb +4 -2
  156. data/lib/contrast/utils/object_share.rb +0 -1
  157. data/lib/contrast/utils/service_response_util.rb +14 -12
  158. data/lib/contrast/utils/service_sender_util.rb +78 -21
  159. data/resources/assess/policy.json +9 -50
  160. data/resources/inventory/policy.json +2 -2
  161. data/resources/protect/policy.json +6 -6
  162. data/ruby-agent.gemspec +5 -1
  163. data/service_executables/VERSION +1 -1
  164. data/service_executables/linux/contrast-service +0 -0
  165. data/service_executables/mac/contrast-service +0 -0
  166. metadata +69 -83
  167. data/funchook/src/libfunchook.dylib +0 -0
  168. data/funchook/test/libfunchook_test.so.dSYM/Contents/Info.plist +0 -20
  169. data/funchook/test/libfunchook_test.so.dSYM/Contents/Resources/DWARF/libfunchook_test.so +0 -0
  170. data/lib/contrast/agent/assess/rule/csrf.rb +0 -66
  171. data/lib/contrast/agent/assess/rule/csrf/csrf_action.rb +0 -28
  172. data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +0 -53
  173. data/lib/contrast/agent/assess/rule/csrf/csrf_watcher.rb +0 -136
  174. data/lib/contrast/agent/assess/rule/response_scanning_rule.rb +0 -47
  175. data/lib/contrast/agent/assess/rule/response_watcher.rb +0 -36
  176. data/lib/contrast/agent/assess/rule/watcher.rb +0 -36
  177. data/lib/contrast/agent/feature_state.rb +0 -346
  178. data/lib/contrast/agent/protect/rule/csrf.rb +0 -119
  179. data/lib/contrast/agent/protect/rule/csrf/csrf_evaluator.rb +0 -100
  180. data/lib/contrast/agent/protect/rule/csrf/csrf_token_injector.rb +0 -85
  181. data/lib/contrast/agent/settings_state.rb +0 -88
  182. data/lib/contrast/api/decorators/exclusion.rb +0 -20
  183. data/lib/contrast/extensions/framework/rack/cookie.rb +0 -24
  184. data/lib/contrast/extensions/framework/rack/request.rb +0 -24
  185. data/lib/contrast/extensions/framework/rack/response.rb +0 -23
  186. data/lib/contrast/extensions/framework/rails/action_controller_inheritance.rb +0 -39
  187. data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +0 -20
  188. data/lib/contrast/extensions/framework/rails/active_record.rb +0 -26
  189. data/lib/contrast/extensions/framework/rails/active_record_named.rb +0 -58
  190. data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +0 -21
  191. data/lib/contrast/extensions/framework/rails/buffer.rb +0 -28
  192. data/lib/contrast/extensions/framework/rails/configuration.rb +0 -27
  193. data/lib/contrast/extensions/framework/sinatra/base.rb +0 -59
  194. data/lib/contrast/extensions/ruby_core/assess/array.rb +0 -59
  195. data/lib/contrast/extensions/ruby_core/assess/basic_object.rb +0 -15
  196. data/lib/contrast/extensions/ruby_core/assess/kernel.rb +0 -96
  197. data/lib/contrast/extensions/ruby_core/assess/module.rb +0 -14
  198. data/lib/contrast/extensions/ruby_core/assess/tilt_template_trigger.rb +0 -78
  199. data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +0 -40
  200. data/lib/contrast/extensions/ruby_core/eval_trigger.rb +0 -51
  201. data/lib/contrast/extensions/ruby_core/inventory/datastores.rb +0 -37
  202. data/lib/contrast/extensions/ruby_core/protect/applies_command_injection_rule.rb +0 -61
  203. data/lib/contrast/extensions/ruby_core/protect/applies_deserialization_rule.rb +0 -50
  204. data/lib/contrast/extensions/ruby_core/protect/applies_no_sqli_rule.rb +0 -66
  205. data/lib/contrast/extensions/ruby_core/protect/applies_path_traversal_rule.rb +0 -115
  206. data/lib/contrast/extensions/ruby_core/protect/applies_sqli_rule.rb +0 -53
  207. data/lib/contrast/extensions/ruby_core/protect/applies_xxe_rule.rb +0 -127
  208. data/lib/contrast/extensions/ruby_core/protect/kernel.rb +0 -30
  209. data/lib/contrast/extensions/ruby_core/protect/rule_applicator.rb +0 -50
  210. data/lib/contrast/framework/rails_support.rb +0 -104
  211. data/lib/contrast/framework/sinatra_application_helper.rb +0 -49
  212. data/lib/contrast/framework/sinatra_support.rb +0 -104
  213. data/lib/contrast/utils/data_store_util.rb +0 -23
  214. data/lib/contrast/utils/rack_assess_session_cookie.rb +0 -104
  215. data/lib/contrast/utils/rails_assess_configuration.rb +0 -95
  216. data/lib/contrast/utils/random_util.rb +0 -22
  217. data/resources/csrf/inject.js +0 -44
@@ -1,28 +0,0 @@
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 Agent
6
- module Assess
7
- module Rule
8
- class Csrf
9
- # CSRF is only reported on those Requests which also result in a
10
- # state changing action (database call). This class servers as a
11
- # record of that action.
12
- class CsrfAction
13
- attr_accessor :type, :evidence
14
-
15
- TYPE = 'type'
16
- EVIDENCE = 'evidence'
17
- def to_h
18
- {
19
- TYPE => type,
20
- EVIDENCE => evidence
21
- }
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,53 +0,0 @@
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
- cs__scoped_require 'contrast/components/interface'
6
-
7
- module Contrast
8
- module Agent
9
- module Assess
10
- module Rule
11
- class Csrf
12
- # This class is called by our patches to determine if a CSRF
13
- # vulnerability exists within an application. It is used through a
14
- # CUSTOM propagation in order to capture that a Database call was
15
- # made in response to a request that did not have the Contrast CSRF
16
- # token.
17
- class CsrfApplicator
18
- include Contrast::Components::Interface
19
- access_component :analysis, :logging, :scope
20
-
21
- class << self
22
- def csrf_tagger patcher, preshift, _ret, _block
23
- return unless rule&.enabled?
24
-
25
- idx = patcher.sources[0].to_i
26
- args = preshift.args
27
- return unless args&.length.to_i > idx
28
-
29
- sql = args[idx]
30
- return unless sql
31
-
32
- with_contrast_scope do
33
- rule.record_db_state_change(
34
- Contrast::Agent::REQUEST_TRACKER.current,
35
- sql)
36
- end
37
- rescue StandardError => e
38
- logger.warn('Error running CSRF assess rule', e)
39
- end
40
-
41
- private
42
-
43
- def rule
44
- @_rule ||= Contrast::Agent::FeatureState.instance.assess_rule(
45
- Contrast::Agent::Assess::Rule::Csrf::NAME)
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,136 +0,0 @@
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/agent/assess/rule/response_watcher'
5
- cs__scoped_require 'contrast/components/interface'
6
-
7
- module Contrast
8
- module Agent
9
- module Assess
10
- module Rule
11
- class Csrf
12
- # Watchers are how those Rules which do not act on dataflow function.
13
- # This one is used by the CSRF rule to determine if a request was
14
- # made in a way that is susceptible to a CSRF attack.
15
- class Watcher < Contrast::Agent::Assess::Rule::ResponseWatcher
16
- include Contrast::Components::Interface
17
- access_component :logging
18
-
19
- def supports? context
20
- return false unless super(context)
21
- return false unless user_agent?(context)
22
- return false unless form_content_type?(context)
23
- return false unless form_method?(context)
24
- return false if empty_get?(context)
25
-
26
- true
27
- end
28
-
29
- X_REQUESTED_WITH = 'X-REQUESTED-WITH'
30
- # ignore this request if X-Requested-With is set
31
- def x_requested_with? context
32
- !context.request.normalized_request_headers[X_REQUESTED_WITH].nil?
33
- end
34
-
35
- USER_AGENT = 'USER-AGENT'
36
- # ignore this request if User-Agent is NOT set
37
- # since that indicates this isn't a browser interaction
38
- def user_agent? context
39
- !context.request.normalized_request_headers[USER_AGENT].nil?
40
- end
41
-
42
- CONTENT_TYPE = 'CONTENT-TYPE'
43
- FORM_TYPES = %w[
44
- text/plain
45
- multipart/form-data
46
- application/x-www-form-urlencoded
47
- ].cs__freeze
48
- # ignore this request if the Content-Type is NOT set
49
- # to a form content type
50
- def form_content_type? context
51
- type = context.request.content_type
52
- # We need to account for the charset being part of the header. It doesn't
53
- # affect how we determine CSRF-ability or not
54
- type = type.split(Contrast::Utils::ObjectShare::SEMICOLON)[0] if type
55
- FORM_TYPES.include?(type)
56
- end
57
-
58
- FORM_METHODS = %w[GET POST].cs__freeze
59
- # ignore this request if the request method
60
- # is not one that can be set with forms
61
- def form_method? context
62
- FORM_METHODS.include?(context.request.request_method)
63
- end
64
-
65
- GET = 'GET'
66
- # ignore this request if the method is get
67
- # and the query string is empty
68
- def empty_get? context
69
- request = context.request
70
- request.request_method ==
71
- GET &&
72
- (request.query_string.nil? || request.query_string.empty?)
73
- end
74
-
75
- # If a parameter contains 'csrf' or 'token', we consider
76
- # it to be a guard against CSRF and therefore determine
77
- # that this request is not vulnerable.
78
- CSRF_PATTERN = /csrf/i.cs__freeze
79
- TOKEN_PATTERN = /token/i.cs__freeze
80
- def vulnerable? context
81
- # having the property 'csrf.token.checked' indicates
82
- # that a CSRF check was preformed at some point during
83
- # this request, so we consider it to not be vulnerable
84
- return false if context.get_property(CHECKED)
85
-
86
- params = context.request.parameters
87
- params.each_key do |key|
88
- return false if key.match?(CSRF_PATTERN)
89
- return false if key.match?(TOKEN_PATTERN) && looks_like_token?(params[key])
90
- end
91
- # having no actions means there's no vulnerability
92
- return false unless context.get_property(STATE_CHANGING_ACTIONS_KEY)&.any?
93
-
94
- true
95
- end
96
-
97
- MINIMUM_CSRF_TOKEN_SIZE = 8
98
- MAXIMUM_CSRF_TOKEN_SIZE = 24
99
- TOKEN_REGEXP = /^[A-Za-z0-9]*$/.cs__freeze
100
- def looks_like_token? value
101
- return false unless value
102
-
103
- values = [value]
104
- values.each do |parameter|
105
- next unless parameter
106
-
107
- length = parameter.length
108
- next if length < MINIMUM_CSRF_TOKEN_SIZE
109
- next if length > MAXIMUM_CSRF_TOKEN_SIZE
110
- return true if parameter.match?(TOKEN_REGEXP)
111
- end
112
- false
113
- end
114
-
115
- DATA_KEY = 'actions'
116
- def build_finding context
117
- actions = context.get_property(STATE_CHANGING_ACTIONS_KEY)
118
- return if actions.nil? || actions.empty?
119
-
120
- string = actions.map(&:to_h).to_json
121
- finding = Contrast::Api::Dtm::Finding.new
122
- finding.rule_id = Contrast::Agent::Assess::Rule::Csrf::NAME
123
- finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
124
- hash_code = Contrast::Utils::HashDigest.generate_response_hash(finding)
125
- finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
126
- finding.properties[DATA_KEY] = Contrast::Utils::StringUtils.force_utf8(string)
127
- finding
128
- rescue StandardError => e
129
- logger.error('Unable to build a finding for CSRF', e)
130
- end
131
- end
132
- end
133
- end
134
- end
135
- end
136
- end
@@ -1,47 +0,0 @@
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 Agent
6
- module Assess
7
- module Rule
8
- # Those rules which function by scanning the Response body in order to
9
- # detect vulnerabilities. These rules should each have their own
10
- # Contrast::Agent::Assess::RuleResponseWatcher.
11
- #
12
- # Note: Most have been moved to the Service, as they typically watch
13
- # the Request or Response bodies, parsing out vulnerabilities
14
- # therein. CSRF is an exception to this as the rule requires a change
15
- # to the Response body to function.
16
- class ResponseScanningRule < Contrast::Agent::Assess::Rule::Base
17
- def watcher
18
- # raise(
19
- # NotImplementedError,
20
- # 'A child rule should have overridden the watcher method')
21
- end
22
-
23
- def stream_safe?
24
- false
25
- end
26
-
27
- def generate_hash finding
28
- Contrast::Utils::HashDigest.generate_response_hash(finding)
29
- end
30
-
31
- def postfilter context
32
- findings = watcher.postfilter(context) if watcher && context
33
- return unless findings
34
-
35
- if findings.is_a?(Array)
36
- findings.each do |finding|
37
- send_report(finding) if finding
38
- end
39
- else
40
- send_report(findings)
41
- end
42
- end
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,36 +0,0 @@
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 Agent
6
- module Assess
7
- module Rule
8
- # A watcher focused on the Response body, parsing out vulnerabilities
9
- # therein.
10
- #
11
- # Note: Most have been moved to the Service, as they typically watch
12
- # the Request or Response bodies, parsing out vulnerabilities
13
- # therein. CSRF is an exception to this as the rule requires a change
14
- # to the Response body to function.
15
- class ResponseWatcher < Contrast::Agent::Assess::Rule::Watcher
16
- def postfilter context
17
- return unless supports?(context)
18
- return unless vulnerable?(context)
19
-
20
- build_finding(context)
21
- end
22
-
23
- def vulnerable? _context
24
- raise(
25
- NotImplementedError,
26
- 'A child rule should have overridden the vulnerable? method')
27
- end
28
-
29
- def build_finding _context
30
- Contrast::Api::Dtm::Finding.new
31
- end
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,36 +0,0 @@
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 Agent
6
- module Assess
7
- module Rule
8
- # Watchers are how those Rules which do not act on dataflow function.
9
- #
10
- # Note: Most have been moved to the Service, as they typically watch
11
- # the Request or Response bodies, parsing out vulnerabilities
12
- # therein. CSRF is an exception to this as the rule requires a change
13
- # to the Response body to function.
14
- class Watcher
15
- def supports? context
16
- return false if context.request.static_request?
17
- return false unless context.response
18
- return false if undesired_response_code? context.response.response_code
19
- return false if undesired_response_type? context.response.content_type
20
-
21
- true
22
- end
23
-
24
- UNDESIRED_RESPONSE_CODES = [301, 302, 307, 404, 410, 500].cs__freeze
25
- def undesired_response_code? code
26
- UNDESIRED_RESPONSE_CODES.include?(code)
27
- end
28
-
29
- def undesired_response_type? _type
30
- false
31
- end
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,346 +0,0 @@
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/agent/settings_state'
5
- cs__scoped_require 'contrast/utils/boolean_util'
6
-
7
- module Contrast
8
- module Agent
9
- # This class functions as a way to query the Agent for its current feature
10
- # set without having to expose other sections of code to the decision tree
11
- # needed to make that determination.
12
- class FeatureState < SettingsState
13
- include Singleton
14
-
15
- # FeatureState methods are grouped in modules,
16
- # and these modules are organized roughly in ascending order
17
- # according to their complexity - where more complex
18
- # methods call simpler ones.
19
-
20
- module ConfigWrappers
21
- # Wrapper to provide convenience for boolean values, allowing for the
22
- # handling of nil defaults that behave like true and other strange
23
- # cases.
24
- module Booleans
25
- include Contrast::Components::Interface
26
- access_component :config
27
-
28
- def false? config
29
- Contrast::Utils::BooleanUtil.false?(config)
30
- end
31
-
32
- def true? config
33
- Contrast::Utils::BooleanUtil.true?(config)
34
- end
35
-
36
- def inventory_enabled?
37
- @_inventory_enabled = !false?(CONFIG.root.inventory.enable) if @_inventory_enabled.nil?
38
- @_inventory_enabled
39
- end
40
-
41
- def protect_forcibly_enabled?
42
- @_protect_forcibly_enabled = true?(CONFIG.root.protect.enable) if @_protect_forcibly_enabled.nil?
43
- @_protect_forcibly_enabled
44
- end
45
-
46
- def protect_forcibly_disabled?
47
- @_protect_forcibly_disabled = false?(CONFIG.root.protect.enable) if @_protect_forcibly_disabled.nil?
48
- @_protect_forcibly_disabled
49
- end
50
-
51
- def assess_forcibly_enabled?
52
- @_assess_forcibly_enabled = true?(CONFIG.root.assess.enable) if @_assess_forcibly_enabled.nil?
53
- @_assess_forcibly_enabled
54
- end
55
-
56
- def assess_forcibly_disabled?
57
- @_assess_forcibly_disabled = false?(CONFIG.root.assess.enable) if @_assess_forcibly_disabled.nil?
58
- @_assess_forcibly_disabled
59
- end
60
-
61
- def assess_track_frozen_sources?
62
- @_assess_track_frozen_sources = !false?(CONFIG.root.agent.ruby.track_frozen_sources) if @_assess_track_frozen_sources.nil?
63
- @_assess_track_frozen_sources
64
- end
65
-
66
- def scan_response?
67
- @_scan_response = !false?(CONFIG.root.assess.enable_scan_response) if @_scan_response.nil?
68
- @_scan_response
69
- end
70
-
71
- def omit_body?
72
- @_omit_body = true?(CONFIG.root.agent.omit_body) if @_omit_body.nil?
73
- @_omit_body
74
- end
75
-
76
- def require_scanning_enabled?
77
- @_require_scanning_enabled = !false?(CONFIG.root.agent.ruby.require_scan) if @_require_scanning_enabled.nil?
78
- @_require_scanning_enabled
79
- end
80
-
81
- def service_forcibly_disabled?
82
- @_service_forcibly_disabled = false?(CONFIG.root.agent.start_bundled_service) if @_service_forcibly_disabled.nil?
83
- @_service_forcibly_disabled
84
- end
85
-
86
- def report_any_command_execution?
87
- if @_report_any_command_execution.nil?
88
- ctrl = protect_rule_config[Contrast::Agent::Protect::Rule::CmdInjection::NAME]
89
- @_report_any_command_execution = true?(ctrl.disable_system_commands)
90
- end
91
- @_report_any_command_execution
92
- end
93
-
94
- def report_custom_code_sysfile_access?
95
- if @_report_custom_code_sysfile_access.nil?
96
- ctrl = protect_rule_config[Contrast::Agent::Protect::Rule::PathTraversal::NAME]
97
- @_report_custom_code_sysfile_access = true?(ctrl.detect_custom_code_accessing_system_files)
98
- end
99
- @_report_custom_code_sysfile_access
100
- end
101
-
102
- # Determines if the Process we're currently in matches that of the
103
- # Process in which the FeatureState instance was created.
104
- # If it doesn't, that indicates the running context is in a new
105
- # Process.
106
- # @return [Boolean] if we're in the original Process in which the
107
- # FeaturesState instance was initialized.
108
- def in_new_process?
109
- current_pid = Process.pid
110
- original_pid = pid
111
- current_pid != original_pid
112
- end
113
- end
114
-
115
- # Wrapper for configurations regarding assess & protect rules
116
- module Parameters
117
- include Contrast::Components::Interface
118
- access_component :settings, :config
119
-
120
- def protect_rule_config
121
- CONFIG.root.protect.rules || []
122
- end
123
-
124
- def assess_rule_disabled? name
125
- assess_disabled_rules.include? name
126
- end
127
-
128
- def protect_rule_enabled? rule_id
129
- protect_rule_mode(rule_id).to_sym != :NO_ACTION
130
- end
131
-
132
- def assess_tags
133
- CONFIG.root.assess&.tags
134
- end
135
-
136
- def assess_disabled_rules
137
- CONFIG.root.assess&.rules&.disabled_rules || SETTINGS.disabled_assess_rules || []
138
- end
139
-
140
- def current_session_id
141
- Contrast::Utils::StringUtils.force_utf8(SETTINGS.session_id)
142
- end
143
-
144
- def disabled_agent_rake_tasks
145
- CONFIG.root.agent.ruby.disabled_agent_rake_tasks
146
- end
147
-
148
- def exclusions
149
- SETTINGS.exclusion_matchers
150
- end
151
-
152
- def code_exclusions
153
- exclusions.select(&:code?)
154
- end
155
- end
156
- end
157
-
158
- # Wrapper for the Assess & Protect states
159
- module SettingWrappers
160
- include Contrast::Components::Interface
161
- access_component :settings, :config
162
-
163
- def protect_setting_enabled?
164
- SETTINGS.protect_state[:enabled]
165
- end
166
-
167
- def assess_setting_enabled?
168
- SETTINGS.assess_state[:enabled]
169
- end
170
- end
171
-
172
- # whether it's connected to the Contrast Service,
173
- # whether config succeeded,
174
- # whether writable paths are actually writeable, etc.
175
- module AgentState
176
- # Indicates the status of the Agent - initialized & ready - as well as
177
- # the mode(s) in which it is functioning
178
- module Operation
179
- include Contrast::Components::Interface
180
- access_component :agent
181
-
182
- # Return true if the agent is ready to protect the application
183
- def agent_ready?
184
- AGENT.enabled? && connection_established? && update_received?
185
- end
186
-
187
- def protect_enabled?
188
- return false unless AGENT.enabled?
189
-
190
- # config overrides if forcibly set
191
- return false if protect_forcibly_disabled?
192
- return true if protect_forcibly_enabled?
193
-
194
- !!protect_setting_enabled?
195
- end
196
-
197
- def assess_enabled?
198
- return false unless AGENT.enabled?
199
-
200
- # config overrides if forcibly set
201
- return false if assess_forcibly_disabled?
202
- return true if assess_forcibly_enabled?
203
-
204
- !!assess_setting_enabled?
205
- end
206
- end
207
-
208
- # Wrapper for the state of our Service settings and a way to access the
209
- # Contrast::Agent::SocketClient responsible for interactions between
210
- # the Agent & Service.
211
- module Service
212
- include Contrast::Components::Interface
213
- access_component :agent
214
-
215
- def service_enabled?
216
- AGENT.enabled? && !service_forcibly_disabled?
217
- end
218
-
219
- def client
220
- @_client ||= Contrast::Agent::SocketClient.new
221
- end
222
-
223
- # Return true if the agent has connected with the service
224
- def connection_established?
225
- client.connection_established?
226
- end
227
-
228
- # Return true if the agent has processed a response from the service
229
- def update_received?
230
- !@last_update.nil?
231
- end
232
- end
233
- end
234
-
235
- # 'Controls' synthesize all of the above into parameter sets.
236
- # These can have more complex logic.
237
- module Controls
238
- include Contrast::Components::Interface
239
- access_component :config
240
-
241
- DEFAULT_LOG_FILENAME = 'contrast_service.log'
242
- def service_control
243
- @_service_control ||= {
244
- host: CONFIG.root.agent.service.host || Contrast::Configuration::DEFAULT_HOST,
245
- port: CONFIG.root.agent.service.port || Contrast::Configuration::DEFAULT_PORT,
246
- socket_path: CONFIG.root.agent.service.socket,
247
- logger_path: CONFIG.root.agent.service.logger.path || DEFAULT_LOG_FILENAME
248
- }
249
- end
250
-
251
- def exception_control
252
- @_exception_control ||= {
253
- enable: true?(CONFIG.root.agent.ruby.exceptions.capture),
254
- status: CONFIG.root.agent.ruby.exceptions.override_status || 403,
255
- message: CONFIG.root.agent.ruby.exceptions.override_message || Contrast::Utils::ObjectShare::OVERRIDE_MESSAGE
256
- }
257
- end
258
- end
259
-
260
- # Wrapper for the state of our diagnostics settings
261
- module Diagnostic
262
- include Contrast::Components::Interface
263
- access_component :config
264
-
265
- def uninstrument_namespaces
266
- tmp = CONFIG.root.agent.ruby.uninstrument_namespace.map(&:to_sym)
267
- tmp.select! { |sym| Object.cs__const_defined?(sym) }
268
- tmp.map! { |sym| Object.cs__const_get(sym) }
269
- tmp
270
- end
271
- end
272
-
273
- # Wrapper for the state of our logging settings and holder for utility
274
- # methods for logging state.
275
- module Logging
276
- include Contrast::Components::Interface
277
- access_component :config, :logging
278
-
279
- ENV_KEYS = %w[HOME PWD RACK_ENV RAILS_ENV RUBY_VERSION GEM_HOME GEM_PATH].cs__freeze
280
- # Utility method to log some current ruby and rails information from environment
281
- def log_environment
282
- return unless logger.info?
283
-
284
- logger.info('Process environment information',
285
- p_id: Process.pid,
286
- pp_id: Process.ppid,
287
- agent_version: Contrast::Agent::VERSION)
288
- ENV.each do |env_key, env_value|
289
- env_key = env_key.to_s
290
- next unless ENV_KEYS.include?(env_key) ||
291
- (env_key.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER) &&
292
- !env_key.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER + 'API'))
293
-
294
- logger.info('Environment settings', key: env_key, value: env_value)
295
- end
296
- end
297
-
298
- def log_configuration
299
- return unless logger.info?
300
-
301
- loggable = CONFIG.raw.send(:load_config)
302
- loggable.delete('api')
303
- logger.info('Current configuration', configruation: JSON.pretty_generate(loggable))
304
- end
305
-
306
- FRAMEWORKS = %w[rails sinatra grape].cs__freeze
307
- WEB_SERVERS = %w[agoo falcon hoof iodine mongrel mongrel2 passenger puma rack skinny thin trinidad unicorn webrick yarn].cs__freeze
308
- LIBRARIES = %w[excon json mongo moped mysql nokogiri oga ox pg psych sqlite3 typhoeus yaml].cs__freeze
309
- def log_specific_libraries
310
- FRAMEWORKS.each(&cs__method(:log_gem_data))
311
- WEB_SERVERS.each(&cs__method(:log_gem_data))
312
- LIBRARIES.each(&cs__method(:log_gem_data))
313
- end
314
-
315
- def log_all_libraries
316
- return unless logger.debug?
317
-
318
- Gem.loaded_specs.each_pair do |_name, gem_spec|
319
- logger.debug('Gem loaded',
320
- gem_name: gem_spec.name,
321
- gem_version: gem_spec.version.to_s)
322
- end
323
- end
324
-
325
- def log_gem_data gem_name
326
- gem_spec = Gem.loaded_specs[gem_name]
327
- return unless gem_spec
328
-
329
- logger.info('Gem loaded',
330
- gem_name: gem_spec.name,
331
- gem_version: gem_spec.version.to_s)
332
- end
333
- end
334
-
335
- # Methods that query the config.
336
- include ConfigWrappers::Booleans # These check boolean config options.
337
- include ConfigWrappers::Parameters # These check against the config and return parameters.
338
- include SettingWrappers # Methods that query settings from TeamServer.
339
- include AgentState::Operation # Enable/disable the agent, ask if the agent can run.
340
- include AgentState::Service # Agent's client that talks to the service.
341
- include Controls # Syntheses of the above.
342
- include Diagnostic # Misc.
343
- include Logging # Logging.
344
- end
345
- end
346
- end