contrast-agent 3.15.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -0
  3. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +22 -10
  4. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +4 -3
  5. data/lib/contrast/agent.rb +4 -12
  6. data/lib/contrast/agent/assess/contrast_event.rb +121 -130
  7. data/lib/contrast/agent/assess/contrast_object.rb +51 -0
  8. data/lib/contrast/agent/assess/events/source_event.rb +5 -10
  9. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +10 -3
  10. data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
  11. data/lib/contrast/agent/assess/policy/policy_node.rb +46 -69
  12. data/lib/contrast/agent/assess/policy/policy_scanner.rb +19 -2
  13. data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
  14. data/lib/contrast/agent/assess/policy/propagation_method.rb +13 -19
  15. data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
  16. data/lib/contrast/agent/assess/policy/propagator/append.rb +1 -2
  17. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
  18. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  19. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -3
  20. data/lib/contrast/agent/assess/policy/propagator/insert.rb +2 -3
  21. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
  22. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -5
  23. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
  24. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
  25. data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
  26. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
  27. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
  28. data/lib/contrast/agent/assess/policy/propagator/select.rb +4 -7
  29. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -9
  30. data/lib/contrast/agent/assess/policy/propagator/split.rb +77 -122
  31. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +32 -25
  32. data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
  33. data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
  34. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +9 -13
  35. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  36. data/lib/contrast/agent/assess/policy/trigger_method.rb +39 -14
  37. data/lib/contrast/agent/assess/policy/trigger_node.rb +31 -37
  38. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
  39. data/lib/contrast/agent/assess/property/evented.rb +5 -18
  40. data/lib/contrast/agent/assess/property/tagged.rb +28 -16
  41. data/lib/contrast/agent/assess/property/updated.rb +0 -5
  42. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
  43. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +23 -8
  44. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +83 -14
  45. data/lib/contrast/agent/assess/rule/redos.rb +1 -1
  46. data/lib/contrast/agent/assess/tag.rb +1 -1
  47. data/lib/contrast/agent/assess/tracker.rb +16 -18
  48. data/lib/contrast/agent/at_exit_hook.rb +5 -5
  49. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
  50. data/lib/contrast/agent/inventory.rb +15 -0
  51. data/lib/contrast/agent/inventory/dependencies.rb +50 -0
  52. data/lib/contrast/agent/inventory/dependency_analysis.rb +37 -0
  53. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +104 -0
  54. data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +38 -0
  55. data/lib/contrast/agent/middleware.rb +51 -3
  56. data/lib/contrast/agent/patching/policy/after_load_patch.rb +5 -5
  57. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +20 -20
  58. data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
  59. data/lib/contrast/agent/patching/policy/module_policy.rb +10 -10
  60. data/lib/contrast/agent/patching/policy/patch.rb +6 -0
  61. data/lib/contrast/agent/patching/policy/policy.rb +16 -2
  62. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +3 -5
  63. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
  64. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +4 -3
  65. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
  66. data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
  67. data/lib/contrast/agent/protect/rule/base.rb +63 -14
  68. data/lib/contrast/agent/protect/rule/cmd_injection.rb +12 -28
  69. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
  70. data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
  71. data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
  72. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
  73. data/lib/contrast/agent/protect/rule/sqli.rb +3 -3
  74. data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
  75. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
  76. data/lib/contrast/agent/reaction_processor.rb +1 -1
  77. data/lib/contrast/agent/request.rb +34 -34
  78. data/lib/contrast/agent/request_handler.rb +1 -1
  79. data/lib/contrast/agent/response.rb +5 -5
  80. data/lib/contrast/agent/rewriter.rb +3 -3
  81. data/lib/contrast/agent/scope.rb +81 -55
  82. data/lib/contrast/agent/static_analysis.rb +15 -9
  83. data/lib/contrast/agent/tracepoint_hook.rb +1 -1
  84. data/lib/contrast/agent/version.rb +1 -1
  85. data/lib/contrast/api/communication/socket_client.rb +36 -1
  86. data/lib/contrast/api/decorators.rb +3 -0
  87. data/lib/contrast/api/decorators/address.rb +13 -14
  88. data/lib/contrast/api/decorators/application_update.rb +1 -1
  89. data/lib/contrast/api/decorators/library.rb +54 -0
  90. data/lib/contrast/api/decorators/library_usage_update.rb +31 -0
  91. data/lib/contrast/api/decorators/message.rb +1 -0
  92. data/lib/contrast/api/decorators/trace_event.rb +31 -41
  93. data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
  94. data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
  95. data/lib/contrast/api/decorators/user_input.rb +2 -1
  96. data/lib/contrast/common_agent_configuration.rb +2 -1
  97. data/lib/contrast/components/agent.rb +6 -5
  98. data/lib/contrast/components/app_context.rb +39 -30
  99. data/lib/contrast/components/assess.rb +36 -0
  100. data/lib/contrast/components/config.rb +29 -37
  101. data/lib/contrast/components/contrast_service.rb +9 -9
  102. data/lib/contrast/components/interface.rb +30 -6
  103. data/lib/contrast/components/inventory.rb +6 -1
  104. data/lib/contrast/components/scope.rb +72 -6
  105. data/lib/contrast/components/settings.rb +23 -23
  106. data/lib/contrast/config/assess_configuration.rb +2 -1
  107. data/lib/contrast/config/inventory_configuration.rb +2 -2
  108. data/lib/contrast/config/service_configuration.rb +4 -2
  109. data/lib/contrast/configuration.rb +1 -1
  110. data/lib/contrast/extension/assess/array.rb +9 -6
  111. data/lib/contrast/extension/assess/erb.rb +6 -3
  112. data/lib/contrast/extension/assess/eval_trigger.rb +6 -6
  113. data/lib/contrast/extension/assess/exec_trigger.rb +0 -3
  114. data/lib/contrast/extension/assess/fiber.rb +5 -6
  115. data/lib/contrast/extension/assess/hash.rb +7 -5
  116. data/lib/contrast/extension/assess/kernel.rb +19 -22
  117. data/lib/contrast/extension/assess/marshal.rb +40 -28
  118. data/lib/contrast/extension/assess/regexp.rb +6 -11
  119. data/lib/contrast/extension/assess/string.rb +14 -13
  120. data/lib/contrast/extension/protect/kernel.rb +3 -3
  121. data/lib/contrast/framework/base_support.rb +51 -53
  122. data/lib/contrast/framework/manager.rb +6 -5
  123. data/lib/contrast/framework/rack/patch/session_cookie.rb +10 -10
  124. data/lib/contrast/framework/rack/support.rb +2 -1
  125. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +14 -14
  126. data/lib/contrast/framework/rails/patch/assess_configuration.rb +1 -1
  127. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +11 -11
  128. data/lib/contrast/framework/rails/patch/support.rb +1 -1
  129. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +12 -12
  130. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +13 -13
  131. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -3
  132. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +13 -13
  133. data/lib/contrast/framework/rails/support.rb +5 -1
  134. data/lib/contrast/framework/sinatra/patch/base.rb +11 -11
  135. data/lib/contrast/framework/sinatra/support.rb +7 -6
  136. data/lib/contrast/logger/application.rb +1 -4
  137. data/lib/contrast/logger/log.rb +7 -2
  138. data/lib/contrast/utils/duck_utils.rb +1 -1
  139. data/lib/contrast/utils/heap_dump_util.rb +1 -1
  140. data/lib/contrast/utils/invalid_configuration_util.rb +2 -5
  141. data/lib/contrast/utils/inventory_util.rb +0 -7
  142. data/lib/contrast/utils/object_share.rb +3 -3
  143. data/lib/contrast/utils/preflight_util.rb +1 -1
  144. data/lib/contrast/utils/prevent_serialization.rb +1 -1
  145. data/lib/contrast/utils/resource_loader.rb +1 -1
  146. data/lib/contrast/utils/sha256_builder.rb +2 -14
  147. data/lib/contrast/utils/string_utils.rb +1 -1
  148. data/lib/contrast/utils/tag_util.rb +9 -13
  149. data/resources/assess/policy.json +31 -12
  150. data/resources/deadzone/policy.json +156 -0
  151. data/resources/protect/policy.json +12 -0
  152. data/ruby-agent.gemspec +11 -6
  153. data/service_executables/VERSION +1 -1
  154. data/service_executables/linux/contrast-service +0 -0
  155. data/service_executables/mac/contrast-service +0 -0
  156. metadata +91 -28
  157. data/lib/contrast/utils/boolean_util.rb +0 -30
  158. data/lib/contrast/utils/gemfile_reader.rb +0 -193
