Hapi/Joi Validation: How to process text to a specific format - node.js

Is it possible to pass a string to hapi/joi and for it to process it and return a required format? For example consider the code below:
const acceptedCitiesSchema = Joi.object({
isCapitalCity: Joi.boolean().default(true),
cityName: Joi.string().valid('MyCity', 'YourCity').required(),
})
How can I make it that even if a user inputs 'myCity' or 'yourCity' joi automatically converts them to a format that the first word is capitalized('MyCity' and 'YourCity' respecively). And returns the desired results? Is it possible to achieve this using hapi/joi?

You can try the following:
const schema = Joi.object({
isCapitalCity: Joi.boolean().default(true),
cityName: Joi.string().valid('MyCity', 'YourCity').insensitive().required()
})
And while validating with schema, you can use the convert:true option as:
schema.validate({"cityName": "myCity"}, {"convert": true})
Alternatively, you can directly provide additional preferences to convert the Joi validated object in the schema:
const schema = Joi.object({
isCapitalCity: Joi.boolean().default(true),
cityName: Joi.string().valid('MyCity', 'YourCity').insensitive().prefs({convert:true}).required()
})
Here is a link to working example: https://repl.it/repls/HarmfulEvenPhases

Related

Node.js mongoose schema validation - when using enum, is it case sensitive?

when creating a mongoose schema, and using enum in order to validate the values, is it case sensitive?
As in:
const mySchema = new Schema({
animal: {
type: String,
enum: ['cat', 'dog']
}
});
const myModel = db.model('animal', mySchema);
const animal1 = new myModel({
animal: 'CAT'
});
Will animal1 be saved or rejected?
Thanks in advance!
Mongoose has several inbuilt validators. Strings have enum as one of the validators. So enum creates a validator and checks if the value is given in an array & these are case sensitive so it will throw validation error, so you can use .toLowerCase() while saving the doc.
If only Mongoose had a way of manually validating a document. Oh wait, there is, and it's even documented.
console.log( animal1.validateSync() );
…
Error: animal validation failed: animal: `CAT` is not a valid enum value for path `animal`.

Mongoose Sync local schema with mongoose document

Im trying to get this going or wondering if this is even necessary. I have a local schema I define for my user profiles. What im trying to do is write a code block that checks my local schema with what the database has. If a field is missing, add a default value.
My goal for this is to be able to add fields locally in my schema and have the database update when I add a new field.
My schema is as follows:
import mongoose, { Schema } from "mongoose";
const reqString = {
type: String,
required: true,
};
const reqNumber = {
type: Number,
required: true,
};
const userProfileSchema = new Schema({
//Discord User ID - Primary Key
_id: reqString,
wallet: reqNumber,
bank: reqNumber,
net_worth: reqNumber,
classID: reqNumber,
});
const name = "core-userprofile";
export default mongoose.models[name] ||
mongoose.model(name, userProfileSchema, name);
I have a class that pulls the user profile for the rest of the code to access. Id like a function in there that looks at the local schema, realizes there isnt classID, figures out its a type of number, and just places 0 there.
I believe from what im understanding I can use update/upsert when accessing it. Im more wondering if there is a way to sync these two and add/delete anything that doesn't match the local version.
Thanks in advance!

Preventing NoSQL injection: Isn't mongoose supposed to convert inputs based on given schema?

