contrast-agent 6.7.0 → 6.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (280) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/.simplecov +0 -1
  4. data/Rakefile +0 -1
  5. data/ext/cs__assess_array/cs__assess_array.c +41 -10
  6. data/ext/cs__assess_array/cs__assess_array.h +4 -1
  7. data/lib/contrast/agent/assess/policy/trigger_method.rb +3 -3
  8. data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +1 -1
  9. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
  10. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +1 -1
  11. data/lib/contrast/agent/assess/property/evented.rb +11 -11
  12. data/lib/contrast/agent/assess.rb +0 -1
  13. data/lib/contrast/agent/excluder.rb +53 -35
  14. data/lib/contrast/agent/exclusion_matcher.rb +21 -9
  15. data/lib/contrast/agent/middleware.rb +12 -6
  16. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +6 -0
  17. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +146 -127
  18. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +116 -0
  19. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +20 -0
  20. data/lib/contrast/agent/protect/policy/rule_applicator.rb +1 -1
  21. data/lib/contrast/agent/protect/rule/base.rb +47 -55
  22. data/lib/contrast/agent/protect/rule/base_service.rb +48 -24
  23. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker_input_classification.rb +98 -0
  24. data/lib/contrast/agent/protect/rule/bot_blocker.rb +81 -0
  25. data/lib/contrast/agent/protect/rule/cmd_injection.rb +20 -2
  26. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +8 -5
  27. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +22 -22
  28. data/lib/contrast/agent/protect/rule/cmdi/cmdi_chained_command.rb +64 -0
  29. data/lib/contrast/agent/protect/rule/cmdi/cmdi_dangerous_path.rb +63 -0
  30. data/lib/contrast/agent/protect/rule/cmdi/cmdi_input_classification.rb +2 -58
  31. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -1
  32. data/lib/contrast/agent/protect/rule/deserialization.rb +3 -14
  33. data/lib/contrast/agent/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +2 -2
  34. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +0 -11
  35. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli_input_classification.rb +29 -34
  36. data/lib/contrast/agent/protect/rule/no_sqli.rb +25 -18
  37. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_input_classification.rb +61 -0
  38. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_semantic_security_bypass.rb +114 -0
  39. data/lib/contrast/agent/protect/rule/path_traversal.rb +40 -13
  40. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +33 -15
  41. data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +0 -14
  42. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +2 -62
  43. data/lib/contrast/agent/protect/rule/sqli.rb +74 -3
  44. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +39 -63
  45. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +6 -33
  46. data/lib/contrast/agent/protect/rule/xss/reflected_xss_input_classification.rb +58 -0
  47. data/lib/contrast/agent/protect/rule/xss.rb +15 -20
  48. data/lib/contrast/agent/protect/rule/xxe.rb +4 -24
  49. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +19 -40
  50. data/lib/contrast/agent/reporting/attack_result/response_type.rb +9 -9
  51. data/lib/contrast/agent/reporting/details/ip_denylist_details.rb +10 -2
  52. data/lib/contrast/agent/reporting/details/virtual_patch_details.rb +8 -2
  53. data/lib/contrast/agent/reporting/input_analysis/details/bot_blocker_details.rb +27 -0
  54. data/lib/contrast/agent/reporting/input_analysis/details/protect_rule_details.rb +15 -0
  55. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +1 -2
  56. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +16 -2
  57. data/lib/contrast/agent/reporting/masker/masker.rb +2 -0
  58. data/lib/contrast/agent/reporting/report.rb +1 -0
  59. data/lib/contrast/agent/reporting/reporter.rb +35 -14
  60. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +3 -9
  61. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +16 -13
  62. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +12 -7
  63. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +3 -3
  64. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +1 -2
  65. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -1
  66. data/lib/contrast/agent/reporting/reporting_events/application_update.rb +0 -2
  67. data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +0 -1
  68. data/lib/contrast/agent/reporting/reporting_events/finding.rb +6 -6
  69. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +239 -93
  70. data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +10 -23
  71. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +10 -9
  72. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +0 -5
  73. data/lib/contrast/agent/reporting/reporting_events/library_discovery.rb +0 -1
  74. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +12 -0
  75. data/lib/contrast/agent/reporting/reporting_events/poll.rb +1 -11
  76. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +0 -1
  77. data/lib/contrast/agent/reporting/reporting_events/route_discovery_observation.rb +0 -1
  78. data/lib/contrast/agent/reporting/reporting_events/server_reporting_event.rb +8 -0
  79. data/lib/contrast/agent/reporting/reporting_events/server_settings.rb +40 -0
  80. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +2 -2
  81. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +6 -0
  82. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +43 -1
  83. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -4
  84. data/lib/contrast/agent/reporting/reporting_utilities/response.rb +1 -1
  85. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +58 -4
  86. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +4 -6
  87. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +77 -16
  88. data/lib/contrast/agent/reporting/server_settings_worker.rb +44 -0
  89. data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +14 -2
  90. data/lib/contrast/agent/reporting/settings/code_exclusion.rb +6 -1
  91. data/lib/contrast/agent/reporting/settings/exclusion_base.rb +18 -0
  92. data/lib/contrast/agent/reporting/settings/exclusions.rb +2 -1
  93. data/lib/contrast/agent/reporting/settings/helpers.rb +7 -0
  94. data/lib/contrast/agent/reporting/settings/input_exclusion.rb +9 -3
  95. data/lib/contrast/agent/reporting/settings/protect.rb +15 -15
  96. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +39 -2
  97. data/lib/contrast/agent/reporting/settings/rule_definition.rb +3 -0
  98. data/lib/contrast/agent/reporting/settings/security_logger.rb +77 -0
  99. data/lib/contrast/agent/reporting/settings/server_features.rb +9 -0
  100. data/lib/contrast/agent/reporting/settings/syslog.rb +34 -5
  101. data/lib/contrast/agent/request.rb +3 -14
  102. data/lib/contrast/agent/request_context.rb +6 -9
  103. data/lib/contrast/agent/request_context_extend.rb +9 -148
  104. data/lib/contrast/agent/request_handler.rb +5 -10
  105. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +1 -1
  106. data/lib/contrast/agent/thread_watcher.rb +37 -18
  107. data/lib/contrast/agent/version.rb +1 -1
  108. data/lib/contrast/agent.rb +6 -11
  109. data/lib/contrast/agent_lib/api/command_injection.rb +46 -0
  110. data/lib/contrast/agent_lib/api/init.rb +101 -0
  111. data/lib/contrast/agent_lib/api/input_tracing.rb +267 -0
  112. data/lib/contrast/agent_lib/api/method_tempering.rb +29 -0
  113. data/lib/contrast/agent_lib/api/panic.rb +87 -0
  114. data/lib/contrast/agent_lib/api/path_semantic_file_security_bypass.rb +40 -0
  115. data/lib/contrast/agent_lib/interface.rb +260 -0
  116. data/lib/contrast/agent_lib/interface_base.rb +118 -0
  117. data/lib/contrast/agent_lib/return_types/eval_result.rb +44 -0
  118. data/lib/contrast/agent_lib/test.rb +29 -0
  119. data/lib/contrast/api/communication/connection_status.rb +20 -5
  120. data/lib/contrast/components/agent.rb +34 -14
  121. data/lib/contrast/components/api.rb +23 -0
  122. data/lib/contrast/components/app_context.rb +23 -5
  123. data/lib/contrast/components/app_context_extend.rb +0 -25
  124. data/lib/contrast/components/assess.rb +34 -4
  125. data/lib/contrast/components/assess_rules.rb +18 -0
  126. data/lib/contrast/components/base.rb +40 -0
  127. data/lib/contrast/components/config/sources.rb +95 -0
  128. data/lib/contrast/components/config.rb +19 -19
  129. data/lib/contrast/components/heap_dump.rb +10 -0
  130. data/lib/contrast/components/inventory.rb +15 -2
  131. data/lib/contrast/components/logger.rb +18 -0
  132. data/lib/contrast/components/polling.rb +36 -0
  133. data/lib/contrast/components/protect.rb +52 -2
  134. data/lib/contrast/components/ruby_component.rb +16 -1
  135. data/lib/contrast/components/sampling.rb +70 -13
  136. data/lib/contrast/components/security_logger.rb +13 -0
  137. data/lib/contrast/components/settings.rb +105 -90
  138. data/lib/contrast/config/certification_configuration.rb +14 -0
  139. data/lib/contrast/config/config.rb +46 -0
  140. data/lib/contrast/config/diagnostics.rb +114 -0
  141. data/lib/contrast/config/diagnostics_tools.rb +98 -0
  142. data/lib/contrast/config/effective_config.rb +65 -0
  143. data/lib/contrast/config/effective_config_value.rb +32 -0
  144. data/lib/contrast/config/exception_configuration.rb +12 -0
  145. data/lib/contrast/config/protect_rule_configuration.rb +8 -8
  146. data/lib/contrast/config/protect_rules_configuration.rb +23 -60
  147. data/lib/contrast/config/request_audit_configuration.rb +13 -0
  148. data/lib/contrast/config/server_configuration.rb +41 -2
  149. data/lib/contrast/configuration.rb +29 -12
  150. data/lib/contrast/extension/assess/array.rb +9 -0
  151. data/lib/contrast/extension/assess/erb.rb +1 -1
  152. data/lib/contrast/extension/delegator.rb +2 -0
  153. data/lib/contrast/framework/manager.rb +3 -1
  154. data/lib/contrast/framework/rails/railtie.rb +0 -1
  155. data/lib/contrast/framework/rails/support.rb +0 -1
  156. data/lib/contrast/tasks/config.rb +1 -8
  157. data/lib/contrast/utils/assess/event_limit_utils.rb +31 -9
  158. data/lib/contrast/utils/assess/trigger_method_utils.rb +5 -4
  159. data/lib/contrast/utils/duck_utils.rb +1 -0
  160. data/lib/contrast/utils/hash_digest.rb +2 -2
  161. data/lib/contrast/utils/input_classification_base.rb +155 -0
  162. data/lib/contrast/utils/os.rb +0 -20
  163. data/lib/contrast/utils/reporting/application_activity_batch_utils.rb +81 -0
  164. data/lib/contrast/utils/response_utils.rb +0 -16
  165. data/lib/contrast/utils/routes_sent.rb +60 -0
  166. data/lib/contrast/utils/stack_trace_utils.rb +3 -15
  167. data/lib/contrast/utils/string_utils.rb +10 -7
  168. data/lib/contrast/utils/telemetry_client.rb +1 -2
  169. data/lib/contrast/utils/timer.rb +16 -0
  170. data/lib/contrast.rb +5 -4
  171. data/resources/protect/policy.json +1 -2
  172. data/ruby-agent.gemspec +7 -6
  173. metadata +69 -130
  174. data/exe/contrast_service +0 -23
  175. data/lib/contrast/agent/assess/contrast_event.rb +0 -157
  176. data/lib/contrast/agent/assess/events/event_factory.rb +0 -34
  177. data/lib/contrast/agent/assess/events/source_event.rb +0 -46
  178. data/lib/contrast/agent/protect/rule/cmdi/cmdi_worth_watching.rb +0 -64
  179. data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +0 -118
  180. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_matcher.rb +0 -45
  181. data/lib/contrast/agent/reaction_processor.rb +0 -47
  182. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +0 -36
  183. data/lib/contrast/agent/service_heartbeat.rb +0 -35
  184. data/lib/contrast/api/communication/messaging_queue.rb +0 -128
  185. data/lib/contrast/api/communication/response_processor.rb +0 -90
  186. data/lib/contrast/api/communication/service_lifecycle.rb +0 -77
  187. data/lib/contrast/api/communication/socket.rb +0 -44
  188. data/lib/contrast/api/communication/socket_client.rb +0 -130
  189. data/lib/contrast/api/communication/speedracer.rb +0 -138
  190. data/lib/contrast/api/communication/tcp_socket.rb +0 -32
  191. data/lib/contrast/api/communication/unix_socket.rb +0 -28
  192. data/lib/contrast/api/communication.rb +0 -20
  193. data/lib/contrast/api/decorators/address.rb +0 -59
  194. data/lib/contrast/api/decorators/agent_startup.rb +0 -56
  195. data/lib/contrast/api/decorators/application_settings.rb +0 -43
  196. data/lib/contrast/api/decorators/application_startup.rb +0 -56
  197. data/lib/contrast/api/decorators/bot_blocker.rb +0 -37
  198. data/lib/contrast/api/decorators/http_request.rb +0 -137
  199. data/lib/contrast/api/decorators/input_analysis.rb +0 -18
  200. data/lib/contrast/api/decorators/instrumentation_mode.rb +0 -35
  201. data/lib/contrast/api/decorators/ip_denylist.rb +0 -37
  202. data/lib/contrast/api/decorators/message.rb +0 -67
  203. data/lib/contrast/api/decorators/rasp_rule_sample.rb +0 -52
  204. data/lib/contrast/api/decorators/response_type.rb +0 -17
  205. data/lib/contrast/api/decorators/server_features.rb +0 -25
  206. data/lib/contrast/api/decorators/user_input.rb +0 -51
  207. data/lib/contrast/api/decorators/virtual_patch.rb +0 -34
  208. data/lib/contrast/api/decorators.rb +0 -22
  209. data/lib/contrast/api/dtm.pb.rb +0 -363
  210. data/lib/contrast/api/settings.pb.rb +0 -500
  211. data/lib/contrast/api.rb +0 -16
  212. data/lib/contrast/components/contrast_service.rb +0 -88
  213. data/lib/contrast/components/service.rb +0 -55
  214. data/lib/contrast/tasks/service.rb +0 -84
  215. data/lib/contrast/utils/input_classification.rb +0 -73
  216. data/lib/protobuf/code_generator.rb +0 -129
  217. data/lib/protobuf/decoder.rb +0 -28
  218. data/lib/protobuf/deprecation.rb +0 -117
  219. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +0 -79
  220. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +0 -360
  221. data/lib/protobuf/descriptors.rb +0 -3
  222. data/lib/protobuf/encoder.rb +0 -11
  223. data/lib/protobuf/enum.rb +0 -365
  224. data/lib/protobuf/exceptions.rb +0 -9
  225. data/lib/protobuf/field/base_field.rb +0 -380
  226. data/lib/protobuf/field/base_field_object_definitions.rb +0 -504
  227. data/lib/protobuf/field/bool_field.rb +0 -64
  228. data/lib/protobuf/field/bytes_field.rb +0 -67
  229. data/lib/protobuf/field/double_field.rb +0 -25
  230. data/lib/protobuf/field/enum_field.rb +0 -56
  231. data/lib/protobuf/field/field_array.rb +0 -102
  232. data/lib/protobuf/field/field_hash.rb +0 -122
  233. data/lib/protobuf/field/fixed32_field.rb +0 -25
  234. data/lib/protobuf/field/fixed64_field.rb +0 -28
  235. data/lib/protobuf/field/float_field.rb +0 -43
  236. data/lib/protobuf/field/int32_field.rb +0 -21
  237. data/lib/protobuf/field/int64_field.rb +0 -34
  238. data/lib/protobuf/field/integer_field.rb +0 -23
  239. data/lib/protobuf/field/message_field.rb +0 -51
  240. data/lib/protobuf/field/sfixed32_field.rb +0 -27
  241. data/lib/protobuf/field/sfixed64_field.rb +0 -28
  242. data/lib/protobuf/field/signed_integer_field.rb +0 -29
  243. data/lib/protobuf/field/sint32_field.rb +0 -21
  244. data/lib/protobuf/field/sint64_field.rb +0 -21
  245. data/lib/protobuf/field/string_field.rb +0 -51
  246. data/lib/protobuf/field/uint32_field.rb +0 -21
  247. data/lib/protobuf/field/uint64_field.rb +0 -21
  248. data/lib/protobuf/field/varint_field.rb +0 -77
  249. data/lib/protobuf/field.rb +0 -74
  250. data/lib/protobuf/generators/base.rb +0 -85
  251. data/lib/protobuf/generators/enum_generator.rb +0 -39
  252. data/lib/protobuf/generators/extension_generator.rb +0 -27
  253. data/lib/protobuf/generators/field_generator.rb +0 -193
  254. data/lib/protobuf/generators/file_generator.rb +0 -262
  255. data/lib/protobuf/generators/group_generator.rb +0 -122
  256. data/lib/protobuf/generators/message_generator.rb +0 -104
  257. data/lib/protobuf/generators/option_generator.rb +0 -17
  258. data/lib/protobuf/generators/printable.rb +0 -160
  259. data/lib/protobuf/generators/service_generator.rb +0 -50
  260. data/lib/protobuf/lifecycle.rb +0 -33
  261. data/lib/protobuf/logging.rb +0 -39
  262. data/lib/protobuf/message/fields.rb +0 -233
  263. data/lib/protobuf/message/serialization.rb +0 -85
  264. data/lib/protobuf/message.rb +0 -241
  265. data/lib/protobuf/optionable.rb +0 -72
  266. data/lib/protobuf/tasks/compile.rake +0 -80
  267. data/lib/protobuf/tasks.rb +0 -1
  268. data/lib/protobuf/varint.rb +0 -20
  269. data/lib/protobuf/varint_pure.rb +0 -31
  270. data/lib/protobuf/version.rb +0 -3
  271. data/lib/protobuf/wire_type.rb +0 -10
  272. data/lib/protobuf.rb +0 -91
  273. data/proto/dynamic_discovery.proto +0 -46
  274. data/proto/google/protobuf/compiler/plugin.proto +0 -183
  275. data/proto/google/protobuf/descriptor.proto +0 -911
  276. data/proto/rpc.proto +0 -71
  277. data/service_executables/.gitkeep +0 -0
  278. data/service_executables/VERSION +0 -1
  279. data/service_executables/linux/contrast-service +0 -0
  280. data/service_executables/mac/contrast-service +0 -0
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/reporting/reporting_events/route_discovery_observation'
5
- require 'contrast/api/dtm.pb'
6
5
  require 'contrast/components/logger'