@@ -58,9 +58,9 @@ module Contrast
58
58
 
59
59
  def instrument
60
60
  @_instrument_named_track ||= begin
61
- require 'cs__assess_active_record_named/cs__assess_active_record_named'
62
- true
63
- end
61
+ require 'cs__assess_active_record_named/cs__assess_active_record_named'
62
+ true
63
+ end
64
64
  rescue StandardError, LoadError => e
65
65
  logger.error('Error loading active record named track patch', e)
66
66
  false
@@ -9,22 +9,22 @@ module Contrast
9
9
  # TODO: RUBY-714 remove w/ EOL of 2.5
10
10
  # @deprecated Changes to this class are discouraged as this approach is
11
11
  # being phased out with support for those language versions.
12
- class ActiveRecordTimeZoneInherited
12
+ module ActiveRecordTimeZoneInherited
13
13
  def self.instrument
14
14
  @_instrument ||= begin
15
- ::ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods.class_eval do
16
- private
15
+ ::ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods.class_eval do
16
+ private
17
17
 
18
- alias_method :cs__patched_inherited, :inherited
19
- def inherited klass # rubocop:disable Lint/MissingSuper
20
- klass&.instance_variable_set(:@cs__defining_class, true)
21
- cs__patched_inherited(klass) # This calls the original inherited, which should handle super as needed.
22
- ensure
23
- klass&.instance_variable_set(:@cs__defining_class, false)
24
- end
25
- end
26
- true
27
- end
18
+ alias_method :cs__patched_inherited, :inherited
19
+ def inherited klass # rubocop:disable Lint/MissingSuper
20
+ klass&.instance_variable_set(:@cs__defining_class, true)
21
+ cs__patched_inherited(klass) # This calls the original inherited, which should handle super as needed.
22
+ ensure
23
+ klass&.instance_variable_set(:@cs__defining_class, false)
24
+ end
25
+ end
26
+ true
27
+ end
28
28
  end
