@@ -9,13 +9,13 @@ import type {
99 DeleteOptions ,
1010 MapReduceOptions ,
1111 ExplainOptions ,
12+ ServiceProvider ,
1213} from '@mongosh/service-provider-core' ;
1314import {
1415 CommonErrors ,
1516 MongoshInvalidInputError ,
1617 MongoshUnimplementedError ,
1718} from '@mongosh/errors' ;
18- import crypto from 'crypto' ;
1919import type { Database } from './database' ;
2020import type { Collection } from './collection' ;
2121import type { CursorIterationResult } from './result' ;
@@ -27,8 +27,12 @@ import { shellApiType } from './enums';
2727import type { AbstractFiniteCursor } from './abstract-cursor' ;
2828import type ChangeStreamCursor from './change-stream-cursor' ;
2929import type { BSON , ShellBson } from '@mongosh/shell-bson' ;
30- import { inspect } from 'util' ;
3130import 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+ } ;
3236
3337/**
3438 * Helper method to adapt aggregation pipeline options.
@@ -173,6 +177,35 @@ export function adaptOptions(
173177 } , additions ) ;
174178}
175179
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+
176209/**
177210 * Optionally digest password if passwordDigestor field set to 'client'. If it's false,
178211 * then hash the password.
@@ -181,11 +214,12 @@ export function adaptOptions(
181214 * @param passwordDigestor
182215 * @param {Object } command
183216 */
184- export function processDigestPassword (
217+ export async function processDigestPassword (
185218 username : string ,
186219 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 } > {
189223 if ( passwordDigestor === undefined ) {
190224 return { } ;
191225 }
@@ -202,14 +236,10 @@ export function processDigestPassword(
202236 CommonErrors . InvalidArgument
203237 ) ;
204238 }
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+ } ;
213243 }
214244 return { digestPassword : true } ;
215245}
@@ -630,24 +660,35 @@ export async function getPrintableShardStatus(
630660 'on shard' : chunk . shard ,
631661 'last modified' : chunk . lastmod ,
632662 } 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+ }
651692 if ( chunk . jumbo ) c . jumbo = 'yes' ;
652693 chunksRes . push ( c ) ;
653694 } else if ( chunksRes . length === 20 && ! verbose ) {
0 commit comments