6

I am working with NodeJS and Express using Express-Handlebars template engine.

Handlebars is throwing the following error when trying to render a template:

Handlebars: Access has been denied to resolve the property "username" because it is not an "own property" of its parent. You can add a runtime option to disable the check or this warning: See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details

According to the above link:

From version 4.6.0 on, Handlebars forbids accessing prototype properties and methods of the context object by default. The reason are various security issues that arise from this possibility.

My app.js contains the following:

const exphbs = require('express-handlebars');
const express = require('express');
// Init Express
const app = express();
// VIEW ENGINE
app.engine('handlebars', exphbs({
  defaultLayout: 'main'
}));
app.set('view engine', 'handlebars');

My route file fetches from MongoDB via Mongoose:

//@GET - View
router.get('/', authMiddleware, (req, res, next) => {
  // Mongoose
  Model.find({ user: req.user._id })
    .sort({ date: -1 })
    .then(model => {
      res.render('/overview', { model: model })
    })
    .catch(err => {
      if (err) throw err;
      req.flash('error_msg', 'No Model Found');
    })

})

model is an array of objects

This issue only started happening after I began to mess around with adding handlebar helpers. I have removed the helpers and reverted to my original configuration settings (above) to no avail. I've tried deleting node_modules folder and reinstalling npm.

What's being sent is an array of objects, and I am trying to loop over the properties of the objects using the {{#each model}} helper and reference the individual properties via {{prop1}} within the each.

According to Handlebars, this disabling of proto properties is false by default, and this change shouldn't break anything.

My question:

  1. Am I sending data to handlebars incorrectly? If so, what is the correct method (not exposing my server to security holes) to send data to the express-handlebars template for rendering?

Thank you in advance.

CoatCat
  • 162
  • 1
  • 9
  • Try to strip the data from its context object properties by doing this; ```model = model.toJSON();``` – MAS Jan 15 '20 at 14:39
  • Hi Mas, thanks for the suggestion. Documentation for toJSON() says it's for a date so this won't work. – CoatCat Jan 15 '20 at 16:25
  • Correct! I used to work with Sequelize and ```toJSON()``` did the trick. If you tried it already and it didn't work, I think the same result in Mongoose could be achieved by using [lean](https://mongoosejs.com/docs/tutorials/lean.html) – MAS Jan 15 '20 at 17:34
  • 1
    lean worked! amazing, than kyou Mas! – CoatCat Jan 15 '20 at 20:23

6 Answers6

8

I had the same issue when I upgraded the handlebars package. To get your system back online as quick as possible remove the handlebars entry in package.json And then insert this line in its place.

"handlebars": "4.5.3",

From version 4.6.0 onward Handlebars forbids accessing prototype properties and methods of the context object by default. This is related to a security issue described here: https://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html

Refer to https://github.com/wycats/handlebars.js/issues/1642

An example from the above URL shows the Mongoose response converted into JSON:

app.get('/test', function (_req, res) {
    Kitten.find({}).then(kittens => {
        res.render('test.hbs', {
            kittens: kittens.map(kitten => kitten.toJSON())
        })
    })
});

If you are certain that only trusted developers and no end users have access to the handlebars templates it's possible to allow prototype access by installing the following package:

npm install @handlebars/allow-prototype-access

Here is an example of it's use:

const express = require('express');
const Handlebars = require('handlebars')
const expressHandlebars = require('express-handlebars');
const {allowInsecurePrototypeAccess} = require('@handlebars/allow-prototype-access')

const app = express();

app.engine('handlebars', expressHandlebars({
    handlebars: allowInsecurePrototypeAccess(Handlebars)
}));
app.set('view engine', 'handlebars');
...

Another option is to use the mongoose .lean() function. This has the benefit of being much faster than a traditional mongoose query. But it does have some cons as well. By default, Mongoose queries return an instance of the Mongoose Document class. These objects contain a lot of internal state for change tracking and have additional methods such as .save(). Enabling the lean option tells Mongoose to skip instantiating a full Mongoose document and just return the plain javascript object.

Andrew Taylor
  • 610
  • 7
  • 26
7

Correct! I used to work with Sequelize and toJSON() did the trick.

If you tried it already and it didn't work, I think the same result in Mongoose could be achieved by using lean – mas 2 hours ago

I added .lean between .sort() and .then(), This worked!

Tom Slabbaert
  • 21,288
  • 10
  • 30
  • 43
CoatCat
  • 162
  • 1
  • 9
7

use .lean() after .find() it works properly

H Hassanshahi
  • 101
  • 1
  • 3
  • 4
    When possible, please make an effort to provide additional explanation instead of just code. Such answers tend to be more useful as they help members of the community—and especially new developers—better understand the reasoning of the solution, and can help prevent the need to address follow-up questions. – Jeremy Caney May 06 '20 at 02:27
2

@OctavianLabs for that kind of error.you should try following code.

 Model.find({ user: req.user._id })
    .sort({ date: -1 })
    .lean()
    .then(model => {
      res.render('/overview', { model: model })
    })
1

As answered @OctavianLabs it's helped for me:

const posts = await this.find({})
 .limit(amount)
 .lean();

The lean method of mongoose returns plain JavaScript objects (POJOs), not Mongoose documents.

Vladimir Mironov
  • 185
  • 1
  • 1
  • 9
1

use .lean() after .find() This function makes data fetching much faster

linux2
  • 165
  • 1
  • 1
  • 8