diff --git a/packages/mongodb-redact/src/index.spec.ts b/packages/mongodb-redact/src/index.spec.ts index ca48dbdd..02566a5f 100644 --- a/packages/mongodb-redact/src/index.spec.ts +++ b/packages/mongodb-redact/src/index.spec.ts @@ -161,13 +161,82 @@ describe('mongodb-redact', function () { expect(res).to.equal(''); }); - 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(''); - res = redact('mongodb://localhost,localhost:27018,localhost:27019'); - expect(res).to.equal(''); + 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(''); + res = redact('mongodb://localhost,localhost:27018,localhost:27019'); + expect(res).to.equal(''); + }); + + it('should redact MongoDB URIs with credentials', function () { + let res = redact('mongodb://user:password@localhost:27017/admin'); + expect(res).to.equal(''); + res = redact('mongodb://admin:secret123@db.example.com/mydb'); + expect(res).to.equal(''); + }); + + 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(''); + res = redact('mongodb://ad!min:te%st#123$@db.example.com:27017/'); + expect(res).to.equal(''); + res = redact('mongodb://!user:my%20pass@localhost/mydb'); + expect(res).to.equal(''); + res = redact( + 'mongodb://user:p&ssw!rd#123@host.com:27017/db?authSource=admin', + ); + expect(res).to.equal(''); + }); + + it('should redact MongoDB SRV URIs', function () { + let res = redact( + 'mongodb+srv://user:password@cluster0.example.com/test', + ); + expect(res).to.equal(''); + res = redact( + 'mongodb+srv://admin:secret@mycluster.mongodb.net/mydb?retryWrites=true', + ); + expect(res).to.equal(''); + }); + + it('should redact MongoDB URIs with query parameters', function () { + let res = redact( + 'mongodb://localhost:27017/mydb?ssl=true&replicaSet=rs0', + ); + expect(res).to.equal(''); + res = redact( + 'mongodb://user:pass@host.com/db?authSource=admin&readPreference=primary', + ); + expect(res).to.equal(''); + }); + + 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(''); + res = redact('mongodb://user:pass@host1,host2,host3/db?replicaSet=rs0'); + expect(res).to.equal(''); + }); + + it('should redact MongoDB URIs with IP addresses', function () { + let res = redact('mongodb://192.168.1.100:27017/mydb'); + expect(res).to.equal(''); + res = redact('mongodb://user:password@10.0.0.5:27017/admin'); + expect(res).to.equal(''); + }); + + it('should redact simple MongoDB URIs', function () { + let res = redact('mongodb://localhost'); + expect(res).to.equal(''); + res = redact('mongodb://localhost:27017'); + expect(res).to.equal(''); + res = redact('mongodb://localhost/mydb'); + expect(res).to.equal(''); + }); }); it('should redact general linux/unix user paths', function () { diff --git a/packages/mongodb-redact/src/regexes.ts b/packages/mongodb-redact/src/regexes.ts index c4abff94..3266c7a2 100644 --- a/packages/mongodb-redact/src/regexes.ts +++ b/packages/mongodb-redact/src/regexes.ts @@ -27,6 +27,9 @@ export const regexes = [ '$1$6', ], + // MongoDB connection strings + [/mongodb(?:\+srv)?:\/\/\S+/gim, ''], + // 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, @@ -39,12 +42,6 @@ export const regexes = [ '', ], - // MongoDB connection strings - [ - /(mongodb:\/\/)(www\.)?[-a-zA-Z0-9@:%._+~#=,]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/gim, - '', - ], - // Compass Schema URL fragments [/#schema\/\w+\.\w+/, '#schema/'], ] as const; diff --git a/packages/mongodb-redact/src/secrets.spec.ts b/packages/mongodb-redact/src/secrets.spec.ts index 3af8b617..ed2ae16e 100644 --- a/packages/mongodb-redact/src/secrets.spec.ts +++ b/packages/mongodb-redact/src/secrets.spec.ts @@ -93,4 +93,55 @@ describe('dictionary-based secret redaction', function () { usr: '', }); }); + + 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(' is pwd'); + + expect( + redact('pwd is end!', [{ value: 'end!', kind: 'password' }]), + ).to.equal('pwd is '); + + expect( + redact('The password is !@#$%', [{ value: '!@#$%', kind: 'password' }]), + ).to.equal('The password is '); + }); + + 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: '); + }); + + 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: end'); + }); + } + }); }); diff --git a/packages/mongodb-redact/src/secrets.ts b/packages/mongodb-redact/src/secrets.ts index 4c0a519e..fe4aa6b8 100644 --- a/packages/mongodb-redact/src/secrets.ts +++ b/packages/mongodb-redact/src/secrets.ts @@ -32,7 +32,10 @@ export function redactSecretsOnString( ); } - 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(`(?`) as T; }