contrast-agent 4.9.1 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rspec_parallel +6 -0
  4. data/ext/cs__contrast_patch/cs__contrast_patch.c +0 -1
  5. data/ext/cs__contrast_patch/cs__contrast_patch.h +0 -2
  6. data/lib/contrast/agent/assess/contrast_event.rb +0 -1
  7. data/lib/contrast/agent/assess/finalizers/hash.rb +0 -1
  8. data/lib/contrast/agent/assess/policy/patcher.rb +0 -1
  9. data/lib/contrast/agent/assess/policy/policy_scanner.rb +0 -2
  10. data/lib/contrast/agent/assess/policy/preshift.rb +8 -5
  11. data/lib/contrast/agent/assess/policy/propagation_method.rb +100 -57
  12. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +0 -2
  13. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
  14. data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -2
  15. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -0
  16. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +0 -1
  17. data/lib/contrast/agent/assess/policy/source_method.rb +13 -17
  18. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -1
  19. data/lib/contrast/agent/assess/policy/trigger_method.rb +59 -83
  20. data/lib/contrast/agent/assess/property/evented.rb +2 -1
  21. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +0 -1
  22. data/lib/contrast/agent/disable_reaction.rb +1 -1
  23. data/lib/contrast/agent/exclusion_matcher.rb +0 -4
  24. data/lib/contrast/agent/inventory/database_config.rb +117 -0
  25. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +5 -4
  26. data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
  27. data/lib/contrast/agent/middleware.rb +1 -0
  28. data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -0
  29. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +18 -12
  30. data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
  31. data/lib/contrast/agent/patching/policy/patch.rb +5 -0
  32. data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
  33. data/lib/contrast/agent/patching/policy/patcher.rb +8 -8
  34. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
  35. data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
  36. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
  37. data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
  38. data/lib/contrast/agent/reaction_processor.rb +1 -1
  39. data/lib/contrast/agent/request.rb +5 -2
  40. data/lib/contrast/agent/request_context.rb +19 -22
  41. data/lib/contrast/agent/static_analysis.rb +1 -1
  42. data/lib/contrast/agent/tracepoint_hook.rb +6 -1
  43. data/lib/contrast/agent/version.rb +1 -1
  44. data/lib/contrast/api/communication/messaging_queue.rb +12 -6
  45. data/lib/contrast/api/communication/service_lifecycle.rb +4 -1
  46. data/lib/contrast/api/communication/socket_client.rb +4 -4
  47. data/lib/contrast/api/decorators/agent_startup.rb +4 -4
  48. data/lib/contrast/api/decorators/application_startup.rb +6 -5
  49. data/lib/contrast/api/decorators/route_coverage.rb +24 -1
  50. data/lib/contrast/components/agent.rb +5 -2
  51. data/lib/contrast/components/assess.rb +6 -3
  52. data/lib/contrast/components/base.rb +2 -2
  53. data/lib/contrast/components/config.rb +1 -0
  54. data/lib/contrast/components/contrast_service.rb +4 -2
  55. data/lib/contrast/components/logger.rb +13 -8
  56. data/lib/contrast/components/scope.rb +9 -28
  57. data/lib/contrast/config/base_configuration.rb +14 -6
  58. data/lib/contrast/configuration.rb +19 -15
  59. data/lib/contrast/extension/assess/array.rb +1 -11
  60. data/lib/contrast/extension/assess/eval_trigger.rb +0 -20
  61. data/lib/contrast/extension/assess/fiber.rb +0 -11
  62. data/lib/contrast/extension/assess/hash.rb +0 -10
  63. data/lib/contrast/extension/assess/kernel.rb +1 -10
  64. data/lib/contrast/extension/assess/marshal.rb +3 -11
  65. data/lib/contrast/extension/assess/regexp.rb +0 -11
  66. data/lib/contrast/extension/assess/string.rb +1 -26
  67. data/lib/contrast/extension/extension.rb +61 -0
  68. data/lib/contrast/extension/protect/kernel.rb +0 -10
  69. data/lib/contrast/framework/grape/support.rb +174 -0
  70. data/lib/contrast/framework/manager.rb +42 -6
  71. data/lib/contrast/framework/rack/support.rb +1 -1
  72. data/lib/contrast/framework/rails/patch/assess_configuration.rb +0 -1
  73. data/lib/contrast/framework/rails/patch/support.rb +6 -3
  74. data/lib/contrast/framework/rails/railtie.rb +1 -1
  75. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +1 -0
  76. data/lib/contrast/framework/rails/support.rb +60 -13
  77. data/lib/contrast/framework/sinatra/support.rb +1 -1
  78. data/lib/contrast/logger/log.rb +89 -15
  79. data/lib/contrast/utils/io_util.rb +1 -1
  80. data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
  81. data/lib/contrast/utils/tag_util.rb +2 -1
  82. data/resources/assess/policy.json +197 -2
  83. data/resources/deadzone/policy.json +10 -0
  84. data/ruby-agent.gemspec +10 -1
  85. metadata +78 -12
  86. data/lib/contrast/utils/inventory_util.rb +0 -113
