Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 76 additions & 7 deletions packages/mongodb-redact/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,82 @@ describe('mongodb-redact', function () {
expect(res).to.equal('<url>');
});

it('should redact MongoDB connection URIs', function () {
let res = redact(
'mongodb://db1.example.net,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000',
);
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://localhost,localhost:27018,localhost:27019');
expect(res).to.equal('<mongodb uri>');
describe('MongoDB connection strings', function () {
it('should redact MongoDB connection URIs', function () {
let res = redact(
'mongodb://db1.example.net,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000',
);
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://localhost,localhost:27018,localhost:27019');
expect(res).to.equal('<mongodb uri>');
});

it('should redact MongoDB URIs with credentials', function () {
let res = redact('mongodb://user:password@localhost:27017/admin');
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://admin:[email protected]/mydb');
expect(res).to.equal('<mongodb uri>');
});

it('should redact MongoDB URIs with special characters in usernames and passwords', function () {
let res = redact('mongodb://user:p%40ss!word@localhost:27017/');
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://ad!min:te%st#[email protected]:27017/');
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://!user:my%20pass@localhost/mydb');
expect(res).to.equal('<mongodb uri>');
res = redact(
'mongodb://user:p&ssw!rd#[email protected]:27017/db?authSource=admin',
);
expect(res).to.equal('<mongodb uri>');
});

it('should redact MongoDB SRV URIs', function () {
let res = redact(
'mongodb+srv://user:[email protected]/test',
);
expect(res).to.equal('<mongodb uri>');
res = redact(
'mongodb+srv://admin:[email protected]/mydb?retryWrites=true',
);
expect(res).to.equal('<mongodb uri>');
});

it('should redact MongoDB URIs with query parameters', function () {
let res = redact(
'mongodb://localhost:27017/mydb?ssl=true&replicaSet=rs0',
);
expect(res).to.equal('<mongodb uri>');
res = redact(
'mongodb://user:[email protected]/db?authSource=admin&readPreference=primary',
);
expect(res).to.equal('<mongodb uri>');
});

it('should redact MongoDB URIs with replica sets', function () {
let res = redact(
'mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplSet',
);
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://user:pass@host1,host2,host3/db?replicaSet=rs0');
expect(res).to.equal('<mongodb uri>');
});

it('should redact MongoDB URIs with IP addresses', function () {
let res = redact('mongodb://192.168.1.100:27017/mydb');
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://user:[email protected]:27017/admin');
expect(res).to.equal('<mongodb uri>');
});

it('should redact simple MongoDB URIs', function () {
let res = redact('mongodb://localhost');
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://localhost:27017');
expect(res).to.equal('<mongodb uri>');
res = redact('mongodb://localhost/mydb');
expect(res).to.equal('<mongodb uri>');
});
});

it('should redact general linux/unix user paths', function () {
Expand Down
9 changes: 3 additions & 6 deletions packages/mongodb-redact/src/regexes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export const regexes = [
'$1<email>$6',
],

// MongoDB connection strings
[/mongodb(?:\+srv)?:\/\/\S+/gim, '<mongodb uri>'],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured we might as well be very liberal and match any mongodb(+srv):// until we see a whitespace.

Alternatively, we can just redact the credentials by using mongodb-connection-url? I'm currently sticking to this as I guess the idea here is that URI itself is sensitive.


// IP addresses
[
/((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])/gm,
Expand All @@ -39,12 +42,6 @@ export const regexes = [
'<url>',
],

// MongoDB connection strings
[
/(mongodb:\/\/)(www\.)?[-a-zA-Z0-9@:%._+~#=,]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/gim,
'<mongodb uri>',
],

// Compass Schema URL fragments
[/#schema\/\w+\.\w+/, '#schema/<namespace>'],
] as const;
51 changes: 51 additions & 0 deletions packages/mongodb-redact/src/secrets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,55 @@ describe('dictionary-based secret redaction', function () {
usr: '<user>',
});
});

describe('special characters in passwords', function () {
it('redacts passwords at start, end, or entire string', function () {
expect(
redact('!start is pwd', [{ value: '!start', kind: 'password' }]),
).to.equal('<password> is pwd');

expect(
redact('pwd is end!', [{ value: 'end!', kind: 'password' }]),
).to.equal('pwd is <password>');

expect(
redact('The password is !@#$%', [{ value: '!@#$%', kind: 'password' }]),
).to.equal('The password is <password>');
});

it('redacts a special-character only connection string', function () {
const secret = '!#!!';
const content = 'Connection string: mongodb://!!!#:!#!!@localhost:27017/';

const redacted = redact(content, [{ value: secret, kind: 'password' }]);

expect(redacted).to.equal('Connection string: <mongodb uri>');
});

for (const { char, password } of [
{ char: '.', password: 'test.pass' },
{ char: '*', password: 'test*pass' },
{ char: '+', password: 'test+pass' },
{ char: '?', password: 'test?pass' },
{ char: '[', password: 'test[123]' },
{ char: '(', password: 'test(abc)' },
{ char: '|', password: 'test|pass' },
{ char: '\\', password: 'test\\pass' },
{ char: '^', password: '^test123' },
{ char: '$', password: 'test$123' },
{ char: '@', password: 'user@123' },
{ char: '#', password: 'pass#word' },
{ char: '%', password: 'test%20' },
{ char: '&', password: 'rock&roll' },
{ char: 'լավ', password: 'լավ' },
]) {
it(`redacts passwords with ${char}`, function () {
const content = `pwd: ${password} end`;
const redacted = redact(content, [
{ value: password, kind: 'password' },
]);
expect(redacted).to.equal('pwd: <password> end');
});
}
});
});
5 changes: 4 additions & 1 deletion packages/mongodb-redact/src/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export function redactSecretsOnString<T extends string>(
);
}

const regex = new RegExp(`\\b${escape(value)}\\b`, 'g');
// Escape the value for use in regex and use negative lookahead/lookbehind
// to match secrets not surrounded by word characters
const escapedValue = escape(value);
const regex = new RegExp(`(?<!\\w)${escapedValue}(?!\\w)`, 'g');
result = result.replace(regex, `<${kind}>`) as T;
}

Expand Down
Loading