@@ -9,13 +9,13 @@ import type {
9
9
DeleteOptions ,
10
10
MapReduceOptions ,
11
11
ExplainOptions ,
12
+ ServiceProvider ,
12
13
} from '@mongosh/service-provider-core' ;
13
14
import {
14
15
CommonErrors ,
15
16
MongoshInvalidInputError ,
16
17
MongoshUnimplementedError ,
17
18
} from '@mongosh/errors' ;
18
- import crypto from 'crypto' ;
19
19
import type { Database } from './database' ;
20
20
import type { Collection } from './collection' ;
21
21
import type { CursorIterationResult } from './result' ;
@@ -27,8 +27,12 @@ import { shellApiType } from './enums';
27
27
import type { AbstractFiniteCursor } from './abstract-cursor' ;
28
28
import type ChangeStreamCursor from './change-stream-cursor' ;
29
29
import type { BSON , ShellBson } from '@mongosh/shell-bson' ;
30
- import { inspect } from 'util' ;
31
30
import type { MQLPipeline , MQLQuery } from './mql-types' ;
31
+ import type { InspectOptions } from 'util' ;
32
+
33
+ let coreUtilInspect : ( ( obj : any , options : InspectOptions ) => string ) & {
34
+ defaultOptions : InspectOptions ;
35
+ } ;
32
36
33
37
/**
34
38
* Helper method to adapt aggregation pipeline options.
@@ -173,6 +177,35 @@ export function adaptOptions(
173
177
} , additions ) ;
174
178
}
175
179
180
+ async function computeLegacyHexMD5 (
181
+ sp : ServiceProvider ,
182
+ str : string
183
+ ) : Promise < string > {
184
+ const digested = await sp . computeLegacyHexMD5 ?.( str ) ;
185
+ if ( digested ) return digested ;
186
+
187
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
188
+ let crypto : typeof import ( 'crypto' ) ;
189
+ try {
190
+ // Try to dynamically import crypto so that we don't break plain-JS-runtime builds.
191
+ // The Web Crypto API does not provide MD5, which is reasonable for a modern API
192
+ // but means that we cannot use it as a fallback.
193
+ crypto = require ( 'crypto' ) ;
194
+ } catch {
195
+ throw new MongoshUnimplementedError (
196
+ 'Legacy password digest algorithms like SCRAM-SHA-1 are not supported by this instance of mongosh' ,
197
+ CommonErrors . Deprecated
198
+ ) ;
199
+ }
200
+ // NOTE: this code has raised a code scanning alert about the "use of a broken or weak cryptographic algorithm":
201
+ // we inherited this code from `mongo`, and we cannot replace MD5 with a different algorithm, since MD5 is part of the SCRAM-SHA-1 protocol,
202
+ // and the purpose of `passwordDigestor=client` is to improve the security of SCRAM-SHA-1, allowing the creation of new users
203
+ // without the need to communicate their password to the server.
204
+ const hash = crypto . createHash ( 'md5' ) ;
205
+ hash . update ( str ) ;
206
+ return hash . digest ( 'hex' ) ;
207
+ }
208
+
176
209
/**
177
210
* Optionally digest password if passwordDigestor field set to 'client'. If it's false,
178
211
* then hash the password.
@@ -181,11 +214,12 @@ export function adaptOptions(
181
214
* @param passwordDigestor
182
215
* @param {Object } command
183
216
*/
184
- export function processDigestPassword (
217
+ export async function processDigestPassword (
185
218
username : string ,
186
219
passwordDigestor : 'server' | 'client' ,
187
- command : { pwd : string }
188
- ) : { digestPassword ?: boolean ; pwd ?: string } {
220
+ command : { pwd : string } ,
221
+ sp : ServiceProvider
222
+ ) : Promise < { digestPassword ?: boolean ; pwd ?: string } > {
189
223
if ( passwordDigestor === undefined ) {
190
224
return { } ;
191
225
}
@@ -202,14 +236,10 @@ export function processDigestPassword(
202
236
CommonErrors . InvalidArgument
203
237
) ;
204
238
}
205
- // NOTE: this code has raised a code scanning alert about the "use of a broken or weak cryptographic algorithm":
206
- // we inherited this code from `mongo`, and we cannot replace MD5 with a different algorithm, since MD5 is part of the SCRAM-SHA-1 protocol,
207
- // and the purpose of `passwordDigestor=client` is to improve the security of SCRAM-SHA-1, allowing the creation of new users
208
- // without the need to communicate their password to the server.
209
- const hash = crypto . createHash ( 'md5' ) ;
210
- hash . update ( `${ username } :mongo:${ command . pwd } ` ) ;
211
- const digested = hash . digest ( 'hex' ) ;
212
- return { digestPassword : false , pwd : digested } ;
239
+ return {
240
+ digestPassword : false ,
241
+ pwd : await computeLegacyHexMD5 ( sp , `${ username } :mongo:${ command . pwd } ` ) ,
242
+ } ;
213
243
}
214
244
return { digestPassword : true } ;
215
245
}
@@ -630,24 +660,35 @@ export async function getPrintableShardStatus(
630
660
'on shard' : chunk . shard ,
631
661
'last modified' : chunk . lastmod ,
632
662
} as any ;
633
- // Displaying a full, multi-line output for each chunk is a bit verbose,
634
- // even if there are only a few chunks. Where supported, we use a custom
635
- // inspection function to inspect a copy of this object with an unlimited
636
- // line break length (i.e. all objects on a single line).
637
- Object . defineProperty (
638
- c ,
639
- Symbol . for ( 'nodejs.util.inspect.custom' ) ,
640
- {
641
- value : function ( depth : number , options : any ) : string {
642
- return inspect (
643
- { ...this } ,
644
- { ...options , breakLength : Infinity }
645
- ) ;
646
- } ,
647
- writable : true ,
648
- configurable : true ,
649
- }
650
- ) ;
663
+ try {
664
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
665
+ coreUtilInspect ??= require ( 'util' ) . inspect ;
666
+ } catch {
667
+ // No util.inspect available, e.g. in browser builds.
668
+ }
669
+ if ( coreUtilInspect ) {
670
+ // Displaying a full, multi-line output for each chunk is a bit verbose,
671
+ // even if there are only a few chunks. Where supported, we use a custom
672
+ // inspection function to inspect a copy of this object with an unlimited
673
+ // line break length (i.e. all objects on a single line).
674
+ Object . defineProperty (
675
+ c ,
676
+ Symbol . for ( 'nodejs.util.inspect.custom' ) ,
677
+ {
678
+ value : function (
679
+ depth : number ,
680
+ options : InspectOptions
681
+ ) : string {
682
+ return coreUtilInspect (
683
+ { ...this } ,
684
+ { ...options , breakLength : Infinity }
685
+ ) ;
686
+ } ,
687
+ writable : true ,
688
+ configurable : true ,
689
+ }
690
+ ) ;
691
+ }
651
692
if ( chunk . jumbo ) c . jumbo = 'yes' ;
652
693
chunksRes . push ( c ) ;
653
694
} else if ( chunksRes . length === 20 && ! verbose ) {
0 commit comments