@@ -17,7 +17,6 @@ module Contrast
17
17
  class Split < Contrast::Agent::Assess::Policy::Propagator::Base
18
18
  extend Contrast::Components::Scope::InstanceMethods
19
19
  extend Contrast::Components::Logger::InstanceMethods
20
- #cs__const_set('AGENT', Contrast::AGENT)
21
20
 
22
21
  SPLIT_TRACKER = Contrast::Utils::ThreadTracker.new
23
22
 
@@ -111,7 +110,9 @@ module Contrast
111
110
  # Load patch.
112
111
  def instrument_string_split
113
112
  @_instrument_string_split ||= begin
114
- require 'cs__assess_yield_track/cs__assess_yield_track' if ::Contrast::AGENT.patch_yield? && Funchook.available?
113
+ if ::Contrast::AGENT.patch_yield? && Funchook.available?
114
+ require 'cs__assess_yield_track/cs__assess_yield_track'
115
+ end
115
116
  true
116
117
  rescue StandardError => e
117
118
  logger.error('Error loading split rb_yield patch', e)
@@ -16,6 +16,7 @@ module Contrast
16
16
  # a 'get it right' state soon.
17
17
  class Substitution
18
18
  include Contrast::Components::Logger::InstanceMethods
19
+ extend Contrast::Components::Logger::InstanceMethods
19
20
 
20
21
  CAPTURE_GROUP_REGEXP = /\\[[:digit:]]/.cs__freeze
21
22
  CAPTURE_NAME_REGEXP = /\\k<[[:alpha:]]/.cs__freeze
@@ -22,7 +22,6 @@ module Contrast
22
22
  module RewriterPatch
23
23
  extend Contrast::Components::Logger::InstanceMethods
24
24
 
25
-
26
25
  class << self
27
26
  def rewrite_interpolations
28
27
  return unless agent_should_rewrite?
@@ -17,7 +17,6 @@ module Contrast
17
17
  module SourceMethod
18
18
  extend Contrast::Components::Logger::InstanceMethods
19
19
 
20
-
21
20
  PARAMETER_TYPE = 'PARAMETER'
22
21
  PARAMETER_KEY_TYPE = 'PARAMETER_KEY'
23
22
  HEADER_TYPE = 'HEADER'
@@ -26,8 +25,7 @@ module Contrast
26
25
  COOKIE_KEY_TYPE = 'COOKIE_KEY'
27
26
 
28
27
  class << self
29
- # This is called from within our woven proc. It will be called as if it were inline in the Rack
30
- # application.
28
+ # This is called from within our woven proc. It will be called as if it were inline in the Rack application.
31
29
  #
32
30
  # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] the policy that applies to the
33
31
  # method being called
@@ -37,29 +35,27 @@ module Contrast
37
35
  # @return [Object, nil] the tracked Return or nil if no changes were made
38
36
  def source_patchers method_policy, object, ret, args
39
37
  return unless analyze?(method_policy, object, ret, args)
38
+ return unless (source_node = method_policy.source_node)
39
+ return unless (target = determine_target(source_node, object, ret, args))
40
40
 
