contrast-agent 4.7.0 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -1
  3. data/.rspec +0 -1
  4. data/.rspec_parallel +6 -0
  5. data/.simplecov +1 -0
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +0 -1
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +0 -2
  8. data/lib/contrast/agent/assess/contrast_event.rb +1 -5
  9. data/lib/contrast/agent/assess/finalizers/hash.rb +2 -5
  10. data/lib/contrast/agent/assess/policy/patcher.rb +5 -4
  11. data/lib/contrast/agent/assess/policy/policy.rb +1 -1
  12. data/lib/contrast/agent/assess/policy/policy_scanner.rb +2 -6
  13. data/lib/contrast/agent/assess/policy/preshift.rb +11 -8
  14. data/lib/contrast/agent/assess/policy/propagation_method.rb +102 -59
  15. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -7
  16. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
  17. data/lib/contrast/agent/assess/policy/propagator/rack_protection.rb +73 -0
  18. data/lib/contrast/agent/assess/policy/propagator/split.rb +10 -6
  19. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +3 -3
  20. data/lib/contrast/agent/assess/policy/propagator.rb +1 -0
  21. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +6 -7
  22. data/lib/contrast/agent/assess/policy/source_method.rb +18 -22
  23. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -4
  24. data/lib/contrast/agent/assess/policy/trigger_method.rb +61 -86
  25. data/lib/contrast/agent/assess/policy/trigger_node.rb +1 -1
  26. data/lib/contrast/agent/assess/property/evented.rb +2 -1
  27. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +3 -4
  28. data/lib/contrast/agent/at_exit_hook.rb +3 -3
  29. data/lib/contrast/agent/class_reopener.rb +6 -5
  30. data/lib/contrast/agent/disable_reaction.rb +4 -5
  31. data/lib/contrast/agent/exclusion_matcher.rb +2 -7
  32. data/lib/contrast/agent/inventory/database_config.rb +117 -0
  33. data/lib/contrast/agent/inventory/dependency_analysis.rb +2 -6
  34. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +8 -9
  35. data/lib/contrast/agent/inventory/policy/datastores.rb +5 -6
  36. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  37. data/lib/contrast/agent/middleware.rb +15 -13
  38. data/lib/contrast/agent/patching/policy/after_load_patch.rb +6 -3
  39. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +21 -16
  40. data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
  41. data/lib/contrast/agent/patching/policy/patch.rb +13 -8
  42. data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
  43. data/lib/contrast/agent/patching/policy/patcher.rb +14 -14
  44. data/lib/contrast/agent/patching/policy/policy.rb +2 -4
  45. data/lib/contrast/agent/patching/policy/policy_node.rb +2 -3
  46. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
  47. data/lib/contrast/agent/protect/policy/policy.rb +1 -1
  48. data/lib/contrast/agent/protect/policy/rule_applicator.rb +3 -5
  49. data/lib/contrast/agent/protect/rule/base.rb +10 -10
  50. data/lib/contrast/agent/protect/rule/cmd_injection.rb +4 -5
  51. data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
  52. data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -5
  53. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
  54. data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
  55. data/lib/contrast/agent/reaction_processor.rb +3 -4
  56. data/lib/contrast/agent/request.rb +9 -5
  57. data/lib/contrast/agent/request_context.rb +28 -31
  58. data/lib/contrast/agent/request_handler.rb +5 -3
  59. data/lib/contrast/agent/response.rb +2 -3
  60. data/lib/contrast/agent/rewriter.rb +4 -3
  61. data/lib/contrast/agent/rule_set.rb +5 -4
  62. data/lib/contrast/agent/service_heartbeat.rb +2 -3
  63. data/lib/contrast/agent/static_analysis.rb +7 -6
  64. data/lib/contrast/agent/thread.rb +2 -4
  65. data/lib/contrast/agent/thread_watcher.rb +3 -4
  66. data/lib/contrast/agent/tracepoint_hook.rb +10 -5
  67. data/lib/contrast/agent/version.rb +1 -1
  68. data/lib/contrast/api/communication/messaging_queue.rb +16 -11
  69. data/lib/contrast/api/communication/response_processor.rb +11 -11
  70. data/lib/contrast/api/communication/service_lifecycle.rb +9 -5
  71. data/lib/contrast/api/communication/socket_client.rb +18 -14
  72. data/lib/contrast/api/communication/speedracer.rb +5 -6
  73. data/lib/contrast/api/decorators/address.rb +2 -3
  74. data/lib/contrast/api/decorators/agent_startup.rb +7 -9
  75. data/lib/contrast/api/decorators/application_startup.rb +9 -10
  76. data/lib/contrast/api/decorators/application_update.rb +0 -4
  77. data/lib/contrast/api/decorators/http_request.rb +3 -7
  78. data/lib/contrast/api/decorators/instrumentation_mode.rb +3 -5
  79. data/lib/contrast/api/decorators/message.rb +7 -7
  80. data/lib/contrast/api/decorators/route_coverage.rb +24 -1
  81. data/lib/contrast/api/decorators/trace_event_object.rb +2 -3
  82. data/lib/contrast/components/agent.rb +13 -15
  83. data/lib/contrast/components/app_context.rb +7 -11
  84. data/lib/contrast/components/assess.rb +19 -16
  85. data/lib/contrast/components/base.rb +40 -0
  86. data/lib/contrast/components/config.rb +1 -2
  87. data/lib/contrast/components/contrast_service.rb +8 -11
  88. data/lib/contrast/components/heap_dump.rb +5 -4
  89. data/lib/contrast/components/inventory.rb +2 -7
  90. data/lib/contrast/components/logger.rb +14 -10
  91. data/lib/contrast/components/protect.rb +10 -13
  92. data/lib/contrast/components/sampling.rb +5 -5
  93. data/lib/contrast/components/scope.rb +9 -32
  94. data/lib/contrast/components/settings.rb +1 -5
  95. data/lib/contrast/config/base_configuration.rb +14 -6
  96. data/lib/contrast/configuration.rb +22 -19
  97. data/lib/contrast/extension/assess/array.rb +3 -15
  98. data/lib/contrast/extension/assess/eval_trigger.rb +2 -23
  99. data/lib/contrast/extension/assess/fiber.rb +6 -16
  100. data/lib/contrast/extension/assess/hash.rb +3 -13
  101. data/lib/contrast/extension/assess/kernel.rb +3 -14
  102. data/lib/contrast/extension/assess/marshal.rb +6 -14
  103. data/lib/contrast/extension/assess/regexp.rb +5 -15
  104. data/lib/contrast/extension/assess/string.rb +6 -31
  105. data/lib/contrast/extension/extension.rb +61 -0
  106. data/lib/contrast/extension/kernel.rb +2 -4
  107. data/lib/contrast/extension/protect/kernel.rb +0 -15
  108. data/lib/contrast/framework/grape/support.rb +174 -0
  109. data/lib/contrast/framework/manager.rb +44 -9
  110. data/lib/contrast/framework/rack/patch/session_cookie.rb +6 -6
  111. data/lib/contrast/framework/rack/support.rb +1 -1
  112. data/lib/contrast/framework/rails/patch/assess_configuration.rb +5 -8
  113. data/lib/contrast/framework/rails/patch/support.rb +44 -37
  114. data/lib/contrast/framework/rails/railtie.rb +34 -0
  115. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +4 -4
  116. data/lib/contrast/framework/rails/support.rb +60 -13
  117. data/lib/contrast/framework/sinatra/support.rb +1 -1
  118. data/lib/contrast/funchook/funchook.rb +4 -3
  119. data/lib/contrast/logger/application.rb +1 -6
  120. data/lib/contrast/logger/log.rb +103 -13
  121. data/lib/contrast/logger/request.rb +0 -4
  122. data/lib/contrast/tasks/config.rb +0 -1
  123. data/lib/contrast/tasks/service.rb +1 -6
  124. data/lib/contrast/utils/assess/sampling_util.rb +2 -3
  125. data/lib/contrast/utils/assess/tracking_util.rb +2 -4
  126. data/lib/contrast/utils/heap_dump_util.rb +5 -3
  127. data/lib/contrast/utils/invalid_configuration_util.rb +4 -3
  128. data/lib/contrast/utils/io_util.rb +3 -5
  129. data/lib/contrast/utils/job_servers_running.rb +4 -3
  130. data/lib/contrast/utils/os.rb +2 -3
  131. data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
  132. data/lib/contrast/utils/string_utils.rb +2 -3
  133. data/lib/contrast/utils/tag_util.rb +26 -19
  134. data/lib/contrast.rb +24 -14
  135. data/resources/assess/policy.json +252 -2
  136. data/resources/deadzone/policy.json +10 -0
  137. data/ruby-agent.gemspec +14 -3
  138. data/service_executables/VERSION +1 -1
  139. data/service_executables/linux/contrast-service +0 -0
  140. data/service_executables/mac/contrast-service +0 -0
  141. metadata +104 -24
  142. data/lib/contrast/agent/railtie.rb +0 -31
  143. data/lib/contrast/components/interface.rb +0 -196
  144. data/lib/contrast/delegators/input_analysis.rb +0 -12
  145. data/lib/contrast/utils/inventory_util.rb +0 -114
