@newrelic/security-agent 0.5.0 → 0.7.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 (33) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -0
  3. package/THIRD_PARTY_NOTICES.md +1 -211
  4. package/lib/instrumentation-security/core/sec-utils.js +1 -0
  5. package/lib/instrumentation-security/hooks/@nestjs/nr-nestjs-core.js +1 -0
  6. package/lib/instrumentation-security/hooks/express/nr-express.js +2 -0
  7. package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +1 -0
  8. package/lib/instrumentation-security/hooks/fs/nr-fs.js +56 -20
  9. package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +2 -1
  10. package/lib/instrumentation-security/hooks/http/nr-http.js +96 -4
  11. package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +3 -1
  12. package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +1 -0
  13. package/lib/instrumentation-security/hooks/mongodb/common.js +2 -0
  14. package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +1 -1
  15. package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +5 -2
  16. package/lib/instrumentation-security/hooks/native/nr-childProcess.js +3 -0
  17. package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +1 -0
  18. package/lib/instrumentation-security/hooks/require/nr-require.js +3 -2
  19. package/lib/instrumentation-security/hooks/restify/nr-restify.js +3 -1
  20. package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +4 -2
  21. package/lib/nr-security-agent/index.js +9 -4
  22. package/lib/nr-security-agent/lib/core/Auth-headers.js +7 -0
  23. package/lib/nr-security-agent/lib/core/LogMessage.js +40 -14
  24. package/lib/nr-security-agent/lib/core/commonUtils.js +35 -4
  25. package/lib/nr-security-agent/lib/core/connections/aws-lambda-client.js +6 -8
  26. package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +21 -6
  27. package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +5 -2
  28. package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +20 -1
  29. package/lib/nr-security-agent/lib/core/sec-agent-constants.js +3 -0
  30. package/lib/nr-security-agent/lib/core/sec-util.js +14 -6
  31. package/lib/nr-security-agent/lib/core/statusUtils.js +4 -1
  32. package/lib/nr-security-agent/lib/core/websocket-client.js +19 -8
  33. package/package.json +4 -5
@@ -9,14 +9,16 @@ const stringify = require('fast-safe-stringify');
9
9
  const restClient = require('../../restclient');
10
10
  const { Agent } = require('../../../agent');
11
11
  const { FuzzFailEvent } = require('../../../FuzzFailEvent');
12
- const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH, CSEC_SEP } = require('../../../sec-agent-constants');
12
+ const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH, CSEC_SEP, SEVERE } = require('../../../sec-agent-constants');
13
13
  const LOCALHOST = 'localhost';
14
14
  const COLON_SLASH_SLASH = '://';
15
15
  const http = require('http');
16
16
  const https = require('https');
17
17
  const IASTUtil = require('./IASTUtils');
18
+ const LogMessage = require('../../../LogMessage');
18
19
 
19
20
  require('dns').setDefaultResultOrder('ipv4first')
21
+ const PolicyManager = require('../../../Policy');
20
22
 
21
23
  const {
22
24
  IS_LAMBDA_ENV,
@@ -42,6 +44,16 @@ function startIASTSchedular() {
42
44
  if (iastIntervalConst) {
43
45
  clearInterval(iastIntervalConst);
44
46
  }
47
+ let probingInterval = 5;
48
+
49
+ const policyInstance = PolicyManager.getInstance();
50
+ if (policyInstance.data) {
51
+ probingInterval = policyInstance.data.vulnerabilityScan.iastScan.probing.interval;
52
+ }
53
+ if (isNaN(probingInterval)) {
54
+ probingInterval = 5;
55
+ }
56
+
45
57
  iastIntervalConst = setInterval(() => {
46
58
  let data = IASTUtil.generateIASTDataRequest();
47
59
  let currentTime = Date.now();
@@ -55,7 +67,7 @@ function startIASTSchedular() {
55
67
  if (timeDiffInSeconds > 5 && additionalCoolDownTime == 0) {
56
68
  Agent.getAgent().client.dispatcher(data);
57
69
  }
58
- }, 5000);
70
+ }, probingInterval * 1000);
59
71
  }
60
72
 