41
- source_node = method_policy.source_node
42
- target = determine_target(source_node, object, ret, args)
43
- restore_frozen_state = false
41
+ return_val = nil
44
42
  if target.cs__frozen? && !Contrast::Agent::Assess::Tracker.trackable?(target)
45
43
  return unless ::Contrast::ASSESS.track_frozen_sources?
46
44
  return unless source_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
45
+ return unless (dup = safe_dup(ret))
46
+ return unless Contrast::Agent::Assess::Tracker.trackable?(dup)
47
47
 
48
- dup = safe_dup(ret)
49
- return unless dup
50
-
51
- restore_frozen_state = true
52
- ret = dup
53
- target = ret
54
- Contrast::Agent::Assess::Tracker.pre_freeze(ret)
55
- ret.cs__freeze
56
- # double check that we were able to finalize the replaced return
57
- return unless Contrast::Agent::Assess::Tracker.trackable?(target)
48
+ Contrast::Agent::Assess::Tracker.pre_freeze(dup)
49
+ return_val = dup.cs__freeze
50
+ target = dup
58
51
  end
52
+
59
53
  apply_source(Contrast::Agent::REQUEST_TRACKER.current, source_node, target, object, ret,
60
54
  source_node.type, nil, *args)
61
- restore_frozen_state ? ret : nil
55
+
56
+ return_val
62
57
  end
58
+ Contrast::Components::Logger.add_trace_log_timing_for(SourceMethod, :source_patchers)
63
59
 
64
60
  private
65
61
 
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
-
5
4
  module Contrast
6
5
  module Agent
7
6
  module Assess
@@ -11,30 +11,28 @@ module Contrast
11
11
  module Agent
12
12
  module Assess
13
13
  module Policy
14
- # A trigger method is one which can perform a dangerous action, as
15
- # described by the Contrast::Agent::Assess::Policy::TriggerNode class.
16
- # Each such method will call to this module just after invocation in
17
- # order to determine if the call was done safely. In those cases where
18
- # it was not, a Finding report is issued to the Service
14
+ # A trigger method is one which can perform a dangerous action, as described by the
15
+ # Contrast::Agent::Assess::Policy::TriggerNode class. Each such method will call to this module just after
16
+ # invocation in order to determine if the call was done safely. In those cases where it was not, a Finding
17
+ # report is issued to the Service.
19
18
  module TriggerMethod
20
19
  extend Contrast::Components::Logger::InstanceMethods
21
20
 
22
- # The level of TeamServer compliance our traces meet when in the
23
- # abnormal condition of being dataflow rules without routes
21
+ # The level of TeamServer compliance our traces meet when in the abnormal condition of being dataflow rules
22
+ # without routes.
24
23
  MINIMUM_FINDING_VERSION = 3
25
- # The level of TeamServer compliance our traces meet
24
+ # The level of TeamServer compliance our traces meet.
26
25
  CURRENT_FINDING_VERSION = 4
27
26
 
28
27
  class << self
29
- # This is called from within our woven proc. It will be called as if it
30
- # were inline in the Rack application.
28
+ # This is called from within our woven proc. It will be called as if it were inline in the Rack
29
+ # application.
31
30
  #
32
- # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
33
- # the node that applies to the method being called
31
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node that applies to the method
32
+ # being called
34
33
  # @param object [Object] the Object on which the method was invoked
35
34
  # @param ret [Object] the Return of the invoked method
36
- # @param args [Array<Object>] the Arguments with which the method
37
- # was invoked
35
+ # @param args [Array<Object>] the Arguments with which the method was invoked
38
36
  def apply_trigger_rule trigger_node, object, ret, args
39
37
  return if trigger_node.nil?
40
38
 
@@ -52,19 +50,16 @@ module Contrast
52
50
  apply_trigger(trigger_node, source, object, ret, *args)
53
51
  end
54
52
 
55
- # This converts the source of the finding, and the events leading
56
- # up to it into a Finding
53
+ # This converts the source of the finding, and the events leading up to it into a Finding
57
54
  #
58
- # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
59
- # the node to direct applying this trigger event
55
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
56
+ # trigger event
60
57
  # @param source [Object] the source of the Trigger Event
61
58
  # @param object [Object] the Object on which the method was invoked
62
59
  # @param ret [Object] the Return of the invoked method
