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
2 changes: 2 additions & 0 deletions package-lock.json

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

10 changes: 5 additions & 5 deletions packages/devtools-connect/src/log-hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function hookLogger(
emitter: ConnectLogEmitter,
log: MongoLogWriter,
contextPrefix: string,
redactURICredentials: (uri: string) => string,
redactConnectionString: (uri: string) => string,
): void {
oidcHookLogger(emitter, log, contextPrefix);
proxyHookLogger(emitter, log, contextPrefix);
Expand All @@ -44,7 +44,7 @@ export function hookLogger(
'Initiating connection attempt',
{
...ev,
uri: redactURICredentials(ev.uri),
uri: redactConnectionString(ev.uri),
},
);
},
Expand Down Expand Up @@ -119,7 +119,7 @@ export function hookLogger(
`${contextPrefix}-connect`,
'Resolving SRV record failed',
{
from: redactURICredentials(ev.from),
from: redactConnectionString(ev.from),
error: ev.error?.message,
duringLoad: ev.duringLoad,
resolutionDetails: ev.resolutionDetails,
Expand All @@ -138,8 +138,8 @@ export function hookLogger(
`${contextPrefix}-connect`,
'Resolving SRV record succeeded',
{
from: redactURICredentials(ev.from),
to: redactURICredentials(ev.to),
from: redactConnectionString(ev.from),
to: redactConnectionString(ev.to),
resolutionDetails: ev.resolutionDetails,
durationMs: ev.durationMs,
},
Expand Down
3 changes: 2 additions & 1 deletion packages/mongodb-redact/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"typescript": "^5.0.4"
},
"dependencies": {
"regexp.escape": "^2.0.1"
"regexp.escape": "^2.0.1",
"mongodb-connection-string-url": "^3.0.1 || ^7.0.0"
}
}
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
3 changes: 3 additions & 0 deletions packages/mongodb-redact/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@ export function redact<T>(
return message;
}

export { shouldRedactCommand } from './should-redact-command';
export { redactConnectionString } from './redact-connection-string';

export default redact;
export type { Secret } from './secrets';
173 changes: 173 additions & 0 deletions packages/mongodb-redact/src/redact-connection-string.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { expect } from 'chai';
import { redactConnectionString } from './redact-connection-string';
import redact from '.';

describe('redactConnectionString', function () {
const testCases: Array<{
description: string;
input: string;
expected: string;
}> = [
{
description: 'should redact username and password',
input: 'mongodb://user:password@localhost:27017/admin',
expected: 'mongodb://<credentials>@localhost:27017/admin',
},
{
description: 'should redact only username when no password',
input: 'mongodb://user@localhost:27017/admin',
expected: 'mongodb://<credentials>@localhost:27017/admin',
},
{
description: 'should redact credentials in SRV URIs',
input: 'mongodb+srv://admin:[email protected]/test',
expected: 'mongodb+srv://<credentials>@cluster0.example.com/test',
},
{
description: 'should redact passwords with ! character',
input: 'mongodb://user:p@ss!word@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description: 'should redact passwords with # character',
input: 'mongodb://admin:test#[email protected]:27017/',
expected: 'mongodb://<credentials>@db.example.com:27017/',
},
{
description: 'should redact passwords with $ character',
input: 'mongodb://user:price$100@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description: 'should redact passwords with % character',
input: 'mongodb://user:test%pass@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description: 'should redact passwords with & character',
input: 'mongodb://user:rock&roll@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description: 'should redact URL-encoded passwords',
input: 'mongodb://user:my%20password@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description:
'should redact complex passwords with multiple special characters',
input: 'mongodb://user:p&ssw!rd#[email protected]:27017/db?authSource=admin',
expected: 'mongodb://<credentials>@host.com:27017/db?authSource=admin',
},
{
description: 'should redact usernames with special characters',
input: 'mongodb://us!er:password@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description: 'should return URI unchanged when no credentials',
input: 'mongodb://localhost:27017/admin',
expected: 'mongodb://localhost:27017/admin',
},
{
description: 'should handle simple localhost URI',
input: 'mongodb://localhost',
expected: 'mongodb://localhost/',
},
{
description: 'should handle URI with database',
input: 'mongodb://localhost/mydb',
expected: 'mongodb://localhost/mydb',
},
{
description: 'should handle URI with query parameters',
input: 'mongodb://localhost:27017/mydb?ssl=true&replicaSet=rs0',
expected: 'mongodb://localhost:27017/mydb?ssl=true&replicaSet=rs0',
},
// URIs with replica sets
{
description: 'should redact credentials in replica set URIs',
input:
'mongodb://user:pass@host1:27017,host2:27017,host3:27017/db?replicaSet=rs0',
expected:
'mongodb://<credentials>@host1:27017,host2:27017,host3:27017/db?replicaSet=rs0',
},
{
description: 'should handle replica set URIs without credentials',
input:
'mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplSet',
expected:
'mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplSet',
},
// URIs with IP addresses
{
description: 'should redact credentials with IP address host',
input: 'mongodb://user:[email protected]:27017/mydb',
expected: 'mongodb://<credentials>@192.168.1.100:27017/mydb',
},
{
description: 'should handle IP address URIs without credentials',
input: 'mongodb://10.0.0.5:27017/admin',
expected: 'mongodb://10.0.0.5:27017/admin',
},
// SRV URIs
{
description: 'should handle SRV URIs without credentials',
input: 'mongodb+srv://cluster0.example.com/test',
expected: 'mongodb+srv://cluster0.example.com/test',
},
// URIs with query parameters
{
description: 'should redact credentials and preserve query parameters',
input:
'mongodb://user:[email protected]/db?authSource=admin&readPreference=primary',
expected:
'mongodb://<credentials>@host.com/db?authSource=admin&readPreference=primary',
},
{
description: 'should handle URIs with SSL options',
input:
'mongodb://admin:secret@localhost:27017/mydb?ssl=true&tlsAllowInvalidCertificates=true',
expected:
'mongodb://<credentials>@localhost:27017/mydb?ssl=true&tlsAllowInvalidCertificates=true',
},
{
description: 'should redact credentials in SRV URIs with query params',
input:
'mongodb+srv://admin:[email protected]/mydb?retryWrites=true',
expected:
'mongodb+srv://<credentials>@mycluster.mongodb.net/mydb?retryWrites=true',
},
// Edge cases
{
description: 'should handle empty password',
input: 'mongodb://user:@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description:
'should handle password with only special characters (URL-encoded)',
input: 'mongodb://user:%21%40%23%24%25@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description: 'should handle very long passwords',
input: `mongodb://user:${'a'.repeat(100)}@localhost:27017/`,
expected: 'mongodb://<credentials>@localhost:27017/',
},
{
description: 'should handle international characters in password',
input: 'mongodb://user:пароль@localhost:27017/',
expected: 'mongodb://<credentials>@localhost:27017/',
},
];

testCases.forEach(({ description, input, expected }) => {
it(description, function () {
const result = redactConnectionString(input);
expect(result).to.equal(expected);

expect(redact(input)).to.equal('<mongodb uri>');
});
});
});
5 changes: 5 additions & 0 deletions packages/mongodb-redact/src/redact-connection-string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redactConnectionString as redactConnectionStringImpl } from 'mongodb-connection-string-url';

export function redactConnectionString(uri: string): string {
return redactConnectionStringImpl(uri);
}
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>'],

// 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;
Loading
Loading