From d11258c49035c5bb4517dcd02b56bfb424233d60 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:25:38 -0800 Subject: [PATCH] Sanitized paging arguments for history fetch --- src/helpers/index.js | 23 ++++++++++++++ src/services/history.js | 14 ++++++--- tests/services/history.test.js | 56 ++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/helpers/index.js b/src/helpers/index.js index b68de76..92309a0 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -88,6 +88,29 @@ export function sortBy(args) { return sort; } +export function validatePagingArgs(args) { + if (args.limit && !Number.isInteger(args.limit)) + return false; + + if (args.skip && !Number.isInteger(args.skip)) + return false; + + if (args.sortBy) { + const parts = args.sortBy.split(':'); + + if (parts.length != 2) + return false; + + if (!parts[0].match(/^[A-Za-z]+$/)) + return false; + + if (parts[1] != 'asc' && parts[1] != 'desc') + return false; + } + + return true; +} + export async function verifyOwnership(admin, element, domainId, action, routerType, cascade = false) { const domain = await getDomainById(domainId); if (admin._id.equals(domain.owner)) { diff --git a/src/services/history.js b/src/services/history.js index c599f22..5d20ce1 100644 --- a/src/services/history.js +++ b/src/services/history.js @@ -1,14 +1,18 @@ import History from '../models/history'; -import { sortBy } from '../helpers'; +import { sortBy, validatePagingArgs } from '../helpers'; +import { BadRequestError } from '../exceptions'; -export async function getHistory(query, domainId, elementId, specs = {}) { +export async function getHistory(query, domainId, elementId, pagingArgs = {}) { const findQuery = elementId ? { domainId, elementId } : { domainId }; + if (!validatePagingArgs(pagingArgs)) + throw new BadRequestError('Invalid paging args'); + return History.find(findQuery) .select(query) - .sort(sortBy(specs)) - .limit(parseInt(specs.limit || 10)) - .skip(parseInt(specs.skip || 0)) + .sort(sortBy(pagingArgs)) + .limit(parseInt(pagingArgs.limit || 10)) + .skip(parseInt(pagingArgs.skip || 0)) .exec(); } diff --git a/tests/services/history.test.js b/tests/services/history.test.js index a59b0bf..45f49e7 100644 --- a/tests/services/history.test.js +++ b/tests/services/history.test.js @@ -50,6 +50,26 @@ describe('Testing history services', () => { expect(history[0].elementId).toMatchObject(element1Id); }); + test('HISTORY_SERVICE - Should NOT get history - invalid paging args - limit', async () => { + const call = async () => { + await getHistory('elementId', domainId, element1Id, { + limit: '0' + }); + }; + + await expect(call()).rejects.toThrowError('Invalid paging args'); + }); + + test('HISTORY_SERVICE - Should NOT get history - invalid paging args - skip', async () => { + const call = async () => { + await getHistory('elementId', domainId, element1Id, { + skip: '0' + }); + }; + + await expect(call()).rejects.toThrowError('Invalid paging args'); + }); + test('HISTORY_SERVICE - Should get history - skip first entry', async () => { // given const timestamp = Date.now(); @@ -78,6 +98,42 @@ describe('Testing history services', () => { expect(history[0].oldValue.toJSON()).toMatchObject({ value: 1 }); }); + test('HISTORY_SERVICE - Should NOT get history entries sorted - invalid sortBy query spec', async () => { + const call = async () => { + await getHistory('elementId', domainId, element1Id, { + sortBy: 'oldValue:ASC' + }); + }; + + await expect(call()).rejects.toThrowError('Invalid paging args'); + }); + + test('HISTORY_SERVICE - Should NOT get history entries sorted - invalid sortBy query spec #2', async () => { + const call = async () => { + await getHistory('elementId', domainId, element1Id, { + sortBy: 'oldValue' + }); + }; + + await expect(call()).rejects.toThrowError('Invalid paging args'); + }); + + test('HISTORY_SERVICE - Should NOT get history entries sorted - invalid sortBy query argument', async () => { + // given + const timestamp = Date.now(); + await addHistory(domainId, element1Id, { value: 1 }, { value: 2 }, timestamp); + await addHistory(domainId, element1Id, { value: 3 }, { value: 4 }, timestamp); + + // test + const call = async () => { + await getHistory('elementId', domainId, element1Id, { + sortBy: 'oldValue&:asc' + }); + }; + + await expect(call()).rejects.toThrowError('Invalid paging args'); + }); + test('HISTORY_SERVICE - Should get history entries sorted by desc', async () => { // given const timestamp = Date.now();