7
6
 
8
7
  module Contrast
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'contrast/api/dtm.pb'
5
4
  require 'contrast/utils/string_utils'
6
5
  require 'contrast/components/logger'
7
6
 
@@ -21,6 +21,14 @@ module Contrast
21
21
  def since_last_update
22
22
  (update_time = Contrast::SETTINGS.last_server_update_ms) ? Contrast::Utils::Timer.now_ms - update_time : 0
23
23
  end
24
+
25
+ # Human readable last time update for header set. Set to 0 if the agent is just starting and have not received
26
+ # the latest header from TS.
27
+ #
28
+ # @return [String]
29
+ def since_last_update_httpdate
30
+ Contrast::SETTINGS.server_settings_last_httpdate || Contrast::Utils::Timer.ms_to_httpdate(0)
31
+ end
24
32
  end
25
33
  end
26
34
  end
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/agent/reporting/reporting_events/server_reporting_event'
5
+ require 'contrast/agent/reporting/reporting_utilities/endpoints'
6
+ require 'contrast/utils/timer'
7
+
8
+ module Contrast
9
+ module Agent
10
+ module Reporting
11
+ # This class will initialize a GET request to be send to TS. The server settings endpoint is the way the Agent
12
+ # receives server sittings - Protect rules settings, patterns, keywords and deny/allow lists, log setting.
13
+ class ServerSettings < Contrast::Agent::Reporting::ServerReportingEvent
14
+ def initialize
15
+ @event_method = :GET
16
+ @event_endpoint = Contrast::Agent::Reporting::Endpoints.server_settings
17
+ super
18
+ end
19
+
20
+ def file_name
21
+ 'server-settings'
22
+ end
23
+
24
+ # Attach the last server settings received timestamp to the request as it is required.
25
+ #
26
+ # If-Modified-Since: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
27
+ # @param request [Net::HTTPRequest]
28
+ def attach_headers request
29
+ request['If-Modified-Since'] = since_last_update_httpdate
30
+ end
31
+
32
+ # @return [Hash]
33
+ # @raise [ArgumentError]
34
+ def to_controlled_hash
35
+ {}
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -19,8 +19,8 @@ module Contrast
19
19
  generate_paths if enabled?
