The express 4x api docs claim that you can pass regex as a second argument to router.param in order validate params.
The method could now be used to effectively validate parameters (and
optionally parse them to provide capture groups)
It then provides the following examples.
// validation rule for id: should be one or more digits
router.param('id', /^\d+$/);
router.get('/user/:id', function(req, res) {
res.send('user ' + req.params.id);
});
// validation rule for range: should start with one more alphanumeric characters, followed by two dots, and end with one more alphanumeric characters
router.param('range', /^(\w+)\.\.(\w+)?$/);
router.get('/range/:range', function(req, res) {
var range = req.params.range;
res.send('from ' + range[1] + ' to ' + range[2]);
});
But, this doesn't actually seem to work. Taking a deeper dive, it doesn't look as if the express code actually supports what the docs claim. In fact, passing anything other than a function will get you a tidy invalid param() call exception.
Using
express 4.12.3
node 0.12.4
So my question is whether this functionality actually exists or if I'm doing something wrong. I'm trying to accomplish the same thing provided in the doc but am receiving the error mentioned above. Any guidance would be greatly appreciated :)
The answer can be found here.
Essentially the following snippet would need to be run prior to leveraging the router.param(fn) method as outlined above if you're using express <= 4.11.
express 4.11
router.param(function(name, fn) {
if (fn instanceof RegExp) {
return function(req, res, next, val) {
var captures;
if (captures = fn.exec(String(val))) {
req.params[name] = captures;
next();
} else {
next('route');
}
}
}
});
express 4.12
If you're using express >= 4.12 you can accomplish the same without the need of router.param(fn) using the following. In fact, the pre 4.12 example above will pop a deprecation warning.
app.get('/user/:userId([0-9]+)', fn);
While this is stated in the doc, it isn't quite clear.Hope this helps.
I was facing the same issue and I got it resolved by converting my code
exports.customerValidator = () => {
return [
body("name").trim().toString(),
body("email")
.isEmail()
.withMessage("Please enter a valid email id")
.custom((value, { req }) => {
return Customer.findOne({ email: value })
.then((customer) => {
if (customer) {
return Promise.reject("E-mail address is already exists!");
}
})
})
.normalizeEmail(),
body("address").trim().toString(),
body("city").trim().toString()
]
}
to this:
exports.customerValidator = () => {
return [
body('name').trim(),
body("email")
.isEmail()
.normalizeEmail()
.custom((value, { req }) => {
return Customer.find({ email: value })
.then((customer) => {
if (customer) {
return Promise.reject("E-mail address is already exists!");
}
})
}),
body("address").trim(),
body("city").trim(),
];
};
Related
Codecademy video: link
Explanation:
As part of my Codecademy Back-End Engineer training, I have to do a project outside of their platform. The goal of this project is to make sure a node application is protected from common web attacks.
One challenge I faced was securing the code from Cross-Site Scripting (XSS) attacks. To do this, I used a package called express-validator#6.12.1. The code uses a function called validator.escape which is supposed to protect against any malicious code being inserted into an input form. However, I am getting an error in the console when I try to use it.
Terminal output :
TypeError: validator.escape is not a function
Here is the code :
const validator = require("express-validator");
app.post("/public_forum", function (request, response) {
if (request.session.loggedin) {
var comment = validator.escape(request.body.comment);
var username = request.session.username;
if (comment) {
db.all(
`INSERT INTO public_forum (username,message) VALUES ('${username}','${comment}')`,
(err, rows) => {
console.log(err);
}
);
db.all(`SELECT username,message FROM public_forum`, (err, rows) => {
console.log(rows);
console.log(err);
response.render("forum", { rows });
});
} else {
db.all(`SELECT username,message FROM public_forum`, (err, rows) => {
console.log(rows);
console.log(err);
response.render("forum", { rows });
});
}
comment = "";
} else {
response.redirect("/");
}
comment = "";
//response.end();
});
In the video of Codecademy, the guy uses this function.
Try with:
const {check, validationResult} = require('express-validator');
app.post('/public_forum', async function (request, response) {
if (request.session.loggedin) {
await check('comment').trim().escape().run(req);
const validationResult = await validationResult(req);
if (validationResult.isEmpty()) {
// Good to go...
const { comment } = req.body;
}
...
Link to official docs
I have implemented your code. I tried to add both a malicious and safe comment, but I got an error message on my browser that said, "Port 4000 Not Found." Every time I run the code, it kills the port. So I have implemented another code that works well based on what you sent me.
// This code defines a post request handler for the "/public_forum" endpoint.
app.post('/public_forum', async function (request, response) {
// Check if the user is logged in by checking the session data.
if (request.session.loggedin) {
// Trim and escape the incoming comment.
await check('comment').trim().escape().run(request);
// Get the validation result of the incoming comment.
const errors = validationResult(request);
// If the validation result contains errors, return a 400 status with the errors in a JSON format.
if (!errors.isEmpty()) {
return response.status(400).json({ errors: errors.array() });
}
// Get the comment from the request body.
const { comment } = request.body;
// If a valid comment exists, insert it into the "public_forum" database table.
if (comment) {
db.run(
`INSERT INTO public_forum (username,message) VALUES (?,?)`, [request.session.username, comment],
(err) => {
// If an error occurs while inserting the comment, log the error.
if (err) {
console.error(err);
}
}
);
}
// Select all the rows from the "public_forum" table.
db.all(`SELECT username,message FROM public_forum`, (err, rows) => {
// If an error occurs while selecting the rows, log the error.
if (err) {
console.error(err);
}
// Log the selected rows.
console.log(rows);
// Render the "forum" template, passing in the selected rows as a parameter.
response.render("forum", { rows });
});
} else {
// If the user is not logged in, redirect them to the homepage.
response.redirect("/");
}
});
Is there a better way to implement the PUT method in Express than explicitly assigning each value from the request to a db loaded object (from MongoDB)?
function put(req, res) {
const { school } = req;
school.name = req.body.name;
school.nature = req.body.nature;
school.website = req.body.website;
school.facebookURL = req.body.facebookURL;
school.instagramURL = req.body.instagramURL;
...
req.school.save((err) => {
if (err) {
return res.send(err);
}
return res.json(school);
});
}
My document has quite a lot of attributes and the JSON will get quite complex. I am relatively new to Express and would like to know if there is a trick to do this more optimally.
Assuming the loaded object(school) is a mongodb collection.
You can use the update method of the collection without explicitly specifying the data to update.
function put(req, res) {
const { school } = req;
school.update({
_id:ObjectId(req.param.id)
},
req.body)
.then(function (success) {
res.json();
})
.catch(function (error) {
res.status(404).send(err);
}); }
ps: on mobile device right now, so formatting might be poor.
Following the hint given to me by #LawrenceCherone in the comments, I came up with a pretty elegant solution to the problem. I am doing the validation in the model schema, so the code here can be short and sweet!
function put(req, res) {
const { school } = req;
Object.assign(school, req.body);
req.school.save((err) => {
if (err) {
return res.send(err);
}
return res.json(school);
});
}
I am write API in expressjs with Sequence. But I got problem with return value when update!
I follow a tutorial on internet but it got error when num=[1] not num=1 although updating success!
exports.update = (req, res) => {
const { id } = req.params;
Post.update(req.body, {
where: { id }
})
.then((num) => {
if (num === 1) {
res.send({
message: 'Post was updated successfully.'
});
}
else {
res.send({
message: `Cannot update Post with id=${id}. Maybe Post was not found or req.body is empty!`
});
}
})
.catch((err) => {
res.status(500).send({
message: `Error updating Post with id=${id}`
});
});
};
So, what return value after call update method? and how do I solve it? Thanks.
The sequelize document of update is
public static async update(values: object, options: object): Promise<Array<number, number>>
Promise<Array<number, number>>
The promise returns an array with one or two elements. The first element is always the number of affected rows, while the second element is the actual affected rows (only supported in postgres with options.returning true).
So, it will not return only the number. you need to follow the document.
To resolve
.then((nums) => {
const num = nums[0]
.....
I have a form with the onSubmit function collecting input data from the state and sending it to the backend.
I then collect the input from the req.body and the ip from the headers on the backend.
The ip is persisted to redis and the form input is being passed to another daemon process through pm2 and finally mailed with mandrill, rather than being persisted to any db.
Scenario I
The clients ip is collected and persisted to redis:
module.exports = (req, res, next) => {
const client = redis.createClient()
client.select(2, (err) => {
console.log('redisWriteIP selected 2snd redis db')
if (err) {
next(new DbErr(err))
} else {
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
client.set(ip, true, 'EX', 120, (err, rep) => {
if (err) {
next(new DbErr(err))
} else {
return next()
}
})
}
})
}
Question 1:
Do I need to sanitise the ip In this scenario? Can a user temper with the request headers and send anything else other than his ip address or numbers?
Scenario 2
Input fields filled in by the user and sent to the api on the req.body
The api server - using body parser:
const api = express()
// Body parser for the post requests
const bodyParser = require('body-parser')
api.use(bodyParser.urlencoded({ extended: false }))
api.use(bodyParser.json())
api.set('trust proxy', 'loopback')
const routes = require('./routes')
api.use('/api', routes)
Validating fields middlware:
module.exports = (req, res, next) => {
let payload = req.body
const err = {}
let isFormValid = true
// Validating a form.
if (payload.question) {
if (typeof payload.email !== 'string' || !validator.isEmail(payload.email)) {
isFormValid = false
err.email = 'Please provide a correct email address.'
}
if (typeof payload.name !== 'string' || payload.name.trim().length === 0) {
isFormValid = false
err.name = 'Please provide your name.'
}
// Validating another form.
} else if (payload.booking) {
if (typeof payload.email !== 'string' || !validator.isEmail(payload.email)) {
isFormValid = false
err.email = 'Please provide a correct email address.'
}
if (typeof payload.dates !== 'string' || payload.dates.trim().length === 0) {
isFormValid = false
err.msg = 'Something went wrong'
}
} else {
// No form type in the payload.
isFormValid = false
err.msg = 'Something went wrong'
}
if (!isFormValid) {
next(new FormFieldErr(JSON.stringify(err)))
} else {
return next()
}
}
Example of how the data is being sent to another process:
...
// Send the payload to the mandrill pid.
pm2.sendDataToProcessId(pid, payload, (err, res) => {
if (err) {
next(new MailerErr(err))
} else {
next()
}
})
Question 2:
Do I need to sanitise the req.body before doing any kind of operations with it's data even when it's not persisted to any db.
For example before I check if (payload.question) {...} in the validation middleware or before I send the payload with the pm2.sendDataToProcessId method?
I worry that a function can be passed from the client and executed on the backend even if no data is persisted.
Question 3
If the above are indeed a security risk, can I simply have a middlware running in the beginning of the chain on the req.body and any other parts of the request I might use, escaping or deleting all dangerous characters and effectively solving the problem?
EDIT
I've seen libs that validate the fields, but I don't need an extensive validation solution, but rather a simple sanitation solution. That's why I thought of making or installing a middleware which will first save the req.body or any other data without dangerous characters and then the other middlwares can deal with the data safely.
Something like:
sanitise middleware:
module.exports = (req, res, next) => {
req.body.replace(/[|&;$%#"<>()+,]/g, "")
return next()
}
some api route:
api.route('/', sanitise, someMiddleware, (req, res, next) => {
// Now we can safely handle req.body in the middlwares.
})
Answer 1: yes user can change any of the headers. But not the req.connection.remoteAddress. So you may want to give priority to that one.
Answer 2: Yes, escaping strings and validating combination of data is generally a good practice. You should do it at the API level.
Answer 3: I like Joi as a nice API validation package. There are other packages which might suit your needs better.
what's the proper way to check for undefined values? What I want to do is to have a PUT method that will update those fields that are not empty. For example, if I send req.body.name = 'John' and no req.body.job I want my request to only change the name.
Some code:
router.put('/:id', (req, res) => {
const query = {_id: req.params.id};
const update = {
$set: {
name: req.body.name,
job: req.body.job
}
};
User.findOneAndUpdate(query, update,
(err, userUpdated) => {
if (err) {
console.log('Error while updating');
console.log(err);
} else {
res.send(userUpdated);
}
});
});
This will of course throw an error:
CastError: Cast to number failed for value "undefined" at path "job"
Now I can manually check if req.body.job is empty and if it is set it's value to the value the user had previously, but that seems like a hack, not elegant and a lot of writing for each route.
I have checked the docs but none of the options provided there seem to do the job. I also came across something like express validator but this will probably just do a return if the value is empty. Another options would be to simply send the value from the front-end part.
I'm new to backend development and I'm not sure if I'm doing stuff the "right way". So please, any comment on how it should be done would be nice (also if my code looks odd, feel free to guide me :)), thanks!
You can write your own method to do this.
For example this example
var req = {body: {name: undefined, job: 'yes'}};
const _ = require('lodash');
const out = {};
_(req.body).forEach((value,key) => {
if (!_.isEmpty(value)){
out[key] = value;
}
});
console.log(out);
Is having this output
{ job: 'yes' }
You can also write it as middleware, if you want, if you write it as this
function onlyNotEmpty(req, res, next) => {
const out = {};
_(req.body).forEach((value, key) => {
if (!_.isEmpty(value)) {
out[key] = value;
}
});
req.bodyNotEmpty = out;
next();
}
Then you can write your method with middleware
router.put('/:id', onlyNotEmpty, (req, res) => {
const query = {_id: req.params.id};
const update = {
$set: req.bodyNotEmpty
};
// This will be the same
});