0

I've created a KeystoneJS-based website, that uses handlebars. I've created some models and templates for them. But when I try to reach one of my website pages I get the following error:

Handlebars: Access has been denied to resolve the property "title" 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
Handlebars: Access has been denied to resolve the property "coverImage" 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
Handlebars: Access has been denied to resolve the property "duration" 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
Handlebars: Access has been denied to resolve the property "distance" 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
Handlebars: Access has been denied to resolve the property "price" 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
Handlebars: Access has been denied to resolve the property "content" 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
Handlebars: Access has been denied to resolve the property "images" 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

Here's a code of the page I'm trying to reach:

<!DOCTYPE html>

<html lang="en">
<head>
    <title>{{#if data.trip}}{{data.trip.title}}{{/if}}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" type="text/css" href="/styles/custom/trip.css">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
    <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
    <script>
        function showDiv () {
            let order = document.getElementById("order");
            if (order.style.display === 'flex') {
                order.style.display = 'none';
            } else {
                order.style.display = 'flex';
            }
        }
    </script>
</head>
<body>
    {{#if data.trip}}
        <div class="container" style="background: url('{{ cloudinaryUrl data.trip.coverImage }}') center no-repeat; background-size: cover; ">
            <div class="tour">
                <div class="content">
                    <h1 class="title">{{data.trip.title}}</h1>
                    <div class="info">
                        <p class="item">Duration: <br>{{data.trip.duration}}</p>
                        <p class="item">Distance: <br>{{data.trip.distance}}</p>
                        <p class="item">Price: <br>{{data.trip.price}}€</p>    
                    </div>
                    {{#if data.trip.content.additional}}
                        <div class="additional">
                            <p>{{{data.trip.content.additional}}}</p>
                        </div>
                    {{/if}}
                </div>
            </div>
        </div>
        <div class="extended">
            <p class="text">{{# if data.trip.content.extended }} {{{data.trip.content.extended}}} {{/if}}</p>
        </div>
        <div class="photos">
            {{#each data.trip.images }}
                <img src="{{cloudinaryUrl}}" class="list-photo" alt="{{../data.trip.title}} Photo">
            {{/each}}
        </div>
        <div class="order-container">
            <button class="order" onclick="showDiv()">Order</button>
            <div id="order" class="order-form">
                <h1 class="form-title">Order</h1>
                <form class="form-container" id="order-trip-form" name="simple-contact-form" accept-charset="utf-8" action="https://formspree.io/info@seebelarus.by" method="post">
                    <div class="input-1">
                        <input class="input input-name" type="text" name="name" placeholder="Name" required="required">
                        <input class="input input-email" type="email" name="email" placeholder="Email" required="required">
                        <input class="input input-phone" type="telephone" name="telephone" placeholder="Phone (Optional)" required="">
                        <input class="input input-number" type="number" name="plus" placeholder="Number of people" required="required">
                    </div>
                    <div class="input-2">
                        <input class="input input-place" type="text" name="street" placeholder="Place to meet" required="required">
                        <input class="input input-date" type="date" name="date" placeholder="Date" required="required">
                        <textarea class="comments" name="message" placeholder="Any comments" required=""></textarea>
                    </div>
                    <div class="submit-buttons">
                        <input type="hidden" name="_subject" id="email-subject" value="Contact Form Submission">
                        <input type="submit" value="Submit" class="input-button" id="button-submit">
                    </div>
                </form>
            </div>
        </div>
    {{/if}}
</body>
</html>

This is my route file:


let keystone = require('keystone');

exports = module.exports = function (req, res) {

    let view = new keystone.View(req, res);
    let locals = res.locals;

    // Set locals
    locals.section = 'tours';
    locals.filters = {
        trip: req.params.trip,
    };
    locals.data = {
        trip: [],
    };

    view.on('init', function (next) {


        keystone.list('Trip').model.findOne({
            slug: locals.filters.trip,
        }).exec(function (err, results) {
            locals.data.trip = results;
            console.log(locals.data.trip);
            next(err);
        });

    });


    view.render('trip');
};

And, finally, here's the model:

let keystone = require('keystone');
let Types = keystone.Field.Types;

let Trip = new keystone.List('Trip', {
    map: { name: 'title' },
    singular: 'Trip',
    plural: 'Trips',
    autokey: { path: 'slug', from: 'title', unique: true },
});

Trip.add({
    title: { type: String, required: true },
    content: {
        extended: { type: Types.Html, wysiwyg: true, height: 400 },
        additional: { type: Types.Html, wysiwyg: true, height: 300 },
    },
    category: { type: Types.Relationship, ref: 'TripCategory' },
    duration: { type: Types.Html, wysiwyg: false },
    distance: { type: Types.Html, wysiwyg: false },
    price: { type: Number },
    images: { type: Types.CloudinaryImages },
    coverImage: { type: Types.CloudinaryImage },
});

Trip.register();

That's how it must work: enter image description here

That's how it works: enter image description here

By the way, I checked the link that was showed in the error, but I don't know where I must use allowProtoPropertiesByDefault (I guess I have to use exactly this code).

Here's the link: https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access

  • Kinda old but this might help.. https://stackoverflow.com/a/59704492/10431732 – Matt Oestreich Feb 23 '20 at 19:01
  • @MattOestreich I saw this question. Actually, it didn't help :( – Michael Shkarubski Feb 23 '20 at 19:14
  • I don't see `slug` or `name` anywhere in your code which makes me think its an issue with `cloudinaryUrl`.. You can try to use those runtime options at render.. Just do `res.render('YourTemplate.ejs', { data: "that", you: "are", passing: "into", the: "template" }, { allowedProtoMethods: { slug: true, name: true } });` – Matt Oestreich Feb 23 '20 at 19:19
  • @MattOestreich this code works locally. Sorry, this error is connected with another model, I'll edit my question. – Michael Shkarubski Feb 24 '20 at 18:14
  • @MattOestreich I've updated my question with the right info :) – Michael Shkarubski Feb 24 '20 at 18:17
  • You should be able to do this in your route.. `res.render('YourTemplate.ejs', { data: someDataYouArePassing }, { allowedProtoMethods: { data: true } });` – Matt Oestreich Feb 24 '20 at 18:25
  • ..or you may have to list them individually, like: `res.render('YourTemplate.ejs', { data: someDataYouArePassing }, { allowedProtoMethods: { title: true, coverImage: true, duration: true, distance: true, price: true, content: true, images: true } });` .. according to that article that is how you get around that error. It's hard to tell how you are passing data to your template, as you aren't showing us the route and how you are passing data into your template.. – Matt Oestreich Feb 24 '20 at 18:29
  • Or you may be able to do `res.render('YourTemplate.ejs', { data: someDataYouArePassing }, { allowProtoMethodsByDefault: true });` - these are considered 'band-aids', though.. [this article](https://dev.to/abourass/how-to-solve-the-own-property-issue-in-handlebars-with-mongoose-2l7c) gets a little deeper on the actual issue.. So, where does the variable `data` come from? MongoDB? – Matt Oestreich Feb 24 '20 at 18:36
  • @MattOestreich I've tried your decision, but it didn't help. I'll edit my question to show the route and model. – Michael Shkarubski Feb 24 '20 at 19:02
  • Did you restart the app after changes? – Matt Oestreich Feb 24 '20 at 19:07
  • @MattOestreich of course I did. – Michael Shkarubski Feb 24 '20 at 19:10
  • Ha not trying to offend you - just making sure :) – Matt Oestreich Feb 24 '20 at 19:17
  • @MattOestreich :) I realized, that if I type ```handlebars --version``` locally it works correctly and me ```4.0.5```, but on my server, this doesn't work even if I run ```npm install``` before ```handlebars --version```. That's rather strange. – Michael Shkarubski Feb 24 '20 at 19:29
  • Where do you set the View engine for Keystone at? I'm not very familiar with Keystone, but it would seem they are handling all of this internally.. If you find [view engine here](https://v4.keystonejs.com/documentation/configuration/server-options/) it looks like you specify handlebars as the View engine somewhere in your keystone app. – Matt Oestreich Feb 24 '20 at 19:38
  • Also, if you [scroll ALL the way to the bottom here](https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access), they say you can use [this package as your View engine](https://www.npmjs.com/package/@handlebars/allow-prototype-access) to get around this issue. Apparently handlebars just updated to this behavior like a month ago. Lastly, you could also [file an issue](https://github.com/keystonejs/keystone/issues) on Keystones GitHHub repo. I was unable to find an existing issue for this, but maybe they can help? – Matt Oestreich Feb 24 '20 at 19:40

3 Answers3

4

I had this problem after updating my keystonejs classic project.

TL;DR this express-handlebars pull fixes it https://github.com/express-handlebars/express-handlebars/pull/53

Update express-handlebars to 4.0.6 or above (I used 5.1.0 from 3.x and it worked fine) in your package.json file

you'll then need to add the following to your 'custom engine' after extname: '.hbs' in keystone.js

  runtimeOptions: {
    allowProtoPropertiesByDefault: true,
    allowProtoMethodsByDefault: true
  }

Voilà

sub6as
  • 56
  • 2
1

I tried changing the version of handlebars but it didn't work. However, after trying to read further I found the following walk around.

  1. run npm install @handlebars/allow-prototype-access

  2. run npm install express-handlebars

     // require these packages into your target, in my case I required it in app.js  where I run my server and other sets the templating engine.
    
    const Handlebars = require('handlebars')
    const { allowInsecurePrototypeAccess } = require('@handlebars/allow-prototype-access')
    const exphbs = require('express-handlebars');
    
    // load engines as such
    
    app.engine('handlebars', exphbs({
     defaultLayout: 'home',
     handlebars: allowInsecurePrototypeAccess(Handlebars)
    }));
    app.set('view engine', 'handlebars');
    

And rerun your app. It worked for me without me having to downgrade the version of handlebars used. So I think I will work for most people.

for more information, read the following articles that I used. https://www.npmjs.com/package/@handlebars/allow-prototype-access at the section Usage (express-handlebars and mongoose)

Hirumina
  • 738
  • 1
  • 9
  • 23
0

I had the same problem, when I had 4.0.3 version of express-handlebars. I installed 3.0.2 version(npm install express-handlebars@3.0.2) and I don't have this issue anymore.