63
- # @param args [Array<Object>] the Arguments with which the method
64
- # was invoked
65
- # @return [Contrast::Api::Dtm::Finding, nil] the
66
- # Contrast::Api::Dtm::Finding to send to TeamServer or nil if
67
- # conditions were not met
60
+ # @param args [Array<Object>] the Arguments with which the method was invoked
61
+ # @return [Contrast::Api::Dtm::Finding, nil] the Contrast::Api::Dtm::Finding to send to TeamServer or
62
+ # nil if conditions were not met
68
63
  def build_finding trigger_node, source, object, ret, *args
69
64
  return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
70
65
 
@@ -85,13 +80,12 @@ module Contrast
85
80
  logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
86
81
  end
87
82
 
88
- # Given a finding, append it to an activity message and send it to
89
- # the Service for processing.
83
+ # Given a finding, append it to an activity message and send it to the Service for processing. If an
84
+ # activity message does not exist, b/c we're invoked outside of a request context, build an activity and
85
+ # immediately report it with the finding.
90
86
  #
91
- # @param finding [Contrast::Api::Dtm::Finding] the Finding to
92
- # report.
93
- # @param request [Contrast::Agent::Request] our wrapper around the
94
- # Rack::Request.
87
+ # @param finding [Contrast::Api::Dtm::Finding] the Finding to report.
88
+ # @param request [Contrast::Agent::Request] our wrapper around the Rack::Request.
95
89
  def report_finding finding, request = nil
96
90
  context = Contrast::Agent::REQUEST_TRACKER.current
97
91
  if context
@@ -107,9 +101,8 @@ module Contrast
107
101
  logger.debug('Attempted to report finding without request', finding: finding)
108
102
  end
109
103
 
110
- # If we're out of request context, then we need to report this
111
- # finding ourselves, so we'll send it in the one-off activity we
112
- # created.
104
+ # If we're out of request context, then we need to report this finding ourselves, so we'll send it in the
105
+ # one-off activity we created.
113
106
  Contrast::Agent.messaging_queue.send_event_eventually(activity)
114
107
  end
115
108
 
@@ -125,15 +118,12 @@ module Contrast
125
118
  env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live))
126
119
  end
127
120
 
128
- # Find the request for this finding. This assumes, for now, that if
129
- # there is an active request, then that is the request to report.
130
- # Otherwise, we'll use the first request found in the events of the
121
+ # Find the request for this finding. This assumes, for now, that if there is an active request, then that
122
+ # is the request to report. Otherwise, we'll use the first request found in the events of the
131
123
  # source_object.
132
124
  #
133
- # @param source [Object,nil] some Object used as the source of a
134
- # trigger event
135
- # @return [Contrast::Agent::Request,nil] the request from which the
136
- # dataflow on the request originated.
125
+ # @param source [Object,nil] some Object used as the source of a trigger event
126
+ # @return [Contrast::Agent::Request,nil] the request from which the dataflow on the request originated.
137
127
  def find_request source
138
128
  return Contrast::Agent::REQUEST_TRACKER.current.request if Contrast::Agent::REQUEST_TRACKER.current
139
129
  return unless (properties = Contrast::Agent::Assess::Tracker.properties(source))
@@ -150,16 +140,14 @@ module Contrast
150
140
  Contrast::Agent::FeatureState.instance
151
141
  end
152
142
 
153
- # This is our method that actually checks the taint on the object
154
- # our trigger_node targets.
143
+ # This is our method that actually checks the taint on the object our trigger_node targets.
155
144
  #
156
- # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
157
- # the node to direct applying this trigger event
145
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
146
+ # trigger event
158
147
  # @param source [Object] the source of the Trigger Event
159
148
  # @param object [Object] the Object on which the method was invoked
160
149
  # @param ret [Object] the Return of the invoked method
161
- # @param args [Array<Object>] the Arguments with which the method
162
- # was invoked
150
+ # @param args [Array<Object>] the Arguments with which the method was invoked
163
151
  def apply_trigger trigger_node, source, object, ret, *args
164
152
  return unless trigger_node
165
153
  return if trigger_node.rule_disabled?
