diff --git a/server/src/api/controllers/Accounts.ts b/server/src/api/controllers/Accounts.ts index 98790f1d9..f4ce2c961 100644 --- a/server/src/api/controllers/Accounts.ts +++ b/server/src/api/controllers/Accounts.ts @@ -7,6 +7,7 @@ import AccountsService from 'services/Accounts/AccountsService'; import { IAccountDTO, IAccountsFilter } from 'interfaces'; import { ServiceError } from 'exceptions'; import DynamicListingService from 'services/DynamicListing/DynamicListService'; +import { DATATYPES_LENGTH } from 'data/DataTypes'; @Service() export default class AccountsController extends BaseController{ @@ -112,7 +113,7 @@ export default class AccountsController extends BaseController{ return [ check('name') .exists() - .isLength({ min: 3, max: 255 }) + .isLength({ min: 3, max: DATATYPES_LENGTH.STRING }) .trim() .escape(), check('code') @@ -122,16 +123,16 @@ export default class AccountsController extends BaseController{ .escape(), check('account_type_id') .exists() - .isNumeric() + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .toInt(), check('description') .optional({ nullable: true }) - .isLength({ max: 512 }) + .isLength({ max: DATATYPES_LENGTH.TEXT }) .trim() .escape(), check('parent_account_id') .optional({ nullable: true }) - .isNumeric() + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .toInt(), ]; } diff --git a/server/src/api/controllers/Authentication.ts b/server/src/api/controllers/Authentication.ts index e219e5a81..b5c50649c 100644 --- a/server/src/api/controllers/Authentication.ts +++ b/server/src/api/controllers/Authentication.ts @@ -6,6 +6,7 @@ import asyncMiddleware from 'api/middleware/asyncMiddleware'; import AuthenticationService from 'services/Authentication'; import { ILoginDTO, ISystemUser, IRegisterOTD } from 'interfaces'; import { ServiceError, ServiceErrors } from "exceptions"; +import { DATATYPES_LENGTH } from 'data/DataTypes'; @Service() export default class AuthenticationController extends BaseController{ @@ -60,12 +61,12 @@ export default class AuthenticationController extends BaseController{ */ get registerSchema(): ValidationChain[] { return [ - check('first_name').exists().trim().escape(), - check('last_name').exists().trim().escape(), - check('email').exists().isEmail().trim().escape(), - check('phone_number').exists().trim().escape(), - check('password').exists().trim().escape(), - check('country').exists().trim().escape(), + check('first_name').exists().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('last_name').exists().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('email').exists().isString().isEmail().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('phone_number').exists().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('password').exists().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('country').exists().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), ]; } diff --git a/server/src/api/controllers/Contacts/Contacts.ts b/server/src/api/controllers/Contacts/Contacts.ts index 3ea24623f..4d8f845f3 100644 --- a/server/src/api/controllers/Contacts/Contacts.ts +++ b/server/src/api/controllers/Contacts/Contacts.ts @@ -1,5 +1,6 @@ import { check, param, query, body, ValidationChain } from 'express-validator'; import BaseController from "api/controllers/BaseController"; +import { DATATYPES_LENGTH } from 'data/DataTypes'; export default class ContactsController extends BaseController { /** @@ -7,37 +8,37 @@ export default class ContactsController extends BaseController { */ get contactDTOSchema(): ValidationChain[] { return [ - check('salutation').optional().trim().escape(), - check('first_name').optional().trim().escape(), - check('last_name').optional().trim().escape(), + check('salutation').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('first_name').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('last_name').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), - check('company_name').optional().trim().escape(), - check('display_name').exists().trim().escape(), + check('company_name').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('display_name').exists().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), - check('email').optional({ nullable: true }).normalizeEmail().isEmail(), - check('website').optional().trim().isURL(), - check('work_phone').optional().trim().escape(), - check('personal_phone').optional().trim().escape(), + check('email').optional({ nullable: true }).isString().normalizeEmail().isEmail().isLength({ max: DATATYPES_LENGTH.STRING }), + check('website').optional().isString().trim().isURL().isLength({ max: DATATYPES_LENGTH.STRING }), + check('work_phone').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('personal_phone').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), - check('billing_address_1').optional().trim().escape(), - check('billing_address_2').optional().trim().escape(), - check('billing_address_city').optional().trim().escape(), - check('billing_address_country').optional().trim().escape(), - check('billing_address_email').optional().isEmail().trim().escape(), - check('billing_address_postcode').optional().trim().escape(), - check('billing_address_phone').optional().trim().escape(), - check('billing_address_state').optional().trim().escape(), + check('billing_address_1').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('billing_address_2').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('billing_address_city').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('billing_address_country').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('billing_address_email').optional().isString().isEmail().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('billing_address_postcode').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('billing_address_phone').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('billing_address_state').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), - check('shipping_address_1').optional().trim().escape(), - check('shipping_address_2').optional().trim().escape(), - check('shipping_address_city').optional().trim().escape(), - check('shipping_address_country').optional().trim().escape(), - check('shipping_address_email').optional().isEmail().trim().escape(), - check('shipping_address_postcode').optional().trim().escape(), - check('shipping_address_phone').optional().trim().escape(), - check('shipping_address_state').optional().trim().escape(), + check('shipping_address_1').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('shipping_address_2').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('shipping_address_city').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('shipping_address_country').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('shipping_address_email').optional().isString().isEmail().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('shipping_address_postcode').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('shipping_address_phone').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), + check('shipping_address_state').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.STRING }), - check('note').optional().trim().escape(), + check('note').optional().isString().trim().escape().isLength({ max: DATATYPES_LENGTH.TEXT }), check('active').optional().isBoolean().toBoolean(), ]; } @@ -48,8 +49,10 @@ export default class ContactsController extends BaseController { */ get contactNewDTOSchema(): ValidationChain[] { return [ - check('opening_balance').optional({ nullable: true }).isNumeric().toInt(), - body('opening_balance_at').if(body('opening_balance').exists()).exists(), + check('opening_balance').optional({ nullable: true }).isInt({ min: 0, max: DATATYPES_LENGTH.DECIMAL_13_3 }).toInt(), + body('opening_balance_at') + .if(body('opening_balance').exists()).exists() + .isISO8601(), ]; } diff --git a/server/src/api/controllers/Contacts/Customers.ts b/server/src/api/controllers/Contacts/Customers.ts index 433f74b51..80a0d5ed8 100644 --- a/server/src/api/controllers/Contacts/Customers.ts +++ b/server/src/api/controllers/Contacts/Customers.ts @@ -90,7 +90,7 @@ export default class CustomersController extends ContactsController { */ get createCustomerDTOSchema() { return [ - check('currency_code').optional().trim().escape(), + check('currency_code').optional().isString().trim().escape().isLength({ max: 3, min: 3 }), ]; } diff --git a/server/src/api/controllers/Contacts/Vendors.ts b/server/src/api/controllers/Contacts/Vendors.ts index 5a7041823..8c6566194 100644 --- a/server/src/api/controllers/Contacts/Vendors.ts +++ b/server/src/api/controllers/Contacts/Vendors.ts @@ -73,7 +73,12 @@ export default class VendorsController extends ContactsController { */ get vendorDTOSchema(): ValidationChain[] { return [ - check('currency_code').optional().trim().escape(), + check('currency_code') + .optional() + .isString() + .trim() + .escape() + .isLength({ min: 3, max: 3 }), ]; } diff --git a/server/src/api/controllers/ItemCategories.ts b/server/src/api/controllers/ItemCategories.ts index 3b8a63e40..10c9255c0 100644 --- a/server/src/api/controllers/ItemCategories.ts +++ b/server/src/api/controllers/ItemCategories.ts @@ -11,6 +11,7 @@ import { IItemCategoryOTD } from 'interfaces'; import { ServiceError } from 'exceptions'; import BaseController from 'api/controllers/BaseController'; import DynamicListingService from 'services/DynamicListing/DynamicListService'; +import { DATATYPES_LENGTH } from 'data/DataTypes'; @Service() export default class ItemsCategoriesController extends BaseController { @@ -78,26 +79,32 @@ export default class ItemsCategoriesController extends BaseController { */ get categoryValidationSchema() { return [ - check('name').exists().trim().escape(), + check('name') + .exists() + .trim() + .escape() + .isLength({ min: 0, max: DATATYPES_LENGTH.STRING }), check('parent_category_id') .optional({ nullable: true }) - .isNumeric() + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .toInt(), check('description') .optional() + .isString() .trim() - .escape(), + .escape() + .isLength({ max: DATATYPES_LENGTH.TEXT }), check('sell_account_id') .optional({ nullable: true }) - .isNumeric() + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .toInt(), check('cost_account_id') .optional({ nullable: true }) - .isNumeric() + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .toInt(), check('inventory_account_id') .optional({ nullable: true }) - .isNumeric() + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .toInt(), ] } diff --git a/server/src/api/controllers/Items.ts b/server/src/api/controllers/Items.ts index e24c0f47d..b0f83caec 100644 --- a/server/src/api/controllers/Items.ts +++ b/server/src/api/controllers/Items.ts @@ -7,6 +7,7 @@ import BaseController from 'api/controllers/BaseController'; import DynamicListingService from 'services/DynamicListing/DynamicListService'; import { ServiceError } from 'exceptions'; import { IItemDTO } from 'interfaces'; +import { DATATYPES_LENGTH } from 'data/DataTypes'; @Service() export default class ItemsController extends BaseController { @@ -78,44 +79,74 @@ export default class ItemsController extends BaseController { */ get validateItemSchema(): ValidationChain[] { return [ - check('name').exists(), - check('type').exists().trim().escape() + check('name').exists().isString().isLength({ max: DATATYPES_LENGTH.STRING }), + check('type').exists() + .isString() + .trim() + .escape() .isIn(['service', 'non-inventory', 'inventory']), - check('code').optional({ nullable: true }).trim().escape(), + check('code') + .optional({ nullable: true }) + .isString() + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.STRING }), // Purchase attributes. check('purchasable').optional().isBoolean().toBoolean(), check('cost_price') + .optional({ nullable: true }) + .isFloat({ min: 0, max: DATATYPES_LENGTH.DECIMAL_13_3 }) + .toFloat() .if(check('purchasable').equals('true')) - .exists() - .isNumeric() - .toFloat(), + .exists(), check('cost_account_id') + .optional({ nullable: true }) + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) + .toInt() .if(check('purchasable').equals('true')) - .exists() - .isInt() - .toInt(), + .exists(), // Sell attributes. check('sellable').optional().isBoolean().toBoolean(), check('sell_price') + .optional({ nullable: true }) + .isFloat({ min: 0, max: DATATYPES_LENGTH.DECIMAL_13_3 }) + .toFloat() .if(check('sellable').equals('true')) - .exists() - .isNumeric() - .toFloat(), + .exists(), check('sell_account_id') + .optional({ nullable: true }) + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) + .toInt() .if(check('sellable').equals('true')) - .exists() - .isInt() - .toInt(), + .exists(), check('inventory_account_id') + .optional({ nullable: true }) + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) + .toInt() .if(check('type').equals('inventory')) - .exists() - .isInt() + .exists(), + check('sell_description') + .optional({ nullable: true }) + .isString() + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.TEXT }), + check('cost_description') + .optional({ nullable: true }) + .isString() + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.TEXT }), + check('category_id') + .optional({ nullable: true }) + .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .toInt(), - check('sell_description').optional({ nullable: true }).trim().escape(), - check('cost_description').optional({ nullable: true }).trim().escape(), - - check('category_id').optional({ nullable: true }).isInt().toInt(), - check('note').optional(), + check('note') + .optional() + .isString() + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.TEXT }), check('active').optional().isBoolean().toBoolean(), check('media_ids').optional().isArray(), diff --git a/server/src/api/controllers/ManualJournals.ts b/server/src/api/controllers/ManualJournals.ts index b4ad00dc8..b375d7076 100644 --- a/server/src/api/controllers/ManualJournals.ts +++ b/server/src/api/controllers/ManualJournals.ts @@ -6,6 +6,7 @@ import ManualJournalsService from 'services/ManualJournals/ManualJournalsService import { Inject, Service } from "typedi"; import { ServiceError } from 'exceptions'; import DynamicListingService from 'services/DynamicListing/DynamicListService'; +import { DATATYPES_LENGTH } from 'data/DataTypes'; @Service() export default class ManualJournalsController extends BaseController { @@ -113,30 +114,54 @@ export default class ManualJournalsController extends BaseController { get manualJournalValidationSchema() { return [ check('date').exists().isISO8601(), - check('journal_number').exists().trim().escape(), - check('journal_type').optional({ nullable: true }).trim().escape(), - check('reference').optional({ nullable: true }), - check('description').optional().trim().escape(), + check('journal_number') + .exists() + .isString() + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.STRING }), + check('journal_type') + .optional({ nullable: true }) + .isString() + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.STRING }), + check('reference') + .optional({ nullable: true }) + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.STRING }), + check('description') + .optional() + .isString() + .trim() + .escape() + .isLength({ max: DATATYPES_LENGTH.TEXT }), check('status').optional().isBoolean().toBoolean(), check('entries').isArray({ min: 2 }), - check('entries.*.index').exists().isNumeric().toInt(), + check('entries.*.index') + .exists() + .isInt({ max: DATATYPES_LENGTH.INT_10 }).toInt(), check('entries.*.credit') .optional({ nullable: true }) .isNumeric() .isDecimal() - .isFloat({ max: 9999999999.999 }) // 13, 3 + .isFloat({ max: DATATYPES_LENGTH.INT_13_3 }) // 13, 3 .toFloat(), check('entries.*.debit') .optional({ nullable: true }) .isNumeric() .isDecimal() - .isFloat({ max: 9999999999.999 }) // 13, 3 + .isFloat({ max: DATATYPES_LENGTH.INT_13_3 }) // 13, 3 .toFloat(), - check('entries.*.account_id').isNumeric().toInt(), - check('entries.*.note').optional(), + check('entries.*.account_id').isInt({ max: DATATYPES_LENGTH.INT_10 }).toInt(), + check('entries.*.note') + .optional() + .isString() + .isLength({ max: DATATYPES_LENGTH.STRING }), check('entries.*.contact_id') .optional({ nullable: true }) - .isNumeric() + .isInt({ max: DATATYPES_LENGTH.INT_10 }) .toInt(), check('entries.*.contact_type').optional().isIn(['vendor', 'customer']), ] diff --git a/server/src/data/DataTypes.js b/server/src/data/DataTypes.js new file mode 100644 index 000000000..d5ab1086f --- /dev/null +++ b/server/src/data/DataTypes.js @@ -0,0 +1,8 @@ + +export const DATATYPES_LENGTH = { + STRING: 255, + TEXT: 65535, + INT_10: 4294967295, + DECIMAL_13_3: 9999999999.999, + DECIMAL_15_5: 999999999999.999, +};