newrelic_security 0.1.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.
Files changed (205) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  3. data/.github/ISSUE_TEMPLATE/enhancement.md +27 -0
  4. data/.github/actions/simplecov-report/LICENSE +22 -0
  5. data/.github/actions/simplecov-report/README.md +36 -0
  6. data/.github/actions/simplecov-report/__tests__/.keep +0 -0
  7. data/.github/actions/simplecov-report/__tests__/main.test.ts +3 -0
  8. data/.github/actions/simplecov-report/action.yml +25 -0
  9. data/.github/actions/simplecov-report/dist/index.js +10238 -0
  10. data/.github/actions/simplecov-report/dummy_coverage/.last_run.json +5 -0
  11. data/.github/actions/simplecov-report/jest.config.js +11 -0
  12. data/.github/actions/simplecov-report/package.json +51 -0
  13. data/.github/actions/simplecov-report/src/main.ts +54 -0
  14. data/.github/actions/simplecov-report/src/report.ts +28 -0
  15. data/.github/actions/simplecov-report/tsconfig.json +12 -0
  16. data/.github/workflows/pr_ci.yml +77 -0
  17. data/.github/workflows/release.yml +51 -0
  18. data/.github/workflows/repolinter.yml +31 -0
  19. data/.github/workflows/rubocop.yml +17 -0
  20. data/.github/workflows/scripts/rubygems-authenticate.py +13 -0
  21. data/.github/workflows/scripts/rubygems-publish.rb +33 -0
  22. data/.gitignore +72 -0
  23. data/.rubocop.yml +9 -0
  24. data/.rubocop_todo.yml +1414 -0
  25. data/.simplecov +16 -0
  26. data/CHANGELOG.md +69 -0
  27. data/CONTRIBUTING.md +22 -0
  28. data/Gemfile +6 -0
  29. data/Gemfile_test +58 -0
  30. data/LICENSE +43 -0
  31. data/README.md +133 -0
  32. data/README_agent.md +44 -0
  33. data/Rakefile +28 -0
  34. data/THIRD_PARTY_NOTICES.md +36 -0
  35. data/lib/newrelic_security/agent/agent.rb +109 -0
  36. data/lib/newrelic_security/agent/configuration/default_source.rb +8 -0
  37. data/lib/newrelic_security/agent/configuration/environment_source.rb +8 -0
  38. data/lib/newrelic_security/agent/configuration/manager.rb +178 -0
  39. data/lib/newrelic_security/agent/configuration/manual_source.rb +8 -0
  40. data/lib/newrelic_security/agent/configuration/server_source.rb +8 -0
  41. data/lib/newrelic_security/agent/configuration/yaml_source.rb +8 -0
  42. data/lib/newrelic_security/agent/control/app_info.rb +132 -0
  43. data/lib/newrelic_security/agent/control/application_url_mappings.rb +66 -0
  44. data/lib/newrelic_security/agent/control/collector.rb +117 -0
  45. data/lib/newrelic_security/agent/control/control_command.rb +117 -0
  46. data/lib/newrelic_security/agent/control/critical_message.rb +58 -0
  47. data/lib/newrelic_security/agent/control/event.rb +149 -0
  48. data/lib/newrelic_security/agent/control/event_counter.rb +28 -0
  49. data/lib/newrelic_security/agent/control/event_processor.rb +134 -0
  50. data/lib/newrelic_security/agent/control/event_stats.rb +26 -0
  51. data/lib/newrelic_security/agent/control/event_subscriber.rb +28 -0
  52. data/lib/newrelic_security/agent/control/exit_event.rb +38 -0
  53. data/lib/newrelic_security/agent/control/fuzz_request.rb +18 -0
  54. data/lib/newrelic_security/agent/control/grpc_context.rb +57 -0
  55. data/lib/newrelic_security/agent/control/health_check.rb +136 -0
  56. data/lib/newrelic_security/agent/control/http_context.rb +73 -0
  57. data/lib/newrelic_security/agent/control/iast_client.rb +151 -0
  58. data/lib/newrelic_security/agent/control/iast_data_transfer_request.rb +32 -0
  59. data/lib/newrelic_security/agent/control/reflected_xss.rb +258 -0
  60. data/lib/newrelic_security/agent/control/websocket_client.rb +131 -0
  61. data/lib/newrelic_security/agent/logging/init_logger.rb +91 -0
  62. data/lib/newrelic_security/agent/logging/logger.rb +92 -0
  63. data/lib/newrelic_security/agent/logging/null_logger.rb +21 -0
  64. data/lib/newrelic_security/agent/resources/cert.pem +50 -0
  65. data/lib/newrelic_security/agent/utils/agent_utils.rb +219 -0
  66. data/lib/newrelic_security/agent.rb +57 -0
  67. data/lib/newrelic_security/constants.rb +67 -0
  68. data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/chain.rb +70 -0
  69. data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/instrumentation.rb +187 -0
  70. data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/prepend.rb +54 -0
  71. data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/chain.rb +60 -0
  72. data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/instrumentation.rb +143 -0
  73. data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/prepend.rb +48 -0
  74. data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/chain.rb +72 -0
  75. data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/instrumentation.rb +187 -0
  76. data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/prepend.rb +54 -0
  77. data/lib/newrelic_security/instrumentation-security/async-http/chain.rb +21 -0
  78. data/lib/newrelic_security/instrumentation-security/async-http/instrumentation.rb +46 -0
  79. data/lib/newrelic_security/instrumentation-security/async-http/prepend.rb +16 -0
  80. data/lib/newrelic_security/instrumentation-security/curb/chain.rb +26 -0
  81. data/lib/newrelic_security/instrumentation-security/curb/instrumentation.rb +52 -0
  82. data/lib/newrelic_security/instrumentation-security/curb/prepend.rb +18 -0
  83. data/lib/newrelic_security/instrumentation-security/dir/chain.rb +42 -0
  84. data/lib/newrelic_security/instrumentation-security/dir/instrumentation.rb +102 -0
  85. data/lib/newrelic_security/instrumentation-security/dir/prepend.rb +28 -0
  86. data/lib/newrelic_security/instrumentation-security/ethon/chain.rb +53 -0
  87. data/lib/newrelic_security/instrumentation-security/ethon/instrumentation.rb +122 -0
  88. data/lib/newrelic_security/instrumentation-security/ethon/prepend.rb +39 -0
  89. data/lib/newrelic_security/instrumentation-security/excon/chain.rb +23 -0
  90. data/lib/newrelic_security/instrumentation-security/excon/instrumentation.rb +44 -0
  91. data/lib/newrelic_security/instrumentation-security/excon/prepend.rb +17 -0
  92. data/lib/newrelic_security/instrumentation-security/file/chain.rb +34 -0
  93. data/lib/newrelic_security/instrumentation-security/file/instrumentation.rb +62 -0
  94. data/lib/newrelic_security/instrumentation-security/file/prepend.rb +22 -0
  95. data/lib/newrelic_security/instrumentation-security/grape/chain.rb +42 -0
  96. data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +56 -0
  97. data/lib/newrelic_security/instrumentation-security/grape/prepend.rb +30 -0
  98. data/lib/newrelic_security/instrumentation-security/grpc/client/chain.rb +47 -0
  99. data/lib/newrelic_security/instrumentation-security/grpc/client/instrumentation.rb +37 -0
  100. data/lib/newrelic_security/instrumentation-security/grpc/client/prepend.rb +36 -0
  101. data/lib/newrelic_security/instrumentation-security/grpc/server/chain.rb +62 -0
  102. data/lib/newrelic_security/instrumentation-security/grpc/server/instrumentation.rb +65 -0
  103. data/lib/newrelic_security/instrumentation-security/grpc/server/prepend.rb +46 -0
  104. data/lib/newrelic_security/instrumentation-security/httpclient/chain.rb +30 -0
  105. data/lib/newrelic_security/instrumentation-security/httpclient/instrumentation.rb +82 -0
  106. data/lib/newrelic_security/instrumentation-security/httpclient/prepend.rb +22 -0
  107. data/lib/newrelic_security/instrumentation-security/httprb/chain.rb +21 -0
  108. data/lib/newrelic_security/instrumentation-security/httprb/instrumentation.rb +44 -0
  109. data/lib/newrelic_security/instrumentation-security/httprb/prepend.rb +16 -0
  110. data/lib/newrelic_security/instrumentation-security/httpx/chain.rb +23 -0
  111. data/lib/newrelic_security/instrumentation-security/httpx/instrumentation.rb +51 -0
  112. data/lib/newrelic_security/instrumentation-security/httpx/prepend.rb +18 -0
  113. data/lib/newrelic_security/instrumentation-security/instrumentation_loader.rb +50 -0
  114. data/lib/newrelic_security/instrumentation-security/instrumentation_utils.rb +165 -0
  115. data/lib/newrelic_security/instrumentation-security/io/chain.rb +113 -0
  116. data/lib/newrelic_security/instrumentation-security/io/instrumentation.rb +300 -0
  117. data/lib/newrelic_security/instrumentation-security/io/prepend.rb +86 -0
  118. data/lib/newrelic_security/instrumentation-security/kernel/chain.rb +65 -0
  119. data/lib/newrelic_security/instrumentation-security/kernel/instrumentation.rb +167 -0
  120. data/lib/newrelic_security/instrumentation-security/kernel/prepend.rb +50 -0
  121. data/lib/newrelic_security/instrumentation-security/mongo/chain.rb +106 -0
  122. data/lib/newrelic_security/instrumentation-security/mongo/instrumentation.rb +273 -0
  123. data/lib/newrelic_security/instrumentation-security/mongo/prepend.rb +77 -0
  124. data/lib/newrelic_security/instrumentation-security/mysql2/chain.rb +53 -0
  125. data/lib/newrelic_security/instrumentation-security/mysql2/instrumentation.rb +84 -0
  126. data/lib/newrelic_security/instrumentation-security/mysql2/prepend.rb +37 -0
  127. data/lib/newrelic_security/instrumentation-security/net_http/chain.rb +21 -0
  128. data/lib/newrelic_security/instrumentation-security/net_http/instrumentation.rb +60 -0
  129. data/lib/newrelic_security/instrumentation-security/net_http/prepend.rb +16 -0
  130. data/lib/newrelic_security/instrumentation-security/net_ldap/chain.rb +21 -0
  131. data/lib/newrelic_security/instrumentation-security/net_ldap/instrumentation.rb +42 -0
  132. data/lib/newrelic_security/instrumentation-security/net_ldap/prepend.rb +16 -0
  133. data/lib/newrelic_security/instrumentation-security/nokogiri/chain.rb +46 -0
  134. data/lib/newrelic_security/instrumentation-security/nokogiri/instrumentation.rb +36 -0
  135. data/lib/newrelic_security/instrumentation-security/nokogiri/prepend.rb +31 -0
  136. data/lib/newrelic_security/instrumentation-security/padrino/chain.rb +26 -0
  137. data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +42 -0
  138. data/lib/newrelic_security/instrumentation-security/padrino/prepend.rb +20 -0
  139. data/lib/newrelic_security/instrumentation-security/patron/chain.rb +23 -0
  140. data/lib/newrelic_security/instrumentation-security/patron/instrumentation.rb +50 -0
  141. data/lib/newrelic_security/instrumentation-security/patron/prepend.rb +18 -0
  142. data/lib/newrelic_security/instrumentation-security/pg/chain.rb +49 -0
  143. data/lib/newrelic_security/instrumentation-security/pg/instrumentation.rb +102 -0
  144. data/lib/newrelic_security/instrumentation-security/pg/prepend.rb +36 -0
  145. data/lib/newrelic_security/instrumentation-security/pty/chain.rb +31 -0
  146. data/lib/newrelic_security/instrumentation-security/pty/instrumentation.rb +52 -0
  147. data/lib/newrelic_security/instrumentation-security/pty/prepend.rb +22 -0
  148. data/lib/newrelic_security/instrumentation-security/rails/chain.rb +46 -0
  149. data/lib/newrelic_security/instrumentation-security/rails/instrumentation.rb +67 -0
  150. data/lib/newrelic_security/instrumentation-security/rails/prepend.rb +33 -0
  151. data/lib/newrelic_security/instrumentation-security/roda/chain.rb +22 -0
  152. data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +41 -0
  153. data/lib/newrelic_security/instrumentation-security/roda/prepend.rb +16 -0
  154. data/lib/newrelic_security/instrumentation-security/sinatra/chain.rb +29 -0
  155. data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +49 -0
  156. data/lib/newrelic_security/instrumentation-security/sinatra/prepend.rb +21 -0
  157. data/lib/newrelic_security/instrumentation-security/sqlite3/chain.rb +79 -0
  158. data/lib/newrelic_security/instrumentation-security/sqlite3/instrumentation.rb +164 -0
  159. data/lib/newrelic_security/instrumentation-security/sqlite3/prepend.rb +56 -0
  160. data/lib/newrelic_security/newrelic-security-api/api.rb +72 -0
  161. data/lib/newrelic_security/version.rb +5 -0
  162. data/lib/newrelic_security/websocket-client-simple/client.rb +128 -0
  163. data/lib/newrelic_security/websocket-client-simple/event_emitter.rb +72 -0
  164. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/error.rb +129 -0
  165. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/exception_handler.rb +32 -0
  166. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/base.rb +62 -0
  167. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/data.rb +49 -0
  168. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/base.rb +41 -0
  169. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler03.rb +224 -0
  170. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler04.rb +18 -0
  171. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler05.rb +15 -0
  172. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler07.rb +78 -0
  173. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler75.rb +78 -0
  174. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler.rb +15 -0
  175. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming/client.rb +17 -0
  176. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming/server.rb +17 -0
  177. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming.rb +52 -0
  178. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing/client.rb +17 -0
  179. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing/server.rb +17 -0
  180. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing.rb +35 -0
  181. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame.rb +11 -0
  182. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/base.rb +142 -0
  183. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/client.rb +130 -0
  184. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/base.rb +49 -0
  185. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client.rb +32 -0
  186. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client01.rb +20 -0
  187. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client04.rb +63 -0
  188. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client11.rb +22 -0
  189. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client75.rb +39 -0
  190. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client76.rb +105 -0
  191. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server.rb +10 -0
  192. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server04.rb +56 -0
  193. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server75.rb +40 -0
  194. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server76.rb +75 -0
  195. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler.rb +21 -0
  196. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/server.rb +179 -0
  197. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake.rb +10 -0
  198. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/nice_inspect.rb +12 -0
  199. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/version.rb +5 -0
  200. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket.rb +50 -0
  201. data/lib/newrelic_security.rb +6 -0
  202. data/lib/tasks/all.rb +8 -0
  203. data/lib/tasks/coverage_report.rake +27 -0
  204. data/newrelic_security.gemspec +51 -0
  205. metadata +342 -0
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module NewRelic::Security
6
+ module Agent
7
+ module Control
8
+
9
+ HTTP_ = 'HTTP_'
10
+ UNDERSCORE = '_'
11
+ HYPHEN = '-'
12
+ REQUEST_METHOD = 'REQUEST_METHOD'
13
+ PATH_INFO = 'PATH_INFO'
14
+ 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
+
17
+ class HTTPContext
18
+
19
+ attr_accessor :time_stamp, :req, :method, :headers, :params, :body, :data_truncated, :route, :cache, :fuzz_files, :event_counter
20
+
21
+ def initialize(env)
22
+ @time_stamp = current_time_millis
23
+ @req = env.select { |key, _| CGI_VARIABLES.include? key}
24
+ @method = @req[REQUEST_METHOD]
25
+ @headers = env.select { |key, _| key.include?(HTTP_) }
26
+ @headers = @headers.transform_keys{ |key| key[5..-1].gsub(UNDERSCORE, HYPHEN).downcase }
27
+ request = Rack::Request.new(env) unless env.empty?
28
+ @params = request&.params
29
+ @params&.each { |k, v| v.force_encoding(Encoding::UTF_8) if v.is_a?(String) }
30
+ strio = env[RACK_INPUT]
31
+ if strio.instance_of?(::StringIO)
32
+ offset = strio.tell
33
+ @body = strio.read(NewRelic::Security::Agent.config[:'security.request.body_limit'] * 1024) #after read, offset changes
34
+ strio.seek(offset)
35
+ # In case of Grape and Roda strio.read giving empty result, added below approach to handle such cases
36
+ @body = strio.string if @body.nil? && strio.size > 0
37
+ elsif defined?(::Rack) && defined?(::Rack::Lint::InputWrapper) && strio.instance_of?(::Rack::Lint::InputWrapper)
38
+ @body = strio.read(NewRelic::Security::Agent.config[:'security.request.body_limit'] * 1024)
39
+ elsif defined?(::Protocol::Rack::Input) && defined?(::Protocol::Rack::Input) && strio.instance_of?(::Protocol::Rack::Input)
40
+ @body = strio.read(NewRelic::Security::Agent.config[:'security.request.body_limit'] * 1024)
41
+ elsif defined?(::PhusionPassenger::Utils::TeeInput) && strio.instance_of?(::PhusionPassenger::Utils::TeeInput)
42
+ @body = strio.read(NewRelic::Security::Agent.config[:'security.request.body_limit'] * 1024)
43
+ end
44
+ @data_truncated = @body && @body.size >= NewRelic::Security::Agent.config[:'security.request.body_limit'] * 1024
45
+ strio&.rewind
46
+ @body = @body.force_encoding(Encoding::UTF_8) if @body.is_a?(String)
47
+ @cache = Hash.new
48
+ @fuzz_files = ::Set.new
49
+ @event_counter = 0
50
+ NewRelic::Security::Agent.agent.http_request_count.increment
51
+ NewRelic::Security::Agent.agent.iast_client.completed_requests[@headers[NR_CSEC_PARENT_ID]] = [] if @headers.key?(NR_CSEC_PARENT_ID)
52
+ end
53
+
54
+ def current_time_millis
55
+ (Time.now.to_f * 1000).to_i
56
+ end
57
+
58
+ def self.get_context
59
+ ::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data) if ::NewRelic::Agent::Tracer.current_transaction.instance_variable_defined?(:@security_context_data)
60
+ end
61
+
62
+ def self.set_context(env)
63
+ ::NewRelic::Agent::Tracer.current_transaction.instance_variable_set(:@security_context_data, HTTPContext.new(env))
64
+ end
65
+
66
+ def self.reset_context
67
+ ::NewRelic::Agent::Tracer.current_transaction.remove_instance_variable(:@security_context_data) if ::NewRelic::Agent::Tracer.current_transaction.instance_variable_defined?(:@security_context_data)
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+ require 'net/http'
3
+ require 'json'
4
+ require 'uri'
5
+ require 'set'
6
+ require 'resolv'
7
+
8
+ module NewRelic::Security
9
+ module Agent
10
+ module Control
11
+ FUZZQ_QUEUE_SIZE = 10000
12
+ METHOD = 'method'
13
+ URL = 'url'
14
+ BODY = 'body'
15
+ HEADERS = 'headers'
16
+ VERSION = 'version'
17
+ IS_GRPC = 'isGrpc'
18
+ INPUT_CLASS = 'inputClass'
19
+ SERVER_PORT_1 = 'serverPort'
20
+ PROBING = 'probing'
21
+ INTERVAL = 'interval'
22
+ IS_GRPC_CLIENT_STREAM = 'isGrpcClientStream'
23
+
24
+ class IASTClient
25
+
26
+ attr_reader :fuzzQ, :iast_dequeue_threads
27
+ attr_accessor :cooldown_till_timestamp, :last_fuzz_cc_timestamp, :pending_request_ids, :completed_requests, :iast_data_transfer_request_processor_thread
28
+
29
+ def initialize
30
+ @http = nil
31
+ @stub = nil
32
+ @fuzzQ = ::SizedQueue.new(FUZZQ_QUEUE_SIZE)
33
+ @cooldown_till_timestamp = current_time_millis
34
+ @last_fuzz_cc_timestamp = current_time_millis
35
+ @pending_request_ids = ::Set.new
36
+ @completed_requests = {}
37
+ create_dequeue_threads
38
+ create_iast_data_transfer_request_processor
39
+ end
40
+
41
+ def enqueue(message)
42
+ @fuzzQ.push(message)
43
+ rescue ThreadError => error
44
+ NewRelic::Security::Agent.logger.error "Exception in event enqueue, #{error.inspect}, Dropping fuzz request"
45
+ end
46
+
47
+ private
48
+
49
+ def create_dequeue_threads
50
+ # TODO: Create 3 or more consumers for event sending
51
+ @iast_dequeue_threads = []
52
+ 3.times do |t|
53
+ @iast_dequeue_threads << Thread.new do
54
+ Thread.current.name = "newrelic_security_iast_thread-#{t}"
55
+ loop do
56
+ fuzz_request = @fuzzQ.deq #thread blocks when the queue is empty
57
+ if fuzz_request.request[IS_GRPC]
58
+ fire_grpc_request(fuzz_request.id, fuzz_request.request, fuzz_request.reflected_metadata)
59
+ else
60
+ fire_request(fuzz_request.id, fuzz_request.request)
61
+ end
62
+ fuzz_request = nil
63
+ end
64
+ end
65
+ end
66
+ rescue Exception => exception
67
+ NewRelic::Security::Agent.logger.error "Exception in event queue creation : #{exception.inspect}"
68
+ end
69
+
70
+ def create_iast_data_transfer_request_processor
71
+ @iast_data_transfer_request_processor_thread = Thread.new do
72
+ Thread.current.name = "newrelic_security_iast_data_transfer_request_processor"
73
+ loop do
74
+ sleep NewRelic::Security::Agent.config[:policy][VULNERABILITY_SCAN][IAST_SCAN][PROBING][INTERVAL]
75
+ current_timestamp = current_time_millis
76
+ cooldown_sleep_time = @cooldown_till_timestamp - current_timestamp
77
+ sleep cooldown_sleep_time/1000 if cooldown_sleep_time > 0
78
+ next if current_timestamp - @last_fuzz_cc_timestamp < 5000
79
+
80
+ current_fetch_threshold = 300
81
+ remaining_record_capacity = @fuzzQ.max
82
+ current_record_backlog = @fuzzQ.size
83
+ batch_size = current_fetch_threshold - current_record_backlog
84
+ if batch_size > 100 && remaining_record_capacity > batch_size
85
+ iast_data_transfer_request = NewRelic::Security::Agent::Control::IASTDataTransferRequest.new
86
+ iast_data_transfer_request.batchSize = batch_size * 2
87
+ iast_data_transfer_request.pendingRequestIds = pending_request_ids.to_a
88
+ iast_data_transfer_request.completedRequests = completed_requests
89
+ NewRelic::Security::Agent.agent.event_processor.send_iast_data_transfer_request(iast_data_transfer_request)
90
+ end
91
+ end
92
+ end
93
+ rescue Exception => exception
94
+ NewRelic::Security::Agent.logger.error "Exception in create_iast_data_transfer_request_processor creation : #{exception.inspect}"
95
+ end
96
+
97
+ def current_time_millis
98
+ (Time.now.to_f * 1000).to_i
99
+ end
100
+
101
+ def fire_request(fuzz_request_id, request)
102
+ unless ::Thread.current[:http]
103
+ Thread.current[:http] = ::Net::HTTP.new('127.0.0.1', NewRelic::Security::Agent.config[:listen_port])
104
+ Thread.current[:http].open_timeout = 5
105
+ if request[PROTOCOL] == HTTPS
106
+ Thread.current[:http].use_ssl = true
107
+ Thread.current[:http].verify_mode = OpenSSL::SSL::VERIFY_NONE
108
+ end
109
+ end
110
+ request[HEADERS].delete(VERSION) if request[HEADERS].key?(VERSION)
111
+ time_before_request = (Time.now.to_f * 1000).to_i
112
+ response = Thread.current[:http].send_request(request[METHOD], ::URI.parse(request[URL]).to_s, request[BODY], request[HEADERS])
113
+ time_after_request = (Time.now.to_f * 1000).to_i
114
+ NewRelic::Security::Agent.logger.debug "IAST fuzz request : time taken : #{time_after_request - time_before_request}ms, #{request.inspect} \nresponse: #{response.inspect}\n"
115
+ rescue Exception => exception
116
+ NewRelic::Security::Agent.logger.debug "Unable to fire IAST fuzz request Request : #{request.inspect} Exception : #{exception.inspect} #{exception.backtrace}"
117
+ ensure
118
+ NewRelic::Security::Agent.agent.iast_client.completed_requests[fuzz_request_id] = []
119
+ NewRelic::Security::Agent.agent.iast_client.pending_request_ids.delete(fuzz_request_id)
120
+ end
121
+
122
+ def fire_grpc_request(fuzz_request_id, request, reflected_metadata)
123
+ service = Object.const_get(request[METHOD].split(SLASH)[0]).superclass
124
+ method = request[METHOD].split(SLASH)[1]
125
+ @stub = service.rpc_stub_class.new("localhost:#{request[SERVER_PORT_1]}", :this_channel_is_insecure) unless @stub
126
+
127
+ parsed_body = request[BODY][1..-2].split(',')
128
+ if reflected_metadata[IS_GRPC_CLIENT_STREAM]
129
+ chunks_enum = Enumerator.new do |y|
130
+ parsed_body.each do |b|
131
+ y << Object.const_get(reflected_metadata[INPUT_CLASS]).decode_json(b)
132
+ end
133
+ end
134
+ else
135
+ chunks_enum = Object.const_get(reflected_metadata[INPUT_CLASS]).decode_json(request[BODY])
136
+ end
137
+ response = @stub.public_send(method, chunks_enum, metadata: request[HEADERS])
138
+ # response = @stub.send(method, JSON.parse(request['body'], object_class: OpenStruct))
139
+ # request[HEADERS].delete(VERSION) if request[HEADERS].key?(VERSION)
140
+ NewRelic::Security::Agent.logger.debug "IAST gRPC client response : #{request.inspect} \n#{response.inspect}\n\n\n\n"
141
+ rescue Exception => exception
142
+ NewRelic::Security::Agent.logger.debug "Unable to fire IAST gRPC fuzz request Request : #{request.inspect} Exception : #{exception.inspect} #{exception.backtrace}"
143
+ ensure
144
+ NewRelic::Security::Agent.agent.iast_client.pending_request_ids.delete(fuzz_request_id)
145
+ end
146
+
147
+ end
148
+ end
149
+ end
150
+ end
151
+
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+ require 'set'
3
+
4
+ module NewRelic::Security
5
+ module Agent
6
+ module Control
7
+ class IASTDataTransferRequest
8
+ attr_reader :jsonName
9
+ attr_accessor :batchSize, :pendingRequestIds, :completedRequests
10
+
11
+ def initialize
12
+ @jsonName = :'iast-data-request'
13
+ @applicationUUID = NewRelic::Security::Agent.config[:uuid]
14
+ @batchSize = 10
15
+ @pendingRequestIds = []
16
+ @completedRequests = Hash.new
17
+ end
18
+
19
+ def as_json
20
+ instance_variables.map! do |ivar|
21
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
22
+ end.to_h
23
+ end
24
+
25
+ def to_json
26
+ as_json.to_json
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+ require 'cgi'
4
+ require 'json'
5
+
6
+ module NewRelic::Security
7
+ module Agent
8
+ module Control
9
+ module ReflectedXSS
10
+
11
+ LESS_THAN = '<'
12
+ GREATER_THAN = '>'
13
+ EQUAL = '='
14
+ HTML_COMMENT_START = '!--'
15
+ HTML_COMMENT_END = '-->'
16
+ FIVE_COLON = ':::::'
17
+ SCRIPT = 'script'
18
+ Content_Type = 'Content-Type'
19
+ QUERY_STRING = 'QUERY_STRING'
20
+ REQUEST_URI = 'REQUEST_URI'
21
+ APPLICATION_JSON = 'application/json'
22
+ APPLICATION_XML = 'application/xml'
23
+ APPLICATION_X_WWW_FORM_URLENCODED = 'application/x-www-form-urlencoded'
24
+ ON1 = 'on'
25
+ ON2 = 'ON'
26
+ ON3 = 'On'
27
+ ON4 = 'oN'
28
+ SRC ='src'
29
+ HREF = 'href'
30
+ ACTION = 'action'
31
+ FORMACTION = 'formaction'
32
+ SRCDOC = 'srcdoc'
33
+ DATA = 'data'
34
+
35
+ TAG_NAME_REGEX = ::Regexp.new("<([a-zA-Z_\\-]+[0-9]*|!--)", ::Regexp::MULTILINE | ::Regexp::IGNORECASE )
36
+ ATTRIBUTE_REGEX = ::Regexp.new("([^(\\/\\s<'\">)]+?)(?:\\s*)=\\s*(('|\")([\\s\\S]*?)(?:(?=(\\\\?))\\5.)*?\\3|.+?(?=\\/>|>|\\?>|\\s|<\\/|$))", Regexp::MULTILINE | Regexp::IGNORECASE)
37
+ UNSUPPORTED_MEDIA_TYPES = %w[video/ image/ font/ audio/].freeze
38
+ UNSUPPORTED_CONTENT_TYPES = %w[application/zip application/epub+zip application/gzip application/java-archive application/msword application/octet-stream application/ogg application/pdf application/rtf application/vnd.amazon.ebook application/vnd.apple.installer+xml application/vnd.ms-excel application/vnd.ms-fontobject
39
+ application/vnd.ms-powerpoint application/vnd.oasis.opendocument.presentation application/vnd.oasis.opendocument.spreadsheet application/vnd.oasis.opendocument.text application/vnd.openxmlformats-officedocument.presentationml.presentation
40
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet application/vnd.openxmlformats-officedocument.wordprocessingml.document application/vnd.rar application/vnd.visio application/x-7z-compressed application/x-abiword application/x-bzip application/x-bzip2 application/x-cdf
41
+ application/x-freearc application/x-tar application/zip text/calendar ].freeze
42
+
43
+
44
+ extend self
45
+
46
+ def check_xss(http_req, retval)
47
+ # TODO: Check if enableHTTPRequestPrinting is required.
48
+ return if http_req.nil? || retval.empty?
49
+ if retval[1].key?(Content_Type) && (retval[1][Content_Type].start_with?(*UNSUPPORTED_MEDIA_TYPES) || retval[1][Content_Type].start_with?(*UNSUPPORTED_CONTENT_TYPES))
50
+ return
51
+ end
52
+ response_body = ::String.new
53
+ retval[2].each { |string| response_body << string }
54
+ construct = check_for_reflected_xss(http_req, retval[1], response_body)
55
+ NewRelic::Security::Agent.logger.debug "RXSS Attack DATA: #{construct}"
56
+ if !construct.empty? || NewRelic::Security::Agent::Utils.is_IAST?
57
+ parameters = Array.new
58
+ parameters << construct
59
+ parameters << response_body.force_encoding(ISO_8859_1).encode(UTF_8)
60
+ NewRelic::Security::Agent::Control::Collector.collect(REFLECTED_XSS, parameters, nil, :response_header => retval[1][Content_Type])
61
+ end
62
+ rescue Exception => exception
63
+ NewRelic::Security::Agent.logger.error "Exception in Reflected XSS detection : #{exception.inspect} #{exception.backtrace}"
64
+ end
65
+
66
+ private
67
+
68
+ def check_for_reflected_xss(http_req, headers, response_body)
69
+ final_attack_construct = ::String.new
70
+ to_return = ::String.new
71
+ combined_request_data = decode_request_data(http_req)
72
+ combined_response_data = decode_response_data(headers, response_body)
73
+ combined_response_data_string = combined_response_data.to_a.join(FIVE_COLON)
74
+ attack_constructs = is_xss(combined_request_data)
75
+ NewRelic::Security::Agent.logger.debug "RXSS attack_constructs ==> #{attack_constructs}"
76
+ attack_constructs.each { |construct| to_return = construct if combined_response_data_string.include?(construct) }
77
+ if !to_return.empty?
78
+ response_constructs = is_xss(combined_response_data)
79
+ response_constructs.each { |construct| final_attack_construct = to_return if construct.include?(to_return) }
80
+ end
81
+ combined_request_data = nil
82
+ combined_response_data = nil
83
+ combined_response_data_string = nil
84
+ final_attack_construct
85
+ end
86
+
87
+ def decode_request_data(http_req)
88
+ processed_data = ::Set.new
89
+ content_type = http_req.req[CONTENT_TYPE]
90
+ body = http_req.body
91
+ http_req.req.each do | key, value |
92
+ process_url_encoded_data_for_xss(processed_data, key)
93
+ process_url_encoded_data_for_xss(processed_data, value)
94
+ end
95
+ if http_req.params != nil
96
+ items = ::Set.new
97
+ get_key_values(http_req.params, items)
98
+ items.each { |item| processed_data.add(item) if item.include?(LESS_THAN) }
99
+ end
100
+ process_url_encoded_data_for_xss(processed_data, http_req.req[REQUEST_URI])
101
+ processed_data.add(body) unless body.nil? || body.empty?
102
+ if body != nil && !body.empty?
103
+ case content_type
104
+ when APPLICATION_JSON
105
+ oldBody = body.dup
106
+ body = ::JSON.parse(body)
107
+ if oldBody != body && body.include?(LESS_THAN)
108
+ processed_data.add(body)
109
+ end
110
+ when APPLICATION_XML
111
+ # Unescaping of xml data is remaining
112
+ processed_data.add(body)
113
+ when APPLICATION_X_WWW_FORM_URLENCODED
114
+ body = ::CGI.unescape(body, UTF_8)
115
+ processed_data.add(body)
116
+ oldBody = body
117
+ body = ::CGI.unescape(body, UTF_8)
118
+ processed_data.add(body) if oldBody != body && body.include?(LESS_THAN)
119
+ end
120
+ end
121
+ processed_data
122
+ end
123
+
124
+ def decode_response_data(headers, response_body)
125
+ processed_data = ::Set.new
126
+ content_type = headers[Content_Type]
127
+ response_body = response_body
128
+ processed_body = response_body.force_encoding(UTF_8)
129
+ processed_data.add(processed_body)
130
+ old_processed_body = String.new
131
+ if response_body != nil && !response_body.empty?
132
+ case content_type
133
+ when APPLICATION_JSON
134
+ # do while loop in java code here
135
+ old_processed_body = processed_body
136
+ body = ::JSON.parse(processed_body)
137
+ processed_data.add(body) if old_processed_body != body && body.to_s.include?(LESS_THAN)
138
+ when APPLICATION_XML
139
+ # Unescaping of xml data is remaining
140
+ processed_data.add(processed_data)
141
+ end
142
+ end
143
+ processed_data
144
+ end
145
+
146
+ def is_xss(combined_data)
147
+ attack_constructs = ::Set.new
148
+ for data in combined_data do
149
+ constructs = get_xss_constructs(data)
150
+ constructs.each { |str| attack_constructs.add(str) }
151
+ end
152
+ return attack_constructs
153
+ end
154
+
155
+ def process_url_encoded_data_for_xss(processed_data, data)
156
+ processed_data.add(data) if data && data.include?(LESS_THAN)
157
+ decoded_uri = ::CGI.unescape(data, UTF_8) if data
158
+ processed_data.add(decoded_uri) if decoded_uri && decoded_uri.include?(LESS_THAN)
159
+ end
160
+
161
+ def get_key_values(hash, items)
162
+ hash.each do |k,v|
163
+ items.add(k.to_s)
164
+ if v.instance_of?(Hash)
165
+ get_key_values(v, items)
166
+ else
167
+ items.add(v.to_s) unless v.nil?
168
+ end
169
+ end
170
+ end
171
+
172
+ def get_xss_constructs(data)
173
+ constructs = ::Set.new
174
+ is_attack_construct = false
175
+ curr_pos = 0
176
+ start_pos = 0
177
+ tmp_curr_pos = 0
178
+ tmp_start_pos = 0
179
+
180
+ while curr_pos < data.length
181
+ matcher = TAG_NAME_REGEX.match(data, curr_pos)
182
+ is_attack_construct = false
183
+ return constructs if matcher.nil?
184
+ tagName = matcher[1]
185
+ return constructs if tagName.empty?
186
+ start_pos = matcher.begin(0)
187
+ curr_pos = matcher.end(0) - 1
188
+ if tagName == HTML_COMMENT_START
189
+ tmp_curr_pos = start_pos + data.index(HTML_COMMENT_END, start_pos)
190
+ if tmp_curr_pos == nil
191
+ break
192
+ else
193
+ curr_pos = tmp_curr_pos
194
+ next
195
+ end
196
+ end
197
+ tmp_start_pos = tmp_curr_pos = data.index(GREATER_THAN, start_pos)
198
+ tmp_start_pos = start_pos if tmp_curr_pos.nil?
199
+ while ATTRIBUTE_REGEX.match?(data, curr_pos)
200
+ attribute_matcher = ATTRIBUTE_REGEX.match(data, curr_pos)
201
+ attribute_data = attribute_matcher[0]
202
+ curr_pos = attribute_matcher.end(0) - 1
203
+ tmp_curr_pos = data.index(GREATER_THAN, tmp_start_pos ? tmp_start_pos : -1)
204
+ if tmp_curr_pos == nil || attribute_matcher.begin(0) < tmp_curr_pos
205
+ tmp_start_pos = tmp_curr_pos = attribute_matcher.end(0) - 1
206
+ tmp_start_pos += 1
207
+ if (attribute_matcher[3] == nil || attribute_matcher[3] == EMPTY_STRING) && attribute_matcher.end(0) > tmp_curr_pos
208
+ tmp_start_pos = tmp_curr_pos = data.index(GREATER_THAN, attribute_matcher.begin(0)) ? data.index(GREATER_THAN, attribute_matcher.begin(0)) : -1
209
+ attribute_data = attribute_data[0..tmp_start_pos]
210
+ end
211
+ key = attribute_data[0..attribute_data.index(EQUAL) - 1]
212
+ val = attribute_data[attribute_data.index(EQUAL) + 1.. attribute_data.length]
213
+ if key != nil && key != EMPTY_STRING && key.start_with?(ON1, ON2, ON3, ON4) || key.casecmp?(SRC) || key.casecmp?(HREF) || key.casecmp?(ACTION) || key.casecmp?(FORMACTION) || key.casecmp?(SRCDOC) || key.casecmp?(DATA) || ::CGI.unescapeHTML(val).gsub(/[[:space:]]/, EMPTY_STRING).match?(/javascript:/i)
214
+ is_attack_construct = true
215
+ end
216
+ else
217
+ break
218
+ end
219
+ end
220
+ if tmp_curr_pos != nil && tmp_curr_pos > 0
221
+ curr_pos = tmp_curr_pos
222
+ end
223
+ if data[curr_pos] != GREATER_THAN
224
+ tmp = data.index(GREATER_THAN, curr_pos)
225
+ if tmp != nil
226
+ curr_pos = tmp
227
+ elsif !is_attack_construct
228
+ next
229
+ end
230
+ end
231
+
232
+ if tagName.strip.casecmp?(SCRIPT)
233
+ location_of_end_tag = data.index(/<\/script/i, curr_pos)
234
+ if location_of_end_tag != nil
235
+ body = data[curr_pos + 1..location_of_end_tag-1]
236
+ if body != nil && body != EMPTY_STRING
237
+ constructs.add(data[start_pos..curr_pos] + body)
238
+ next
239
+ end
240
+ else
241
+ body = data[curr_pos + 1 .. data.length]
242
+ tag_end = body.index(GREATER_THAN)
243
+ if body != nil && body != EMPTY_STRING && tag_end != nil
244
+ body = body[tag_end..data.length]
245
+ constructs.add(data[start_pos..curr_pos] + body)
246
+ break
247
+ end
248
+ end
249
+ end
250
+ constructs.add(data[start_pos..curr_pos]) if is_attack_construct
251
+ end
252
+ constructs
253
+ end
254
+
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'newrelic_security/websocket-client-simple/client'
4
+ require 'openssl'
5
+ require 'singleton'
6
+
7
+ module NewRelic::Security
8
+ module Agent
9
+ module Control
10
+
11
+ NR_CSEC_CONNECTION_TYPE = 'NR-CSEC-CONNECTION-TYPE'
12
+ NR_LICENSE_KEY = 'NR-LICENSE-KEY'
13
+ NR_AGENT_RUN_TOKEN = 'NR-AGENT-RUN-TOKEN'
14
+ NR_CSEC_VERSION = 'NR-CSEC-VERSION'
15
+ NR_CSEC_COLLECTOR_TYPE = 'NR-CSEC-COLLECTOR-TYPE'
16
+ NR_CSEC_BUILD_NUMBER = 'NR-CSEC-BUILD-NUMBER'
17
+ NR_CSEC_MODE = 'NR-CSEC-MODE'
18
+ NR_CSEC_APP_UUID = 'NR-CSEC-APP-UUID'
19
+ NR_CSEC_JSON_VERSION = 'NR-CSEC-JSON-VERSION'
20
+ NR_ACCOUNT_ID = 'NR-ACCOUNT-ID'
21
+ NR_CSEC_ENTITY_NAME = 'NR-CSEC-ENTITY-NAME'
22
+ NR_CSEC_ENTITY_GUID = 'NR-CSEC-ENTITY-GUID'
23
+ NR_CSEC_IAST_DATA_TRANSFER_MODE = 'NR-CSEC-IAST-DATA-TRANSFER-MODE'
24
+
25
+ class WebsocketClient
26
+ include Singleton
27
+
28
+ attr_accessor :ws
29
+
30
+ def connect()
31
+
32
+ headers = Hash.new
33
+ headers[NR_CSEC_CONNECTION_TYPE] = LANGUAGE_COLLECTOR
34
+ headers[NR_LICENSE_KEY] = NewRelic::Security::Agent.config[:license_key]
35
+ headers[NR_AGENT_RUN_TOKEN] = NewRelic::Security::Agent.config[:agent_run_id]
36
+ headers[NR_CSEC_VERSION] = NewRelic::Security::VERSION
37
+ headers[NR_CSEC_COLLECTOR_TYPE] = RUBY
38
+ headers[NR_CSEC_BUILD_NUMBER] = '0000'
39
+ headers[NR_CSEC_MODE] = NewRelic::Security::Agent.config[:mode]
40
+ headers[NR_CSEC_APP_UUID] = NewRelic::Security::Agent.config[:uuid]
41
+ headers[NR_CSEC_JSON_VERSION] = NewRelic::Security::Agent.config[:json_version]
42
+ headers[NR_ACCOUNT_ID] = NewRelic::Security::Agent.config[:account_id]
43
+ headers[NR_CSEC_ENTITY_NAME] = NewRelic::Security::Agent.config[:app_name]
44
+ headers[NR_CSEC_ENTITY_GUID] = NewRelic::Security::Agent.config[:entity_guid]
45
+ headers[NR_CSEC_IAST_DATA_TRANSFER_MODE] = PULL
46
+
47
+ begin
48
+ cert_store = ::OpenSSL::X509::Store.new
49
+ cert_store.add_cert ::OpenSSL::X509::Certificate.new(::IO.read("#{__dir__}/../resources/cert.pem"))
50
+ NewRelic::Security::Agent.logger.info "Websocket connection URL : #{NewRelic::Security::Agent.config[:validator_service_url]}"
51
+ connection = NewRelic::Security::WebSocket::Client::Simple.connect NewRelic::Security::Agent.config[:validator_service_url], headers: headers, cert_store: cert_store
52
+ @ws = connection
53
+
54
+ connection.on :open do
55
+ NewRelic::Security::Agent.logger.debug "Websocket connected with IC, AgentEventMachine #{NewRelic::Security::Agent::Utils.filtered_log(connection.inspect)}"
56
+ NewRelic::Security::Agent.init_logger.info "[STEP-4] => Web socket connection to SaaS validator established successfully"
57
+ NewRelic::Security::Agent.agent.event_processor.send_app_info
58
+ NewRelic::Security::Agent.agent.event_processor.send_application_url_mappings
59
+ NewRelic::Security::Agent.config.enable_security
60
+ end
61
+
62
+ connection.on :message do |msg|
63
+ if msg.type == :ping
64
+ connection.send(EMPTY_STRING, :type => :pong)
65
+ elsif msg.type == :text
66
+ # NewRelic::Security::Agent.logger.debug "Received IC Agent Message: #{msg.data.inspect}"
67
+ ControlCommand.handle_ic_command(msg.data)
68
+ end
69
+ end
70
+
71
+ connection.on :close do |e|
72
+ NewRelic::Security::Agent.logger.info "Closing websocket connection : #{e.inspect}\n"
73
+ NewRelic::Security::Agent.config.disable_security
74
+ Thread.new { NewRelic::Security::Agent.agent.reconnect(0) } if e
75
+ end
76
+
77
+ connection.on :error do |e|
78
+ NewRelic::Security::Agent.logger.error "Error in websocket connection : #{e.inspect} #{e.backtrace}"
79
+ Thread.new { NewRelic::Security::Agent::Control::WebsocketClient.instance.close(true) }
80
+ end
81
+ rescue Errno::EPIPE => exception
82
+ NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
83
+ NewRelic::Security::Agent.config.disable_security
84
+ rescue Errno::ECONNRESET => exception
85
+ NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
86
+ NewRelic::Security::Agent.config.disable_security
87
+ Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
88
+ rescue Errno::ECONNREFUSED => exception
89
+ NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
90
+ NewRelic::Security::Agent.config.disable_security
91
+ Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
92
+ rescue => exception
93
+ NewRelic::Security::Agent.logger.error "Exception in websocket init: #{exception.inspect} #{exception.backtrace}"
94
+ NewRelic::Security::Agent.config.disable_security
95
+ Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
96
+ end
97
+ headers = nil
98
+ end
99
+
100
+ def send(message)
101
+ message_json = message.to_json
102
+ NewRelic::Security::Agent.logger.debug "Sending #{message.jsonName} : #{message_json}"
103
+ res = @ws.send(message_json)
104
+ if res && message.jsonName == :Event
105
+ NewRelic::Security::Agent.agent.event_sent_count.increment
106
+ if NewRelic::Security::Agent::Utils.is_IAST_request?(message.httpRequest[:headers])
107
+ NewRelic::Security::Agent.agent.iast_event_stats.sent.increment
108
+ else
109
+ NewRelic::Security::Agent.agent.rasp_event_stats.sent.increment
110
+ end
111
+ end
112
+ NewRelic::Security::Agent.agent.exit_event_stats.sent.increment if res && message.jsonName == :'exit-event'
113
+ rescue Exception => exception
114
+ NewRelic::Security::Agent.logger.error "Exception in sending message : #{exception.inspect} #{exception.backtrace}"
115
+ NewRelic::Security::Agent.agent.event_drop_count.increment if message.jsonName == :Event
116
+ NewRelic::Security::Agent.agent.event_processor.send_critical_message(exception.message, "SEVERE", caller_locations[0].to_s, Thread.current.name, exception)
117
+ end
118
+
119
+ def close(reconnect = true)
120
+ @ws.close(reconnect) if @ws
121
+ end
122
+
123
+ def is_open?
124
+ return @ws.open? if @ws
125
+ false
126
+ end
127
+
128
+ end
129
+ end
130
+ end
131
+ end