29
29
  end
30
30
  end
@@ -10,7 +10,8 @@ module Contrast
10
10
  module Framework
11
11
  module Rails
12
12
  # Used when Rails is present to define framework specific behavior
13
- class Support < BaseSupport
13
+ class Support
14
+ extend Contrast::Framework::BaseSupport
14
15
  extend Contrast::Framework::Rails::Patch::Support
15
16
 
16
17
  class << self
@@ -45,6 +46,9 @@ module Contrast
45
46
  find_all_routes(::Rails.application, [])
46
47
  end
47
48
 
49
+ # Find the current route, based on the provided Request wrapper
50
+ # @param request[Contrast::Agent::Request]
51
+ # @return [Contrast::Api::Dtm::RouteCoverage]
48
52
  def current_route request
49
53
  return unless ::Rails.cs__respond_to?(:application)
50
54
 
@@ -34,17 +34,17 @@ module Contrast
34
34
 
35
35
  def instrument
36
36
  @_instrument ||= begin
37
- ::Sinatra::Base.class_eval do
38
- alias_method :cs__patched_sinatra_base_call!, :call!
39
- # publicly available method for Sinatra::Base things -- unfortunately,
40
- # getting the routes appear to require a lookup every time
41
- def call! *args
42
- Contrast::Framework::Sinatra::Patch::Base.map_route(cs__class, settings, *args)
43
- cs__patched_sinatra_base_call!(*args)
44
- end
45
- end
46
- true
47
- end
37
+ ::Sinatra::Base.class_eval do
38
+ alias_method :cs__patched_sinatra_base_call!, :call!
39
+ # publicly available method for Sinatra::Base things -- unfortunately,
40
+ # getting the routes appear to require a lookup every time
41
+ def call! *args
42
+ Contrast::Framework::Sinatra::Patch::Base.map_route(cs__class, settings, *args)
43
+ cs__patched_sinatra_base_call!(*args)
44
+ end
45
+ end
46
+ true
47
+ end
48
48
  end
