Skip to content

Commit 02f6dd2

Browse files
committed
chore: refactor to reuse the iteration, instead of iterating twice
1 parent d163d6b commit 02f6dd2

File tree

3 files changed

+35
-47
lines changed

3 files changed

+35
-47
lines changed

packages/mongodb-redact/src/index.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import { regexes } from './regexes';
22
import { isPlainObject } from './utils';
3-
import { redactSecrets } from './secrets';
3+
import { redactSecretsOnString } from './secrets';
44
import type { Secret } from './secrets';
55

66
export function redact<T>(
77
message: T,
88
secrets: Secret[] | undefined = undefined,
99
): T {
10-
if (secrets) {
11-
message = redactSecrets(message, secrets);
12-
}
13-
1410
if (isPlainObject(message)) {
1511
// recursively walk through all values of an object
16-
return Object.fromEntries(
12+
const newMessage = Object.fromEntries(
1713
Object.entries(message).map(([key, value]) => [
1814
key,
1915
redact(value, secrets),
2016
]),
2117
) as T;
18+
19+
// make sure we inherit the prototype so we don't add new behaviour to the object
20+
// nobody is expecting
21+
return Object.setPrototypeOf(newMessage, Object.getPrototypeOf(message));
2222
}
23+
2324
if (Array.isArray(message)) {
2425
// walk through array and redact each value
2526
return message.map((msg) => redact(msg, secrets)) as T;
@@ -28,6 +29,11 @@ export function redact<T>(
2829
// all non-string types can be safely returned
2930
return message;
3031
}
32+
33+
if (secrets) {
34+
message = redactSecretsOnString(message, secrets);
35+
}
36+
3137
// apply all available regexes to the string
3238
for (const [regex, replacement] of regexes) {
3339
// The type here isn't completely accurate in case `T` is a specific string template
@@ -38,3 +44,4 @@ export function redact<T>(
3844
}
3945

4046
export default redact;
47+
export type { Secret } from './secrets';

packages/mongodb-redact/src/secrets.spec.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,38 @@
11
import { expect } from 'chai';
2-
import { SECRET_KIND, redactSecrets } from './secrets';
2+
import { SECRET_KIND } from './secrets';
33
import type { SecretKind } from './secrets';
4+
import { redact } from './index';
45

5-
describe('secret redaction on a string', function () {
6+
describe('dictionary-based secret redaction', function () {
67
for (const kind of SECRET_KIND) {
78
it(`redacts content of kind '${kind}'`, function () {
89
const secret = '123456';
910
const content = '123456';
1011

11-
const redacted = redactSecrets(content, [{ value: secret, kind: kind }]);
12+
const redacted = redact(content, [{ value: secret, kind: kind }]);
1213

1314
expect(redacted).equal(`<${kind}>`);
1415
});
1516
}
1617

1718
for (const invalidValue of [null, undefined, false, 0]) {
1819
it(`returns itself on an invalid value like ${String(invalidValue)}`, function () {
19-
expect(redactSecrets(invalidValue as any, [])).equal(invalidValue);
20+
expect(redact(invalidValue as any, [])).equal(invalidValue);
2021
});
2122
}
2223

24+
it('redacts multiple coincidences of a secret', function () {
25+
const secret = '123456';
26+
const content = '123456 abc 123456';
27+
28+
const redacted = redact(content, [{ value: secret, kind: 'password' }]);
29+
30+
expect(redacted).to.equal('<password> abc <password>');
31+
});
32+
2333
it('rejects unknown types', function () {
2434
expect(() =>
25-
redactSecrets('some content to redact', [
35+
redact('some content to redact', [
2636
{
2737
value: 'some',
2838
kind: 'invalid' as SecretKind,
@@ -35,9 +45,7 @@ describe('secret redaction on a string', function () {
3545
const secret = '123456';
3646
const content = '.123456.';
3747

38-
const redacted = redactSecrets(content, [
39-
{ value: secret, kind: 'password' },
40-
]);
48+
const redacted = redact(content, [{ value: secret, kind: 'password' }]);
4149

4250
expect(redacted).equal('.<password>.');
4351
});
@@ -46,9 +54,7 @@ describe('secret redaction on a string', function () {
4654
const secret = '123456';
4755
const content = 'abc123456def';
4856

49-
const redacted = redactSecrets(content, [
50-
{ value: secret, kind: 'password' },
51-
]);
57+
const redacted = redact(content, [{ value: secret, kind: 'password' }]);
5258

5359
expect(redacted).equal('abc123456def');
5460
});
@@ -57,9 +63,7 @@ describe('secret redaction on a string', function () {
5763
const secret = '.+';
5864
const content = '.abcdef.';
5965

60-
const redacted = redactSecrets(content, [
61-
{ value: secret, kind: 'password' },
62-
]);
66+
const redacted = redact(content, [{ value: secret, kind: 'password' }]);
6367

6468
expect(redacted).equal('.abcdef.');
6569
});
@@ -68,9 +72,7 @@ describe('secret redaction on a string', function () {
6872
const secret = 'abc';
6973
const content = ['abc', 'cbd'];
7074

71-
const redacted = redactSecrets(content, [
72-
{ value: secret, kind: 'password' },
73-
]);
75+
const redacted = redact(content, [{ value: secret, kind: 'password' }]);
7476

7577
expect(redacted).deep.equal(['<password>', 'cbd']);
7678
});
@@ -81,7 +83,7 @@ describe('secret redaction on a string', function () {
8183

8284
const content = { pwd: pwdSecret, usr: usrSecret };
8385

84-
const redacted = redactSecrets(content, [
86+
const redacted = redact(content, [
8587
{ value: pwdSecret, kind: 'password' },
8688
{ value: usrSecret, kind: 'user' },
8789
]);

packages/mongodb-redact/src/secrets.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export type Secret = {
2222
readonly kind: SecretKind;
2323
};
2424

25-
function redactSecretsOnString<T extends string>(
25+
export function redactSecretsOnString<T extends string>(
2626
content: T,
2727
secrets: Secret[],
2828
): T {
@@ -34,30 +34,9 @@ function redactSecretsOnString<T extends string>(
3434
);
3535
}
3636

37-
const regex = new RegExp(`\\b${escape(value)}\\b`);
37+
const regex = new RegExp(`\\b${escape(value)}\\b`, 'g');
3838
result = result.replace(regex, `<${kind}>`) as T;
3939
}
4040

4141
return result;
4242
}
43-
44-
export function redactSecrets<T>(message: T, secrets: Secret[]): T {
45-
if (isPlainObject(message)) {
46-
return Object.fromEntries(
47-
Object.entries(message).map(([key, value]) => [
48-
key,
49-
redactSecrets(value, secrets),
50-
]),
51-
) as T;
52-
}
53-
54-
if (Array.isArray(message)) {
55-
return message.map((e) => redactSecrets(e, secrets)) as T;
56-
}
57-
58-
if (typeof message === 'string') {
59-
return redactSecretsOnString(message, secrets);
60-
}
61-
62-
return message;
63-
}

0 commit comments

Comments
 (0)