From 234125196e310a2a1401cc856a615a8188be01c5 Mon Sep 17 00:00:00 2001 From: Sumit Suthar Date: Thu, 21 Sep 2023 09:14:26 +0530 Subject: [PATCH 1/8] instrumentation for memcached db --- .../hooks/memcached/nr-memcached.js | 73 +++++++++++++++++++ lib/instrumentation-security/index.js | 7 ++ 2 files changed, 80 insertions(+) create mode 100644 lib/instrumentation-security/hooks/memcached/nr-memcached.js diff --git a/lib/instrumentation-security/hooks/memcached/nr-memcached.js b/lib/instrumentation-security/hooks/memcached/nr-memcached.js new file mode 100644 index 00000000..b3fbbc77 --- /dev/null +++ b/lib/instrumentation-security/hooks/memcached/nr-memcached.js @@ -0,0 +1,73 @@ +/* + * Copyright 2023 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' + +const requestManager = require('../../core/request-manager'); +const secUtils = require('../../core/sec-utils'); +const API = require("../../../nr-security-api"); +const securityMetaData = require('../../core/security-metadata'); +const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants'); +const logger = API.getLogger(); +const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants'); + + +module.exports = function initialize(shim, memcached, moduleName) { + logger.info('Instrumenting ' + moduleName) + const utils = shim.require('./lib/utils'); + validateArgHook(shim, utils, 'validateArg'); +} + + +function prepareSecurityArgs(args) { + const obj = args[0]; + let parameters = Object.create(null); + try { + if (obj) { + parameters.type = obj['type']; + parameters.key = obj['key']; + if(obj['value']){ + parameters.value = JSON.parse(obj['value']); + } + parameters.command = obj['command']; + } + } catch (error) { + logger.debug("Error in preparing memcached parameters:",error); + } + + return parameters; +} + +/** + * Wrapper to hook validateArg method + * @param {*} shim + * @param {*} mod + * @param {*} method + */ +function validateArgHook(shim, mod, method) { + shim.wrap(mod, method, function makeQueryWrapper(shim, fn) { + return function queryWrapper() { + let params = prepareSecurityArgs(arguments); + shim.params = params; + const request = requestManager.getRequest(shim); + + if (request) { + const traceObject = secUtils.getTraceObject(shim); + traceObject.sourceMethod = method; + const secMetadata = securityMetaData.getSecurityMetaData(request, params, traceObject, secUtils.getExecutionId(), EVENT_TYPE.DB_COMMAND, EVENT_CATEGORY.MEMCACHED) + const secEvent = API.generateSecEvent(secMetadata); + this.secEvent = secEvent; + API.sendEvent(secEvent); + } + + const result = fn.apply(this, arguments); + if (result && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) { + API.generateExitEvent(this.secEvent); + delete this.secEvent + } + return result; + }; + }); +} \ No newline at end of file diff --git a/lib/instrumentation-security/index.js b/lib/instrumentation-security/index.js index 58fe1827..904829f3 100644 --- a/lib/instrumentation-security/index.js +++ b/lib/instrumentation-security/index.js @@ -181,6 +181,13 @@ newrelic.instrumentWebframework({ } }) +newrelic.instrumentDatastore({ + moduleName: 'memcached', + onRequire: require('./hooks/memcached/nr-memcached'), + onError: function intrumentErrorHandler(err) { + logger.error(err.message, err.stack) + } +}) From 646e6e2f5a92478a9a0bcb07124815149049ac26 Mon Sep 17 00:00:00 2001 From: Sumit Suthar Date: Thu, 21 Sep 2023 17:28:54 +0530 Subject: [PATCH 2/8] event category added for memcached --- lib/instrumentation-security/core/event-constants.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/instrumentation-security/core/event-constants.js b/lib/instrumentation-security/core/event-constants.js index 41c986b4..315e47ba 100644 --- a/lib/instrumentation-security/core/event-constants.js +++ b/lib/instrumentation-security/core/event-constants.js @@ -40,7 +40,8 @@ const EVENT_CATEGORY = { UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT', REFLECTED_XSS: 'REFLECTED_XSS', XPATH: 'XPATH', - LDAP: 'LDAP' + LDAP: 'LDAP', + MEMCACHED: 'MEMCACHED' } module.exports = { From ce254b4cb4855948e8b00d319c6a7141f6bf5806 Mon Sep 17 00:00:00 2001 From: Sumit Suthar Date: Wed, 4 Oct 2023 11:51:13 +0530 Subject: [PATCH 3/8] unit test cases for memcached --- .../hooks/memcached/nr-memcached.js | 22 +++-- lib/instrumentation-security/index.js | 1 + package-lock.json | 47 ++++++++++ package.json | 2 + .../nr-memcached.test.js | 90 +++++++++++++++++++ 5 files changed, 155 insertions(+), 7 deletions(-) create mode 100755 test/instrumentation-security/nr-memcached.test.js diff --git a/lib/instrumentation-security/hooks/memcached/nr-memcached.js b/lib/instrumentation-security/hooks/memcached/nr-memcached.js index b3fbbc77..1ebd43d5 100644 --- a/lib/instrumentation-security/hooks/memcached/nr-memcached.js +++ b/lib/instrumentation-security/hooks/memcached/nr-memcached.js @@ -12,11 +12,17 @@ const securityMetaData = require('../../core/security-metadata'); const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants'); const logger = API.getLogger(); const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants'); +const path = require('path'); - -module.exports = function initialize(shim, memcached, moduleName) { +module.exports = function initialize(shim, memcached, moduleName, additionalMod) { logger.info('Instrumenting ' + moduleName) - const utils = shim.require('./lib/utils'); + let utils; + if (additionalMod) { + utils = additionalMod; + } + else { + utils = shim.require('./lib/utils'); + } validateArgHook(shim, utils, 'validateArg'); } @@ -28,15 +34,15 @@ function prepareSecurityArgs(args) { if (obj) { parameters.type = obj['type']; parameters.key = obj['key']; - if(obj['value']){ + if (obj['value']) { parameters.value = JSON.parse(obj['value']); } parameters.command = obj['command']; } } catch (error) { - logger.debug("Error in preparing memcached parameters:",error); + logger.debug("Error in preparing memcached parameters:", error); } - + return parameters; } @@ -50,6 +56,7 @@ function validateArgHook(shim, mod, method) { shim.wrap(mod, method, function makeQueryWrapper(shim, fn) { return function queryWrapper() { let params = prepareSecurityArgs(arguments); + shim.interceptedArgs = params; shim.params = params; const request = requestManager.getRequest(shim); @@ -70,4 +77,5 @@ function validateArgHook(shim, mod, method) { return result; }; }); -} \ No newline at end of file +} + diff --git a/lib/instrumentation-security/index.js b/lib/instrumentation-security/index.js index 904829f3..a9483e02 100644 --- a/lib/instrumentation-security/index.js +++ b/lib/instrumentation-security/index.js @@ -183,6 +183,7 @@ newrelic.instrumentWebframework({ newrelic.instrumentDatastore({ moduleName: 'memcached', + isEsm: true, onRequire: require('./hooks/memcached/nr-memcached'), onError: function intrumentErrorHandler(err) { logger.error(err.message, err.stack) diff --git a/package-lock.json b/package-lock.json index be149b40..67833635 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5859,6 +5859,12 @@ "typedarray": "^0.0.6" } }, + "connection-parse": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/connection-parse/-/connection-parse-0.0.7.tgz", + "integrity": "sha512-bTTG28diWg7R7/+qE5NZumwPbCiJOT8uPdZYu674brDjBWQctbaQbYlDKhalS+4i5HxIx+G8dZsnBHKzWpp01A==", + "dev": true + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -7117,6 +7123,16 @@ } } }, + "hashring": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/hashring/-/hashring-3.2.0.tgz", + "integrity": "sha512-xCMovURClsQZ+TR30icCZj+34Fq1hs0y6YCASD6ZqdRfYRybb5Iadws2WS+w09mGM/kf9xyA5FCdJQGcgcraSA==", + "dev": true, + "requires": { + "connection-parse": "0.0.x", + "simple-lru-cache": "0.0.x" + } + }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -7682,6 +7698,15 @@ "istanbul-lib-report": "^3.0.0" } }, + "jackpot": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/jackpot/-/jackpot-0.0.6.tgz", + "integrity": "sha512-rbWXX+A9ooq03/dfavLg9OXQ8YB57Wa7PY5c4LfU3CgFpwEhhl3WyXTQVurkaT7zBM5I9SSOaiLyJ4I0DQmC0g==", + "dev": true, + "requires": { + "retry": "0.6.0" + } + }, "jackspeak": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.2.tgz", @@ -8133,6 +8158,16 @@ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, + "memcached": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/memcached/-/memcached-2.2.2.tgz", + "integrity": "sha512-lHwUmqkT9WdUUgRsAvquO4xsKXYaBd644Orz31tuth+w/BIfFNuJMWwsG7sa7H3XXytaNfPTZ5R/yOG3d9zJMA==", + "dev": true, + "requires": { + "hashring": "3.2.x", + "jackpot": ">=0.0.6" + } + }, "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -9612,6 +9647,12 @@ "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", "dev": true }, + "retry": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.6.0.tgz", + "integrity": "sha512-RgncoxLF1GqwAzTZs/K2YpZkWrdIYbXsmesdomi+iPilSzjUyr/wzNIuteoTVaWokzdwZIJ9NHRNQa/RUiOB2g==", + "dev": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -9821,6 +9862,12 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "simple-lru-cache": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/simple-lru-cache/-/simple-lru-cache-0.0.2.tgz", + "integrity": "sha512-uEv/AFO0ADI7d99OHDmh1QfYzQk/izT1vCmu/riQfh7qjBVUUgRT87E5s5h7CxWCA/+YoZerykpEthzVrW3LIw==", + "dev": true + }, "sinon": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", diff --git a/package.json b/package.json index 21226ff9..453c3d60 100755 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ ], "scripts": { "test": "tap --test-regex='(\\/|^test\\/instrumentation-security\\/.*\\.test\\.js)$' --no-coverage", + "foo": "tap test/instrumentation-security/nr-memcached.test.js", "posttest": "rm -f newrelic_agent.log && rm -rf nr-security-home && rm -rf .nyc_output", "third-party-updates": "oss third-party manifest && oss third-party notices && git add THIRD_PARTY_NOTICES.md third_party_manifest.json" }, @@ -77,6 +78,7 @@ "eslint-plugin-promise": "^4.3.1", "eslint-plugin-sonarjs": "^0.15.0", "koa": "^2.14.1", + "memcached": "^2.2.2", "mongodb": "^4.13.0", "mongodb-memory-server": "^8.11.2", "mongodb2": "npm:mongodb@^2.2.36", diff --git a/test/instrumentation-security/nr-memcached.test.js b/test/instrumentation-security/nr-memcached.test.js new file mode 100755 index 00000000..69ad4990 --- /dev/null +++ b/test/instrumentation-security/nr-memcached.test.js @@ -0,0 +1,90 @@ +/* + * Copyright 2023 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: New Relic Pre-Release + */ + +'use strict' + +const test = require('tap').test; +const utils = require('@newrelic/test-utilities') +const cp = require('child_process'); +const Memcached = require('memcached'); +let memcached = new Memcached(); + +const dbSetup = async () => { + cp.execSync('docker rm -f csec_memcache && docker run --name csec_memcache -p 11212:11211 -d memcached && sleep 1'); + memcached = new Memcached("localhost:11212"); +} + +test('memcached', (t) => { + t.autoend(); + let helper = null; + let initialize = null; + let shim = null; + + t.before(async () => { + await dbSetup(); + }) + + t.beforeEach(() => { + helper = utils.TestAgent.makeInstrumented() + shim = helper.getShim(); + initialize = require('../../lib/instrumentation-security/hooks/memcached/nr-memcached'); + initialize(shim, Memcached, 'memcached', require('memcached/lib/utils')); + }) + + t.afterEach(() => { + helper && helper.unload() + }) + + t.test('set', (t) => { + memcached.set("hello", 1, 100, function (err, result) { + t.equal('set', shim.interceptedArgs.type); + t.equal('hello', shim.interceptedArgs.key); + t.equal(1, shim.interceptedArgs.value); + t.end(); + }); + }) + + t.test('get', (t) => { + memcached.get("hello", function (err, result) { + t.equal('get', shim.interceptedArgs.type); + t.equal('hello', shim.interceptedArgs.key); + t.end(); + }); + }) + + t.test('get_multi', (t) => { + memcached.get(["hello", "hello_json"], function (err, result) { + t.equal('get', shim.interceptedArgs.type); + t.equal(2, shim.interceptedArgs.key.length); + t.end(); + }); + }) + + t.test('delete', (t) => { + memcached.del('foo', function (err, result) { + t.equal('delete', shim.interceptedArgs.type); + t.equal('foo', shim.interceptedArgs.key); + t.end(); + }); + }) + + t.test('set_json', (t) => { + memcached.set("hello_json", { javascript: 'objects', are: ['no', 'problem', 4], nMemcached: true }, 10000, function (err, result) { + t.equal('set', shim.interceptedArgs.type); + t.equal('hello_json', shim.interceptedArgs.key); + t.equal("objects", shim.interceptedArgs.value.javascript); + t.equal(true, shim.interceptedArgs.value.nMemcached); + t.equal(3, shim.interceptedArgs.value.are.length); + t.end(); + }); + }) + + t.teardown(() => { + cp.execSync('docker rm -f csec_memcache'); + }) + + +}) + From 599a3280e0fe06652368321fcd4c2bf897d3b0d1 Mon Sep 17 00:00:00 2001 From: Sumit Suthar Date: Wed, 4 Oct 2023 12:09:02 +0530 Subject: [PATCH 4/8] cleanup --- lib/instrumentation-security/hooks/memcached/nr-memcached.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/instrumentation-security/hooks/memcached/nr-memcached.js b/lib/instrumentation-security/hooks/memcached/nr-memcached.js index 1ebd43d5..7f2959fa 100644 --- a/lib/instrumentation-security/hooks/memcached/nr-memcached.js +++ b/lib/instrumentation-security/hooks/memcached/nr-memcached.js @@ -12,7 +12,6 @@ const securityMetaData = require('../../core/security-metadata'); const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants'); const logger = API.getLogger(); const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants'); -const path = require('path'); module.exports = function initialize(shim, memcached, moduleName, additionalMod) { logger.info('Instrumenting ' + moduleName) From 57b70b76d80faec708ef9ee3a023bb104b05fea5 Mon Sep 17 00:00:00 2001 From: Sumit Suthar Date: Wed, 4 Oct 2023 12:30:06 +0530 Subject: [PATCH 5/8] memcached unit test added --- package.json | 1 - .../nr-memcached.test.js | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 453c3d60..3b15b2b5 100755 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ ], "scripts": { "test": "tap --test-regex='(\\/|^test\\/instrumentation-security\\/.*\\.test\\.js)$' --no-coverage", - "foo": "tap test/instrumentation-security/nr-memcached.test.js", "posttest": "rm -f newrelic_agent.log && rm -rf nr-security-home && rm -rf .nyc_output", "third-party-updates": "oss third-party manifest && oss third-party notices && git add THIRD_PARTY_NOTICES.md third_party_manifest.json" }, diff --git a/test/instrumentation-security/nr-memcached.test.js b/test/instrumentation-security/nr-memcached.test.js index 69ad4990..00be5aeb 100755 --- a/test/instrumentation-security/nr-memcached.test.js +++ b/test/instrumentation-security/nr-memcached.test.js @@ -81,6 +81,24 @@ test('memcached', (t) => { }); }) + t.test('increment', (t) => { + memcached.increment( "hello", 1, function( err, result ){ + t.equal('incr', shim.interceptedArgs.type); + t.equal('hello', shim.interceptedArgs.key); + t.equal(1, shim.interceptedArgs.value); + t.end(); + }); + }) + + t.test('decrement', (t) => { + memcached.decr( "hello", 1, function( err, result ){ + t.equal('decr', shim.interceptedArgs.type); + t.equal('hello', shim.interceptedArgs.key); + t.equal(1, shim.interceptedArgs.value); + t.end(); + }); + }) + t.teardown(() => { cp.execSync('docker rm -f csec_memcache'); }) From e84132234bc9a1ebf0e596f134e49bd63babe8cb Mon Sep 17 00:00:00 2001 From: Sumit Suthar Date: Wed, 22 Nov 2023 09:39:09 +0530 Subject: [PATCH 6/8] package-lock update --- package-lock.json | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/package-lock.json b/package-lock.json index 8d828a1b..b97ae6f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "eslint-plugin-promise": "^4.3.1", "eslint-plugin-sonarjs": "^0.15.0", "koa": "^2.14.1", + "memcached": "^2.2.2", "mongodb": "^4.13.0", "mongodb-memory-server": "^8.11.2", "mongodb2": "npm:mongodb@^2.2.36", @@ -7644,6 +7645,12 @@ "typedarray": "^0.0.6" } }, + "node_modules/connection-parse": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/connection-parse/-/connection-parse-0.0.7.tgz", + "integrity": "sha512-bTTG28diWg7R7/+qE5NZumwPbCiJOT8uPdZYu674brDjBWQctbaQbYlDKhalS+4i5HxIx+G8dZsnBHKzWpp01A==", + "dev": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -9672,6 +9679,16 @@ "node": ">=8" } }, + "node_modules/hashring": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/hashring/-/hashring-3.2.0.tgz", + "integrity": "sha512-xCMovURClsQZ+TR30icCZj+34Fq1hs0y6YCASD6ZqdRfYRybb5Iadws2WS+w09mGM/kf9xyA5FCdJQGcgcraSA==", + "dev": true, + "dependencies": { + "connection-parse": "0.0.x", + "simple-lru-cache": "0.0.x" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -10474,6 +10491,15 @@ "node": ">=8" } }, + "node_modules/jackpot": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/jackpot/-/jackpot-0.0.6.tgz", + "integrity": "sha512-rbWXX+A9ooq03/dfavLg9OXQ8YB57Wa7PY5c4LfU3CgFpwEhhl3WyXTQVurkaT7zBM5I9SSOaiLyJ4I0DQmC0g==", + "dev": true, + "dependencies": { + "retry": "0.6.0" + } + }, "node_modules/jackspeak": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.2.tgz", @@ -11063,6 +11089,16 @@ "node": ">= 0.6" } }, + "node_modules/memcached": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/memcached/-/memcached-2.2.2.tgz", + "integrity": "sha512-lHwUmqkT9WdUUgRsAvquO4xsKXYaBd644Orz31tuth+w/BIfFNuJMWwsG7sa7H3XXytaNfPTZ5R/yOG3d9zJMA==", + "dev": true, + "dependencies": { + "hashring": "3.2.x", + "jackpot": ">=0.0.6" + } + }, "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -13009,6 +13045,15 @@ "node": ">=4" } }, + "node_modules/retry": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.6.0.tgz", + "integrity": "sha512-RgncoxLF1GqwAzTZs/K2YpZkWrdIYbXsmesdomi+iPilSzjUyr/wzNIuteoTVaWokzdwZIJ9NHRNQa/RUiOB2g==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -13286,6 +13331,12 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-lru-cache": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/simple-lru-cache/-/simple-lru-cache-0.0.2.tgz", + "integrity": "sha512-uEv/AFO0ADI7d99OHDmh1QfYzQk/izT1vCmu/riQfh7qjBVUUgRT87E5s5h7CxWCA/+YoZerykpEthzVrW3LIw==", + "dev": true + }, "node_modules/sinon": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", From 441c41ccc42c6b37b19615e13c7fabaec3731d13 Mon Sep 17 00:00:00 2001 From: sumitsuthar Date: Wed, 22 Nov 2023 12:37:56 +0530 Subject: [PATCH 7/8] fix: Memcached event schema update (#134) --- .../hooks/memcached/nr-memcached.js | 34 +++++++++++++++--- .../nr-memcached.test.js | 35 ++++++++----------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/lib/instrumentation-security/hooks/memcached/nr-memcached.js b/lib/instrumentation-security/hooks/memcached/nr-memcached.js index 7f2959fa..2409a9eb 100644 --- a/lib/instrumentation-security/hooks/memcached/nr-memcached.js +++ b/lib/instrumentation-security/hooks/memcached/nr-memcached.js @@ -13,6 +13,29 @@ const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants'); const logger = API.getLogger(); const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants'); +const OperationMap = { + 'set': 'write', + 'get': 'read', + 'gets': 'read', + 'replace': 'update', + 'add': 'write', + 'cas': 'write', + 'append': 'write', + 'prepend': 'write', + 'delete': 'delete', + 'touch': 'update', + 'incr': 'update', + 'decr': 'update', + 'version': 'read', + 'flush_all': 'delete', + 'stats': 'read', + 'stats settings': 'read', + 'stats slabs': 'read', + 'stats items': 'read', + 'stats cachedump': 'read' + +} + module.exports = function initialize(shim, memcached, moduleName, additionalMod) { logger.info('Instrumenting ' + moduleName) let utils; @@ -31,12 +54,14 @@ function prepareSecurityArgs(args) { let parameters = Object.create(null); try { if (obj) { - parameters.type = obj['type']; - parameters.key = obj['key']; + parameters.mode = obj['type']; + let params = []; + params.push(obj['key'].toString()); if (obj['value']) { - parameters.value = JSON.parse(obj['value']); + params.push(obj['value']); } - parameters.command = obj['command']; + parameters.arguments = params; + parameters.type = OperationMap[parameters.mode]? OperationMap[parameters.mode]: 'unknown'; } } catch (error) { logger.debug("Error in preparing memcached parameters:", error); @@ -56,7 +81,6 @@ function validateArgHook(shim, mod, method) { return function queryWrapper() { let params = prepareSecurityArgs(arguments); shim.interceptedArgs = params; - shim.params = params; const request = requestManager.getRequest(shim); if (request) { diff --git a/test/instrumentation-security/nr-memcached.test.js b/test/instrumentation-security/nr-memcached.test.js index 00be5aeb..290582cc 100755 --- a/test/instrumentation-security/nr-memcached.test.js +++ b/test/instrumentation-security/nr-memcached.test.js @@ -39,62 +39,57 @@ test('memcached', (t) => { t.test('set', (t) => { memcached.set("hello", 1, 100, function (err, result) { - t.equal('set', shim.interceptedArgs.type); - t.equal('hello', shim.interceptedArgs.key); - t.equal(1, shim.interceptedArgs.value); + t.equal('set', shim.interceptedArgs.mode); + t.equal('hello', shim.interceptedArgs.arguments[0]); t.end(); }); }) t.test('get', (t) => { memcached.get("hello", function (err, result) { - t.equal('get', shim.interceptedArgs.type); - t.equal('hello', shim.interceptedArgs.key); + t.equal('get', shim.interceptedArgs.mode); + t.equal('hello', shim.interceptedArgs.arguments[0]); t.end(); }); }) t.test('get_multi', (t) => { memcached.get(["hello", "hello_json"], function (err, result) { - t.equal('get', shim.interceptedArgs.type); - t.equal(2, shim.interceptedArgs.key.length); + t.equal('get', shim.interceptedArgs.mode); + t.equal(1, shim.interceptedArgs.arguments.length); t.end(); }); }) t.test('delete', (t) => { memcached.del('foo', function (err, result) { - t.equal('delete', shim.interceptedArgs.type); - t.equal('foo', shim.interceptedArgs.key); + t.equal('delete', shim.interceptedArgs.mode); + t.equal('foo', shim.interceptedArgs.arguments[0]); t.end(); }); }) t.test('set_json', (t) => { memcached.set("hello_json", { javascript: 'objects', are: ['no', 'problem', 4], nMemcached: true }, 10000, function (err, result) { - t.equal('set', shim.interceptedArgs.type); - t.equal('hello_json', shim.interceptedArgs.key); - t.equal("objects", shim.interceptedArgs.value.javascript); - t.equal(true, shim.interceptedArgs.value.nMemcached); - t.equal(3, shim.interceptedArgs.value.are.length); + t.equal('set', shim.interceptedArgs.mode); + t.equal('hello_json', shim.interceptedArgs.arguments[0]); t.end(); }); }) t.test('increment', (t) => { memcached.increment( "hello", 1, function( err, result ){ - t.equal('incr', shim.interceptedArgs.type); - t.equal('hello', shim.interceptedArgs.key); - t.equal(1, shim.interceptedArgs.value); + t.equal('incr', shim.interceptedArgs.mode); + t.equal('hello', shim.interceptedArgs.arguments[0]); + t.equal(1, shim.interceptedArgs.arguments[1]); t.end(); }); }) t.test('decrement', (t) => { memcached.decr( "hello", 1, function( err, result ){ - t.equal('decr', shim.interceptedArgs.type); - t.equal('hello', shim.interceptedArgs.key); - t.equal(1, shim.interceptedArgs.value); + t.equal('decr', shim.interceptedArgs.mode); + t.equal('hello', shim.interceptedArgs.arguments[0]); t.end(); }); }) From 01a5cff7dad518be1fc5037e5762538ef1ad1db3 Mon Sep 17 00:00:00 2001 From: Sumit Suthar Date: Wed, 22 Nov 2023 13:25:25 +0530 Subject: [PATCH 8/8] updated caseType for memcached --- lib/instrumentation-security/core/event-constants.js | 3 ++- lib/instrumentation-security/hooks/memcached/nr-memcached.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/instrumentation-security/core/event-constants.js b/lib/instrumentation-security/core/event-constants.js index 315e47ba..dbd91a0b 100644 --- a/lib/instrumentation-security/core/event-constants.js +++ b/lib/instrumentation-security/core/event-constants.js @@ -20,7 +20,8 @@ const EVENT_TYPE = { UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT', REFLECTED_XSS: 'REFLECTED_XSS', XPATH: 'XPATH', - LDAP: 'LDAP' + LDAP: 'LDAP', + CACHING_DATA_STORE: 'CACHING_DATA_STORE' } const EVENT_CATEGORY = { MYSQL: 'MYSQL', diff --git a/lib/instrumentation-security/hooks/memcached/nr-memcached.js b/lib/instrumentation-security/hooks/memcached/nr-memcached.js index 2409a9eb..7cc9b1f4 100644 --- a/lib/instrumentation-security/hooks/memcached/nr-memcached.js +++ b/lib/instrumentation-security/hooks/memcached/nr-memcached.js @@ -86,7 +86,7 @@ function validateArgHook(shim, mod, method) { if (request) { const traceObject = secUtils.getTraceObject(shim); traceObject.sourceMethod = method; - const secMetadata = securityMetaData.getSecurityMetaData(request, params, traceObject, secUtils.getExecutionId(), EVENT_TYPE.DB_COMMAND, EVENT_CATEGORY.MEMCACHED) + const secMetadata = securityMetaData.getSecurityMetaData(request, params, traceObject, secUtils.getExecutionId(), EVENT_TYPE.CACHING_DATA_STORE, EVENT_CATEGORY.MEMCACHED) const secEvent = API.generateSecEvent(secMetadata); this.secEvent = secEvent; API.sendEvent(secEvent);