contrast-agent 4.9.1 → 4.10.0

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