contrast-agent 7.4.0 → 7.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/contrast/agent/hooks/at_exit_hook.rb +16 -1
- data/lib/contrast/agent/middleware/middleware.rb +1 -1
- data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +19 -12
- data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +55 -20
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +1 -4
- data/lib/contrast/agent/protect/rule/base.rb +56 -25
- data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker.rb +12 -4
- data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker_input_classification.rb +0 -26
- data/lib/contrast/agent/protect/rule/cmdi/cmd_injection.rb +2 -5
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +2 -4
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +2 -1
- data/lib/contrast/agent/protect/rule/deserialization/deserialization.rb +4 -4
- data/lib/contrast/agent/protect/rule/input_classification/base.rb +1 -4
- data/lib/contrast/agent/protect/rule/input_classification/encoding.rb +34 -2
- data/lib/contrast/agent/protect/rule/no_sqli/no_sqli.rb +5 -2
- data/lib/contrast/agent/protect/rule/path_traversal/path_traversal.rb +12 -7
- data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_semantic_security_bypass.rb +2 -2
- data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +2 -3
- data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +3 -4
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload.rb +3 -0
- data/lib/contrast/agent/protect/rule/utils/builders.rb +3 -4
- data/lib/contrast/agent/protect/rule/utils/filters.rb +32 -16
- data/lib/contrast/agent/protect/rule/xss/xss.rb +80 -0
- data/lib/contrast/agent/protect/rule/xxe/xxe.rb +9 -2
- data/lib/contrast/agent/reporting/details/xss_match.rb +17 -0
- data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +32 -0
- data/lib/contrast/agent/reporting/input_analysis/input_type.rb +4 -34
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +1 -5
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -5
- data/lib/contrast/agent/reporting/reporting_utilities/build_preflight.rb +4 -4
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +5 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
- data/lib/contrast/agent/request/request_context_extend.rb +0 -2
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/components/assess.rb +4 -0
- data/lib/contrast/framework/rails/support.rb +2 -2
- data/lib/contrast/logger/cef_log.rb +30 -4
- data/lib/contrast/utils/io_util.rb +3 -0
- data/lib/contrast/utils/json.rb +1 -1
- data/lib/contrast/utils/log_utils.rb +21 -10
- data/ruby-agent.gemspec +3 -2
- metadata +18 -12
@@ -52,8 +52,6 @@ module Contrast
|
|
52
52
|
if (ia = Contrast::Agent::Protect::InputAnalyzer.analyse(request))
|
53
53
|
# Handle prefilter
|
54
54
|
Contrast::Agent::Protect::InputAnalyzer.input_classification(ia, prefilter: true)
|
55
|
-
# Reflected xss infilter
|
56
|
-
Contrast::Agent::Protect::InputAnalyzer.input_classification_for('reflected-xss', ia)
|
57
55
|
@agent_input_analysis = ia
|
58
56
|
else
|
59
57
|
logger.trace('Analysis from Agent was empty.')
|
@@ -237,6 +237,10 @@ module Contrast
|
|
237
237
|
|
238
238
|
# The id for this process, based on the session metadata or id provided by the user, as indicated in
|
239
239
|
# application startup.
|
240
|
+
#
|
241
|
+
# The ID of the current application run, as returned by the application settings endpoint or set by
|
242
|
+
# application.session_id. If there is no session associated with this run, this field should be omitted
|
243
|
+
# when reporting to TS.
|
240
244
|
def session_id
|
241
245
|
::Contrast::SETTINGS.assess_state.session_id
|
242
246
|
end
|
@@ -59,7 +59,7 @@ module Contrast
|
|
59
59
|
# ActionDispatch::Journey::Path::Pattern::MatchData, Hash, ActionDispatch::Journey::Route, Array<String>
|
60
60
|
match, _params, route, path = get_full_route(request.rack_request)
|
61
61
|
unless route
|
62
|
-
logger.
|
62
|
+
logger.debug("Unable to determine the current route of this request: #{ request.rack_request }")
|
63
63
|
return
|
64
64
|
end
|
65
65
|
|
@@ -77,7 +77,7 @@ module Contrast
|
|
77
77
|
new_route_coverage&.attach_rails_data(route, original_url)
|
78
78
|
new_route_coverage
|
79
79
|
rescue StandardError => e
|
80
|
-
logger.
|
80
|
+
logger.error('Unable to determine the current route of this request due to exception: ', e)
|
81
81
|
nil
|
82
82
|
end
|
83
83
|
|
@@ -131,7 +131,16 @@ module Contrast
|
|
131
131
|
log([message, block_entry, outcome], ::Logger::Severity::DEBUG)
|
132
132
|
end
|
133
133
|
|
134
|
-
|
134
|
+
# Log successful attack attack
|
135
|
+
#
|
136
|
+
# @param rule_id [String] the rule that was triggered
|
137
|
+
# @param outcome [String] the outcome of the rule
|
138
|
+
# @param input_type [String] the type of input that was detected
|
139
|
+
# @param input_value [String] the value of the input that was detected
|
140
|
+
# @param attack_context [Contrast::Agent::RequestContext] the request context of the attack
|
141
|
+
def successful_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
|
142
|
+
# We may log from the worthwatching Queue with saved attack_context
|
143
|
+
update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
|
135
144
|
if input_type.present? && input_value.present?
|
136
145
|
successful_attack_with_input = "#{ input_type } had a value that successfully exploited" \
|
137
146
|
"#{ rule_id } - #{ input_value }"
|
@@ -142,7 +151,16 @@ module Contrast
|
|
142
151
|
end
|
143
152
|
end
|
144
153
|
|
145
|
-
|
154
|
+
# Log ineffective attack attack
|
155
|
+
#
|
156
|
+
# @param rule_id [String] the rule that was triggered
|
157
|
+
# @param outcome [String] the outcome of the rule
|
158
|
+
# @param input_type [String] the type of input that was detected
|
159
|
+
# @param input_value [String] the value of the input that was detected
|
160
|
+
# @param attack_context [Contrast::Agent::RequestContext] the request context of the attack
|
161
|
+
def ineffective_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
|
162
|
+
# We may log from the worthwatching Queue with saved attack_context
|
163
|
+
update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
|
146
164
|
if input_type.present? && input_value.present?
|
147
165
|
ineffective_attack_with_input = "#{ input_type } had a value that matched a signature for, " \
|
148
166
|
"but did not successfully exploit #{ rule_id } - #{ input_value }"
|
@@ -153,8 +171,16 @@ module Contrast
|
|
153
171
|
end
|
154
172
|
end
|
155
173
|
|
156
|
-
#
|
157
|
-
|
174
|
+
# Log suspicious attack
|
175
|
+
#
|
176
|
+
# @param rule_id [String] the rule that was triggered
|
177
|
+
# @param outcome [String] the outcome of the rule
|
178
|
+
# @param input_type [String] the type of input that was detected
|
179
|
+
# @param input_value [String] the value of the input that was detected
|
180
|
+
# @param attack_context [Contrast::Agent::RequestContext] the request context of the attack
|
181
|
+
def suspicious_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
|
182
|
+
# We may log from the worthwatching Queue with saved attack_context
|
183
|
+
update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
|
158
184
|
if input_type.present? && input_value.present?
|
159
185
|
suspicious_attack_with = "#{ input_type } included a potential attack value that was detected" \
|
160
186
|
"as suspicious using #{ rule_id } - #{ input_value }"
|
@@ -7,6 +7,8 @@ module Contrast
|
|
7
7
|
module Utils
|
8
8
|
# Util for information about an IO
|
9
9
|
module IOUtil
|
10
|
+
UNKNOWN_IO = 'unknown'
|
11
|
+
|
10
12
|
extend Contrast::Components::Logger::InstanceMethods
|
11
13
|
|
12
14
|
class << self
|
@@ -48,6 +50,7 @@ module Contrast
|
|
48
50
|
return false unless status
|
49
51
|
return false if status.pipe?
|
50
52
|
return false if status.socket?
|
53
|
+
return false if status.ftype == UNKNOWN_IO
|
51
54
|
|
52
55
|
true
|
53
56
|
end
|
data/lib/contrast/utils/json.rb
CHANGED
@@ -14,7 +14,7 @@ module Contrast
|
|
14
14
|
|
15
15
|
# Add any known cases where parsing error might arise from older json parser:
|
16
16
|
# @return [Array<String>]
|
17
|
-
SPECIAL_CASES = ["\"\""].cs__freeze # rubocop:disable Style/StringLiterals
|
17
|
+
SPECIAL_CASES = ["\"\"", "\"0\""].cs__freeze # rubocop:disable Style/StringLiterals
|
18
18
|
|
19
19
|
# Parses a string using JSON.parser. This method is used instead of standard JSON.parse to
|
20
20
|
# support older versions of json gem => not supporting key-value second parameter, which is
|
@@ -146,6 +146,7 @@ module Contrast
|
|
146
146
|
private
|
147
147
|
|
148
148
|
def build path: STDOUT_STR, level_const: DEFAULT_LEVEL
|
149
|
+
context = Contrast::Agent::REQUEST_TRACKER.current
|
149
150
|
logger = case path
|
150
151
|
when STDOUT_STR, STDERR_STR
|
151
152
|
::Logger.new(Object.cs__const_get(path))
|
@@ -154,15 +155,23 @@ module Contrast
|
|
154
155
|
end
|
155
156
|
logger.progname = PROGNAME
|
156
157
|
logger.level = level_const
|
157
|
-
|
158
|
+
update_logger_formatter(logger, new_context: context)
|
158
159
|
logger
|
159
160
|
end
|
160
161
|
|
161
162
|
def context
|
162
|
-
Contrast::Agent::REQUEST_TRACKER.current
|
163
|
+
@_context ||= Contrast::Agent::REQUEST_TRACKER.current
|
163
164
|
end
|
164
165
|
|
165
|
-
def
|
166
|
+
def context_update new_context
|
167
|
+
@_context = new_context unless new_context.nil?
|
168
|
+
end
|
169
|
+
|
170
|
+
# @param logger [Logger]
|
171
|
+
# @param new_context [Contrast::Agent::RequestContext]
|
172
|
+
def update_logger_formatter logger, new_context: nil
|
173
|
+
context_update(new_context) if new_context
|
174
|
+
|
166
175
|
ip_address = extract_ip_address
|
167
176
|
logger.formatter = proc do |severity, datetime, progname, msg|
|
168
177
|
date_format = datetime.strftime(DATE_TIME_FORMAT)
|
@@ -206,7 +215,7 @@ module Contrast
|
|
206
215
|
request_method = assign_request_method(context)
|
207
216
|
app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
|
208
217
|
attach_request_and_sender_info(message, sender_info)
|
209
|
-
message << "request=#{ context
|
218
|
+
message << "request=#{ context&.request&.url } "
|
210
219
|
message << "requestMethod=#{ request_method } "
|
211
220
|
message << "app=#{ app_name } "
|
212
221
|
message << "outcome=#{ outcome } "
|
@@ -238,16 +247,18 @@ module Contrast
|
|
238
247
|
end
|
239
248
|
|
240
249
|
def extract_sender_ip
|
241
|
-
request_headers = context
|
250
|
+
request_headers = context&.activity&.request&.headers&.transform_keys(&:to_s)
|
251
|
+
return unless request_headers
|
252
|
+
|
242
253
|
request_headers['X-Forwarded-For']
|
243
254
|
end
|
244
255
|
|
245
256
|
def assign_request_method context
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
257
|
+
request_method = context&.request&.rack_request&.env
|
258
|
+
request_method = request_method['REQUEST_METHOD'] if request_method
|
259
|
+
return DEFAULT_METADATA if request_method.nil? || !request_method.length.positive?
|
260
|
+
|
261
|
+
request_method
|
251
262
|
end
|
252
263
|
end
|
253
264
|
end
|
data/ruby-agent.gemspec
CHANGED
@@ -116,9 +116,10 @@ end
|
|
116
116
|
# dependencies.csv in this directory to indicate that and create a
|
117
117
|
# corresponding update to the fake gem server data in TeamServer.
|
118
118
|
def self.add_dependencies spec
|
119
|
-
|
119
|
+
# TODO: RUBY-99999 investigate init_with_options segmentation fault
|
120
|
+
spec.add_dependency 'ffi'
|
120
121
|
spec.add_dependency 'ougai', '>= 1.8', '< 3.0.0'
|
121
|
-
spec.add_dependency 'rack', '
|
122
|
+
spec.add_dependency 'rack', '>= 2.0', '< 4.0.0'
|
122
123
|
|
123
124
|
# bind this directly as we've had issues w/ build changes on bug release
|
124
125
|
spec.add_dependency 'contrast-agent-lib', '1.1.1'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contrast-agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.
|
4
|
+
version: 7.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- galen.palmer@contrastsecurity.com
|
@@ -10,10 +10,10 @@ authors:
|
|
10
10
|
- alex.macdonald@contrastsecurity.com
|
11
11
|
- mark.petersen@contrastsecurity.com
|
12
12
|
- joshua.reed@contrastsecurity.com
|
13
|
-
autorequire:
|
13
|
+
autorequire:
|
14
14
|
bindir: exe
|
15
15
|
cert_chain: []
|
16
|
-
date: 2023-
|
16
|
+
date: 2023-10-06 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: bundler
|
@@ -619,16 +619,16 @@ dependencies:
|
|
619
619
|
name: ffi
|
620
620
|
requirement: !ruby/object:Gem::Requirement
|
621
621
|
requirements:
|
622
|
-
- - "
|
622
|
+
- - ">="
|
623
623
|
- !ruby/object:Gem::Version
|
624
|
-
version: '
|
624
|
+
version: '0'
|
625
625
|
type: :runtime
|
626
626
|
prerelease: false
|
627
627
|
version_requirements: !ruby/object:Gem::Requirement
|
628
628
|
requirements:
|
629
|
-
- - "
|
629
|
+
- - ">="
|
630
630
|
- !ruby/object:Gem::Version
|
631
|
-
version: '
|
631
|
+
version: '0'
|
632
632
|
- !ruby/object:Gem::Dependency
|
633
633
|
name: ougai
|
634
634
|
requirement: !ruby/object:Gem::Requirement
|
@@ -653,16 +653,22 @@ dependencies:
|
|
653
653
|
name: rack
|
654
654
|
requirement: !ruby/object:Gem::Requirement
|
655
655
|
requirements:
|
656
|
-
- - "
|
656
|
+
- - ">="
|
657
657
|
- !ruby/object:Gem::Version
|
658
658
|
version: '2.0'
|
659
|
+
- - "<"
|
660
|
+
- !ruby/object:Gem::Version
|
661
|
+
version: 4.0.0
|
659
662
|
type: :runtime
|
660
663
|
prerelease: false
|
661
664
|
version_requirements: !ruby/object:Gem::Requirement
|
662
665
|
requirements:
|
663
|
-
- - "
|
666
|
+
- - ">="
|
664
667
|
- !ruby/object:Gem::Version
|
665
668
|
version: '2.0'
|
669
|
+
- - "<"
|
670
|
+
- !ruby/object:Gem::Version
|
671
|
+
version: 4.0.0
|
666
672
|
- !ruby/object:Gem::Dependency
|
667
673
|
name: contrast-agent-lib
|
668
674
|
requirement: !ruby/object:Gem::Requirement
|
@@ -1370,7 +1376,7 @@ metadata:
|
|
1370
1376
|
support_uri: https://support.contrastsecurity.com
|
1371
1377
|
trouble_shooting_uri: https://support.contrastsecurity.com/hc/en-us/search?utf8=%E2%9C%93&query=Ruby
|
1372
1378
|
wiki_uri: https://docs.contrastsecurity.com/
|
1373
|
-
post_install_message:
|
1379
|
+
post_install_message:
|
1374
1380
|
rdoc_options: []
|
1375
1381
|
require_paths:
|
1376
1382
|
- lib
|
@@ -1388,8 +1394,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1388
1394
|
- !ruby/object:Gem::Version
|
1389
1395
|
version: '0'
|
1390
1396
|
requirements: []
|
1391
|
-
rubygems_version: 3.
|
1392
|
-
signing_key:
|
1397
|
+
rubygems_version: 3.3.26
|
1398
|
+
signing_key:
|
1393
1399
|
specification_version: 4
|
1394
1400
|
summary: Contrast Security's agent for rack-based applications.
|
1395
1401
|
test_files: []
|