@@ -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
- require 'contrast/components/interface'
5
4
  require 'contrast/utils/timer'
6
5
 
7
6
  module Contrast
@@ -9,9 +8,6 @@ module Contrast
9
8
  # Our decorator for the Ougai logger allowing for the logging of the
10
9
  # request lifecycle, used to provide context during troubleshooting.
11
10
  module Request
12
- include Contrast::Components::Interface
13
- access_component :config
14
-
15
11
  # Utility method to log the start of a request
16
12
  def request_start
17
13
  debug('Beginning request analysis')
@@ -7,7 +7,6 @@ module Contrast
7
7
  # A Rake task to generate a contrast_security.yaml file with some basic settings
8
8
  module Config
9
9
  extend Rake::DSL
10
- include Contrast::Components::Interface
11
10
  DEFAULT_CONFIG = {
12
11
  'api' => {
13
12
  'url' => 'Enter your Contrast URL ex: https://app.contrastsecurity.com/Contrast',
@@ -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
- require 'contrast/components/interface'
5
4
  require 'contrast/utils/os'
6
5
 
7
6
  module Contrast
@@ -10,14 +9,10 @@ module Contrast
10
9
  # forked from the application
11
10
  module Service
12
11
  extend Rake::DSL
13
- include Contrast::Components::Interface
14
-
15
- access_component :contrast_service
16
-
17
12
  # Start the service if it is not already running
18
13
  def self.start_service
19
14
  puts 'Starting Contrast Service'
20
- service_log = CONTRAST_SERVICE.logger_path
15
+ service_log = ::Contrast::CONTRAST_SERVICE.logger_path
21
16
  if File.writable?(service_log)
22
17
  spawn('contrast_service', out: File::NULL, err: service_log)
23
18
  else
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'singleton'
5
- require 'contrast/components/interface'
5
+ require 'contrast/components/sampling'
6
6
 
7
7
  module Contrast
8
8
  module Utils
@@ -11,8 +11,7 @@ module Contrast
11
11
  class SamplingUtil
12
12
  include Singleton
13
13
 
14
- include Contrast::Components::Interface
15
- access_component :sampling
14
+ extend Contrast::Components::Sampling::InstanceMethods
16
15
 
17
16
  def initialize
18
17
  @requests = {}
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/assess/tracker'
5
- require 'contrast/components/interface'
5
+ require 'contrast/components/logger'
6
6
  require 'contrast/utils/duck_utils'
7
7
 
8
8
  module Contrast
@@ -10,9 +10,7 @@ module Contrast
10
10
  module Assess
11
11
  # TrackingUtil has methods for determining if a object is being tracked
12
12
  class TrackingUtil
13
- include Contrast::Components::Interface
14
-
15
- access_component :logging
13
+ extend Contrast::Components::Logger::InstanceMethods
16
14
 
17
15
  class << self
18
16
  # Public interface to our tracking check, isolating the internals
@@ -3,14 +3,16 @@
3
3
 
4
4
  require 'objspace'
5
5
  require 'singleton'
6
- require 'contrast/components/interface'
6
+ require 'contrast/components/heap_dump'
7
+ require 'contrast/components/logger'
7
8
 
8
9
  module Contrast
9
10
  module Utils
10
11
  # Implementation of a heap dump util to automate generation
11
12
  class HeapDumpUtil < Contrast::Agent::WorkerThread
12
- include Contrast::Components::Interface
13
- access_component :heap_dump, :logging
13
+ extend Contrast::Components::Logger::InstanceMethods
14
+ include Contrast::Components::Logger::InstanceMethods
15
+ extend Contrast::Components::HeapDump::InstanceMethods
14
16
 
15
17
  LOG_ERROR_DUMPS = 'Unable to generate heap dumps'
16
18
  FILE_WRITE_FLAGS = 'w'
@@ -2,15 +2,16 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/assess/policy/trigger_method'
5
- require 'contrast/components/interface'
5
+ require 'contrast/components/logger'
6
+ require 'contrast/components/scope'
6
7
 
7
8
  module Contrast
8
9
  module Utils
9
10
  # This utility allows us to report invalid configurations detected in
10
11
  # customer applications, as determined by Configuration Rules at runtime.
11
12
  module InvalidConfigurationUtil
12
- include Contrast::Components::Interface
13
- access_component :analysis, :app_context, :logging, :scope
13
+ include Contrast::Components::Logger::InstanceMethods
14
+ include Contrast::Components::Scope::InstanceMethods
14
15
 
15
16
  CS__PATH = 'path'
16
17
  CS__SESSION_ID = 'sessionId'
@@ -1,15 +1,13 @@
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
- require 'contrast/components/interface'
4
+ require 'contrast/components/logger'
5
5
 
6
6
  module Contrast
7
7
  module Utils
8
8
  # Util for information about an IO
9
- class IOUtil
10
- include Contrast::Components::Interface
11
-
12
- access_component :logging
9
+ module IOUtil
10
+ extend Contrast::Components::Logger::InstanceMethods
13
11
 
14
12
  # We're only going to call rewind on things that we believe are safe to
15
13
  # call it on. This method white lists those cases and returns false in
@@ -1,13 +1,14 @@
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
+ require 'contrast/components/logger'
5
+
4
6
  module Contrast
5
7
  module Utils
6
8
  # A module that detects whether any job servers attached to
7
9
  # the application are running
8
10
  module JobServersRunning
9
- include Contrast::Components::Interface
10
- access_component :app_context, :logging
11
+ extend Contrast::Components::Logger::InstanceMethods
11
12
 
12
13
  class << self
13
14
  def job_servers_running?
@@ -31,7 +32,7 @@ module Contrast
31
32
  return
32
33
  end
33
34
 
34
- disabled_rake_tasks = APP_CONTEXT.disabled_agent_rake_tasks
35
+ disabled_rake_tasks = ::Contrast::APP_CONTEXT.disabled_agent_rake_tasks
35
36
  has_disabled_task = Rake.application.top_level_tasks.any? do |top_level_task|
36
37
  disabled_rake_tasks.include?(top_level_task)
37
38
  end
@@ -1,7 +1,7 @@
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
- require 'contrast/components/interface'
4
+ require 'contrast/components/scope'
5
5
 
6
6
  module Contrast
7
7
  module Utils
@@ -9,8 +9,7 @@ module Contrast
9
9
  # which will not change at runtime, such as the operating system, the
10
10
  # Utility memozies to avoid multiple lookups.
11
11
  module OS
12
- include Contrast::Components::Interface
13
- access_component :scope
12
+ extend Contrast::Components::Scope::InstanceMethods
14
13
 
15
14
  class << self
16
15
  def running?
@@ -36,36 +36,39 @@ module Contrast
36
36
  # the replace within the given node.
37
37
  def on_dstr node
38
38
  return if node.children.all? { |child_node| child_node.type == :str }
39
-
40
39
  new_content = +'('
41
- node.children.each_with_index do |child_node, index|
40
+ idx = 0
41
+ while idx < node.children.size
42
+ #node.children.each_with_index do |child_node, index|
43
+ child_node = node.children[idx]
42
44
  # A begin node looks like #{some_code} in ruby, we do a substring
43
45
  # from [2...-1] to get rid of the #{ & trailing }.
44
46
  if child_node.type == :begin
45
47
  code = child_node.
46
- location.
47
- expression.
48
- source_buffer.
49
- source[child_node.location.begin.begin_pos...child_node.location.end.end_pos]
48
+ location.
49
+ expression.
50
+ source_buffer.
51
+ source[child_node.location.begin.begin_pos...child_node.location.end.end_pos]
50
52
  code = code[2...-1]
51
53
  new_content += "((#{ code }).to_s)"
52
54
 
53
- # For interpolations that use class, instance, or global variables,
54
- # those are NOT within a begin block, but instead are a ivar, cvar,
55
- # or gvar node, not stripping of interpolation markers required.
55
+ # For interpolations that use class, instance, or global variables,
56
+ # those are NOT within a begin block, but instead are a ivar, cvar,
57
+ # or gvar node, not stripping of interpolation markers required.
56
58
  elsif VARIABLES.include?(child_node.type)
57
59
  variable = child_node.children.first
58
60
  new_content << "((#{ variable }).to_s)"
59
61
 
60
- # When interpolation includes strings before or after an
61
- # interpolation they are simple :str nodes, in order to preserve
62
- # escaping we need to do a dump of the string value.
62
+ # When interpolation includes strings before or after an
63
+ # interpolation they are simple :str nodes, in order to preserve
64
+ # escaping we need to do a dump of the string value.
63
65
  elsif child_node.type == :str
64
66
  literal_value = child_node.children.first
65
67
  literal_value = literal_value.dump[1...-1]
66
68
  new_content << "\"#{ literal_value }\""
67
69
  end
68
- new_content << ' + ' unless index == node.children.length - 1
70
+ new_content << ' + ' unless idx == node.children.length - 1
71
+ idx += 1
69
72
  end
70
73
  new_content << ')'
71
74
  if node.location.cs__respond_to?(:heredoc_body)
@@ -1,14 +1,13 @@
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
- require 'contrast/components/interface'
4
+ require 'contrast/components/logger'
5
5
 
6
6
  module Contrast
7
7
  module Utils
8
8
  # Utilities for encoding and normalizing strings
9
9
  class StringUtils
10
- include Contrast::Components::Interface
11
- access_component :logging
10
+ include Contrast::Components::Logger::InstanceMethods
12
11
 
13
12
  UTF8 = 'utf-8'
14
13
  HTTP_PREFIX = 'HTTP_'
@@ -7,8 +7,10 @@ module Contrast
7
7
  class TagUtil
8
8
  class << self
9
9
  # Determine if the given array of tags is covered by the other
10
- # remaining_ranges: the tags left that haven't been covered by those given
11
- # ranges: the tags that are covering the first
10
+ #
11
+ # @param remaining_ranges [Array<Contrast::Agent::Assess::Tag>] the tags left that haven't been covered by
12
+ # those given
13
+ # @param ranges Array<Contrast::Agent::Assess::Tag> the tags that are covering the first
12
14
  def covered? remaining_ranges, ranges
13
15
  return true unless remaining_ranges&.any?
14
16
 
@@ -74,14 +76,14 @@ module Contrast
74
76
 
75
77
  # Given a collection of tags, merge any tags that are continuous
76
78
  #
77
- # If tags is a hash, it should be in the format label => [tags]
78
- # The array of tags will each be merged
79
+ # If tags is a hash, it should be in the format label => [tags]. The array of tags will each be merged
80
+ # If tags is an array in the format [tags], the array will be merged
79
81
  #
80
- # If tags is an array in the format [tags], the array will be
81
- # merged
82
+ # The original object is returned, although setters should not be necessary since tags is a collection in
83
+ # either case
82
84
  #
83
- # The original object is returned, although setters should not be
84
- # necessary since tags is a collection in either case
85
+ # @param tags [Hash{String => Array<Contrast::Agent::Assess::Tag>}, Array<Contrast::Agent::Assess::Tag>]
86
+ # @return [Hash{String => Array<Contrast::Agent::Assess::Tag>}, Array<Contrast::Agent::Assess::Tag>]
85
87
  def merge_tags tags
86
88
  if tags.is_a?(Hash)
87
89
  tags.each_value { |value| smallerize(value) }
@@ -90,6 +92,12 @@ module Contrast
90
92
  end
91
93
  end
92
94
 
95
+ # Merge the given set of tags such that any overlap combines. For any tag which extends beyond the size of the
96
+ # target_object, the end will be updated to the target_object's length.
97
+ #
98
+ # @param target_object [Object] the thing to which the tags apply
99
+ # @param tags [Hash{String => Array<Contrast::Agent::Assess::Tag>}, Array<Contrast::Agent::Assess::Tag>]
100
+ # @return [Hash{String => Array<Contrast::Agent::Assess::Tag>}, Array<Contrast::Agent::Assess::Tag>]
93
101
  def size_aware_merge target_object, tags
94
102
  max_size = target_object.to_s.length
95
103
  tags = merge_tags(tags)
@@ -100,14 +108,12 @@ module Contrast
100
108
 
101
109
  private
102
110
 
103
- # Add one new element to the given array
111
+ # Add one new element to the given array. The addition is done such that the new entry is inserted so that the
112
+ # range they cover is in order. Any overlapping ranges are merged before returning.
104
113
  #
105
- # The addition is done such that the new entry(ies)
106
- # are inserted so that the range they cover is in order
107
- # Any overlapping ranges are merged before returning
108
- #
109
- # arr: the array to which the element is added
110
- # new_element: the element to be added to the array
114
+ # @param arr [Array<Contrast::Agent::Assess::Tag>]
115
+ # @param new_element []Contrast::Agent::Assess::Tag]
116
+ # @return [Array<Contrast::Agent::Assess::Tag>]
111
117
  def single_ordered_merge arr, new_element
112
118
  idx = 0
113
119
  arr.each do |existing|
@@ -122,10 +128,11 @@ module Contrast
122
128
  arr.insert(idx, new_element)
123
129
  end
124
130
 
125
- # Given an arry of tags, merge any that overlap
126
- # The tag that was higher up is removed from the
127
- # list of tags.
128
- # ranges like [0-3][3-6]-6-9] that should become [0-9]
131
+ # Given an arry of tags, merge any that overlap. The tag that was higher up is removed from the list of tags.
132
+ # ranges like [0-3][3-6]-6-9] become [0-9]
133
+ #
134
+ # @param tags [Array<Contrast::Agent::Assess::Tag>]
135
+ # @return [Array<Contrast::Agent::Assess::Tag>]
129
136
  def smallerize tags
130
137
  smallered = []
131
138
  curr = nil
data/lib/contrast.rb CHANGED
@@ -4,10 +4,6 @@
4
4
  # Used to prevent deprecation warnings from flooding stdout
5
5
  ENV['PB_IGNORE_DEPRECATIONS'] = 'true'
6
6
 
7
- # Top-level namespace for Contrast Security agent
8
- module Contrast
9
- end
10
-
11
7
  # Some developers override various methods on Object, which can often involve
12
8
  # changing expected method parity/behavior which in turn prevents us from being
13
9
  # able to reliably use affected methods.
@@ -38,22 +34,36 @@ if RUBY_VERSION >= '3.0.0'
38
34
  end
39
35
  end
40
36
 
41
- # component interface for class creation
42
- # config gets built as a consequence of this require
43
- require 'contrast/components/interface'
37
+ require 'contrast/components/agent'
38
+ require 'contrast/components/app_context'
39
+ require 'contrast/components/assess'
40
+ require 'contrast/components/config'
41
+ require 'contrast/components/contrast_service'
42
+ require 'contrast/components/inventory'
43
+ require 'contrast/components/logger'
44
+ require 'contrast/components/protect'
45
+ require 'contrast/components/sampling'
46
+ require 'contrast/components/scope'
47
+ require 'contrast/components/settings'
48
+
49
+ module Contrast
50
+ SCOPE = Contrast::Components::Scope::Interface.new
51
+ CONFIG = Contrast::Components::Config::Interface.new
52
+ SETTINGS = Contrast::Components::Settings::Interface.new
53
+ ASSESS = Contrast::Components::Assess::Interface.new
54
+ PROTECT = Contrast::Components::Protect::Interface.new
55
+ INVENTORY = Contrast::Components::Inventory::Interface.new
56
+ LOGGER = Contrast::Components::Logger::Interface.new
57
+ AGENT = Contrast::Components::Agent::Interface.new
58
+ CONTRAST_SERVICE = Contrast::Components::ContrastService::Interface.new
59
+ APP_CONTEXT = Contrast::Components::AppContext::Interface.new
60
+ end
44
61
 
45
62
  # This needs to be required very early, after component interfaces, and before instrumentation attempts
46
63
  require 'contrast/funchook/funchook'
47
64
 
48
- # shared configuration support
49
- require 'contrast/config'
50
- require 'contrast/configuration'
51
-
52
65
  require 'contrast/agent/version'
53
66
 
54
- # errors and exceptions
55
- require 'contrast/security_exception'
56
-
57
67
  # shared utils
58
68
  require 'contrast/utils/timer'
59
69
  require 'contrast/utils/preflight_util'
@@ -34,6 +34,23 @@
34
34
  "type": "BODY",
35
35
  "tags":["NO_NEWLINES", "CROSS_SITE"]
36
36
  }, {
37
+ "class_name":"ActionDispatch::Request",
38
+ "instance_method": true,
39
+ "method_visibility": "public",
40
+ "method_name": "body",
41
+ "source": "P0",
42
+ "target": "R",
43
+ "type": "BODY",
44
+ "tags":["NO_NEWLINES", "CROSS_SITE"]
45
+ }, {
46
+ "class_name":"ActionDispatch::Cookies::CookieJar",
47
+ "instance_method": true,
48
+ "method_visibility": "public",
49
+ "method_name": "[]",
50
+ "target": "R",
51
+ "type": "COOKIE",
52
+ "tags":["NO_NEWLINES", "CROSS_SITE"]
53
+ }, {
37
54
  "class_name":"Rack::Request::Helpers",
38
55
  "instance_method": true,
39
56
  "method_visibility": "public",
@@ -129,10 +146,45 @@
129
146
  "target":"R",
130
147
  "type":"PARAMETER",
131
148
  "tags":["CROSS_SITE"]
149
+ }, {
150
+ "class_name":"Grape::Env",
151
+ "instance_method": true,
152
+ "method_visibility": "public",
153
+ "method_name":"[]",
154
+ "source": "P0",
155
+ "target":"R",
156
+ "type":"HEADER",
157
+ "tags":["CROSS_SITE"]
158
+ }, {
159
+ "class_name":"Grape::Request",
160
+ "instance_method": true,
161
+ "method_visibility": "public",
162
+ "method_name":"headers",
163
+ "source": "P0",
164
+ "target":"R",
165
+ "type":"HEADER",
166
+ "tags":["NO_NEWLINES", "CROSS_SITE"]
167
+ }, {
168
+ "class_name":"Grape::Request",
169
+ "instance_method": true,
170
+ "method_visibility": "public",
171
+ "method_name":"body",
172
+ "target":"R",
173
+ "type":"BODY",
174
+ "tags":["CROSS_SITE"]
175
+ }, {
176
+ "class_name":"Grape::Validations::Base",
177
+ "instance_method": true,
178
+ "method_visibility": "public",
179
+ "method_name":"validate!",
180
+ "source": "P0",
181
+ "target":"R",
182
+ "type":"PARAMETER",
183
+ "tags":["CROSS_SITE"]
132
184
  }
133
185
  ],