@@ -178,23 +166,17 @@ module Contrast
178
166
  logger.warn('Unable to apply trigger', e, node_id: trigger_node.id)
179
167
  end
180
168
 
181
- # Given the marker from the trigger_node (the pointer indicating
182
- # the entity from which the taint originated), return the entity on
183
- # which this trigger needs to operate.
169
+ # Given the marker from the trigger_node (the pointer indicating the entity from which the taint
170
+ # originated), return the entity on which this trigger needs to operate.
184
171
  #
185
- # In an effort to speed up this lookup, we've changed the marker
186
- # for parameters to be implicit - if it is not a return or an
187
- # object, it must be a parameter, which we can reference either by
188
- # index or by name.
172
+ # In an effort to speed up this lookup, we've changed the marker for parameters to be implicit - if it is
173
+ # not a return or an object, it must be a parameter, which we can reference by index.
189
174
  #
190
- # @param marker [String] the source marker that indicates which
191
- # Object
175
+ # @param marker [String] the source marker that indicates which Object
192
176
  # @param object [Object] the Object on which the method was invoked
193
177
  # @param ret [Object] the Return of the invoked method
194
- # @param args [Array<Object>] the Arguments with which the method
195
- # was invoked
196
- # @return [Object] the literal object that this Trigger Event
197
- # verifies
178
+ # @param args [Array<Object>] the Arguments with which the method was invoked
179
+ # @return [Object] the literal object that this Trigger Event verifies
198
180
  def determine_source marker, object, ret, args
199
181
  case marker
200
182
  when Contrast::Utils::ObjectShare::RETURN_KEY
@@ -217,16 +199,15 @@ module Contrast
217
199
  end
218
200
  end
219
201
 
220
- # This is our method that actually checks the taint on the object
221
- # our trigger_node targets for our Regexp based rules.
202
+ # This is our method that actually checks the taint on the object our trigger_node targets for our Regexp
203
+ # based rules.
222
204
  #
223
- # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
224
- # the node to direct applying this trigger event
205
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
206
+ # trigger event
225
207
  # @param source [Object] the source of the Trigger Event
226
208
  # @param object [Object] the Object on which the method was invoked
227
209
  # @param ret [Object] the Return of the invoked method
228
- # @param args [Array<Object>] the Arguments with which the method
229
- # was invoked
210
+ # @param args [Array<Object>] the Arguments with which the method was invoked
230
211
  def apply_regexp_rule trigger_node, source, object, ret, *args
231
212
  return unless source.is_a?(String)
232
213
  return if trigger_node.good_value && source.match?(trigger_node.good_value)
@@ -235,16 +216,15 @@ module Contrast
235
216
  build_finding(trigger_node, source, object, ret, *args)
236
217
  end
237
218
 
238
- # This is our method that actually checks the taint on the object
239
- # our trigger_node targets for our Dataflow based rules.
219
+ # This is our method that actually checks the taint on the object our trigger_node targets for our Dataflow
220
+ # based rules.
240
221
  #
241
- # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
242
- # the node to direct applying this trigger event
222
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
223
+ # trigger event
243
224
  # @param source [Object] the source of the Trigger Event
244
225
  # @param object [Object] the Object on which the method was invoked
245
226
  # @param ret [Object] the Return of the invoked method
246
- # @param args [Array<Object>] the Arguments with which the method
247
- # was invoked
227
+ # @param args [Array<Object>] the Arguments with which the method was invoked
248
228
  def apply_dataflow_rule trigger_node, source, object, ret, *args
249
229
  return unless source
250
230
 
@@ -287,8 +267,7 @@ module Contrast
287
267
 
288
268
  build_events finding, properties.event if properties.event
289
269
 
290
- # Google::Protobuf::Map doesn't support merge!, so we have to do this
291
- # long form
270
+ # Google::Protobuf::Map doesn't support merge!, so we have to do this long form
292
271
  source_props = properties.properties
293
272
  return unless source_props
294
273
 
@@ -304,8 +283,8 @@ module Contrast
304
283
  event.parent_events&.each do |parent_event|
305
284
  build_events(finding, parent_event)
306
285
  end
