Handlebars: Access has been denied to resolve the property “from” because it is not an “own property” of its parent

后端 未结 11 1732
我在风中等你
我在风中等你 2020-12-05 13:27

I am using a Nodejs backend with server-side rendering using handlebars. After reading a doc array of objects from handlebars, which contains key \"content\" an

相关标签:
11条回答
  • 2020-12-05 13:49

    "Wow this worked, Why is this happening though? I am currently using express-handlebars (3.1.0) which I set as a render engine in my express app." – Lee Boon Kong Jan 12 at 14:13

    "In the past, Handlebars would allow you to access prototype methods and properties of the input object from the template... Multiple security issues have come from this behaviour... In handlebars@^4.6.0. access to the object prototype has been disabled completely. Now, if you use custom classes as input to Handlebars, your code won't work anymore... This package automatically adds runtime options to each template-calls, disabling the security restrictions... If your users are writing templates and you execute them on your server you should NOT use this package, but rather find other ways to solve the problem... I suggest you convert your class-instances to plain JavaScript objects before passing them to the template function. Every property or function you access, must be an "own property" of its parent." – README

    More details here: https://www.npmjs.com/package/@handlebars/allow-prototype-access

    QUICK AND DIRTY INSECURE METHOD

    Usage (express-handlebars and mongoose):

    express-handlebars does not allow you to specify runtime-options to pass to the template function. This package can help you disable prototype checks for your models.

    "Only do this, if you have full control over the templates that are executed in the server."

    Steps:

    1 - Install dependency

    npm i @handlebars/allow-prototype-access

    2 - Use this snippet as an example to rewrite your express server

    const express = require('express');
    const mongoose = require('mongoose');
    const Handlebars = require('handlebars');
    const exphbs = require('express-handlebars');
    
    // Import function exported by newly installed node modules.
    const { allowInsecurePrototypeAccess } = require('@handlebars/allow-prototype-access');
    
    const PORT = process.env.PORT || 3000;
    
    const app = express();
    
    const routes = require('./routes');
    
    app.use(express.urlencoded({ extended: true }));
    app.use(express.json());
    app.use(express.static('public'));
    
    // When connecting Handlebars to the Express app...
    app.engine('handlebars', exphbs({
        defaultLayout: 'main',
        // ...implement newly added insecure prototype access
        handlebars: allowInsecurePrototypeAccess(Handlebars)
        })
    );
    app.set('view engine', 'handlebars');
    
    app.use(routes);
    
    const MONGODB_URI = process.env.MONGODB_URI || >'mongodb://localhost/dbName';
    
    mongoose.connect(MONGODB_URI);
    
    app.listen(PORT, function () {
      console.log('Listening on port: ' + PORT);
    });
    

    3 - Run the server and do your happy dance.


    LONGER MORE SECURE METHOD

    Before passing the object returned by your AJAX call to the Handlebars template, map it into a new object with each property or function you need to access in your .hbs file. Below you can see the new object made before passing it to the Handlebars template.

    const router = require("express").Router();
    const db = require("../../models");
    
    router.get("/", function (req, res) {
        db.Article.find({ saved: false })
            .sort({ date: -1 })
            .then(oldArticleObject => {
                const newArticleObject = {
                    articles: oldArticleObject.map(data => {
                        return {
                            headline: data.headline,
                            summary: data.summary,
                            url: data.url,
                            date: data.date,
                            saved: data.saved
                        }
                    })
                }
                res.render("home", {
                    articles: newArticleObject.articles
                })
            })
            .catch(error => res.status(500).send(error));
    });
    

    Your mongoose query

    Correct me if I'm wrong but I think this might work for your query...

    Confession.find()
        .sort({ date: -1 })
        .then(function (oldDoc) {
    
            for (var i = 0; i < oldDoc.length; i++) {
                //Check whether sender is anonymous
                if (oldDoc[i].from === "" || oldDoc[i].from == null) {
                    oldDoc[i].from = "Anonymous";
                }
    
                //Add an extra JSON Field for formatted date
                oldDoc[i].formattedDate = formatTime(oldDoc[i].date);
            }
    
            const newDoc = {
                doc: oldDoc.map(function (data) {
                    return {
                        from: data.from,
                        formattedDate: data.formattedDate
                    }
                })
            }
            
            res.render('index', { title: 'Confession Box', success: req.session.success, errors: req.session.errors, confession: newDoc.doc });
            req.session.errors = null;
            req.session.success = null;
        });
    
    0 讨论(0)
  • 2020-12-05 13:50

    Creating another new Object or Array from data returned by find() will solve the problem . See below a simple illustration

    app.get("/",(req,res)=>{
    
     let com = require('./MODELCOM')    // loading model
     let source=fs.readFileSync(__dirname+"/views/template.hbs","utf-8");
    
     com.find((err,data)=>{
        // creation new array  using map
       let wanted = data.map(doc=>{
           return {
               name:doc.name,
               _id:doc._id
            }
       })
    
        let html= handlebar.compile(source);
      fs.writeFileSync(__dirname+"/views/reciever.html",html({communities:wanted}))
        res.sendFile(__dirname+"/views/reciever.html")
    });
    
    0 讨论(0)
  • 2020-12-05 13:51

    Today I have the same warning from handlebars and the view is empty. Below is how I fixed that:

    //  * USERS PAGE
    // @description        users route
    // @returns           ../views/users.hbs
    router.get('/users', async (req, res) => {
      // get all items from db collection
      const collection = 'User'
      await dbFindAllDocs(collection) // <=> wrapper for Model.find() ...
        .then(documents => {
          // create context Object with 'usersDocuments' key
          const context = {
            usersDocuments: documents.map(document => {
              return {
                name: document.name,
                location: document.location
              }
            })
          }
          // rendering usersDocuments from context Object
          res.render('users', {
            usersDocuments: context.usersDocuments
          })
        })
        .catch(error => res.status(500).send(error))
    })
    

    the users.hbs file

    <ul>
    {{#each usersDocuments}}
    <li>name: {{this.name}} location: {{this.location}}</li>
    {{/each}}    
    </ul>
    

    Creating an entire new Object named context with its own properties then pass in it into the render function will fix the issue...

    note:

    When we do not create a new Object, it is easy to accidentally expose confidential information, or information that could compromise the security of the projet, mapping the data that's returned from the database and passing only what's needed onto the view could be a good practice...

    0 讨论(0)
  • 2020-12-05 13:56

    I am using Angular version 8.0.2 and Node version 10.16.3 While running test cases was facing below issue:

    Handlebars: Access has been denied to resolve the property "statements" because it is not an "own property" of its parent.

    Handlebars: Access has been denied to resolve the property "functions" because it is not an "own property" of its parent.

    While debugging the issue further found that in package.json, "karma-coverage-istanbul-reporter": "2.0.1" is there but "istanbul-lib-report" was missing so did following steps:

    1. In package.json file, under dependencies included "istanbul-lib-report": "3.0.0"
    2. Execute npm install

    And it solved my issue :) (Hope this helps someone)

    0 讨论(0)
  • 2020-12-05 13:57

    If using mongoose, this issue can be solved by using .lean() to get a json object (instead of a mongoose one):

    dbName.find({}).lean()
      // execute query
      .exec(function(error, body) {
         //Some code
      });
    
    0 讨论(0)
提交回复
热议问题