contrast-agent 4.2.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) 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 +5 -1
  6. data/lib/contrast/agent/assess.rb +0 -9
  7. data/lib/contrast/agent/assess/contrast_event.rb +49 -132
  8. data/lib/contrast/agent/assess/contrast_object.rb +54 -0
  9. data/lib/contrast/agent/assess/events/source_event.rb +4 -9
  10. data/lib/contrast/agent/assess/finalizers/hash.rb +7 -0
  11. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +17 -3
  12. data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
  13. data/lib/contrast/agent/assess/policy/policy_node.rb +31 -59
  14. data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +41 -32
  16. data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
  17. data/lib/contrast/agent/assess/policy/propagator/append.rb +29 -15
  18. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
  19. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  20. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +21 -18
  21. data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -2
  22. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
  23. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
  24. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
  25. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
  26. data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
  27. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
  28. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
  29. data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -4
  30. data/lib/contrast/agent/assess/policy/propagator/splat.rb +25 -17
  31. data/lib/contrast/agent/assess/policy/propagator/split.rb +83 -120
  32. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +41 -25
  33. data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
  34. data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
  35. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -8
  36. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  37. data/lib/contrast/agent/assess/policy/trigger_method.rb +13 -8
  38. data/lib/contrast/agent/assess/policy/trigger_node.rb +28 -7
  39. data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +59 -0
  40. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -3
  41. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +6 -4
  42. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +2 -4
  43. data/lib/contrast/agent/assess/properties.rb +0 -2
  44. data/lib/contrast/agent/assess/property/tagged.rb +56 -32
  45. data/lib/contrast/agent/assess/tracker.rb +16 -18
  46. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
  47. data/lib/contrast/agent/middleware.rb +134 -55
  48. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +4 -0
  49. data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
  50. data/lib/contrast/agent/patching/policy/patch.rb +4 -4
  51. data/lib/contrast/agent/patching/policy/patch_status.rb +1 -1
  52. data/lib/contrast/agent/patching/policy/patcher.rb +51 -44
  53. data/lib/contrast/agent/patching/policy/trigger_node.rb +5 -2
  54. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
  55. data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
  56. data/lib/contrast/agent/protect/rule/base.rb +63 -14
  57. data/lib/contrast/agent/protect/rule/cmd_injection.rb +3 -3
  58. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
  59. data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
  60. data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
  61. data/lib/contrast/agent/protect/rule/sqli.rb +20 -14
  62. data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
  63. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
  64. data/lib/contrast/agent/reaction_processor.rb +1 -1
  65. data/lib/contrast/agent/request_context.rb +12 -0
  66. data/lib/contrast/agent/response.rb +5 -5
  67. data/lib/contrast/agent/rewriter.rb +3 -3
  68. data/lib/contrast/agent/scope.rb +33 -13
  69. data/lib/contrast/agent/static_analysis.rb +13 -7
  70. data/lib/contrast/agent/thread.rb +1 -1
  71. data/lib/contrast/agent/thread_watcher.rb +20 -5
  72. data/lib/contrast/agent/version.rb +1 -1
  73. data/lib/contrast/api/communication/messaging_queue.rb +18 -21
  74. data/lib/contrast/api/communication/response_processor.rb +8 -1
  75. data/lib/contrast/api/communication/socket_client.rb +22 -14
  76. data/lib/contrast/api/decorators.rb +2 -0
  77. data/lib/contrast/api/decorators/agent_startup.rb +58 -0
  78. data/lib/contrast/api/decorators/application_startup.rb +51 -0
  79. data/lib/contrast/api/decorators/library.rb +1 -0
  80. data/lib/contrast/api/decorators/library_usage_update.rb +1 -0
  81. data/lib/contrast/api/decorators/route_coverage.rb +15 -5
  82. data/lib/contrast/api/decorators/trace_event.rb +58 -42
  83. data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
  84. data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
  85. data/lib/contrast/api/decorators/user_input.rb +2 -1
  86. data/lib/contrast/common_agent_configuration.rb +1 -1
  87. data/lib/contrast/components/agent.rb +2 -0
  88. data/lib/contrast/components/app_context.rb +4 -22
  89. data/lib/contrast/components/assess.rb +36 -0
  90. data/lib/contrast/components/interface.rb +5 -3
  91. data/lib/contrast/components/sampling.rb +48 -6
  92. data/lib/contrast/components/scope.rb +23 -0
  93. data/lib/contrast/components/settings.rb +8 -7
  94. data/lib/contrast/config/assess_configuration.rb +2 -1
  95. data/lib/contrast/extension/assess/array.rb +1 -2
  96. data/lib/contrast/extension/assess/erb.rb +1 -3
  97. data/lib/contrast/extension/assess/exec_trigger.rb +1 -1
  98. data/lib/contrast/extension/assess/fiber.rb +2 -3
  99. data/lib/contrast/extension/assess/hash.rb +4 -2
  100. data/lib/contrast/extension/assess/kernel.rb +1 -2
  101. data/lib/contrast/extension/assess/marshal.rb +34 -26
  102. data/lib/contrast/extension/assess/regexp.rb +3 -8
  103. data/lib/contrast/extension/assess/string.rb +1 -2
  104. data/lib/contrast/framework/base_support.rb +51 -53
  105. data/lib/contrast/framework/manager.rb +16 -14
  106. data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
  107. data/lib/contrast/framework/rack/support.rb +2 -1
  108. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
  109. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
  110. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +1 -1
  111. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +1 -1
  112. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +1 -1
  113. data/lib/contrast/framework/rails/support.rb +44 -44
  114. data/lib/contrast/framework/sinatra/support.rb +102 -42
  115. data/lib/contrast/logger/application.rb +0 -3
  116. data/lib/contrast/logger/log.rb +31 -15
  117. data/lib/contrast/utils/class_util.rb +3 -1
  118. data/lib/contrast/utils/duck_utils.rb +1 -1
  119. data/lib/contrast/utils/heap_dump_util.rb +103 -87
  120. data/lib/contrast/utils/invalid_configuration_util.rb +21 -12
  121. data/lib/contrast/utils/object_share.rb +3 -3
  122. data/lib/contrast/utils/preflight_util.rb +1 -1
  123. data/lib/contrast/utils/resource_loader.rb +1 -1
  124. data/lib/contrast/utils/sha256_builder.rb +2 -2
  125. data/lib/contrast/utils/string_utils.rb +1 -1
  126. data/lib/contrast/utils/tag_util.rb +9 -13
  127. data/resources/assess/policy.json +12 -18
  128. data/resources/deadzone/policy.json +150 -0
  129. data/resources/protect/policy.json +12 -0
  130. data/ruby-agent.gemspec +60 -19
  131. data/service_executables/VERSION +1 -1
  132. data/service_executables/linux/contrast-service +0 -0
  133. data/service_executables/mac/contrast-service +0 -0
  134. metadata +124 -112
  135. data/lib/contrast/agent/assess/rule.rb +0 -18
  136. data/lib/contrast/agent/assess/rule/base.rb +0 -52
  137. data/lib/contrast/agent/assess/rule/redos.rb +0 -67
  138. data/lib/contrast/framework/sinatra/patch/base.rb +0 -83
  139. data/lib/contrast/framework/sinatra/patch/support.rb +0 -27
  140. data/lib/contrast/utils/prevent_serialization.rb +0 -52
