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,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module NewRelic::Security
6
+ module Agent
7
+ module Control
8
+
9
+ class CriticalMessage
10
+
11
+ attr_reader :jsonName
12
+
13
+ def initialize(message, level, caller, thread_name, exception = nil)
14
+ @collectorType = RUBY
15
+ @language = Ruby
16
+ @jsonName = :'critical-messages'
17
+ @framework = NewRelic::Security::Agent.config[:framework]
18
+ @groupName = NewRelic::Security::Agent.config[:mode]
19
+ @policyVersion = nil
20
+ @collectorVersion = NewRelic::Security::VERSION
21
+ @buildNumber = nil
22
+ @jsonVersion = NewRelic::Security::Agent.config[:json_version]
23
+ @applicationUUID = NewRelic::Security::Agent.config[:uuid]
24
+ @linkingMetadata = add_linking_metadata
25
+ @timestamp = current_time_millis
26
+ @message = message
27
+ @level = level
28
+ @caller = caller
29
+ @threadName = thread_name
30
+ @exception = exception
31
+ end
32
+
33
+ def as_json
34
+ instance_variables.map! do |ivar|
35
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
36
+ end.to_h
37
+ end
38
+
39
+ def to_json
40
+ as_json.to_json
41
+ end
42
+
43
+ private
44
+
45
+ def current_time_millis
46
+ (Time.now.to_f * 1000).to_i
47
+ end
48
+
49
+ def add_linking_metadata
50
+ linking_metadata = Hash.new
51
+ linking_metadata[:agentRunId] = NewRelic::Security::Agent.config[:agent_run_id]
52
+ linking_metadata.merge!(NewRelic::Security::Agent.config[:linking_metadata])
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'uri'
5
+
6
+ module NewRelic::Security
7
+ module Agent
8
+ module Control
9
+
10
+ ROOT_PATH = '/'
11
+
12
+ class Event
13
+
14
+ attr_accessor :sourceMethod, :userMethodName, :userFileName, :lineNumber, :id, :apiId, :isIASTEnable, :isIASTRequest, :httpRequest, :httpResponse, :stacktrace, :metaData, :parentId
15
+ attr_reader :jsonName, :caseType, :eventCategory, :parameters
16
+
17
+ def initialize(case_type, args, event_category)
18
+ @collectorType = RUBY
19
+ @language = Ruby
20
+ @jsonName = :Event
21
+ @eventType = :sec_event
22
+ @framework = NewRelic::Security::Agent.config[:framework]
23
+ @groupName = NewRelic::Security::Agent.config[:mode]
24
+ @policyVersion = nil
25
+ @collectorVersion = NewRelic::Security::VERSION
26
+ @buildNumber = nil
27
+ @jsonVersion = NewRelic::Security::Agent.config[:json_version]
28
+ @applicationUUID = NewRelic::Security::Agent.config[:uuid]
29
+ @httpRequest = Hash.new
30
+ @httpResponse = Hash.new
31
+ @metaData = { :reflectedMetaData => { :listen_port => NewRelic::Security::Agent.config[:listen_port].to_s } }
32
+ @linkingMetadata = add_linking_metadata
33
+ @pid = pid
34
+ @parameters = args
35
+ @sourceMethod = nil
36
+ @userMethodName = nil
37
+ @userFileName = nil
38
+ @lineNumber = nil
39
+ @caseType = case_type
40
+ @eventCategory = event_category
41
+ @id = event_id
42
+ @eventGenerationTime = current_time_millis
43
+ @startTime = current_time_millis
44
+ @stacktrace = []
45
+ @apiId = nil
46
+ @isAPIBlocked = nil
47
+ @isIASTEnable = false
48
+ @isIASTRequest = false
49
+ @parentId = nil
50
+ end
51
+
52
+ def as_json
53
+ instance_variables.map! do |ivar|
54
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
55
+ end.to_h
56
+ end
57
+
58
+ def to_json
59
+ as_json.to_json
60
+ end
61
+
62
+ # # TODO: Use this approach if, performace is very low with require 'json'
63
+ # def as_json(options={})
64
+ # {
65
+ # fname: @fname,
66
+ # lname: @lname
67
+ # }
68
+ # end
69
+
70
+ # # This method needs, require 'json'
71
+ # def to_json(*options)
72
+ # as_json(*options).to_json(*options)
73
+ # end
74
+
75
+ def copy_http_info(ctxt)
76
+ return if ctxt.nil?
77
+ http_request = {}
78
+ http_request[:parameterMap] = {}
79
+ http_request[:body] = ctxt.body
80
+ http_request[:generationTime] = ctxt.time_stamp
81
+ http_request[:dataTruncated] = false
82
+ http_request[:method] = ctxt.method
83
+ http_request[:route] = ctxt.route.split(AT_THE_RATE)[1] if ctxt.route
84
+ http_request[:url] = URI(ctxt.req[REQUEST_URI]).respond_to?(:request_uri) ? URI(ctxt.req[REQUEST_URI]).request_uri : ctxt.req[REQUEST_URI]
85
+ http_request[:clientIP] = ctxt.headers.has_key?(X_FORWARDED_FOR) ? ctxt.headers[X_FORWARDED_FOR].split(COMMA)[0].to_s : ctxt.req[REMOTE_ADDR].to_s
86
+ http_request[:serverPort] = ctxt.req[SERVER_PORT].to_i
87
+ http_request[:protocol] = ctxt.req[RACK_URL_SCHEME]
88
+ http_request[:contextPath] = ROOT_PATH
89
+ http_request[:headers] = ctxt.headers
90
+ http_request[:contentType] = ctxt.req[CONTENT_TYPE] if ctxt.req.has_key?(CONTENT_TYPE)
91
+ http_request[:headers][CONTENT_TYPE1] = ctxt.req[CONTENT_TYPE] if ctxt.req.has_key?(CONTENT_TYPE)
92
+ http_request[:dataTruncated] = ctxt.data_truncated
93
+ @httpRequest = http_request
94
+ @metaData[:isClientDetectedFromXFF] = ctxt.headers.has_key?(X_FORWARDED_FOR) ? true : false
95
+ end
96
+
97
+ def copy_grpc_info(ctxt)
98
+ # TODO: optimise this method and combine copy_http_info and copy_grpc_info
99
+ return if ctxt.nil?
100
+ http_request = {}
101
+ http_request[:body] = ctxt.body.is_a?(Array) ? "[#{ctxt.body.join(',')}]" : ctxt.body
102
+ http_request[:generationTime] = ctxt.time_stamp
103
+ http_request[:dataTruncated] = false
104
+ http_request[:method] = ctxt.method
105
+ http_request[:route] = ctxt.route
106
+ http_request[:url] = ctxt.url
107
+ http_request[:serverName] = ctxt.server_name
108
+ http_request[:serverPort] = ctxt.server_port
109
+ http_request[:clientIP] = ctxt.client_ip
110
+ http_request[:clientPort] = ctxt.client_port
111
+ http_request[:protocol] = :grpc
112
+ http_request[:headers] = ctxt.headers
113
+ http_request[:contentType] = "TODO: "
114
+ http_request[:isGrpc] = ctxt.is_grpc
115
+ @httpRequest = http_request
116
+ @metaData = ctxt.metadata
117
+ end
118
+
119
+ private
120
+
121
+ def pid
122
+ return ::Process.pid if ::Gem.win_platform?
123
+ ::Process.getpgrp
124
+ end
125
+
126
+ def current_time_millis
127
+ (Time.now.to_f * 1000).to_i
128
+ end
129
+
130
+ def event_id
131
+ "#{Process.pid}:#{::Thread.current.object_id}:#{thread_monotonic_ctr}"
132
+ end
133
+
134
+ def thread_monotonic_ctr
135
+ ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context if NewRelic::Security::Agent::Control::HTTPContext.get_context
136
+ ctxt = NewRelic::Security::Agent::Control::GRPCContext.get_context if NewRelic::Security::Agent::Control::GRPCContext.get_context
137
+ ctxt.event_counter = ctxt.event_counter + 1 if ctxt
138
+ end
139
+
140
+ def add_linking_metadata
141
+ linking_metadata = Hash.new
142
+ linking_metadata[:agentRunId] = NewRelic::Security::Agent.config[:agent_run_id]
143
+ linking_metadata.merge!(NewRelic::Security::Agent.config[:linking_metadata])
144
+ end
145
+
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,28 @@
1
+ require 'thread'
2
+
3
+ module NewRelic::Security
4
+ module Agent
5
+ module Control
6
+ class EventCounter
7
+ def initialize
8
+ @mutex = Mutex.new
9
+ @counter = 0
10
+ end
11
+
12
+ def increment
13
+ @mutex.synchronize do
14
+ @counter += 1
15
+ end
16
+ end
17
+
18
+ def fetch_and_reset_counter
19
+ @mutex.synchronize do
20
+ old_count = @counter
21
+ @counter = 0
22
+ old_count
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,134 @@
1
+ require 'thread'
2
+
3
+ module NewRelic::Security
4
+ module Agent
5
+ module Control
6
+ EVENT_QUEUE_SIZE = 10_000
7
+ HEALTH_INTERVAL = 300
8
+
9
+ class EventProcessor
10
+
11
+ attr_accessor :eventQ, :event_dequeue_thread, :healthcheck_thread
12
+
13
+ def initialize
14
+ @first_event = true
15
+ @eventQ = ::SizedQueue.new(EVENT_QUEUE_SIZE)
16
+ create_dequeue_threads
17
+ create_keep_alive_thread
18
+ NewRelic::Security::Agent.init_logger.info "[STEP-5] => Security agent components started"
19
+ end
20
+
21
+ def send_app_info
22
+ NewRelic::Security::Agent.init_logger.info "[STEP-3] => Gathering information about the application"
23
+ app_info = NewRelic::Security::Agent::Control::AppInfo.new
24
+ app_info.update_app_info
25
+ NewRelic::Security::Agent.logger.info "Sending application info : #{app_info.to_json}"
26
+ NewRelic::Security::Agent.init_logger.info "Sending application info : #{app_info.to_json}"
27
+ enqueue(app_info)
28
+ app_info = nil
29
+ end
30
+
31
+ def send_application_url_mappings
32
+ application_url_mappings = NewRelic::Security::Agent::Control::ApplicationURLMappings.new
33
+ application_url_mappings.update_application_url_mappings
34
+ NewRelic::Security::Agent.logger.info "Sending application URL Mappings : #{application_url_mappings.to_json}"
35
+ enqueue(application_url_mappings)
36
+ application_url_mappings = nil
37
+ end
38
+
39
+ def send_event(event)
40
+ NewRelic::Security::Agent.agent.event_processed_count.increment
41
+ if NewRelic::Security::Agent::Utils.is_IAST_request?(event.httpRequest[:headers])
42
+ NewRelic::Security::Agent.agent.iast_event_stats.processed.increment
43
+ else
44
+ NewRelic::Security::Agent.agent.rasp_event_stats.processed.increment
45
+ end
46
+ enqueue(event)
47
+ if @first_event
48
+ NewRelic::Security::Agent.init_logger.info "[STEP-8] => First event sent for validation. Security agent started successfully : #{event.to_json}"
49
+ @first_event = false
50
+ end
51
+ event = nil
52
+ end
53
+
54
+ def send_health
55
+ health = NewRelic::Security::Agent::Control::Health.new
56
+ health.update_health_check
57
+ NewRelic::Security::Agent::Control::WebsocketClient.instance.send(health)
58
+ health = nil
59
+ end
60
+
61
+ def send_critical_message(message, level, caller, thread_name, exc)
62
+ if exc
63
+ exception = {}
64
+ exception[:message] = exc.message
65
+ exception[:cause] = exc.cause
66
+ exception[:stackTrace] = exc.backtrace.map(&:to_s)
67
+ end
68
+ critical_message = NewRelic::Security::Agent::Control::CriticalMessage.new(message, level, caller, thread_name, exception)
69
+ enqueue(critical_message)
70
+ critical_message = nil
71
+ end
72
+
73
+ def send_exit_event(exit_event)
74
+ NewRelic::Security::Agent.agent.exit_event_stats.processed.increment
75
+ enqueue(exit_event)
76
+ exit_event = nil
77
+ end
78
+
79
+ def send_iast_data_transfer_request(iast_data_transfer_request)
80
+ enqueue(iast_data_transfer_request)
81
+ iast_data_transfer_request = nil
82
+ end
83
+
84
+ private
85
+
86
+ def create_dequeue_threads
87
+ # TODO: Create 3 or more consumers for event sending
88
+ @event_dequeue_thread = Thread.new do
89
+ Thread.current.name = "newrelic_security_event_thread"
90
+ loop do
91
+ begin
92
+ data_to_be_sent = @eventQ.pop
93
+ NewRelic::Security::Agent::Control::WebsocketClient.instance.send(data_to_be_sent)
94
+ rescue => exception
95
+ NewRelic::Security::Agent.logger.error "Exception in event pop operation : #{exception.inspect}"
96
+ end
97
+ end
98
+ end
99
+ rescue Exception => exception
100
+ NewRelic::Security::Agent.logger.error "Exception in event queue creation : #{exception.inspect}"
101
+ end
102
+
103
+ def enqueue(message)
104
+ @eventQ.push(message, true)
105
+ rescue Exception => exception
106
+ NewRelic::Security::Agent.logger.error "Exception in event enqueue, #{exception.inspect}, Dropping message"
107
+ if message.jsonName == :Event
108
+ NewRelic::Security::Agent.agent.event_drop_count.increment
109
+ if NewRelic::Security::Agent::Utils.is_IAST_request?(message.httpRequest[:headers])
110
+ NewRelic::Security::Agent.agent.iast_event_stats.rejected.increment
111
+ else
112
+ NewRelic::Security::Agent.agent.rasp_event_stats.rejected.increment
113
+ end
114
+ end
115
+ NewRelic::Security::Agent.agent.exit_event_stats.rejected.increment if message.jsonName == :'exit-event'
116
+ NewRelic::Security::Agent.agent.iast_client.completed_requests.delete(message.parentId)
117
+ end
118
+
119
+ def create_keep_alive_thread
120
+ @healthcheck_thread = Thread.new {
121
+ Thread.current.name = "newrelic_security_healthcheck_thread"
122
+ while true do
123
+ sleep HEALTH_INTERVAL
124
+ send_health if NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open?
125
+ end
126
+ }
127
+ rescue Exception => exception
128
+ NewRelic::Security::Agent.logger.error "Exception in health check thread, #{exception.inspect}"
129
+ end
130
+
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,26 @@
1
+ module NewRelic::Security
2
+ module Agent
3
+ module Control
4
+ class EventStats
5
+
6
+ attr_accessor :processed, :sent, :rejected, :error_count
7
+
8
+ def initialize
9
+ @processed = NewRelic::Security::Agent::Control::EventCounter.new
10
+ @sent = NewRelic::Security::Agent::Control::EventCounter.new
11
+ @rejected = NewRelic::Security::Agent::Control::EventCounter.new
12
+ @error_count = NewRelic::Security::Agent::Control::EventCounter.new
13
+ end
14
+
15
+ def prepare_for_health_check
16
+ hash = {}
17
+ hash[:processed] = @processed.fetch_and_reset_counter
18
+ hash[:sent] = @sent.fetch_and_reset_counter
19
+ hash[:rejected] = @rejected.fetch_and_reset_counter
20
+ hash[:errorCount] = @error_count.fetch_and_reset_counter
21
+ hash
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ module NewRelic::Security
2
+ module Agent
3
+ module Control
4
+ class EventSubscriber
5
+ def initialize
6
+ ::NewRelic::Agent.instance.events.subscribe(:server_source_configuration_added) {
7
+ NewRelic::Security::Agent.logger.info "NewRelic server_source_configuration_added for pid : #{Process.pid}, Parent Pid : #{Process.ppid}"
8
+ NewRelic::Security::Agent.init_logger.info "NewRelic server_source_configuration_added for pid : #{Process.pid}, Parent Pid : #{Process.ppid}"
9
+ NewRelic::Security::Agent.config.update_server_config
10
+ if NewRelic::Security::Agent.config[:enabled] && !NewRelic::Security::Agent.config[:high_security]
11
+ NewRelic::Security::Agent.agent.init
12
+ else
13
+ NewRelic::Security::Agent.logger.info "New Relic Security is disabled by one of the user provided config `security.enabled` or `high_security`."
14
+ NewRelic::Security::Agent.init_logger.info "New Relic Security is disabled by one of the user provided config `security.enabled` or `high_security`."
15
+ end
16
+ }
17
+ ::NewRelic::Agent.instance.events.subscribe(:security_policy_received) { |received_policy|
18
+ NewRelic::Security::Agent.logger.info "security_policy_received pid ::::::: #{Process.pid} #{Process.ppid}, #{received_policy}"
19
+ NewRelic::Security::Agent.config[:policy].merge!(received_policy)
20
+ NewRelic::Security::Agent.init_logger.info "[STEP-7] => Received and applied policy/configuration : #{received_policy}"
21
+ NewRelic::Security::Agent.agent.start_iast_client if NewRelic::Security::Agent::Utils.is_IAST?
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,38 @@
1
+ require 'json'
2
+
3
+ module NewRelic::Security
4
+ module Agent
5
+ module Control
6
+ class ExitEvent
7
+ attr_accessor :executionId, :caseType, :k2RequestIdentifier
8
+ attr_reader :jsonName
9
+
10
+ def initialize
11
+ @collectorType = RUBY
12
+ @language = Ruby
13
+ @jsonName = :'exit-event'
14
+ @collectorVersion = NewRelic::Security::VERSION
15
+ @buildNumber = nil
16
+ @applicationUUID = NewRelic::Security::Agent.config[:uuid]
17
+ @groupName = NewRelic::Security::Agent.config[:mode]
18
+ @jsonVersion = NewRelic::Security::Agent.config[:json_version]
19
+ @policyVersion = nil
20
+ @framework = NewRelic::Security::Agent.config[:framework]
21
+ @executionId = nil
22
+ @caseType = nil
23
+ @k2RequestIdentifier = nil
24
+ end
25
+
26
+ def as_json
27
+ instance_variables.map! do |ivar|
28
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
29
+ end.to_h
30
+ end
31
+
32
+ def to_json
33
+ as_json.to_json
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ module NewRelic::Security
2
+ module Agent
3
+ module Control
4
+ class FuzzRequest
5
+ attr_reader :id
6
+ attr_accessor :request, :case_type, :reflected_metadata
7
+
8
+ def initialize(id)
9
+ @id = id
10
+ @request = nil
11
+ @case_type = nil
12
+ @reflected_metadata = nil
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module NewRelic::Security
6
+ module Agent
7
+ module Control
8
+
9
+ class GRPCContext
10
+
11
+ attr_accessor :time_stamp, :method, :headers, :body, :route, :cache, :fuzz_files, :url, :server_name, :server_port, :client_ip, :client_port, :is_grpc, :metadata, :event_counter
12
+
13
+ def initialize(grpc_request)
14
+ @time_stamp = current_time_millis
15
+ @method = grpc_request[:method]
16
+ @route = "/#{@method}"
17
+ @headers = grpc_request[:headers]
18
+ @body = grpc_request[:body]
19
+ if defined?(::GRPC::RpcServer)
20
+ ObjectSpace.each_object(::GRPC::RpcServer) do |z|
21
+ grpc_host = z.instance_variable_get(:@host_nr)
22
+ grpc_port = z.instance_variable_get(:@port_nr)
23
+ @server_name = "#{grpc_host}:#{grpc_port}"
24
+ @server_port = grpc_port
25
+ @client_ip = grpc_request[:peer].rpartition(COLON)[0] if grpc_request[:peer]
26
+ @client_port = grpc_request[:peer].rpartition(COLON)[-1] if grpc_request[:peer]
27
+ @url = "/#{@method}"
28
+ end
29
+ end
30
+ @is_grpc = true
31
+ @metadata = { :reflectedMetaData => { :isGrpcClientStream => grpc_request[:is_grpc_client_stream], :isGrpcServerStream => grpc_request[:is_grpc_server_stream] } }
32
+ @cache = {}
33
+ @fuzz_files = ::Set.new
34
+ @event_counter = 0
35
+ NewRelic::Security::Agent.agent.http_request_count.increment
36
+ end
37
+
38
+ def current_time_millis
39
+ (Time.now.to_f * 1000).to_i
40
+ end
41
+
42
+ def self.get_context
43
+ Thread.current[:security_context_data]
44
+ end
45
+
46
+ def self.set_context(grpc_request)
47
+ Thread.current[:security_context_data] = GRPCContext.new(grpc_request)
48
+ end
49
+
50
+ def self.reset_context
51
+ Thread.current[:security_context_data] = nil if Thread.current[:security_context_data]
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,136 @@
1
+ require 'json'
2
+
3
+ module NewRelic::Security
4
+ module Agent
5
+ module Control
6
+ class Health
7
+ attr_reader :jsonName, :stats, :serviceStatus
8
+
9
+ def initialize
10
+ @collectorType = RUBY
11
+ @language = Ruby
12
+ @jsonName = :LAhealthcheck
13
+ @eventType = :sec_health_check_lc
14
+ @timestamp = current_time_millis
15
+ @version = EMPTY_STRING
16
+ @groupName = NewRelic::Security::Agent.config[:mode]
17
+ @policyVersion = nil
18
+ @framework = NewRelic::Security::Agent.config[:framework]
19
+ @protectedServer = nil
20
+ @applicationUUID = NewRelic::Security::Agent.config[:uuid]
21
+ @collectorVersion = NewRelic::Security::VERSION
22
+ @buildNumber = nil
23
+ @jsonVersion = NewRelic::Security::Agent.config[:json_version]
24
+ @eventSentCount = 0
25
+ @eventProcessed = 0
26
+ @eventDropCount = 0
27
+ @httpRequestCount = 0
28
+ @protectedVulnerabilties = nil
29
+ @protectedDB = nil
30
+ @linkingMetadata = add_linking_metadata
31
+ @stats = {}
32
+ @serviceStatus = {} # TODO: Fill this
33
+ @iastEventStats = {}
34
+ @raspEventStats = {}
35
+ @exitEventStats = {}
36
+ end
37
+
38
+ def as_json
39
+ instance_variables.map! do |ivar|
40
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
41
+ end.to_h
42
+ end
43
+
44
+ def to_json
45
+ as_json.to_json
46
+ end
47
+
48
+ def update_health_check
49
+ @httpRequestCount = NewRelic::Security::Agent.agent.http_request_count.fetch_and_reset_counter
50
+ @eventProcessed = NewRelic::Security::Agent.agent.event_processed_count.fetch_and_reset_counter
51
+ @eventSentCount = NewRelic::Security::Agent.agent.event_sent_count.fetch_and_reset_counter
52
+ @eventDropCount = NewRelic::Security::Agent.agent.event_drop_count.fetch_and_reset_counter
53
+ @stats[:nCores] = nil # TODO: add cpu count here
54
+ @stats[:systemTotalMemoryMB] = system_total_memory_mb
55
+ @stats[:systemFreeMemoryMB] = system_free_memory_mb
56
+ @stats[:systemCpuLoad] = system_cpu_load
57
+ @stats[:processCpuUsage] = nil
58
+ @stats[:processRssMB] = nil # TODO: add process rss here
59
+ @stats[:processMaxHeapMB] = nil
60
+ @stats[:processHeapUsageMB] = nil
61
+ @stats[:processDirDiskFreeSpaceMB] = nil
62
+ @stats[:rootDiskFreeSpaceMB] = nil
63
+ @serviceStatus[:websocket] = NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open? ? 'OK' : 'Error'
64
+ @serviceStatus[:logWriter] = NewRelic::Security::Agent.logger ? 'OK' : 'Error'
65
+ @serviceStatus[:initLogWriter] = NewRelic::Security::Agent.init_logger ? 'OK' : 'Error'
66
+ @serviceStatus[:agentActiveStat] = NewRelic::Security::Agent.config[:enabled] ? 'OK' : 'Error'
67
+ @serviceStatus[:iastRestClient] = NewRelic::Security::Agent::Utils.is_IAST? && !NewRelic::Security::Agent.agent.iast_client ? 'Error' : 'OK'
68
+ @iastEventStats = NewRelic::Security::Agent.agent.iast_event_stats.prepare_for_health_check
69
+ @raspEventStats = NewRelic::Security::Agent.agent.rasp_event_stats.prepare_for_health_check
70
+ @exitEventStats = NewRelic::Security::Agent.agent.exit_event_stats.prepare_for_health_check
71
+ rescue Exception => exception
72
+ NewRelic::Security::Agent::logger.error "Exception in finding update_health_check : #{exception.inspect} #{exception.backtrace}"
73
+ end
74
+
75
+ private
76
+
77
+ def current_time_millis
78
+ (Time.now.to_f * 1000).to_i
79
+ end
80
+
81
+ def add_linking_metadata
82
+ linking_metadata = Hash.new
83
+ linking_metadata[:agentRunId] = NewRelic::Security::Agent.config[:agent_run_id]
84
+ linking_metadata.merge!(NewRelic::Security::Agent.config[:linking_metadata])
85
+ end
86
+
87
+ def system_total_memory_mb
88
+ case RbConfig::CONFIG['host_os']
89
+ when /darwin9/
90
+ `sysctl -n hw.memsize`.to_f/1024**2
91
+ when /darwin/
92
+ `sysctl -n hw.memsize`.to_f/1024**2
93
+ when /linux/
94
+ `awk '/MemTotal/ {print $2}' /proc/meminfo`.to_f/1024
95
+ when /freebsd/
96
+ `sysctl sysctl hw.physmem`.to_f/1024**2
97
+ end
98
+ rescue Exception => exception
99
+ NewRelic::Security::Agent::logger.error "Exception in finding system_total_memory_mb : #{exception.inspect} #{exception.backtrace}"
100
+ end
101
+
102
+ def system_free_memory_mb
103
+ # TODO: Add free memory logic for darwin
104
+ case RbConfig::CONFIG['host_os']
105
+ when /darwin9/
106
+ ''
107
+ when /darwin/
108
+ ''
109
+ when /linux/
110
+ `awk '/MemFree/ {print $2}' /proc/meminfo`.to_f/1024
111
+ when /freebsd/
112
+ ''
113
+ end
114
+ rescue Exception => exception
115
+ NewRelic::Security::Agent::logger.error "Exception in finding system_free_memory_mb : #{exception.inspect} #{exception.backtrace}"
116
+ end
117
+
118
+ def system_cpu_load
119
+ case RbConfig::CONFIG['host_os']
120
+ when /darwin9/
121
+ `uptime | awk '// {print $11}'`.to_f
122
+ when /darwin/
123
+ `uptime | awk '// {print $11}'`.to_f
124
+ when /linux/
125
+ `uptime | awk '// {print $11}' | sed 's/,$//'`.to_f
126
+ when /freebsd/
127
+ ''
128
+ end
129
+ rescue Exception => exception
130
+ NewRelic::Security::Agent::logger.error "Exception in finding system_cpu_load : #{exception.inspect} #{exception.backtrace}"
131
+ end
132
+
133
+ end
134
+ end
135
+ end
136
+ end