@newrelic/security-agent 0.1.0 → 0.1.1

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ###v0.1.1
2
+ * Fix in mysql instrumentation on getConnection to check if callback is wrapped
3
+ * NR-123832: Support for fire and forget vulnerability detection
4
+ * Fixes for snapshot file.
5
+ * Handling for high_security config.
1
6
  ### v0.1.0
2
7
  * Handling to use OS specific path separator.
3
8
  * Functionality to create directories in windows environment.
package/index.js CHANGED
@@ -13,7 +13,7 @@ function start(NRAgentObject) {
13
13
  const agent = NRAgentObject.agent;
14
14
  API.newrelic = NRAgentObject;
15
15
  API.setNRAgent(agent);
16
- if (!agent.config.security.agent.enabled || agent.config.security.enabled == null) {
16
+ if (agent.config.high_security) {
17
17
  return;
18
18
  }
19
19
  require('./lib/nr-security-agent');
@@ -34,6 +34,13 @@ function getRequest(shim) {
34
34
  const transactionId = segment.transaction.id;
35
35
  return getRequestFromId(transactionId);
36
36
  }
37
+ else if (shim.agent.getLinkingMetadata()) {
38
+ let linkingMetadata = shim.agent.getLinkingMetadata();
39
+ if (linkingMetadata['trace.id']) {
40
+ let traceId = linkingMetadata['trace.id'];
41
+ return getRequestFromId(traceId);
42
+ }
43
+ }
37
44
  }
38
45
 
39
46
  /**
@@ -51,16 +58,32 @@ function updateRequestBody(shim, data) {
51
58
  setRequest(transactionId, requestData);
52
59
  }
53
60
  }
61
+ if (shim.agent.getLinkingMetadata()) {
62
+ let linkingMetadata = shim.agent.getLinkingMetadata();
63
+ if (linkingMetadata['trace.id']) {
64
+ let traceId = linkingMetadata['trace.id'];
65
+ const requestData = getRequestFromId(traceId);
66
+ if (requestData) {
67
+ requestData.body = data;
68
+ setRequest(traceId, requestData)
69
+ }
70
+ }
71
+ }
54
72
  }
55
73
 
56
74
  /**
57
75
  * Utility to clear data based on transaction Id
58
76
  * @param {*} transactionId
59
77
  */
60
- function gcRequestMap(transactionId) {
78
+ function gcRequestMap(transaction) {
79
+ let transactionId = transaction.id;
80
+ let traceId = transaction._traceId;
61
81
  if (requestMap.has(transactionId)) {
62
82
  requestMap.delete(transactionId);
63
83
  }
84
+ if (requestMap.has(traceId)) {
85
+ requestMap.delete(traceId);
86
+ }
64
87
  }
65
88
 
66
89
  module.exports = {
@@ -126,7 +126,7 @@ function traceElementForRoute () {
126
126
  const trace = stackTraceModule.get();
127
127
  let methodName = null;
128
128
 
129
- for (let i = 0; i < trace.length - 1; i++) {
129
+ for (let i = 0; i <= trace.length - 1; i++) {
130
130
  const funcName = trace[i].getFunctionName();
131
131
  const fileName = trace[i].getFileName();
132
132
  const lineNumber = trace[i].getLineNumber();
@@ -198,6 +198,13 @@ function addRequestData(shim, request) {
198
198
  data.parameterMap = storedRequest.parameterMap;
199
199
  }
200
200
  requestManager.setRequest(transactionId, data);
201
+ if(shim.agent.getLinkingMetadata()){
202
+ let linkingMetadata = shim.agent.getLinkingMetadata();
203
+ if(linkingMetadata['trace.id']){
204
+ let traceId = linkingMetadata['trace.id'];
205
+ requestManager.setRequest(traceId, data)
206
+ }
207
+ }
201
208
  }
202
209
  } catch (error) {
203
210
  logger.debug("Error while preparing incoming request:", error);
@@ -72,7 +72,9 @@ function setupRequestMapCleanup() {
72
72
  }
73
73
  }
74
74
  // Here removing request data corresponding to transactionId
75
- requestManager.gcRequestMap(transaction.id)
75
+ setTimeout(() => {
76
+ requestManager.gcRequestMap(transaction);
77
+ }, 5000);
76
78
  })
77
79
  }
78
80
  }