20
20
  end
21
21
 
22
- # This method will be handling the auditing of the requests and responses we send to SpeedRacer. If the audit
23
- # feature is enabled, we'll log to file the request and/or response protobuf objects.
22
+ # This method will be handling the auditing of the requests and responses we send to TeamServer. If the audit
23
+ # feature is enabled, we'll log to file the request and/or response JSON objects.
24
24
  #
25
25
  # @param event [Contrast::Agent::Reporting::ReportingEvent] One of the DTMs valid for the
26
26
  # event field of Contrast::Agent::Reporting::ReportingEvent
@@ -70,6 +70,12 @@ module Contrast
70
70
  end
71
71
  end
72
72
 
73
+ def server_settings
74
+ with_rescue do
75
+ "#{ server_endpoint }/settings".cs__freeze
76
+ end
77
+ end
78
+
73
79
  # @return [String,nil]
74
80
  def trace_observed
75
81
  with_rescue do
@@ -10,6 +10,8 @@ require 'contrast/agent/reporting/reporting_utilities/response_handler'
10
10
  require 'contrast/agent/reporting/reporting_utilities/reporter_client_utils'
11
11
  require 'contrast/agent/reporting/reporting_utilities/endpoints'
12
12
  require 'contrast/agent/reporting/reporting_utilities/headers'