61
73
  /**
@@ -65,7 +77,7 @@ function startIASTSchedular() {
65
77
  function logScannedApiId(fuzzHeader, requestURL) {
66
78
  try {
67
79
  let apiId = fuzzHeader.split(CSEC_SEP)[0]
68
- if (apiId && !fuzzedApiIDSet.has(apiId) ) {
80
+ if (apiId && !fuzzedApiIDSet.has(apiId)) {
69
81
  fuzzedApiIDSet.add(apiId);
70
82
  logger.info("IAST Scan for API %s with ID : %s started.", requestURL, apiId);
71
83
  }
@@ -87,6 +99,9 @@ function handler(json) {
87
99
  IASTUtil.addPendingRequestId(json.id);
88
100
  } catch (error) {
89
101
  logger.error('Parsing exeception in fuzz request: ', error);
102
+
103
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Parsing exeception in fuzz request', __filename, error);
104
+ require('../../../commonUtils').addLogEventtoBuffer(logMessage);
90
105
  }
91
106
  if (!fuzzRequest) {
92
107
  return;
@@ -132,10 +147,10 @@ function handleFuzzRequest(fuzzDetails) {
132
147
 
133
148
  IASTUtil.completedRequestsMapInit(fuzzRequest.id);
134
149
 
135
- if(fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]){
150
+ if (fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
136
151
  logScannedApiId(fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID], fuzzRequest.requestURI)
137
152
  }
138
-
153
+
139
154
 
140
155
  logger.info('Firing http request:: URL: ' + config.url);
141
156
 
@@ -181,7 +196,7 @@ function handleFuzzResponse(response, fuzzDetails) {
181
196
  function parseAxiosHttpRequestToFuzz(requestObject) {
182
197
  let serverName = requestObject.serverName ? requestObject.serverName : LOCALHOST;
183
198
  let host = serverName + COLON + requestObject.serverPort;
184
- if(requestObject.headers && requestObject.headers['content-length']){
199
+ if (requestObject.headers && requestObject.headers['content-length']) {
185
200
  delete requestObject.headers['content-length'];
186
201
  }
187
202
  return {
@@ -5,7 +5,7 @@
5
5
 
6
6
  const _ = require('lodash');
7
7
 
8
- const { SERVER_COMMAND } = require('../../../sec-agent-constants');
8
+ const { SERVER_COMMAND, SEVERE } = require('../../../sec-agent-constants');
9
9
  const UnsupportedAgentHandler = require('./unsupported-agent-handler');
10
10
  const StartupHandshakeHandler = require('./startup-handshake-handler');
11
11
  const FuzzRequestHandler = require('./fuzz-request-handler');
@@ -17,6 +17,7 @@ const coolDown = require('./iast-cool-down');
17
17
  const recordDeletion = require('./iast-record-deletion');
18
18
  const logs = require('../../../logging');
19
19
  let logger = logs.getLogger();
20
+ const LogMessage = require('../../../LogMessage');
20
21
 
21
22
  const commandHandlerMap = {
22
23
  [SERVER_COMMAND.STARTUP_WELCOME_MSG]: StartupHandshakeHandler,
@@ -38,7 +39,9 @@ const handle = function handle (json) {
38
39
  NoOpHandler.handler.call(this, json);
39
40
  }
40
41
  } catch (error) {
41
- logger.error("Error in handling Control Commnad",error);
42
+ logger.error("Error in handling Control Command",error);
43
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error in handling Control Command', __filename, error);
44
+ require('../../../commonUtils').addLogEventtoBuffer(logMessage);
42
45
  }
43
46
 
44
47
  };
@@ -17,10 +17,11 @@ const hc = require('../../health-check');
17
17
  const fs = require('fs');
18
18
  const { promisify } = require('../../sec-util');
19
19
  const ResponseHandler = require('./response');
20
- const { EMPTY_APPLICATION_UUID, LOG_MESSAGES, CSEC_SEP, NR_CSEC_FUZZ_REQUEST_ID, EXITEVENT, JSON_NAME, RASP } = require('../../sec-agent-constants');
20
+ const { EMPTY_APPLICATION_UUID, LOG_MESSAGES, CSEC_SEP, NR_CSEC_FUZZ_REQUEST_ID, EXITEVENT, JSON_NAME, RASP, SEVERE } = require('../../sec-agent-constants');
21
21
  const statusUtils = require('../../statusUtils');
22
22
  const commonUtils = require('../../commonUtils');
23
23
  const IASTUtil = require('../websocket/response/IASTUtils');
24
+ const LogMessage = require('../../LogMessage');
24
25
 
25
26
  const BUSY = 'busy';
26
27
  const IDLE = 'idle';
@@ -109,6 +110,9 @@ SecWebSocket.prototype.init = function init() {
109
110
  cert = fs.readFileSync(certPath, 'utf8');
110
111
  } catch (error) {
111
112
  logger.error("Error in reading certificate:", error);
113
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error in reading certificate for websocket', __filename, error);
114
+ commonUtils.addLogEventtoBuffer(logMessage);
115
+
112
116
  }
113
117
 
114
118
  webSocket = new WebSocket(validatorService, { headers: authHeaders, cert: cert, handshakeTimeout: 10000 });
@@ -120,6 +124,17 @@ SecWebSocket.prototype.init = function init() {
120
124
  lastPongtime = Date.now();
121
125
  commonUtils.setWSHealthStatus('OK');
122
126
  logger.debug("Pong received from Validator");
127
+
128
+ try {
129
+ let logBuffer = commonUtils.getLogEvents();
130
+ while (logBuffer.size() > 0) {
131
+ let logEvent = logBuffer.deq();
132
+ this.dispatch(logEvent);
133
+ }
134
+ } catch (error) {
135
+ logger.error("Error while sending log events", error);
136
+ }
137
+
123
138
  })
124
139
  webSocket.on('close', () => {
125
140
  logger.warn("WS connection closed");
@@ -172,6 +187,7 @@ SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
172
187
  SecWebSocket.prototype.pingWS = async function pingWS() {
173
188
  const self = this;
174
189
  let currentTime = Date.now();
190
+
175
191
  let timeDiffInPing = (currentTime - lastPongtime) / 1000;
176
192
  if (timeDiffInPing > 40 && lastPongtime != 0) {
177
193
  self.obeyReconnect();
@@ -193,6 +209,7 @@ SecWebSocket.prototype.pingWS = async function pingWS() {
193
209
  commonUtils.setWSHealthStatus('Error');
194
210
  return;
195
211
  }
212
+
196
213
  }
197
214
 
198
215
  /**
@@ -412,6 +429,8 @@ const defaultOnErrCB = () => function onClose(error) {
412
429
  statusUtils.addErrortoBuffer(error);
413
430
  const self = this;
414
431
  logger.error("Error while connecting to validator:", error.message)
432
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error while connecting to validator', __filename, error);
433
+ commonUtils.addLogEventtoBuffer(logMessage);
415
434
  logger.warn("Security Agent INACTIVE!!!")
416
435
  if (error && error.code && (error.code === 'ECONNREFUSED' || error.code === 'EPIPE' || error.code === 'ECONNRESET')) {
417
436
  logger.debug("WS Client Error callback called:", error);
@@ -108,6 +108,9 @@ module.exports = {
108
108
  CSEC_SEP: ':IAST:',
109
109
  RASP: 'RASP',
110
110
  IAST: 'IAST',
111
+ SEVERE: 'SEVERE',
112
+ HIGH: 'HIGH',
113
+ CRITICAL: 'CRITICAL',
111
114
 
112
115
  LOG_MESSAGES: {
113
116
  LOADED_CSEC_ENVS_MSG: '[STEP-1][COMPLETE][env] Environment Information Gathering Done.',
@@ -9,10 +9,12 @@ const { SecEvent } = require('./event');
9
9
  const logs = require('./logging');
10
10
  const { Agent } = require('./agent');
11
11
  const shaUtil = require('./sha-size-util');
12
- const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP } = require('./sec-agent-constants');
12
+ const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP, SEVERE } = require('./sec-agent-constants');
13
13
  const API = require('../../../nr-security-api');
14
14
  const NRAgent = API.getNRAgent();
15
15
  const HC = require('./health-check');
16
+ const LogMessage = require('./LogMessage');
17
+ const commonUtils = require('./commonUtils');
16
18
 
17
19
  const statusUtils = require('./statusUtils');
18
20
 
@@ -67,10 +69,10 @@ function generateSecEvent(securityMetadata) {
67
69
  const traceObject = securityMetadata.traceObject;
68
70
  const request = securityMetadata.request;
69
71
 
70
- if(request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
72
+ if (request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
71
73
  HC.getInstance().iastEventStats.processed++;
72
74
  }
73
- else{
75
+ else {
74
76
  HC.getInstance().raspEventStats.processed++;
75
77
  }
76
78
  try {
@@ -102,11 +104,14 @@ function generateSecEvent(securityMetadata) {
102
104
  } catch (e) {
103
105
  statusUtils.addErrortoBuffer(e);
104
106
  logger.error("Error in generating event:", e);
105
-
106
- if(request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
107
+
108
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error while generating event', __filename, e);
109
+ commonUtils.addLogEventtoBuffer(logMessage);
110
+
111
+ if (request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
107
112
  HC.getInstance().iastEventStats.errorCount++;
108
113
  }
109
- else{
114
+ else {
110
115
  HC.getInstance().raspEventStats.errorCount++;
111
116
  }
112
117
  }
@@ -141,6 +146,9 @@ function generateExitEvent(secEvent) {
141
146
  }
142
147
  } catch (error) {
143
148
  logger.error('Error in generating exit event', error);
149
+
150
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error in generating exit event', __filename, error);
151
+ commonUtils.addLogEventtoBuffer(logMessage);
144
152
  HC.getInstance().exitEventStats.errorCount++;
145
153
  }
146
154
  }
@@ -83,7 +83,7 @@ function getFormattedData() {
83
83
  const appInfo = Agent.applicationInfo;
84
84
  const deployedApplications = appInfo && appInfo.serverInfo && appInfo.serverInfo.deployedApplications;
85
85
  const appLoc = deployedApplications[0].deployedPath;
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()));
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()));
87
87
  return formattedSnapshot;
88
88
  }
89
89
 
@@ -96,6 +96,9 @@ function writeSnapshot() {
96
96
  fs.writeFile(statusFile, snapshot, { mode: 0o777 }, function (err) {
97
97
  if (err) {
98
98
  logger.debug(err.message);
99
+ const LogMessage = require('./LogMessage');
100
+ const logMessage = new LogMessage.logMessage("SEVERE", 'Error in creating snapshot file', __filename, err);
101
+ commonUtils.addLogEventtoBuffer(logMessage);
99
102
  } else {
100
103
  logger.info('Snapshot updated to file: %s', statusFile);
101
104
  fs.chmod(statusFile, 0o777, (err) => {
@@ -15,6 +15,7 @@ const initLogger = require('./logging').getInitLogger();
15
15
  const commonUtils = require('./commonUtils');
16
16
  const API = require('../../../nr-security-api');
17
17
  const NRAgent = API.getNRAgent();
18
+ const LogMessage = require('./LogMessage');
18
19
  const {
19
20
  SecWebSocket,
20
21
  setWebSocketConn,
@@ -69,6 +70,15 @@ const initialize = function () {
69
70
  setTimeout(() => {
70
71
  setInterval(sendHC, HC_INTERVAL_MS);
71
72
  sendHC();
73
+ try {
74
+ const framework = NRAgent.environment.get('Framework')[0];
75
+ const dispatcher = NRAgent.environment.get('Dispatcher')[0];
76
+ const logMessage = new LogMessage.logMessage("SEVERE", `Detected framework: ${framework} and dispatcher: ${dispatcher}`, __filename, null);
77
+ commonUtils.addLogEventtoBuffer(logMessage);
78
+ } catch (error) {
79
+ logger.debug("Error while extracting data from APM environment",error);
80
+ }
81
+
72
82
  }, 30000);
73
83
 
74
84
  setInterval(commonUtils.logRollOver, 30000);
@@ -140,17 +150,18 @@ const sendHC = async function () {
140
150
  * @param {JSON} eventJson
141
151
  */