49
49
 
50
50
  private
@@ -8,7 +8,8 @@ module Contrast
8
8
  module Framework
9
9
  module Sinatra
10
10
  # Used when Sinatra is present to define framework specific behavior
11
- class Support < BaseSupport
11
+ class Support
12
+ extend Contrast::Framework::BaseSupport
12
13
  extend Contrast::Framework::Sinatra::Patch::Support
13
14
  class << self
14
15
  def detection_class
@@ -67,13 +68,13 @@ module Contrast
67
68
  private
68
69
 
69
70
  def app_class
70
- return nil unless defined?(::Sinatra) && defined?(::Sinatra::Base)
71
+ return unless defined?(::Sinatra) && defined?(::Sinatra::Base)
71
72
 
72
73
  @_app_class ||= begin
73
- sinatra_layers = ObjectSpace.each_object(::Sinatra::Base).to_a
74
- result_layer = sinatra_layers.find { |layer| layer.app.nil? }
75
- result_layer
76
- end
74
+ sinatra_layers = ObjectSpace.each_object(::Sinatra::Base).to_a
75
+ result_layer = sinatra_layers.find { |layer| layer.app.nil? }
76
+ result_layer
77
+ end
77
78
  end
78
79
 
79
80
  # Iterate over every class that extends Sinatra::Base, pull out its routes
@@ -33,7 +33,7 @@ module Contrast
33
33
  def application_configuration
34
34
  return unless info?
35
35
 
36
- loggable = CONFIG.raw.loggable
36
+ loggable = CONFIG.loggable
37
37
  info('Current configuration', configuration: loggable)
38
38
  env_keys = ENV.keys.select { |env_key| env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER) }
39
39
  env_items = env_keys.map { |env_key| Contrast::Utils::EnvConfigurationItem.new(env_key, nil) }
@@ -41,9 +41,6 @@ module Contrast
41
41
  hash[conversion.key] = conversion.dot_path_array.join('.')
42
42
  end
43
43
  info('Set by environment', overrides: env_translations)
44
- rescue StandardError => e
45
- puts e
46
- sleep(5)
47
44
  end
48
45
 
49
46
  def application_libraries
@@ -49,14 +49,19 @@ module Contrast
49
49
  path = valid_path(config_path || log_file)
50
50
  level_const = valid_level(config_level || log_level)
51
51
 
52
+ path_change = path != previous_path
53
+ level_change = level_const != previous_level
54
+
52
55
  # don't needlessly recreate logger
53
- return if @_logger && (path == previous_path) && (level_const == previous_level)
56
+ return if @_logger && !(path_change || level_change)
54
57
 
55
58
  @previous_path = path
56
59
  @previous_level = level_const
57
60
 
58
61
  @_logger = build(path: path, level_const: level_const)
59
- log_update
62
+ # If we're logging to a new path, then let's start it w/ our helpful
63
+ # data gathering messages
64
+ log_update if path_change
60
65
  rescue StandardError => e
61
66
  if logger
62
67
  logger.error('Unable to process update to LoggerManager.', e)
@@ -4,7 +4,7 @@
4
4
  module Contrast
5
5
  module Utils
6
6
  # Utility methods for identifying instances that can be used interchangeably
7
- class DuckUtils
7
+ module DuckUtils
8
8
  class << self
9
9
  # Determine if the given object, or the object to which it delegates,