@@ -1,18 +0,0 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- module Contrast
5
- module Agent
6
- module Assess
7
- # This is the base module for our assess rule classes. It is intended to
8
- # facilitate the patching of the application for Assess functionality. Any
9
- # class under this namespace should be required here, providing a single
10
- # point of require for this functionality.
11
- module Rule
12
- end
13
- end
14
- end
15
- end
16
-
17
- require 'contrast/agent/assess/rule/base'
18
- require 'contrast/agent/assess/rule/provider'
@@ -1,52 +0,0 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'digest'
5
- require 'zlib'
6
- require 'contrast/components/interface'
7
-
8
- module Contrast
9
- module Agent
10
- module Assess
11
- module Rule
12
- # The base class for each of our Assess Rules
13
- class Base
14
- include Contrast::Components::Interface
15
- access_component :agent, :analysis, :logging, :settings
16
-
17
- def initialize
18
- SETTINGS.assess_rules[name] = self
19
- end
20
-
21
- # Should return the name as it is known to Teamserver; defaults to
22
- # class
23
- def name
24
- cs__class.name
25
- end
26
-
27
- def enabled?
28
- ASSESS.enabled? && !ASSESS.rule_disabled?(name)
29
- end
30
-
31
- def prefilter _context; end
32
-
33
- # If a rule needs to inspect the response body it is not stream safe
34
- # The rule should override this and return false
35
- def stream_safe?
36
- true
37
- end
38
-
39
- def postfilter _context; end
40
-
41
- # this rule is excluded if any of the given exclusions have a
42
- # protection rule that matches this rule name
43
- def excluded? exclusions
44
- Array(exclusions).any? do |ex|
45
- ex.assess_rule?(name)
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,67 +0,0 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- module Contrast
5
- module Agent
6
- module Assess
7
- module Rule
8
- # A regexp is only vulnerable to REDOS if it's going to run
9
- # with pathologically bad performance.
10
- # We report a vulnerability if the regexp is liable to run
11
- # with quadratic time for some input.
12
- # This vastly errs on the side of false positives.
13
- class Redos < Contrast::Agent::Assess::Rule::Base
14
- class << self
15
- NAME = 'redos'
16
- def name
17
- NAME
18
- end
19
-
20
- def regexp_complexity_check context, trigger_node, source, object, ret, *args
21
- # we can arrive here either from:
22
- # regexp =~ string
23
- # string =~ regexp
24
- # regexp.match string
25
- #
26
- # so object/args[0] can be string/regexp or regexp/string.
27
- regexp = object.is_a?(Regexp) ? object : args[0]
28
- string = object.is_a?(String) ? object : args[0]
29
-
30
- # (1) regexp must be exploitable
31
- return unless regexp_vulnerable?(regexp)
32
-
33
- # (2) regexp must evaluate against user input
34
- return unless trigger_node.violated?(string)
35
-
36
- Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(context, trigger_node, source, object, ret, args)
37
- end
38
-
39
- protected
40
-
41
- VULNERABLE_PATTERN = /[\[(].*?[\[(].*?[\])][*+?].*?[\])][*+?]/.cs__freeze
42
-
43
- # Does the regexp
44
- # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/redos.md
45
- def regexp_vulnerable? regexp
46
- # A pattern is considered vulnerable if it has 2 or more levels of nested multi-matching.
47
- # A level is defined as any set of opening and closing control characters immediately followed by a multi match control character.
48
- # A control character is defined as one of the OPENING_CHARS, CLOSING_CHARS,
49
- # or MULTI_MATCH_CHARS that is not immediately preceded by an escaping \ character.
50
- # OPENING_CHARS are ( and [ CLOSING_CHARS are ) and ] MULTI_MATCH_CHARS are +, *, and ?
51
-
52
- # Nota bene about Regexp#to_s: it doesn't necessarily give you the original Regexp back
53
- # (in the sense of `my_str == Regexp.new(my_str).to_s`), it gives you a Regexp that
54
- # will have the same functional characteristics as the original.
55
- # Regexp#inspect gives you a "more nicely formatted" version than #to_s.
56
- # Regexp#source will give you the original source.
57
-
58
- # Use #match? because it doesn't fill out global variables
59
- # in the way match or =~ do.
60
- VULNERABLE_PATTERN.match? regexp.source
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,83 +0,0 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'contrast/framework/sinatra/support'
5
-
6
- module Contrast
7
- module Framework
8
- module Sinatra
9
- module Patch
10
- # Our patch into the Sinatra::Base Class, allowing for the inventory of the
11
- # routes called by the application
12
- module Base
13
- class << self
14
- # Use logic copied from Sinatra::Base#process_route to determine which
15
- # pattern matches the request being invoked by Sinatra::Base#call!
16
- #
17
- # @param clazz [Class] the Class that extends Sinatra::Base in
18
- # which this route is defined.
19
- # @param settings [Object] the Sinatra::Base @settings object.
20
- # @param args [Array<Object>] we rely on the @settings object in
21
- # Sinatra::Base and the env variable passed in as args[0].
22
- def map_route clazz, settings, *args
23
- context = Contrast::Agent::REQUEST_TRACKER.current
24
- return unless context
25
-
26
- env = args[0]
27
- return unless env
28
- return unless settings
29
-
30
- convert_routes(context, clazz, settings, env)
31
- rescue StandardError
32
- # Being careful here since we're directly patching something
33
- end
34
-
35
- def instrument
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
48
- end
49
-
50
- private
51
-
52
- # Find the route that matches this request. Since we're using
53
- # settings, we should resolve in the same precedence as Sinatra
54
- def convert_routes context, clazz, settings, env
55
- # There isn't a Request object in the Sinatra::Base yet - it's made
56
- # during the #call! method, which we're patching - so we need to
57
- # access the env
58
- method = env[::Rack::REQUEST_METHOD]
59
- # get all potential routes for this request type
60
- routes = settings.routes[method]
61
- return unless routes
62
-
63
- route = env[::Rack::PATH_INFO]
64
- route = route.to_s
65
-
66
- # Do some cleanup that matches Sinatra::Base#process_route
67
- route = '/' if route.empty? && !settings.empty_path_info?
68
- route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
69
-
70
- routes.each do |pattern, _, _| # Mustermann::Sinatra
71
- next unless pattern.params(route)
72
-
73
- dtm = Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(clazz, method, pattern)
74
- context.append_route_coverage(dtm)
75
- break
76
- end
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end
83
- end
@@ -1,27 +0,0 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'contrast/agent/patching/policy/after_load_patch'
5
-
6
- module Contrast
7
- module Framework
8
- module Sinatra
9
- module Patch
10
- # Extension point allowing for the registration of Patches required to
11
- # support the Sinatra framework.
12
- module Support
13
- # (See BaseSupport#after_load_patches)
14
- def after_load_patches
15
- Set.new([
16
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
17
- 'Sinatra::Base',
18
- 'contrast/framework/sinatra/patch/base',
19
- method_to_instrument: nil,
20
- instrumenting_module: 'Contrast::Framework::Sinatra::Patch::Base')
21
- ])
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,52 +0,0 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- module Contrast
5
- module Utils
6
- # DO NOT REMOVE THIS!
7
- #
8
- # Marshal is pretty cool. It does a lot of things well. What it doesn't
9
- # mess around with though is StringIO. And what we don't want to do is
10
- # serialize ourselves out with Marshal#dump.
11
- #
12
- # Unfortunately, we have to mess around w/ that. To isolate our things from
13
- # user dumped Strings (and so that we can marshal findings), we have
14
- # decided to make this class not marshalled.
15
- module PreventMarshalSerialization
16
- def marshal_dump
17
- nil
18
- end
19
-
20
- def marshal_load *_args
21
- nil
22
- end
23
- end
24
-
25
- # DO NOT REMOVE THIS!
26
- #
27
- # Psych/YAML is also pretty cool. But it doesn't mess with anonymous
28
- # classes. In order to make things we extend serializable, we need to make
29
- # sure we play nice.
30
- module PreventPsychSerialization
31
- def encode_with *_args
32
- nil
33
- end
34
-
35
- def init_with *_args
36
- nil
37
- end
38
- end
39
-
40
- # DO NOT REMOVE THIS!
41
- #
42
- # This module is used to prevent deserialization of our classes, not b/c
43
- # we're trying to be sneaky, but b/c there is a high probability that the
44
- # events we're capturing have non-serializable data in them and b/c we
45
- # can't be sure the serialized data will be used in an application running
46
- # Contrast.
47
- module PreventSerialization
48
- include PreventMarshalSerialization
49
- include PreventPsychSerialization
50
- end
51
- end
52
- end