newrelic_security 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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