307
- # events could technically be nil, but we would have failed
308
- # the rule check before getting here. not worth the nil check
286
+ # events could technically be nil, but we would have failed the rule check before getting here. not
287
+ # worth the nil check
309
288
  finding.events << event.to_dtm_event
310
289
  end
311
290
 
@@ -324,21 +303,18 @@ module Contrast
324
303
  finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
325
304
  end
326
305
 
327
- # Because our APIs are not versioned, TeamServer relies on a field,
328
- # version, to tell it what, if any, validation it can preform on
329
- # the findings we send it. Examine the finding and determine the
330
- # level to which it conforms.
306
+ # Because our APIs are not versioned, TeamServer relies on a field, version, to tell it what, if any,
307
+ # validation it can preform on the findings we send it. Examine the finding and determine the level to
308
+ # which it conforms.
331
309
  #
332
310
  # @param finding [Contrast::Api::Dtm::Finding]
333
311
  # @return [int] the version of compliance
334
312
  def determine_compliance_version finding
335
313
  return MINIMUM_FINDING_VERSION unless finding
336
- # as routes are the only variable between findings, in the case
337
- # where we couldn't determine one, any finding with a route is at
338
- # maximum compliance
314
+ # as routes are the only variable between findings, in the case where we couldn't determine one, any
315
+ # finding with a route is at maximum compliance
339
316
  return CURRENT_FINDING_VERSION if finding.routes.any?
340
- # any finding without events is not of a dataflow type and
341
- # therefore at maximum compliance
317
+ # any finding without events is not of a dataflow type and therefore at maximum compliance
342
318
  return CURRENT_FINDING_VERSION unless finding.events.any?
343
319
 
344
320
  MINIMUM_FINDING_VERSION
@@ -50,7 +50,8 @@ module Contrast
50
50
  return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
51
51
 
52
52
  if current_request.observed_route.sources.any? do |source|
53
- source.type == event.forced_source_type && source.name == event.forced_source_name
53
+ source.type == event.forced_source_type &&
54
+ source.name == event.forced_source_name # rubocop:disable Security/Module/Name
54
55
  end
55
56
 
56
57
  return
@@ -20,7 +20,6 @@ module Contrast
20
20
  module HardcodedValueRule
21
21
  include Contrast::Components::Logger::InstanceMethods
22
22
 
23
-
24
23
  def disabled?
25
24
  !::Contrast::ASSESS.enabled? || ::Contrast::ASSESS.rule_disabled?(rule_id)
26
25
  end
@@ -8,7 +8,7 @@ module Contrast
8
8
  # A Reaction from TeamServer which indicates the Agent should be disabled,
9
9
  # typically because some configuration setting did not satisfy requirements
10
10
  # set by the Organization's Administrator
11
- class DisableReaction
11
+ module DisableReaction
12
12
  extend Contrast::Components::Logger::InstanceMethods
13
13
 
14
14
  def self.run _reaction, level
@@ -94,10 +94,6 @@ module Contrast
94
94
  @exclusion.type == Contrast::Api::Settings::Exclusion::ExclusionType::CODE
95
95
  end
96
96
 
97
- def exc_name
98
- @exclusion.name # rubocop:disable Security/Module/Name -- part of the API.
99
- end
100
-
101
97
  def match_all?
102
98
  @exclusion.urls.nil? || @exclusion.urls.empty?
103
99
  end
