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
|
@@ -0,0 +1,21 @@
|
|
|
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 Framework
|
|
6
|
+
# Used to map version strings from frameworks to ApplicationUpdate dtm
|
|
7
|
+
class PlatformVersion
|
|
8
|
+
attr_reader :major, :minor, :patch
|
|
9
|
+
def initialize major, minor, patch
|
|
10
|
+
@major = major || ''
|
|
11
|
+
@minor = minor || ''
|
|
12
|
+
@patch = patch || ''
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.from_string platform_version_string
|
|
16
|
+
version_array = platform_version_string.split(Contrast::Utils::ObjectShare::PERIOD)
|
|
17
|
+
new(version_array[0], version_array[1], version_array[2])
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
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 Framework
|
|
6
|
+
# Used when Rails is present to define framework specific behavior
|
|
7
|
+
class RailsSupport < BaseSupport
|
|
8
|
+
class << self
|
|
9
|
+
RAILS_VIEWS = [
|
|
10
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('app/assets', 'coffee', %w[CoffeeScript]),
|
|
11
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('app/assets', 'scss', %w[SASS]),
|
|
12
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('app/views', 'html', %w[HTML5]),
|
|
13
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('app/views', 'html.erb', %w[HTML5 ERB]),
|
|
14
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('app/views', 'html.haml', %w[HTML5 HAML]),
|
|
15
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('public', 'html', %w[HTML5])
|
|
16
|
+
].cs__freeze
|
|
17
|
+
|
|
18
|
+
RAILS_MODULE_NAME_VERSION = Gem::Version.new('6.0.0')
|
|
19
|
+
|
|
20
|
+
def detection_class
|
|
21
|
+
'Rails'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def version
|
|
25
|
+
Rails.version
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def application_name
|
|
29
|
+
# Rails version 6.0.0 deprecated Rails::Application#parent_name, in Rails 6.1.0 that method will be removed entirely
|
|
30
|
+
# and instead we need to use parent_module_name
|
|
31
|
+
return Rails.application.cs__class.parent_module_name if Gem::Version.new(Rails.version) >= RAILS_MODULE_NAME_VERSION
|
|
32
|
+
|
|
33
|
+
Rails.application.cs__class.parent_name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def server_type
|
|
37
|
+
'rails'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def scan_views
|
|
41
|
+
scan_view_directories(RAILS_VIEWS)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def collect_routes
|
|
45
|
+
routes = Rails.application.routes.routes.map do |route|
|
|
46
|
+
route_to_coverage(route)
|
|
47
|
+
end
|
|
48
|
+
routes
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def current_route request
|
|
52
|
+
return unless Rails.cs__respond_to?(:application)
|
|
53
|
+
|
|
54
|
+
# returns array of arrays [[match_data, path_parameters, route]], sorted by
|
|
55
|
+
# precedence
|
|
56
|
+
# match_data: ActionDispatch::Journey::Path::Pattern::MatchData
|
|
57
|
+
# path_parameters: hash of various things
|
|
58
|
+
# route: ActionDispatch::Journey::Route
|
|
59
|
+
full_routes = Rails.application.routes.router.send(:find_routes, request.rack_request)
|
|
60
|
+
return if full_routes.empty?
|
|
61
|
+
|
|
62
|
+
full_route = full_routes[0] # [match_data, path_parameters, route]
|
|
63
|
+
return unless full_route
|
|
64
|
+
|
|
65
|
+
route = full_route[2] # route w/ highest precedence
|
|
66
|
+
return unless route
|
|
67
|
+
|
|
68
|
+
route_to_coverage(route)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# Convert ActionDispatch::Journey::Route to Contrast::Api::Dtm::RouteCoverage
|
|
74
|
+
def route_to_coverage route
|
|
75
|
+
route_coverage = Contrast::Api::Dtm::RouteCoverage.new
|
|
76
|
+
route_coverage.route = "#{ route.defaults[:controller] }##{ route.defaults[:action] }"
|
|
77
|
+
|
|
78
|
+
verb = source_or_string(route.verb)
|
|
79
|
+
route_coverage.verb = Contrast::Utils::StringUtils.force_utf8(verb)
|
|
80
|
+
|
|
81
|
+
url = source_or_string(route.path.spec)
|
|
82
|
+
route_coverage.url = Contrast::Utils::StringUtils.force_utf8(url)
|
|
83
|
+
route_coverage
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
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 Framework
|
|
6
|
+
# This module is to help perform sometimes necessary tasks specific to Sinatra
|
|
7
|
+
module SinatraApplicationHelper
|
|
8
|
+
SINATRA_VIEWS = [
|
|
9
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('public/stylesheets', 'scss', %w[SASS]),
|
|
10
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('views', 'html', %w[HTML5]),
|
|
11
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('views', 'html.erb', %w[HTML5 ERB]),
|
|
12
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('views', 'html.haml', %w[HTML5 HAML]),
|
|
13
|
+
Contrast::Framework::ViewTechnologiesDescriptor.new('public', 'html', %w[HTML5])
|
|
14
|
+
].cs__freeze
|
|
15
|
+
|
|
16
|
+
def app_class
|
|
17
|
+
@_app_class ||= begin
|
|
18
|
+
return nil unless defined?(Sinatra) && defined?(Sinatra::Base)
|
|
19
|
+
|
|
20
|
+
sinatra_layers = ObjectSpace.each_object(Sinatra::Base).to_a
|
|
21
|
+
result_layer = sinatra_layers.find { |layer| layer.app.nil? }
|
|
22
|
+
result_layer
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def scannable_view_dirs
|
|
27
|
+
@_scannable_view_dirs ||= begin
|
|
28
|
+
views = SINATRA_VIEWS.dup
|
|
29
|
+
views << Contrast::Framework::ViewTechnologiesDescriptor.new(view_directory, 'erb', %w[HTML5 ERB]) if view_directory
|
|
30
|
+
views << Contrast::Framework::ViewTechnologiesDescriptor.new(view_directory, 'haml', %w[HTML5 HAML]) if view_directory
|
|
31
|
+
views << Contrast::Framework::ViewTechnologiesDescriptor.new(public_directory, 'html', %w[HTML5]) if public_directory
|
|
32
|
+
views
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def view_directory
|
|
37
|
+
@_view_directory ||= begin
|
|
38
|
+
app_class&.settings&.views
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def public_directory
|
|
43
|
+
@_public_directory ||= begin
|
|
44
|
+
app_class&.settings&.public_dir
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
cs__scoped_require 'contrast/framework/sinatra_application_helper'
|
|
5
|
+
|
|
6
|
+
module Contrast
|
|
7
|
+
module Framework
|
|
8
|
+
# Used when Sinatra is present to define framework specific behavior
|
|
9
|
+
class SinatraSupport < BaseSupport
|
|
10
|
+
extend Contrast::Framework::SinatraApplicationHelper
|
|
11
|
+
class << self
|
|
12
|
+
def detection_class
|
|
13
|
+
'Sinatra'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def version
|
|
17
|
+
Sinatra::VERSION
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def application_name
|
|
21
|
+
return unless app_class
|
|
22
|
+
|
|
23
|
+
app_class.cs__class.cs__name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def server_type
|
|
27
|
+
'sinatra'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def scan_views
|
|
31
|
+
return unless app_class
|
|
32
|
+
|
|
33
|
+
scan_view_directories(scannable_view_dirs)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Iterate over every class that extends Sinatra::Base, pull out its routes
|
|
37
|
+
# (array of arrays with Mustermann::Sinatra as [][0]) and convert them into
|
|
38
|
+
# Contrast::Api::Dtm::RouteCoverage
|
|
39
|
+
def collect_routes
|
|
40
|
+
routes = []
|
|
41
|
+
controllers = sinatra_controllers
|
|
42
|
+
controllers.each do |clazz|
|
|
43
|
+
class_routes = sinatra_class_routes(clazz)
|
|
44
|
+
next unless class_routes
|
|
45
|
+
|
|
46
|
+
class_routes.each_pair do |method, list|
|
|
47
|
+
# item: [ Mustermann::Sinatra, [], Proc]
|
|
48
|
+
list.each do |item|
|
|
49
|
+
routes << route_to_coverage(clazz, method, item[0])
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
routes
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# TODO: RUBY-763
|
|
57
|
+
def current_route _request
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# Iterate over every class that extends Sinatra::Base, pull out its routes
|
|
64
|
+
# (array of arrays with Mustermann::Sinatra as [][0]) and convert them into
|
|
65
|
+
# Contrast::Api::Dtm::RouteCoverage
|
|
66
|
+
def sinatra_controllers
|
|
67
|
+
return [] unless defined?(Sinatra) && defined?(Sinatra::Base)
|
|
68
|
+
|
|
69
|
+
Contrast::Utils::ClassUtil.descendants(Sinatra::Base)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def sinatra_class_routes controller
|
|
73
|
+
controller.instance_variable_get(:@routes)
|
|
74
|
+
rescue StandardError
|
|
75
|
+
logger.debug(nil, "#{ controller } has no route instances")
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# TODO: RUBY-763
|
|
80
|
+
# given clazz, method, and Mustermann::Sinatra, build a
|
|
81
|
+
# Contrast::Api::Dtm::RouteCoverage
|
|
82
|
+
def route_to_coverage clazz, method, pattern
|
|
83
|
+
safe_pattern = source_or_string(pattern)
|
|
84
|
+
|
|
85
|
+
route_coverage = Contrast::Api::Dtm::RouteCoverage.new
|
|
86
|
+
route_coverage.route = "#{ clazz }##{ method } #{ safe_pattern }"
|
|
87
|
+
route_coverage.verb = Contrast::Utils::StringUtils.force_utf8(method)
|
|
88
|
+
route_coverage.url = Contrast::Utils::StringUtils.force_utf8(safe_pattern)
|
|
89
|
+
route_coverage
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
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 Framework
|
|
6
|
+
# Used to define common locations and extensions for specific technologies
|
|
7
|
+
class ViewTechnologiesDescriptor
|
|
8
|
+
attr_reader :path, :extension, :technology_names
|
|
9
|
+
def initialize path, extension, technology_names
|
|
10
|
+
@path = path
|
|
11
|
+
@extension = extension
|
|
12
|
+
@technology_names = technology_names
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def empty?
|
|
16
|
+
Dir["#{ path }/**/*#{ extension }"].empty?
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -1,8 +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
|
-
|
|
6
4
|
module Contrast
|
|
7
5
|
module Utils
|
|
8
6
|
module Assess
|
|
@@ -11,13 +9,13 @@ module Contrast
|
|
|
11
9
|
def self.tracked? obj
|
|
12
10
|
return false if obj.nil?
|
|
13
11
|
|
|
14
|
-
if Contrast::Utils::DuckUtils.
|
|
12
|
+
if Contrast::Utils::DuckUtils.iterable_hash?(obj)
|
|
15
13
|
obj.each_pair do |k, v|
|
|
16
14
|
return true if tracked?(k)
|
|
17
15
|
return true if tracked?(v)
|
|
18
16
|
end
|
|
19
17
|
false
|
|
20
|
-
elsif Contrast::Utils::DuckUtils.
|
|
18
|
+
elsif Contrast::Utils::DuckUtils.iterable_enumerable?(obj)
|
|
21
19
|
obj.any? do |ele|
|
|
22
20
|
tracked?(ele) unless obj == ele
|
|
23
21
|
end
|
|
@@ -1,57 +1,112 @@
|
|
|
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/
|
|
4
|
+
cs__scoped_require 'contrast/extensions/ruby_core/module'
|
|
5
5
|
|
|
6
6
|
module Contrast
|
|
7
7
|
module Utils
|
|
8
|
-
# Utility methods for exploring the complete space of
|
|
8
|
+
# Utility methods for exploring the complete space of Objects
|
|
9
9
|
class ClassUtil
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
class << self
|
|
11
|
+
# Given a module, return all of its descendants
|
|
12
|
+
#
|
|
13
|
+
# @param mod [Module] the module whose descendants you want to find.
|
|
14
|
+
# @return [Array<Module>] those Modules that inherit from the given.
|
|
15
|
+
def descendants mod
|
|
16
|
+
ObjectSpace.each_object(mod).to_a
|
|
17
|
+
end
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
# some classes have had things prepended to them, like Marshal in Rails
|
|
20
|
+
# 5 and higher. Their ActiveSupport::MarshalWithAutoloading will break
|
|
21
|
+
# our alias patching approach, as will any other prepend on something
|
|
22
|
+
# that we touch. Prepend and Alias are inherently incompatible monkey
|
|
23
|
+
# patching approaches. As such, we need to know if something has been
|
|
24
|
+
# prepended to.
|
|
25
|
+
#
|
|
26
|
+
# @param mod [Module] the Module to check to see if it has had something
|
|
27
|
+
# prepended
|
|
28
|
+
# @param ancestors [Array<Module>] the array of ancestors for the mod
|
|
29
|
+
# @return [Boolean] if the mod has been prepended or not
|
|
30
|
+
def prepended? mod, ancestors = nil
|
|
31
|
+
ancestors ||= mod.ancestors
|
|
32
|
+
ancestors[0] != mod
|
|
16
33
|
end
|
|
17
|
-
arr
|
|
18
|
-
end
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
# return true if the given module is not its first ancestor
|
|
28
|
-
def self.prepended? mod, ancestors = nil
|
|
29
|
-
ancestors ||= mod.ancestors
|
|
30
|
-
ancestors[0] != mod
|
|
31
|
-
end
|
|
35
|
+
# return true if the given method is overwritten by one of the ancestors
|
|
36
|
+
# in the ancestor change that comes before the given module
|
|
37
|
+
def prepended_method? mod, method_policy
|
|
38
|
+
target_module = determine_target_class mod, method_policy.instance_method
|
|
39
|
+
ancestors = target_module.ancestors
|
|
40
|
+
return false unless prepended?(target_module, ancestors)
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
ancestors.each do |ancestor|
|
|
43
|
+
break if ancestor == target_module
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
methods = ancestor.instance_methods(false)
|
|
46
|
+
return true if methods.include?(method_policy.method_name)
|
|
47
|
+
end
|
|
48
|
+
false
|
|
49
|
+
end
|
|
37
50
|
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
# Return a String representing the object invoking this method in the
|
|
52
|
+
# form expected by our dataflow events.
|
|
53
|
+
#
|
|
54
|
+
# @param object [Object] the entity to convert to a String
|
|
55
|
+
# @return [String] the human readable form of the String, as defined by
|
|
56
|
+
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/vulnerability/capture-snapshot.md
|
|
57
|
+
def to_contrast_string object
|
|
58
|
+
if object.cs__is_a?(String)
|
|
59
|
+
return Contrast::Utils::ObjectShare::EMPTY_STRING if object.empty?
|
|
60
|
+
|
|
61
|
+
object.dup
|
|
62
|
+
elsif object.cs__is_a?(Symbol)
|
|
63
|
+
":#{ object }"
|
|
64
|
+
elsif object.cs__is_a?(Numeric)
|
|
65
|
+
object.to_s
|
|
66
|
+
elsif object.cs__is_a?(Module) || object.cs__is_a?(Class)
|
|
67
|
+
"#{ object.cs__name }@#{ object.__id__ }"
|
|
68
|
+
else
|
|
69
|
+
"#{ object.cs__class.cs__name }@#{ object.__id__ }"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The method const_defined? can cause autoload, which is bad for us.
|
|
74
|
+
# The method autoload? doesn't traverse namespaces. This method lets us
|
|
75
|
+
# provide a constant, as a String, and parse it to determine if it has
|
|
76
|
+
# been truly truly defined, meaning it existed before this method was
|
|
77
|
+
# invoked, not as a result of it.
|
|
78
|
+
#
|
|
79
|
+
# This is required to handle a bug in Ruby prior to 2.7.0. When we drop
|
|
80
|
+
# support for 2.6.X, we should remove this code.
|
|
81
|
+
# https://bugs.ruby-lang.org/issues/10741
|
|
82
|
+
# @param name [String] the name of the constant to look up
|
|
83
|
+
# @return [Boolean]
|
|
84
|
+
def truly_defined? name
|
|
85
|
+
return false unless name
|
|
86
|
+
|
|
87
|
+
segments = name.split(Contrast::Utils::ObjectShare::DOUBLE_COLON)
|
|
88
|
+
previous_module = Module
|
|
89
|
+
segments.each do |segment|
|
|
90
|
+
return false if previous_module.cs__autoload?(segment)
|
|
91
|
+
return false unless previous_module.cs__const_defined?(segment)
|
|
92
|
+
|
|
93
|
+
previous_module = previous_module.cs__const_get(segment)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
true
|
|
97
|
+
rescue NameError # account for nonsense / poorly formatted constants
|
|
98
|
+
false
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
40
102
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def self.prepended_method? mod, method_policy
|
|
44
|
-
target_module = determine_target_class mod, method_policy.instance_method
|
|
45
|
-
ancestors = target_module.ancestors
|
|
46
|
-
return false unless prepended?(target_module, ancestors)
|
|
103
|
+
def determine_target_class mod, is_instance
|
|
104
|
+
return mod if mod.singleton_class?
|
|
47
105
|
|
|
48
|
-
|
|
49
|
-
break if ancestor == target_module
|
|
106
|
+
return mod.cs__singleton_class unless is_instance
|
|
50
107
|
|
|
51
|
-
|
|
52
|
-
return true if methods.include?(method_policy.method_name)
|
|
108
|
+
mod
|
|
53
109
|
end
|
|
54
|
-
false
|
|
55
110
|
end
|
|
56
111
|
end
|
|
57
112
|
end
|