10
10
  # responds to the given method.
@@ -106,7 +106,7 @@ module Contrast
106
106
  logger.info('******** HEAP DUMP HAS CONCLUDED ********')
107
107
  logger.info('*** APPLICATION PROCESS WILL EXIT SHORTLY ***')
108
108
  logger.info('*****************************************************')
109
- exit # We weren't kidding!
109
+ exit # rubocop:disable Rails/Exit We weren't kidding!
110
110
  end
111
111
  end
112
112
  end
@@ -1,6 +1,7 @@
1
1
  # Copyright (c) 2020 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/agent/assess/policy/trigger_method'
4
5
  require 'contrast/components/interface'
5
6
 
6
7
  module Contrast
@@ -41,11 +42,7 @@ module Contrast
41
42
  hash = Contrast::Utils::HashDigest.generate_config_hash(finding)
42
43
  finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash)
43
44
  finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
44
-
45
- activity = Contrast::Api::Dtm::Activity.new
46
- activity.findings << finding
47
-
48
- Contrast::Agent.messaging_queue.send_event_eventually(activity)
45
+ Contrast::Agent::Assess::Policy::TriggerMethod.report_finding(finding)
49
46
  end
50
47
  rescue StandardError => e
51
48
  logger.error('Unable to build a finding', e, rule: rule_id)
@@ -3,7 +3,6 @@
3
3
 
4
4
  require 'contrast/utils/timer'
5
5
  require 'contrast/utils/object_share'
6
- require 'contrast/utils/gemfile_reader'
7
6
  require 'contrast/components/interface'
8
7
 
9
8
  module Contrast
@@ -25,12 +24,6 @@ module Contrast
25
24
  DEFAULT = 'default'
26
25
  LOCALHOST = 'localhost'
27
26
 
28
- def self.inventory_class class_path
29
- Contrast::Utils::GemfileReader.instance.map_class(class_path)
30
- rescue StandardError => e
31
- logger.error('Unable to inventory module', e, path: class_path)
32
- end
33
-
34
27
  def self.active_record_config
35
28
  return @_active_record_config if instance_variable_defined?(:@_active_record_config)
36
29
 
@@ -1,13 +1,13 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- # rubocop:disable Object/Freeze
4
+ # rubocop:disable Security/Object/Freeze
5
5
  module Contrast
6
6
  module Utils
7
7
  # A utility class where a series of commonly used Strings and other
8
8
  # commonly used objects can be store and frozen to prevent unnecessary
9
9
  # duplication.
10
- class ObjectShare
10
+ module ObjectShare
11
11
  # Strings
12
12
  ASTERISK = '*'
13
13
  BACK_SLASH = '\\'
@@ -76,4 +76,4 @@ module Contrast
76
76
  end
77
77
  end
78
78
  end
79
- # rubocop:enable Object/Freeze
79
+ # rubocop:enable Security/Object/Freeze
@@ -4,7 +4,7 @@
4
4
  module Contrast
5
5
  module Utils
6
6
  # Utility for generating preflight message token
7
- class PreflightUtil
7
+ module PreflightUtil
8
8
  def self.create_preflight finding
9
9
  "#{ finding.rule_id },#{ finding.hash_code }"
10
10
  end
@@ -7,7 +7,7 @@ module Contrast
7
7
  #
8
8
  # Marshal is pretty cool. It does a lot of things well. What it doesn't
9
9
  # mess around with though is StringIO. And what we don't want to do is
10
- # serialize ourselves out with Marshal#dump.
10
+ # serialize ourselves out with Marshal.dump.
11
11
  #
12
12
  # Unfortunately, we have to mess around w/ that. To isolate our things from
13
13
  # user dumped Strings (and so that we can marshal findings), we have
@@ -4,7 +4,7 @@
4
4
  module Contrast
5
5
  module Utils
6
6
  # ResourceLoader can attempt to read a file from a predefined resource directory
7
- class ResourceLoader
7
+ module ResourceLoader
8
8
  RESOURCES = 'resources'