Looking to prevent NoSQL injection attacks for a node.js app using mongodb.
var mongoose = require('mongoose'); // "^5.5.9"
var Schema = mongoose.Schema;
var historySchema = new Schema({
userId: {
type: String,
index: true,
},
message: {},
date: {
type: Date,
default: Date.now,
}
});
var history = mongoose.model('history', historySchema);
// the following is to illustrate the logic, not actual code
function getHistory(user){
history.find({userId: user}, function(err, docs) {
console.log(docs)
}
}
Based on this answer to a similar question, my understanding is that using mongoose and defining the field as string should prevent query injection. However, by changing the user input to a query object, it is possible to return all users. For example:
getHistory({$ne: 1}) // returns the history for all users
I am aware of other ways to prevent this type of attack before it gets to the mongoose query, like using mongo-sanitize. But I'd like to know if there's something wrong with the way I defined the schema or if one can't expect mongoose to convert inputs according to the schema.
Thanks in advance!
this part is good enough, you do not need anything else there. There is method that receives string and uses the string.
The best approach is to validate the input that can be modified (usually HTTP request) on top level before processing anything (I can recommend https://github.com/hapijs/joi its easy to use and you can check if there all required fields and if all fields are in correct format).
So put the validation into middleware just before it hits your controller. Or at the beginning of your controller.
From that point you are in full control of all the code and you believe what you got through your validation, so it cannot happen that someone pass object instead of string and get through.
Following the "skinny controllers, fat model" paradigm, it would be best to expose a custom validation schema from your model to be used in your controller for POST and PUT requests. This means that any data that attempts to enter your database will first be sanitized against a validation schema. Every Mongoose model should own its own validation schema.
My personal favorite for this is Joi. It's relatively simple and effective. Here is a link to the documentation: https://www.npmjs.com/package/#hapi/joi
A Joi schema permits type checking (i.e., Boolean vs. String vs. Number, etc), mandatory inputs if your document has the field required, and other type-specific enforcement such as "max" for numbers, enumerable values, etc.
Here is an example you'd include in your model:
const Joi = require('joi');
...
function validateHistory(history) {
const historySchema = {
userId: Joi.string(),
message: Joi.object(),
date: Joi.date()
}
return Joi.validate(history, historySchema);
}
...
module.exports.validate = validateHistory;
And then in your controller you can do:
const {
validate
} = require('../models/history');
...
router.post('/history', async (req, res) => {
const {
error
} = validate(req.body.data);
if (error) return res.status(400).send(error.details[0].message);
let history = new History({
userID: req.body.user,
message: req.body.message,
date: req.body.date
})
history = await history.save();
res.send(history);
});
*Note that in a real app this route would also have an authentication callback before handling the request.

Joi validating time field

There is object with a property time (22:30:00).
const schema = Joi.object.keys({
...
transactionDate: Joi.date().required(),
transactionTime: Joi.time().required(), // ???
...
});
How to validate a time field using Joi?
Try this way
const schema = Joi.object().keys({
...
transactionDate: Joi.string().regex(/^([0-9]{2})\:([0-9]{2})$/)
})
Hear I have used simple regex format.
You can also use this : ^([01]\d|2[0-3]):?([0-5]\d)$
for AM and PM \b((1[0-2]|0?[1-9]):([0-5][0-9])([AaPp][Mm]))
AM PM

Nodejs - Joi Check if string is in a given list

I'm using Joi package for server side Validation. I want to check if a given string is in a given list or if it is not in a given list.(define black list or white list for values)
sth like an "in" or "notIn" function. How can I do that?
var schema = Joi.object().keys({
firstname: Joi.string().in(['a','b']),
lastname : Joi.string().notIn(['c','d']),
});
You are looking for the valid and invalid functions.
v16: https://hapi.dev/module/joi/api/?v=16.1.8#anyvalidvalues---aliases-equal
v17: https://hapi.dev/module/joi/api/?v=17.1.1#anyvalidvalues---aliases-equal
As of Joi v16 valid and invalid no longer accepts arrays, they take a variable number of arguments.
Your code becomes
var schema = Joi.object().keys({
firstname: Joi.string().valid(...['a','b']),
lastname: Joi.string().invalid(...['c','d']),
});
Can also just pass in as .valid('a', 'b') if not getting the values from an array (-:
How about:
var schema = Joi.object().keys({
firstname: Joi.string().valid(['a','b']),
lastname : Joi.string().invalid(['c','d']),
});
There are also aliases: .allow and .only
and .disallow and .not
Update from 10 Oct 2022
Now valid function requires Object
Joi.string().valid({ ...values_array }),

Resources