newrelic_security 0.2.0 → 0.4.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/.github/workflows/pr_ci.yml +4 -4
- data/.github/workflows/release.yml +1 -1
- data/.github/workflows/rubocop.yml +1 -1
- data/CHANGELOG.md +90 -1
- data/Gemfile_test +3 -0
- data/README.md +1 -0
- data/THIRD_PARTY_NOTICES.md +8 -0
- data/lib/newrelic_security/agent/agent.rb +22 -4
- data/lib/newrelic_security/agent/configuration/manager.rb +65 -7
- data/lib/newrelic_security/agent/control/application_runtime_error.rb +1 -1
- data/lib/newrelic_security/agent/control/collector.rb +41 -4
- data/lib/newrelic_security/agent/control/control_command.rb +2 -3
- data/lib/newrelic_security/agent/control/error_reporting.rb +8 -6
- data/lib/newrelic_security/agent/control/event.rb +15 -1
- data/lib/newrelic_security/agent/control/event_processor.rb +25 -14
- data/lib/newrelic_security/agent/control/event_subscriber.rb +6 -8
- data/lib/newrelic_security/agent/control/health_check.rb +4 -0
- data/lib/newrelic_security/agent/control/http_context.rb +10 -6
- data/lib/newrelic_security/agent/control/iast_client.rb +24 -11
- data/lib/newrelic_security/agent/control/reflected_xss.rb +3 -4
- data/lib/newrelic_security/agent/control/scan_scheduler.rb +77 -0
- data/lib/newrelic_security/agent/control/websocket_client.rb +71 -16
- data/lib/newrelic_security/agent/utils/agent_utils.rb +25 -17
- data/lib/newrelic_security/constants.rb +1 -2
- data/lib/newrelic_security/instrumentation-security/async-http/instrumentation.rb +2 -13
- data/lib/newrelic_security/instrumentation-security/curb/instrumentation.rb +1 -14
- data/lib/newrelic_security/instrumentation-security/ethon/chain.rb +0 -6
- data/lib/newrelic_security/instrumentation-security/ethon/instrumentation.rb +7 -42
- data/lib/newrelic_security/instrumentation-security/ethon/prepend.rb +0 -4
- data/lib/newrelic_security/instrumentation-security/excon/instrumentation.rb +3 -13
- data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +1 -0
- 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/grpc/server/instrumentation.rb +3 -2
- data/lib/newrelic_security/instrumentation-security/httpclient/instrumentation.rb +4 -28
- data/lib/newrelic_security/instrumentation-security/httprb/instrumentation.rb +1 -12
- data/lib/newrelic_security/instrumentation-security/httpx/instrumentation.rb +1 -15
- data/lib/newrelic_security/instrumentation-security/instrumentation_utils.rb +0 -17
- 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/net_http/instrumentation.rb +6 -23
- data/lib/newrelic_security/instrumentation-security/net_ldap/instrumentation.rb +1 -1
- data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +1 -0
- data/lib/newrelic_security/instrumentation-security/patron/instrumentation.rb +2 -15
- 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/instrumentation-security/rails/instrumentation.rb +1 -0
- data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +1 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +1 -0
- data/lib/newrelic_security/newrelic-security-api/api.rb +1 -1
- data/lib/newrelic_security/parse-cron/cron_parser.rb +294 -0
- data/lib/newrelic_security/version.rb +1 -1
- data/lib/newrelic_security/websocket-client-simple/client.rb +5 -1
- data/newrelic_security.gemspec +1 -1
- metadata +15 -7
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
|
@@ -72,6 +72,6 @@ jobs:
|
|
72
72
|
with:
|
73
73
|
token: ${{ secrets.GITHUB_TOKEN }}
|
74
74
|
resultPath: lib/coverage_results/.last_run.json
|
75
|
-
failedThreshold:
|
76
|
-
failedThresholdBranch:
|
75
|
+
failedThreshold: 67
|
76
|
+
failedThresholdBranch: 25
|
77
77
|
|
@@ -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,10 +1,99 @@
|
|
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
|
+
|
31
|
+
## v0.3.0
|
32
|
+
|
33
|
+
Version 0.3.0 introduces more control on IAST scanning through new configs(exclude_from_iast_scan, scan_schedule & scan_controllers) and
|
34
|
+
features like API inventory for gRPC server and IAST scan start related timestamps.
|
35
|
+
|
36
|
+
Updated json_version: **1.2.8**
|
37
|
+
|
38
|
+
- Feature: IAST scan exclusion for apis, http request parameters(header, query & body) & IAST detection categories and scan scheduling through delay, duration & cron schedule. [PR#131](https://github.com/newrelic/csec-ruby-agent/pull/131)
|
39
|
+
|
40
|
+
- Feature: IAST scan request rate limit to control IAST scan request firing. [PR#132](https://github.com/newrelic/csec-ruby-agent/pull/132)
|
41
|
+
|
42
|
+
- Feature: API endpoints support for gRPC server applications. [PR#143](https://github.com/newrelic/csec-ruby-agent/pull/143)
|
43
|
+
|
44
|
+
- Feature: Reporting of IAST scanning application procStartTime, trafficStartedTime & scanStartTime. [PR#136](https://github.com/newrelic/csec-ruby-agent/pull/136)
|
45
|
+
|
46
|
+
- Misc Chore: Optimised SSRF events parameters to send only URL in parameters. [PR#129](https://github.com/newrelic/csec-ruby-agent/pull/129)
|
47
|
+
|
48
|
+
##### New security configs
|
49
|
+
|
50
|
+
```yaml
|
51
|
+
security:
|
52
|
+
exclude_from_iast_scan:
|
53
|
+
api: []
|
54
|
+
http_request_parameters:
|
55
|
+
header: []
|
56
|
+
query: []
|
57
|
+
body: []
|
58
|
+
iast_detection_category:
|
59
|
+
insecure_settings: false
|
60
|
+
invalid_file_access: false
|
61
|
+
sql_injection: false
|
62
|
+
nosql_injection: false
|
63
|
+
ldap_injection: false
|
64
|
+
javascript_injection: false
|
65
|
+
command_injection: false
|
66
|
+
xpath_injection: false
|
67
|
+
ssrf: false
|
68
|
+
rxss: false
|
69
|
+
scan_schedule:
|
70
|
+
delay: 0
|
71
|
+
duration: 0
|
72
|
+
schedule: ""
|
73
|
+
always_sample_traces: false
|
74
|
+
scan_controllers:
|
75
|
+
iast_scan_request_rate_limit: 3600
|
76
|
+
```
|
77
|
+
|
78
|
+
##### Deprecated security configs (will be removed in next major release v1.0.0)
|
79
|
+
```yaml
|
80
|
+
security:
|
81
|
+
request:
|
82
|
+
body_limit: 300
|
83
|
+
detection:
|
84
|
+
rci:
|
85
|
+
enabled: true
|
86
|
+
rxss:
|
87
|
+
enabled: true
|
88
|
+
deserialization:
|
89
|
+
enabled: true
|
90
|
+
```
|
91
|
+
|
3
92
|
## v0.2.0
|
4
93
|
|
5
94
|
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
95
|
|
7
|
-
- Feature: Unhandled and 5xx error
|
96
|
+
- Feature: Unhandled and 5xx error reporting [PR#134](https://github.com/newrelic/csec-ruby-agent/pull/134)
|
8
97
|
|
9
98
|
- Bugfix: Fix for API route not present in rails7 [PR#127](https://github.com/newrelic/csec-ruby-agent/pull/127)
|
10
99
|
|
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
data/THIRD_PARTY_NOTICES.md
CHANGED
@@ -34,3 +34,11 @@ Distributed under the following license(s):
|
|
34
34
|
|
35
35
|
* [The MIT License](http://opensource.org/licenses/MIT)
|
36
36
|
|
37
|
+
|
38
|
+
## [parse-cron](https://github.com/siebertm/parse-cron)
|
39
|
+
|
40
|
+
Copyright (C) 2013 Michael Siebert
|
41
|
+
|
42
|
+
Distributed under the following license(s):
|
43
|
+
|
44
|
+
* [The MIT License](http://opensource.org/licenses/MIT)
|
@@ -19,13 +19,14 @@ require 'newrelic_security/agent/control/event_stats'
|
|
19
19
|
require 'newrelic_security/agent/control/exit_event'
|
20
20
|
require 'newrelic_security/agent/control/application_runtime_error'
|
21
21
|
require 'newrelic_security/agent/control/error_reporting'
|
22
|
+
require 'newrelic_security/agent/control/scan_scheduler'
|
22
23
|
require 'newrelic_security/instrumentation-security/instrumentation_loader'
|
23
24
|
|
24
25
|
module NewRelic::Security
|
25
26
|
module Agent
|
26
27
|
class Agent
|
27
28
|
|
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
|
29
|
+
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, :scan_scheduler
|
29
30
|
|
30
31
|
def initialize
|
31
32
|
NewRelic::Security::Agent.config
|
@@ -44,6 +45,7 @@ module NewRelic::Security
|
|
44
45
|
@rasp_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
45
46
|
@exit_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
46
47
|
@error_reporting = NewRelic::Security::Agent::Control::ErrorReporting.new
|
48
|
+
@scan_scheduler = NewRelic::Security::Agent::Control::ScanScheduler.new
|
47
49
|
end
|
48
50
|
|
49
51
|
def init
|
@@ -59,22 +61,38 @@ module NewRelic::Security
|
|
59
61
|
NewRelic::Security::Agent.logger.error "Exception in security agent init: #{exception.inspect} #{exception.backtrace}\n"
|
60
62
|
end
|
61
63
|
|
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
|
67
|
+
@iast_client&.fuzzQ&.clear
|
68
|
+
@iast_client&.completed_requests&.clear
|
69
|
+
@iast_client&.pending_request_ids&.clear
|
70
|
+
@iast_client&.iast_data_transfer_request_processor_thread&.kill
|
71
|
+
NewRelic::Security::Agent.config.disable_security
|
72
|
+
stop_websocket_client_if_open
|
73
|
+
end
|
74
|
+
|
62
75
|
def start_websocket_client
|
63
|
-
|
76
|
+
stop_websocket_client_if_open
|
64
77
|
@websocket_client = NewRelic::Security::Agent::Control::WebsocketClient.instance.connect
|
65
78
|
end
|
66
79
|
|
80
|
+
def stop_websocket_client_if_open
|
81
|
+
NewRelic::Security::Agent::Control::WebsocketClient.instance.close(false) if NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open?
|
82
|
+
end
|
83
|
+
|
67
84
|
def start_event_processor
|
68
|
-
@event_processor&.
|
85
|
+
@event_processor&.event_dequeue_threads&.each { |t| t&.kill }
|
69
86
|
@event_processor&.healthcheck_thread&.kill
|
70
87
|
@event_processor = nil
|
71
88
|
@event_processor = NewRelic::Security::Agent::Control::EventProcessor.new
|
72
89
|
end
|
73
90
|
|
74
91
|
def start_iast_client
|
75
|
-
@iast_client&.iast_dequeue_threads&.each { |t| t
|
92
|
+
@iast_client&.iast_dequeue_threads&.each { |t| t&.kill }
|
76
93
|
@iast_client&.iast_data_transfer_request_processor_thread&.kill
|
77
94
|
@iast_client = nil
|
95
|
+
NewRelic::Security::Agent.logger.info "Starting IAST client now at current time: #{Time.now}"
|
78
96
|
@iast_client = NewRelic::Security::Agent::Control::IASTClient.new
|
79
97
|
end
|
80
98
|
|
@@ -27,19 +27,45 @@ module NewRelic::Security
|
|
27
27
|
@cache[:log_level] = ::NewRelic::Agent.config[:log_level]
|
28
28
|
@cache[:high_security] = ::NewRelic::Agent.config[:high_security]
|
29
29
|
@cache[:'agent.enabled'] = ::NewRelic::Agent.config[:'security.agent.enabled']
|
30
|
-
@cache[:enabled] = ::NewRelic::Agent.config[:'security.enabled']
|
30
|
+
@cache[:'security.enabled'] = ::NewRelic::Agent.config[:'security.enabled']
|
31
|
+
@cache[:enabled] = false
|
31
32
|
@cache[:mode] = ::NewRelic::Agent.config[:'security.mode']
|
32
33
|
@cache[:validator_service_url] = ::NewRelic::Agent.config[:'security.validator_service_url']
|
33
|
-
|
34
|
-
@cache[:'security.detection.
|
35
|
-
@cache[:'security.detection.
|
34
|
+
# TODO: Remove security.detection.* & security.request.body_limit in next major release
|
35
|
+
@cache[:'security.detection.rci.enabled'] = ::NewRelic::Agent.config[:'security.detection.rci.enabled'].nil? ? true : ::NewRelic::Agent.config[:'security.detection.rci.enabled']
|
36
|
+
@cache[:'security.detection.rxss.enabled'] = ::NewRelic::Agent.config[:'security.detection.rxss.enabled'].nil? ? true : ::NewRelic::Agent.config[:'security.detection.rxss.enabled']
|
37
|
+
@cache[:'security.detection.deserialization.enabled'] = ::NewRelic::Agent.config[:'security.detection.deserialization.enabled'].nil? ? true : ::NewRelic::Agent.config[:'security.detection.deserialization.enabled']
|
38
|
+
@cache[:'security.scan_controllers.iast_scan_request_rate_limit'] = ::NewRelic::Agent.config[:'security.scan_controllers.iast_scan_request_rate_limit'].to_i
|
36
39
|
@cache[:framework] = detect_framework
|
40
|
+
@cache[:app_class] = detect_app_class if @cache[:framework] == :rack
|
37
41
|
@cache[:'security.application_info.port'] = ::NewRelic::Agent.config[:'security.application_info.port'].to_i
|
38
|
-
@cache[:'security.request.body_limit'] = ::NewRelic::Agent.config[:'security.request.body_limit'].to_i > 0 ? ::NewRelic::Agent.config[:'security.request.body_limit'].to_i : 300
|
39
42
|
@cache[:listen_port] = nil
|
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.
|
44
|
+
@cache[:traffic_start_time] = nil
|
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']
|
40
48
|
@cache[:app_root] = NewRelic::Security::Agent::Utils.app_root
|
41
49
|
@cache[:jruby_objectspace_enabled] = false
|
42
|
-
@cache[:json_version] = :'1.2.
|
50
|
+
@cache[:json_version] = :'1.2.9'
|
51
|
+
@cache[:'security.exclude_from_iast_scan.api'] = convert_to_regexp_list(::NewRelic::Agent.config[:'security.exclude_from_iast_scan.api'])
|
52
|
+
@cache[:'security.exclude_from_iast_scan.http_request_parameters.header'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.header']
|
53
|
+
@cache[:'security.exclude_from_iast_scan.http_request_parameters.query'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.query']
|
54
|
+
@cache[:'security.exclude_from_iast_scan.http_request_parameters.body'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.body']
|
55
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.insecure_settings'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.insecure_settings']
|
56
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.invalid_file_access'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.invalid_file_access']
|
57
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.sql_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.sql_injection']
|
58
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.nosql_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.nosql_injection']
|
59
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.ldap_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ldap_injection']
|
60
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.javascript_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.javascript_injection']
|
61
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.command_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.command_injection']
|
62
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.xpath_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.xpath_injection']
|
63
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.ssrf'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ssrf']
|
64
|
+
@cache[:'security.exclude_from_iast_scan.iast_detection_category.rxss'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.rxss']
|
65
|
+
@cache[:'security.scan_schedule.delay'] = ::NewRelic::Agent.config[:'security.scan_schedule.delay'].to_i
|
66
|
+
@cache[:'security.scan_schedule.duration'] = ::NewRelic::Agent.config[:'security.scan_schedule.duration'].to_i
|
67
|
+
@cache[:'security.scan_schedule.schedule'] = ::NewRelic::Agent.config[:'security.scan_schedule.schedule']
|
68
|
+
@cache[:'security.scan_schedule.always_sample_traces'] = ::NewRelic::Agent.config[:'security.scan_schedule.always_sample_traces']
|
43
69
|
|
44
70
|
@environment_source = NewRelic::Security::Agent::Configuration::EnvironmentSource.new
|
45
71
|
@server_source = NewRelic::Security::Agent::Configuration::ServerSource.new
|
@@ -93,6 +119,14 @@ module NewRelic::Security
|
|
93
119
|
@cache[:listen_port] = listen_port
|
94
120
|
end
|
95
121
|
|
122
|
+
def traffic_start_time=(traffic_start_time)
|
123
|
+
@cache[:traffic_start_time] = traffic_start_time
|
124
|
+
end
|
125
|
+
|
126
|
+
def scan_start_time=(scan_start_time)
|
127
|
+
@cache[:scan_start_time] = scan_start_time
|
128
|
+
end
|
129
|
+
|
96
130
|
def app_server=(app_server)
|
97
131
|
@cache[:app_server] = app_server
|
98
132
|
end
|
@@ -122,6 +156,16 @@ module NewRelic::Security
|
|
122
156
|
return :sinatra if defined?(::Sinatra)
|
123
157
|
return :roda if defined?(::Roda)
|
124
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
|
125
169
|
end
|
126
170
|
|
127
171
|
def generate_uuid
|
@@ -136,7 +180,8 @@ module NewRelic::Security
|
|
136
180
|
end
|
137
181
|
::SecureRandom.uuid
|
138
182
|
rescue Exception => exception
|
139
|
-
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
|
140
185
|
end
|
141
186
|
|
142
187
|
def create_uuid
|
@@ -171,6 +216,19 @@ module NewRelic::Security
|
|
171
216
|
def generate_key(entity_guid)
|
172
217
|
::OpenSSL::PKCS5.pbkdf2_hmac(entity_guid, entity_guid[0..15], 1024, 32, SHA1)
|
173
218
|
end
|
219
|
+
|
220
|
+
def current_time_millis
|
221
|
+
(Time.now.to_f * 1000).to_i
|
222
|
+
end
|
223
|
+
|
224
|
+
def convert_to_regexp_list(value_list)
|
225
|
+
value_list.map do |value|
|
226
|
+
next unless value && !value.empty?
|
227
|
+
value = "^#{value}" if value[0] != '^'
|
228
|
+
value = "#{value}$" if value[-1] != '$'
|
229
|
+
/#{value}/
|
230
|
+
end
|
231
|
+
end
|
174
232
|
end
|
175
233
|
end
|
176
234
|
end
|
@@ -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
|
@@ -19,6 +19,8 @@ module NewRelic::Security
|
|
19
19
|
def collect(case_type, args, event_category = nil, **keyword_args)
|
20
20
|
return unless NewRelic::Security::Agent.config[:enabled]
|
21
21
|
return if NewRelic::Security::Agent::Control::HTTPContext.get_context.nil? && NewRelic::Security::Agent::Control::GRPCContext.get_context.nil?
|
22
|
+
return if check_and_exclude_from_iast_scan_for_api
|
23
|
+
return if check_and_exclude_from_iast_scan_for_detection_category(case_type)
|
22
24
|
args.map! { |file| Pathname.new(file).relative? ? File.join(Dir.pwd, file) : file } if [FILE_OPERATION, FILE_INTEGRITY].include?(case_type)
|
23
25
|
|
24
26
|
event = NewRelic::Security::Agent::Control::Event.new(case_type, args, event_category)
|
@@ -43,8 +45,9 @@ module NewRelic::Security
|
|
43
45
|
# In rails 5 method name keeps chaning for same api call (ex: _app_views_sqli_sqlinjectionattackcase_html_erb__1999281606898621405_2624809100).
|
44
46
|
# Hence, considering only frame absolute_path & lineno for apiId calculation.
|
45
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
|
46
49
|
event.apiId = "#{case_type}-#{calculate_api_id(stk[0..user_frame_index].map { |frame| "#{frame.absolute_path}:#{frame.lineno}" }, event.httpRequest[:method], route)}"
|
47
|
-
stk.delete_if {|frame| frame.path.match(/newrelic_security/) || frame.path.match(/new_relic/)}
|
50
|
+
stk.delete_if { |frame| frame.path.match?(/newrelic_security/) || frame.path.match?(/new_relic/) }
|
48
51
|
user_frame_index = get_user_frame_index(stk)
|
49
52
|
return if case_type != REFLECTED_XSS && user_frame_index == -1 # TODO: Add log message here: "Filtered because User Stk frame NOT FOUND \r\n"
|
50
53
|
if user_frame_index != -1
|
@@ -58,9 +61,9 @@ module NewRelic::Security
|
|
58
61
|
event.lineNumber = stk[0].lineno
|
59
62
|
end
|
60
63
|
event.stacktrace = stk[0..user_frame_index].map(&:to_s)
|
61
|
-
NewRelic::Security::Agent.agent.event_processor
|
62
|
-
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]
|
63
|
-
NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId] << event.id
|
64
|
+
NewRelic::Security::Agent.agent.event_processor&.send_event(event)
|
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]
|
66
|
+
NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId] << event.id
|
64
67
|
end
|
65
68
|
event
|
66
69
|
rescue Exception => exception
|
@@ -71,6 +74,10 @@ module NewRelic::Security
|
|
71
74
|
else
|
72
75
|
NewRelic::Security::Agent.agent.rasp_event_stats.error_count.increment
|
73
76
|
end
|
77
|
+
ensure
|
78
|
+
event = nil
|
79
|
+
stk = nil
|
80
|
+
route = nil
|
74
81
|
end
|
75
82
|
|
76
83
|
private
|
@@ -78,6 +85,7 @@ module NewRelic::Security
|
|
78
85
|
def get_user_frame_index(stk)
|
79
86
|
return -1 if NewRelic::Security::Agent.config[:app_root].nil?
|
80
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])
|
81
89
|
return index if val.path.start_with?(NewRelic::Security::Agent.config[:app_root])
|
82
90
|
end
|
83
91
|
return -1
|
@@ -111,6 +119,35 @@ module NewRelic::Security
|
|
111
119
|
}
|
112
120
|
end
|
113
121
|
|
122
|
+
def check_and_exclude_from_iast_scan_for_api
|
123
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.api'].each do |api|
|
124
|
+
return true if api&.match?(NewRelic::Security::Agent::Control::HTTPContext.get_context.url)
|
125
|
+
end
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
|
129
|
+
def check_and_exclude_from_iast_scan_for_detection_category(case_type)
|
130
|
+
case case_type
|
131
|
+
when FILE_OPERATION, FILE_INTEGRITY
|
132
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.invalid_file_access']
|
133
|
+
when SQL_DB_COMMAND
|
134
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.sql_injection']
|
135
|
+
when NOSQL_DB_COMMAND
|
136
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.nosql_injection']
|
137
|
+
when LDAP
|
138
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ldap_injection']
|
139
|
+
when SYSTEM_COMMAND
|
140
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.command_injection']
|
141
|
+
when XPATH
|
142
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.xpath_injection']
|
143
|
+
when HTTP_REQUEST
|
144
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ssrf']
|
145
|
+
when REFLECTED_XSS
|
146
|
+
NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.rxss']
|
147
|
+
else
|
148
|
+
false
|
149
|
+
end
|
150
|
+
end
|
114
151
|
end
|
115
152
|
end
|
116
153
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'securerandom'
|
2
3
|
|
3
4
|
module NewRelic::Security
|
4
5
|
module Agent
|
@@ -49,8 +50,6 @@ module NewRelic::Security
|
|
49
50
|
message_object[:arguments].each { |processed_id| NewRelic::Security::Agent.agent.iast_client.completed_requests.delete(processed_id) }
|
50
51
|
when 100
|
51
52
|
NewRelic::Security::Agent.logger.debug "Control command : '100', #{message_object.to_json}"
|
52
|
-
::NewRelic::Agent.instance.events.notify(:security_policy_received, message_object[:data])
|
53
|
-
# TODO: Update policy from file here, if enabled.
|
54
53
|
when 101
|
55
54
|
|
56
55
|
when 102
|
@@ -95,7 +94,7 @@ module NewRelic::Security
|
|
95
94
|
NewRelic::Security::Agent.agent.iast_client.completed_requests.clear if NewRelic::Security::Agent.agent.iast_client
|
96
95
|
NewRelic::Security::Agent.agent.iast_client.pending_request_ids.clear if NewRelic::Security::Agent.agent.iast_client
|
97
96
|
NewRelic::Security::Agent.config.disable_security
|
98
|
-
Thread.new { NewRelic::Security::Agent.agent.reconnect(
|
97
|
+
Thread.new { NewRelic::Security::Agent.agent.reconnect(SecureRandom.random_number(10) + 5) }
|
99
98
|
end
|
100
99
|
|
101
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
|
|
@@ -30,7 +30,20 @@ module NewRelic::Security
|
|
30
30
|
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
31
31
|
@httpRequest = Hash.new
|
32
32
|
@httpResponse = Hash.new
|
33
|
-
@metaData = {
|
33
|
+
@metaData = {
|
34
|
+
:reflectedMetaData => {
|
35
|
+
:listen_port => NewRelic::Security::Agent.config[:listen_port].to_s
|
36
|
+
},
|
37
|
+
:appServerInfo => {
|
38
|
+
:applicationDirectory => NewRelic::Security::Agent.config[:app_root],
|
39
|
+
:serverBaseDirectory => NewRelic::Security::Agent.config[:app_root]
|
40
|
+
},
|
41
|
+
:skipScanParameters => {
|
42
|
+
:header => NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.header'],
|
43
|
+
:query => NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.query'],
|
44
|
+
:body => NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.body']
|
45
|
+
}
|
46
|
+
}
|
34
47
|
@linkingMetadata = add_linking_metadata
|
35
48
|
@pid = pid
|
36
49
|
@parameters = args
|
@@ -91,6 +104,7 @@ module NewRelic::Security
|
|
91
104
|
http_request[:headers] = ctxt.headers
|
92
105
|
http_request[:contentType] = ctxt.req[CONTENT_TYPE] if ctxt.req.has_key?(CONTENT_TYPE)
|
93
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
|
94
108
|
http_request[:dataTruncated] = ctxt.data_truncated
|
95
109
|
@httpRequest = http_request
|
96
110
|
@metaData[:isClientDetectedFromXFF] = ctxt.headers.has_key?(X_FORWARDED_FOR) ? true : false
|