newrelic_security 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/pr_ci.yml +2 -2
- data/.github/workflows/release.yml +1 -1
- data/.github/workflows/rubocop.yml +1 -1
- data/CHANGELOG.md +28 -0
- data/Gemfile_test +3 -0
- data/README.md +1 -0
- data/lib/newrelic_security/agent/agent.rb +3 -1
- data/lib/newrelic_security/agent/configuration/manager.rb +16 -2
- data/lib/newrelic_security/agent/control/application_runtime_error.rb +1 -1
- data/lib/newrelic_security/agent/control/collector.rb +7 -1
- data/lib/newrelic_security/agent/control/control_command.rb +2 -1
- data/lib/newrelic_security/agent/control/error_reporting.rb +8 -6
- data/lib/newrelic_security/agent/control/event.rb +1 -0
- data/lib/newrelic_security/agent/control/event_processor.rb +20 -14
- data/lib/newrelic_security/agent/control/event_subscriber.rb +5 -1
- data/lib/newrelic_security/agent/control/health_check.rb +1 -0
- data/lib/newrelic_security/agent/control/http_context.rb +2 -1
- data/lib/newrelic_security/agent/control/iast_client.rb +1 -1
- data/lib/newrelic_security/agent/control/reflected_xss.rb +3 -4
- data/lib/newrelic_security/agent/control/websocket_client.rb +53 -16
- data/lib/newrelic_security/agent/utils/agent_utils.rb +14 -10
- data/lib/newrelic_security/instrumentation-security/graphql/chain.rb +26 -0
- data/lib/newrelic_security/instrumentation-security/graphql/instrumentation.rb +28 -0
- data/lib/newrelic_security/instrumentation-security/graphql/prepend.rb +18 -0
- data/lib/newrelic_security/instrumentation-security/io/chain.rb +2 -2
- data/lib/newrelic_security/instrumentation-security/io/prepend.rb +1 -1
- data/lib/newrelic_security/instrumentation-security/rack/chain.rb +24 -0
- data/lib/newrelic_security/instrumentation-security/rack/instrumentation.rb +44 -0
- data/lib/newrelic_security/instrumentation-security/rack/prepend.rb +18 -0
- data/lib/newrelic_security/version.rb +1 -1
- data/lib/newrelic_security/websocket-client-simple/client.rb +5 -1
- metadata +11 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90213be8f4f16d3ac2402f570f734466927d341581a73e3382f8d843d0a4486e
|
4
|
+
data.tar.gz: a329513f2be86620ecccd38f2811b842b897028c06027e47930b3dea4e8b7b4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 565fbe75ccb52096f5e1e1549f5dfdea51f96435a2ee0a8819723e1c996c731f6090a6edb3bb1673318a9273288b031a3645e7d64f81a56cd0212181d64647e4
|
7
|
+
data.tar.gz: d176f0228f7f448e2fd08abd28906f59c4e0cac88abcd41bd7a2c02b30c427a78c990cc08a41d3626d5e4ec1cb62809cc2d029bcce7b8f88aa20736c883373c6
|
data/.github/workflows/pr_ci.yml
CHANGED
@@ -22,7 +22,7 @@ jobs:
|
|
22
22
|
run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libcurl4-nss-dev libxslt1-dev libc6-dev openjdk-11-jdk
|
23
23
|
|
24
24
|
- name: Install Ruby ${{ matrix.ruby-version }}
|
25
|
-
uses: ruby/setup-ruby@
|
25
|
+
uses: ruby/setup-ruby@086ffb1a2090c870a3f881cc91ea83aa4243d408 # v1.195.0
|
26
26
|
with:
|
27
27
|
ruby-version: ${{ matrix.ruby-version }}
|
28
28
|
|
@@ -53,7 +53,7 @@ jobs:
|
|
53
53
|
- name: Configure git
|
54
54
|
run: 'git config --global init.defaultBranch main'
|
55
55
|
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # tag v4.1.2
|
56
|
-
- uses: ruby/setup-ruby@
|
56
|
+
- uses: ruby/setup-ruby@086ffb1a2090c870a3f881cc91ea83aa4243d408 # v1.195.0
|
57
57
|
with:
|
58
58
|
ruby-version: '3.1'
|
59
59
|
- run: bundle
|
@@ -10,7 +10,7 @@ jobs:
|
|
10
10
|
- name: Configure git
|
11
11
|
run: 'git config --global init.defaultBranch main'
|
12
12
|
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # tag v4.1.2
|
13
|
-
- uses: ruby/setup-ruby@
|
13
|
+
- uses: ruby/setup-ruby@086ffb1a2090c870a3f881cc91ea83aa4243d408 # v1.195.0
|
14
14
|
with:
|
15
15
|
ruby-version: '3.3'
|
16
16
|
- run: bundle
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# New Relic Ruby Security Agent Release Notes
|
2
2
|
|
3
|
+
## v0.4.0
|
4
|
+
|
5
|
+
Version 0.4.0 introuduces Rack framework support, GraphQL support & CI/CD integration as part of security. Updated json_version: **1.2.9**
|
6
|
+
|
7
|
+
- Feature: Rack framework support [PR#149](https://github.com/newrelic/csec-ruby-agent/pull/149)
|
8
|
+
|
9
|
+
- Feature: GraphQL support [PR#133](https://github.com/newrelic/csec-ruby-agent/pull/133)
|
10
|
+
|
11
|
+
- Feature: CI/CD integration support [PR#146](https://github.com/newrelic/csec-ruby-agent/pull/146)
|
12
|
+
|
13
|
+
- Bugfix: Fix for Agent is sending 404 http error in runtime error json [PR#147](https://github.com/newrelic/csec-ruby-agent/pull/147)
|
14
|
+
|
15
|
+
- Bugfix: Fix for Exception occurred in generating unhandled exception in Sinatra jruby app [PR#150](https://github.com/newrelic/csec-ruby-agent/pull/150)
|
16
|
+
|
17
|
+
- Bugfix: Fix for Do not send empty url endpoint json & Reconnect in random interval between 5-15 secs [PR#151](https://github.com/newrelic/csec-ruby-agent/pull/151)
|
18
|
+
|
19
|
+
- Bugfix: Fix for Wrong formated critical error message [PR#148](https://github.com/newrelic/csec-ruby-agent/pull/148)
|
20
|
+
|
21
|
+
- BugFix: Fix for Vulnerability is missed in text/json because of small stackTrace [PR#155](https://github.com/newrelic/csec-ruby-agent/pull/155)
|
22
|
+
|
23
|
+
- BugFix: Fix for API endpoints not detected in grape framework in JRuby environment [PR#158](https://github.com/newrelic/csec-ruby-agent/pull/158)
|
24
|
+
|
25
|
+
- BugFix: Fix for XML content type vulnerability not detected [PR#159](https://github.com/newrelic/csec-ruby-agent/pull/159)
|
26
|
+
|
27
|
+
- BugFix: Fix for Concurrency issue observed in JRuby environment, BufferOverflowException observed in writing events to websocket [PR#161](https://github.com/newrelic/csec-ruby-agent/pull/161)
|
28
|
+
|
29
|
+
- Memory usage optimisations [PR#153](https://github.com/newrelic/csec-ruby-agent/pull/153)
|
30
|
+
|
3
31
|
## v0.3.0
|
4
32
|
|
5
33
|
Version 0.3.0 introduces more control on IAST scanning through new configs(exclude_from_iast_scan, scan_schedule & scan_controllers) and
|
data/Gemfile_test
CHANGED
@@ -13,7 +13,9 @@ gem 'rubocop'
|
|
13
13
|
gem 'rubocop-minitest'
|
14
14
|
gem 'rubocop-rake'
|
15
15
|
gem 'simplecov'
|
16
|
+
gem 'concurrent-ruby', '1.3.4'
|
16
17
|
gem 'railties'
|
18
|
+
gem 'ffi', '1.15.5' if RUBY_VERSION < '3.1.0'
|
17
19
|
if RUBY_VERSION >= '3.0.0'
|
18
20
|
gem 'rails', '~>6.0.0'
|
19
21
|
elsif RUBY_VERSION < '2.5.0'
|
@@ -23,6 +25,7 @@ else
|
|
23
25
|
end
|
24
26
|
gem 'loofah', '~> 2.19.0'
|
25
27
|
gem 'sinatra'
|
28
|
+
gem 'tilt', '~> 2.4.0' if RUBY_VERSION < '2.5.0'
|
26
29
|
gem 'padrino'
|
27
30
|
gem 'grape'
|
28
31
|
gem 'roda'
|
data/README.md
CHANGED
@@ -62,6 +62,8 @@ module NewRelic::Security
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def shutdown_security_agent
|
65
|
+
NewRelic::Security::Agent.logger.info "Flushing eventQ (#{NewRelic::Security::Agent.agent.event_processor.eventQ.size} events) and closing websocket connection"
|
66
|
+
NewRelic::Security::Agent.agent.event_processor&.eventQ&.clear
|
65
67
|
@iast_client&.fuzzQ&.clear
|
66
68
|
@iast_client&.completed_requests&.clear
|
67
69
|
@iast_client&.pending_request_ids&.clear
|
@@ -80,7 +82,7 @@ module NewRelic::Security
|
|
80
82
|
end
|
81
83
|
|
82
84
|
def start_event_processor
|
83
|
-
@event_processor&.
|
85
|
+
@event_processor&.event_dequeue_threads&.each { |t| t&.kill }
|
84
86
|
@event_processor&.healthcheck_thread&.kill
|
85
87
|
@event_processor = nil
|
86
88
|
@event_processor = NewRelic::Security::Agent::Control::EventProcessor.new
|
@@ -37,14 +37,17 @@ module NewRelic::Security
|
|
37
37
|
@cache[:'security.detection.deserialization.enabled'] = ::NewRelic::Agent.config[:'security.detection.deserialization.enabled'].nil? ? true : ::NewRelic::Agent.config[:'security.detection.deserialization.enabled']
|
38
38
|
@cache[:'security.scan_controllers.iast_scan_request_rate_limit'] = ::NewRelic::Agent.config[:'security.scan_controllers.iast_scan_request_rate_limit'].to_i
|
39
39
|
@cache[:framework] = detect_framework
|
40
|
+
@cache[:app_class] = detect_app_class if @cache[:framework] == :rack
|
40
41
|
@cache[:'security.application_info.port'] = ::NewRelic::Agent.config[:'security.application_info.port'].to_i
|
41
42
|
@cache[:listen_port] = nil
|
42
43
|
@cache[:process_start_time] = current_time_millis # TODO: Ruby doesn't provide process start time in pure ruby implementation using agent loading time for now.
|
43
44
|
@cache[:traffic_start_time] = nil
|
44
45
|
@cache[:scan_start_time] = nil
|
46
|
+
@cache[:'security.scan_controllers.scan_instance_count'] = ::NewRelic::Agent.config[:'security.scan_controllers.scan_instance_count']
|
47
|
+
@cache[:'security.iast_test_identifier'] = ::NewRelic::Agent.config[:'security.iast_test_identifier']
|
45
48
|
@cache[:app_root] = NewRelic::Security::Agent::Utils.app_root
|
46
49
|
@cache[:jruby_objectspace_enabled] = false
|
47
|
-
@cache[:json_version] = :'1.2.
|
50
|
+
@cache[:json_version] = :'1.2.9'
|
48
51
|
@cache[:'security.exclude_from_iast_scan.api'] = convert_to_regexp_list(::NewRelic::Agent.config[:'security.exclude_from_iast_scan.api'])
|
49
52
|
@cache[:'security.exclude_from_iast_scan.http_request_parameters.header'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.header']
|
50
53
|
@cache[:'security.exclude_from_iast_scan.http_request_parameters.query'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.query']
|
@@ -153,6 +156,16 @@ module NewRelic::Security
|
|
153
156
|
return :sinatra if defined?(::Sinatra)
|
154
157
|
return :roda if defined?(::Roda)
|
155
158
|
return :grape if defined?(::Grape)
|
159
|
+
return :rack if defined?(::Rack) && defined?(Rack::Builder)
|
160
|
+
end
|
161
|
+
|
162
|
+
def detect_app_class
|
163
|
+
target_class = nil
|
164
|
+
ObjectSpace.each_object(::Rack::Builder) do |z| target_class = z.instance_variable_get(:@run).target end
|
165
|
+
target_class
|
166
|
+
rescue StandardError => exception
|
167
|
+
NewRelic::Security::Agent.logger.error "Exception in detect_app_class : #{exception.inspect} #{exception.backtrace}"
|
168
|
+
nil
|
156
169
|
end
|
157
170
|
|
158
171
|
def generate_uuid
|
@@ -167,7 +180,8 @@ module NewRelic::Security
|
|
167
180
|
end
|
168
181
|
::SecureRandom.uuid
|
169
182
|
rescue Exception => exception
|
170
|
-
NewRelic::Security::Agent.logger.
|
183
|
+
NewRelic::Security::Agent.logger.warn "Error in generate_uuid, generating it through default approach : #{exception.inspect} #{exception.backtrace}"
|
184
|
+
::SecureRandom.uuid
|
171
185
|
end
|
172
186
|
|
173
187
|
def create_uuid
|
@@ -81,7 +81,7 @@ module NewRelic::Security
|
|
81
81
|
|
82
82
|
def generate_trace_id(ctxt, category)
|
83
83
|
@exception[:stackTrace]
|
84
|
-
method, route = ctxt.route.split(AT_THE_RATE) if ctxt
|
84
|
+
method, route = ctxt.route.split(AT_THE_RATE) if ctxt&.route
|
85
85
|
if @exception[:stackTrace]
|
86
86
|
::Digest::SHA256.hexdigest("#{@exception[:stackTrace].join(PIPE)}#{category}#{route}#{method}").to_s
|
87
87
|
else
|
@@ -45,6 +45,7 @@ module NewRelic::Security
|
|
45
45
|
# In rails 5 method name keeps chaning for same api call (ex: _app_views_sqli_sqlinjectionattackcase_html_erb__1999281606898621405_2624809100).
|
46
46
|
# Hence, considering only frame absolute_path & lineno for apiId calculation.
|
47
47
|
user_frame_index = get_user_frame_index(stk)
|
48
|
+
route = route&.gsub(/\d+/, EMPTY_STRING) if NewRelic::Security::Agent.config[:framework] == :rack || NewRelic::Security::Agent.config[:framework] == :roda
|
48
49
|
event.apiId = "#{case_type}-#{calculate_api_id(stk[0..user_frame_index].map { |frame| "#{frame.absolute_path}:#{frame.lineno}" }, event.httpRequest[:method], route)}"
|
49
50
|
stk.delete_if { |frame| frame.path.match?(/newrelic_security/) || frame.path.match?(/new_relic/) }
|
50
51
|
user_frame_index = get_user_frame_index(stk)
|
@@ -60,7 +61,7 @@ module NewRelic::Security
|
|
60
61
|
event.lineNumber = stk[0].lineno
|
61
62
|
end
|
62
63
|
event.stacktrace = stk[0..user_frame_index].map(&:to_s)
|
63
|
-
NewRelic::Security::Agent.agent.event_processor
|
64
|
+
NewRelic::Security::Agent.agent.event_processor&.send_event(event)
|
64
65
|
if event.httpRequest[:headers].key?(NR_CSEC_FUZZ_REQUEST_ID) && event.apiId == event.httpRequest[:headers][NR_CSEC_FUZZ_REQUEST_ID].split(COLON_IAST_COLON)[0] && NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId]
|
65
66
|
NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId] << event.id
|
66
67
|
end
|
@@ -73,6 +74,10 @@ module NewRelic::Security
|
|
73
74
|
else
|
74
75
|
NewRelic::Security::Agent.agent.rasp_event_stats.error_count.increment
|
75
76
|
end
|
77
|
+
ensure
|
78
|
+
event = nil
|
79
|
+
stk = nil
|
80
|
+
route = nil
|
76
81
|
end
|
77
82
|
|
78
83
|
private
|
@@ -80,6 +85,7 @@ module NewRelic::Security
|
|
80
85
|
def get_user_frame_index(stk)
|
81
86
|
return -1 if NewRelic::Security::Agent.config[:app_root].nil?
|
82
87
|
stk.each_with_index do |val, index|
|
88
|
+
next if stk[index + 1] && stk[index + 1].path.start_with?(NewRelic::Security::Agent.config[:app_root])
|
83
89
|
return index if val.path.start_with?(NewRelic::Security::Agent.config[:app_root])
|
84
90
|
end
|
85
91
|
return -1
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'securerandom'
|
2
3
|
|
3
4
|
module NewRelic::Security
|
4
5
|
module Agent
|
@@ -93,7 +94,7 @@ module NewRelic::Security
|
|
93
94
|
NewRelic::Security::Agent.agent.iast_client.completed_requests.clear if NewRelic::Security::Agent.agent.iast_client
|
94
95
|
NewRelic::Security::Agent.agent.iast_client.pending_request_ids.clear if NewRelic::Security::Agent.agent.iast_client
|
95
96
|
NewRelic::Security::Agent.config.disable_security
|
96
|
-
Thread.new { NewRelic::Security::Agent.agent.reconnect(
|
97
|
+
Thread.new { NewRelic::Security::Agent.agent.reconnect(SecureRandom.random_number(10) + 5) }
|
97
98
|
end
|
98
99
|
|
99
100
|
def current_time_millis
|
@@ -40,22 +40,24 @@ module NewRelic::Security
|
|
40
40
|
# TODO: when do refactoring of ctxt.route, use both route and method to generate key
|
41
41
|
ctxt.route&.+ response_code.to_s
|
42
42
|
else
|
43
|
-
application_runtime_error.exception[:type]
|
43
|
+
application_runtime_error.exception[:type]&.+ application_runtime_error.exception[:stackTrace]&.first
|
44
44
|
end
|
45
|
+
return if key.nil? || key.empty?
|
45
46
|
application_runtime_error.counter = @exceptions_map[key].counter + 1 if @exceptions_map.key?(key)
|
46
47
|
@exceptions_map[key] = application_runtime_error
|
47
|
-
rescue
|
48
|
+
rescue StandardError => exception
|
48
49
|
NewRelic::Security::Agent.logger.error "Exception in generating unhandled exception: #{exception.inspect} #{exception.backtrace}\n"
|
49
50
|
end
|
50
51
|
|
51
|
-
def extract_noticed_error(current_transaction, ctxt,
|
52
|
+
def extract_noticed_error(current_transaction, ctxt, http_response_code)
|
53
|
+
return if http_response_code&.between?(400, 499)
|
52
54
|
# TODO: Below operation is expensive, talk to APM to get optimized way to do this
|
53
55
|
current_transaction.exceptions.each do |_, span|
|
54
56
|
current_transaction.segments.each do |segment|
|
55
|
-
generate_unhandled_exception(segment.noticed_error, ctxt,
|
57
|
+
generate_unhandled_exception(segment.noticed_error, ctxt, http_response_code) if span[:span_id] == segment.guid
|
56
58
|
end
|
57
59
|
end
|
58
|
-
rescue
|
60
|
+
rescue StandardError => exception
|
59
61
|
NewRelic::Security::Agent.logger.error "Exception in extract_noticed_error: #{exception.inspect} #{exception.backtrace}\n"
|
60
62
|
end
|
61
63
|
|
@@ -64,7 +66,7 @@ module NewRelic::Security
|
|
64
66
|
if current_transaction.exceptions.empty? && http_response_code&.between?(500, 599)
|
65
67
|
generate_unhandled_exception(nil, ctxt, response_code)
|
66
68
|
else
|
67
|
-
extract_noticed_error(current_transaction, ctxt,
|
69
|
+
extract_noticed_error(current_transaction, ctxt, http_response_code) unless current_transaction.exceptions.empty?
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
@@ -104,6 +104,7 @@ module NewRelic::Security
|
|
104
104
|
http_request[:headers] = ctxt.headers
|
105
105
|
http_request[:contentType] = ctxt.req[CONTENT_TYPE] if ctxt.req.has_key?(CONTENT_TYPE)
|
106
106
|
http_request[:headers][CONTENT_TYPE1] = ctxt.req[CONTENT_TYPE] if ctxt.req.has_key?(CONTENT_TYPE)
|
107
|
+
http_request[:customDataType] = ctxt.custom_data_type
|
107
108
|
http_request[:dataTruncated] = ctxt.data_truncated
|
108
109
|
@httpRequest = http_request
|
109
110
|
@metaData[:isClientDetectedFromXFF] = ctxt.headers.has_key?(X_FORWARDED_FOR) ? true : false
|
@@ -9,7 +9,7 @@ module NewRelic::Security
|
|
9
9
|
|
10
10
|
class EventProcessor
|
11
11
|
|
12
|
-
attr_accessor :eventQ, :
|
12
|
+
attr_accessor :eventQ, :event_dequeue_threads, :healthcheck_thread
|
13
13
|
|
14
14
|
def initialize
|
15
15
|
@first_event = true
|
@@ -24,18 +24,22 @@ module NewRelic::Security
|
|
24
24
|
NewRelic::Security::Agent.init_logger.info "[STEP-3] => Gathering information about the application"
|
25
25
|
app_info = NewRelic::Security::Agent::Control::AppInfo.new
|
26
26
|
app_info.update_app_info
|
27
|
-
|
28
|
-
NewRelic::Security::Agent.
|
27
|
+
app_info_json = app_info.to_json
|
28
|
+
NewRelic::Security::Agent.logger.info "Sending application info : #{app_info_json}"
|
29
|
+
NewRelic::Security::Agent.init_logger.info "Sending application info : #{app_info_json}"
|
29
30
|
enqueue(app_info)
|
30
31
|
app_info = nil
|
32
|
+
app_info_json = nil
|
31
33
|
end
|
32
34
|
|
33
35
|
def send_application_url_mappings
|
34
36
|
application_url_mappings = NewRelic::Security::Agent::Control::ApplicationURLMappings.new
|
35
37
|
application_url_mappings.update_application_url_mappings
|
36
|
-
|
38
|
+
application_url_mappings_json = application_url_mappings.to_json
|
39
|
+
NewRelic::Security::Agent.logger.info "Sending application URL Mappings : #{application_url_mappings_json}"
|
37
40
|
enqueue(application_url_mappings)
|
38
41
|
application_url_mappings = nil
|
42
|
+
application_url_mappings_json = nil
|
39
43
|
end
|
40
44
|
|
41
45
|
def send_event(event)
|
@@ -65,7 +69,7 @@ module NewRelic::Security
|
|
65
69
|
if exc
|
66
70
|
exception = {}
|
67
71
|
exception[:message] = exc.message
|
68
|
-
exception[:cause] = exc.cause
|
72
|
+
exception[:cause] = { :message => exc.cause }
|
69
73
|
exception[:stackTrace] = exc.backtrace.map(&:to_s)
|
70
74
|
end
|
71
75
|
critical_message = NewRelic::Security::Agent::Control::CriticalMessage.new(message, level, caller, thread_name, exception)
|
@@ -87,15 +91,17 @@ module NewRelic::Security
|
|
87
91
|
private
|
88
92
|
|
89
93
|
def create_dequeue_threads
|
90
|
-
|
91
|
-
|
92
|
-
Thread.
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
@event_dequeue_threads = []
|
95
|
+
3.times do |t|
|
96
|
+
@event_dequeue_threads<< Thread.new do
|
97
|
+
Thread.current.name = "newrelic_security_event_thread-#{t}"
|
98
|
+
loop do
|
99
|
+
begin
|
100
|
+
data_to_be_sent = @eventQ.pop
|
101
|
+
NewRelic::Security::Agent::Control::WebsocketClient.instance.send(data_to_be_sent)
|
102
|
+
rescue => exception
|
103
|
+
NewRelic::Security::Agent.logger.error "Exception in event pop operation : #{exception.inspect}"
|
104
|
+
end
|
99
105
|
end
|
100
106
|
end
|
101
107
|
end
|
@@ -8,7 +8,11 @@ module NewRelic::Security
|
|
8
8
|
NewRelic::Security::Agent.init_logger.info "NewRelic server_source_configuration_added for pid : #{Process.pid}, Parent Pid : #{Process.ppid}"
|
9
9
|
NewRelic::Security::Agent.config.update_server_config
|
10
10
|
if NewRelic::Security::Agent.config[:'security.enabled'] && !NewRelic::Security::Agent.config[:high_security]
|
11
|
-
|
11
|
+
NewRelic::Security::Agent.agent.event_processor&.event_dequeue_threads&.each { |t| t&.kill }
|
12
|
+
NewRelic::Security::Agent.agent.event_processor = nil
|
13
|
+
@csec_agent_main_thread&.kill
|
14
|
+
@csec_agent_main_thread = nil
|
15
|
+
@csec_agent_main_thread = Thread.new { NewRelic::Security::Agent.agent.scan_scheduler.init_via_scan_scheduler }
|
12
16
|
else
|
13
17
|
NewRelic::Security::Agent.logger.info "New Relic Security is disabled by one of the user provided config `security.enabled` or `high_security`."
|
14
18
|
NewRelic::Security::Agent.init_logger.info "New Relic Security is disabled by one of the user provided config `security.enabled` or `high_security`."
|
@@ -38,6 +38,7 @@ module NewRelic::Security
|
|
38
38
|
@procStartTime = NewRelic::Security::Agent.config[:process_start_time]
|
39
39
|
@trafficStartedTime = NewRelic::Security::Agent.config[:traffic_start_time]
|
40
40
|
@scanStartTime = NewRelic::Security::Agent.config[:scan_start_time]
|
41
|
+
@iastTestIdentifer = NewRelic::Security::Agent.config[:'security.iast_test_identifier']
|
41
42
|
end
|
42
43
|
|
43
44
|
def as_json
|
@@ -19,7 +19,7 @@ module NewRelic::Security
|
|
19
19
|
|
20
20
|
class HTTPContext
|
21
21
|
|
22
|
-
attr_accessor :time_stamp, :req, :method, :headers, :params, :body, :data_truncated, :route, :cache, :fuzz_files, :event_counter, :mutex, :url
|
22
|
+
attr_accessor :time_stamp, :req, :method, :headers, :params, :body, :data_truncated, :route, :cache, :fuzz_files, :event_counter, :custom_data_type, :mutex, :url
|
23
23
|
|
24
24
|
def initialize(env)
|
25
25
|
@time_stamp = current_time_millis
|
@@ -48,6 +48,7 @@ module NewRelic::Security
|
|
48
48
|
@data_truncated = @body && @body.size >= REQUEST_BODY_LIMIT * 1024
|
49
49
|
strio&.rewind
|
50
50
|
@body = @body.force_encoding(Encoding::UTF_8) if @body.is_a?(String)
|
51
|
+
@custom_data_type = {}
|
51
52
|
@cache = Hash.new
|
52
53
|
@fuzz_files = ::Set.new
|
53
54
|
@event_counter = 0
|
@@ -99,7 +99,7 @@ module NewRelic::Security
|
|
99
99
|
end
|
100
100
|
iast_data_transfer_request.pendingRequestIds = pending_request_ids.to_a
|
101
101
|
iast_data_transfer_request.completedRequests = completed_requests
|
102
|
-
NewRelic::Security::Agent.agent.event_processor&.send_iast_data_transfer_request(iast_data_transfer_request)
|
102
|
+
NewRelic::Security::Agent.agent.event_processor&.send_iast_data_transfer_request(iast_data_transfer_request) if NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open?
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -108,8 +108,8 @@ module NewRelic::Security
|
|
108
108
|
processed_data.add(body)
|
109
109
|
end
|
110
110
|
when APPLICATION_XML
|
111
|
-
|
112
|
-
processed_data.add(
|
111
|
+
xml_data = ::CGI.unescapeHTML(body)
|
112
|
+
processed_data.add(xml_data)
|
113
113
|
when APPLICATION_X_WWW_FORM_URLENCODED
|
114
114
|
body = ::CGI.unescape(body, UTF_8)
|
115
115
|
processed_data.add(body)
|
@@ -134,7 +134,7 @@ module NewRelic::Security
|
|
134
134
|
# do while loop in java code here
|
135
135
|
old_processed_body = processed_body
|
136
136
|
body = ::JSON.parse(processed_body)
|
137
|
-
processed_data.add(body) if old_processed_body != body && body.to_s.include?(LESS_THAN)
|
137
|
+
processed_data.add(body.to_s) if old_processed_body != body && body.to_s.include?(LESS_THAN)
|
138
138
|
when APPLICATION_XML
|
139
139
|
# Unescaping of xml data is remaining
|
140
140
|
processed_data.add(processed_data)
|
@@ -176,7 +176,6 @@ module NewRelic::Security
|
|
176
176
|
start_pos = 0
|
177
177
|
tmp_curr_pos = 0
|
178
178
|
tmp_start_pos = 0
|
179
|
-
|
180
179
|
while curr_pos < data.length
|
181
180
|
matcher = TAG_NAME_REGEX.match(data, curr_pos)
|
182
181
|
is_attack_construct = false
|
@@ -23,6 +23,8 @@ module NewRelic::Security
|
|
23
23
|
NR_CSEC_IAST_DATA_TRANSFER_MODE = 'NR-CSEC-IAST-DATA-TRANSFER-MODE'
|
24
24
|
NR_CSEC_IGNORED_VUL_CATEGORIES = 'NR-CSEC-IGNORED-VUL-CATEGORIES'
|
25
25
|
NR_CSEC_PROCESS_START_TIME = 'NR-CSEC-PROCESS-START-TIME'
|
26
|
+
NR_CSEC_IAST_SCAN_INSTANCE_COUNT = 'NR-CSEC-IAST-SCAN-INSTANCE-COUNT'
|
27
|
+
NR_CSEC_IAST_TEST_IDENTIFIER = 'NR-CSEC-IAST-TEST-IDENTIFIER'
|
26
28
|
|
27
29
|
class WebsocketClient
|
28
30
|
include Singleton
|
@@ -47,6 +49,11 @@ module NewRelic::Security
|
|
47
49
|
headers[NR_CSEC_IAST_DATA_TRANSFER_MODE] = PULL
|
48
50
|
headers[NR_CSEC_IGNORED_VUL_CATEGORIES] = ingnored_vul_categories.join(COMMA)
|
49
51
|
headers[NR_CSEC_PROCESS_START_TIME] = NewRelic::Security::Agent.config[:process_start_time]
|
52
|
+
headers[NR_CSEC_IAST_SCAN_INSTANCE_COUNT] = NewRelic::Security::Agent.config[:'security.scan_controllers.scan_instance_count']
|
53
|
+
if NewRelic::Security::Agent.config[:'security.iast_test_identifier'] && !NewRelic::Security::Agent.config[:'security.iast_test_identifier'].empty?
|
54
|
+
headers[NR_CSEC_IAST_TEST_IDENTIFIER] = NewRelic::Security::Agent.config[:'security.iast_test_identifier']
|
55
|
+
headers[NR_CSEC_IAST_SCAN_INSTANCE_COUNT] = 1
|
56
|
+
end
|
50
57
|
|
51
58
|
begin
|
52
59
|
cert_store = ::OpenSSL::X509::Store.new
|
@@ -54,13 +61,16 @@ module NewRelic::Security
|
|
54
61
|
NewRelic::Security::Agent.logger.info "Websocket connection URL : #{NewRelic::Security::Agent.config[:validator_service_url]}"
|
55
62
|
connection = NewRelic::Security::WebSocket::Client::Simple.connect NewRelic::Security::Agent.config[:validator_service_url], headers: headers, cert_store: cert_store
|
56
63
|
@ws = connection
|
64
|
+
@mutex = Mutex.new
|
57
65
|
|
58
66
|
connection.on :open do
|
67
|
+
headers = nil
|
59
68
|
NewRelic::Security::Agent.logger.debug "Websocket connected with IC, AgentEventMachine #{NewRelic::Security::Agent::Utils.filtered_log(connection.inspect)}"
|
60
69
|
NewRelic::Security::Agent.init_logger.info "[STEP-4] => Web socket connection to SaaS validator established successfully"
|
61
70
|
NewRelic::Security::Agent.agent.event_processor.send_app_info
|
62
71
|
NewRelic::Security::Agent.agent.event_processor.send_application_url_mappings
|
63
72
|
NewRelic::Security::Agent.config.enable_security
|
73
|
+
NewRelic::Security::Agent::Control::WebsocketClient.instance.start_ping_thread
|
64
74
|
end
|
65
75
|
|
66
76
|
connection.on :message do |msg|
|
@@ -75,13 +85,14 @@ module NewRelic::Security
|
|
75
85
|
connection.on :close do |e|
|
76
86
|
NewRelic::Security::Agent.logger.info "Closing websocket connection : #{e.inspect}\n"
|
77
87
|
NewRelic::Security::Agent.config.disable_security
|
78
|
-
|
88
|
+
reconnect_interval = e.instance_of?(TrueClass) ? 0 : 15
|
89
|
+
Thread.new { NewRelic::Security::Agent.agent.reconnect(reconnect_interval) } if e
|
79
90
|
end
|
80
91
|
|
81
92
|
connection.on :error do |e|
|
82
93
|
NewRelic::Security::Agent.logger.error "Error in websocket connection : #{e.inspect} #{e.backtrace}"
|
83
94
|
::NewRelic::Agent.notice_error(e)
|
84
|
-
Thread.new { NewRelic::Security::Agent::Control::WebsocketClient.instance.close(
|
95
|
+
Thread.new { NewRelic::Security::Agent::Control::WebsocketClient.instance.close(e) }
|
85
96
|
end
|
86
97
|
rescue Errno::EPIPE => exception
|
87
98
|
NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
|
@@ -107,25 +118,37 @@ module NewRelic::Security
|
|
107
118
|
end
|
108
119
|
|
109
120
|
def send(message)
|
110
|
-
message_json =
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
121
|
+
message_json = nil
|
122
|
+
begin
|
123
|
+
message_json = message.to_json
|
124
|
+
NewRelic::Security::Agent.logger.debug "Sending #{message.jsonName} : #{message_json}"
|
125
|
+
@mutex.synchronize do
|
126
|
+
res = @ws.send(message_json)
|
127
|
+
if res && message.jsonName == :Event
|
128
|
+
NewRelic::Security::Agent.agent.event_sent_count.increment
|
129
|
+
if NewRelic::Security::Agent::Utils.is_IAST_request?(message.httpRequest[:headers])
|
130
|
+
NewRelic::Security::Agent.agent.iast_event_stats.sent.increment
|
131
|
+
else
|
132
|
+
NewRelic::Security::Agent.agent.rasp_event_stats.sent.increment
|
133
|
+
end
|
134
|
+
end
|
135
|
+
NewRelic::Security::Agent.agent.exit_event_stats.sent.increment if res && message.jsonName == :'exit-event'
|
119
136
|
end
|
137
|
+
rescue Exception => exception
|
138
|
+
NewRelic::Security::Agent.logger.error "Exception in sending message : #{exception.inspect} #{exception.backtrace}, message: #{message_json}"
|
139
|
+
NewRelic::Security::Agent.agent.event_drop_count.increment if message.jsonName == :Event
|
140
|
+
NewRelic::Security::Agent.agent.event_processor.send_critical_message(exception.message, "SEVERE", caller_locations[0].to_s, Thread.current.name, exception)
|
141
|
+
ensure
|
142
|
+
message_json = nil
|
120
143
|
end
|
121
|
-
NewRelic::Security::Agent.agent.exit_event_stats.sent.increment if res && message.jsonName == :'exit-event'
|
122
|
-
rescue Exception => exception
|
123
|
-
NewRelic::Security::Agent.logger.error "Exception in sending message : #{exception.inspect} #{exception.backtrace}"
|
124
|
-
NewRelic::Security::Agent.agent.event_drop_count.increment if message.jsonName == :Event
|
125
|
-
NewRelic::Security::Agent.agent.event_processor.send_critical_message(exception.message, "SEVERE", caller_locations[0].to_s, Thread.current.name, exception)
|
126
144
|
end
|
127
145
|
|
128
146
|
def close(reconnect = true)
|
147
|
+
NewRelic::Security::Agent.config.disable_security
|
148
|
+
NewRelic::Security::Agent.logger.info "Flushing eventQ (#{NewRelic::Security::Agent.agent.event_processor.eventQ.size} events) and closing websocket connection"
|
149
|
+
NewRelic::Security::Agent.agent.event_processor&.eventQ&.clear
|
150
|
+
@iast_client&.iast_data_transfer_request_processor_thread&.kill
|
151
|
+
stop_ping_thread
|
129
152
|
@ws.close(reconnect) if @ws
|
130
153
|
end
|
131
154
|
|
@@ -134,8 +157,22 @@ module NewRelic::Security
|
|
134
157
|
false
|
135
158
|
end
|
136
159
|
|
160
|
+
def start_ping_thread
|
161
|
+
@ping_thread = Thread.new do
|
162
|
+
loop do
|
163
|
+
sleep 30
|
164
|
+
@ws.send(EMPTY_STRING, :type => :ping)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
137
169
|
private
|
138
170
|
|
171
|
+
def stop_ping_thread
|
172
|
+
@ping_thread&.kill
|
173
|
+
@ping_thread = nil
|
174
|
+
end
|
175
|
+
|
139
176
|
def ingnored_vul_categories
|
140
177
|
list = []
|
141
178
|
list << FILE_OPERATION << FILE_INTEGRITY if NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.invalid_file_access']
|
@@ -114,12 +114,14 @@ module NewRelic::Security
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
when :grape
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
117
|
+
if defined?(::Grape::API)
|
118
|
+
ObjectSpace.each_object(Class).select { |klass| klass < ::Grape::API }.each do |api_class|
|
119
|
+
api_class.routes.each do |route|
|
120
|
+
http_method = route.request_method || route.options[:method]
|
121
|
+
NewRelic::Security::Agent.agent.route_map << "#{http_method}@#{route.pattern.origin}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
123
125
|
when :padrino
|
124
126
|
if router.instance_of?(::Padrino::PathRouter::Router)
|
125
127
|
router.instance_variable_get(:@routes).each do |route|
|
@@ -127,17 +129,19 @@ module NewRelic::Security
|
|
127
129
|
end
|
128
130
|
end
|
129
131
|
when :roda
|
130
|
-
NewRelic::Security::Agent.logger.
|
132
|
+
NewRelic::Security::Agent.logger.debug "TODO: Roda is a routing tree web toolkit, which generates route dynamically, hence route extraction is not possible."
|
131
133
|
when :grpc
|
132
134
|
router.owner.superclass.public_instance_methods(false).each do |m|
|
133
135
|
NewRelic::Security::Agent.agent.route_map << "*@/#{router.owner}/#{m}"
|
134
136
|
end
|
137
|
+
when :rack
|
138
|
+
# TODO: API enpointes(routes) extraction for rack
|
135
139
|
else
|
136
140
|
NewRelic::Security::Agent.logger.error "Unable to get app routes as Framework not detected"
|
137
141
|
end
|
138
142
|
disable_object_space_in_jruby if NewRelic::Security::Agent.config[:jruby_objectspace_enabled]
|
139
143
|
NewRelic::Security::Agent.logger.debug "ALL ROUTES : #{NewRelic::Security::Agent.agent.route_map}"
|
140
|
-
NewRelic::Security::Agent.agent.event_processor&.send_application_url_mappings
|
144
|
+
NewRelic::Security::Agent.agent.event_processor&.send_application_url_mappings unless NewRelic::Security::Agent.agent.route_map.empty?
|
141
145
|
rescue Exception => exception
|
142
146
|
NewRelic::Security::Agent.logger.error "Error in get app routes : #{exception.inspect} #{exception.backtrace}"
|
143
147
|
end
|
@@ -199,14 +203,14 @@ module NewRelic::Security
|
|
199
203
|
end
|
200
204
|
|
201
205
|
def enable_object_space_in_jruby
|
202
|
-
if RUBY_ENGINE == 'jruby' && !JRuby.objectspace
|
206
|
+
if RUBY_ENGINE == 'jruby' && JRuby.respond_to?(:objectspace) && !JRuby.objectspace
|
203
207
|
JRuby.objectspace = true
|
204
208
|
NewRelic::Security::Agent.config.jruby_objectspace_enabled = true
|
205
209
|
end
|
206
210
|
end
|
207
211
|
|
208
212
|
def disable_object_space_in_jruby
|
209
|
-
if RUBY_ENGINE == 'jruby' && JRuby.objectspace
|
213
|
+
if RUBY_ENGINE == 'jruby' && JRuby.respond_to?(:objectspace) && JRuby.objectspace
|
210
214
|
JRuby.objectspace = false
|
211
215
|
NewRelic::Security::Agent.config.jruby_objectspace_enabled = false
|
212
216
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module NewRelic::Security
|
2
|
+
module Instrumentation
|
3
|
+
module GraphQL
|
4
|
+
module Query
|
5
|
+
module Executor
|
6
|
+
module Chain
|
7
|
+
def self.instrument!
|
8
|
+
::GraphQL::Query::Executor.class_eval do
|
9
|
+
class << self
|
10
|
+
include NewRelic::Security::Instrumentation::GraphQL::Query::Executor
|
11
|
+
|
12
|
+
alias_method :execute_without_security, :execute
|
13
|
+
|
14
|
+
def execute
|
15
|
+
execute_on_enter { return execute_without_security }
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'prepend'
|
2
|
+
require_relative 'chain'
|
3
|
+
|
4
|
+
module NewRelic::Security
|
5
|
+
module Instrumentation
|
6
|
+
module GraphQL::Query::Executor
|
7
|
+
|
8
|
+
GRAPHQL_QUERY = 'GRAPHQL_QUERY'.freeze
|
9
|
+
GRAPHQL_VARIABLE = 'GRAPHQL_VARIABLE'.freeze
|
10
|
+
STAR_DOT_QUERY = '*.query'.freeze
|
11
|
+
STAR_DOT_VARIABLES = '*.variables'.freeze
|
12
|
+
|
13
|
+
def execute_on_enter
|
14
|
+
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
15
|
+
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
16
|
+
ctxt.custom_data_type[STAR_DOT_QUERY] = GRAPHQL_QUERY if query.query_string
|
17
|
+
ctxt.custom_data_type[STAR_DOT_VARIABLES] = GRAPHQL_VARIABLE if query.instance_variable_get(:@provided_variables)
|
18
|
+
rescue => exception
|
19
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
20
|
+
ensure
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:graphql, ::GraphQL::Query::Executor, ::NewRelic::Security::Instrumentation::GraphQL::Query::Executor)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module NewRelic::Security
|
2
|
+
module Instrumentation
|
3
|
+
module GraphQL
|
4
|
+
module Query
|
5
|
+
module Executor
|
6
|
+
module Prepend
|
7
|
+
include NewRelic::Security::Instrumentation::GraphQL::Query::Executor
|
8
|
+
|
9
|
+
def execute
|
10
|
+
execute_on_enter { return super }
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -99,9 +99,9 @@ module NewRelic::Security
|
|
99
99
|
|
100
100
|
alias_method :popen_without_security, :popen
|
101
101
|
|
102
|
-
def popen(*var)
|
102
|
+
def popen(*var, &block)
|
103
103
|
retval = nil
|
104
|
-
event = popen_on_enter(*var) { retval = popen_without_security(*var) }
|
104
|
+
event = popen_on_enter(*var) { retval = popen_without_security(*var, &block) }
|
105
105
|
popen_on_exit(event) { return retval }
|
106
106
|
end
|
107
107
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NewRelic::Security
|
2
|
+
module Instrumentation
|
3
|
+
module Rack
|
4
|
+
module Builder
|
5
|
+
module Chain
|
6
|
+
def self.instrument!
|
7
|
+
::Rack::Builder.class_eval do
|
8
|
+
include NewRelic::Security::Instrumentation::Rack::Builder
|
9
|
+
|
10
|
+
alias_method :call_without_security, :call
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
retval = nil
|
14
|
+
event = call_on_enter(env) { retval = call_without_security(env) }
|
15
|
+
call_on_exit(event, retval) { return retval }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'prepend'
|
2
|
+
require_relative 'chain'
|
3
|
+
|
4
|
+
module NewRelic::Security
|
5
|
+
module Instrumentation
|
6
|
+
module Rack::Builder
|
7
|
+
|
8
|
+
def call_on_enter(env)
|
9
|
+
event = nil
|
10
|
+
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
11
|
+
return unless NewRelic::Security::Agent.config[:enabled]
|
12
|
+
NewRelic::Security::Agent.config.update_port = NewRelic::Security::Agent::Utils.app_port(env) unless NewRelic::Security::Agent.config[:listen_port]
|
13
|
+
NewRelic::Security::Agent::Utils.get_app_routes(:rack) if NewRelic::Security::Agent.agent.route_map.empty?
|
14
|
+
NewRelic::Security::Agent::Control::HTTPContext.set_context(env)
|
15
|
+
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
16
|
+
ctxt.route = "#{env[REQUEST_METHOD]}@#{env[PATH_INFO]}" if ctxt
|
17
|
+
NewRelic::Security::Agent::Utils.parse_fuzz_header(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
18
|
+
rescue => exception
|
19
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
20
|
+
ensure
|
21
|
+
yield
|
22
|
+
return event
|
23
|
+
end
|
24
|
+
|
25
|
+
def call_on_exit(event, retval)
|
26
|
+
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
|
27
|
+
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
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
|
+
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])
|
31
|
+
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
32
|
+
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
33
|
+
rescue => exception
|
34
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
35
|
+
ensure
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:rack, NewRelic::Security::Agent.config[:app_class].class, ::NewRelic::Security::Instrumentation::Rack::Builder) if NewRelic::Security::Agent.config[:framework] == :rack
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module NewRelic::Security
|
2
|
+
module Instrumentation
|
3
|
+
module Rack
|
4
|
+
module Builder
|
5
|
+
module Prepend
|
6
|
+
include NewRelic::Security::Instrumentation::Rack::Builder
|
7
|
+
|
8
|
+
def call(env, &block)
|
9
|
+
retval = nil
|
10
|
+
event = call_on_enter(env) { retval = super }
|
11
|
+
call_on_exit(event, retval) { return retval }
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Prateek Sen
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: newrelic_rpm
|
@@ -208,6 +208,9 @@ files:
|
|
208
208
|
- lib/newrelic_security/instrumentation-security/grape/chain.rb
|
209
209
|
- lib/newrelic_security/instrumentation-security/grape/instrumentation.rb
|
210
210
|
- lib/newrelic_security/instrumentation-security/grape/prepend.rb
|
211
|
+
- lib/newrelic_security/instrumentation-security/graphql/chain.rb
|
212
|
+
- lib/newrelic_security/instrumentation-security/graphql/instrumentation.rb
|
213
|
+
- lib/newrelic_security/instrumentation-security/graphql/prepend.rb
|
211
214
|
- lib/newrelic_security/instrumentation-security/grpc/client/chain.rb
|
212
215
|
- lib/newrelic_security/instrumentation-security/grpc/client/instrumentation.rb
|
213
216
|
- lib/newrelic_security/instrumentation-security/grpc/client/prepend.rb
|
@@ -258,6 +261,9 @@ files:
|
|
258
261
|
- lib/newrelic_security/instrumentation-security/pty/chain.rb
|
259
262
|
- lib/newrelic_security/instrumentation-security/pty/instrumentation.rb
|
260
263
|
- lib/newrelic_security/instrumentation-security/pty/prepend.rb
|
264
|
+
- lib/newrelic_security/instrumentation-security/rack/chain.rb
|
265
|
+
- lib/newrelic_security/instrumentation-security/rack/instrumentation.rb
|
266
|
+
- lib/newrelic_security/instrumentation-security/rack/prepend.rb
|
261
267
|
- lib/newrelic_security/instrumentation-security/rails/chain.rb
|
262
268
|
- lib/newrelic_security/instrumentation-security/rails/instrumentation.rb
|
263
269
|
- lib/newrelic_security/instrumentation-security/rails/prepend.rb
|
@@ -324,7 +330,7 @@ metadata:
|
|
324
330
|
documentation_uri: https://docs.newrelic.com/docs/iast/introduction/
|
325
331
|
source_code_uri: https://github.com/newrelic/csec-ruby-agent
|
326
332
|
homepage_uri: https://github.com/newrelic/csec-ruby-agent
|
327
|
-
post_install_message:
|
333
|
+
post_install_message:
|
328
334
|
rdoc_options: []
|
329
335
|
require_paths:
|
330
336
|
- lib
|
@@ -340,7 +346,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
340
346
|
version: 1.3.1
|
341
347
|
requirements: []
|
342
348
|
rubygems_version: 3.4.19
|
343
|
-
signing_key:
|
349
|
+
signing_key:
|
344
350
|
specification_version: 4
|
345
351
|
summary: Extension for newrelic_rpm with security feature
|
346
352
|
test_files: []
|