9
9
 
10
10
  # __FILE__/../../../resources
@@ -29,8 +29,8 @@ module Contrast
29
29
 
30
30
  # Generate a SHA256 hash of the combined source code of this Gem
31
31
  def sha256 path
32
- return nil unless path
33
- return nil unless File.exist?(path) && !File.directory?(path)
32
+ return unless path
33
+ return unless File.exist?(path) && !File.directory?(path)
34
34
 
35
35
  @sha256_cache[path] ||= Digest::SHA256.file(path).to_s
36
36
  end
@@ -52,18 +52,6 @@ module Contrast
52
52
  parent_dir = File.dirname(gems_dir)
53
53
  File.join(parent_dir, Contrast::Utils::ObjectShare::CACHE)
54
54
  end
55
-
56
- def self.files path
57
- instance.files(path)
58
- end
59
-
60
- def self.sha256 path
61
- instance.sha256(path)
62
- end
63
-
64
- def self.build_from_spec spec
65
- instance.build_from_spec(spec)
66
- end
67
55
  end
68
56
  end
69
57
  end
@@ -74,7 +74,7 @@ module Contrast
74
74
  # @return [String] a copy of the given String, upper cased, trimmed,
75
75
  # dashes replaced with underscore, and HTTP trimmed
76
76
  def self.normalized_key str
77
- return nil unless str
77
+ return unless str
78
78
 
79
79
  str = str.to_s
80
80
  @_normalized_keys ||= {}
@@ -19,16 +19,15 @@ module Contrast
19
19
 
20
20
  relationship = tag.compare_range(range.start_idx, range.end_idx)
21
21
  case relationship
22
- when Contrast::Agent::Assess::Tag::BELOW
23
22
  # since the tags are ordered, if we're below, nope out
24
- return false
25
- when Contrast::Agent::Assess::Tag::LOW_SPAN
26
- # if we ever get a low span, that means a low part
27
- # won't be covered. there's no need to continue
28
- return false
29
- when Contrast::Agent::Assess::Tag::WITHOUT
30
- # if we ever get a without, that means a low part won't
31
- # be covered. there's no need to continue
23
+ when Contrast::Agent::Assess::Tag::BELOW,
24
+ # if we ever get a low span, that means a low part
25
+ # won't be covered. there's no need to continue
26
+ Contrast::Agent::Assess::Tag::LOW_SPAN,
27
+ # if we ever get a without, that means a low part won't
28
+ # be covered. there's no need to continue
29
+ Contrast::Agent::Assess::Tag::WITHOUT
30
+
32
31
  return false
33
32
  when Contrast::Agent::Assess::Tag::WITHIN
34
33
  # if we're within, then 0 out this tag since it is
@@ -131,10 +130,7 @@ module Contrast
131
130
  smallered = []
132
131
  curr = nil
133
132
  tags.each do |tag|
134
- if curr.nil?
135
- curr = tag
136
- smallered << curr
137
- elsif tag.start_idx <= curr.end_idx
133
+ if curr && tag.start_idx <= curr.end_idx
138
134
  curr.update_end(tag.end_idx) if tag.end_idx > curr.end_idx
139
135
  else
140
136
  curr = tag
@@ -275,7 +275,7 @@
275
275
  "instance_method": true,
276
276
  "method_visibility": "public",
277
277
  "method_name":"insert",
278
- "source":"P1",
278
+ "source":"O,P1",
279
279
  "target":"O",
280
280
  "action":"INSERT"