@@ -88,7 +90,6 @@ function outboundHook(shim, mod, method, moduleName) {
88
90
  shim.wrap(mod, method, function makeRequestWrapper(shim, fn) {
89
91
  return function requestWrapper() {
90
92
  const request = requestManager.getRequest(shim);
91
-
92
93
  const data = {};
93
94
  data.headers = {};
94
95
  data.method = arguments[0].method;
@@ -96,7 +97,6 @@ function outboundHook(shim, mod, method, moduleName) {
96
97
  data.protocol = arguments[0].protocol ? arguments[0].protocol : 'http:';
97
98
  data.headers.host = arguments[0].hostname ? arguments[0].hostname : arguments[0].host;
98
99
  data.port = arguments[0].port;
99
-
100
100
  if (method === 'get') {
101
101
  data.method = 'GET';
102
102
  data.path = '/';
@@ -107,13 +107,11 @@ function outboundHook(shim, mod, method, moduleName) {
107
107
 
108
108
  }
109
109
  }
110
-
111
110
  let completeURL = data.protocol + '//' + data.headers.host + (data.port ? (':' + data.port) : '') + data.path;
112
111
  completeURL = completeURL.split(QUESTION_MARK)[0];
113
112
 
114
113
  if (request && !lodash.isEmpty(data.headers.host) && !lodash.isEmpty(data.path)) {
115
114
  const appUUID = API.getSecAgent().applicationInfo.applicationUUID;
116
-
117
115
  const id = secUtils.getExecutionId().toString();
118
116
  let generateFlag = false;
119
117
  if (arguments[0] && arguments[0].headers && arguments[0].headers.SECHOOK === id) {
@@ -130,7 +128,6 @@ function outboundHook(shim, mod, method, moduleName) {
130
128
  }
131
129
 
132
130
  let traceHeader = EMPTY_STRING;
133
-
134
131
  if (request.headers['nr-csec-tracing-data']) {
135
132
  traceHeader = request.headers['nr-csec-tracing-data'];
136
133
  }
@@ -235,6 +232,13 @@ function addRequestData(shim, request) {
235
232
  data.parameterMap = storedRequest.parameterMap;
236
233
  }
237
234
  requestManager.setRequest(transactionId, data);
235
+ if (shim.agent.getLinkingMetadata()) {
236
+ let linkingMetadata = shim.agent.getLinkingMetadata();
237
+ if (linkingMetadata['trace.id']) {
238
+ let traceId = linkingMetadata['trace.id'];
239
+ requestManager.setRequest(traceId, data)
240
+ }
241
+ }
238
242
  parseFuzzheaders(data, transactionId);
239
243
  }
240
244
  } catch (error) {
@@ -5,13 +5,19 @@
5
5
 
6
6
  const requestManager = require("../../core/request-manager");
7
7
 
8
- const async_hooks = require("async_hooks");
9
8
  const secUtils = require('../../core/sec-utils');
10
9
  const API = require("../../../nr-security-api");
11
10
  const securityMetaData = require('../../core/security-metadata');
12
11
  const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
13
12
  const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
14
13
  const logger = API.getLogger();
14
+ const symbols = {
15
+ unwrapConnection: Symbol('unwrapConnection'),
16
+ unwrapPool: Symbol('unwrapPool'),
17
+ clusterOf: Symbol('clusterOf'),
18
+ createPoolCluster: Symbol('createPoolCluster'),
19
+ wrappedPoolConnection: Symbol('wrappedPoolConnection')
20
+ }
15
21
 
16
22
  module.exports = initialize;
17
23
 
@@ -34,37 +40,53 @@ function initialize(shim, mysql, moduleName) {
34
40
  * Wrapper for mysql module hooks
35
41
  * @param {*} shim
36
42
  * @param {*} mysql
37
- * @param {*} moduleName
38
43
  */
39
- function callbackInitialize(shim, mysql, moduleName) {
40
- shim.__wrappedPoolConnection = false;
44
+ function callbackInitialize(shim, mysql) {
45
+ shim[symbols.wrappedPoolConnection] = false
41
46
 
42
47
  shim.wrapReturn(mysql, "createConnection", wrapCreateConnection);
43
48
  function wrapCreateConnection(shim, fn, fnName, connection) {
49
+ if (shim[symbols.unwrapConnection]) {
50
+ return
51
+ }
52
+
44
53
  if (wrapQueryable(shim, connection, false)) {
45
54
  const connProto = Object.getPrototypeOf(connection);
46
- shim.unwrap(mysql, "createConnection");
55
+ shim[symbols.unwrapConnection] = true
47
56
  }
48
57
  }
49
58
 
50
59
  shim.wrapReturn(mysql, "createPool", wrapCreatePool);
51
60
  function wrapCreatePool(shim, fn, fnName, pool) {
61
+ if (shim[symbols.unwrapPool]) {
62
+ return
63
+ }
64
+
52
65
  if (wrapQueryable(shim, pool, true) && wrapGetConnection(shim, pool)) {
53
- shim.unwrap(mysql, "createPool");
66
+ shim[symbols.unwrapPool] = true
54
67
  }
55
68
  }
56
69
 
57
70
  shim.wrapReturn(mysql, "createPoolCluster", wrapCreatePoolCluster);
58
71
  function wrapCreatePoolCluster(shim, fn, fnName, poolCluster) {
72
+ if (shim[symbols.createPoolCluster]) {
73
+ return
74
+ }
75
+
59
76
  const proto = Object.getPrototypeOf(poolCluster);
60
77
  shim.wrapReturn(proto, "of", wrapPoolClusterOf);
61
78
  function wrapPoolClusterOf(shim, of, _n, poolNamespace) {
62
- if (wrapGetConnection(shim, poolNamespace)) {
63
- shim.unwrap(proto, "of");
79
+ if (poolNamespace[symbols.clusterOf]) {
80
+ return
81
+ }
82
+
83
+ if (wrapGetConnection(shim, poolNamespace) && wrapQueryable(shim, poolNamespace, false)) {
84
+ poolNamespace[symbols.clusterOf] = true
64
85
  }
86
+
65
87
  }
66
88
  if (wrapGetConnection(shim, poolCluster)) {
67
- shim.unwrap(mysql, "createPoolCluster");
89
+ shim[symbols.createPoolCluster] = true
68
90
  }
69
91
  }
70
92
  }
@@ -101,9 +123,12 @@ function wrapGetConnection(shim, connectable) {
101
123
  const args = shim.toArray(arguments);
102
124
  const cbIdx = args.length - 1;
103
125
 
104
- if (shim.isFunction(args[cbIdx]) && !shim.isWrapped(args[cbIdx])) {
126
+ // avoid an infinite loop and check both the cb and the "original" cb before re-wrapping
127
+ // this is only applicable now with the security agent + us doing the same thing
128
+ const original = shim.getOriginalOnce(args[cbIdx])
129
+ if (shim.isFunction(args[cbIdx]) && !(shim.isWrapped(args[cbIdx]) || shim.isWrapped(original))) {
105
130
  let cb = args[cbIdx];
106
- if (!shim.__wrappedPoolConnection) {
131
+ if (!shim[symbols.wrappedPoolConnection]) {
107
132
  cb = shim.wrap(cb, wrapGetConnectionCallback);
108
133
  }
109
134
  args[cbIdx] = shim.bindSegment(cb);
@@ -121,7 +146,7 @@ function wrapGetConnectionCallback(shim, cb) {
121
146
  if (!err && wrapQueryable(shim, conn, false)) {
122
147
  // Leave getConnection wrapped in order to maintain TX state, but we can
123
148
  // simplify the wrapping of its callback in future calls.
124
- shim.__wrappedPoolConnection = true;
149
+ shim[symbols.wrappedPoolConnection] = true
125
150
  }
126
151
  } catch (_err) { }
127
152
  return cb.apply(this, arguments);
@@ -186,7 +211,6 @@ function extractQueryArgs(shim, args) {
186
211
  * @param {*} method
187
212
  */
188
213
  function queryHook(shim, mod, method, isPoolQuery) {
189
-
190
214
  shim.wrap(mod, method, function makeQueryWrapper(shim, fn) {
191
215
  return function queryWrapper() {
192
216
  const parameters = extractQueryArgs(shim, arguments);
@@ -229,4 +253,4 @@ function callbackHook(shim, mod, fun, secEvent) {
229
253
  return fn.apply(this, arguments);
230
254
  }
231
255
  })
232
- }
256
+ }
@@ -106,10 +106,10 @@ function initialize() {
106
106
  initLogger.info("[STEP-3] => Gathering information about the application :", JSON.stringify(applicationInfo));
107
107
  if (NRAgent) {
108
108
  NRAgent.on('started', () => {
109
- if (NRAgent.config.security.enabled) {
109
+ if (NRAgent.config.security.enabled || !NRAgent.config.high_security) {
110
110
  wsClient.initialize();
111
111
  logger.info('NR Agent started with agent_run_id:', NRAgent.config.run_id, NRAgent.config.account_id);
112
- Agent.getAgent().status.setStatus('active');
112
+ Agent.getAgent().status.setStatus('connecting');
113
113
  Agent.setNRAgent(NRAgent);
114
114
  }
115
115
  else {
@@ -36,7 +36,8 @@ SecAgentStatus.codes = {
36
36
  CONNECTING: 'connecting',
37
37
  ACTIVE: "active",
38
38
  DISABLED: "disabled",
39
- DETACHED: "detached"
39
+ DETACHED: "detached",
40
+ INACTIVE:'inactive'
40
41
  }
41
42
 
42
43
  /**
@@ -425,7 +425,7 @@ function getProcStartTime () {
425
425
  function setBuildDetails (agentDir, applicationInfo) {
426
426
  const jsonFileHandler = new JsonHandler(PACKAGE_JSON_FILE, agentDir);
427
427
  const pkg = jsonFileHandler.getJSON();
428
- applicationInfo.collectorVersion = pkg.collectorVersion;
428
+ applicationInfo.collectorVersion = pkg.version;
429
429
  applicationInfo.jsonVersion = pkg.jsonVersion;
430
430
  applicationInfo.buildNumber = pkg.version;
431
431
  }
@@ -8,7 +8,6 @@ const fs = require('fs');
8
8
  const uuidv4 = require('uuid/v4');
9
9
  const logs = require('./logging');
10
10
  const logger = logs.getLogger();
11
- const cp = require('child_process');
12
11
  const OS = require('os');
13
12
  const path = require('path');
14
13
  const checkDiskSpace = require('check-disk-space').default;
@@ -22,7 +21,7 @@ const API = require('../../../nr-security-api');
22
21
  const NRAgent = API.getNRAgent();
23
22
  const lodash = require('lodash')
24
23
  const backupLogFileRegex = /.log.\d/gm;
25
-
24
+ let websocketHealth;
26
25
  /**
27
26
  * Utility to generate UUID
28
27
  * @returns UUID
@@ -238,8 +237,7 @@ async function getHCStats () {
238
237
  * Utility to check Agent status
239
238
  */
240
239
  function isAgentActiveState () {
241
- const linkingMetaData = LinkingMetaData.getLinkingMetadata();
242
- if (AgentStatus.getInstance().getStatus() === AgentStatus.CSECAgentStatus.codes.ACTIVE && (NRAgent ? (NRAgent.config.security.enabled && NRAgent.canCollectData()) : true)) {
240
+ if (AgentStatus.getInstance().getStatus() === AgentStatus.CSECAgentStatus.codes.ACTIVE && (NRAgent ? (NRAgent.config.security.enabled && NRAgent.canCollectData()) : true) && getWSHealthStatus()=='OK') {
243
241
  return 'OK';
244
242
  } else {
245
243
  return 'Error';
@@ -391,6 +389,26 @@ function getPathOfCACert(){
391
389
  const certPath = NRAgent && NRAgent.config.security.ca_bundle_path ? NRAgent.config.security.ca_bundle_path : relativePath;
392
390
  return certPath
393
391
  }
392
+ /**
393
+ * Setting ws health status
394
+ * @param {*} status
395
+ */
396
+ function setWSHealthStatus(status){
397
+ websocketHealth = status;
398
+ }
399
+
400
+ /**
401
+ * Returns value of websocketHealth
402
+ * @returns websocketHealth
403
+ */
404
+ function getWSHealthStatus(){
405
+ if(!websocketHealth){
406
+ return 'Error';
407
+ }
408
+ else{
409
+ return websocketHealth;
410
+ }
411
+ }
394
412
 
395
413
  module.exports = {
396
414
  getUUID,
@@ -411,5 +429,7 @@ module.exports = {
411
429
  logRollOver,
412
430
  getValidatorServiceEndpointURL,
413
431
  getCSECmode,
414
- getPathOfCACert
432
+ getPathOfCACert,
433
+ getWSHealthStatus,
434
+ setWSHealthStatus
415
435
  };
@@ -14,7 +14,6 @@ const { Agent } = require('../../agent');
14
14
  const logs = require('../../logging');
15
15
  const hc = require('../../health-check');
16
16
  const fs = require('fs');
17
- const path = require('path');
18
17
  const { promisify } = require('../../sec-util');
19
18
  const ResponseHandler = require('./response');
20
19
  const { EMPTY_APPLICATION_UUID, LOG_MESSAGES } = require('../../sec-agent-constants');
@@ -27,6 +26,7 @@ const CONNECTING = 'connecting';
27
26
  const WS_ON_OPEN = 'open';
28
27
  const WS_ON_MSG = 'message';
29
28
  const WS_ON_ERR = 'error';
29
+ const WS_ON_PONG = 'pong';
30
30
  const ACK = 'ACK';
31
31
  const INIT_PING_RETRY_DELAY_ON_FAIL = 200; // in ms
32
32
  const RECONNECTION_DELAY = 500; // ms
@@ -39,6 +39,7 @@ const initLogger = logs.getInitLogger();
39
39
  let webSocket;
40
40
  let pingIntervalConst;
41
41
  let initTimeOut;
42
+ let lastPongtime;
42
43
 
43
44
  /**
44
45
  * defines the WebSocket callbacks object
@@ -107,11 +108,14 @@ SecWebSocket.prototype.init = function init() {
107
108
  logger.error("Error in reading certificate:", error);
108
109
  }
109
110
 
110
- webSocket = new WebSocket(validatorService, null, { headers: authHeaders, cert: cert });
111
+ webSocket = new WebSocket(validatorService, null, { headers: authHeaders, cert: cert, handshakeTimeout: 10000 });
111
112
 
112
113
  webSocket.on(WS_ON_OPEN, this.openCB);
113
114
  webSocket.on(WS_ON_MSG, this.msgCB);
114
115
  webSocket.on(WS_ON_ERR, this.errCB);
116
+ webSocket.on(WS_ON_PONG, () => {
117
+ lastPongtime = Date.now();
118
+ })
115
119
  this.instance = webSocket;
116
120
  };
117
121
 
@@ -121,7 +125,6 @@ SecWebSocket.prototype.init = function init() {
121
125
  */
122
126
  SecWebSocket.prototype.reconnect = function reconnect() {
123
127
  const { instance } = this;
124
-
125
128
  if (instance.readyState === WebSocket.CONNECTING &&
126
129
  process.env.CSEC_WS_STATE === CONNECTING) {
127
130
  return;
@@ -133,7 +136,7 @@ SecWebSocket.prototype.reconnect = function reconnect() {
133
136
  this.openCB();
134
137
  return;
135
138
  }
136
- process.env.WS_HEALTH = 'Error';
139
+ commonUtils.setWSHealthStatus('Error');
137
140
  logger.debug(LOG_MESSAGES.DETECTED_BROKEN_CONN);
138
141
  this.obeyReconnect();
139
142
 
@@ -141,10 +144,10 @@ SecWebSocket.prototype.reconnect = function reconnect() {
141
144
 
142
145
  SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
143
146
  const { instance } = this;
144
- if(initTimeOut){
147
+ if (initTimeOut) {
145
148
  clearTimeout(initTimeOut);
146
149
  }
147
-
150
+
148
151
  initTimeOut = setTimeout(() => {
149
152
  logger.debug("Terminating ws instance and reconnecting");
150
153
  try {
@@ -159,16 +162,26 @@ SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
159
162
 
160
163
  SecWebSocket.prototype.pingWS = async function pingWS() {
161
164
  const self = this;
165
+ let currentTime = Date.now();
166
+ let timeDiffInPing = (currentTime - lastPongtime) / 1000;
167
+ if (timeDiffInPing > 40 && lastPongtime != 0) {
168
+ self.obeyReconnect();
169
+ lastPongtime = 0;
170
+ return;
171
+ }
162
172
  try {
163
173
  await promisify(self.instance, self.instance.ping)(ACK, true, function (err) {
164
174
  if (err) {
165
175
  logger.debug("Error while pinging:", err);
176
+ commonUtils.setWSHealthStatus('Error');
177
+ statusUtils.addErrortoBuffer(err);
166
178
  self.obeyReconnect();
167
179
  }
168
180
  });
169
181
  } catch (err) {
170
182
  logger.debug("error in ping:", err);
171
183
  statusUtils.addErrortoBuffer(err);
184
+ commonUtils.setWSHealthStatus('Error');
172
185
  return;
173
186
  }
174
187
  }
@@ -188,16 +201,19 @@ SecWebSocket.prototype.dispatch = async function dispatch(event) {
188
201
  !this.isICReadyForEvents
189
202
  ) {
190
203
  handleDispatchFailure(this, event, eventStr);
204
+ commonUtils.setWSHealthStatus('Error')
191
205
  return;
192
206
  }
193
207
  try {
194
208
  aquireWebSocket();
195
- logger.debug(LOG_MESSAGES.EVENT_SENT + eventStr);
196
- await promisify(this.instance, this.instance.send)(eventStr, { mask: true });
209
+ await promisify(this.instance, this.instance.send)(eventStr, { mask: true }, () => {
210
+ logger.debug(LOG_MESSAGES.EVENT_SENT + eventStr);
211
+ });
197
212
  releaseWebSocket();
198
213
  } catch (error) {
199
214
  logger.debug(LOG_MESSAGES.ERROR_WHILE_SEND_EVENT, eventStr, error);
200
215
  statusUtils.addErrortoBuffer(error);
216
+ commonUtils.setWSHealthStatus('Error')
201
217
  handleDispatchFailure(this, event, eventStr);
202
218
  }
203
219
  };
@@ -278,10 +294,6 @@ const handleDispatchFailure = (self, event, eventStr) => {
278
294
  hc.getInstance().registerEventDrop();
279
295
  queue.enq(eventStr);
280
296
  }
281
- process.env.WS_HEALTH = 'Error';
282
- // If the reconnection is not already in progress
283
- // we would want to reconnect to IC
284
- // self.reconnect();
285
297
  };
286
298
 
287
299
  /**
@@ -316,8 +328,8 @@ const defaultOnOpenCB = () => async function onOpen() {
316
328
  try {
317
329
 
318
330
  this.wsRetryCount = 0;
319
- process.env.WS_HEALTH = 'OK';
320
-
331
+ commonUtils.setWSHealthStatus('OK')
332
+ Agent.getAgent().status.setStatus('active');
321
333
  const applicationInfo = APP_INFO || Agent.getAgent().applicationInfo;
322
334
  await self.dispatchApplicationInfo(applicationInfo);
323
335
 
@@ -328,11 +340,11 @@ const defaultOnOpenCB = () => async function onOpen() {
328
340
  await self.flushEventQueue();
329
341
  ResponseHandler.FuzzRequestHandler.setLastFuzzEnventTime();
330
342
  releaseWebSocket();
331
- initLogger.info(LOG_MESSAGES.SENDING_APPINFO_COMPLETE, JSON.stringify(applicationInfo));
343
+ initLogger.info(LOG_MESSAGES.SENDING_APPINFO_COMPLETE, JSON.stringify(applicationInfo));
332
344
  } catch (err) {
333
345
  logger.error(`Connection broken: ${err.message}`);
334
346
  statusUtils.addErrortoBuffer(err);
335
- process.env.WS_HEALTH = 'Error';
347
+ commonUtils.setWSHealthStatus('Error')
336
348
  setWebSocketConn();
337
349
  self.setValidatorNotReadyForEvents();
338
350
  setTimeout(self.openCB, INIT_PING_RETRY_DELAY_ON_FAIL);
@@ -362,7 +374,9 @@ const defaultOnMsgCB = () => function onMessage(data) {
362
374
  *
363
375
  * @param {SecWebSocket} self
364
376
  */
365
- const defaultOnErrCB = (error) => function onClose(error) {
377
+ const defaultOnErrCB = () => function onClose(error) {
378
+ commonUtils.setWSHealthStatus('Error');
379
+ statusUtils.addErrortoBuffer(error);
366
380
  const self = this;
367
381
  if (error && error.code && (error.code === 'ECONNREFUSED' || error.code === 'EPIPE' || error.code === 'ECONNRESET')) {
368
382
  logger.debug("WS Client Error callback called:", error);
@@ -372,8 +386,6 @@ const defaultOnErrCB = (error) => function onClose(error) {
372
386
  aquireWebSocket(true);
373
387
  self.reconnect();
374
388
  }
375
- statusUtils.addErrortoBuffer(new Error('Error connecting to websocket'));
376
- process.env.WS_HEALTH = 'Error';
377
389
  };
378
390
 
379
391
  SecWebSocket.Events = {
@@ -133,9 +133,9 @@ function generateExitEvent(secEvent) {
133
133
 
134
134
  function sendEvent(secEvent) {
135
135
  const agentModule = Agent.getAgent();
136
- if (agentModule.status.getStatus() === 'active' && NRAgent.config.security.enabled) {
136
+ if (agentModule.status.getStatus() != 'connecting' && NRAgent.config.security.enabled) {
137
137
  agentModule.client.dispatcher(secEvent);
138
- if (!firstEventSent) {
138
+ if (!firstEventSent && agentModule.status.getStatus() === 'active') {
139
139
  initLogger.info("[STEP-8] => First event sent for validation. Security Agent started successfully", JSON.stringify(secEvent));
140
140
  firstEventSent = true;
141
141
  }
@@ -264,23 +264,10 @@ function getSHA256ForFileSync (filePath) {
264
264
  */
265
265
  function getSHA256ForData (data) {
266
266
  try {
267
- if (!process.traceMap) {
268
- return hash.sha256().update(data).digest(HEX_DIGEST); ;
269
- }
270
- const cachedSha = process.traceMap.get(data);
271
-
272
- if (cachedSha) {
273
- return cachedSha;
274
- } else {
275
- const calSha = hash.sha256().update(data).digest(HEX_DIGEST);
276
- if (process.traceMap.size < 2000 && (httpreq && httpreq.data)) {
277
- process.traceMap.set(data, calSha);
278
- }
279
- return calSha;
280
- }
267
+ const calSha = hash.sha256().update(data).digest(HEX_DIGEST);
268
+ return calSha;
281
269
  } catch (err) {
282
- logger.error('Unable to calculate SHA256 for data.');
283
- logger.debug(err);
270
+ logger.debug('Unable to calculate SHA256 for data.',err);
284
271
  return null;
285
272
  }
286
273
  }
@@ -25,33 +25,33 @@ const logger = logs.getLogger();
25
25
  const statusFile = njsAgentConstants.STATUS_LOG_FILE;
26
26
 
27
27
  const statusTemplate = 'Snapshot timestamp: : %s\n' +
28
- `CSEC Agent start timestamp: ${njsAgentConstants.AGENT_START_TIME} with application uuid:${commonUtils.getUUID()}\n` +
29
- `SEC_HOME: ${njsAgentConstants.CSEC_HOME}\n` +
30
- `Agent location: ${AGENT_DIR}\n` +
31
- `Using CSEC Agent for Node.js, Node version:${process.version}, PID:${process.pid}\n` +
32
- `Process title: ${process.title}\n` +
33
- `Process binary: ${process.execPath}\n` +
34
- 'Application location: %s\n' +
35
- `Current working directory: ${process.cwd()}\n` +
36
- `Agent mode: ${commonUtils.getCSECmode()}\n` +
37
- 'Application server: %s\n' +
38
- 'Application Framework: %s\n' +
39
- `Websocket connection to Prevent Web: ${commonUtils.getValidatorServiceEndpointURL()}, Status: %s\n` +
40
- 'Instrumentation successful:\n' +
41
- 'Tracking loaded modules in the application:\n' +
42
- 'Policy applied successfully. Policy version is: %s\n' +
43
- 'Started Health Check for Agent\n' +
44
- 'Started Inbound and Outbound monitoring \n' +
45
- '\nProcess stats:\n%s\n' +
46
- '\nService stats:\n%s\n' +
47
- '\nLast 5 errors: \n%s\n' +
48
- '\nLast 5 Health Checks are:\n %s \n';
28
+ `CSEC Agent start timestamp: ${njsAgentConstants.AGENT_START_TIME} with application uuid:${commonUtils.getUUID()}\n` +
29
+ `SEC_HOME: ${njsAgentConstants.CSEC_HOME}\n` +
30
+ `Agent location: ${AGENT_DIR}\n` +
31
+ `Using CSEC Agent for Node.js, Node version:${process.version}, PID:${process.pid}\n` +
32
+ `Process title: ${process.title}\n` +
33
+ `Process binary: ${process.execPath}\n` +
34
+ 'Application location: %s\n' +
35
+ `Current working directory: ${process.cwd()}\n` +
36
+ `Agent mode: ${commonUtils.getCSECmode()}\n` +
37
+ 'Application server: %s\n' +
38
+ 'Application Framework: %s\n' +
39
+ `Websocket connection to Prevent Web: ${commonUtils.getValidatorServiceEndpointURL()}, Status: %s\n` +
40
+ 'Instrumentation successful:\n' +
41
+ 'Tracking loaded modules in the application:\n' +
42
+ 'Policy applied successfully. Policy version is: %s\n' +
43
+ 'Started Health Check for Agent\n' +
44
+ 'Started Inbound and Outbound monitoring \n' +
45
+ '\nProcess stats:\n%s\n' +
46
+ '\nService stats:\n%s\n' +
47
+ '\nLast 5 errors: \n%s\n' +
48
+ '\nLast 5 Health Checks are:\n %s \n';
49
49
 
50
50
  /**
51
51
  * Utility to add hc in hc buffer
52
52
  * @param {*} healthCheck
53
53
  */
54
- function addHCtoBuffer (healthCheck) {
54
+ function addHCtoBuffer(healthCheck) {
55
55
  try {
56
56
  bufferedHC.enq(healthCheck);
57
57
  lastHC = healthCheck;
@@ -64,9 +64,12 @@ function addHCtoBuffer (healthCheck) {
64
64
  * Utility to add error object in error buffer
65
65
  * @param {*} error
66
66
  */
67
- function addErrortoBuffer (error) {
67
+ function addErrortoBuffer(error) {
68
68
  try {
69
- bufferedErrors.enq(error);
69
+ let errorObj = {
70
+ 'error': error.stack
71
+ }
72
+ bufferedErrors.enq(errorObj);
70
73
  } catch (error) {
71
74
  logger.debug(error);
72
75
  }
@@ -75,19 +78,19 @@ function addErrortoBuffer (error) {
75
78
  /**
76
79
  * utility to format status template with dynamic values
77
80
  */
78
- function getFormattedData () {
81
+ function getFormattedData() {
79
82
  const Agent = require('./agent').Agent.getAgent();
80
83
  const appInfo = Agent.applicationInfo;
81
84
  const deployedApplications = appInfo && appInfo.serverInfo && appInfo.serverInfo.deployedApplications;
82
85
  const appLoc = deployedApplications[0].deployedPath;
83
- const formattedSnapshot = util.format(statusTemplate, new Date().toString(), appLoc, appInfo.serverInfo.name, njsAgentConstants.FRAMEWORK, process.env.WS_HEALTH, appInfo.policyVersion, getKeyValPairs(lastHC.stats), getKeyValPairs(lastHC.serviceStatus), JSON.stringify(getBufferedErrors()), JSON.stringify(getBufferedHC()));
86
+ const formattedSnapshot = util.format(statusTemplate, new Date().toString(), appLoc, appInfo.serverInfo.name, njsAgentConstants.FRAMEWORK,commonUtils.getWSHealthStatus(), appInfo.policyVersion, getKeyValPairs(lastHC.stats), getKeyValPairs(lastHC.serviceStatus), JSON.stringify(getBufferedErrors()), JSON.stringify(getBufferedHC()));
84
87
  return formattedSnapshot;
85
88
  }
86
89
 
87
90
  /**
88
91
  * utility to write snapshot in snapshot file
89
92
  */
90
- function writeSnapshot () {
93
+ function writeSnapshot() {
91
94
  const snapshot = getFormattedData();
92
95
  commonUtils.removeOlderSnapshots();
93
96
  fs.writeFile(statusFile, snapshot, { mode: 0o777 }, function (err) {
@@ -106,14 +109,14 @@ function writeSnapshot () {
106
109
  /**
107
110
  * return buffered error instance.
108
111
  */
109
- function getRingBufferedErrors () {
112
+ function getRingBufferedErrors() {
110
113
  return bufferedErrors;
111
114
  }
112
115
 
113
116
  /**
114
117
  * return buffered HC list without null, undefined
115
118
  */
116
- function getBufferedHC () {
119
+ function getBufferedHC() {
117
120
  try {
118
121
  HC = lodash.compact(bufferedHC._elements);
119
122
  } catch (error) {
@@ -125,7 +128,7 @@ function getBufferedHC () {
125
128
  /**
126
129
  * returns buffered error list without null, undefined
127
130
  */
128
- function getBufferedErrors () {
131
+ function getBufferedErrors() {
129
132
  try {
130
133
  errors = lodash.compact(bufferedErrors._elements);
131
134
  } catch (error) {
@@ -138,7 +141,7 @@ function getBufferedErrors () {
138
141
  * Utility to get key val pairs from json object
139
142
  * @param {*} jsonObject
140
143
  */
141
- function getKeyValPairs (jsonObject) {
144
+ function getKeyValPairs(jsonObject) {
142
145
  let statStr = njsAgentConstants.EMPTY_STR;
143
146
  for (const key in jsonObject) {
144
147
  statStr = statStr + key + ': ' + jsonObject[key] + '\n';
@@ -195,7 +195,7 @@ function handleWorkerMessage(json) {
195
195
  break;
196
196
  }
197
197
  case WorkerMessage.TYPE.ERROR: {
198
- statusUtils.addErrortoBuffer(msg.getPayload().stack);
198
+ statusUtils.addErrortoBuffer(msg.getPayload());
199
199
  break;
200
200
  }
201
201
  default:
@@ -206,7 +206,7 @@ function handleWorkerMessage(json) {
206
206
  * Sends Agent HelthCheck to Validator.
207
207
  */
208
208
  const sendHC = async function () {
209
- if (AgentStatus.getInstance().getStatus() === AgentStatus.CSECAgentStatus.codes.ACTIVE && (NRAgent ? (NRAgent.config.security.enabled && NRAgent.canCollectData()) : true)) {
209
+ if (AgentStatus.getInstance().getStatus() != AgentStatus.CSECAgentStatus.codes.DISABLED && (NRAgent ? (NRAgent.config.security.enabled && NRAgent.canCollectData()) : true)) {
210
210
  let json = hc.getInstance().get();
211
211
  json = JSON.parse(json);
212
212
  json.policyVersion = applicationInfo.policyVersion;
@@ -218,7 +218,7 @@ const sendHC = async function () {
218
218
  }
219
219
 
220
220
  json.serviceStatus = {};
221
- json.serviceStatus.websocket = process.env.WS_HEALTH;
221
+ json.serviceStatus.websocket = commonUtils.getWSHealthStatus();
222
222
  json.serviceStatus.logWriter = await commonUtils.isLogAccessible(`${LOG_DIR}node-security-collector.log`);
223
223
  json.serviceStatus.initLogWriter = await commonUtils.isLogAccessible(`${LOG_DIR}node-security-collector-init.log`);
224
224
  json.serviceStatus.statusLogWriter = await commonUtils.isLogAccessible(njsAgentConstants.STATUS_LOG_FILE);
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@newrelic/security-agent",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "New Relic Security Agent for Node.js",
5
5
  "main": "index.js",
6
- "collectorVersion": "1.0.3-limited-preview",
7
6
  "jsonVersion": "1.0.1",
8
7
  "contributors": [
9
8
  {