13
+ require 'contrast/agent/reporting/reporting_events/server_settings'
14
+ require 'contrast/config/diagnostics'
13
15
 
14
16
  module Contrast
15
17
  module Agent
@@ -21,8 +23,10 @@ module Contrast
21
23
 
22
24
  include Contrast::Agent::Reporting::Endpoints
23
25
  include Contrast::Agent::Reporting::ReporterClientUtils
26
+ include Contrast::Agent::Reporting::ResponseHandlerUtils
24
27
  include Contrast::Components::Logger::InstanceMethods
25
28
  SERVICE_NAME = 'Reporter'
29
+ REPORT_CONFIG_WHEN = %w[200 304].cs__freeze
26
30
  def initialize
27
31
  @headers = Contrast::Agent::Reporting::Headers.new
28
32
  super()
@@ -61,13 +65,47 @@ module Contrast
61
65
  request = build_request(event)
62
66
  response = connection.request(request)
63
67
  audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable
64
- process_settings_response(response)
68
+ process_settings_response(response, event)
69
+ record_status(response, event)
70
+ record_configuration(response, event)
65
71
  process_preflight_response(event, response, connection)
66
72
  response
67
73
  rescue StandardError => e
68
74
  handle_error(event, e)
69
75
  end
70
76
 
77
+ # This is going to populate the config status value in the diagnostics json
78
+ #
79
+ # @param response [Contrast::Agent::Reporting::Response, nil]
80
+ # @param event [Contrast::Agent::Reporting::ReportingEvent]
81
+ def record_status response, event
82
+ return unless response
83
+ return unless event&.cs__class == Contrast::Agent::Reporting::AgentStartup ||
84
+ event&.cs__class == Contrast::Agent::Reporting::ApplicationStartup
85
+
86
+ diagnostics.config.determine_config_status(response)
87
+ nil
88
+ end
89
+
90
+ # Write effective config to file:
91
+ # If we are here the create and server messages are sent and the code received is
92
+ # 200 or 304. In case of 304 there will be no new settings and we can write current ones.
93
+ # This is done on every settings request.
94
+ #
95
+ # @param response [Contrast::Agent::Reporting::Response, nil]
96
+ # @param event [Contrast::Agent::Reporting::ReportingEvent]
97
+ def record_configuration response, event
98
+ return unless response
99
+ return unless REPORT_CONFIG_WHEN.include?(response_handler.last_response_code)
100
+ return unless event&.cs__class == Contrast::Agent::Reporting::ServerSettings ||
101
+ event&.cs__class == Contrast::Agent::Reporting::AgentStartup ||
102
+ event&.cs__class == Contrast::Agent::Reporting::ApplicationStartup
103
+
104
+ logger.info('[Reporter Diagnostics] last response code:', response_code: response_handler.last_response_code)
105
+ diagnostics.write_to_file
106
+ status.server_response_success!
107
+ end
108
+
71
109
  def status
