@newrelic/security-agent 0.0.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 +0 -0
- package/CONTRIBUTING.md +23 -0
- package/LICENSE +43 -0
- package/README.md +98 -0
- package/THIRD_PARTY_NOTICES.md +2909 -0
- package/index.js +24 -0
- package/lib/instrumentation-security/core/constants.js +66 -0
- package/lib/instrumentation-security/core/event-constants.js +49 -0
- package/lib/instrumentation-security/core/request-manager.js +73 -0
- package/lib/instrumentation-security/core/route-manager.js +30 -0
- package/lib/instrumentation-security/core/sec-trace.js +134 -0
- package/lib/instrumentation-security/core/sec-utils.js +212 -0
- package/lib/instrumentation-security/core/security-metadata.js +34 -0
- package/lib/instrumentation-security/core/stack-trace.js +156 -0
- package/lib/instrumentation-security/hooks/director/nr-director.js +98 -0
- package/lib/instrumentation-security/hooks/express/nr-express.js +106 -0
- package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +155 -0
- package/lib/instrumentation-security/hooks/fs/nr-fs.js +391 -0
- package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +131 -0
- package/lib/instrumentation-security/hooks/http/nr-http.js +461 -0
- package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +110 -0
- package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +81 -0
- package/lib/instrumentation-security/hooks/mongodb/common.js +239 -0
- package/lib/instrumentation-security/hooks/mongodb/constants.js +91 -0
- package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +49 -0
- package/lib/instrumentation-security/hooks/mongodb/v2-mongo.js +91 -0
- package/lib/instrumentation-security/hooks/mongodb/v3-mongo.js +36 -0
- package/lib/instrumentation-security/hooks/mongodb/v4-mongo.js +17 -0
- package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +232 -0
- package/lib/instrumentation-security/hooks/native/nr-childProcess.js +131 -0
- package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +131 -0
- package/lib/instrumentation-security/hooks/require/nr-require.js +93 -0
- package/lib/instrumentation-security/hooks/restify/nr-restify.js +104 -0
- package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +91 -0
- package/lib/instrumentation-security/index.js +171 -0
- package/lib/nr-security-agent/index.js +170 -0
- package/lib/nr-security-agent/lib/core/Auth-headers.js +58 -0
- package/lib/nr-security-agent/lib/core/ContainerProperties.js +42 -0
- package/lib/nr-security-agent/lib/core/ECSProperties.js +34 -0
- package/lib/nr-security-agent/lib/core/FuzzFailEvent.js +27 -0
- package/lib/nr-security-agent/lib/core/LinkingMetadata.js +21 -0
- package/lib/nr-security-agent/lib/core/LogMessage.js +47 -0
- package/lib/nr-security-agent/lib/core/PodProperties.js +36 -0
- package/lib/nr-security-agent/lib/core/Policy.js +105 -0
- package/lib/nr-security-agent/lib/core/agent-status.js +56 -0
- package/lib/nr-security-agent/lib/core/agent.js +146 -0
- package/lib/nr-security-agent/lib/core/applicationinfo.js +634 -0
- package/lib/nr-security-agent/lib/core/commonUtils.js +411 -0
- package/lib/nr-security-agent/lib/core/connections/aws-lambda-client.js +55 -0
- package/lib/nr-security-agent/lib/core/connections/restclient.js +20 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/index.js +25 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/collecotor-policy-error-handler.js +18 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/collector-policy-handler.js +20 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +171 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +47 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/no-op-handler.js +12 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +27 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/unsupported-agent-handler.js +18 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/ws-reconnect.js +14 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +443 -0
- package/lib/nr-security-agent/lib/core/event.js +186 -0
- package/lib/nr-security-agent/lib/core/health-check.js +159 -0
- package/lib/nr-security-agent/lib/core/json-file-handler.js +66 -0
- package/lib/nr-security-agent/lib/core/jsonValidator.js +149 -0
- package/lib/nr-security-agent/lib/core/lambda-delegated-task-service.js +90 -0
- package/lib/nr-security-agent/lib/core/logging.js +176 -0
- package/lib/nr-security-agent/lib/core/sec-agent-constants.js +173 -0
- package/lib/nr-security-agent/lib/core/sec-util.js +166 -0
- package/lib/nr-security-agent/lib/core/sha-size-util.js +338 -0
- package/lib/nr-security-agent/lib/core/sha_worker.js +24 -0
- package/lib/nr-security-agent/lib/core/startup-config.js +29 -0
- package/lib/nr-security-agent/lib/core/statusUtils.js +157 -0
- package/lib/nr-security-agent/lib/core/timeunit.js +44 -0
- package/lib/nr-security-agent/lib/core/websocket-client.js +335 -0
- package/lib/nr-security-agent/lib/core/worker-entities.js +82 -0
- package/lib/nr-security-agent/lib/core/worker.js +148 -0
- package/lib/nr-security-agent/lib/core/xss-utils.js +297 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/constants.js +12 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/index.js +15 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-sha-calculation.js +37 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-base.js +50 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-type.js +17 -0
- package/lib/nr-security-agent/resources/cert.pem +31 -0
- package/lib/nr-security-agent/resources/config.js +19 -0
- package/lib/nr-security-agent/resources/default-policy.yaml +36 -0
- package/lib/nr-security-api/index.js +111 -0
- package/package.json +95 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Access the workerData by requiring it.
|
|
7
|
+
const { parentPort, workerData } = require('worker_threads');
|
|
8
|
+
// we need to initialize logger first hand
|
|
9
|
+
const logs = require('./logging');
|
|
10
|
+
const { applicationInfo } = workerData;
|
|
11
|
+
const logger = logs.getLogger(applicationInfo.applicationUUID);
|
|
12
|
+
|
|
13
|
+
const { SecWebSocket, WSCallbacks, releaseWebSocket } = require('./connections/websocket');
|
|
14
|
+
const {
|
|
15
|
+
SetDefaultLogLevelHandler,
|
|
16
|
+
FuzzRequestHandler,
|
|
17
|
+
setLogger: setResponseHandlerLogger
|
|
18
|
+
} = require('./connections/websocket/response');
|
|
19
|
+
const { SERVER_COMMAND } = require('./sec-agent-constants');
|
|
20
|
+
const { WorkerMessage, WORKER_STATUS } = require('./worker-entities');
|
|
21
|
+
const { Agent } = require('./agent');
|
|
22
|
+
const statusUtils = require('./statusUtils');
|
|
23
|
+
const commonUtils = require('./commonUtils');
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const MESSAGE = 'message';
|
|
27
|
+
|
|
28
|
+
const flushInitiatedMsg = new WorkerMessage(WorkerMessage.TYPE.FLUSH, { status: 'initiated' });
|
|
29
|
+
const flushFinishedMsg = new WorkerMessage(WorkerMessage.TYPE.FLUSH, { status: 'flushed' });
|
|
30
|
+
|
|
31
|
+
let connection;
|
|
32
|
+
// we would want to provide a custome on data
|
|
33
|
+
// handler as the some control comands might
|
|
34
|
+
// need change in current thread, and main thread
|
|
35
|
+
// as well.
|
|
36
|
+
const webSocketCallbacks = new WSCallbacks(null, function (data) {
|
|
37
|
+
try {
|
|
38
|
+
const json = JSON.parse(data);
|
|
39
|
+
if (!json.controlCommand) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// we need some updates in worker threads also
|
|
43
|
+
switch (json.controlCommand) {
|
|
44
|
+
case SERVER_COMMAND.SET_DEFAULT_LOG_LEVEL:
|
|
45
|
+
SetDefaultLogLevelHandler(json);
|
|
46
|
+
break;
|
|
47
|
+
case SERVER_COMMAND.FUZZ_REQUEST:
|
|
48
|
+
FuzzRequestHandler.handler(json);
|
|
49
|
+
// we would not want this to be duplicated
|
|
50
|
+
// in main thread, so we would return from here
|
|
51
|
+
return;
|
|
52
|
+
default:
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// send the message to parent in non-blocking mode
|
|
56
|
+
setImmediate(function () {
|
|
57
|
+
parentPort.postMessage(new WorkerMessage(WorkerMessage.TYPE.MESSAGE, json));
|
|
58
|
+
});
|
|
59
|
+
} catch (err) {
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
}, null);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* This would initiate the worker and bootstrap
|
|
66
|
+
* all the necessary configurations.
|
|
67
|
+
*/
|
|
68
|
+
function initWorker () {
|
|
69
|
+
Agent.init(applicationInfo, logger, null, null);
|
|
70
|
+
setResponseHandlerLogger(logger);
|
|
71
|
+
logger.info('Creating websocket connection in worker thread');
|
|
72
|
+
const validatorService = commonUtils.getValidatorServiceEndpointURL();
|
|
73
|
+
connection = new SecWebSocket(validatorService, webSocketCallbacks, logger);
|
|
74
|
+
Agent.getAgent().setClient(connection);
|
|
75
|
+
// Main thread will pass the data you need
|
|
76
|
+
// through this event listener.
|
|
77
|
+
parentPort.on(MESSAGE, handleMessage);
|
|
78
|
+
// we would set the worker as initialized here
|
|
79
|
+
process.env.WS_WORKER_STATUS = WORKER_STATUS.RUNNING;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Makes the event queue flush
|
|
84
|
+
* when the websocket is initialized in
|
|
85
|
+
* worker, using the connection instance.
|
|
86
|
+
* This also triggers events to main to mark
|
|
87
|
+
* the flushing initiated or complete
|
|
88
|
+
*/
|
|
89
|
+
async function flushEventQueueFromWorker () {
|
|
90
|
+
parentPort.postMessage(flushInitiatedMsg);
|
|
91
|
+
await connection.flushEventQueue();
|
|
92
|
+
parentPort.postMessage(flushFinishedMsg);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* This handles any messages received from main
|
|
97
|
+
* if websocket worker is initialized
|
|
98
|
+
*
|
|
99
|
+
* @param {JSON} msg
|
|
100
|
+
*/
|
|
101
|
+
function handleMessage (msg) {
|
|
102
|
+
const workerMsg = WorkerMessage.parseFromObject(msg);
|
|
103
|
+
if (logger.level && (logger.level.levelStr !== process.env.loglevel)) {
|
|
104
|
+
logger.level = process.env.loglevel;
|
|
105
|
+
}
|
|
106
|
+
switch (workerMsg.getType()) {
|
|
107
|
+
case WorkerMessage.TYPE.MESSAGE: {
|
|
108
|
+
connection.dispatch(workerMsg.getPayload());
|
|
109
|
+
releaseWebSocket();
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case WorkerMessage.TYPE.FLUSH: {
|
|
113
|
+
flushEventQueueFromWorker();
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case WorkerMessage.TYPE.UPDATE_APPLICATION_INFO:
|
|
117
|
+
Agent.getAgent().setApplicationInfo(workerMsg.getPayload());
|
|
118
|
+
break;
|
|
119
|
+
case WorkerMessage.TYPE.RECONNECT: {
|
|
120
|
+
connection.reconnect();
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case WorkerMessage.TYPE.OBEY_RECONNECT: {
|
|
124
|
+
connection.obeyReconnect();
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
default:
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
setInterval(() => {
|
|
132
|
+
try {
|
|
133
|
+
const buffErrors = statusUtils.getRingBufferedErrors();
|
|
134
|
+
while (!buffErrors.isEmpty()) {
|
|
135
|
+
const err = buffErrors.deq();
|
|
136
|
+
parentPort.postMessage(new WorkerMessage(WorkerMessage.TYPE.ERROR, err));
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
}
|
|
140
|
+
}, 240000);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* This would initiate the worker when the
|
|
144
|
+
* module is loaded.
|
|
145
|
+
*/
|
|
146
|
+
(() => {
|
|
147
|
+
initWorker();
|
|
148
|
+
})();
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const _ = require('lodash');
|
|
7
|
+
const { AllHtmlEntities } = require('html-entities');
|
|
8
|
+
const decode = require('unescape');
|
|
9
|
+
const querystring = require('querystring');
|
|
10
|
+
|
|
11
|
+
const { Agent } = require('./agent');
|
|
12
|
+
|
|
13
|
+
const entities = new AllHtmlEntities();
|
|
14
|
+
|
|
15
|
+
const tagNameRegex = /<([a-zA-Z_-]+[0-9]*|!--)/gmi;
|
|
16
|
+
const attribRegex = /([^(/\s<'">)]+?)(?:\s*)=\s*(('|")([\s\S]*?)(?:(?=(\\?))\5.)*?\3|.+?(?=\/>|>|\?>|\s|<\/|$))/gmi;
|
|
17
|
+
const ANGLE_END = '>';
|
|
18
|
+
const HTML_COMMENT_END = '-->';
|
|
19
|
+
const HTML_COMMENT_START = '!--';
|
|
20
|
+
const EQUALS = '=';
|
|
21
|
+
const SCRIPT_END = '</SCRIPT';
|
|
22
|
+
const ANGLE_END_CHAR = '>';
|
|
23
|
+
const FIVE_COLON = ':::::';
|
|
24
|
+
const EMPTY_STRING = '';
|
|
25
|
+
const ANGLE_START = '<';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* returns detected xss construct if any
|
|
29
|
+
*
|
|
30
|
+
* @param {ContextEntity} csecRequest
|
|
31
|
+
* @param {Object} responseBody
|
|
32
|
+
* @param {Object} responseHeaders
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
function checkForReflectedXSS (csecRequest, responseBody, responseHeaders) {
|
|
36
|
+
const combinedRequestData = decodeRequestData(csecRequest);
|
|
37
|
+
const combinedResponseData = decodeResponseData(responseBody, responseHeaders);
|
|
38
|
+
|
|
39
|
+
const combinedResponseDataString = [...combinedResponseData].join(FIVE_COLON);
|
|
40
|
+
let attackConstructs = isXSS(combinedRequestData);
|
|
41
|
+
|
|
42
|
+
// Agent.getAgent().logger.debug('attackConstructs:', attackConstructs);
|
|
43
|
+
attackConstructs = Array.from(attackConstructs);
|
|
44
|
+
for (let i = 0; i < attackConstructs.length; i++) {
|
|
45
|
+
if (combinedResponseDataString.toLowerCase().includes(attackConstructs[i].toLowerCase())) {
|
|
46
|
+
Agent.getAgent().logger.debug('construct found::', attackConstructs[i]);
|
|
47
|
+
return attackConstructs[i];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return EMPTY_STRING;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function decodeRequestData (csecRequest) {
|
|
54
|
+
const processedData = new Set();
|
|
55
|
+
if (csecRequest) {
|
|
56
|
+
const body = csecRequest.body;
|
|
57
|
+
const processedUrl = csecRequest.url;
|
|
58
|
+
let processedBody = body;
|
|
59
|
+
try {
|
|
60
|
+
for (const [key, value] of Object.entries(csecRequest.headers)) {
|
|
61
|
+
// For key
|
|
62
|
+
processURLEncodedDataForXSS(processedData, key);
|
|
63
|
+
|
|
64
|
+
// For Value
|
|
65
|
+
processURLEncodedDataForXSS(processedData, value);
|
|
66
|
+
}
|
|
67
|
+
if (csecRequest.parameterMap) {
|
|
68
|
+
for (const [key, value] of Object.entries(csecRequest.parameterMap)) {
|
|
69
|
+
if (key) {
|
|
70
|
+
processURLEncodedDataForXSS(processedData, key.toString());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (value) {
|
|
74
|
+
processURLEncodedDataForXSS(processedData, value.toString());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const querystringObj = querystring.decode(processedUrl);
|
|
79
|
+
if (querystringObj) {
|
|
80
|
+
for (const [key, value] of Object.entries(querystringObj)) {
|
|
81
|
+
if (key) {
|
|
82
|
+
processURLEncodedDataForXSS(processedData, key.toString());
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (value) {
|
|
86
|
+
processURLEncodedDataForXSS(processedData, value.toString());
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// For URL
|
|
92
|
+
processURLEncodedDataForXSS(processedData, processedUrl);
|
|
93
|
+
|
|
94
|
+
// For Body
|
|
95
|
+
|
|
96
|
+
if (processedBody) {
|
|
97
|
+
try {
|
|
98
|
+
const bodyObject = querystring.decode(processedBody);
|
|
99
|
+
if (bodyObject) {
|
|
100
|
+
for (const [k, v] of Object.entries(bodyObject)) {
|
|
101
|
+
processedData.add(v);
|
|
102
|
+
processedData.add(k);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
processedData.add(processedBody);
|
|
109
|
+
|
|
110
|
+
const oldProcessedBody = processedBody;
|
|
111
|
+
|
|
112
|
+
processedBody = safeDecode(processedBody);
|
|
113
|
+
if (processedBody !== oldProcessedBody && processedBody.includes(ANGLE_START)) {
|
|
114
|
+
processedData.add(processedBody);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
Agent.getAgent().logger.error('Exception occured while decoding request:', err);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return processedData;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function decodeResponseData (responseBody, responseHeaders) {
|
|
126
|
+
const processedData = new Set();
|
|
127
|
+
const processedBody = responseBody || '';
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
processedData.add(processedBody);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
Agent.getAgent().logger.error('Exception occured while decoding response:', err);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return processedData;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isXSS (combinedData) {
|
|
139
|
+
const attackConstructs = new Set();
|
|
140
|
+
for (const data of combinedData) {
|
|
141
|
+
const constructs = getXSSConstructs(data);
|
|
142
|
+
constructs.forEach(attackConstructs.add, attackConstructs);
|
|
143
|
+
}
|
|
144
|
+
return attackConstructs;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getXSSConstructs (data) {
|
|
148
|
+
const constructs = new Set();
|
|
149
|
+
let isAttackConstruct = false;
|
|
150
|
+
let currPos = 0;
|
|
151
|
+
let startPos = 0;
|
|
152
|
+
let tmpCurrPos = 0;
|
|
153
|
+
let tmpStartPos = 0;
|
|
154
|
+
let matcher;
|
|
155
|
+
let attribMatcher;
|
|
156
|
+
while (currPos < data.length) {
|
|
157
|
+
let tagName;
|
|
158
|
+
|
|
159
|
+
matcher = tagNameRegex.exec(data);
|
|
160
|
+
if (!matcher) {
|
|
161
|
+
return constructs;
|
|
162
|
+
}
|
|
163
|
+
if ((matcher) !== null) {
|
|
164
|
+
// This is necessary to avoid infinite loops with zero-width matches
|
|
165
|
+
if (matcher.index === tagNameRegex.lastIndex) {
|
|
166
|
+
tagNameRegex.lastIndex++;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
tagName = matcher[1];
|
|
170
|
+
|
|
171
|
+
tagNameRegex.lastIndex = currPos;
|
|
172
|
+
if (!tagNameRegex.test(data)) {
|
|
173
|
+
return constructs;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (_.isEmpty(tagName)) {
|
|
177
|
+
return constructs;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
startPos = matcher.index;
|
|
181
|
+
currPos = tagNameRegex.lastIndex - 1;
|
|
182
|
+
|
|
183
|
+
if (HTML_COMMENT_START === tagName) {
|
|
184
|
+
tmpCurrPos = data.indexOf(HTML_COMMENT_END, startPos);
|
|
185
|
+
if (tmpCurrPos === -1) {
|
|
186
|
+
break;
|
|
187
|
+
} else {
|
|
188
|
+
currPos = tmpCurrPos;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
tmpStartPos = tmpCurrPos = data.indexOf(ANGLE_END, startPos);
|
|
193
|
+
if (tmpCurrPos === -1) {
|
|
194
|
+
tmpStartPos = startPos;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
attribRegex.lastIndex = currPos;
|
|
198
|
+
while ((attribMatcher = attribRegex.exec(data)) !== null) {
|
|
199
|
+
// This is necessary to avoid infinite loops with zero-width matches
|
|
200
|
+
if (attribMatcher.index === attribRegex.lastIndex) {
|
|
201
|
+
attribRegex.lastIndex++;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let attribData = _.trim(attribMatcher[0]);
|
|
205
|
+
|
|
206
|
+
currPos = attribRegex.lastIndex - 1;
|
|
207
|
+
tmpCurrPos = data.indexOf(ANGLE_END, tmpStartPos);
|
|
208
|
+
|
|
209
|
+
if ((tmpCurrPos === -1 || attribMatcher.index < tmpCurrPos)) {
|
|
210
|
+
tmpStartPos = tmpCurrPos = attribRegex.lastIndex - 1;
|
|
211
|
+
tmpStartPos++;
|
|
212
|
+
if (_.isEmpty(attribMatcher[3]) && attribRegex.lastIndex >= tmpCurrPos) {
|
|
213
|
+
tmpStartPos = tmpCurrPos = data.indexOf(ANGLE_END, attribMatcher.index);
|
|
214
|
+
if (tmpStartPos > -1) {
|
|
215
|
+
attribData = attribData.substring(0, tmpStartPos);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const key = attribData.split(EQUALS)[0];
|
|
219
|
+
const val = attribData.split(EQUALS)[1];
|
|
220
|
+
const upperCaseAttribKey = key.toUpperCase();
|
|
221
|
+
|
|
222
|
+
if (!_.isEmpty(key) && (upperCaseAttribKey.startsWith('ON') || upperCaseAttribKey === 'SRC' || upperCaseAttribKey === 'HREF' ||
|
|
223
|
+
upperCaseAttribKey === 'ACTION' || upperCaseAttribKey === 'FORMACTION' || upperCaseAttribKey === 'SRCDOC' ||
|
|
224
|
+
upperCaseAttribKey === 'DATA' || decode(val).toUpperCase().replace(/\s/g, '').includes('JAVASCRIPT:') ||
|
|
225
|
+
entities.decode(val).toUpperCase().replace(/\s/g, '').includes('JAVASCRIPT:'))) {
|
|
226
|
+
isAttackConstruct = true;
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (tmpCurrPos > 0) {
|
|
234
|
+
currPos = tmpCurrPos;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (data.charAt(currPos) !== ANGLE_END_CHAR) {
|
|
238
|
+
const tmp = data.indexOf(ANGLE_END, currPos);
|
|
239
|
+
if (tmp !== -1) {
|
|
240
|
+
currPos = tmp;
|
|
241
|
+
} else if (!isAttackConstruct) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (tagName && tagName.toUpperCase().trim() === 'SCRIPT') {
|
|
246
|
+
const locationOfEndTag = data.toUpperCase().indexOf(SCRIPT_END, currPos);
|
|
247
|
+
if (locationOfEndTag !== -1) {
|
|
248
|
+
const body = data.substring(currPos + 1, locationOfEndTag);
|
|
249
|
+
if (body) {
|
|
250
|
+
constructs.add(data.substring(startPos, currPos + 1) + body);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
let body = data.substring(currPos + 1);
|
|
255
|
+
const tagEnd = body.indexOf(ANGLE_END);
|
|
256
|
+
|
|
257
|
+
if (!_.isEmpty(body) && tagEnd !== -1) {
|
|
258
|
+
body = data.substring(tagEnd);
|
|
259
|
+
constructs.add(data.substring(startPos, currPos + 1) + body);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (isAttackConstruct) {
|
|
266
|
+
constructs.add(data.substring(startPos, currPos + 1));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return constructs;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function processURLEncodedDataForXSS (processedData, data) {
|
|
274
|
+
let key = data;
|
|
275
|
+
let oldKey = String(key);
|
|
276
|
+
do {
|
|
277
|
+
if (key.includes(ANGLE_START)) {
|
|
278
|
+
processedData.add(key);
|
|
279
|
+
}
|
|
280
|
+
oldKey = key;
|
|
281
|
+
key = safeDecode(key);
|
|
282
|
+
} while (oldKey !== key);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function safeDecode (data) {
|
|
286
|
+
let decodedData = EMPTY_STRING;
|
|
287
|
+
try {
|
|
288
|
+
decodedData = decodeURIComponent(data);
|
|
289
|
+
} catch (err) {
|
|
290
|
+
decodedData = data;
|
|
291
|
+
}
|
|
292
|
+
return decodedData;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = {
|
|
296
|
+
checkForReflectedXSS
|
|
297
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const CSEC_CALCULATING_APPLICATION_SHA = 'CSEC_CALCULATING_APPLICATION_SHA';
|
|
7
|
+
const CSEC_TASK_PAYLOAD_IDENTIFIER = 'CSEC_TASK_PAYLOAD';
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
CSEC_TASK_PAYLOAD_IDENTIFIER,
|
|
11
|
+
CSEC_CALCULATING_APPLICATION_SHA
|
|
12
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { LambdaDelegatedTaskType } = require('./lambda-delegated-task-type');
|
|
7
|
+
const { LambdaDelegatedTaskBase } = require('./lambda-delegated-task-base');
|
|
8
|
+
const { LambdaDelegatedTaskShaCalculation } = require('./lambda-delegated-sha-calculation');
|
|
9
|
+
const CONST = require('./constants');
|
|
10
|
+
module.exports = {
|
|
11
|
+
LambdaDelegatedTaskType,
|
|
12
|
+
LambdaDelegatedTaskBase,
|
|
13
|
+
LambdaDelegatedTaskShaCalculation,
|
|
14
|
+
CONST
|
|
15
|
+
};
|
package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-sha-calculation.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { LambdaDelegatedTaskBase } = require('./lambda-delegated-task-base');
|
|
7
|
+
const ApplicationInfo = require('../../core/applicationinfo');
|
|
8
|
+
const { Agent } = require('../../core/agent');
|
|
9
|
+
const shaSizeUtil = require('../../core/sha-size-util');
|
|
10
|
+
const { CSEC_CALCULATING_APPLICATION_SHA } = require('./constants');
|
|
11
|
+
function LambdaDelegatedTaskShaCalculation (type, ...args) {
|
|
12
|
+
LambdaDelegatedTaskBase.call(this, type, ...args);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
LambdaDelegatedTaskShaCalculation.prototype = LambdaDelegatedTaskBase.prototype;
|
|
16
|
+
LambdaDelegatedTaskShaCalculation.prototype.constructor = LambdaDelegatedTaskShaCalculation;
|
|
17
|
+
|
|
18
|
+
LambdaDelegatedTaskShaCalculation.prototype.handle = function handleShaCalculation () {
|
|
19
|
+
process.env[CSEC_CALCULATING_APPLICATION_SHA] = true;
|
|
20
|
+
const applicationInfo = ApplicationInfo.getInstance();
|
|
21
|
+
const deployedApplications = applicationInfo && applicationInfo.serverInfo && applicationInfo.serverInfo.deployedApplications;
|
|
22
|
+
|
|
23
|
+
// if there are no applications detected
|
|
24
|
+
if (!deployedApplications) return;
|
|
25
|
+
|
|
26
|
+
// calculate and add sha and size info
|
|
27
|
+
const [application] = deployedApplications;
|
|
28
|
+
const stat = shaSizeUtil.getApplicationSHAAndSizeSync(application.deployedPath, /.*\/(node_modules)\/.*/);
|
|
29
|
+
application.sha256 = stat.sha256;
|
|
30
|
+
application.size = stat.size;
|
|
31
|
+
|
|
32
|
+
Agent.getAgent().client.dispatcher(applicationInfo);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
LambdaDelegatedTaskShaCalculation
|
|
37
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line no-unused-vars
|
|
7
|
+
const { LambdaDelegatedTaskType } = require('./lambda-delegated-task-type');
|
|
8
|
+
const { CSEC_TASK_PAYLOAD_IDENTIFIER } = require('./constants');
|
|
9
|
+
|
|
10
|
+
function LambdaDelegatedTaskBase (type, ...args) {
|
|
11
|
+
this.type = type;
|
|
12
|
+
this.args = args;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
LambdaDelegatedTaskBase.prototype = Object.create(null);
|
|
16
|
+
LambdaDelegatedTaskBase.prototype.constructor = LambdaDelegatedTaskBase;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns type of delegated task
|
|
20
|
+
* @returns {LambdaDelegatedTaskType} type
|
|
21
|
+
*/
|
|
22
|
+
LambdaDelegatedTaskBase.prototype.getType = function getType () {
|
|
23
|
+
return this.type;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns delegated task args
|
|
28
|
+
* @returns {Array} args
|
|
29
|
+
*/
|
|
30
|
+
LambdaDelegatedTaskBase.prototype.getArgs = function getArgs () {
|
|
31
|
+
return this.args;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Adds task delegation details to payload
|
|
36
|
+
* @param {JSON} payload
|
|
37
|
+
*/
|
|
38
|
+
LambdaDelegatedTaskBase.prototype.addToPayload = function addToPayload (payload = {}) {
|
|
39
|
+
payload[CSEC_TASK_PAYLOAD_IDENTIFIER] = {
|
|
40
|
+
type: this.type,
|
|
41
|
+
args: this.args
|
|
42
|
+
};
|
|
43
|
+
return payload;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
LambdaDelegatedTaskBase.prototype.handle = function noop () {};
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
LambdaDelegatedTaskBase
|
|
50
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const SHA_CALCULATION = 'sha_calculation';
|
|
7
|
+
|
|
8
|
+
function LambdaDelegatedTaskType () {}
|
|
9
|
+
|
|
10
|
+
LambdaDelegatedTaskType.prototype = Object.create(null);
|
|
11
|
+
LambdaDelegatedTaskType.prototype.constructor = LambdaDelegatedTaskType;
|
|
12
|
+
|
|
13
|
+
LambdaDelegatedTaskType.SHA_CALCULATION = SHA_CALCULATION;
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
LambdaDelegatedTaskType
|
|
17
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
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
|
|
31
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const AGENT_DIR = path.join(__dirname, '../../../');
|
|
8
|
+
const uuidv4 = require('uuid/v4');
|
|
9
|
+
process.env.applicationUUID = process.env.applicationUUID || uuidv4();
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
AGENT_DIR,
|
|
13
|
+
ENV: process.env.CSEC_ENV,
|
|
14
|
+
APPLICATION_INFO_PUBLISH_MAX_WAIT: 10000, // milliseconds
|
|
15
|
+
HC_INTERVAL_MS: 300000, // milliseconds
|
|
16
|
+
MAX_PENDING_QUEUE_LENGTH: 1000,
|
|
17
|
+
MAX_EVENT_RATE: 1200, // EVENTS/sec
|
|
18
|
+
DEFAULT_LOG_CHANGETIME: 300000 // milliseconds
|
|
19
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
version: "DEFAULT"
|
|
2
|
+
logLevel: "INFO"
|
|
3
|
+
policyPull: true
|
|
4
|
+
policyPullInterval: 60
|
|
5
|
+
vulnerabilityScan:
|
|
6
|
+
enabled: false
|
|
7
|
+
cveScan:
|
|
8
|
+
enabled: false
|
|
9
|
+
enableEnvScan: false
|
|
10
|
+
cveDefinitionUpdateInterval: 360
|
|
11
|
+
iastScan:
|
|
12
|
+
enabled: false
|
|
13
|
+
probing:
|
|
14
|
+
interval: 1
|
|
15
|
+
batchSize: 17
|
|
16
|
+
protectionMode:
|
|
17
|
+
enabled: false
|
|
18
|
+
ipBlocking:
|
|
19
|
+
enabled: false
|
|
20
|
+
attackerIpBlocking: false
|
|
21
|
+
ipDetectViaXFF: false
|
|
22
|
+
timeout: 2
|
|
23
|
+
apiBlocking:
|
|
24
|
+
enabled: false
|
|
25
|
+
protectAllApis: false
|
|
26
|
+
protectKnownVulnerableApis: false
|
|
27
|
+
protectAttackedApis: false
|
|
28
|
+
sendCompleteStackTrace: false
|
|
29
|
+
enableHTTPRequestPrinting: false
|
|
30
|
+
policyParameters:
|
|
31
|
+
allowedIps: []
|
|
32
|
+
blockedIps: []
|
|
33
|
+
allowedApis: []
|
|
34
|
+
blockedApis: []
|
|
35
|
+
allowedRequests: []
|
|
36
|
+
|