contrast-agent 3.8.5 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/cs__assess_array/cs__assess_array.c +1 -1
- data/ext/cs__assess_module/cs__assess_module.c +0 -1
- data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
- data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
- data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
- data/ext/cs__common/cs__common.c +6 -6
- data/ext/cs__common/cs__common.h +3 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
- data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
- data/funchook/autom4te.cache/requests +48 -48
- data/funchook/config.log +2 -2
- data/lib/contrast/agent.rb +15 -5
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/assess/contrast_event.rb +9 -8
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
- data/lib/contrast/agent/assess/policy/policy.rb +0 -14
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
- data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
- data/lib/contrast/agent/assess/properties.rb +29 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
- data/lib/contrast/agent/class_reopener.rb +98 -55
- data/lib/contrast/agent/feature_state.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/logger_manager.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
- data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
- data/lib/contrast/agent/patching/policy/patch.rb +54 -23
- data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
- data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
- data/lib/contrast/agent/patching/policy/policy.rb +4 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
- data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
- data/lib/contrast/agent/protect/policy/policy.rb +6 -6
- data/lib/contrast/agent/protect/rule/base.rb +1 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/railtie.rb +11 -5
- data/lib/contrast/agent/request.rb +1 -19
- data/lib/contrast/agent/request_context.rb +1 -1
- data/lib/contrast/agent/rewriter.rb +4 -3
- data/lib/contrast/agent/scope.rb +116 -19
- data/lib/contrast/agent/service_heartbeat.rb +5 -2
- data/lib/contrast/agent/settings_state.rb +12 -8
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api.rb +1 -0
- data/lib/contrast/api/speedracer.rb +2 -2
- data/lib/contrast/components/agent.rb +26 -7
- data/lib/contrast/components/app_context.rb +8 -45
- data/lib/contrast/components/contrast_service.rb +3 -4
- data/lib/contrast/components/interface.rb +1 -1
- data/lib/contrast/components/scope.rb +56 -26
- data/lib/contrast/config/ruby_configuration.rb +8 -3
- data/lib/contrast/delegators.rb +9 -0
- data/lib/contrast/delegators/application_update.rb +32 -0
- data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
- data/lib/contrast/extensions/framework/rack/request.rb +24 -0
- data/lib/contrast/extensions/framework/rack/response.rb +23 -0
- data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
- data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
- data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
- data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
- data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
- data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
- data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
- data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
- data/lib/contrast/extensions/ruby_core/module.rb +17 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
- data/lib/contrast/framework/base_support.rb +63 -0
- data/lib/contrast/framework/manager.rb +109 -0
- data/lib/contrast/framework/platform_version.rb +21 -0
- data/lib/contrast/framework/rails_support.rb +88 -0
- data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
- data/lib/contrast/framework/sinatra_support.rb +94 -0
- data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
- data/lib/contrast/utils/assess/tracking_util.rb +2 -4
- data/lib/contrast/utils/class_util.rb +92 -37
- data/lib/contrast/utils/duck_utils.rb +59 -39
- data/lib/contrast/utils/environment_util.rb +5 -75
- data/lib/contrast/utils/freeze_util.rb +3 -7
- data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
- data/lib/contrast/utils/job_servers_running.rb +39 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
- data/lib/contrast/utils/service_response_util.rb +0 -6
- data/lib/contrast/utils/sinatra_helper.rb +6 -0
- data/lib/contrast/utils/stack_trace_utils.rb +1 -1
- data/resources/assess/policy.json +74 -23
- data/resources/inventory/policy.json +1 -1
- data/resources/protect/policy.json +11 -9
- data/resources/rubocops/object/frozen_cop.rb +1 -1
- data/ruby-agent.gemspec +2 -0
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +94 -57
- data/ext/cs__scope/cs__scope.c +0 -96
- data/ext/cs__scope/cs__scope.h +0 -33
- data/lib/contrast/agent/assess/class_reverter.rb +0 -82
- data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
- data/lib/contrast/core_extensions/module.rb +0 -42
- data/lib/contrast/core_extensions/object.rb +0 -27
- data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
- data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
- data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
- data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
- data/lib/contrast/rails_extensions/buffer.rb +0 -30
- data/lib/contrast/rails_extensions/rack.rb +0 -45
- data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
- data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
- data/lib/contrast/utils/operating_environment.rb +0 -38
- data/lib/contrast/utils/path_util.rb +0 -151
- data/lib/contrast/utils/scope_util.rb +0 -99
|
@@ -3,55 +3,75 @@
|
|
|
3
3
|
|
|
4
4
|
module Contrast
|
|
5
5
|
module Utils
|
|
6
|
-
# Utility methods for identifying instances that can be used
|
|
6
|
+
# Utility methods for identifying instances that can be used interchangeably
|
|
7
7
|
class DuckUtils
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
class << self
|
|
9
|
+
# Determine if the given object, or the object to which it delegates,
|
|
10
|
+
# responds to the given method.
|
|
11
|
+
#
|
|
12
|
+
# @param object [Object]
|
|
13
|
+
# @param method [Symbol]
|
|
14
|
+
# @return [Boolean]
|
|
15
|
+
def quacks_to? object, method
|
|
16
|
+
return true if object.cs__respond_to?(method)
|
|
17
|
+
return false unless object.is_a?(Delegator)
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
object.cs__delegator_respond_to?(method)
|
|
20
|
+
end
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
return
|
|
21
|
-
|
|
22
|
+
# Most things that are closable IO's will in fact be of the IO type. That
|
|
23
|
+
# being said, some will also be extensions of DelegateClass with IO type,
|
|
24
|
+
# like Tempfile. We need to handle both cases.
|
|
25
|
+
#
|
|
26
|
+
# @param object [Object]
|
|
27
|
+
# @return [Boolean]
|
|
28
|
+
def closable_io? object
|
|
29
|
+
return false unless Contrast::Utils::IOUtil.io?(object)
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
quacks_to?(object, :closed?)
|
|
32
|
+
end
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
# Determine if the given Object is a Hash, or similar enough to a hash
|
|
35
|
+
# for us to iterate on it using the #each_pair method
|
|
36
|
+
#
|
|
37
|
+
# @param object [Object]
|
|
38
|
+
# @return [Boolean]
|
|
39
|
+
def iterable_hash? object
|
|
40
|
+
# do iterate on things believed to safely implement #each_pair
|
|
41
|
+
return true if object.cs__is_a?(Hash)
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# https://stackoverflow.com/questions/20954789/how-do-i-check-if-a-variable-really-responds-to-dup
|
|
37
|
-
def self.quacks_like_duplicable? obj
|
|
38
|
-
obj.cs__class.methods.include? :new
|
|
39
|
-
end
|
|
43
|
+
# otherwise, don't risk it
|
|
44
|
+
false
|
|
45
|
+
end
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
47
|
+
# Determine if the given Object is a concrete implementation of
|
|
48
|
+
# Enumerable known to be safe to call #each on.
|
|
49
|
+
#
|
|
50
|
+
# @param object [Object]
|
|
51
|
+
# @return [Boolean]
|
|
52
|
+
def iterable_enumerable? object
|
|
53
|
+
# do iterate on things believed to safely implement #each. We're
|
|
54
|
+
# purposefully skipping Hash and Hash-like things here as
|
|
55
|
+
# #iterable_hash? should handle those.
|
|
56
|
+
return true if object.cs__is_a?(Array)
|
|
57
|
+
return true if object.cs__is_a?(Enumerator)
|
|
58
|
+
return true if object.cs__is_a?(Hash)
|
|
59
|
+
return true if object.cs__is_a?(Range)
|
|
60
|
+
return true if object.cs__is_a?(Set)
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
# otherwise, don't risk it
|
|
63
|
+
false
|
|
64
|
+
end
|
|
49
65
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
# This method will return true if the object being checked has been patched
|
|
67
|
+
# to have the cs__properties field. We check the cs__tracked? method since it
|
|
68
|
+
# is side effect free (ie doesn't cause the properties object to be created).
|
|
69
|
+
def trackable? object
|
|
70
|
+
return true if object.cs__respond_to?(:cs__tracked?)
|
|
71
|
+
return false unless object.is_a?(Delegator)
|
|
53
72
|
|
|
54
|
-
|
|
73
|
+
object.cs__delegator_respond_to?(:cs__tracked?)
|
|
74
|
+
end
|
|
55
75
|
end
|
|
56
76
|
end
|
|
57
77
|
end
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
cs__scoped_require 'socket'
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
cs__scoped_require 'contrast/extensions/ruby_core/module'
|
|
6
7
|
cs__scoped_require 'contrast/components/interface'
|
|
7
8
|
cs__scoped_require 'contrast/utils/object_share'
|
|
8
9
|
cs__scoped_require 'contrast/utils/string_utils'
|
|
@@ -12,16 +13,10 @@ module Contrast
|
|
|
12
13
|
# Gather information about the application and server environment
|
|
13
14
|
# that the agent is running in.
|
|
14
15
|
class EnvironmentUtil
|
|
15
|
-
include Contrast::Components::Interface
|
|
16
|
-
|
|
17
|
-
access_component :analysis
|
|
18
|
-
|
|
19
|
-
DEFAULT_APP_NAME = 'rack'
|
|
20
|
-
DEFAULT_SERVER_NAME = 'localhost'
|
|
21
|
-
|
|
22
16
|
CS_VERSION = 'VERSION'
|
|
23
17
|
|
|
24
18
|
# Possible names of constants that would hold version info
|
|
19
|
+
# TODO - RUBY-770
|
|
25
20
|
VERSION_CONSTANT_CANDIDATES = %w[
|
|
26
21
|
VERSION
|
|
27
22
|
::VERSION
|
|
@@ -30,16 +25,6 @@ module Contrast
|
|
|
30
25
|
::Application::VERSION
|
|
31
26
|
].cs__freeze
|
|
32
27
|
|
|
33
|
-
# Common places where view-layer files can be found in a Rails application
|
|
34
|
-
RAILS_VIEWS = [
|
|
35
|
-
['app/assets', '*.coffee', %w[CoffeeScript]],
|
|
36
|
-
['app/assets', '*.scss', %w[SASS]],
|
|
37
|
-
['app/views', '*.html', %w[HTML5]],
|
|
38
|
-
['app/views', '*.html.erb', %w[HTML5 ERB]],
|
|
39
|
-
['app/views', '*.html.haml', %w[HTML5 HAML]],
|
|
40
|
-
['public', '*.html', %w[HTML5]]
|
|
41
|
-
].cs__freeze
|
|
42
|
-
|
|
43
28
|
class << self
|
|
44
29
|
######### These are helpers ####################
|
|
45
30
|
|
|
@@ -50,6 +35,7 @@ module Contrast
|
|
|
50
35
|
######### Actually env/framework dependent ####################
|
|
51
36
|
|
|
52
37
|
# See CONTRAST-16380 for more details
|
|
38
|
+
# TODO RUBY-770
|
|
53
39
|
def determine_application_version
|
|
54
40
|
@_determine_application_version ||= begin
|
|
55
41
|
candidates = VERSION_CONSTANT_CANDIDATES.map do |name|
|
|
@@ -80,20 +66,9 @@ module Contrast
|
|
|
80
66
|
end
|
|
81
67
|
end
|
|
82
68
|
|
|
83
|
-
def platform
|
|
84
|
-
version = Rails.version rescue Sinatra::VERSION rescue Rack.version rescue '' # rubocop:disable Style/RescueModifier
|
|
85
|
-
major, minor, patch = *version.split(Contrast::Utils::ObjectShare::PERIOD)
|
|
86
|
-
major ||= ''
|
|
87
|
-
minor ||= ''
|
|
88
|
-
patch ||= ''
|
|
89
|
-
|
|
90
|
-
[major, minor, patch]
|
|
91
|
-
rescue StandardError
|
|
92
|
-
Contrast::Utils::ObjectShare::EMPTY_TRIPLE
|
|
93
|
-
end
|
|
94
|
-
|
|
95
69
|
# Read libraries from the gemfile and append to the given ApplicationUpdate message
|
|
96
70
|
# or other class that has a libraries map association
|
|
71
|
+
# TODO - RUBY-770
|
|
97
72
|
def add_library_to_app_update app_update_pb, library_tags = ''
|
|
98
73
|
libraries = Contrast::Utils::GemfileReader.instance.library_pb_list
|
|
99
74
|
libraries.each do |n|
|
|
@@ -101,51 +76,6 @@ module Contrast
|
|
|
101
76
|
app_update_pb.libraries[n.hash_code] = n
|
|
102
77
|
end
|
|
103
78
|
end
|
|
104
|
-
|
|
105
|
-
# Iterate through known locations, looking for files
|
|
106
|
-
# that represent view or template files. If found, for each file in the directory
|
|
107
|
-
# append the technology and the view object to the application update instance
|
|
108
|
-
def scan_views app_update_msg
|
|
109
|
-
return unless INVENTORY.enabled?
|
|
110
|
-
|
|
111
|
-
scan_rails_views(app_update_msg)
|
|
112
|
-
scan_sinatra_views(app_update_msg)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Find all the predefined routes for this application and append them to the
|
|
116
|
-
# provided inventory message
|
|
117
|
-
# msg should be a Contrast::Api::Dtm::ApplicationUpdate or some other msg
|
|
118
|
-
# that has a routes array consisting of Contrast::Api::Dtm::RouteCoverage
|
|
119
|
-
def scan_routes msg
|
|
120
|
-
return unless INVENTORY.enabled?
|
|
121
|
-
|
|
122
|
-
Contrast::Utils::PathUtil.find_routes.each do |route|
|
|
123
|
-
msg.routes << route
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
private
|
|
128
|
-
|
|
129
|
-
def scan_view_directories app_update_msg, view_location_data
|
|
130
|
-
view_location_data.each do |path, extension, tech_names|
|
|
131
|
-
next if Dir["#{ path }/**/*#{ extension }"].empty?
|
|
132
|
-
|
|
133
|
-
tech_names.each do |tech|
|
|
134
|
-
app_update_msg.technologies[tech] = true
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def scan_rails_views app_update_msg
|
|
140
|
-
scan_view_directories(app_update_msg, RAILS_VIEWS)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def scan_sinatra_views app_update_msg
|
|
144
|
-
sinatra_app = Contrast::Utils::SinatraHelper.app_class
|
|
145
|
-
return unless sinatra_app
|
|
146
|
-
|
|
147
|
-
scan_view_directories(app_update_msg, Contrast::Utils::SinatraHelper.scannable_view_dirs)
|
|
148
|
-
end
|
|
149
79
|
end
|
|
150
80
|
end
|
|
151
81
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
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
|
-
cs__scoped_require 'contrast/core_extensions/object'
|
|
5
4
|
cs__scoped_require 'contrast/utils/duck_utils'
|
|
6
5
|
|
|
7
6
|
module Contrast
|
|
@@ -11,21 +10,18 @@ module Contrast
|
|
|
11
10
|
class FreezeUtil
|
|
12
11
|
class << self
|
|
13
12
|
# Make every attempt to duplicate the frozen object so that it can
|
|
14
|
-
# be tracked.
|
|
15
|
-
# but throw a TypeError. This catches that case and returns the
|
|
16
|
-
# original. We luck out as these three cases do not get propagated to
|
|
13
|
+
# be tracked.
|
|
17
14
|
#
|
|
18
15
|
# @param original [Object] something frozen, usually a String
|
|
19
16
|
# @return [Object] the original or an unfrozen copy
|
|
20
17
|
def unfreeze_dup original
|
|
21
18
|
return original unless original.cs__frozen?
|
|
22
|
-
return original unless Contrast::Utils::DuckUtils.quacks_like_duplicable?(original)
|
|
23
19
|
|
|
24
20
|
copy = original.dup
|
|
25
|
-
if Contrast::Utils::DuckUtils.
|
|
21
|
+
if Contrast::Utils::DuckUtils.iterable_hash?(copy)
|
|
26
22
|
copy.each_key do |key|
|
|
27
23
|
value = original[key]
|
|
28
|
-
copy[key] =
|
|
24
|
+
copy[key] = value.dup
|
|
29
25
|
end
|
|
30
26
|
end
|
|
31
27
|
copy
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
-
# frozen_string_literal:
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
cs__scoped_require 'contrast/components/interface'
|
|
5
5
|
|
|
@@ -11,9 +11,9 @@ module Contrast
|
|
|
11
11
|
include Contrast::Components::Interface
|
|
12
12
|
access_component :agent, :analysis, :contrast_service, :logging
|
|
13
13
|
|
|
14
|
-
CS__PATH = 'path'
|
|
15
|
-
CS__SESSION_ID = 'sessionId'
|
|
16
|
-
CS__SNIPPET = 'snippet'
|
|
14
|
+
CS__PATH = 'path'
|
|
15
|
+
CS__SESSION_ID = 'sessionId'
|
|
16
|
+
CS__SNIPPET = 'snippet'
|
|
17
17
|
|
|
18
18
|
# Build and report a finding for the given rule
|
|
19
19
|
#
|
|
@@ -71,7 +71,7 @@ module Contrast
|
|
|
71
71
|
idx = call_location&.lineno
|
|
72
72
|
if file_path && idx && File.exist?(file_path)
|
|
73
73
|
idx = idx > 5 ? idx - 5 : 0
|
|
74
|
-
snippet = ''
|
|
74
|
+
snippet = +''
|
|
75
75
|
File.foreach(file_path).with_index do |line, line_num|
|
|
76
76
|
next unless line_num >= idx
|
|
77
77
|
break if line_num > idx + 10
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
# A module that detects whether any job servers attached to
|
|
7
|
+
# the application are running
|
|
8
|
+
module JobServersRunning
|
|
9
|
+
include Contrast::Components::Interface
|
|
10
|
+
access_component :logging
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def job_servers_running?
|
|
14
|
+
sidekiq_running? || rake_running?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def sidekiq_running?
|
|
18
|
+
return unless defined?(Sidekiq) && Sidekiq.cs__respond_to?(:server?) && Sidekiq.server?
|
|
19
|
+
|
|
20
|
+
logger.debug(nil, 'Detected the spawn of a Sidekiq process')
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def rake_running?
|
|
25
|
+
return unless defined?(Rake) &&
|
|
26
|
+
Rake.cs__respond_to?(:application) &&
|
|
27
|
+
Rake.application.cs__respond_to?(:top_level_tasks)
|
|
28
|
+
|
|
29
|
+
disabled_rake_tasks = Contrast::Agent::FeatureState.instance.disabled_agent_rake_tasks
|
|
30
|
+
has_disabled_task = Rake.application.top_level_tasks.any? { |top_level_task| disabled_rake_tasks.include?(top_level_task) }
|
|
31
|
+
return false unless has_disabled_task
|
|
32
|
+
|
|
33
|
+
logger.debug(nil, 'Detected startup within Rake task')
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
-
# frozen_string_literal:
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
cs__scoped_require 'parser/current'
|
|
5
5
|
|
|
@@ -32,7 +32,7 @@ module Contrast
|
|
|
32
32
|
def on_dstr node
|
|
33
33
|
return if node.children.all? { |child_node| child_node.type == :str }
|
|
34
34
|
|
|
35
|
-
new_content = '('
|
|
35
|
+
new_content = +'('
|
|
36
36
|
node.children.each_with_index do |child_node, index|
|
|
37
37
|
# A begin node looks like #{some_code} in ruby, we do a substring
|
|
38
38
|
# from [2...-1] to get rid of the #{ & trailing }.
|
|
@@ -100,12 +100,6 @@ module Contrast
|
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
if (dynamic_sources_map_pb = server_features&.assess&.dynamic_sources_map)
|
|
104
|
-
dynamic_sources_map = dynamic_sources_map_pb.keys.inject({}) do |hsh, key|
|
|
105
|
-
hsh[key] = dynamic_sources_map_pb[key]
|
|
106
|
-
end
|
|
107
|
-
Contrast::Agent::Assess::Policy::Policy.instance.update_dynamic_sources dynamic_sources_map
|
|
108
|
-
end
|
|
109
103
|
# This is for assess
|
|
110
104
|
# PERF, we could avoid here with dirty-tracking.
|
|
111
105
|
Contrast::Utils::Assess::SamplingUtil.instance.update
|
|
@@ -8,7 +8,7 @@ module Contrast
|
|
|
8
8
|
module Utils
|
|
9
9
|
# Utilities for converting ruby stack trace into DTMs
|
|
10
10
|
class StackTraceUtils
|
|
11
|
-
CONTRAST_MARKER = 'contrast/
|
|
11
|
+
CONTRAST_MARKER = 'contrast/extensions/ruby_core'
|
|
12
12
|
MONKEYPATCH_MARKER = 'cs__'
|
|
13
13
|
|
|
14
14
|
# TODO: RUBY-532
|
|
@@ -23,15 +23,17 @@
|
|
|
23
23
|
"method_visibility": "public",
|
|
24
24
|
"method_name":"body",
|
|
25
25
|
"target":"R",
|
|
26
|
-
"type":"BODY"
|
|
26
|
+
"type":"BODY",
|
|
27
|
+
"tags":["CROSS_SITE"]
|
|
27
28
|
}, {
|
|
28
29
|
"class_name":"Rack::Request::Env",
|
|
29
30
|
"instance_method": true,
|
|
30
31
|
"method_visibility": "public",
|
|
31
32
|
"method_name":"get_header",
|
|
33
|
+
"source": "P0",
|
|
32
34
|
"target":"R",
|
|
33
35
|
"type":"HEADER",
|
|
34
|
-
"tags":["NO_NEWLINES"]
|
|
36
|
+
"tags":["NO_NEWLINES", "CROSS_SITE"]
|
|
35
37
|
}, {
|
|
36
38
|
"class_name":"ActionDispatch::Request",
|
|
37
39
|
"instance_method": true,
|
|
@@ -39,28 +41,30 @@
|
|
|
39
41
|
"method_name": "raw_post",
|
|
40
42
|
"target": "R",
|
|
41
43
|
"type": "BODY",
|
|
42
|
-
"tags":["NO_NEWLINES"]
|
|
44
|
+
"tags":["NO_NEWLINES", "CROSS_SITE"]
|
|
43
45
|
}, {
|
|
44
46
|
"class_name":"Rack::Request::Helpers",
|
|
45
47
|
"instance_method": true,
|
|
46
48
|
"method_visibility": "public",
|
|
47
49
|
"method_name":"POST",
|
|
48
50
|
"target":"R",
|
|
49
|
-
"type":"PARAMETER"
|
|
51
|
+
"type":"PARAMETER",
|
|
52
|
+
"tags":["CROSS_SITE"]
|
|
50
53
|
}, {
|
|
51
54
|
"class_name":"Rack::Request::Helpers",
|
|
52
55
|
"instance_method": true,
|
|
53
56
|
"method_visibility": "public",
|
|
54
57
|
"method_name":"GET",
|
|
55
58
|
"target":"R",
|
|
56
|
-
"type":"PARAMETER"
|
|
59
|
+
"type":"PARAMETER",
|
|
60
|
+
"tags":["CROSS_SITE"]
|
|
57
61
|
}, {
|
|
58
62
|
"class_name":"Rack::Request::Helpers",
|
|
59
63
|
"instance_method": true,
|
|
60
64
|
"method_visibility": "public",
|
|
61
65
|
"method_name":"cookies",
|
|
62
66
|
"target":"R",
|
|
63
|
-
"type":"
|
|
67
|
+
"type":"COOKIE",
|
|
64
68
|
"tags":["NO_NEWLINES"]
|
|
65
69
|
}, {
|
|
66
70
|
"class_name":"Rack::Request::Helpers",
|
|
@@ -68,71 +72,72 @@
|
|
|
68
72
|
"method_visibility": "public",
|
|
69
73
|
"method_name":"url",
|
|
70
74
|
"target":"R",
|
|
71
|
-
"type":"PARAMETER"
|
|
75
|
+
"type":"PARAMETER",
|
|
76
|
+
"tags":["CROSS_SITE"]
|
|
72
77
|
}, {
|
|
73
78
|
"class_name":"Rack::Request::Helpers",
|
|
74
79
|
"instance_method": true,
|
|
75
80
|
"method_visibility": "public",
|
|
76
81
|
"method_name":"query_string",
|
|
77
82
|
"target":"R",
|
|
78
|
-
"type":"
|
|
83
|
+
"type":"BODY",
|
|
84
|
+
"tags":["CROSS_SITE"]
|
|
79
85
|
}, {
|
|
80
86
|
"class_name":"Rack::Request",
|
|
81
87
|
"instance_method": true,
|
|
82
88
|
"method_visibility": "public",
|
|
83
89
|
"method_name":"body",
|
|
84
90
|
"target":"R",
|
|
85
|
-
"type":"BODY"
|
|
91
|
+
"type":"BODY",
|
|
92
|
+
"tags":["CROSS_SITE"]
|
|
86
93
|
}, {
|
|
87
94
|
"class_name":"Rack::Request",
|
|
88
95
|
"instance_method": true,
|
|
89
96
|
"method_visibility": "public",
|
|
90
97
|
"method_name":"query_string",
|
|
91
98
|
"target":"R",
|
|
92
|
-
"type":"BODY"
|
|
99
|
+
"type":"BODY",
|
|
100
|
+
"tags":["CROSS_SITE"]
|
|
93
101
|
}, {
|
|
94
102
|
"class_name":"Rack::Request",
|
|
95
103
|
"instance_method": true,
|
|
96
104
|
"method_visibility": "public",
|
|
97
105
|
"method_name":"GET",
|
|
98
106
|
"target":"R",
|
|
99
|
-
"type":"PARAMETER"
|
|
107
|
+
"type":"PARAMETER",
|
|
108
|
+
"tags":["CROSS_SITE"]
|
|
100
109
|
}, {
|
|
101
110
|
"class_name":"Rack::Request",
|
|
102
111
|
"instance_method": true,
|
|
103
112
|
"method_visibility": "public",
|
|
104
113
|
"method_name":"POST",
|
|
105
114
|
"target":"R",
|
|
106
|
-
"type":"PARAMETER"
|
|
115
|
+
"type":"PARAMETER",
|
|
116
|
+
"tags":["CROSS_SITE"]
|
|
107
117
|
}, {
|
|
108
118
|
"class_name":"Rack::Request",
|
|
109
119
|
"instance_method": true,
|
|
110
120
|
"method_visibility": "public",
|
|
111
121
|
"method_name":"cookies",
|
|
112
122
|
"target":"R",
|
|
113
|
-
"type":"
|
|
123
|
+
"type":"COOKIE",
|
|
114
124
|
"tags":["NO_NEWLINES"]
|
|
115
|
-
}, {
|
|
116
|
-
"class_name":"Rack::Request",
|
|
117
|
-
"instance_method": true,
|
|
118
|
-
"method_visibility": "public",
|
|
119
|
-
"method_name":"url",
|
|
120
|
-
"target":"R",
|
|
121
|
-
"type":"BODY"
|
|
122
125
|
}, {
|
|
123
126
|
"class_name":"ActionController::Metal",
|
|
124
127
|
"instance_method": true,
|
|
125
128
|
"method_visibility": "public",
|
|
126
129
|
"method_name":"params",
|
|
127
130
|
"target":"R",
|
|
128
|
-
"type":"PARAMETER"
|
|
131
|
+
"type":"PARAMETER",
|
|
132
|
+
"tags":["CROSS_SITE"]
|
|
129
133
|
}, {
|
|
130
134
|
"class_name":"ActionController::StrongParameters",
|
|
131
135
|
"instance_method": true,
|
|
132
136
|
"method_visibility": "public",
|
|
133
137
|
"method_name":"params",
|
|
134
138
|
"target":"R",
|
|
135
|
-
"type":"PARAMETER"
|
|
139
|
+
"type":"PARAMETER",
|
|
140
|
+
"tags":["CROSS_SITE"]
|
|
136
141
|
}
|
|
137
142
|
],
|
|
138
143
|
"propagators":[
|
|
@@ -1213,6 +1218,7 @@
|
|
|
1213
1218
|
]
|
|
1214
1219
|
}, {
|
|
1215
1220
|
"name":"reflected-xss",
|
|
1221
|
+
"required_tags": ["CROSS_SITE"],
|
|
1216
1222
|
"disallowed_tags":["BASE64_ENCODED", "CSS_ENCODED", "CSV_ENCODED", "HTML_ENCODED", "JAVASCRIPT_ENCODED", "JAVA_ENCODED", "LDAP_ENCODED", "OS_ENCODED", "SQL_ENCODED", "URL_ENCODED", "VBSCRIPT_ENCODED", "XML_ENCODED", "XPATH_ENCODED"],
|
|
1217
1223
|
"triggers":[
|
|
1218
1224
|
{
|
|
@@ -1565,7 +1571,6 @@
|
|
|
1565
1571
|
},
|
|
1566
1572
|
{
|
|
1567
1573
|
"name": "xxe",
|
|
1568
|
-
"dataflow": "true",
|
|
1569
1574
|
"triggers": [
|
|
1570
1575
|
{
|
|
1571
1576
|
"class_name": "Ox",
|
|
@@ -1668,6 +1673,52 @@
|
|
|
1668
1673
|
"source": "P0"
|
|
1669
1674
|
}
|
|
1670
1675
|
]
|
|
1676
|
+
}, {
|
|
1677
|
+
"name": "xpath-injection",
|
|
1678
|
+
"disallowed_tags":["XPATH_ENCODED"],
|
|
1679
|
+
"triggers": [
|
|
1680
|
+
{
|
|
1681
|
+
"class_name": "XPath::Expression",
|
|
1682
|
+
"instance_method": true,
|
|
1683
|
+
"method_visibility": "private",
|
|
1684
|
+
"method_name": "initialize",
|
|
1685
|
+
"source": "P1",
|
|
1686
|
+
"trigger_class": "XPathLibraryTrigger",
|
|
1687
|
+
"trigger_method": "xpath_trigger_check"
|
|
1688
|
+
}, {
|
|
1689
|
+
"class_name": "Oga::XML::CharacterNode",
|
|
1690
|
+
"instance_method": true,
|
|
1691
|
+
"method_visibility": "public",
|
|
1692
|
+
"method_name": "initialize",
|
|
1693
|
+
"source": "P0",
|
|
1694
|
+
"trigger_class": "XPathLibraryTrigger",
|
|
1695
|
+
"trigger_method": "xpath_trigger_check"
|
|
1696
|
+
}, {
|
|
1697
|
+
"class_name": "XPather",
|
|
1698
|
+
"instance_method": true,
|
|
1699
|
+
"method_visibility": "public",
|
|
1700
|
+
"method_name": "get",
|
|
1701
|
+
"source": "P0"
|
|
1702
|
+
}, {
|
|
1703
|
+
"class_name": "XPather",
|
|
1704
|
+
"instance_method": true,
|
|
1705
|
+
"method_visibility": "public",
|
|
1706
|
+
"method_name": "search",
|
|
1707
|
+
"source": "P0"
|
|
1708
|
+
}, {
|
|
1709
|
+
"class_name": "Ox::HasAttrs",
|
|
1710
|
+
"instance_method": true,
|
|
1711
|
+
"method_visibility": "public",
|
|
1712
|
+
"method_name": "[]=",
|
|
1713
|
+
"source": "P1"
|
|
1714
|
+
}, {
|
|
1715
|
+
"class_name": "Nokogiri::XML::Node",
|
|
1716
|
+
"instance_method": true,
|
|
1717
|
+
"method_visibility": "public",
|
|
1718
|
+
"method_name": "[]=",
|
|
1719
|
+
"source": "P1"
|
|
1720
|
+
}
|
|
1721
|
+
]
|
|
1671
1722
|
}
|
|
1672
1723
|
]
|
|
1673
1724
|
}
|