@newrelic/security-agent
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -0,1 +1,13 @@ | ||
### v1.3.0 (2024-05-31) | ||
#### Features | ||
* Added route field in security event for API endpoint mapping | ||
#### Bug fixes | ||
* Fix for control commands acknowledgement in security agent | ||
* Added assert for typeof response data in Reflected XSS validation | ||
* Updated @grpc/grpc-js instrumentation to instrument submodules | ||
* Handling to convert header values into string | ||
#### Miscellaneous chores | ||
* Updated log level for critical messages | ||
* Readme update | ||
* (deps-dev): bump axios from 0.21.4 to 1.7.2 | ||
@@ -2,0 +14,0 @@ ### v1.2.0 (2024-04-12) |
@@ -18,4 +18,14 @@ /* | ||
const securityMetadata = {}; | ||
const httpRequest = Object.assign({},request); | ||
if(httpRequest.tempFiles){ | ||
const rHeaders = request.headers; | ||
try { | ||
Object.keys(rHeaders).forEach(key => { | ||
if (rHeaders[key]) { | ||
rHeaders[key] = String(rHeaders[key]); | ||
} | ||
}); | ||
} catch (error) { | ||
} | ||
request.headers = rHeaders; | ||
const httpRequest = Object.assign({}, request); | ||
if (httpRequest.tempFiles) { | ||
delete httpRequest.tempFiles; | ||
@@ -22,0 +32,0 @@ } |
@@ -141,5 +141,5 @@ /* | ||
if (parameters[0].startsWith(DOTDOTSLASH)) { | ||
parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + SLASH + parameters[0]; | ||
parameters[0] = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath + SLASH + parameters[0]; | ||
} else if (parameters[0].startsWith(SLASHDOTDOT)) { | ||
parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + parameters[0]; | ||
parameters[0] = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath + parameters[0]; | ||
} else { | ||
@@ -351,3 +351,3 @@ parameters[0] = path.resolve(parameters[0]); | ||
filePath = path.resolve(filePath); | ||
const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath; | ||
const appPath = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath; | ||
const isInPath = isPathInside(filePath, appPath); | ||
@@ -369,3 +369,3 @@ if (typeof filePath === STRING && isInPath) { | ||
filePath = path.resolve(filePath); | ||
const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath; | ||
const appPath = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath; | ||
const isInPath = isPathInside(filePath, appPath); | ||
@@ -372,0 +372,0 @@ if (typeof filePath === STRING && isInPath) { |
@@ -202,8 +202,37 @@ /* | ||
newrelic.instrument({ | ||
moduleName: '@grpc/grpc-js/build/src/server', | ||
isEsm: true, | ||
onRequire: require('./hooks/grpc-js/nr-grpc').wrapServer, | ||
onError: function intrumentErrorHandler(err) { | ||
logger.error(err.message, err.stack) | ||
} | ||
}) | ||
newrelic.instrument({ | ||
moduleName: '@grpc/grpc-js/build/src/make-client', | ||
isEsm: true, | ||
onRequire: require('./hooks/grpc-js/nr-grpc').wrapMakeClient, | ||
onError: function intrumentErrorHandler(err) { | ||
logger.error(err.message, err.stack) | ||
} | ||
}) | ||
newrelic.instrument({ | ||
moduleName: '@grpc/grpc-js/build/src/resolving-call', | ||
isEsm: true, | ||
onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartResolve, | ||
onError: function intrumentErrorHandler(err) { | ||
logger.error(err.message, err.stack) | ||
} | ||
}) | ||
newrelic.instrument({ | ||
moduleName: '@grpc/grpc-js/build/src/call-stream', | ||
isEsm: true, | ||
onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartCall, | ||
onError: function intrumentErrorHandler(err) { | ||
logger.error(err.message, err.stack) | ||
} | ||
}) | ||
@@ -19,2 +19,9 @@ /* | ||
NRLogger.info("Starting New Relic Node.js Security Agent "); | ||
try { | ||
if (NRAgent.config.security.enabled){ | ||
require('../instrumentation-security'); | ||
} | ||
} catch (error) { | ||
logger.error("Error while applying security instrumentation") | ||
} | ||
@@ -173,5 +180,4 @@ const { | ||
if (initialize()) { | ||
require('../instrumentation-security'); | ||
initLogger.info("[STEP-6] => Application instrumentation applied successfully"); | ||
} | ||
})(); |
@@ -110,3 +110,3 @@ /* | ||
const LogMessage = require('./LogMessage'); | ||
const logMessage = new LogMessage.logMessage("SEVERE", 'Error in creating directory', __filename, error); | ||
const logMessage = new LogMessage.logMessage("DEBUG", 'Error in creating directory', __filename, error); | ||
addLogEventtoBuffer(logMessage); | ||
@@ -292,3 +292,3 @@ } | ||
const LogMessage = require('./LogMessage'); | ||
const logMessage = new LogMessage.logMessage("SEVERE", 'Error in processing snapshot files', __filename, error); | ||
const logMessage = new LogMessage.logMessage("DEBUG", 'Error in processing snapshot files', __filename, error); | ||
addLogEventtoBuffer(logMessage); | ||
@@ -295,0 +295,0 @@ } |
@@ -10,2 +10,3 @@ /* | ||
const restClient = require('../../restclient'); | ||
const grpcClient = require('../../grpcClient'); | ||
const { Agent } = require('../../../agent'); | ||
@@ -24,5 +25,2 @@ const { FuzzFailEvent } = require('../../../FuzzFailEvent'); | ||
const { | ||
IS_LAMBDA_ENV, | ||
AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER } = require('../../../sec-agent-constants'); | ||
const statusUtils = require('../../../statusUtils'); | ||
@@ -58,3 +56,3 @@ | ||
} | ||
iastIntervalConst = setInterval(() => { | ||
@@ -131,10 +129,13 @@ let data = IASTUtil.generateIASTDataRequest(); | ||
try { | ||
if (IS_LAMBDA_ENV) { | ||
const qualifiedARN = Agent.getAgent().applicationInfo.applicationUUID; | ||
const splitArn = String.prototype.split.call(qualifiedARN, ':'); | ||
const arn = splitArn.slice(0, splitArn.length - 1).join(':'); | ||
const ver = process.env[AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER]; | ||
logger.info('Invoking Lambda:: ARN: ' + arn); | ||
logger.info('Invoking Lambda:: Version: ' + ver); | ||
if (fuzzRequest.protocol == 'grpc') { | ||
const config = parseGRPCRequestToFuzz(fuzzRequest); | ||
config.headers['nr-csec-parent-id'] = fuzzRequest.id; | ||
IASTUtil.completedRequestsMapInit(fuzzRequest.id); | ||
grpcClient.fireRequest(config); | ||
if (fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) { | ||
logScannedApiId(fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID], fuzzRequest.requestURI) | ||
} | ||
IASTUtil.removePendingRequestId(fuzzRequest.id); | ||
} else { | ||
@@ -160,3 +161,2 @@ const config = parseAxiosHttpRequestToFuzz(fuzzRequest); | ||
logger.info('Firing http request:: URL: ' + config.url); | ||
const response = restClient.fireRequest(config); | ||
@@ -218,2 +218,22 @@ handleFuzzResponse(response, fuzzDetails); | ||
/** | ||
* Parses the passed fuzz request object to | ||
* Axios config. | ||
* | ||
* @param {JSON} requestObject | ||
*/ | ||
function parseGRPCRequestToFuzz(requestObject) { | ||
let serverName = requestObject.serverName ? requestObject.serverName : LOCALHOST; | ||
let host = serverName + COLON + requestObject.serverPort | ||
return { | ||
url: requestObject.protocol + COLON_SLASH_SLASH + host + requestObject.url, | ||
requestURI: requestObject.requestURI, | ||
protocol: requestObject.protocol, | ||
serverPort: requestObject.serverPort, | ||
method: requestObject.method, | ||
data: requestObject.body, | ||
headers: requestObject.headers, | ||
timeout: 5000, | ||
}; | ||
} | ||
@@ -220,0 +240,0 @@ |
@@ -34,2 +34,5 @@ /* | ||
let data = completedRequestsMap.get(id); | ||
if(!data){ | ||
return; | ||
} | ||
data.push(eventId); | ||
@@ -36,0 +39,0 @@ completedRequestsMap.set(id, data); |
@@ -21,3 +21,3 @@ /* | ||
initLogger.info('Security Agent is now ACTIVE for', applicationInfoModule.getInstance().applicationUUID); | ||
const logMessage = new LogMessage.logMessage("SEVERE", `Security Agent is ACTIVE for ${ applicationInfoModule.getInstance().applicationUUID}`, __filename, null); | ||
const logMessage = new LogMessage.logMessage("INFO", `Security Agent is ACTIVE for ${ applicationInfoModule.getInstance().applicationUUID}`, __filename, null); | ||
require('../../../commonUtils').addLogEventtoBuffer(logMessage); | ||
@@ -24,0 +24,0 @@ |
@@ -107,2 +107,3 @@ /* | ||
initLogger.info(`Connecting to Validator at ${validatorService}`); | ||
IASTUtil.IASTCleanup(); | ||
let cert = '' | ||
@@ -142,2 +143,3 @@ try { | ||
logger.warn("WS connection closed"); | ||
this.obeyReconnect(); | ||
}) | ||
@@ -177,3 +179,3 @@ this.instance = webSocket; | ||
initTimeOut = setTimeout(() => { | ||
IASTUtil.IASTCleanup(); | ||
logger.debug("Terminating ws instance and reconnecting"); | ||
@@ -195,3 +197,5 @@ try { | ||
let timeDiffInPing = (currentTime - lastPongtime) / 1000; | ||
if (timeDiffInPing > 40 && lastPongtime != 0) { | ||
logger.debug("Websocket ready state is:",self.instance.readyState) | ||
if (timeDiffInPing > 40 && lastPongtime != 0 && self.instance.readyState > 1) { | ||
self.obeyReconnect(); | ||
@@ -201,2 +205,3 @@ lastPongtime = 0; | ||
} | ||
try { | ||
@@ -208,3 +213,3 @@ await promisify(self.instance, self.instance.ping)(ACK, true, function (err) { | ||
statusUtils.addErrortoBuffer(err); | ||
self.obeyReconnect(); | ||
} | ||
@@ -267,3 +272,3 @@ }); | ||
}); | ||
}) | ||
} catch (error) { | ||
@@ -270,0 +275,0 @@ logger.debug(LOG_MESSAGES.ERROR_WHILE_SEND_EVENT, eventStr, error); |
@@ -104,2 +104,3 @@ /* | ||
event.metaData.isClientDetectedFromXFF = isClientDetectedFromXFF; | ||
event.httpRequest.route = uri; | ||
return event; | ||
@@ -106,0 +107,0 @@ } catch (e) { |
@@ -101,3 +101,3 @@ /* | ||
const LogMessage = require('./LogMessage'); | ||
const logMessage = new LogMessage.logMessage("SEVERE", 'Error in creating snapshot file', __filename, err); | ||
const logMessage = new LogMessage.logMessage("DEBUG", 'Error in creating snapshot file', __filename, err); | ||
commonUtils.addLogEventtoBuffer(logMessage); | ||
@@ -104,0 +104,0 @@ } else { |
@@ -85,3 +85,3 @@ /* | ||
const dispatcher = NRAgent.environment.get('Dispatcher')[0]; | ||
const logMessage = new LogMessage.logMessage("SEVERE", `Detected framework: ${framework} and dispatcher: ${dispatcher}`, __filename, null); | ||
const logMessage = new LogMessage.logMessage("INFO", `Detected framework: ${framework} and dispatcher: ${dispatcher}`, __filename, null); | ||
commonUtils.addLogEventtoBuffer(logMessage); | ||
@@ -88,0 +88,0 @@ } catch (error) { |
@@ -13,2 +13,3 @@ /* | ||
const htmlEntities = require('html-entities'); | ||
const { STRING } = require('./sec-agent-constants'); | ||
@@ -140,6 +141,11 @@ const tagNameRegex = /<([a-zA-Z_-]+[0-9]*|!--)/gmi; | ||
const attackConstructs = new Set(); | ||
for (const data of combinedData) { | ||
const constructs = getXSSConstructs(data); | ||
constructs.forEach(attackConstructs.add, attackConstructs); | ||
try { | ||
for (const data of combinedData) { | ||
const constructs = getXSSConstructs(data); | ||
constructs.forEach(attackConstructs.add, attackConstructs); | ||
} | ||
} catch (error) { | ||
logger.debug("Error while getting rxss constucts:", error); | ||
} | ||
return attackConstructs; | ||
@@ -157,3 +163,3 @@ } | ||
let attribMatcher; | ||
while (currPos < data.length) { | ||
while (currPos < data.length && typeof data == STRING ) { | ||
let tagName; | ||
@@ -239,3 +245,3 @@ | ||
if (data.charAt(currPos) !== ANGLE_END_CHAR) { | ||
if (typeof data == STRING && data.charAt(currPos) !== ANGLE_END_CHAR) { | ||
const tmp = data.indexOf(ANGLE_END, currPos); | ||
@@ -242,0 +248,0 @@ if (tmp !== -1) { |
{ | ||
"name": "@newrelic/security-agent", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "New Relic Security Agent for Node.js", | ||
@@ -61,2 +61,3 @@ "main": "index.js", | ||
"devDependencies": { | ||
"@grpc/proto-loader": "^0.7.10", | ||
"@hapi/hapi": "^21.3.0", | ||
@@ -63,0 +64,0 @@ "@koa/router": "^12.0.0", |
@@ -102,3 +102,3 @@ # New Relic Node.js security agent | ||
Any feedback provided to New Relic about the New Relic security agent, including feedback provided as source code, comments, or other copyrightable or patentable material, is provided to New Relic under the terms of the Apache Software License, version 2. If you do not provide attribution information or a copy of the license with your feedback, you waive the performance of those requirements of the Apache License with respect to New Relic. The license grant regarding any feedback is irrevocable and persists past the termination of the preview license. | ||
Any feedback provided to New Relic about the New Relic security agent, including feedback provided as source code, comments, or other copyrightable or patentable material, is provided to New Relic under the terms of the New Relic Software License v1.0. If you do not provide attribution information or a copy of the license with your feedback, you waive the performance of those requirements of the New Relic Software License v1.0 with respect to New Relic. The license grant regarding any feedback is irrevocable. | ||
Keep in mind that when you submit a pull request or other feedback, you’ll need to sign the CLA via the click-through using CLA-Assistant. You only have to sign the CLA one time per project. | ||
@@ -105,0 +105,0 @@ If you have any questions drop us an email at opensource@newrelic.com. |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
520235
95
9497
48
33