I was wondering if it was possible to use DOMPurify to sanitize user input on a form before it is saved to database. Here's what I've got in my routes.js folder for my form post:
.post('/questionForm', (req, res, next) =>{
console.log(req.body);
/*console.log(req.headers);*/
const questions = new QuestionForm({
_id: mongoose.Types.ObjectId(),
price: req.body.price,
seats: req.body.seats,
body_style: req.body.body_style,
personality: req.body.personality,
activity: req.body.activity,
driving: req.body.driving,
priority: req.body.priority
});
var qClean = DOMPurify.sanitize(questions);
//res.redirect(200, path)({
// res: "Message recieved. Check for a response later."
//});
qClean.save()
.then(result => {
//res.redirect(200, '/path')({
// //res: "Message recieved. Check for a response later."
//});
res.status(200).json({
docs:[questions]
});
})
.catch(err => {
console.log(err);
});
});
I also imported the package at the top of the page with
import DOMPurify from 'dompurify';
When I run the server and submit a post request, it throws a 500 error and claims that dompurify.sanitize is not a function. Am I using it in the wrong place, and/or is it even correct to use it in the back end at all?
This might be a bit late, but for others like me happening to run into this use case I found an npm package that seems well suited so far. It's called isomorphic-dompurify.
isomorphic-dompurify
DOMPurify needs a DOM to interact with; usually supplied by the browser. Isomorphic-dompurify feeds DOMPurify another package, "jsdom", as a dependency that acts like a supplementary virtual DOM so DOMPurify knows how to sanitize your input server-side.
In the packages' own words "DOMPurify needs a DOM tree to base on, which is not available in Node by default. To work on the server side, we need a fake DOM to be created and supplied to DOMPurify. It means that DOMPurify initialization logic on server is not the same as on client".
Building on #Seth Lyness's excellent answer --
If you'd rather not add another dependency, you can just use this code before you require DOMPurify. Basically what isometric-dompurify is doing is just creating a jsdom object and putting it in global.window.
const jsdom = require('jsdom');
const {JSDOM} = jsdom;
const {window} = new JSDOM('<!DOCTYPE html>');
global.window = window;
Related
I'm using strapi community edition v3.6.8. I have two different models ,User and CarModel. The User Model is strapi's integrated user model. The relation User: CarModel is 1:n
So I've got a profile page in which I want to fetch the User and their related CarModels. I can't get my head around how to achieve this.
I've read several answers that include creating a service which then fetches the related CarModelobjects but I can't figure out what to put into the service.
So the conclusion I've reached so far is that it is probably best if I just create a custom endpoint which fetches the current user and related objects.
How do I go on about this? This is the code I currently have:
axios.get(`http://localhost:1337/users/currentUser`, {
headers: {
Authorization: `Bearer ${token}`
}
})
In extensions/users-permissions/config I've created a routes.json with this content:
"method": "GET",
"path": "/users/currentUser",
"handler": "User.currentUser",
"config": {
"policies": ["policies.isAuthenticated"]
}
}
in config/policies I've created a is-authenticated.js - File with the following content:
module.exports = async (ctx, next) => {
if (ctx.state.user) {
return await next();
}
ctx.unauthorized(`You're not logged in!`);
};
And lastly in extensions/users-permissions/controllers I've created a User.js file with the following content:
const { sanitizeEntity } = require('strapi-utils');
const sanitizeUser = user =>
sanitizeEntity(user, {
model: strapi.query('user', 'users-permissions').model,
});
module.exports = {
currentUser: async (ctx, next) => {
strapi.query('user').find({id: ctx.id}, ['car-model']);
await next();
}
};
So now my questions would be:
1st: Something is wrong because when trying to GET /users/currentUser I get a 403. What exactly am I doing wrong?
2nd: Is this approach even valid in the first place?
And 3rd: What would be the correct approach to solving this problem? Because somewhere else I've read another approach which included writing a custom service which handles resolving the relation, but this looked very complicated imho, considering I'm simply trying to resolve a relation that already exists in the database.
I've also tried manipulating the users/me endpoint which didn't yield any results (and is probably also discouraged).
Interestingly: when the user logs in, I get the user object and all foreign key relations returned. Only when I query /users/me I get only the user data without relations. So I've read that this is a security feature, but what endpoint is used then, when posting to /auth/local and why does this endpoint return the user and related objects?
Could I use this endpoint instead of /users/me?
Any help to this problem would be greatly appreciated, best regards,
deM
So for anyone else looking for a solution, I figured it out. I added a custom route to currentUser as described above then I added a controller for this route in which I put the following code:
currentUser: async (ctx, next) => {
let carModelsOfUser = await (strapi.query('user', 'users-permissions').findOne({id: ctx.state.user.id}, ['carModels', 'carModels.images', 'carModels.ratings.rating']));
return carModelsOfUser;
}
CAUTION!
This also returns the user's hashed password and other potentially sensitive information.
Strapi offers the sanitizeEntity function to remove sensitive information, but as of now I haven't figured out how to use this in that context, as I'm not using the "raw" user here but instead joining some fields.
I am using Reactrouter in my front end and Express in my back end.
When I am routing with custom parameters which im using for a findbyID fetch for a component, I found
through my error log on express side read the custom param as styles.css.
React Rounter Side:
<Route path="/id/:id" component={Something} />
On Express:
app.get("/id/:id", (req, res) => {
const id = req.params.id;
database.findById(id, (e, found) => {
console.log(id);
if (!e) {
console.log(found);
} else {
console.log(e);
console.log("consult stack overflow");
}
});
});
Error Message:
MongooseError [CastError]: Cast to ObjectId failed for value "styles.css" at path "_id" for model "database"
Why does it do this and how can I fix it?
So as it turns out I have a Link to a styles.css in my index html page. soon as i removed that everything worked the way it should have. So if anyone Is stuggling with this exact same issue later down the line here's hoping this helps you. Check your index.html file for any matching names.
I'm working on a word game and am trying to return a random wordpair (my collection) on a page load. I'm using Express and have adapted my code from this tutorial if that's of any use.
A GET request renders my page just fine, and I'm trying to send a random WordPair object alongside the title:
router.get('/', function(req, res, next) {
res.render('play', { title: 'play', random_wordpair: wordpair_controller.wordpair_random});
});
The wordpair_random function is here inside a controller file I've made (which also successfully manages listing the wordpairs and creating new ones etc).
// Get random WordPair
exports.wordpair_random = function() {
WordPair.aggregate(
[{
$sample: {
size: 1
}
}]
)
.exec(function(err, random_wordpair) {
if (err) {
return next(err);
}
console.log(random_wordpair);
return random_wordpair;
});
};
Then inside a play.pug template, I'm simply trying to display this result:
h3 random wordpair selection is: #{random_wordpair}
But all I can see is the function rendered as HTML text. Can anyone tell me what I'm doing wrong?
I also understand looking at the documentation for MongoDB $sample aggregation that I need to be calling my function on the database object, but I've seen various examples and some don't do this. When I try calling db.wordpair.aggregate(...) (or WordPair or wordpairs as it appears in mLab) directly after initializing db in my app.js file, I get undefined errors. My db object doesn't seem to contain the correct data for this request.
Thanks!
I guess you're writing this in Node.JS. A core feature in Node.JS is non-blocking IO model. That means, the code won't wait for a database call to complete to move on.
Another concept you need to get it right is that Node.JS, being a variation of JavaScript, in nature is a functional programming. Assigning a function to a property of a JSON object like below won't cause the function to execute. It simply creates a pointer to the function body, that's why your application prints the function itself.
{ title: 'play', random_wordpair: wordpair_controller.wordpair_random}
To fix this, use a callback
exports.wordpair_random = function(callback) {
WordPair.aggregate([$sample: {size: 1}}]).exec(callback);
};
Then in you web function:
router.get('/', function(req, res, next) {
wordpair_controller.wordpair_random(function(err, result) {
//Handle errors if needed.
res.render('play', { title: 'play', random_wordpair:result });
})
});
I'm trying to pull in a livestream of data from a socket.io websocket, coming in as JSON.
I was trying to use the method from these folks, but no luck (I'm getting error - "Uncaught TypeError: Cannot call method 'load' of undefined" - which I haven't been able to figure out on my own):
Socket.IO with Ember and Ember-Data
My code:
var socket = io.connect('http://localhost:8007');
socket.on('my_live_stream', function (data) {
store.load(App.Group, data);
});
And more:
App.Group = DS.Model.extend({
id: DS.attr('string'),
name: DS.attr('string'),
usage: DS.attr('string'),
sunshine: DS.attr('string'),
device_info: DS.attr('string')
});
Edit: What the JSON looks like...
{
"group":{
"usage":{
"case1":0,
"case2":0,
"case3":0
},
"sunshine":"00/00/0000",
"id":1010,
"device_info":11.5,
...
I'm still very new to Ember here, but I'm just trying to get {{name}}, {{usage}}. and {{device_info}} to my Index template. I see a great stream of data when I add console.log(data) to the socket code (to replace store.load...). What's the next step?
Thanks so much!
The question you reference is using a very outdated version of Ember Data. You should pretty much ignore any Stack Overflows from before October.
The new method you want is store.push, in your case, in your case store.push('Group', data).
However, there's yet another problem in your code, which is that you don't have access to store in that context. Normally, you access the store inside routes and controllers via this.store. However, you're not inside a route or controller. If you want, you could hack access to the store like so, store = App.__container__.lookup('store:main'), but this is not the Ember way and will probably cause you problems down the line. Instead, you could add it to one of the hooks in the Application Route, where you do have access to this.store.
For example, you could set it up like this:
App.ApplicationRoute = Ember.Route.extend({
activate: function(){
var that = this;
var socket = io.connect('http://localhost:8007');
socket.on('my_live_stream', function (data) {
that.store.push('Group', data.group);
});
})
});
Docs: http://emberjs.com/guides/models/pushing-records-into-the-store/
I am trying to send a request parameter through to an 'exports' method for a mongodb find in an express.js, backbone.js application. I am having a difficult
time getting the parameters to pass through to mongodb and with '#'.
The breakage is the passing of parameters into the exported mongodb function.
Here is the flow of data:
First the request is successfully routed to the 'upcoming' function:
"upcoming/uni/:uni" : "upcoming",
It flows on to the 'upcoming' function without a problem.
upcoming: function(uni) {
console.log("uni: "+uni);
pag.reset();
console.log("Hit upcoming list target");
setCollectionType('upcoming');
var upcomingCourses = buildCollection();
// ------------------------------------------------------------------------
// here is the problem how do I pass the parameter value through the fetch?
// Although it may also have to do with '#' please read on.
// ------------------------------------------------------------------------
upcomingCourses.fetch({success: function(){
$("#content").html(new ListView({model: upcomingCourses, page: 1}).el);
}});
this.headerView.selectMenuItem('home-menu');
},
The routing for the mongo methods is:
app.get('/upcoming/uni/:uni', mongomod.findUpcoming);
So the following method is exported from the mongodb js file and is executed reliable. However the req.params are not passed through.
Interspersed in the code I have described its' runtime behaviour:
exports.findUpcoming = function(req, res) {
console.log("university", req.params.uni); // This consistently is unpopulated
var uni = req.params.uni;
console.log("Size: "+req.params.length); // This will always be 0
for (var i=0; i < req.params.length; i++) {
console.log("Parameters: "+req.params[i]);
}
db.collection('upcoming', function(err, collection) {
if (typeof uni === 'undefined') {
console.log("The value is undefined");
uni = "Princeton University"; // here we add a string to test it it will work.
}
collection.find({university:uni}).toArray(function(err, items) {
if (err) {
console.log("Error: "+err);
} else {
console.log("No Error");
console.log("Count: "+items.length);
console.log(items[0]['university']);
res.send(items);
}
});
});
};
On additional and important note:
The url, in a working, runtime environment would be:
http://localhost:3000/#upcoming/uni/Exploratorium
This one fails, but the following URL will work in passing the params through these functions however it returns the JSON to the screen rather then
the rendered version:
http://localhost:3000/upcoming/uni/Exploratorium
The problem could be a miss understanding of # and templates. Please, if you see the error enlightenment would be greatly appreciated.
Nothing after the # gets passed to the server. See How to get hash in a server side language? or https://stackoverflow.com/a/318581/711902.
I found a solution to the problem of passing the parameters from the client side to the server side. By changing the url of the collection the parameters will be passed to the server side:
upcomingCourses.url = "/upcoming/uni/"+uni; // <-- here's the ticket where uni is param
upcomingCourses.fetch({success: function(){
$("#content").html(new ListView({model: upcomingCourses, page: 1}).el);
}});
This can be made more elegant but it is a way to pass the parameters on to the server.
Thanks