72
110
  @_status ||= Contrast::Api::Communication::ConnectionStatus.new
73
111
  end
@@ -76,6 +114,10 @@ module Contrast
76
114
  @_response_handler ||= Contrast::Agent::Reporting::ResponseHandler.new
77
115
  end
78
116
 
117
+ def diagnostics
118
+ @_diagnostics ||= Contrast::Agent::DiagnosticsConfig::Diagnostics.new(Contrast::LOGGER.path)
119
+ end
120
+
79
121
  def sleep?
80
122
  response_handler.sleep?
81
123
  end
@@ -84,10 +84,12 @@ module Contrast
84
84
 
85
85
  # Handles response processing and sets status
86
86
  #
87
+ # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
87
88
  # @param response [Net::HTTP::Response]
88
- def process_settings_response response
89
- response_handler.process(response)
90
- status.success!
89
+ def process_settings_response response, event
90
+ res = response_handler.process(response, event)
91
+ status.success! if res
92
+ res
91
93
  end
92
94
 
93
95
  # Given a response from preflght, when the finding hash is desired, then send the finding to which it pertains.
@@ -100,7 +102,7 @@ module Contrast
100
102
  # @param connection [Net::HTTP] open connection
101
103
  def process_preflight_response event, response, connection
102
104
  return unless event.cs__is_a?(Contrast::Agent::Reporting::Preflight)
103
- return unless response && connection
105
+ return unless response&.body && connection
104
106
 
105
107
  findings_to_return = response.body.split(',').delete_if { |el| el.include?('*') }
106
108
  findings_to_return.each do |index|
@@ -125,6 +127,8 @@ module Contrast
125
127
  request = case event.event_method
126
128
  when :PUT
127
129
  Net::HTTP::Put.new(event.event_endpoint)
130
+ when :GET
131
+ Net::HTTP::Get.new(event.event_endpoint)
128
132
  else # :POST
129
133
  Net::HTTP::Post.new(event.event_endpoint)
130
134
  end
@@ -19,7 +19,7 @@ module Contrast
19
19
 
20
20
  # All of the feature server_features
21
21
  #
22
- # @return [Contrast::Agent::Reporting::Settings::FeatureSettings, nil]
22
+ # @return [Contrast::Agent::Reporting::Settings::ServerFeatures, nil]
23
23
  attr_accessor :server_features
24
24
 
25
25
  # Success boolean message value
@@ -49,13 +49,20 @@ module Contrast
49
49
  res.reactions = response_data[:reactions] if response_data[:features]
50
50
  end
51
51
 
52
+ # This method is universal and used for both ng endpoing of feature settings and the new
53
+ # Server settings endpoint. Used in both build_feature_settings and build_server_settings.
54
+ # Passing the ng_ flag determines the use of the endpoint and response used because some of
55
+ # the response fields are with different names or do not exist: [enabled vs enable]
56
+ #
52
57
  # @param response_data [Hash]
53
58
  # @param res [Contrast::Agent::Reporting::Response]
54
- def extract_assess_server_features response_data, res
55
- assess = response_data[:features][:assessment]
59
+ # @param ng_ [Boolean]
60
+ def extract_assess_settings response_data, res, ng_: true
61
+ assess = ng_ ? response_data[:features][:assessment] : response_data[:assess]
56
62
  return unless assess
57
63
 
58
- res.server_features.assess.enabled = assess[:enabled]
64
+ res.server_features.assess.enabled = ng_ ? assess[:enabled] : assess[:enable]
65
+ res.server_features.assess.report_stacktraces = assess[:report_stacktraces] unless ng_
59
66
  res.server_features.assess.sampling = assess[:sampling]
60
67
  res.server_features.assess.sanitizers = assess[:sanitizers]
