@newrelic/security-agent 0.1.3 → 0.2.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 +16 -0
- package/THIRD_PARTY_NOTICES.md +3 -3
- package/lib/instrumentation-security/core/constants.js +0 -1
- package/lib/instrumentation-security/hooks/fs/nr-fs.js +6 -5
- package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +16 -0
- package/lib/instrumentation-security/hooks/require/nr-require.js +12 -5
- package/lib/nr-security-agent/lib/core/Auth-headers.js +1 -0
- package/lib/nr-security-agent/lib/core/applicationinfo.js +1 -1
- package/lib/nr-security-agent/lib/core/commonUtils.js +10 -10
- package/lib/nr-security-agent/lib/core/connections/websocket/index.js +0 -6
- package/lib/nr-security-agent/lib/core/connections/websocket/response/IASTUtils.js +50 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/collector-policy-handler.js +4 -1
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +90 -31
- package/lib/nr-security-agent/lib/core/connections/websocket/response/iast-cool-down.js +15 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/iast-record-deletion.js +19 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +17 -5
- package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +9 -58
- package/lib/nr-security-agent/lib/core/logging.js +89 -71
- package/lib/nr-security-agent/lib/core/sec-agent-constants.js +3 -2
- package/lib/nr-security-agent/lib/core/statusUtils.js +1 -1
- package/lib/nr-security-agent/lib/core/websocket-client.js +5 -168
- package/package.json +4 -4
- package/lib/nr-security-agent/lib/core/worker-entities.js +0 -82
- package/lib/nr-security-agent/lib/core/worker.js +0 -148
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
### v0.2.1 (2023-08-07)
|
|
2
|
+
#### Bug Fixes
|
|
3
|
+
* Pinned check-disk-space to v3.3.1 to support Node.js v14
|
|
4
|
+
#### Miscellaneous Chores
|
|
5
|
+
* deps: Updated semver to v7.5.4 and @aws-sdk/client-lambda to v3.363.0
|
|
6
|
+
#### Continuous Integration
|
|
7
|
+
* Add Node.js 20.x to CI
|
|
8
|
+
|
|
9
|
+
### v0.2.0 (2023-07-24)
|
|
10
|
+
#### Features
|
|
11
|
+
* IAST data pull implementation
|
|
12
|
+
* Logging update
|
|
13
|
+
|
|
14
|
+
#### Bug Fixes
|
|
15
|
+
* Param fixes for mysql and file hooks.
|
|
16
|
+
|
|
1
17
|
### v0.1.3
|
|
2
18
|
* Updated semver to v7.5.3 (Fix for CWE-1333)
|
|
3
19
|
* Updated request-ip, log4js, html-entities, uuid and fast-safe-stringify to latest version.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -75,7 +75,7 @@ code, the source code can be found at [https://github.com/newrelic/csec-node-age
|
|
|
75
75
|
|
|
76
76
|
### @aws-sdk/client-lambda
|
|
77
77
|
|
|
78
|
-
This product includes source derived from [@aws-sdk/client-lambda](https://github.com/aws/aws-sdk-js-v3) ([v3.
|
|
78
|
+
This product includes source derived from [@aws-sdk/client-lambda](https://github.com/aws/aws-sdk-js-v3) ([v3.379.1](https://github.com/aws/aws-sdk-js-v3/tree/v3.379.1)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.379.1/LICENSE):
|
|
79
79
|
|
|
80
80
|
```
|
|
81
81
|
Apache License
|
|
@@ -311,7 +311,7 @@ THE SOFTWARE.
|
|
|
311
311
|
|
|
312
312
|
### check-disk-space
|
|
313
313
|
|
|
314
|
-
This product includes source derived from [check-disk-space](https://github.com/Alex-D/check-disk-space) ([v3.
|
|
314
|
+
This product includes source derived from [check-disk-space](https://github.com/Alex-D/check-disk-space) ([v3.3.1](https://github.com/Alex-D/check-disk-space/tree/v3.3.1)), distributed under the [MIT License](https://github.com/Alex-D/check-disk-space/blob/v3.3.1/LICENSE):
|
|
315
315
|
|
|
316
316
|
```
|
|
317
317
|
MIT License
|
|
@@ -684,7 +684,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
684
684
|
|
|
685
685
|
### semver
|
|
686
686
|
|
|
687
|
-
This product includes source derived from [semver](https://github.com/npm/node-semver) ([v7.5.
|
|
687
|
+
This product includes source derived from [semver](https://github.com/npm/node-semver) ([v7.5.4](https://github.com/npm/node-semver/tree/v7.5.4)), distributed under the [ISC License](https://github.com/npm/node-semver/blob/v7.5.4/LICENSE):
|
|
688
688
|
|
|
689
689
|
```
|
|
690
690
|
The ISC License
|
|
@@ -16,6 +16,7 @@ const logger = API.getLogger();
|
|
|
16
16
|
const { STRING, QUESTIONMARK, DOTDOTSLASH, UNDEFINED, SELF_FD_PATH, SLASH, SLASHDOTDOT, NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
|
|
17
17
|
const fsConsts = process.binding('constants').fs;
|
|
18
18
|
const agentModule = API.getSecAgent();
|
|
19
|
+
const lodash = require('lodash');
|
|
19
20
|
|
|
20
21
|
const requireHook = require('../require/nr-require');
|
|
21
22
|
|
|
@@ -127,7 +128,7 @@ function openHook(shim, mod, moduleName) {
|
|
|
127
128
|
parameters[0] = arguments[0];
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
|
-
if (request && typeof parameters[0] === STRING) {
|
|
131
|
+
if (request && typeof parameters[0] === STRING && !lodash.isEmpty(parameters[0]) ) {
|
|
131
132
|
const policy = API.getPolicy();
|
|
132
133
|
const dynamicScanningFlag = policy.data ? policy.data.vulnerabilityScan.iastScan.enabled : false;
|
|
133
134
|
const url = request.url;
|
|
@@ -180,7 +181,7 @@ function probableToFAHooks(shim, mod, moduleName) {
|
|
|
180
181
|
const interceptedArgs = [arguments[0]];
|
|
181
182
|
shim.interceptedArgs = interceptedArgs;
|
|
182
183
|
const request = requestManager.getRequest(shim);
|
|
183
|
-
if (request && typeof arguments[0] === STRING) {
|
|
184
|
+
if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
|
|
184
185
|
const traceObject = secUtils.getTraceObject(shim);
|
|
185
186
|
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
|
|
186
187
|
this.secEvent = API.generateSecEvent(secMetadata);
|
|
@@ -209,7 +210,7 @@ function probableToFIHooks(shim, mod, moduleName) {
|
|
|
209
210
|
const interceptedArgs = [arguments[0]];
|
|
210
211
|
shim.interceptedArgs = interceptedArgs;
|
|
211
212
|
const request = requestManager.getRequest(shim);
|
|
212
|
-
if (request && typeof arguments[0] === STRING) {
|
|
213
|
+
if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
|
|
213
214
|
const traceObject = secUtils.getTraceObject(shim);
|
|
214
215
|
if (fun === COPY_FILE || fun === RENAME) {
|
|
215
216
|
const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
|
|
@@ -243,7 +244,7 @@ function probablePromiseToFAHooks(shim, mod, moduleName) {
|
|
|
243
244
|
const interceptedArgs = [arguments[0]];
|
|
244
245
|
shim.interceptedArgs = interceptedArgs;
|
|
245
246
|
const request = requestManager.getRequest(shim);
|
|
246
|
-
if (request && typeof arguments[0] === STRING) {
|
|
247
|
+
if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
|
|
247
248
|
const traceObject = secUtils.getTraceObject(shim);
|
|
248
249
|
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
|
|
249
250
|
this.secEvent = API.generateSecEvent(secMetadata);
|
|
@@ -268,7 +269,7 @@ function probablePromiseToFIHooks(shim, mod, moduleName) {
|
|
|
268
269
|
const interceptedArgs = [arguments[0]];
|
|
269
270
|
shim.interceptedArgs = interceptedArgs;
|
|
270
271
|
const request = requestManager.getRequest(shim);
|
|
271
|
-
if (request && typeof arguments[0] === STRING) {
|
|
272
|
+
if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
|
|
272
273
|
const traceObject = secUtils.getTraceObject(shim);
|
|
273
274
|
if (fun === COPY_FILE || fun === RENAME) {
|
|
274
275
|
const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
|
|
@@ -171,6 +171,19 @@ function wrapQueryable(shim, queryable, isPoolQuery) {
|
|
|
171
171
|
}
|
|
172
172
|
return true;
|
|
173
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Utility to check list contains only string
|
|
176
|
+
* @param {*} list
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
179
|
+
function containsOnlyStrings(list) {
|
|
180
|
+
for (let i = 0; i < list.length; i++) {
|
|
181
|
+
if (typeof list[i] !== 'string') {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
174
187
|
|
|
175
188
|
/**
|
|
176
189
|
* Utility to extract query from the intercepted args
|
|
@@ -199,6 +212,9 @@ function extractQueryArgs(shim, args) {
|
|
|
199
212
|
param = args[1];
|
|
200
213
|
param = Object.values(param)
|
|
201
214
|
}
|
|
215
|
+
if(!containsOnlyStrings(param)){
|
|
216
|
+
param = [];
|
|
217
|
+
}
|
|
202
218
|
return {
|
|
203
219
|
rawQuery: rawQuery,
|
|
204
220
|
param: param,
|
|
@@ -14,9 +14,11 @@ const Module = require('module');
|
|
|
14
14
|
const refDirRegEx = /^(\.\/)|^(\.\.\/)/g;
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const CSEC = 'nr-security-agent';
|
|
17
|
+
const FN_INSPECT = '@contrast/fn-inspect';
|
|
18
|
+
const NEWRELIC = 'newrelic';
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
|
-
* Entry point for
|
|
21
|
+
* Entry point for require module hook
|
|
20
22
|
* @param {*} shim
|
|
21
23
|
* @param {*} mod
|
|
22
24
|
* @param {*} moduleName
|
|
@@ -33,17 +35,22 @@ function initialize(shim) {
|
|
|
33
35
|
if (arguments[0].match(refDirRegEx) && arguments[1] && arguments[1].path) {
|
|
34
36
|
moduleName = path.resolve(arguments[1].path, arguments[0]);
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
|
|
37
39
|
let flag = false;
|
|
38
|
-
if (moduleName && typeof (moduleName) === STRING && !moduleName.includes(CSEC)) {
|
|
40
|
+
if (moduleName && typeof (moduleName) === STRING && !moduleName.includes(CSEC) && !moduleName.includes(FN_INSPECT)) {
|
|
39
41
|
flag = true;
|
|
40
42
|
}
|
|
41
|
-
else if (parent && parent.id && typeof (parent.id) === STRING && moduleName.includes(CSEC) && parent.id.includes(CSEC)) {
|
|
43
|
+
else if (parent && parent.id && typeof (parent.id) === STRING && moduleName.includes(CSEC) && parent.id.includes(CSEC) && moduleName.includes(FN_INSPECT)) {
|
|
42
44
|
flag = false;
|
|
43
45
|
}
|
|
44
|
-
else if (parent && parent.id && moduleName && typeof (moduleName) === STRING && moduleName.includes(CSEC) && parent.id.includes(CSEC)) {
|
|
46
|
+
else if (parent && parent.id && moduleName && typeof (moduleName) === STRING && moduleName.includes(CSEC) && parent.id.includes(CSEC) && moduleName.includes(FN_INSPECT)) {
|
|
45
47
|
flag = false;
|
|
46
48
|
}
|
|
49
|
+
|
|
50
|
+
if ((parent && parent.id && typeof (parent.id) === STRING && parent.id.includes(NEWRELIC)) || moduleName.includes(NEWRELIC)){
|
|
51
|
+
flag = false;
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
if (flag) {
|
|
48
55
|
requireHook(shim, moduleName, parent);
|
|
49
56
|
}
|
|
@@ -31,6 +31,7 @@ function AuthHeaders() {
|
|
|
31
31
|
authHeaders['NR-CSEC-BUILD-NUMBER'] = appInfo.buildNumber;
|
|
32
32
|
authHeaders['NR-ACCOUNT-ID'] = NRAgent.config.account_id;
|
|
33
33
|
authHeaders['NR-LICENSE-KEY'] = NRAgent ? NRAgent.config.license_key : '';
|
|
34
|
+
authHeaders["NR-CSEC-IAST-DATA-TRANSFER-MODE"] = "PULL";
|
|
34
35
|
} catch (error) {
|
|
35
36
|
logger.error("Exception in preparing auth headers:", error);
|
|
36
37
|
}
|
|
@@ -200,7 +200,7 @@ ApplicationInfo.prototype.updateSHAAndSize = async function updateSHAAndSize (Gl
|
|
|
200
200
|
applicationInfo.sha256 = await shaSizeUtil.getSHA256ForFile(process.argv[0]);
|
|
201
201
|
|
|
202
202
|
let stat = {};
|
|
203
|
-
if (
|
|
203
|
+
if (commonUtils.hasWorker()) {
|
|
204
204
|
stat = await getApplicationSHAAndSizeFromWorker(applicationInfo, application.deployedPath);
|
|
205
205
|
} else {
|
|
206
206
|
stat = await shaSizeUtil.getApplicationSHAAndSize(application.deployedPath, GlobalPromise);
|
|
@@ -55,8 +55,14 @@ function getKindIdPair (identifier, hostId) {
|
|
|
55
55
|
* @returns
|
|
56
56
|
*/
|
|
57
57
|
function hasWorker () {
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
let workerFlag = false;
|
|
59
|
+
try {
|
|
60
|
+
require('worker_threads');
|
|
61
|
+
workerFlag = true;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
workerFlag = false;
|
|
64
|
+
}
|
|
65
|
+
return workerFlag;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
function runtimeSupportsFunctionGenerators () {
|
|
@@ -136,13 +142,6 @@ function createDirectories () {
|
|
|
136
142
|
*/
|
|
137
143
|
async function initialSetup () {
|
|
138
144
|
try {
|
|
139
|
-
checkDiskSpace(`${CSEC_HOME}`).then((diskSpace) => {
|
|
140
|
-
if (diskSpace.free < 1000000000) {
|
|
141
|
-
logger.error(LOG_MESSAGES.AVAIL_DISK_ERROR, `${CSEC_HOME}`, prettyBytes(diskSpace.free));
|
|
142
|
-
process.exit();
|
|
143
|
-
}
|
|
144
|
-
}).catch(() => {
|
|
145
|
-
});
|
|
146
145
|
createDirectories();
|
|
147
146
|
} catch (err) {
|
|
148
147
|
logger.error(err);
|
|
@@ -329,7 +328,8 @@ function logRollOver () {
|
|
|
329
328
|
return;
|
|
330
329
|
}
|
|
331
330
|
} catch (error) {
|
|
332
|
-
logger.
|
|
331
|
+
logger.debug('Error in getting stats of log file:', error.message);
|
|
332
|
+
return;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
try {
|
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
const {
|
|
7
7
|
SecWebSocket,
|
|
8
8
|
WSCallbacks,
|
|
9
|
-
aquireWebSocket,
|
|
10
|
-
isWebSocketBusy,
|
|
11
|
-
releaseWebSocket,
|
|
12
9
|
setWebSocketConn
|
|
13
10
|
} = require('./websocket');
|
|
14
11
|
|
|
@@ -17,9 +14,6 @@ const ResponseHandler = require('./response');
|
|
|
17
14
|
module.exports = {
|
|
18
15
|
SecWebSocket,
|
|
19
16
|
WSCallbacks,
|
|
20
|
-
aquireWebSocket,
|
|
21
|
-
isWebSocketBusy,
|
|
22
|
-
releaseWebSocket,
|
|
23
17
|
setWebSocketConn,
|
|
24
18
|
ResponseHandler
|
|
25
19
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: New Relic Pre-Release
|
|
4
|
+
*/
|
|
5
|
+
const { Agent } = require('../../../agent');
|
|
6
|
+
let completedRequestIds = new Set();
|
|
7
|
+
const PolicyManager = require('../../../Policy');
|
|
8
|
+
let batchSize;
|
|
9
|
+
const batchthreshold = 300;
|
|
10
|
+
function getCompletedRequestIds() {
|
|
11
|
+
return [...completedRequestIds];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function addRequestId(id) {
|
|
15
|
+
completedRequestIds.add(id);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function removeRequestId(id) {
|
|
19
|
+
completedRequestIds.delete(id);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function generateIASTDataRequest() {
|
|
23
|
+
const policyInstance = PolicyManager.getInstance();
|
|
24
|
+
if (policyInstance.data && policyInstance.data.vulnerabilityScan && policyInstance.data.vulnerabilityScan.iastScan) {
|
|
25
|
+
batchSize = batchSize ? batchSize : policyInstance.data.vulnerabilityScan.iastScan.probing.batchSize;
|
|
26
|
+
if (batchSize > batchthreshold) {
|
|
27
|
+
batchSize = policyInstance.data.vulnerabilityScan.iastScan.probing.batchSize;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (getCompletedRequestIds().length >= batchSize || batchSize < batchthreshold) {
|
|
31
|
+
batchSize = (2 * batchSize);
|
|
32
|
+
}
|
|
33
|
+
let object = {};
|
|
34
|
+
object['jsonName'] = 'iast-data-request';
|
|
35
|
+
object['applicationUUID'] = Agent.getAgent().applicationInfo.applicationUUID;
|
|
36
|
+
object["batchSize"] = batchSize ? batchSize : 300;
|
|
37
|
+
object['completedRequestIds'] = getCompletedRequestIds();
|
|
38
|
+
return object;
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
getCompletedRequestIds,
|
|
45
|
+
addRequestId,
|
|
46
|
+
removeRequestId,
|
|
47
|
+
generateIASTDataRequest,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
package/lib/nr-security-agent/lib/core/connections/websocket/response/collector-policy-handler.js
CHANGED
|
@@ -11,7 +11,10 @@ module.exports.handler = function handler(json) {
|
|
|
11
11
|
const policyData = json.data;
|
|
12
12
|
const PolicyManager = require('../../../Policy');
|
|
13
13
|
PolicyManager.setPolicyData(policyData);
|
|
14
|
-
|
|
14
|
+
if (policyData.vulnerabilityScan.enabled && policyData.vulnerabilityScan.iastScan.enabled) {
|
|
15
|
+
require('./fuzz-request-handler').startIASTSchedular();
|
|
16
|
+
}
|
|
17
|
+
|
|
15
18
|
} catch (error) {
|
|
16
19
|
logger.error('paring exception while parsing configuration', error);
|
|
17
20
|
}
|
package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js
CHANGED
|
@@ -9,19 +9,19 @@ 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 { hasWorker: isWorkerSupported } = require('../../../commonUtils');
|
|
13
12
|
const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH } = require('../../../sec-agent-constants');
|
|
14
13
|
const LOCALHOST = 'localhost';
|
|
15
14
|
const COLON_SLASH_SLASH = '://';
|
|
16
15
|
const http = require('http');
|
|
17
16
|
const https = require('https');
|
|
17
|
+
const IASTUtil = require('./IASTUtils');
|
|
18
|
+
const CSEC_SEP = ':IAST:';
|
|
19
|
+
|
|
18
20
|
require('dns').setDefaultResultOrder('ipv4first')
|
|
19
21
|
|
|
20
22
|
const {
|
|
21
23
|
IS_LAMBDA_ENV,
|
|
22
|
-
AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER
|
|
23
|
-
CSEC_LAST_FUZZ_REQUEST_TIME
|
|
24
|
-
} = require('../../../sec-agent-constants');
|
|
24
|
+
AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER } = require('../../../sec-agent-constants');
|
|
25
25
|
const statusUtils = require('../../../statusUtils');
|
|
26
26
|
|
|
27
27
|
const find = `${SLASH}{{NR_CSEC_VALIDATOR_HOME_TMP}}`;
|
|
@@ -29,23 +29,71 @@ const CSEC_HOME_TMP_CONST = new RegExp(find, 'g');
|
|
|
29
29
|
|
|
30
30
|
let logger;
|
|
31
31
|
let lastFuzzEventTime = 0;
|
|
32
|
+
let iastIntervalConst;
|
|
33
|
+
let coolDownIntervalConst;
|
|
34
|
+
let additionalCoolDownTime = 0;
|
|
35
|
+
let fuzzedApiIDSet = new Set();
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* utility to start IAST Schedular
|
|
40
|
+
*/
|
|
41
|
+
function startIASTSchedular() {
|
|
42
|
+
Agent.getAgent().client.dispatcher(IASTUtil.generateIASTDataRequest());
|
|
43
|
+
if (iastIntervalConst) {
|
|
44
|
+
clearInterval(iastIntervalConst);
|
|
45
|
+
}
|
|
46
|
+
iastIntervalConst = setInterval(() => {
|
|
47
|
+
let data = IASTUtil.generateIASTDataRequest();
|
|
48
|
+
let currentTime = Date.now();
|
|
49
|
+
let completedListSize = IASTUtil.getCompletedRequestIds().length;
|
|
50
|
+
let timeDiffInSeconds = ((currentTime - lastFuzzEventTime) / 1000);
|
|
51
|
+
logger.trace("Time difference since last fuzz request:", timeDiffInSeconds);
|
|
52
|
+
logger.trace("Completed requests so far:", completedListSize);
|
|
53
|
+
|
|
54
|
+
if (timeDiffInSeconds > 5 && additionalCoolDownTime == 0) {
|
|
55
|
+
Agent.getAgent().client.dispatcher(data);
|
|
56
|
+
}
|
|
57
|
+
}, 5000);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Utility to log scanned apiId
|
|
62
|
+
* @param {*} fuzzHeader
|
|
63
|
+
*/
|
|
64
|
+
function logScannedApiId(fuzzHeader, requestURL) {
|
|
65
|
+
try {
|
|
66
|
+
let apiId = fuzzHeader.split(CSEC_SEP)[0]
|
|
67
|
+
if (apiId && !fuzzedApiIDSet.has(apiId) ) {
|
|
68
|
+
fuzzedApiIDSet.add(apiId);
|
|
69
|
+
logger.info("IAST Scan for API %s with ID : %s started.", requestURL, apiId);
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
32
76
|
|
|
33
77
|
// handler function to parse fuzz event and prepare fuzz request
|
|
34
|
-
function handler
|
|
35
|
-
|
|
78
|
+
function handler(json) {
|
|
79
|
+
setLastFuzzEventTime();
|
|
36
80
|
let rawFuzzRequest = json.arguments[0];
|
|
37
|
-
logger.debug('Fuzz request received::' + rawFuzzRequest);
|
|
38
81
|
rawFuzzRequest = rawFuzzRequest.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
|
|
39
82
|
let fuzzRequest;
|
|
40
83
|
try {
|
|
41
84
|
fuzzRequest = JSON.parse(rawFuzzRequest);
|
|
85
|
+
fuzzRequest['id'] = json.id;
|
|
86
|
+
let completedList = IASTUtil.getCompletedRequestIds();
|
|
87
|
+
if (completedList.includes(json.id)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
42
90
|
} catch (error) {
|
|
43
91
|
logger.error('Parsing exeception in fuzz request: ', error);
|
|
44
92
|
}
|
|
45
93
|
if (!fuzzRequest) {
|
|
46
94
|
return;
|
|
47
95
|
}
|
|
48
|
-
|
|
96
|
+
logger.debug('Fuzz request received::' + JSON.stringify(json));
|
|
49
97
|
logger.info('Fuzz request:: Method: ' + fuzzRequest.method);
|
|
50
98
|
logger.info('Fuzz request:: Headers: ' + stringify(fuzzRequest.headers));
|
|
51
99
|
logger.info('Fuzz request:: Data: ' + fuzzRequest.body);
|
|
@@ -61,7 +109,7 @@ function handler (json) {
|
|
|
61
109
|
*
|
|
62
110
|
* @param {Object} fuzzDetails
|
|
63
111
|
*/
|
|
64
|
-
function handleFuzzRequest
|
|
112
|
+
function handleFuzzRequest(fuzzDetails) {
|
|
65
113
|
const { fuzzRequest } = fuzzDetails;
|
|
66
114
|
try {
|
|
67
115
|
if (IS_LAMBDA_ENV) {
|
|
@@ -71,7 +119,7 @@ function handleFuzzRequest (fuzzDetails) {
|
|
|
71
119
|
const ver = process.env[AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER];
|
|
72
120
|
logger.info('Invoking Lambda:: ARN: ' + arn);
|
|
73
121
|
logger.info('Invoking Lambda:: Version: ' + ver);
|
|
74
|
-
|
|
122
|
+
|
|
75
123
|
} else {
|
|
76
124
|
const config = parseAxiosHttpRequestToFuzz(fuzzRequest);
|
|
77
125
|
const type = config.headers['content-type'];
|
|
@@ -81,10 +129,16 @@ function handleFuzzRequest (fuzzDetails) {
|
|
|
81
129
|
config.headers['content-type'] = splitted[0];
|
|
82
130
|
}
|
|
83
131
|
}
|
|
132
|
+
|
|
133
|
+
if(fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]){
|
|
134
|
+
logScannedApiId(fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID], fuzzRequest.requestURI)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
84
138
|
logger.info('Firing http request:: URL: ' + config.url);
|
|
85
139
|
|
|
86
|
-
const
|
|
87
|
-
handleFuzzResponse(
|
|
140
|
+
const response = restClient.fireRequest(config);
|
|
141
|
+
handleFuzzResponse(response, fuzzDetails);
|
|
88
142
|
}
|
|
89
143
|
} catch (err) {
|
|
90
144
|
statusUtils.addErrortoBuffer(err);
|
|
@@ -101,13 +155,14 @@ function handleFuzzRequest (fuzzDetails) {
|
|
|
101
155
|
/**
|
|
102
156
|
* Handles the fuzz response Promise.
|
|
103
157
|
*
|
|
104
|
-
* @param {Promise}
|
|
158
|
+
* @param {Promise} response
|
|
105
159
|
* @param {Object} fuzzDetails
|
|
106
160
|
*/
|
|
107
|
-
function handleFuzzResponse
|
|
161
|
+
function handleFuzzResponse(response, fuzzDetails) {
|
|
108
162
|
const { rawFuzzRequest, fuzzRequest } = fuzzDetails;
|
|
109
|
-
if (
|
|
110
|
-
|
|
163
|
+
if (response) {
|
|
164
|
+
IASTUtil.addRequestId(fuzzRequest.id);
|
|
165
|
+
response.then(() => {
|
|
111
166
|
logger.info('Fuzz success: ' + rawFuzzRequest);
|
|
112
167
|
}).catch(() => {
|
|
113
168
|
logger.debug('Error occured:', fuzzRequest.url, fuzzRequest);
|
|
@@ -121,11 +176,11 @@ function handleFuzzResponse (request, fuzzDetails) {
|
|
|
121
176
|
*
|
|
122
177
|
* @param {JSON} requestObject
|
|
123
178
|
*/
|
|
124
|
-
function parseAxiosHttpRequestToFuzz
|
|
179
|
+
function parseAxiosHttpRequestToFuzz(requestObject) {
|
|
125
180
|
let serverName = requestObject.serverName ? requestObject.serverName : LOCALHOST;
|
|
126
181
|
let host = serverName + COLON + requestObject.serverPort
|
|
127
182
|
return {
|
|
128
|
-
url: requestObject.protocol + COLON_SLASH_SLASH + host
|
|
183
|
+
url: requestObject.protocol + COLON_SLASH_SLASH + host + requestObject.url,
|
|
129
184
|
method: requestObject.method,
|
|
130
185
|
data: requestObject.body,
|
|
131
186
|
headers: requestObject.headers,
|
|
@@ -141,7 +196,7 @@ function parseAxiosHttpRequestToFuzz (requestObject) {
|
|
|
141
196
|
* set the handler logger
|
|
142
197
|
* @param {*} l
|
|
143
198
|
*/
|
|
144
|
-
function setLogger
|
|
199
|
+
function setLogger(l) {
|
|
145
200
|
logger = l;
|
|
146
201
|
};
|
|
147
202
|
|
|
@@ -149,25 +204,29 @@ function setLogger (l) {
|
|
|
149
204
|
* Sets the last fuzz event time epoch
|
|
150
205
|
* @param {Number} time
|
|
151
206
|
*/
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
process.env[CSEC_LAST_FUZZ_REQUEST_TIME] = time;
|
|
155
|
-
} else {
|
|
156
|
-
lastFuzzEventTime = time;
|
|
157
|
-
}
|
|
207
|
+
function setLastFuzzEventTime(time = Date.now()) {
|
|
208
|
+
lastFuzzEventTime = time;
|
|
158
209
|
}
|
|
159
210
|
|
|
211
|
+
|
|
160
212
|
/**
|
|
161
|
-
*
|
|
162
|
-
* @
|
|
213
|
+
* Sets the last fuzz event time epoch
|
|
214
|
+
* @param {Number} time
|
|
163
215
|
*/
|
|
164
|
-
function
|
|
165
|
-
|
|
216
|
+
function setAdditionalCoolDown(timeInSeconds) {
|
|
217
|
+
additionalCoolDownTime = timeInSeconds ? timeInSeconds : 10;
|
|
218
|
+
if (coolDownIntervalConst) {
|
|
219
|
+
clearTimeout(coolDownIntervalConst)
|
|
220
|
+
}
|
|
221
|
+
coolDownIntervalConst = setTimeout(() => {
|
|
222
|
+
additionalCoolDownTime = 0;
|
|
223
|
+
}, timeInSeconds * 1000);
|
|
166
224
|
}
|
|
167
225
|
|
|
168
226
|
module.exports = {
|
|
169
227
|
handler,
|
|
170
228
|
setLogger,
|
|
171
|
-
|
|
172
|
-
|
|
229
|
+
setLastFuzzEventTime,
|
|
230
|
+
startIASTSchedular,
|
|
231
|
+
setAdditionalCoolDown
|
|
173
232
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: New Relic Pre-Release
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
let logger;
|
|
7
|
+
|
|
8
|
+
module.exports.handler = function handler (json) {
|
|
9
|
+
logger.debug('IAST cool down command received:', JSON.stringify(json));
|
|
10
|
+
const fuzzRequestHandler = require('./fuzz-request-handler');
|
|
11
|
+
fuzzRequestHandler.setLastFuzzEventTime();
|
|
12
|
+
fuzzRequestHandler.setAdditionalCoolDown(json.data);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports.setLogger = l => { logger = l };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: New Relic Pre-Release
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
let logger;
|
|
7
|
+
const IASTUtil = require('./IASTUtils');
|
|
8
|
+
module.exports.handler = function handler (json) {
|
|
9
|
+
let deletionList = json.arguments;
|
|
10
|
+
let count = 0;
|
|
11
|
+
deletionList.forEach(element => {
|
|
12
|
+
count++;
|
|
13
|
+
IASTUtil.removeRequestId(element);
|
|
14
|
+
});
|
|
15
|
+
logger.debug("Purging confirmed %s IAST processed records: ", count, JSON.stringify(deletionList));
|
|
16
|
+
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
module.exports.setLogger = l => { logger = l };
|
|
@@ -13,6 +13,10 @@ const CollectorPolicyHandler = require('./collector-policy-handler');
|
|
|
13
13
|
const CollectorPolicyErrorHandler = require('./collecotor-policy-error-handler');
|
|
14
14
|
const NoOpHandler = require('./no-op-handler');
|
|
15
15
|
const WSReconnect = require('./ws-reconnect');
|
|
16
|
+
const coolDown = require('./iast-cool-down');
|
|
17
|
+
const recordDeletion = require('./iast-record-deletion');
|
|
18
|
+
const logs = require('../../../logging');
|
|
19
|
+
let logger = logs.getLogger();
|
|
16
20
|
|
|
17
21
|
const commandHandlerMap = {
|
|
18
22
|
[SERVER_COMMAND.STARTUP_WELCOME_MSG]: StartupHandshakeHandler,
|
|
@@ -20,15 +24,23 @@ const commandHandlerMap = {
|
|
|
20
24
|
[SERVER_COMMAND.FUZZ_REQUEST]: FuzzRequestHandler,
|
|
21
25
|
[SERVER_COMMAND.COLLECTOR_CONFIG]: CollectorPolicyHandler,
|
|
22
26
|
[SERVER_COMMAND.POLICY_ERROR]: CollectorPolicyErrorHandler,
|
|
23
|
-
[SERVER_COMMAND.OBEY_WS_RECONNECT]: WSReconnect
|
|
27
|
+
[SERVER_COMMAND.OBEY_WS_RECONNECT]: WSReconnect,
|
|
28
|
+
[SERVER_COMMAND.IAST_RECORD_DELETE_CONFIRMATION]:recordDeletion,
|
|
29
|
+
[SERVER_COMMAND.ENTER_IAST_COOLDOWN]:coolDown
|
|
24
30
|
};
|
|
25
31
|
|
|
26
32
|
const handle = function handle (json) {
|
|
27
|
-
|
|
28
|
-
commandHandlerMap[json.controlCommand]
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
try {
|
|
34
|
+
if (json && json.controlCommand && json.arguments && commandHandlerMap[json.controlCommand]) {
|
|
35
|
+
logger.debug("Incoming Control Command:", JSON.stringify(json));
|
|
36
|
+
commandHandlerMap[json.controlCommand].handler.call(this, json);
|
|
37
|
+
} else {
|
|
38
|
+
NoOpHandler.handler.call(this, json);
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error("Error in handling Control Commnad",error);
|
|
31
42
|
}
|
|
43
|
+
|
|
32
44
|
};
|
|
33
45
|
|
|
34
46
|
const setLogger = function setLogger (logger) {
|