281
281
  }, {
@@ -614,7 +614,6 @@
614
614
  "action":"CUSTOM",
615
615
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::MatchData",
616
616
  "patch_method": "captures_tagger"
617
-
618
617
  }, {
619
618
  "class_name":"MatchData",
620
619
  "instance_method": true,
@@ -640,7 +639,9 @@
640
639
  "method_name": "gsub",
641
640
  "action": "CUSTOM",
642
641
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Substitution",
643
- "patch_method": "gsub_tagger"
642
+ "patch_method": "gsub_tagger",
643
+ "source": "O,P1",
644
+ "target": "R"
644
645
  }, {
645
646
  "class_name": "String",
646
647
  "instance_method": true,
@@ -648,7 +649,9 @@
648
649
  "method_name": "gsub!",
649
650
  "action": "CUSTOM",
650
651
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Substitution",
651
- "patch_method": "gsub_tagger"
652
+ "patch_method": "gsub_tagger",
653
+ "source": "O,P1",
654
+ "target": "O"
652
655
  }, {
653
656
  "class_name": "String",
654
657
  "instance_method": true,
@@ -656,7 +659,9 @@
656
659
  "method_name": "sub",
657
660
  "action": "CUSTOM",
658
661
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Substitution",
659
- "patch_method": "sub_tagger"
662
+ "patch_method": "sub_tagger",
663
+ "source": "O,P1",
664
+ "target": "R"
660
665
  }, {
661
666
  "class_name": "String",
662
667
  "instance_method": true,
@@ -664,7 +669,9 @@
664
669
  "method_name": "sub!",
665
670
  "action": "CUSTOM",
666
671
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Substitution",
667
- "patch_method": "sub_tagger"
672
+ "patch_method": "sub_tagger",
673
+ "source": "O,P1",
674
+ "target": "O"
668
675
  }, {
669
676
  "class_name": "String",
670
677
  "instance_method": true,
@@ -672,7 +679,9 @@
672
679
  "method_name": "tr",
673
680
  "action": "CUSTOM",
674
681
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Trim",
675
- "patch_method": "tr_tagger"
682
+ "patch_method": "tr_tagger",
683
+ "source": "O,P1",
684
+ "target": "R"
676
685
  }, {
677
686
  "class_name": "String",
678
687
  "instance_method": true,
@@ -680,7 +689,9 @@
680
689
  "method_name": "tr!",
681
690
  "action": "CUSTOM",
682
691
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Trim",
683
- "patch_method": "tr_tagger"
692
+ "patch_method": "tr_tagger",
693
+ "source": "O,P1",
694
+ "target": "O"
684
695
  }, {
685
696
  "class_name": "String",
686
697
  "instance_method": true,
@@ -688,7 +699,9 @@
688
699
  "method_name": "tr_s",
689
700
  "action": "CUSTOM",
690
701
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Trim",
691
- "patch_method": "tr_s_tagger"
702
+ "patch_method": "tr_s_tagger",
703
+ "source": "O,P1",
704
+ "target": "R"
692
705
  }, {
693
706
  "class_name": "String",
694
707
  "instance_method": true,
@@ -696,7 +709,9 @@
696
709
  "method_name": "tr_s!",
697
710
  "action": "CUSTOM",
698
711
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Trim",
699
- "patch_method": "tr_s_tagger"
712
+ "patch_method": "tr_s_tagger",
713
+ "source": "O,P1",
714
+ "target": "O"
700
715
  }, {
701
716
  "class_name": "String",
702
717
  "instance_method": true,
@@ -704,7 +719,9 @@
704
719
  "method_name": "[]",
705
720
  "action": "CUSTOM",
706
721
  "patch_class": "Contrast::Agent::Assess::Policy::Propagator::Select",
707
- "patch_method": "select_tagger"
722
+ "patch_method": "select_tagger",
723
+ "source": "O",
724
+ "target": "R"
708
725
  }, {
709
726
  "class_name":"CGI::Util",
710
727
  "method_name":"escapeHTML",
@@ -966,7 +983,9 @@
966
983
  "method_name": "sprintf",
967
984
  "action": "CUSTOM",
968
985
  "patch_class": "Contrast::Extension::Assess::KernelPropagator",
969
- "patch_method": "sprintf_tagger"
986
+ "patch_method": "sprintf_tagger",
987
+ "source": "O,P1",
988
+ "target": "R"
970
989
  }, {
971
990
  "class_name":"ActiveRecord::ConnectionAdapters::Quoting",
972
991
  "instance_method": true,