@@ -0,0 +1,117 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/timer'
5
+ require 'contrast/utils/object_share'
6
+ require 'contrast/components/logger'
7
+
8
+ module Contrast
9
+ module Agent
10
+ module Inventory
11
+ # Methods used for parsing database connection configurations
12
+ # for getting inventory information from the application
13
+ module DatabaseConfig
14
+ extend Contrast::Components::Logger::InstanceMethods
15
+
16
+ # TeamServer only accepts certain values for ArchitectureComponents.
17
+ # DO NOT CHANGE THIS!
18
+ AC_TYPE_DB = 'db'
19
+ # TeamServer only accepts certain values for FlowMap Services.
20
+ # DO NOT CHANGE THIS
21
+ ADAPTER = 'adapter'
22
+ HOST = 'host'
23
+ PORT = 'port'
24
+ DATABASE = 'database'
25
+ DEFAULT = 'default'
26
+ LOCALHOST = 'localhost'
27
+
28
+ def self.active_record_config
29
+ return @_active_record_config if instance_variable_defined?(:@_active_record_config)
30
+
31
+ @_active_record_config = ActiveRecord::Base.connection_config rescue nil # rubocop:disable Style/RescueModifier
32
+ end
33
+
34
+ def self.append_db_config(activity_or_update,
35
+ hash_or_str = Contrast::Agent::Inventory::DatabaseConfig.active_record_config)
36
+ arr = build_from_db_config(hash_or_str)
37
+ return unless arr&.any?
38
+
39
+ arr.each do |a|
40
+ next unless a
41
+
42
+ if activity_or_update.is_a?(Contrast::Api::Dtm::Activity)
43
+ activity_or_update.architectures << a
44
+ else
45
+ activity_or_update.components << a
46
+ end
47
+ end
48
+ rescue StandardError => e
49
+ logger.error('Unable to append db config', e)
50
+ nil
51
+ end
52
+
53
+ def self.build_from_db_config hash_or_str
54
+ return unless hash_or_str
55
+
56
+ if hash_or_str.is_a?(Hash)
57
+ build_from_db_hash(hash_or_str)
58
+ else
59
+ build_from_db_string(hash_or_str.to_s)
60
+ end
61
+ end
62
+
63
+ def self.build_from_db_hash hash
64
+ ac = Contrast::Api::Dtm::ArchitectureComponent.new
65
+ ac.vendor = hash[:adapter] || hash[ADAPTER] || Contrast::Utils::ObjectShare::EMPTY_STRING
66
+ ac.remote_host = host_from_hash(hash)
67
+ ac.remote_port = port_from_hash(hash)
68
+ ac.type = AC_TYPE_DB
69
+ ac.url = hash[:database] || hash[DATABASE] || DEFAULT
70
+ [ac]
71
+ end
72
+
73
+ def self.host_from_hash hash
74
+ hash[:host] || hash[HOST] || Contrast::Utils::ObjectShare::EMPTY_STRING
75
+ end
76
+
77
+ def self.port_from_hash hash
78
+ p = hash[:port] || hash[PORT] || Contrast::Utils::ObjectShare::EMPTY_STRING
79
+ p.to_i
80
+ end
81
+
82
+ # Examples:
83
+ # mongodb://[user:pass@]host1[:port1][,host2[:port2],[,hostN[:portN]]][/[database][?options]]
84
+ # postgresql://scott:tiger@localhost/mydatabase
85
+ # mysql+mysqlconnector://scott:tiger@localhost/foo
86
+ def self.build_from_db_string str
87
+ adapter, hosts, database = split_connection_str(str)
88
+ acs = []
89
+ hosts.split(Contrast::Utils::ObjectShare::COMMA).map do |s|
90
+ host, port = s.split(Contrast::Utils::ObjectShare::COLON)
91
+
92
+ ac = Contrast::Api::Dtm::ArchitectureComponent.new
93
+ ac.vendor = Contrast::Utils::StringUtils.force_utf8(adapter)
94
+ ac.remote_host = Contrast::Utils::StringUtils.force_utf8(host)
95
+ ac.remote_port = port.to_i
96
+ ac.type = AC_TYPE_DB
97
+ ac.url = Contrast::Utils::StringUtils.force_utf8(database)
98
+ acs << ac
99
+ end
100
+ acs
101
+ end
102
+
103
+ def self.split_connection_str str
104
+ adapter, str = str.split(Contrast::Utils::ObjectShare::COLON_SLASH_SLASH)
105
+ _auth, str = str.split(Contrast::Utils::ObjectShare::AT)
106
+ # Not currently used
107
+ # user, pass = auth.split(Contrast::Utils::ObjectShare::COLON)
108
+ hosts, db_and_options = str.split(Contrast::Utils::ObjectShare::SLASH)
109
+ hosts << LOCALHOST if hosts.empty?
110
+ database, _options = db_and_options.split(Contrast::Utils::ObjectShare::QUESTION_MARK)
111
+
112
+ [adapter, hosts, database]
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end