Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@newrelic/security-agent

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@newrelic/security-agent - npm Package Compare versions

Comparing version 1.1.1 to 1.2.0

12

CHANGELOG.md

@@ -0,1 +1,13 @@

### v1.2.0 (2024-04-12)
#### Features
* Added instrumentation for express framework's res.download() and res.sendFile()
#### Bug fixes
* Handling to decrypt fuzz header data for IAST scanning
* Logging and snapshot file fixes
#### Miscellaneous chores
* Prepend vulnerability case type with apiId
* Updated jsonVersion to v1.2.0
* Bumped undici from 5.28.3 to 5.28.4
### v1.1.1 (2024-03-21)

@@ -2,0 +14,0 @@ #### Bug fixes

1

lib/instrumentation-security/core/constants.js

@@ -57,2 +57,3 @@

EMPTY_STRING: '',
COMMA: ',',
QUESTION_MARK: '?',

@@ -59,0 +60,0 @@ DOUBLE_PIPE: '||',

156

lib/instrumentation-security/core/sec-utils.js

@@ -12,6 +12,7 @@ /*

const async_hooks = require('async_hooks');
const crypto = require('crypto');
let AGENT_DIR = path.join(__dirname, '../../');
let AGENT_DIR = path.join(__dirname, '../../');
if(process.platform == 'win32'){
if (process.platform == 'win32') {
AGENT_DIR = path.join(__dirname, "..\\..\\");

@@ -40,6 +41,7 @@ }

createSkipList();
const fs = require('fs');
const fs = require('fs');
const cp = require('child_process');
const requestIp = require('request-ip');
const URL = require('url')
const URL = require('url');
const { EMPTY_STR } = require('./constants');

@@ -52,3 +54,3 @@ /**

*/
function createSkipList () {
function createSkipList() {
let loadList = [];

@@ -69,3 +71,3 @@ process.moduleLoadList.forEach(function (loadString) {

function getTraceObject (shim) {
function getTraceObject(shim) {
const trace = stackTraceModule.get();

@@ -88,8 +90,8 @@ const traceLength = 10;

const routeFile = routeManager.getRoute(key);
if(routeFile){
if (routeFile) {
stkTrace.push(routeFile);
}
const sourceDetails = secTrace.getSourceDetailsFromTrace(trace,__filename, stkTrace);
const sourceDetails = secTrace.getSourceDetailsFromTrace(trace, __filename, stkTrace);
const traceObject = {
sourceDetails:sourceDetails,
sourceDetails: sourceDetails,
stacktrace: stkTrace

@@ -100,3 +102,3 @@ }

function getTraceObjectFallback (request) {
function getTraceObjectFallback(request) {
const trace = stackTraceModule.get();

@@ -118,8 +120,8 @@ const traceLength = 10;

const routeFile = routeManager.getRoute(key);
if(routeFile){
if (routeFile) {
stkTrace.push(routeFile);
}
const sourceDetails = secTrace.getSourceDetailsFromTrace(trace,__filename, stkTrace);
const sourceDetails = secTrace.getSourceDetailsFromTrace(trace, __filename, stkTrace);
const traceObject = {
sourceDetails:sourceDetails,
sourceDetails: sourceDetails,
stacktrace: stkTrace

@@ -131,3 +133,3 @@ }

function traceElementForRoute () {
function traceElementForRoute() {
const stkTrace = [];

@@ -141,3 +143,3 @@ const trace = stackTraceModule.get();

const lineNumber = trace[i].getLineNumber();
if(i>0){
if (i > 0) {
methodName = trace[i - 1].getMethodName();

@@ -155,7 +157,7 @@ }

function getExecutionId(){
function getExecutionId() {
return async_hooks.executionAsyncId();
}
function createPathIfNotExist (dir) {
function createPathIfNotExist(dir) {
try {

@@ -182,46 +184,84 @@ if (!fs.existsSync(dir)) {

try {
const data = Object.assign({});
const segment = shim.getActiveSegment();
if (segment && segment.transaction) {
data.protocol = (request.connection && request.connection.encrypted) ? 'https' : 'http';
data.body = null;
data.headers = request.headers;
data.url = request.url;
data.method = request.method;
data.httpVersion = request.httpVersion;
data.serverPort = segment.transaction.port;
data.contextPath = '/';
const queryObject = URL.parse(request.url, true).query;
data.parameterMap = {};
if (queryObject) {
Object.keys(queryObject).forEach(function (key) {
if (queryObject[key]) {
if (!data.parameterMap[key]) {
data.parameterMap[key] = new Array(queryObject[key].toString());
}
const data = Object.assign({});
const segment = shim.getActiveSegment();
if (segment && segment.transaction) {
data.protocol = (request.connection && request.connection.encrypted) ? 'https' : 'http';
data.body = null;
data.headers = request.headers;
data.url = request.url;
data.method = request.method;
data.httpVersion = request.httpVersion;
data.serverPort = segment.transaction.port;
data.contextPath = '/';
const queryObject = URL.parse(request.url, true).query;
data.parameterMap = {};
if (queryObject) {
Object.keys(queryObject).forEach(function (key) {
if (queryObject[key]) {
if (!data.parameterMap[key]) {
data.parameterMap[key] = new Array(queryObject[key].toString());
}
}
});
}
});
data.clientIP = requestIp.getClientIp(request);
const transactionId = segment.transaction.id;
const storedRequest = requestManager.getRequestFromId(transactionId);
if (storedRequest && storedRequest.uri) {
data.uri = storedRequest.uri;
data.parameterMap = storedRequest.parameterMap;
}
requestManager.setRequest(transactionId, data);
if (shim.agent.getLinkingMetadata()) {
let linkingMetadata = shim.agent.getLinkingMetadata();
if (linkingMetadata['trace.id']) {
let traceId = linkingMetadata['trace.id'];
requestManager.setRequest(traceId, data)
}
}
}
data.clientIP = requestIp.getClientIp(request);
const transactionId = segment.transaction.id;
const storedRequest = requestManager.getRequestFromId(transactionId);
if (storedRequest && storedRequest.uri) {
data.uri = storedRequest.uri;
data.parameterMap = storedRequest.parameterMap;
} catch (error) {
logger.debug("Error while preparing incoming request:", error);
}
}
function decryptData(encryptedData) {
let decryptedData = EMPTY_STR;
try {
const linkingMetadata = API.newrelic.getLinkingMetadata();
let entityGuid = linkingMetadata['entity.guid'];
let password = entityGuid;
let salt = password.slice(0, 16);
//derive key
const key = crypto.pbkdf2Sync(password, salt, 1024, 32, 'sha1');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.alloc(16));
let decrypted = decipher.update(encryptedData, 'hex');
decrypted += decipher.final();
decryptedData = decrypted.slice(16, decrypted.length);
} catch (error) {
logger.debug("Error while decrypting the data:", error);
}
return decryptedData;
}
function hashVerifier(decryptedData, hashFromSE) {
let flag = false;
try {
const hash = crypto.createHash('sha256'); // Create a new hash object
hash.update(decryptedData); // Write data to the hash object
let calculatedSHA256Hash = hash.digest('hex');
if (calculatedSHA256Hash === hashFromSE) {
flag = true;
}
requestManager.setRequest(transactionId, data);
if(shim.agent.getLinkingMetadata()){
let linkingMetadata = shim.agent.getLinkingMetadata();
if(linkingMetadata['trace.id']){
let traceId = linkingMetadata['trace.id'];
requestManager.setRequest(traceId, data)
}
}
}
} catch (error) {
logger.debug("Error while preparing incoming request:", error);
}
}
logger.debug("Error while calculating SHA256 of decrypted data:", error);
}
return flag;
}
module.exports = {

@@ -233,3 +273,5 @@ getTraceObject,

getTraceObjectFallback,
addRequestData
addRequestData,
decryptData,
hashVerifier
}

@@ -21,4 +21,7 @@ /*

const logger = API.getLogger();
const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
const { STRING } = require('../../core/constants');
const securityMetaData = require('../../core/security-metadata');
module.exports = function initialize(shim, express, moduleName) {
module.exports = function initialize(shim, express) {
logger.info("Instrumenting express")

@@ -32,3 +35,4 @@ if (!express || !express.Router) {

}
expressFileHook(shim, express && express.response, 'download')
expressFileHook(shim, express && express.response, 'sendFile')
}

@@ -111,2 +115,38 @@

}
}
}
/**
* Wrapper for resp.download()
* @param {*} shim
* @param {*} mod
* @param {*} fun
*/
function expressFileHook(shim, mod, fun){
shim.wrap(mod, fun, function makeFAWrapper(shim, fn) {
if (!shim.isFunction(fn)) {
return fn
}
logger.debug(`Instrumenting express.response.${fun}`)
return function FAWrapper() {
let parameters = Array.prototype.slice.apply(arguments);
const interceptedArgs = [arguments[0]];
shim.interceptedArgs = interceptedArgs;
const request = requestManager.getRequest(shim);
if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
const traceObject = secUtils.getTraceObject(shim);
try {
parameters[0] = path.resolve(parameters[0]);
} catch (error) {
}
let absoluteParameters = [parameters[0]];
const secMetadata = securityMetaData.getSecurityMetaData(request, absoluteParameters, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
this.secEvent = API.generateSecEvent(secMetadata);
API.sendEvent(this.secEvent);
}
return fn.apply(this, arguments);
};
});
}

@@ -8,3 +8,3 @@ /*

const requestManager = require('../../core/request-manager');
const { NR_CSEC_FUZZ_REQUEST_ID, QUESTION_MARK, EMPTY_STRING, UTF8, CONTENT_TYPE, TEXT_HTML, APPLICATION_JSON, APPLICATION_XML, APPLICATION_XHTML, TEXT_PLAIN, APPLICATION_X_FORM_URLENCODED, MULTIPART_FORM_DATA } = require('../../core/constants');
const { NR_CSEC_FUZZ_REQUEST_ID, QUESTION_MARK, EMPTY_STRING, UTF8, CONTENT_TYPE, TEXT_HTML, APPLICATION_JSON, APPLICATION_XML, APPLICATION_XHTML, TEXT_PLAIN, APPLICATION_X_FORM_URLENCODED, MULTIPART_FORM_DATA, COMMA } = require('../../core/constants');
const ARRAY_TYPE = 'Array';

@@ -172,23 +172,34 @@ const STRING_TYPE = 'string';

logger.debug('AdditionalData:', additionalData);
if (additionalData.length >= 7) {
for (let i = 6; i < additionalData.length; i++) {
let file = additionalData[i].trim();
file = file.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
file = path.resolve(file);
const parentDir = path.dirname(file);
if (!isInvalid(parentDir) && isPathInside(parentDir, CSEC_HOME_TMP)) {
try {
if (!fs.existsSync(parentDir)) {
secUtils.createPathIfNotExist(parentDir);
} else {
logger.debug(parentDir + ' Already Exists');
if (additionalData.length >= 8) {
let encryptedData = additionalData[6];
let hashVerifier = additionalData[7];
let decryptedData = secUtils.decryptData(encryptedData);
let verifiedHash = secUtils.hashVerifier(decryptedData, hashVerifier);
logger.debug("verifiedHash:", verifiedHash);
let filesToCreate = decryptedData.split(COMMA);
if (verifiedHash) {
logger.debug("Encrypted Data:", encryptedData);
logger.debug("Decrypted Data:", decryptedData)
logger.debug("fliesTocreate:",filesToCreate);
for (let i = 0; i < filesToCreate.length; i++) {
let file = filesToCreate[i].trim();
file = file.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
file = path.resolve(file);
const parentDir = path.dirname(file);
if (!isInvalid(parentDir) && isPathInside(parentDir, CSEC_HOME_TMP)) {
try {
if (!fs.existsSync(parentDir)) {
secUtils.createPathIfNotExist(parentDir);
} else {
logger.debug(parentDir + ' Already Exists');
}
fs.closeSync(fs.openSync(file, 'w'));
requestData.tempFiles = [];
requestData.tempFiles.push(file);
requestManager.setRequest(transactionId, requestData);
} catch (error) {
logger.debug(error);
}
fs.closeSync(fs.openSync(file, 'w'));
requestData.tempFiles = [];
requestData.tempFiles.push(file);
requestManager.setRequest(transactionId, requestData);
} catch (error) {
logger.debug(error);
}

@@ -397,3 +408,3 @@ }

let isUnsupportedType = isUnsupportedContentType(type);
if (request && (construct || dynamicScanningFlag) && !isUnsupportedType) {

@@ -406,3 +417,3 @@ const args = [];

const secEvent = API.generateSecEvent(secMetadata);
secEvent.httpResponse = {};
secEvent.httpResponse = {};
secEvent.httpResponse.contentType = response.getHeader(CONTENT_TYPE);

@@ -409,0 +420,0 @@ API.sendEvent(secEvent);

@@ -15,3 +15,3 @@ /*

const AgentStatus = require('../core/agent-status');
const { CSEC_HOME, SLASH } = require('./sec-agent-constants');
const { CSEC_HOME, SLASH, EMPTY_STR } = require('./sec-agent-constants');

@@ -449,3 +449,17 @@ const njsAgentConstants = require('./sec-agent-constants');

}
/**
* Utility to get framework from APM
* @returns
*/
function getFramework(){
let framework = EMPTY_STR;
try {
framework = NRAgent.environment.get('Framework')[0];
} catch (error) {
logger.debug("Unable to get framework");
}
return framework;
}
module.exports = {

@@ -474,3 +488,4 @@ getUUID,

addLogEventtoBuffer,
getLogEvents
getLogEvents,
getFramework
};

@@ -54,3 +54,2 @@

initLogger.info("[STEP-7] => Received and applied policy/configuration", JSON.stringify(policy.data));
logger.info('Applied policy is:', JSON.stringify(policy.data));
if (NRAgent && NRAgent.config.security.detection) {

@@ -57,0 +56,0 @@ logger.info('Security detection flags:', JSON.stringify(NRAgent.config.security.detection));

@@ -114,2 +114,3 @@ /*

CRITICAL: 'CRITICAL',
HYPHEN: '-',

@@ -169,3 +170,3 @@ LOG_MESSAGES: {

SENDING_APPINFO: '[APP_INFO] Sending Application Info To Prevent-web Service: ',
SENDING_APPINFO_COMPLETE: '[COMPLETE][APP_INFO] Application info sent to Prevent-Web service :',
SENDING_APPINFO_COMPLETE: '[STEP-3][COMPLETE][APP_INFO] Application info sent to Prevent-Web service :',
APPLY_INSTRUMENTATION: '[STEP-6][BEGIN][instrumentation] Applying Instrumentation',

@@ -172,0 +173,0 @@ AVAIL_DISK: 'Available Disk Space Is: ',

@@ -12,3 +12,3 @@ /*

const shaUtil = require('./sha-size-util');
const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP, SEVERE } = require('./sec-agent-constants');
const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP, SEVERE, HYPHEN } = require('./sec-agent-constants');
const API = require('../../../nr-security-api');

@@ -87,2 +87,3 @@ const NRAgent = API.getNRAgent();

let apiId = shaUtil.getSHA256ForData(traceObject.stacktrace.join('|') + uri);
apiId = securityMetadata.eventType + HYPHEN + apiId;
const agentModule = Agent.getAgent();

@@ -89,0 +90,0 @@ const metaData = {};

@@ -26,6 +26,7 @@ /*

const statusFile = njsAgentConstants.STATUS_LOG_FILE;
const sep = require('path').sep;
const statusTemplate = 'Snapshot timestamp: : %s\n' +
`CSEC Agent start timestamp: ${njsAgentConstants.AGENT_START_TIME} with application uuid:${commonUtils.getUUID()}\n` +
`SEC_HOME: ${njsAgentConstants.CSEC_HOME}\n` +
`CSEC_HOME: ${njsAgentConstants.CSEC_HOME}${sep}nr-security-home${sep}\n` +
`Agent location: ${AGENT_DIR}\n` +

@@ -39,6 +40,6 @@ `Using CSEC Agent for Node.js, Node version:${process.version}, PID:${process.pid}\n` +

'Application server: %s\n' +
'Application Framework: %s\n' +
`Application Framework: %s\n` +
`Websocket connection to Prevent Web: ${commonUtils.getValidatorServiceEndpointURL()}, Status: %s\n` +
'Instrumentation successful:\n' +
'Tracking loaded modules in the application:\n' +
'Instrumentation successful\n' +
'Tracking loaded modules in the application\n' +
'Policy applied successfully. Policy version is: %s\n' +

@@ -88,3 +89,3 @@ 'Started Health Check for Agent\n' +

const appLoc = deployedApplications[0].deployedPath;
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()));
const formattedSnapshot = util.format(statusTemplate, new Date().toString(), appLoc, appInfo.serverInfo.name, commonUtils.getFramework(), commonUtils.getWSHealthStatus(), appInfo.policyVersion, getKeyValPairs(lastHC.stats), getKeyValPairs(lastHC.serviceStatus), JSON.stringify(getBufferedErrors()), JSON.stringify(getBufferedHC()));
return formattedSnapshot;

@@ -91,0 +92,0 @@ }

{
"name": "@newrelic/security-agent",
"version": "1.1.1",
"version": "1.2.0",
"description": "New Relic Security Agent for Node.js",
"main": "index.js",
"jsonVersion": "1.1.1",
"jsonVersion": "1.2.0",
"contributors": [

@@ -8,0 +8,0 @@ {

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc