Fastify equivalent of express-mongo-sanitize - jsonschema

Hello Fastify Experts,
In MongoDB queries I can pass various operators, which may risks the security aspect by having various attack surfaces.
So before sending the payload, I would like to sanitize the query/filters/sort etc. However I don't think I need to sanitize the request payload as such because Mongo will anyway store it as BSON, hence safer.
Now in Express world, we used to have the express-mongo-sanitize sort of plugin.
What open source plugin you propose for Fastify world to achieve the similar functionality?
Thanks,
Pradip

You have two options:
use the schema eviction: adding additionalProperties as flag into the input schema, will remove all the keys you did not expect from input
With this code, you can submit a payload with:
{
foo: 'hello',
$where: 'cut'
}
and the $where key will be removed.
const fastify = require('fastify')({ logger: true })
fastify.post('/', {
schema: {
body: {
type: 'object',
additionalProperties: false,
properties: {
foo: { type: 'string' }
}
}
}
},
async (request, reply) => {
console.log(request.body)
return request.body
})
fastify.listen(8080)
The framework you linked has a module feature and you can integrate it with an hook:
const mongoSanitize = require('express-mongo-sanitize');
fastify.addHook('preHandler', function hook (request, reply, done) {
mongoSanitize.sanitize(request.body);
done(null)
})

Related

RTK Query where result is not JSON parsable

I am doing this as a learning exercise for react-toolkit:
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
export const getIPv4AddressApi = createApi({
reducerPath: "getIPv4Address",
baseQuery: fetchBaseQuery({
baseUrl: "https://ip4.8n1.org/",
}),
endpoints: builder => ({
getIPv4Address: builder.query<string, void>({
query: () => "",
}),
}),
});
export const { useGetIPv4AddressQuery } = getIPv4AddressApi;
When run, I get an error
{"status":"PARSING_ERROR","originalStatus":200,"data":"xxx.xxx.xxx.xxx\n","error":"SyntaxError: Unexpected token . in JSON at position 6"}
How can I just return the whole response without it trying to JSON parse? Many thanks
See parsing a response
endpoints: builder => ({
getIPv4Address: builder.query<string, void>({
query: () => "",
responseHandler: "text"
}),
}),
Instead of leaving the query: as an empty string, you can specified the data format. Here is an example:enter image description here
You have to provide your own ResponseHandler in order for the response body to not be parsed as JSON. From https://redux-toolkit.js.org/rtk-query/api/fetchBaseQuery#parsing-a-response
Parsing a Response
By default, fetchBaseQuery assumes that every Response you get will be parsed as json. In the event that you don't want that to happen, you can customize the behavior by specifying an alternative response handler like text, or take complete control and use a custom function that accepts the raw Response object — allowing you to use any Response method.
The responseHandler field can be either:
TypeScript
JavaScript
type ResponseHandler =
| 'content-type'
| 'json'
| 'text'
| ((response: Response) => Promise<any>)
The "json" and "text" values instruct fetchBaseQuery to the corresponding fetch response methods for reading the body. content-type will check the header field to first determine if this appears to be JSON, and then use one of those two methods. The callback allows you to process the body yourself.
More helpful discussions here as well: https://github.com/reduxjs/redux-toolkit/issues/2440
Examples from the GitHub:
export const api = generatedAuthApi.enhanceEndpoints({
endpoints: {
users: {
query: queryArg => ({
url: '/users',
method: 'POST',
body: queryArg.body,
responseHandler: (response: { text: () => any }) => response.text()
})
},
}
})

Cypress access alias with this.* doesn't works

