2.0.0 breaks Express.js res.render views configuration
SamuelGaona opened this issue · comments
The configuration for the new version 2.0.0 of eta breaks res.render views configuration.
To Reproduce
Steps to reproduce the behavior:
- Use the proposed configuration
var eta = require("eta")
eta.configure({ views: "./OTHER-VIEWS-FOLDER", cache: true })
app.engine("eta", eta.renderFile)
app.set("view engine", "eta")
- Use
res.render(templateName);
- It looks over the default views folder of express.js,
'./views'
Expected behavior
res.render should render as if it was configured with app.set("views", "./OTHER-VIEWS-FOLDER")
renderFile
works just fine.
Summary
Continue using app.set("views", "./OTHER-VIEWS-FOLDER")
would be fine.
Detail
When we visit /
in the following code, express.js will call renderFile(this.path, options, callback)
.
Eta.js will then set the renderConfig.filename
to this.path
, so renderConfig.filename
will be fine.
this.path
is resolved by express.js by using the setting from app.set("views", "./OTHER-VIEWS-FOLDER")
.
If we use eta.configure({ views: "./views", cache: true })
instead, express.js will then use its default value to resolve this.path
(since it only tells eta.js where to resolve the file, not express.js).
var express = require("express")
var app = express()
var eta = require("eta")
var eta = require("eta")
app.engine("eta", eta.renderFile)
app.set("views", "./OTHER-VIEWS-FOLDER")
app.set("view cache", true)
app.set("view engine", "eta")
app.get("/", function (req, res) {
res.render("template",
req.query
)
})
app.listen(8000, function () {
console.log("listening to requests on port 8000")
})
There is another thing worth mentioning about the cache option. (I think its relevent to this issue.)
The following code set the cache option of eta.js, which caches templateFunc
.
eta.configure({ views: "./OTHER-VIEWS-FOLDER", cache: true })
The following code set the cache option of express.js, which caches view
.
app.set("view cache", true)
THEY ARE DIFFERENT.
@Ching367436 app.set("views", "...")
appears to be fine when configured like so but if you set up multiple directories, it seems that it won't attempt to resolve partials using all of them. This works fine in 1.14.2 but fails in 2.0.0 (Stackblitz):
index.js
const express = require('express');
const app = express();
const eta = require('eta');
app.engine('eta', eta.renderFile);
app.set('view engine', 'eta');
app.set('views', ['./views1', './views2']);
app.get('/', function (req, res) {
res.render('template', {
favorite: 'Eta',
name: 'Ben',
reasons: ['fast', 'lightweight', 'simple'],
});
});
app.listen(3000);
views1/template.eta
My favorite template engine is <%= it.favorite %> because it is: <%= it.reasons.join(', ') %>
<%~ includeFile('./footer', it) %>
views2/footer.eta
<footer>Signed: <%= it.name %> </footer>
The error:
Eta Error: Could not find the template "./footer". Paths tried: /home/projects/node-n8sqsu/views1/footer.eta
at EtaErr (file:///home/projects/node-n8sqsu/node_modules/eta/dist/eta.umd.js:48:17)
at getPath (file:///home/projects/node-n8sqsu/node_modules/eta/dist/eta.umd.js:679:15)
at includeFile (file:///home/projects/node-n8sqsu/node_modules/eta/dist/eta.umd.js:799:17)
at Object.includeFileHelper [as includeFile] (file:///home/projects/node-n8sqsu/node_modules/eta/dist/eta.umd.js:851:31)
at eval (eval at _0x579686 (https://noden8sqsu-el15-d7et1jf1.w-credentialless.staticblitz.com/blitz.685d3c93544a5e80be2c9e6906d68c0f4bd854cc.js:15:338547), <anonymous>:10:5)
at tryHandleCache (file:///home/projects/node-n8sqsu/node_modules/eta/dist/eta.umd.js:759:9)
at View.renderFile [as engine] (file:///home/projects/node-n8sqsu/node_modules/eta/dist/eta.umd.js:834:12)
at View.render (file:///home/projects/node-n8sqsu/node_modules/express/lib/view.js:135:8)
at tryRender (file:///home/projects/node-n8sqsu/node_modules/express/lib/application.js:640:10)
at Function.render (file:///home/projects/node-n8sqsu/node_modules/express/lib/application.js:592:3)
@SamuelGaona @Ching367436 @gkentr thanks for giving details on this issue -- it's one that I didn't expect.
It seems to me that the above problems could be solved in two ways:
- Within Eta, merge the values of
data.settings.views
anddata.settings["view cache"]
withconfig.views
andconfig["view cache"]
. This is the behavior of 1.14.2.
@Ching367436 you're right that Eta's cache option and Express's "view cache" option are different. Perhaps we should only mergedata.settings.views
andconfig.views
, then instruct users to run botheta.configure({ cache: true })
andapp.set("view cache", true)
.
However, if a developer passes in req.query
as template data, there is a chance that malicious users could change the views
option and gain access to another folder on the user's machine.
- Instruct users to set both Express.js config values and Eta.js config values:
app.engine("eta", eta.renderFile)
eta.configure({ views: "./views-folder", cache: true })
app.set("views", "./views-folder")
app.set("view cache", true)
app.set("view engine", "eta")
This is slightly annoying, but may be safer.
What do you all think?
The second option would be better.
If we choose the first option, an attacker could do more than just gain access to another folder on the user's machine under certain circumstances.
For example:
If an attacker could control files on the server (like file upload functionality), the attacker could possibly perform server-side template injection.
In @gkentr's example above, if we change app.get('/', .)
to the following and assume that the app has file upload functionality, the attacker could upload footer.eta to THE_UPLOAD_FOLDER.
Then when visiting /?reasons[0]=&settings[views][0]=THE_UPLOAD_FOLDER, THE_UPLOAD_FOLDER/footer.eta will be included (server-side template injection!).
app.get('/', function (req, res) {
res.render('template',
req.query
);
});
Server-side template injection could sometime lead to remote code execution.
I haven't tested out if server-side template injection in Eta could lead to remote code execution yet, maybe it could.
Update: Server-side template injection in Eta could lead to remote code execution.
@Ching367436 I think you're right. I'll update the docs to update recommended ExpressJS usage.