134
186
  "propagators":[
135
- {
187
+ {
136
188
  "class_name":"String",
137
189
  "instance_method": true,
138
190
  "method_visibility": "public",
@@ -140,7 +192,7 @@
140
192
  "source":"O",
141
193
  "target":"R",
142
194
  "action":"KEEP"
143
- }, {
195
+ }, {
144
196
  "class_name": "String",
145
197
  "instance_method": true,
146
198
  "method_visibility": "public",
@@ -722,6 +774,24 @@
722
774
  "patch_method": "select_tagger",
723
775
  "source": "O",
724
776
  "target": "R"
777
+ },{
778
+ "class_name":"CGI::Util",
779
+ "method_name":"unescape",
780
+ "instance_method": true,
781
+ "method_visibility": "public",
782
+ "source":"P0",
783
+ "target":"R",
784
+ "action":"SPLAT",
785
+ "tags":[],
786
+ "untags":[]
787
+ }, {
788
+ "class_name":"StringIO",
789
+ "instance_method": true,
790
+ "method_visibility": "public",
791
+ "method_name": "read",
792
+ "source": "O",
793
+ "target": "R",
794
+ "action": "SPLAT"
725
795
  }, {
726
796
  "class_name":"CGI::Util",
727
797
  "method_name":"escapeHTML",
@@ -742,6 +812,16 @@
742
812
  "action":"SPLAT",
743
813
  "tags":["HTML_ENCODED"],
744
814
  "untags":["HTML_DECODED"]
815
+ }, {
816
+ "class_name":"Rack::Utils",
817
+ "method_name":"escape_html",
818
+ "instance_method": false,
819
+ "method_visibility": "public",
820
+ "source":"P0",
821
+ "target":"R",
822
+ "action":"SPLAT",
823
+ "tags":["HTML_ENCODED"],
824
+ "untags":["HTML_DECODED"]
745
825
  }, {
746
826
  "class_name":"CGI::Util",
747
827
  "method_name":"h",
@@ -1026,6 +1106,61 @@
1026
1106
  "action": "CUSTOM",
1027
1107
  "patch_class": "ERBPropagator",
1028
1108
  "patch_method": "result_tagger"
1109
+ },
1110
+ {
1111
+ "class_name": "ActionView::Helpers::SanitizeHelper",
1112
+ "method_name": "sanitize",
1113
+ "method_visibility": "public",
1114
+ "instance_method": true,
1115
+ "source": "P0",
1116
+ "target": "R",
1117
+ "action": "REMOVE",
1118
+ "tags":["HTML_ENCODED"],
1119
+ "untags":["HTML_DECODED"]
1120
+ },
1121
+ {
1122
+ "class_name": "ActionView::Helpers::SanitizeHelper",
1123
+ "method_name": "strip_tags",
1124
+ "method_visibility": "public",
1125
+ "instance_method": true,
1126
+ "source": "P0",
1127
+ "target": "R",
1128
+ "action": "REMOVE",
1129
+ "tags":["HTML_ENCODED"],
1130
+ "untags":["HTML_DECODED"]
1131
+ },
1132
+ {
1133
+ "class_name": "Rack::Protection::EscapedParams",
1134
+ "method_name": "escape_string",
1135
+ "method_visibility": "public",
1136
+ "instance_method": true,
1137
+ "source": "P0",
1138
+ "target": "R",
1139
+ "action": "CUSTOM",
1140
+ "patch_class": "Contrast::Agent::Assess::Policy::Propagator::RackProtection",
1141
+ "patch_method": "escaped_params"
1142
+ },
1143
+ {
1144
+ "class_name": "Rails::Html::FullSanitizer",
1145
+ "method_name": "sanitize",
1146
+ "method_visibility": "public",
1147
+ "instance_method": true,
1148
+ "source": "P0",
1149
+ "target": "R",
1150
+ "action": "REMOVE",
1151
+ "tags":["HTML_ENCODED"],
1152
+ "untags":["HTML_DECODED"]
1153
+ },
1154
+ {
1155
+ "class_name": "Rails::Html::SafeListSanitizer",
1156
+ "method_name": "sanitize",
1157
+ "method_visibility": "public",
1158
+ "instance_method": true,
1159
+ "source": "P0",
1160
+ "target": "R",
1161
+ "action": "REMOVE",
1162
+ "tags":["HTML_ENCODED"],
1163
+ "untags":["HTML_DECODED"]
1029
1164
  }
1030
1165
  ],
1031
1166
  "rules":[
@@ -1232,6 +1367,18 @@
1232
1367
  "instance_method": true,
1233
1368
  "method_visibility": "public",
1234
1369
  "source":"P0"
1370
+ }, {
1371
+ "class_name":"Rack::Response",
1372
+ "method_name":"body=",
1373
+ "instance_method": true,
1374
+ "method_visibility": "public",
1375
+ "source":"P0"
1376
+ }, {
1377
+ "class_name":"Rack::Response",
1378
+ "method_name":"write",
1379
+ "instance_method": true,
1380
+ "method_visibility": "public",
1381
+ "source":"P0"
1235
1382
  }, {
1236
1383
  "class_name":"Sinatra::Helpers",
1237
1384
  "method_name":"body",
@@ -1292,12 +1439,108 @@
1292
1439
  "method_visibility": "public",
1293
1440
  "method_name":"async_exec",
1294
1441
  "source":"P0"
1442
+ }, {
1443
+ "class_name":"ActiveRecord::Relation::Calculations",
1444
+ "instance_method": true,
1445
+ "method_visibility": "public",
1446
+ "method_name":"calculate",
1447
+ "source":"P0"
1448
+ }, {
1449
+ "class_name":"ActiveRecord::FinderMethods",
1450
+ "instance_method": true,
1451
+ "method_visibility": "public",
1452
+ "method_name":"exists?",
1453
+ "source":"P0"
1454
+ }, {
1455
+ "class_name":"ActiveRecord::FinderMethods",
1456
+ "instance_method": true,
1457
+ "method_visibility": "public",
1458
+ "method_name":"find_by",
1459
+ "source":"P0"
1295
1460
  }, {
1296
1461
  "class_name":"ActiveRecord::Querying",
1297
1462
  "instance_method": false,
1298
1463
  "method_visibility": "public",
1299
1464
  "method_name":"select",
1300
1465
  "source":"P0"
1466
+ }, {
1467
+ "class_name":"ActiveRecord::QueryMethods",
1468
+ "instance_method": true,
1469
+ "method_visibility": "public",
1470
+ "method_name":"from",
1471
+ "source":"P0"
1472
+ }, {
1473
+ "class_name":"ActiveRecord::QueryMethods",
1474
+ "instance_method": true,
1475
+ "method_visibility": "public",
1476
+ "method_name":"group",
1477
+ "source":"P0"
1478
+ }, {
1479
+ "class_name":"ActiveRecord::QueryMethods",
1480
+ "instance_method": true,
1481
+ "method_visibility": "public",
1482
+ "method_name":"having",
1483
+ "source":"P0"
1484
+ }, {
1485
+ "class_name":"ActiveRecord::QueryMethods",
1486
+ "instance_method": true,
1487
+ "method_visibility": "public",
1488
+ "method_name":"joins",
1489
+ "source":"P0"
1490
+ }, {
1491
+ "class_name":"ActiveRecord::QueryMethods",
1492
+ "instance_method": true,
1493
+ "method_visibility": "public",
1494
+ "method_name":"lock",
1495
+ "source":"P0"
1496
+ }, {
1497
+ "class_name":"ActiveRecord::QueryMethods",
1498
+ "instance_method": true,
1499
+ "method_visibility": "public",
1500
+ "method_name":"select",
1501
+ "source":"P0"
1502
+ }, {
1503
+ "class_name":"ActiveRecord::QueryMethods",
1504
+ "instance_method": true,
1505
+ "method_visibility": "public",
1506
+ "method_name":"reselect",
1507
+ "source":"P0"
1508
+ }, {
1509
+ "class_name":"ActiveRecord::QueryMethods",
1510
+ "instance_method": true,
1511
+ "method_visibility": "public",
1512
+ "method_name":"where",
1513
+ "source":"P0"
1514
+ }, {
1515
+ "class_name":"ActiveRecord::QueryMethods",
1516
+ "instance_method": true,
1517
+ "method_visibility": "public",
1518
+ "method_name":"rewhere",
1519
+ "source":"P0"
1520
+ }, {
1521
+ "class_name":"ActiveRecord::QueryMethods::WhereChain",
1522
+ "instance_method": true,
1523
+ "method_visibility": "public",
1524
+ "method_name":"not",
1525
+ "source":"P0"
1526
+ }, {
1527
+ "class_name":"ActiveRecord::Relation",
1528
+ "instance_method": true,
1529
+ "method_visibility": "public",
1530
+ "method_name":"delete_by",
1531
+ "source":"P0"
1532
+ }, {
1533
+ "class_name":"ActiveRecord::Relation",
1534
+ "instance_method": true,
1535
+ "method_visibility": "public",
1536
+ "method_name":"destroy_by",
1537
+ "source":"P0"
1538
+ }, {
1539
+ "class_name":"ActiveRecord::Relation",
1540
+ "instance_method": true,
1541
+ "method_visibility": "public",
1542
+ "method_name":"update_all",
1543
+ "source":"P0"
1301
1544
  }
1302
1545
  ]
1303
1546
  }, {
@@ -1630,6 +1873,13 @@
1630
1873
  "method_visibility": "public",
1631
1874
  "method_name": "redirect_to",
1632
1875
  "source": "P0"
1876
+ },
1877
+ {
1878
+ "class_name": "Grape::DSL::InsideRoute",
1879
+ "instance_method": true,
1880
+ "method_visibility": "public",
1881
+ "method_name": "redirect",
1882
+ "source": "P0"
1633
1883
  }
1634
1884
  ]
1635
1885
  }, {