142
152
  function getDispatcherAndSendEvent(eventJson) {
143
- const eventType = eventJson.jsonName;
144
- if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
145
- hc.getInstance().registerEventProcessed();
146
- }
153
+ if (eventJson) {
154
+ const eventType = eventJson.jsonName;
155
+ if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
156
+ hc.getInstance().registerEventProcessed();
157
+ }
147
158
 
148
- dispatcher(eventJson);
159
+ dispatcher(eventJson);
149
160
 
150
- if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
151
- hc.getInstance().registerEventSent();
161
+ if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
162
+ hc.getInstance().registerEventSent();
163
+ }
152
164
  }
153
-
154
165
  }
155
166
 
156
167
  /**
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@newrelic/security-agent",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "New Relic Security Agent for Node.js",
5
5
  "main": "index.js",
6
- "jsonVersion": "1.1.0",
6
+ "jsonVersion": "1.1.1",
7
7
  "contributors": [
8
8
  {
9
9
  "name": "Sumit Suthar",
@@ -35,9 +35,8 @@
35
35
  "url": "https://github.com/newrelic/csec-node-agent.git"
36
36
  },
37
37
  "dependencies": {
38
- "@aws-sdk/client-lambda": "^3.436.0",
39
- "axios": "1.6.0",
40
- "check-disk-space": "3.4.0",
38
+ "axios": "^1.6.5",
39
+ "check-disk-space": "^3.4.0",
41
40
  "content-type": "^1.0.5",
42
41
  "fast-safe-stringify": "^2.1.1",
43
42
  "find-package-json": "^1.2.0",