From 0709637f12dabe6f6c44af71a4fd2e67549857ae Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 9 Jan 2025 20:38:17 +0100 Subject: [PATCH] chore(mongodb-redact): remove lodash usage MONGOSH-1975 Just ran into this while writing a test for mongosh that ensures that `shell-api` is really as runtime-independent as we expect. --- package-lock.json | 4 --- packages/mongodb-redact/package.json | 3 -- packages/mongodb-redact/src/index.spec.ts | 40 +++++++++++++++++++++++ packages/mongodb-redact/src/index.ts | 31 +++++++++++++++--- 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2537417..275d5a51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26563,9 +26563,6 @@ "packages/mongodb-redact": { "version": "1.1.4", "license": "Apache-2.0", - "dependencies": { - "lodash": "^4.17.21" - }, "devDependencies": { "@mongodb-js/eslint-config-devtools": "0.9.10", "@mongodb-js/mocha-config-devtools": "^1.0.4", @@ -43539,7 +43536,6 @@ "depcheck": "^1.4.1", "eslint": "^7.25.0", "gen-esm-wrapper": "^1.1.0", - "lodash": "^4.17.21", "mocha": "^8.4.0", "nyc": "^15.1.0", "prettier": "^2.3.2", diff --git a/packages/mongodb-redact/package.json b/packages/mongodb-redact/package.json index e518adce..9b2c3fce 100644 --- a/packages/mongodb-redact/package.json +++ b/packages/mongodb-redact/package.json @@ -45,9 +45,6 @@ "test-ci": "npm run test-cov", "reformat": "npm run prettier -- --write ." }, - "dependencies": { - "lodash": "^4.17.21" - }, "devDependencies": { "@mongodb-js/eslint-config-devtools": "0.9.10", "@mongodb-js/mocha-config-devtools": "^1.0.4", diff --git a/packages/mongodb-redact/src/index.spec.ts b/packages/mongodb-redact/src/index.spec.ts index 0ed3b93e..fc3fed4e 100644 --- a/packages/mongodb-redact/src/index.spec.ts +++ b/packages/mongodb-redact/src/index.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import redact from './'; +import { runInNewContext } from 'vm'; /* eslint no-multi-str:0 */ const PRIVATE_KEY = @@ -195,5 +196,44 @@ describe('mongodb-redact', function () { }, }); }); + + it('should work on objects with a null prototype', function () { + const res = redact({ + obj: { + path: '/Users/thomas/something.txt', + __proto__: null, + }, + }); + expect(res).to.deep.equal({ + obj: { + path: '/Users//something.txt', + }, + }); + }); + + it('should ignore class instances', function () { + const obj = new (class Foo { + path = '/Users/thomas/something.txt'; + })(); + const res = redact({ + obj, + }); + expect(res.obj).to.equal(obj); + }); + + it('should work on objects from another vm context', function () { + const res = redact( + runInNewContext(`({ + obj: { + path: '/Users/thomas/something.txt', + }, + })`) + ); + expect(res).to.deep.equal({ + obj: { + path: '/Users//something.txt', + }, + }); + }); }); }); diff --git a/packages/mongodb-redact/src/index.ts b/packages/mongodb-redact/src/index.ts index 7da4311a..f1331cc6 100644 --- a/packages/mongodb-redact/src/index.ts +++ b/packages/mongodb-redact/src/index.ts @@ -1,14 +1,35 @@ -import _ from 'lodash'; import { regexes } from './regexes'; +const plainObjectTag = Object.prototype.toString.call({}); +function isPlainObject(val: unknown): val is object { + if ( + typeof val !== 'object' || + !val || + Object.prototype.toString.call(val) !== plainObjectTag + ) { + return false; + } + const proto = Object.getPrototypeOf(val); + if (proto === null) return true; + if (!Object.prototype.hasOwnProperty.call(proto, 'constructor')) return false; + const ctor = proto.constructor; + if (typeof ctor !== 'function') return ctor; + // `ctor === Object` but this works across contexts + // (Object is special because Object.__proto__.__proto__ === Object.prototype), + const ctorPrototype = Object.getPrototypeOf(ctor); + return Object.getPrototypeOf(ctorPrototype) === ctor.prototype; +} + export function redact(message: T): T { - if (_.isPlainObject(message)) { + if (isPlainObject(message)) { // recursively walk through all values of an object - return _.mapValues(message as any, redact); + return Object.fromEntries( + Object.entries(message).map(([key, value]) => [key, redact(value)]) + ) as T; } - if (_.isArray(message)) { + if (Array.isArray(message)) { // walk through array and redact each value - return _.map(message, redact) as T; + return message.map(redact) as T; } if (typeof message !== 'string') { // all non-string types can be safely returned