Skip to content

Commit

Permalink
chore(mongodb-redact): remove lodash usage MONGOSH-1975
Browse files Browse the repository at this point in the history
Just ran into this while writing a test for mongosh that ensures
that `shell-api` is really as runtime-independent as we expect.
  • Loading branch information
addaleax committed Jan 9, 2025
1 parent 6283bab commit 0709637
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 12 deletions.
4 changes: 0 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions packages/mongodb-redact/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
40 changes: 40 additions & 0 deletions packages/mongodb-redact/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect } from 'chai';
import redact from './';
import { runInNewContext } from 'vm';

/* eslint no-multi-str:0 */
const PRIVATE_KEY =
Expand Down Expand Up @@ -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/<user>/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/<user>/something.txt',
},
});
});
});
});
31 changes: 26 additions & 5 deletions packages/mongodb-redact/src/index.ts
Original file line number Diff line number Diff line change
@@ -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<T>(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
Expand Down

0 comments on commit 0709637

Please sign in to comment.