61
68
  res.server_features.assess.validators = assess[:validators]
@@ -78,7 +85,7 @@ module Contrast
78
85
  def extract_syslog response_data, res
79
86
  return unless (syslog = response_data[:features][:defend][:syslog])
80
87
 
81
- res.server_features.protect.syslog.assign_array(syslog)
88
+ res.server_features.protect.syslog.assign_array(syslog, ng_: true)
82
89
  end
83
90
 
84
91
  # @param response_data [Hash]
@@ -116,6 +123,53 @@ module Contrast
116
123
  res.server_features.log_level = log_level
117
124
  res.server_features.log_file = response_data[:logFile] if response_data[:logFile]
118
125
  end
126
+
127
+ # This method is used with ServerSettings as we expect to have data for
128
+ # all the loggers - security_logger, logger, syslog.
129
+ #
130
+ # @param response_data [Hash]
131
+ # @param res [Contrast::Agent::Reporting::Response]
132
+ def extract_loggers response_data, res
133
+ logger = response_data[:logger]
134
+ security_logger = response_data[:security_logger]
135
+
136
+ if logger
137
+ res.server_features.log_level = logger[:level]
138
+ res.server_features.log_file = logger[:path]
139
+ end
140
+ return unless security_logger
141
+
142
+ log_level = security_logger[:level]
143
+ log_file = security_logger[:path]
144
+ res.server_features.security_logger.log_level = log_level
145
+ res.server_features.security_logger.log_file = log_file
146
+ res.server_features.security_logger.not_blank!
147
+ res.server_features.security_logger.syslog.assign_array(response_data[:security_logger][:syslog], ng_: false)
148
+ end
149
+
150
+ # @param response_data [Hash]
151
+ # @param res [Contrast::Agent::Reporting::Response]
152
+ def extract_protect_server_settings response_data, res
153
+ protect = response_data[:protect]
154
+ return unless protect
155
+
156
+ res.server_features.protect.enabled = protect[:enable]
157
+ res.server_features.protect.observability = protect[:observability]
158
+ res.server_features.protect.log_enhancers = protect[:log_enhancers]
159
+ update_protect_rules(protect, res)
160
+ end
161
+
162
+ # @param protect [Hash] response data
163
+ # @param res [Contrast::Agent::Reporting::Response]
164
+ def update_protect_rules protect, res
165
+ return unless (rules = protect[:rules])
166
+
167
+ res.server_features.protect.ip_allowlist = rules[:ip_allowlist]
168
+ res.server_features.protect.ip_denylist = rules[:ip_denylist]
169
+ res.server_features.protect.bot_blocker.enable = rules[:bot_blocker][:enable]
170
+ res.server_features.protect.bot_blocker.bots = rules[:bot_blocker][:bots]
171
+ res.server_features.protect.rules_to_definition_list(rules)
172
+ end
119
173
  end
120
174
  end
121
175
  end
@@ -1,12 +1,9 @@
1
1
  # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'contrast/api/communication/response_processor'
5
- require 'contrast/api/decorators/application_settings'
6
4
  require 'contrast/agent/reporting/reporting_utilities/response'
7
5
  require 'contrast/agent/reporting/reporting_utilities/response_handler_utils'
8
6
  require 'contrast/agent/reporting/reporting_utilities/response_handler_mode'
9
- require 'contrast/api/decorators/server_features'
10
7
  require 'contrast/components/logger'
11
8
  require 'json'
12
9
 
@@ -23,13 +20,14 @@ module Contrast
23
20
  # Process the response from TS
24
21
  #
25
22
  # @param response [Net::HTTP::Response, nil]
23
+ # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
26
24
  # @return response [Net::HTTP::Response, nil]
27
- def process response
25
+ def process response, event
28
26
  logger.debug('Reporter Received a response')
29
27
  return unless analyze_response?(response)
30
28
 
31
29
  # Handle the response body and obtain server_features or app_settings
32
- report_response = convert_response(response)
30
+ report_response = convert_response(response, event)
33
31
  return unless report_response
34
32
 
35
33
  # Update Server Features and Application Settings to provide current agent settings
@@ -90,7 +88,7 @@ module Contrast
90
88
  when ERROR_CODES[:too_many_requests]
91
89
  handle_response_errors(response, RETRY_AFTER_MSG, mode.resending)
92
90
  else
93
- logger.error('Response Error code could not be processed')
91
+ logger.error('Unable to execute agent post_call')
94
92
  end
95
93
  end
96
94
 
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/reporting/reporting_utilities/response_extractor'
5
+ require 'contrast/agent/disable_reaction'
5
6
 
6
7
  module Contrast
7
8
  module Agent
@@ -31,6 +32,17 @@ module Contrast
31
32
  UNPROCESSABLE_ENTITY_MSG = 'Reporter received Unprocessable Entity response. Disabling permanently.'
32
33
  RETRY_AFTER_MSG = "There are too many requests of this type being sent by this Agent. #{ SUSPEND_MSG }"
33
34
 
35
+ def last_response_code
36
+ @_last_response_code ||= ''
37
+ end
38
+
39
+ # String format of the Header:<day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
40
+ #
41
+ # @return [String]
42
+ def last_server_modified
43
+ @_last_server_modified
44
+ end
45
+
34
46
  private
35
47
 
36
48
  # check if response code is valid before analyze it
@@ -78,6 +90,11 @@ module Contrast
78
90
  # in the standard format per RFC 2616
79
91
  # used for in observed routes message.
80
92
  return false unless response && (response_code = response&.code)
