newrelic_security 0.1.0 → 0.2.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/CHANGELOG.md +21 -0
- data/lib/newrelic_security/agent/agent.rb +6 -2
- data/lib/newrelic_security/agent/configuration/manager.rb +2 -3
- data/lib/newrelic_security/agent/control/app_info.rb +2 -0
- data/lib/newrelic_security/agent/control/application_runtime_error.rb +95 -0
- data/lib/newrelic_security/agent/control/application_url_mappings.rb +2 -0
- data/lib/newrelic_security/agent/control/control_command.rb +2 -1
- data/lib/newrelic_security/agent/control/critical_message.rb +2 -0
- data/lib/newrelic_security/agent/control/error_reporting.rb +74 -0
- data/lib/newrelic_security/agent/control/event.rb +13 -4
- data/lib/newrelic_security/agent/control/event_processor.rb +21 -0
- data/lib/newrelic_security/agent/control/exit_event.rb +2 -0
- data/lib/newrelic_security/agent/control/grpc_context.rb +2 -1
- data/lib/newrelic_security/agent/control/health_check.rb +2 -0
- data/lib/newrelic_security/agent/control/http_context.rb +8 -2
- data/lib/newrelic_security/agent/control/iast_data_transfer_request.rb +2 -0
- data/lib/newrelic_security/agent/control/websocket_client.rb +5 -0
- data/lib/newrelic_security/agent/utils/agent_utils.rb +3 -2
- data/lib/newrelic_security/constants.rb +1 -0
- data/lib/newrelic_security/instrumentation-security/grape/chain.rb +7 -2
- data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +2 -1
- data/lib/newrelic_security/instrumentation-security/grape/prepend.rb +7 -1
- data/lib/newrelic_security/instrumentation-security/padrino/chain.rb +17 -0
- data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +14 -2
- data/lib/newrelic_security/instrumentation-security/padrino/prepend.rb +12 -0
- data/lib/newrelic_security/instrumentation-security/rails/chain.rb +26 -2
- data/lib/newrelic_security/instrumentation-security/rails/instrumentation.rb +28 -3
- data/lib/newrelic_security/instrumentation-security/rails/prepend.rb +18 -0
- data/lib/newrelic_security/instrumentation-security/roda/chain.rb +7 -2
- data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +2 -1
- data/lib/newrelic_security/instrumentation-security/roda/prepend.rb +7 -1
- data/lib/newrelic_security/instrumentation-security/sinatra/chain.rb +6 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +8 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/prepend.rb +4 -0
- data/lib/newrelic_security/instrumentation-security/sqlite3/instrumentation.rb +4 -4
- data/lib/newrelic_security/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b1160c4b821177b6ce0400848a72bf09899780ff83abadb60b3e60f35b8338b
|
4
|
+
data.tar.gz: 98cfb5a7d513e842690dce03aa515a7e148a252a5f0666f16e40899e2ad511d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56dea910383ac11f0b87a29f304e0f5042336c142c1d8ca60a2cdaf15bf172102000e28e8f382fd99781afebd061a0693e3f5ff8ea9fba981cdc4de128abdc20
|
7
|
+
data.tar.gz: 04bb28178707141c66ac94158fdb3cd632ccb22a1a9c4b4f5f7d9438989ee3c6fba2124abc89ffbf135d34e249b5921cd5d4b924e50768f07363b219567867bb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# New Relic Ruby Security Agent Release Notes
|
2
2
|
|
3
|
+
## v0.2.0
|
4
|
+
|
5
|
+
Version 0.2.0 introuduces Error reporting as part of security. Any unhandled or 5xx errors in application runtime will now be visible in IAST capability UI. Updated json_version: **1.2.4**
|
6
|
+
|
7
|
+
- Feature: Unhandled and 5xx error reproting [PR#134](https://github.com/newrelic/csec-ruby-agent/pull/134)
|
8
|
+
|
9
|
+
- Bugfix: Fix for API route not present in rails7 [PR#127](https://github.com/newrelic/csec-ruby-agent/pull/127)
|
10
|
+
|
11
|
+
- Bugfix: Fix for Sqlite3 parameters sent in wrong fromat [PR#130](https://github.com/newrelic/csec-ruby-agent/pull/130)
|
12
|
+
|
13
|
+
- Bugfix: Fix for multiple events have same id [PR#135](https://github.com/newrelic/csec-ruby-agent/pull/135)
|
14
|
+
|
15
|
+
- Bugfix: Fix for NR_CSEC_VALIDATOR_HOME_TMP placeholder value not replaced during File Access fuzzing [PR#138](https://github.com/newrelic/csec-ruby-agent/pull/138)
|
16
|
+
|
17
|
+
- Bugfix: Fix for appServerInfo fields are not present in File Operation events [PR#139](https://github.com/newrelic/csec-ruby-agent/pull/139)
|
18
|
+
|
19
|
+
- Sending security agent critical errors to APM error inbox [PR#137](https://github.com/newrelic/csec-ruby-agent/pull/137)
|
20
|
+
|
21
|
+
- Added key identifiers in entityGuid and acccountId in all json reporting [PR#101](https://github.com/newrelic/csec-ruby-agent/pull/101)
|
22
|
+
|
23
|
+
|
3
24
|
## v0.1.0
|
4
25
|
|
5
26
|
Version 0.1.0 introduces `newrelic_security` agent for public preview under Newrelic pre-release software notice.
|
@@ -17,13 +17,15 @@ require 'newrelic_security/agent/control/critical_message'
|
|
17
17
|
require 'newrelic_security/agent/control/event_counter'
|
18
18
|
require 'newrelic_security/agent/control/event_stats'
|
19
19
|
require 'newrelic_security/agent/control/exit_event'
|
20
|
+
require 'newrelic_security/agent/control/application_runtime_error'
|
21
|
+
require 'newrelic_security/agent/control/error_reporting'
|
20
22
|
require 'newrelic_security/instrumentation-security/instrumentation_loader'
|
21
23
|
|
22
24
|
module NewRelic::Security
|
23
25
|
module Agent
|
24
26
|
class Agent
|
25
27
|
|
26
|
-
attr_accessor :websocket_client, :event_processor, :iast_client, :http_request_count, :event_processed_count, :event_sent_count, :event_drop_count, :route_map, :iast_event_stats, :rasp_event_stats, :exit_event_stats
|
28
|
+
attr_accessor :websocket_client, :event_processor, :iast_client, :http_request_count, :event_processed_count, :event_sent_count, :event_drop_count, :route_map, :iast_event_stats, :rasp_event_stats, :exit_event_stats, :error_reporting
|
27
29
|
|
28
30
|
def initialize
|
29
31
|
NewRelic::Security::Agent.config
|
@@ -41,6 +43,7 @@ module NewRelic::Security
|
|
41
43
|
@iast_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
42
44
|
@rasp_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
43
45
|
@exit_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
46
|
+
@error_reporting = NewRelic::Security::Agent::Control::ErrorReporting.new
|
44
47
|
end
|
45
48
|
|
46
49
|
def init
|
@@ -93,7 +96,8 @@ module NewRelic::Security
|
|
93
96
|
def find_or_create_file_path(path)
|
94
97
|
::FileUtils.mkdir_p(path) unless ::File.directory?(path)
|
95
98
|
::File.directory?(path)
|
96
|
-
rescue
|
99
|
+
rescue => e
|
100
|
+
::NewRelic::Agent.notice_error(e)
|
97
101
|
return false
|
98
102
|
end
|
99
103
|
|
@@ -39,7 +39,7 @@ module NewRelic::Security
|
|
39
39
|
@cache[:listen_port] = nil
|
40
40
|
@cache[:app_root] = NewRelic::Security::Agent::Utils.app_root
|
41
41
|
@cache[:jruby_objectspace_enabled] = false
|
42
|
-
@cache[:json_version] = :'1.2.
|
42
|
+
@cache[:json_version] = :'1.2.4'
|
43
43
|
|
44
44
|
@environment_source = NewRelic::Security::Agent::Configuration::EnvironmentSource.new
|
45
45
|
@server_source = NewRelic::Security::Agent::Configuration::ServerSource.new
|
@@ -47,8 +47,7 @@ module NewRelic::Security
|
|
47
47
|
@yaml_source = NewRelic::Security::Agent::Configuration::YamlSource.new
|
48
48
|
@default_source = NewRelic::Security::Agent::Configuration::DefaultSource.new
|
49
49
|
rescue Exception => exception
|
50
|
-
|
51
|
-
puts "Exception in Configuration::Manager.initialize : #{exception.inspect} #{exception.backtrace}"
|
50
|
+
::NewRelic::Agent.notice_error(exception)
|
52
51
|
end
|
53
52
|
|
54
53
|
def [](key)
|
@@ -26,6 +26,8 @@ module NewRelic::Security
|
|
26
26
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
27
27
|
@startTime = current_time_millis
|
28
28
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
29
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
30
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
29
31
|
@framework = NewRelic::Security::Agent.config[:framework]
|
30
32
|
@groupName = NewRelic::Security::Agent.config[:mode]
|
31
33
|
@userProvidedApplicationInfo = Hash.new
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'digest'
|
5
|
+
|
6
|
+
module NewRelic::Security
|
7
|
+
module Agent
|
8
|
+
module Control
|
9
|
+
class ApplicationRuntimeError
|
10
|
+
attr_reader :jsonName, :exception
|
11
|
+
attr_accessor :counter
|
12
|
+
|
13
|
+
def initialize(exception, ctxt, response_code, category)
|
14
|
+
@collectorType = RUBY
|
15
|
+
@language = Ruby
|
16
|
+
@jsonName = :'application-runtime-error'
|
17
|
+
@eventType = :'application-runtime-error'
|
18
|
+
@collectorVersion = NewRelic::Security::VERSION
|
19
|
+
@buildNumber = nil
|
20
|
+
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
21
|
+
@timestamp = current_time_millis
|
22
|
+
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
23
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
24
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
25
|
+
@framework = NewRelic::Security::Agent.config[:framework]
|
26
|
+
@groupName = NewRelic::Security::Agent.config[:mode]
|
27
|
+
@policyVersion = nil
|
28
|
+
@linkingMetadata = add_linking_metadata
|
29
|
+
@httpRequest = get_http_request_data(ctxt)
|
30
|
+
@exception = exception
|
31
|
+
@counter = 1
|
32
|
+
@responseCode = response_code
|
33
|
+
@category = category
|
34
|
+
@traceId = generate_trace_id(ctxt, category)
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_json
|
38
|
+
instance_variables.map! do |ivar|
|
39
|
+
[ivar[1..-1].to_sym, instance_variable_get(ivar)]
|
40
|
+
end.to_h
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_json(*_args)
|
44
|
+
as_json.to_json
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def current_time_millis
|
50
|
+
(Time.now.to_f * 1000).to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_linking_metadata
|
54
|
+
linking_metadata = {}
|
55
|
+
linking_metadata[:agentRunId] = NewRelic::Security::Agent.config[:agent_run_id]
|
56
|
+
linking_metadata.merge!(NewRelic::Security::Agent.config[:linking_metadata])
|
57
|
+
# TODO: add other fields as well in linking metadata, for event and heathcheck as well
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_http_request_data(ctxt)
|
61
|
+
return if ctxt.nil?
|
62
|
+
http_request = {}
|
63
|
+
http_request[:parameterMap] = {}
|
64
|
+
http_request[:body] = ctxt.body
|
65
|
+
http_request[:generationTime] = ctxt.time_stamp
|
66
|
+
http_request[:dataTruncated] = false
|
67
|
+
http_request[:method] = ctxt.method
|
68
|
+
http_request[:route] = ctxt.route.split(AT_THE_RATE)[1] if ctxt.route
|
69
|
+
http_request[:url] = URI(ctxt.req[REQUEST_URI]).respond_to?(:request_uri) ? URI(ctxt.req[REQUEST_URI]).request_uri : ctxt.req[REQUEST_URI]
|
70
|
+
http_request[:requestURI] = "#{ctxt.req[RACK_URL_SCHEME]}://#{ctxt.req[HTTP_HOST]}#{ctxt.req[PATH_INFO]}"
|
71
|
+
http_request[:clientIP] = ctxt.headers.key?(X_FORWARDED_FOR) ? ctxt.headers[X_FORWARDED_FOR].split(COMMA)[0].to_s : ctxt.req[REMOTE_ADDR].to_s
|
72
|
+
http_request[:serverPort] = ctxt.req[SERVER_PORT].to_i
|
73
|
+
http_request[:protocol] = ctxt.req[RACK_URL_SCHEME]
|
74
|
+
http_request[:contextPath] = ROOT_PATH
|
75
|
+
http_request[:headers] = ctxt.headers
|
76
|
+
http_request[:contentType] = ctxt.req[CONTENT_TYPE] if ctxt.req.key?(CONTENT_TYPE)
|
77
|
+
http_request[:headers][CONTENT_TYPE1] = ctxt.req[CONTENT_TYPE] if ctxt.req.key?(CONTENT_TYPE)
|
78
|
+
http_request[:dataTruncated] = ctxt.data_truncated
|
79
|
+
http_request
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_trace_id(ctxt, category)
|
83
|
+
@exception[:stackTrace]
|
84
|
+
method, route = ctxt.route.split(AT_THE_RATE) if ctxt.route
|
85
|
+
if @exception[:stackTrace]
|
86
|
+
::Digest::SHA256.hexdigest("#{@exception[:stackTrace].join(PIPE)}#{category}#{route}#{method}").to_s
|
87
|
+
else
|
88
|
+
::Digest::SHA256.hexdigest("#{category}#{route}#{method}").to_s
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -21,6 +21,8 @@ module NewRelic::Security
|
|
21
21
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
22
22
|
@timestamp = current_time_millis
|
23
23
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
24
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
25
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
24
26
|
@framework = NewRelic::Security::Agent.config[:framework]
|
25
27
|
@groupName = NewRelic::Security::Agent.config[:mode]
|
26
28
|
@policyVersion = nil
|
@@ -32,7 +32,7 @@ module NewRelic::Security
|
|
32
32
|
fuzz_request = NewRelic::Security::Agent::Control::FuzzRequest.new(message_object[:id])
|
33
33
|
fuzz_request.request = prepare_fuzz_request(message_object)
|
34
34
|
fuzz_request.case_type = message_object[:arguments][1]
|
35
|
-
fuzz_request.reflected_metadata =
|
35
|
+
fuzz_request.reflected_metadata = message_object[:reflectedMetaData]
|
36
36
|
NewRelic::Security::Agent.agent.iast_client.pending_request_ids << message_object[:id]
|
37
37
|
NewRelic::Security::Agent.agent.iast_client.enqueue(fuzz_request)
|
38
38
|
fuzz_request = nil
|
@@ -104,6 +104,7 @@ module NewRelic::Security
|
|
104
104
|
|
105
105
|
def prepare_fuzz_request(message_object)
|
106
106
|
message_object[:arguments][0].gsub!(NR_CSEC_VALIDATOR_HOME_TMP, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
107
|
+
message_object[:arguments][0].gsub!(NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
107
108
|
message_object[:arguments][0].gsub!(NR_CSEC_VALIDATOR_FILE_SEPARATOR, ::File::SEPARATOR)
|
108
109
|
prepared_fuzz_request = ::JSON.parse(message_object[:arguments][0])
|
109
110
|
prepared_fuzz_request[HEADERS][NR_CSEC_PARENT_ID] = message_object[:id]
|
@@ -21,6 +21,8 @@ module NewRelic::Security
|
|
21
21
|
@buildNumber = nil
|
22
22
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
23
23
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
24
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
25
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
24
26
|
@linkingMetadata = add_linking_metadata
|
25
27
|
@timestamp = current_time_millis
|
26
28
|
@message = message
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module NewRelic::Security
|
2
|
+
module Agent
|
3
|
+
module Control
|
4
|
+
class ErrorReporting
|
5
|
+
|
6
|
+
STATUS_CODES_5XX = {
|
7
|
+
500 => "Internal Server Error",
|
8
|
+
501 => "Not Implemented",
|
9
|
+
502 => "Bad Gateway",
|
10
|
+
503 => "Service Unavailable",
|
11
|
+
504 => "Gateway Timeout",
|
12
|
+
505 => "HTTP Version Not Supported",
|
13
|
+
506 => "Variant Also Negotiates",
|
14
|
+
507 => "Insufficient Storage",
|
15
|
+
508 => "Loop Detected",
|
16
|
+
509 => "Bandwidth Limit Exceeded",
|
17
|
+
510 => "Not Extended",
|
18
|
+
511 => "Network Authentication Required"
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
attr_accessor :exceptions_map
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@exceptions_map = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_unhandled_exception(noticed_error, ctxt, response_code)
|
28
|
+
unhandled_exception = {}
|
29
|
+
category = nil
|
30
|
+
if noticed_error
|
31
|
+
unhandled_exception[:message] = noticed_error.message
|
32
|
+
unhandled_exception[:cause] = nil
|
33
|
+
unhandled_exception[:type] = noticed_error.exception_class_name
|
34
|
+
unhandled_exception[:stackTrace] = noticed_error.stack_trace
|
35
|
+
category = noticed_error.exception_class_name
|
36
|
+
end
|
37
|
+
category = STATUS_CODES_5XX[response_code] if response_code
|
38
|
+
application_runtime_error = NewRelic::Security::Agent::Control::ApplicationRuntimeError.new(unhandled_exception, ctxt, response_code, category)
|
39
|
+
key = if response_code
|
40
|
+
# TODO: when do refactoring of ctxt.route, use both route and method to generate key
|
41
|
+
ctxt.route&.+ response_code.to_s
|
42
|
+
else
|
43
|
+
application_runtime_error.exception[:type] + application_runtime_error.exception[:stackTrace][0]
|
44
|
+
end
|
45
|
+
application_runtime_error.counter = @exceptions_map[key].counter + 1 if @exceptions_map.key?(key)
|
46
|
+
@exceptions_map[key] = application_runtime_error
|
47
|
+
rescue Exception => exception
|
48
|
+
NewRelic::Security::Agent.logger.error "Exception in generating unhandled exception: #{exception.inspect} #{exception.backtrace}\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_noticed_error(current_transaction, ctxt, response_code)
|
52
|
+
# TODO: Below operation is expensive, talk to APM to get optimized way to do this
|
53
|
+
current_transaction.exceptions.each do |_, span|
|
54
|
+
current_transaction.segments.each do |segment|
|
55
|
+
generate_unhandled_exception(segment.noticed_error, ctxt, response_code) if span[:span_id] == segment.guid
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue Exception => exception
|
59
|
+
NewRelic::Security::Agent.logger.error "Exception in extract_noticed_error: #{exception.inspect} #{exception.backtrace}\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
def report_unhandled_or_5xx_exceptions(current_transaction, ctxt, response_code = nil)
|
63
|
+
http_response_code = response_code || current_transaction&.http_response_code
|
64
|
+
if current_transaction.exceptions.empty? && http_response_code&.between?(500, 599)
|
65
|
+
generate_unhandled_exception(nil, ctxt, response_code)
|
66
|
+
else
|
67
|
+
extract_noticed_error(current_transaction, ctxt, response_code) unless current_transaction.exceptions.empty?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -26,9 +26,11 @@ module NewRelic::Security
|
|
26
26
|
@buildNumber = nil
|
27
27
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
28
28
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
29
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
30
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
29
31
|
@httpRequest = Hash.new
|
30
32
|
@httpResponse = Hash.new
|
31
|
-
@metaData = { :reflectedMetaData => { :listen_port => NewRelic::Security::Agent.config[:listen_port].to_s } }
|
33
|
+
@metaData = { :reflectedMetaData => { :listen_port => NewRelic::Security::Agent.config[:listen_port].to_s }, :appServerInfo => { :applicationDirectory => NewRelic::Security::Agent.config[:app_root], :serverBaseDirectory => NewRelic::Security::Agent.config[:app_root] } }
|
32
34
|
@linkingMetadata = add_linking_metadata
|
33
35
|
@pid = pid
|
34
36
|
@parameters = args
|
@@ -113,7 +115,7 @@ module NewRelic::Security
|
|
113
115
|
http_request[:contentType] = "TODO: "
|
114
116
|
http_request[:isGrpc] = ctxt.is_grpc
|
115
117
|
@httpRequest = http_request
|
116
|
-
@metaData
|
118
|
+
@metaData.merge!(ctxt.metadata)
|
117
119
|
end
|
118
120
|
|
119
121
|
private
|
@@ -128,13 +130,20 @@ module NewRelic::Security
|
|
128
130
|
end
|
129
131
|
|
130
132
|
def event_id
|
131
|
-
"#{Process.pid}:#{
|
133
|
+
"#{Process.pid}:#{current_transaction&.guid}:#{thread_monotonic_ctr}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def current_transaction
|
137
|
+
::NewRelic::Agent::Tracer.current_transaction if defined?(::NewRelic::Agent::Tracer)
|
132
138
|
end
|
133
139
|
|
134
140
|
def thread_monotonic_ctr
|
135
141
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context if NewRelic::Security::Agent::Control::HTTPContext.get_context
|
136
142
|
ctxt = NewRelic::Security::Agent::Control::GRPCContext.get_context if NewRelic::Security::Agent::Control::GRPCContext.get_context
|
137
|
-
|
143
|
+
return unless ctxt
|
144
|
+
ctxt.mutex.synchronize do
|
145
|
+
ctxt.event_counter = ctxt.event_counter + 1
|
146
|
+
end
|
138
147
|
end
|
139
148
|
|
140
149
|
def add_linking_metadata
|
@@ -5,6 +5,7 @@ module NewRelic::Security
|
|
5
5
|
module Control
|
6
6
|
EVENT_QUEUE_SIZE = 10_000
|
7
7
|
HEALTH_INTERVAL = 300
|
8
|
+
ERROR_REPORTING_INTERVAL = 30
|
8
9
|
|
9
10
|
class EventProcessor
|
10
11
|
|
@@ -15,6 +16,7 @@ module NewRelic::Security
|
|
15
16
|
@eventQ = ::SizedQueue.new(EVENT_QUEUE_SIZE)
|
16
17
|
create_dequeue_threads
|
17
18
|
create_keep_alive_thread
|
19
|
+
create_error_reporting_thread
|
18
20
|
NewRelic::Security::Agent.init_logger.info "[STEP-5] => Security agent components started"
|
19
21
|
end
|
20
22
|
|
@@ -128,6 +130,25 @@ module NewRelic::Security
|
|
128
130
|
NewRelic::Security::Agent.logger.error "Exception in health check thread, #{exception.inspect}"
|
129
131
|
end
|
130
132
|
|
133
|
+
def create_error_reporting_thread
|
134
|
+
@error_reporting_thread = Thread.new {
|
135
|
+
Thread.current.name = "newrelic_security_error_reporting_thread"
|
136
|
+
while true do
|
137
|
+
sleep ERROR_REPORTING_INTERVAL
|
138
|
+
send_error_reporting_event if NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open?
|
139
|
+
end
|
140
|
+
}
|
141
|
+
rescue Exception => exception
|
142
|
+
NewRelic::Security::Agent.logger.error "Exception in error_reporting thread, #{exception.inspect}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def send_error_reporting_event
|
146
|
+
NewRelic::Security::Agent.agent.error_reporting&.exceptions_map&.each_value do |exception|
|
147
|
+
NewRelic::Security::Agent::Control::WebsocketClient.instance.send(exception)
|
148
|
+
end
|
149
|
+
NewRelic::Security::Agent.agent.error_reporting&.exceptions_map&.clear
|
150
|
+
end
|
151
|
+
|
131
152
|
end
|
132
153
|
end
|
133
154
|
end
|
@@ -14,6 +14,8 @@ module NewRelic::Security
|
|
14
14
|
@collectorVersion = NewRelic::Security::VERSION
|
15
15
|
@buildNumber = nil
|
16
16
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
17
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
18
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
17
19
|
@groupName = NewRelic::Security::Agent.config[:mode]
|
18
20
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
19
21
|
@policyVersion = nil
|
@@ -8,7 +8,7 @@ module NewRelic::Security
|
|
8
8
|
|
9
9
|
class GRPCContext
|
10
10
|
|
11
|
-
attr_accessor :time_stamp, :method, :headers, :body, :route, :cache, :fuzz_files, :url, :server_name, :server_port, :client_ip, :client_port, :is_grpc, :metadata, :event_counter
|
11
|
+
attr_accessor :time_stamp, :method, :headers, :body, :route, :cache, :fuzz_files, :url, :server_name, :server_port, :client_ip, :client_port, :is_grpc, :metadata, :event_counter, :mutex
|
12
12
|
|
13
13
|
def initialize(grpc_request)
|
14
14
|
@time_stamp = current_time_millis
|
@@ -32,6 +32,7 @@ module NewRelic::Security
|
|
32
32
|
@cache = {}
|
33
33
|
@fuzz_files = ::Set.new
|
34
34
|
@event_counter = 0
|
35
|
+
@mutex = Mutex.new
|
35
36
|
NewRelic::Security::Agent.agent.http_request_count.increment
|
36
37
|
end
|
37
38
|
|
@@ -18,6 +18,8 @@ module NewRelic::Security
|
|
18
18
|
@framework = NewRelic::Security::Agent.config[:framework]
|
19
19
|
@protectedServer = nil
|
20
20
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
21
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
22
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
21
23
|
@collectorVersion = NewRelic::Security::VERSION
|
22
24
|
@buildNumber = nil
|
23
25
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
@@ -10,13 +10,14 @@ module NewRelic::Security
|
|
10
10
|
UNDERSCORE = '_'
|
11
11
|
HYPHEN = '-'
|
12
12
|
REQUEST_METHOD = 'REQUEST_METHOD'
|
13
|
+
HTTP_HOST = 'HTTP_HOST'
|
13
14
|
PATH_INFO = 'PATH_INFO'
|
14
15
|
RACK_INPUT = 'rack.input'
|
15
|
-
CGI_VARIABLES = ::Set.new(%W[ AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE HTTPS PATH_INFO PATH_TRANSLATED REQUEST_URI QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE rack.url_scheme ])
|
16
|
+
CGI_VARIABLES = ::Set.new(%W[ AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE HTTPS HTTP_HOST PATH_INFO PATH_TRANSLATED REQUEST_URI QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE rack.url_scheme ])
|
16
17
|
|
17
18
|
class HTTPContext
|
18
19
|
|
19
|
-
attr_accessor :time_stamp, :req, :method, :headers, :params, :body, :data_truncated, :route, :cache, :fuzz_files, :event_counter
|
20
|
+
attr_accessor :time_stamp, :req, :method, :headers, :params, :body, :data_truncated, :route, :cache, :fuzz_files, :event_counter, :mutex
|
20
21
|
|
21
22
|
def initialize(env)
|
22
23
|
@time_stamp = current_time_millis
|
@@ -47,6 +48,7 @@ module NewRelic::Security
|
|
47
48
|
@cache = Hash.new
|
48
49
|
@fuzz_files = ::Set.new
|
49
50
|
@event_counter = 0
|
51
|
+
@mutex = Mutex.new
|
50
52
|
NewRelic::Security::Agent.agent.http_request_count.increment
|
51
53
|
NewRelic::Security::Agent.agent.iast_client.completed_requests[@headers[NR_CSEC_PARENT_ID]] = [] if @headers.key?(NR_CSEC_PARENT_ID)
|
52
54
|
end
|
@@ -66,6 +68,10 @@ module NewRelic::Security
|
|
66
68
|
def self.reset_context
|
67
69
|
::NewRelic::Agent::Tracer.current_transaction.remove_instance_variable(:@security_context_data) if ::NewRelic::Agent::Tracer.current_transaction.instance_variable_defined?(:@security_context_data)
|
68
70
|
end
|
71
|
+
|
72
|
+
def self.get_current_transaction
|
73
|
+
::NewRelic::Agent::Tracer.current_transaction
|
74
|
+
end
|
69
75
|
end
|
70
76
|
|
71
77
|
end
|
@@ -11,6 +11,8 @@ module NewRelic::Security
|
|
11
11
|
def initialize
|
12
12
|
@jsonName = :'iast-data-request'
|
13
13
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
14
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
15
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
14
16
|
@batchSize = 10
|
15
17
|
@pendingRequestIds = []
|
16
18
|
@completedRequests = Hash.new
|
@@ -76,21 +76,26 @@ module NewRelic::Security
|
|
76
76
|
|
77
77
|
connection.on :error do |e|
|
78
78
|
NewRelic::Security::Agent.logger.error "Error in websocket connection : #{e.inspect} #{e.backtrace}"
|
79
|
+
::NewRelic::Agent.notice_error(e)
|
79
80
|
Thread.new { NewRelic::Security::Agent::Control::WebsocketClient.instance.close(true) }
|
80
81
|
end
|
81
82
|
rescue Errno::EPIPE => exception
|
82
83
|
NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
|
84
|
+
::NewRelic::Agent.notice_error(exception)
|
83
85
|
NewRelic::Security::Agent.config.disable_security
|
84
86
|
rescue Errno::ECONNRESET => exception
|
85
87
|
NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
|
88
|
+
::NewRelic::Agent.notice_error(exception)
|
86
89
|
NewRelic::Security::Agent.config.disable_security
|
87
90
|
Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
|
88
91
|
rescue Errno::ECONNREFUSED => exception
|
89
92
|
NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
|
93
|
+
::NewRelic::Agent.notice_error(exception)
|
90
94
|
NewRelic::Security::Agent.config.disable_security
|
91
95
|
Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
|
92
96
|
rescue => exception
|
93
97
|
NewRelic::Security::Agent.logger.error "Exception in websocket init: #{exception.inspect} #{exception.backtrace}"
|
98
|
+
::NewRelic::Agent.notice_error(exception)
|
94
99
|
NewRelic::Security::Agent.config.disable_security
|
95
100
|
Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
|
96
101
|
end
|
@@ -36,10 +36,11 @@ module NewRelic::Security
|
|
36
36
|
decrypted_data.split(COMMA).each do |filename|
|
37
37
|
begin
|
38
38
|
filename.gsub!(NR_CSEC_VALIDATOR_HOME_TMP, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
39
|
+
filename.gsub!(NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
39
40
|
filename.gsub!(NR_CSEC_VALIDATOR_FILE_SEPARATOR, ::File::SEPARATOR)
|
40
41
|
dirname = ::File.dirname(filename)
|
41
42
|
::FileUtils.mkdir_p(dirname, :mode => 0770) unless ::File.directory?(dirname)
|
42
|
-
ctxt&.fuzz_files
|
43
|
+
ctxt&.fuzz_files&.<< filename
|
43
44
|
::File.open(filename, ::File::WRONLY | ::File::CREAT | ::File::EXCL) do |fd|
|
44
45
|
# puts "Ownership acquired by : #{Process.pid}"
|
45
46
|
end unless ::File.exist?(filename)
|
@@ -115,7 +116,7 @@ module NewRelic::Security
|
|
115
116
|
elsif framework == :grape
|
116
117
|
ObjectSpace.each_object(::Grape::Endpoint) { |z|
|
117
118
|
z.instance_variable_get(:@routes)&.each { |route|
|
118
|
-
http_method = route.instance_variable_get(:@request_method)
|
119
|
+
http_method = route.instance_variable_get(:@request_method) || route.instance_variable_get(:@options)[:method]
|
119
120
|
NewRelic::Security::Agent.agent.route_map << "#{http_method}@#{route.pattern.origin}"
|
120
121
|
}
|
121
122
|
}
|
@@ -7,6 +7,7 @@ module NewRelic::Security
|
|
7
7
|
LANGUAGE_COLLECTOR = 'LANGUAGE_COLLECTOR'
|
8
8
|
STANDARD_OUT = 'STDOUT'
|
9
9
|
NR_CSEC_VALIDATOR_HOME_TMP = '{{NR_CSEC_VALIDATOR_HOME_TMP}}'
|
10
|
+
NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED = '%7B%7BNR_CSEC_VALIDATOR_HOME_TMP%7D%7D'
|
10
11
|
NR_CSEC_VALIDATOR_FILE_SEPARATOR = '{{NR_CSEC_VALIDATOR_FILE_SEPARATOR}}'
|
11
12
|
SEC_HOME_PATH = 'nr-security-home'
|
12
13
|
LOGS_DIR = 'logs'
|
@@ -12,10 +12,15 @@ module NewRelic::Security
|
|
12
12
|
|
13
13
|
def call(env)
|
14
14
|
retval = nil
|
15
|
-
event = call_on_enter(env)
|
15
|
+
event = call_on_enter(env) do
|
16
|
+
begin
|
17
|
+
retval = call_without_security(env)
|
18
|
+
ensure
|
19
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
20
|
+
end
|
21
|
+
end
|
16
22
|
call_on_exit(event, retval) { return retval }
|
17
23
|
end
|
18
|
-
|
19
24
|
end
|
20
25
|
end
|
21
26
|
end
|
@@ -24,6 +24,7 @@ module NewRelic::Security
|
|
24
24
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
25
25
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
26
26
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
27
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
27
28
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
28
29
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
29
30
|
rescue => exception
|
@@ -40,7 +41,7 @@ module NewRelic::Security
|
|
40
41
|
def prepare_env_from_route_on_enter(route)
|
41
42
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
42
43
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
43
|
-
http_method = route.instance_variable_get(:@request_method)
|
44
|
+
http_method = route.instance_variable_get(:@request_method) || route.instance_variable_get(:@options)[:method]
|
44
45
|
ctxt.route = "#{http_method}@#{route.options[:namespace]}" unless ctxt.nil?
|
45
46
|
rescue => exception
|
46
47
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
@@ -8,7 +8,13 @@ module NewRelic::Security
|
|
8
8
|
|
9
9
|
def call(env)
|
10
10
|
retval = nil
|
11
|
-
event = call_on_enter(env)
|
11
|
+
event = call_on_enter(env) do
|
12
|
+
begin
|
13
|
+
retval = super(env)
|
14
|
+
ensure
|
15
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
16
|
+
end
|
17
|
+
end
|
12
18
|
call_on_exit(event, retval) { return retval }
|
13
19
|
end
|
14
20
|
|
@@ -21,6 +21,23 @@ module NewRelic::Security
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
module Router
|
26
|
+
module Chain
|
27
|
+
def self.instrument!
|
28
|
+
::Padrino::Router.class_eval do
|
29
|
+
include NewRelic::Security::Instrumentation::Padrino::Router
|
30
|
+
|
31
|
+
alias_method :call_without_security, :call
|
32
|
+
|
33
|
+
def call(env, &block)
|
34
|
+
retval = call_without_security(env, &block)
|
35
|
+
call_on_exit(retval) { return retval }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
24
41
|
end
|
25
42
|
end
|
26
43
|
end
|
@@ -13,7 +13,7 @@ module NewRelic::Security
|
|
13
13
|
extracted_env = env.instance_variable_get(:@env)
|
14
14
|
NewRelic::Security::Agent::Control::HTTPContext.set_context(extracted_env)
|
15
15
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
16
|
-
ctxt.route = "#{extracted_env[REQUEST_METHOD]
|
16
|
+
ctxt.route = "#{extracted_env[REQUEST_METHOD]}@#{extracted_env[PATH_INFO]}" if ctxt
|
17
17
|
NewRelic::Security::Agent::Utils.parse_fuzz_header(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
18
18
|
rescue => exception
|
19
19
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
@@ -27,6 +27,7 @@ module NewRelic::Security
|
|
27
27
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
28
28
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
29
29
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
30
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
30
31
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
31
32
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
32
33
|
rescue => exception
|
@@ -34,9 +35,20 @@ module NewRelic::Security
|
|
34
35
|
ensure
|
35
36
|
yield
|
36
37
|
end
|
37
|
-
|
38
|
+
end
|
39
|
+
|
40
|
+
module Padrino::Router
|
41
|
+
def call_on_exit(retval)
|
42
|
+
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
|
43
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
44
|
+
rescue => exception
|
45
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
46
|
+
ensure
|
47
|
+
yield
|
48
|
+
end
|
38
49
|
end
|
39
50
|
end
|
40
51
|
end
|
41
52
|
|
42
53
|
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:padrino, ::Padrino::PathRouter::Router, ::NewRelic::Security::Instrumentation::Padrino::PathRouter::Router)
|
54
|
+
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:padrino, ::Padrino::Router, ::NewRelic::Security::Instrumentation::Padrino::Router)
|
@@ -15,6 +15,18 @@ module NewRelic::Security
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
module Router
|
20
|
+
module Prepend
|
21
|
+
include NewRelic::Security::Instrumentation::Padrino::Router
|
22
|
+
|
23
|
+
def call(env, &block)
|
24
|
+
retval = super
|
25
|
+
call_on_exit(retval) { return retval }
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
18
30
|
end
|
19
31
|
end
|
20
32
|
end
|
@@ -29,12 +29,36 @@ module NewRelic::Security
|
|
29
29
|
include NewRelic::Security::Instrumentation::ActionDispatch::Journey::Router
|
30
30
|
|
31
31
|
alias_method :find_routes_without_security, :find_routes
|
32
|
-
|
32
|
+
|
33
33
|
def find_routes(req)
|
34
34
|
retval = nil
|
35
35
|
event = find_routes_on_enter(req) { retval = find_routes_without_security(req) }
|
36
36
|
find_routes_on_exit(event, retval) { return retval }
|
37
|
-
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module ActionDispatch
|
46
|
+
module Routing
|
47
|
+
module RouteSet
|
48
|
+
module Dispatcher
|
49
|
+
module Chain
|
50
|
+
def self.instrument!
|
51
|
+
::ActionDispatch::Routing::RouteSet::Dispatcher.class_eval do
|
52
|
+
include NewRelic::Security::Instrumentation::ActionDispatch::Routing::RouteSet::Dispatcher
|
53
|
+
|
54
|
+
alias_method :serve_without_security, :serve
|
55
|
+
|
56
|
+
def serve(req)
|
57
|
+
retval = nil
|
58
|
+
event = serve_on_enter(req) { retval = serve_without_security(req) }
|
59
|
+
serve_on_exit(event, retval) { return retval }
|
60
|
+
end
|
61
|
+
end
|
38
62
|
end
|
39
63
|
end
|
40
64
|
end
|
@@ -24,6 +24,7 @@ module NewRelic::Security
|
|
24
24
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
25
25
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
26
26
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
27
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
27
28
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
28
29
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
29
30
|
rescue => exception
|
@@ -31,11 +32,11 @@ module NewRelic::Security
|
|
31
32
|
ensure
|
32
33
|
yield
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
end
|
36
37
|
|
37
38
|
module ActionDispatch::Journey::Router
|
38
|
-
|
39
|
+
|
39
40
|
def find_routes_on_enter(req)
|
40
41
|
event = nil
|
41
42
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
@@ -45,7 +46,7 @@ module NewRelic::Security
|
|
45
46
|
yield
|
46
47
|
return event
|
47
48
|
end
|
48
|
-
|
49
|
+
|
49
50
|
def find_routes_on_exit(event, retval)
|
50
51
|
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
|
51
52
|
|
@@ -60,8 +61,32 @@ module NewRelic::Security
|
|
60
61
|
yield
|
61
62
|
end
|
62
63
|
end
|
64
|
+
|
65
|
+
module ActionDispatch::Routing::RouteSet::Dispatcher
|
66
|
+
|
67
|
+
def serve_on_enter(req)
|
68
|
+
event = nil
|
69
|
+
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
70
|
+
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
71
|
+
ctxt.route = "#{ctxt.method}@#{req.route_uri_pattern.to_s.gsub(/\(\.:format\)/, EMPTY_STRING)}" if ctxt && req.respond_to?(:route_uri_pattern)
|
72
|
+
rescue => exception
|
73
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
74
|
+
ensure
|
75
|
+
yield
|
76
|
+
return event
|
77
|
+
end
|
78
|
+
|
79
|
+
def serve_on_exit(event, retval)
|
80
|
+
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
|
81
|
+
rescue => exception
|
82
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
83
|
+
ensure
|
84
|
+
yield
|
85
|
+
end
|
86
|
+
end
|
63
87
|
end
|
64
88
|
end
|
65
89
|
|
66
90
|
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:rails, ::Rails::Engine, ::NewRelic::Security::Instrumentation::Rails::Engine)
|
67
91
|
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:rails, ::ActionDispatch::Journey::Router, ::NewRelic::Security::Instrumentation::ActionDispatch::Journey::Router)
|
92
|
+
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:rails, ::ActionDispatch::Routing::RouteSet::Dispatcher, ::NewRelic::Security::Instrumentation::ActionDispatch::Routing::RouteSet::Dispatcher)
|
@@ -29,5 +29,23 @@ module NewRelic::Security
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
module ActionDispatch
|
34
|
+
module Routing
|
35
|
+
module RouteSet
|
36
|
+
module Dispatcher
|
37
|
+
module Prepend
|
38
|
+
include NewRelic::Security::Instrumentation::ActionDispatch::Routing::RouteSet::Dispatcher
|
39
|
+
|
40
|
+
def serve(req)
|
41
|
+
retval = nil
|
42
|
+
event = serve_on_enter(req) { retval = super }
|
43
|
+
serve_on_exit(event, retval) { return retval }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
32
50
|
end
|
33
51
|
end
|
@@ -10,10 +10,15 @@ module NewRelic::Security
|
|
10
10
|
|
11
11
|
def _roda_handle_main_route(*args)
|
12
12
|
retval = nil
|
13
|
-
event = _roda_handle_main_route_on_enter(
|
13
|
+
event = _roda_handle_main_route_on_enter(env) do
|
14
|
+
begin
|
15
|
+
retval = _roda_handle_main_route_without_security(*args)
|
16
|
+
ensure
|
17
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
18
|
+
end
|
19
|
+
end
|
14
20
|
_roda_handle_main_route_on_exit(event, retval) { return retval }
|
15
21
|
end
|
16
|
-
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -12,7 +12,7 @@ module NewRelic::Security
|
|
12
12
|
NewRelic::Security::Agent::Utils.get_app_routes(:roda) if NewRelic::Security::Agent.agent.route_map.empty?
|
13
13
|
NewRelic::Security::Agent::Control::HTTPContext.set_context(env)
|
14
14
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
15
|
-
ctxt.route = "#{env[REQUEST_METHOD]
|
15
|
+
ctxt.route = "#{env[REQUEST_METHOD]}@#{env[PATH_INFO]}" if ctxt
|
16
16
|
NewRelic::Security::Agent::Utils.parse_fuzz_header(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
17
17
|
rescue => exception
|
18
18
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
@@ -26,6 +26,7 @@ module NewRelic::Security
|
|
26
26
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
27
27
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
28
28
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
29
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
29
30
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
30
31
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
31
32
|
rescue => exception
|
@@ -6,7 +6,13 @@ module NewRelic::Security
|
|
6
6
|
|
7
7
|
def _roda_handle_main_route(*args)
|
8
8
|
retval = nil
|
9
|
-
event = _roda_handle_main_route_on_enter(
|
9
|
+
event = _roda_handle_main_route_on_enter(env) do
|
10
|
+
begin
|
11
|
+
retval = super
|
12
|
+
ensure
|
13
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
14
|
+
end
|
15
|
+
end
|
10
16
|
_roda_handle_main_route_on_exit(event, retval) { return retval }
|
11
17
|
end
|
12
18
|
|
@@ -20,6 +20,12 @@ module NewRelic::Security
|
|
20
20
|
def route_eval(&block)
|
21
21
|
route_eval_on_enter { route_eval_without_security(&block) }
|
22
22
|
end
|
23
|
+
|
24
|
+
alias_method :dispatch_without_security, :dispatch!
|
25
|
+
|
26
|
+
def dispatch!
|
27
|
+
dispatch_on_enter { dispatch_without_security }
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
@@ -24,6 +24,7 @@ module NewRelic::Security
|
|
24
24
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
25
25
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
26
26
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
27
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
27
28
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
28
29
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
29
30
|
rescue => exception
|
@@ -41,6 +42,13 @@ module NewRelic::Security
|
|
41
42
|
ensure
|
42
43
|
yield
|
43
44
|
end
|
45
|
+
|
46
|
+
def dispatch_on_enter
|
47
|
+
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
48
|
+
yield
|
49
|
+
ensure
|
50
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
51
|
+
end
|
44
52
|
|
45
53
|
end
|
46
54
|
end
|
@@ -10,7 +10,7 @@ module NewRelic::Security
|
|
10
10
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
11
11
|
hash = {}
|
12
12
|
hash[:sql] = sql
|
13
|
-
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.
|
13
|
+
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.flatten
|
14
14
|
hash[:parameters] = hash[:parameters] + args.map(&:to_s) unless args.empty?
|
15
15
|
event = NewRelic::Security::Agent::Control::Collector.collect(SQL_DB_COMMAND, [hash], SQLITE) unless NewRelic::Security::Instrumentation::InstrumentationUtils.sql_filter_events?(hash[:sql])
|
16
16
|
rescue => exception
|
@@ -34,7 +34,7 @@ module NewRelic::Security
|
|
34
34
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
35
35
|
hash = {}
|
36
36
|
hash[:sql] = sql
|
37
|
-
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.
|
37
|
+
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.flatten
|
38
38
|
hash[:parameters] = hash[:parameters] + args unless args.empty?
|
39
39
|
event = NewRelic::Security::Agent::Control::Collector.collect(SQL_DB_COMMAND, [hash], SQLITE) unless NewRelic::Security::Instrumentation::InstrumentationUtils.sql_filter_events?(hash[:sql])
|
40
40
|
rescue => exception
|
@@ -102,7 +102,7 @@ module NewRelic::Security
|
|
102
102
|
def bind_params_on_enter(*bind_vars)
|
103
103
|
event = nil
|
104
104
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
105
|
-
NewRelic::Security::Agent::Control::HTTPContext.get_context.cache[self.object_id][:parameters] = bind_vars.
|
105
|
+
NewRelic::Security::Agent::Control::HTTPContext.get_context.cache[self.object_id][:parameters] = bind_vars.flatten if NewRelic::Security::Agent::Control::HTTPContext.get_context && NewRelic::Security::Agent::Control::HTTPContext.get_context.cache.key?(self.object_id)
|
106
106
|
rescue => exception
|
107
107
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
108
108
|
ensure
|
@@ -129,7 +129,7 @@ module NewRelic::Security
|
|
129
129
|
else
|
130
130
|
hash = {}
|
131
131
|
hash[:sql] = NewRelic::Security::Agent::Control::HTTPContext.get_context.cache[key][:sql]
|
132
|
-
hash[:parameters] = bind_vars.
|
132
|
+
hash[:parameters] = bind_vars.flatten
|
133
133
|
ic_args.push(hash)
|
134
134
|
end
|
135
135
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: newrelic_security
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Prateek Sen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: newrelic_rpm
|
@@ -151,10 +151,12 @@ files:
|
|
151
151
|
- lib/newrelic_security/agent/configuration/server_source.rb
|
152
152
|
- lib/newrelic_security/agent/configuration/yaml_source.rb
|
153
153
|
- lib/newrelic_security/agent/control/app_info.rb
|
154
|
+
- lib/newrelic_security/agent/control/application_runtime_error.rb
|
154
155
|
- lib/newrelic_security/agent/control/application_url_mappings.rb
|
155
156
|
- lib/newrelic_security/agent/control/collector.rb
|
156
157
|
- lib/newrelic_security/agent/control/control_command.rb
|
157
158
|
- lib/newrelic_security/agent/control/critical_message.rb
|
159
|
+
- lib/newrelic_security/agent/control/error_reporting.rb
|
158
160
|
- lib/newrelic_security/agent/control/event.rb
|
159
161
|
- lib/newrelic_security/agent/control/event_counter.rb
|
160
162
|
- lib/newrelic_security/agent/control/event_processor.rb
|