Skip to content

Commit 7209ba0

Browse files
committed
feat: add shouldRedactCommand
1 parent 80c0810 commit 7209ba0

File tree

4 files changed

+157
-7
lines changed

4 files changed

+157
-7
lines changed

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

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,82 @@ describe('mongodb-redact', function () {
161161
expect(res).to.equal('<url>');
162162
});
163163

164-
it('should redact MongoDB connection URIs', function () {
165-
let res = redact(
166-
'mongodb://db1.example.net,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000',
167-
);
168-
expect(res).to.equal('<mongodb uri>');
169-
res = redact('mongodb://localhost,localhost:27018,localhost:27019');
170-
expect(res).to.equal('<mongodb uri>');
164+
describe('MongoDB connection strings', function () {
165+
it('should redact MongoDB connection URIs', function () {
166+
let res = redact(
167+
'mongodb://db1.example.net,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000',
168+
);
169+
expect(res).to.equal('<mongodb uri>');
170+
res = redact('mongodb://localhost,localhost:27018,localhost:27019');
171+
expect(res).to.equal('<mongodb uri>');
172+
});
173+
174+
it('should redact MongoDB URIs with credentials', function () {
175+
let res = redact('mongodb://user:password@localhost:27017/admin');
176+
expect(res).to.equal('<mongodb uri>');
177+
res = redact('mongodb://admin:[email protected]/mydb');
178+
expect(res).to.equal('<mongodb uri>');
179+
});
180+
181+
it('should redact MongoDB URIs with special characters in usernames and passwords', function () {
182+
let res = redact('mongodb://user:p%40ss!word@localhost:27017/');
183+
expect(res).to.equal('<mongodb uri>');
184+
res = redact('mongodb://ad!min:te%st#[email protected]:27017/');
185+
expect(res).to.equal('<mongodb uri>');
186+
res = redact('mongodb://!user:my%20pass@localhost/mydb');
187+
expect(res).to.equal('<mongodb uri>');
188+
res = redact(
189+
'mongodb://user:p&ssw!rd#[email protected]:27017/db?authSource=admin',
190+
);
191+
expect(res).to.equal('<mongodb uri>');
192+
});
193+
194+
it('should redact MongoDB SRV URIs', function () {
195+
let res = redact(
196+
'mongodb+srv://user:[email protected]/test',
197+
);
198+
expect(res).to.equal('<mongodb uri>');
199+
res = redact(
200+
'mongodb+srv://admin:[email protected]/mydb?retryWrites=true',
201+
);
202+
expect(res).to.equal('<mongodb uri>');
203+
});
204+
205+
it('should redact MongoDB URIs with query parameters', function () {
206+
let res = redact(
207+
'mongodb://localhost:27017/mydb?ssl=true&replicaSet=rs0',
208+
);
209+
expect(res).to.equal('<mongodb uri>');
210+
res = redact(
211+
'mongodb://user:[email protected]/db?authSource=admin&readPreference=primary',
212+
);
213+
expect(res).to.equal('<mongodb uri>');
214+
});
215+
216+
it('should redact MongoDB URIs with replica sets', function () {
217+
let res = redact(
218+
'mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplSet',
219+
);
220+
expect(res).to.equal('<mongodb uri>');
221+
res = redact('mongodb://user:pass@host1,host2,host3/db?replicaSet=rs0');
222+
expect(res).to.equal('<mongodb uri>');
223+
});
224+
225+
it('should redact MongoDB URIs with IP addresses', function () {
226+
let res = redact('mongodb://192.168.1.100:27017/mydb');
227+
expect(res).to.equal('<mongodb uri>');
228+
res = redact('mongodb://user:[email protected]:27017/admin');
229+
expect(res).to.equal('<mongodb uri>');
230+
});
231+
232+
it('should redact simple MongoDB URIs', function () {
233+
let res = redact('mongodb://localhost');
234+
expect(res).to.equal('<mongodb uri>');
235+
res = redact('mongodb://localhost:27017');
236+
expect(res).to.equal('<mongodb uri>');
237+
res = redact('mongodb://localhost/mydb');
238+
expect(res).to.equal('<mongodb uri>');
239+
});
171240
});
172241

173242
it('should redact general linux/unix user paths', function () {

packages/mongodb-redact/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,7 @@ export function redact<T>(
4646
return message;
4747
}
4848

49+
export { shouldRedactCommand } from './should-redact-command';
50+
4951
export default redact;
5052
export type { Secret } from './secrets';
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { expect } from 'chai';
2+
import { shouldRedactCommand } from '.';
3+
4+
describe('shouldRedactCommand', function () {
5+
describe('shouldRedactCommand', function () {
6+
it('returns true for createUser commands', function () {
7+
expect(shouldRedactCommand('db.createUser({ user: "test" })')).to.be.true;
8+
});
9+
10+
it('returns true for auth commands', function () {
11+
expect(shouldRedactCommand('db.auth("user", "pass")')).to.be.true;
12+
});
13+
14+
it('returns true for updateUser commands', function () {
15+
expect(shouldRedactCommand('db.updateUser("user", { roles: [] })')).to.be
16+
.true;
17+
});
18+
19+
it('returns true for changeUserPassword commands', function () {
20+
expect(shouldRedactCommand('db.changeUserPassword("user", "newpass")')).to
21+
.be.true;
22+
});
23+
24+
it('returns true for connect commands', function () {
25+
expect(shouldRedactCommand('db = connect("mongodb://localhost")')).to.be
26+
.true;
27+
});
28+
29+
it('returns true for Mongo constructor', function () {
30+
expect(shouldRedactCommand('new Mongo("mongodb://localhost")')).to.be
31+
.true;
32+
});
33+
34+
it('returns false for non-sensitive commands', function () {
35+
expect(shouldRedactCommand('db.collection.find()')).to.be.false;
36+
});
37+
38+
it('returns false for partial words like "authentication"', function () {
39+
// The \b (word boundary) should prevent matching "auth" within "authentication"
40+
expect(shouldRedactCommand('db.collection.find({authentication: true})'))
41+
.to.be.false;
42+
});
43+
44+
it('returns false for getUsers command', function () {
45+
expect(shouldRedactCommand('db.getUsers()')).to.be.false;
46+
});
47+
48+
it('returns false for show commands', function () {
49+
expect(shouldRedactCommand('show dbs')).to.be.false;
50+
});
51+
});
52+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Regex pattern for commands that contain sensitive information and should be
3+
* completely removed from history rather than redacted.
4+
*
5+
* These commands typically involve authentication or connection strings with credentials.
6+
*/
7+
const HIDDEN_COMMANDS = String.raw`\b(createUser|auth|updateUser|changeUserPassword|connect|Mongo)\b`;
8+
9+
/**
10+
* Checks if a mongosh command should be redacted because it often contains sensitive information like credentials.
11+
*
12+
* @param input - The command string to check
13+
* @returns true if the command should be hidden/redacted, false otherwise
14+
*
15+
* @example
16+
* ```typescript
17+
* shouldRedactCommand('db.createUser({user: "admin", pwd: "secret"})')
18+
* // Returns: true
19+
*
20+
* shouldRedactCommand('db.getUsers()')
21+
* // Returns: false
22+
* ```
23+
*/
24+
export function shouldRedactCommand(input: string): boolean {
25+
const hiddenCommands = new RegExp(HIDDEN_COMMANDS, 'g');
26+
return hiddenCommands.test(input);
27+
}

0 commit comments

Comments
 (0)