93
+
94
+ # We still need to check the response code even if we are not analyzing it, since the 304 code does not
95
+ # contain settings to be extracted but we still need to know for the diagnostics. Do not move this bellow
96
+ # the ANALYZE_WHEN check.
97
+ @_last_response_code = response_code
81
98
  return true if ANALYZE_WHEN.include?(response_code)
82
99
 
83
100
  handle_error(response) if ERROR_CODES.value?(response_code)
@@ -109,7 +126,7 @@ module Contrast
109
126
 
110
127
  stop_reporting(message, application: Contrast::APP_CONTEXT.name, error_message: error_message) # rubocop:disable Security/Module/Name
111
128
  rescue StandardError => e
112
- logger.debug('Could not handle Response error information', error: e)
129
+ logger.debug('Could not handle Response error information', error: e, backtrace: e.backtrace)
113
130
  end
114
131
 
115
132
  # Extract what we've received.
@@ -126,6 +143,19 @@ module Contrast
126
143
  [ready_after.to_i, error_message, auth_error]
127
144
  end
128
145
 
146
+ # Extract Last-Modified header from ServerSettings response.
147
+ # The new GET server settings endpoint have different payload.
148
+ # Extract the last modify headers with last update form TS.
149
+ #
150
+ # @param response [Net::HTTP::Response, nil]
151
+ # @return last_modified[integer, nil] Time since last server update
152
+ def extract_response_last_modified response
153
+ return unless response.cs__is_a?(Net::HTTPResponse)
154
+
155
+ header = response['Last-Modified'] if response&.to_hash&.keys&.map(&:downcase)&.include?('last-modified')
156
+ @_last_server_modified = header if header
157
+ end
158
+
129
159
  # Cease reporting about this application
130
160
  #
131
161
  # @param message [String] Message to log
@@ -193,27 +223,32 @@ module Contrast
193
223
  # Converts response from Net to Reporting Response object
194
224
  #
195
225
  # @param response [Net::HTTP::Response, nil]
226
+ # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
196
227
  # @return response [Contrast::Agent::Reporting::Response]
197
- def convert_response response
228
+ def convert_response response, event
198
229
  response_body = response&.body
199
230
  return unless response_body
200
231
 
201
232
  response_data = JSON.parse(response_body, symbolize_names: true)
202
233
  return unless response_data.cs__is_a?(Hash)
203
234
 
204
- populate_response(response_data)
235
+ extract_response_last_modified(response)
236
+ populate_response(response_data, event)
205
237
  rescue StandardError => e
206
238
  logger.error('Unable to convert response', e)
207
239
  nil
208
240
  end
209
241
 
210
- # Extracts the data from the response and coverts it to
211
- # Contrast::Agent::Reporting::Response.
242
+ # Extracts the data from the response and coverts it to Contrast::Agent::Reporting::Response.
243
+ # The response is being checked for it's type and settings received so the extractor methods
244
+ # are invoked accordingly to the response type.
212
245
  #
213
246
  # @param response_data[Hash]
214
- # @return response [Contrast::Agent::Reporting::Response]
215
- def populate_response response_data
216
- return unless (success, messages = extract_success(response_data))
247
+ # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
248
+ # this is used to check the expected response type for this event.
249
+ # @return response [Contrast::Agent::Reporting::Response, nil]
250
+ def populate_response response_data, event
251
+ return unless (success, messages = extract_success(response_data, event))
217
252
 
218
253
  # check if response contains application settings or Feature settings
219
254
  if response_data[:settings]
@@ -225,17 +260,23 @@ module Contrast
225
260
  logger.trace('Agent: Received updated application settings', raw: response_data, processed: app_settings)
226
261
  app_settings
227
262
  else
228
- # the response contains FeatureSettings
263
+ # the response contains FeatureSettings. The ng endpoint data feature
229
264
  response = Contrast::Agent::Reporting::Response.build_server_response
230
265
  response.success = success
231
266
  response.messages = messages
232
- server_features = build_feature_settings(response_data, response)
233
- logger.trace('Agent: Received updated application settings', raw: response_data, processed: server_features)
267
+ server_features = if event.cs__is_a?(Contrast::Agent::Reporting::ServerSettings)
268
+ # do the new extraction.
269
+ build_server_settings(response_data, response)
270
+ else
271
+ build_feature_settings(response_data, response)
272
+ end
273
+ logger.trace('Agent: Received updated feature settings', raw: response_data, processed: server_features)
234
274
  server_features
235
275
  end
236
- response
237
276
  end
238
277
 
278
+ # This method is used with the ng endpoint.
279
+ #
239
280
  # @param response_data [Hash]
240
281
  # @return res [Contrast::Agent::Reporting::Response]
241
282
  def build_application_settings response_data, response
@@ -247,11 +288,13 @@ module Contrast
247
288
  response
248
289
  end
249
290
 
291
+ # This method is used with the ng startup endpoint.
292
+ #
250
293
  # @param response_data [Hash]
251
294
  # @return res [Contrast::Agent::Reporting::Response]
252
295
  def build_feature_settings response_data, response
253
296
  extract_reactions(response_data, response)
254
- extract_assess_server_features(response_data, response)
297
+ extract_assess_settings(response_data, response)
255
298
  extract_protect_server_features(response_data, response)
256
299
  extract_protect_lists(response_data, response)
257
300
  extract_log_settings(response_data, response)
@@ -259,15 +302,33 @@ module Contrast
259
302
  response
260
303
  end
261
304
 
305
+ # This method is used with the server settings endpoint.
306
+ #
307
+ # @param response_data [Hash]
308
+ # @return res [Contrast::Agent::Reporting::Response]
309
+ def build_server_settings response_data, response
310
+ extract_loggers(response_data, response)
311
+ extract_protect_server_settings(response_data, response)
312
+ extract_assess_settings(response_data, response, ng_: false)
313
+ response.server_features.telemetry = response_data[:telemetry][:enable]
314
+ response
315
+ end
316
+
262
317
  # This method with check the success and messages field of the response.