I'm having a little problem understanding Cypress documentation. In the alias section they've added a use case of accessing alias with fixtures using the this.* reference:
beforeEach(() => {
// alias the users fixtures
cy.fixture("users.json").as("users");
});
it("utilize users in some way", function () {
// access the users property
const user = this.users[0];
// make sure the header contains the first
// user's name
cy.get("header").should("contain", user.name);
});
But when I try to reproduce it, I keep getting the error: Cannot read property 'SOAP_body' of undefined.
I don't understand where is my error. Here is my spec:
/// <reference types="cypress"/>
describe("SOAP API Test", () => {
beforeEach(() => {
cy.fixture("SOAP_body.xml").as("SOAP_body");
});
it("Test with task", function () {
const body = this.SOAP_body;
cy.request({
method: "POST",
headers: {
"content-type": "text/xml; charset=utf-8",
Authorization: "Token myVerySecretToken",
SOAPAction: "http://tempuri.org/TrackingFull",
},
url: `https://path.of/the/application.asmx`,
body: body,
failOnStatusCode: false,
}).then((result) => {
expect(result.status).to.equal(200);
cy.task("XMLtoJSON", result.body).then((response) => {
expect(
response.elements[0].elements[1].elements[0].elements[0]
.elements[1].elements[0].elements[0].elements[0]
.elements[0].elements[0].text
).to.equal("something");
});
});
});
});
and my task
/**
* #type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
on("task", {
XMLtoJSON(XML_body) {
var convert = require("xml-js");
let result = convert.xml2js(XML_body, {
compact: false,
spaces: 2,
});
return result;
},
});
};
Using debugger just before the const definition I can see that the variables are undefined
I do know about cy.get(), but I just wanted to learn how to use the this.* pattern.
After fiddling with the code I've realized that I was using an arrow function in the step definition:
it("Test with task", () => { ... }
I've done it simply because I use a lot of code snippets in VSC, and never paid attention to the syntax is used.
So, after seeing it, I've remembered that it would never work, as the MDN documentation says:
An arrow function expression is a compact alternative to a traditional
function expression, but is limited and can't be used in all
situations.
Differences & Limitations:
Does not have its own bindings to this or super, and should not be used as methods.
Does not have arguments, or new.target keywords.
Not suitable for call, apply and bind methods, which generally rely on establishing a scope.
Can not be used as constructors.
Can not use yield, within its body.
The solution was simple as replacing it with a function definition:
it("Test with task", function () { ... }
and the this context was as expected
Moral of the history, don't trust blindly in your code editor (even if its VSC)

Store Ability in Express Session?

I have seen the express example, where an ability is stored via middleware in the req object. It then uses the following method to evaluate the permissions:
ForbiddenError.from(req.ability).throwUnlessCan('read', article);
I want to achieve a similar thing. My idea is to save the ability inside an express session that is shared with socket io websockets. Through the sharing req.session = socket.handshake.session. My approach is the following, I make a request from the frontend application to get rules to update the ability on the frontend. The backend saves the ability inside the express session:
// abilities.js file
import { Ability } from '#casl/ability';
export const defineAbilitiesFor = (rules) => {
return new Ability(rules);
};
export default defineAbilitiesFor;
// handler for express route to get permissions from the frontend
export const getPermissions = async (req, res) => {
...
rules.push({
action: ['view'],
subject: views,
});
// manage all own processes
rules.push({
action: ['manage'],
subject: 'Process',
conditions: {
userId: req.kauth.grant.access_token.content.sub,
},
});
// store ability in session
req.session.rules = defineAbilitiesFor(rules);
const token = jwt.sign({ token: packRules(rules) }, 'secret');
if (token) {
return res.status(200).json(token);
} else {
return res.status(400).json('Error');
}
...
Then when a websocket request happens, I want to check in the backend if the user has the permissions to do that action:
ForbiddenError.from(socket.handshake.session.rules).throwUnlessCan('view', 'Process');
However, this throws the following error:
TypeError: this.ability.relevantRuleFor is not a function
at ForbiddenError.throwUnlessCan
The session object seems to have the correct ability object. When I console.log socket.handshake.session.rules, I get the following output:
{
h: false,
l: {},
p: {},
'$': [
{ action: [Array], subject: 'Process', conditions: [Object] },
{ action: [Array], subject: [Array] },
{ action: [Array], subject: 'Process', conditions: [Object] }
],
m: {}
}
Also the can function and everything else I tried wasn't working. I think storing the plain rules as an object inside the session and then updating the ability class before each request would work, but I don't want to do that. I want to store the ability right inside the session, so that I only have to execute the throwUnlessCan or can functions.
Is this even possible and if so, how would you do this?
Thanks so far.
Instead of storing the whole Ability instance, you need to store only its rules! rules is a plain js array of objects, so it can be easily serialized.So, change the code to this:
export const getPermissions = async (req, res) => {
...
rules.push({
action: ['view'],
subject: views,
});
// manage all own processes
rules.push({
action: ['manage'],
subject: 'Process',
conditions: {
userId: req.kauth.grant.access_token.content.sub,
},
});
// store ability RULES in session
req.session.rules = rules;
const token = jwt.sign({
token: packRules(rules) // packRules accepts an array of RawRule! not an Ability instance
}, 'secret');
if (token) {
return res.status(200).json(token);
} else {
return res.status(400).json('Error');
}
To use Ability in other handlers add a middleware:
function defineAbility(req, res, next) {
if (req.session.rules) {
req.ability = new Ability(req.session.rules);
next();
} else {
// handle case when there is no rules in session yet
}
}
// later
app.get('/api/users', defineAbility, (req, res) => {
req.ability.can(...);
// or
ForbiddenError.from(req.ability).throwUnlessCan(...);
})

Express/GraphQL advice

I'm learning GraphQL and looking for some guidance on if I'm on the right track, or if I'm just completely off base here. Essentially, I've written my backend with express-graphql and Prisma, I've seen a few different tutorials that seemed to write the types and resolvers separately using large template strings to define the schema. I followed the Net Ninja's GraphQL tutorial which used new GraphQlObjectType() to build the schema and imported all the different types into a single root query, which is then passed into the GraphQlSchema()
My confusion and question: Is this the right way to be doing this?
What I've ended up with is a very long schema file that is functional and working, but not ideal for maintenance. I've struggled to find any examples online on how to separate this code and still have it functional. I've tried exporting a file of just GraphQL object types and importing them into a schema.js file and then building my root query there but that doesn't seem to work. I've tried exporting only the fields as an object and then using the spread operator to assemble my root query, and I've tried multiple new GraphQlSchema() statements within my type files and then using graphql-tools mergeSchema to bring it all together but that didn't work either. I'm a little lost at this point and just need some guidance or an example to look at of a bigger project built in this way.
For reference, here's a snippet example to be clear on how I've built my GQL backend, to clarify the queries are working just fine, it's code maintenance here I'm gratefully looking for advice on :)
Thank you for any help or examples!
const prisma = require("../config/prismaClient");
const graphql = require('graphql');
const {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
GraphQLList } = graphql;
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
id: { type: GraphQLString },
first_name: { type: GraphQLString },
last_name: { type: GraphQLString },
})
})
const BusinessType = new GraphQLObjectType({
name: 'Business',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
street_address: { type: GraphQLString },
})
})
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
users: {
type: new GraphQLList(UserType),
async resolve() {
return await prisma.users.findMany();
}
},
businesses: {
type: new GraphQLList(BusinessType),
async resolve() {
return await prisma.businesses.findMany();
}
}
}
})
module.exports = new GraphQLSchema({
query: RootQuery
})
My confusion and question: Is this the right way to be doing this?
There is no right or wrong way of writing you schema. You can use SDL (Schema definition language) which is very readable but then you need to add some dependencies to your project to set this up. GraphQL Tools has very good utilities that make it easy to write you schema.
Yes it can be little overwhelming when trying to setup these tools. I like splitting my schema and resolvers in a directory based on Type and then graphql-tools helps me merge all the resolvers and schema together into one.
I found this project https://github.com/lucassus/bookshelf take a look at how they structured there graphql server https://github.com/lucassus/bookshelf/tree/master/apps/server/src/interfaces/graphql

Restify: Set default formatter

Also asked in official Restify repo: #1224
Hi,
Is it possible to have one default formatter that can handle any accept type that is not defined.
For Example:
restify.createServer({
formatters: {
'application/json': () => {},
// All other requests that come in are handled by this, instead of throwing error
'application/every-thing-else': () => {}
}
});
By all appearances, this is impossible. Since the formatters are stored in a dictionary, there is no way to create a key that matches every input (that would kind of defeat the point of a dictionary anyway...) The only way to accomplish this kind of thing outside of JSON would be with a regular expression, and regular expressions don't work with JSON.
Here is a program I wrote to test this.
var restify = require("restify");
var server = restify.createServer({
formatters: {
'application/json': () => { console.log("JSON") },
"[\w\W]*": () => { console.log("Everything else") } // Does not work
}
});
server.get("/", (req, res, next) => {
console.log("Root");
res.setHeader("Content-Type", "not/supported");
res.send(200, {"message": "this is a test"});
next()
});
server.listen(10000);
Also here is a link to the documentation on this in case you can find some hint that I couldn't see.
Restify documentation