Runtime stack overflow from lodash.clonedeep, using mongoose to establish a sessionMongoStore connection

Viewed 25

Update: This is likely a deeper problem and have nothing to do with, e.g., importing of lodash.

It appears that, in this code that I've inherited, cloneDeep is used in several places and that it might be an actual problem with some circularity in what's being cloned. If so, I'll close this issue and (likely) push out some others.

nodeJS v14.20.0
typescript v4.6.4
mongodb v4.9.1
mongoose v6.6.1
connect-mongodb-session v3.1.1
lodash v4.17.21

After upgrading an application across multiple levels, the one show-stopper is configuring and connecting to mongo, via mongoose, switching from connect-mongo to connect-mongodb-session. The build completed without error, but running the app results in:

/Users/.../node_modules/lodash.clonedeep/index.js:841 
function baseClone(value, isDeep, isFull, customizer, key, object, stack) {
                  ^
RangeError: Maximum call stack size exceeded 
   at baseClone (/Users/.../node_modules/lodash.clonedeep/index.js:841:19)
   at /Users/.../node_modules/lodash.clonedeep/index.js:897:30
...

This happens with mongoose configuration:

import mongo from 'connect-mongodb-session';
import mongoose from './db'

const MongoStore = mongo(session);

...

const sessionMongoStore = new MongoStore({
      autoReconnect: true,
      mongooseConnection: mongoose.connection
    });
    app.use(
      session({
        secret: 'Pnv68aFuBy',
        store: sessionMongoStore,
        resave: true,
        saveUninitialized: true,
      })
    );

The file db.ts, which extends mongoose to provide/define a properly configured mongoose connection, contains:

import assert from 'assert';
import mongoose from 'mongoose';
import appEnv from './appenv';
import bluebird from 'bluebird';
import * as _ from 'lodash';

(mongoose as any).Promise = bluebird;
 
...

mongoose.connect( connectionString)
  .then( () => { /* resolves to undefined */})
  .catch(err => {
    console.log('MongoDB connection error: ' + err);
  });

export default mongoose;

This all worked at the following versions:

nodeJS v12.22.12
typescript v3.8.3
mongodb v3.3.13
mongoose v5.9.12
connect-mongo v3.1.2
lodash v4.17.13

I'm wondering whether the inclusion of the db.ts file is creating some kind of infinite recursion for lodash.

1 Answers

Getting past the red herrings, the problem was not within user code but in one of the installed node modules - specifically, the (obsolete) use of app.use(helmet.noCache()); which contained the errant use of lodash.cloneDeep.

A colleague had recommended replacing lodash.cloneDeep with clone. "...with the circular attribute set to true (then you can slowly set it to false in some places until it fails)". As none of the references failed, it was a simple matter of looking into the many node_moodules. The hint was the warning that helmet.noCache() had been deprecated.

Fixing this is simple. In the application code:

  1. insert import noCache from 'nocache';
  2. replace app.use(helmet.noCache()); with app.use(noCache());
Related