From 7ff9cc3e2901ae0181e8b7ac73b9af158a6d62b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Huchet?= <raph@selego.co> Date: Thu, 10 Mar 2022 17:50:28 +0100 Subject: [PATCH 1/2] chore(security): place controller --- api/src/controllers/place.js | 128 ++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/api/src/controllers/place.js b/api/src/controllers/place.js index 476366f6d..8595b413c 100644 --- a/api/src/controllers/place.js +++ b/api/src/controllers/place.js @@ -2,31 +2,45 @@ const express = require("express"); const router = express.Router(); const passport = require("passport"); const { Op } = require("sequelize"); +const { z } = require("zod"); const { catchErrors } = require("../errors"); const validateOrganisationEncryption = require("../middleware/validateOrganisationEncryption"); const validateUser = require("../middleware/validateUser"); const Place = require("../models/place"); +const { looseUuidRegex, positiveIntegerRegex } = require("../utils"); router.post( "/", passport.authenticate("user", { session: false }), validateUser(["admin", "normal"]), validateOrganisationEncryption, - catchErrors(async (req, res, next) => { - if (!req.body.name) return res.status(400).send({ ok: false, error: "Name is needed" }); - - const newPlace = {}; - - newPlace.organisation = req.user.organisation; - - if (!req.body.hasOwnProperty("encrypted") || !req.body.hasOwnProperty("encryptedEntityKey")) { - return next("No encrypted field in place create"); + catchErrors(async (req, res) => { + try { + z.string().parse(req.body.encrypted); + z.string().parse(req.body.encryptedEntityKey); + } catch (e) { + return res.status(400).send({ ok: false, error: "Invalid request" }); } - if (req.body.hasOwnProperty("encrypted")) newPlace.encrypted = req.body.encrypted || null; - if (req.body.hasOwnProperty("encryptedEntityKey")) newPlace.encryptedEntityKey = req.body.encryptedEntityKey || null; - const data = await Place.create(newPlace, { returning: true }); - return res.status(200).send({ ok: true, data }); + const data = await Place.create( + { + organisation: req.user.organisation, + encrypted: req.body.encrypted, + encryptedEntityKey: req.body.encryptedEntityKey, + }, + { returning: true } + ); + return res.status(200).send({ + ok: true, + data: { + _id: data._id, + encrypted: data.encrypted, + encryptedEntityKey: data.encryptedEntityKey, + organisation: data.organisation, + createdAt: data.createdAt, + updatedAt: data.updatedAt, + }, + }); }) ); @@ -35,34 +49,30 @@ router.get( passport.authenticate("user", { session: false }), validateUser(["admin", "normal"]), catchErrors(async (req, res) => { + try { + z.optional(z.string().regex(positiveIntegerRegex)).parse(req.query.limit); + z.optional(z.string().regex(positiveIntegerRegex)).parse(req.query.page); + z.optional(z.string().regex(positiveIntegerRegex)).parse(req.query.lastRefresh); + } catch (e) { + return res.status(400).send({ ok: false, error: "Invalid request" }); + } + const { limit, page, lastRefresh } = req.query; + const query = { - where: { - organisation: req.user.organisation, - }, + where: { organisation: req.user.organisation }, order: [["createdAt", "DESC"]], }; - if (req.query.lastRefresh) { - query.where.updatedAt = { [Op.gte]: new Date(Number(req.query.lastRefresh)) }; - } const total = await Place.count(query); - const limit = parseInt(req.query.limit, 10); - if (!!req.query.limit) query.limit = limit; - if (req.query.page) query.offset = parseInt(req.query.page, 10) * limit; + if (limit) query.limit = Number(limit); + if (page) query.offset = Number(page) * limit; + if (lastRefresh) query.where.updatedAt = { [Op.gte]: new Date(Number(lastRefresh)) }; const data = await Place.findAll({ ...query, - attributes: [ - // Generic fields - "_id", - "encrypted", - "encryptedEntityKey", - "organisation", - "createdAt", - "updatedAt", - ], + attributes: ["_id", "encrypted", "encryptedEntityKey", "organisation", "createdAt", "updatedAt"], }); - return res.status(200).send({ ok: true, data, hasMore: data.length === limit, total }); + return res.status(200).send({ ok: true, data, hasMore: data.length === Number(limit), total }); }) ); @@ -71,28 +81,36 @@ router.put( passport.authenticate("user", { session: false }), validateUser(["admin", "normal"]), validateOrganisationEncryption, - catchErrors(async (req, res, next) => { - const query = { - where: { - _id: req.params._id, - organisation: req.user.organisation, - }, - }; - + catchErrors(async (req, res) => { + try { + z.string().regex(looseUuidRegex).parse(req.params._id); + z.string().parse(req.body.encrypted); + z.string().parse(req.body.encryptedEntityKey); + } catch (e) { + return res.status(400).send({ ok: false, error: "Invalid request" }); + } + const query = { where: { _id: req.params._id, organisation: req.user.organisation } }; const place = await Place.findOne(query); if (!place) return res.status(404).send({ ok: false, error: "Not found" }); - const updatePlace = {}; - if (!req.body.hasOwnProperty("encrypted") || !req.body.hasOwnProperty("encryptedEntityKey")) { - return next("No encrypted field in place update"); - } - if (req.body.hasOwnProperty("encrypted")) updatePlace.encrypted = req.body.encrypted || null; - if (req.body.hasOwnProperty("encryptedEntityKey")) updatePlace.encryptedEntityKey = req.body.encryptedEntityKey || null; - - place.set(updatePlace); + const { encrypted, encryptedEntityKey } = req.body; + place.set({ + encrypted: encrypted, + encryptedEntityKey: encryptedEntityKey, + }); await place.save(); - return res.status(200).send({ ok: true, data: place }); + return res.status(200).send({ + ok: true, + data: { + _id: place._id, + encrypted: place.encrypted, + encryptedEntityKey: place.encryptedEntityKey, + organisation: place.organisation, + createdAt: place.createdAt, + updatedAt: place.updatedAt, + }, + }); }) ); @@ -101,12 +119,12 @@ router.delete( passport.authenticate("user", { session: false }), validateUser(["admin", "normal"]), catchErrors(async (req, res) => { - const query = { - where: { - _id: req.params._id, - organisation: req.user.organisation, - }, - }; + try { + z.string().regex(looseUuidRegex).parse(req.params._id); + } catch (e) { + return res.status(400).send({ ok: false, error: "Invalid request" }); + } + const query = { where: { _id: req.params._id, organisation: req.user.organisation } }; const place = await Place.findOne(query); if (!place) return res.status(404).send({ ok: false, error: "Not Found" }); From 9e15792559cfb828badb03e314ce6509f0649ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Huchet?= <raph@selego.co> Date: Thu, 10 Mar 2022 17:52:01 +0100 Subject: [PATCH 2/2] chore(security): public controller --- api/src/controllers/public.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/api/src/controllers/public.js b/api/src/controllers/public.js index 98b3bb5b6..380357c88 100644 --- a/api/src/controllers/public.js +++ b/api/src/controllers/public.js @@ -1,8 +1,7 @@ const express = require("express"); const router = express.Router(); - +const { z } = require("zod"); const { catchErrors } = require("../errors"); - const Action = require("../models/action"); const Comment = require("../models/comment"); const Person = require("../models/person"); @@ -12,16 +11,25 @@ const TerritoryObservation = require("../models/territoryObservation"); const Report = require("../models/report"); const RelPersonPlace = require("../models/relPersonPlace"); const { Op } = require("sequelize"); +const { positiveIntegerRegex, looseUuidRegex } = require("../utils"); router.get( "/stats", catchErrors(async (req, res) => { + try { + if (req.query.organisation) z.string().regex(looseUuidRegex).parse(req.query.organisation); + z.optional(z.string().regex(positiveIntegerRegex)).parse(req.query.lastRefresh); + } catch (e) { + return res.status(400).send({ ok: false, error: "Invalid request" }); + } + const query = {}; if (req.query.organisation) query.where = { organisation: req.query.organisation }; if (Number(req.query.lastRefresh)) { if (!query.where) query.where = {}; query.where.updatedAt = { [Op.gte]: new Date(Number(req.query.lastRefresh)) }; } + const places = await Place.count(query); const relsPersonPlace = await RelPersonPlace.count(query); const actions = await Action.count(query);