263
318
  # If the success is false, then it will return nil and log the error.
264
319
  #
265
320
  # @param response_data [Hash]
266
321
  # @return [Array, nil] Returns the success status or nil if request
267
322
  # was not processed by TS.
268
- def extract_success response_data
269
- success = response_data[:success]
270
- messages = response_data[:messages]
323
+ def extract_success response_data, event
324
+ if event.cs__is_a?(Contrast::Agent::Reporting::ServerSettings)
325
+ # we don't those in the body but we can count on the response code.
326
+ success = @_last_response_code == '200' ? true : nil
327
+ messages = @_last_response_code == '200' ? ['success'] : nil
328
+ else
329
+ success = response_data[:success]
330
+ messages = response_data[:messages]
331
+ end
271
332
  return [success, messages] if success
272
333
 
273
334
  logger.error('Unable to connect to Contrast UI') if messages.nil?
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/agent/worker_thread'
5
+ require 'contrast/agent/reporting/report'
6
+
7
+ module Contrast
8
+ module Agent
9
+ # The ServerSettingsWorker will send request on interval, to make sure the Agent gets the settings it
10
+ # need to operate, from TS. This Thead should be started after the AgentStartup is complete.
11
+ class ServerSettingsWorker < WorkerThread
12
+ RESEND_INTERVAL_MS = 60_000.cs__freeze
13
+
14
+ def start_thread!
15
+ return if running?
16
+
17
+ @_thread = Contrast::Agent::Thread.new do
18
+ logger.info('Starting Server Settings Worker thread.', sending_interval: server_settings_resend_ms)
19
+ loop do
20
+ logger.info('Fetching Settings', sending_interval: server_settings_resend_ms)
21
+ Contrast::Agent.reporter&.send_event(settings_message)
22
+ sleep(server_settings_resend_ms / 1000)
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ # Polling messages for this thread. Including Server settings:
30
+ #
31
+ # @return [Contrast::Agent::Reporting::ReportingEvent]
32
+ def settings_message
33
+ @_settings_message ||= Contrast::Agent::Reporting::ServerSettings.new
34
+ end
35
+
36
+ # Get the value from settings or use the default one.
37
+ #
38
+ # @return resend_ms [Integer] time to resend the message
39
+ def server_settings_resend_ms
40
+ @_server_settings_resend_ms ||= Contrast::AGENT.polling.server_settings_ms&.to_i || RESEND_INTERVAL_MS
41
+ end
42
+ end
43
+ end
44
+ end
@@ -15,6 +15,7 @@ module Contrast
15
15
  # Application level settings for the Assess featureset.
16
16
  # Used for the FeatureSet TS response
17
17
  class AssessServerFeature
18
+ REPORT_STACKTRACES = %w[ALL SOME NONE].cs__freeze
18
19
  # Indicate if the assess feature set is enabled for this server or not.
19
20
  #
20
21
  # @return enabled [Boolean]
@@ -27,7 +28,17 @@ module Contrast
27
28
  # @param enabled [Boolean]
28
29
  # @return enabled [Boolean]
29
30
  def enabled= enabled
30
- @_enabled = enabled if !!enabled == enabled
31
+ @_enabled = enabled
32
+ end
33
+
34
+ # @return [String] ALL, SOME, NONE
35
+ def report_stacktraces
36
+ @_report_stacktraces
37
+ end
38
+
39
+ # @return [String] ALL, SOME, NONE
40
+ def report_stacktraces= level
41
+ @_report_stacktraces = level if REPORT_STACKTRACES.include?(level)
31
42
  end
32
43
 
33
44
  # Used to control the sampling feature in the agent.
@@ -91,9 +102,10 @@ module Contrast
91
102
  {
92
103
  enabled: enabled?,
93
104
  sampling: sampling.to_controlled_hash,
105
+ report_stacktraces: report_stacktraces, # used with ServerSettings only
94
106
  sanitizers: sanitizers.map(&:to_controlled_hash),
95
107
  validators: validators.map(&:to_controlled_hash)
96
- }
108
+ }.compact
97
109
  end
98
110
  end
99
111
  end
@@ -12,9 +12,14 @@ module Contrast
12
12
  ATTRIBUTES = BASE_ATTRIBUTES.dup << :denylist
13
13
  ATTRIBUTES.cs__freeze
14
14
 
15
- # @return denylist [Array<String>] #rubocop:disable [Naming/InclusiveLanguage]
15
+ # @return [Array<String>]
16
16
  attr_accessor :denylist
17
17
 
18
+ def initialize
19
+ super
20
+ @denylist = []
21
+ end
22
+
18
23
  def to_controlled_hash
19
24
  hash = super
20
25
  hash[:denylist] = denylist
@@ -18,6 +18,24 @@ module Contrast
18
18
  # @return protect_rules [Array<String>]
19
19
  attr_accessor :protect_rules
20
20
 
21
+ def initialize
22
+ @modes = []
23
+ @assess_rules = []
24
+ @protect_rules = []
25
+ end
26
+
27
+ # @return [Boolean] does this exclusion apply to Assess or not
28
+ def assess
29
+ @_assess = modes&.include?('assess') if @_assess.nil?
30
+ @_assess
31
+ end
32
+
33
+ # @return [Boolean] does this exclusion apply to Protect or not
34
+ def protect
35
+ @_protect = modes&.include?('defend') if @_protect.nil?
36
+ @_protect
37
+ end
38
+
21
39
  def to_controlled_hash
22
40
  {
23
41
  name: name, # rubocop:disable Security/Module/Name