diff --git a/apps/server/src/api-data/custom-fields/__tests__/customFields.parser.test.ts b/apps/server/src/api-data/custom-fields/__tests__/customFields.parser.test.ts index de680c234..5d2d004fe 100644 --- a/apps/server/src/api-data/custom-fields/__tests__/customFields.parser.test.ts +++ b/apps/server/src/api-data/custom-fields/__tests__/customFields.parser.test.ts @@ -126,4 +126,15 @@ describe('sanitiseCustomFields()', () => { const sanitationResult = sanitiseCustomFields(customFields); expect(sanitationResult).toStrictEqual(expectedCustomFields); }); + + it('filters keys that collide with Object prototype properties', () => { + const customFields: CustomFields = { + toString: { label: 'toString', type: 'text', colour: 'red' }, + normalField: { label: 'normalField', type: 'text', colour: 'green' }, + }; + const sanitationResult = sanitiseCustomFields(customFields); + expect(sanitationResult).toStrictEqual({ + normalField: { label: 'normalField', type: 'text', colour: 'green' }, + }); + }); }); diff --git a/apps/server/src/api-data/custom-fields/customFields.parser.ts b/apps/server/src/api-data/custom-fields/customFields.parser.ts index 8b0d71002..3d204de8a 100644 --- a/apps/server/src/api-data/custom-fields/customFields.parser.ts +++ b/apps/server/src/api-data/custom-fields/customFields.parser.ts @@ -1,5 +1,5 @@ import { CustomField, CustomFields, DatabaseModel } from 'ontime-types'; -import { checkRegex, customFieldLabelToKey } from 'ontime-utils'; +import { checkRegex, customFieldLabelToKey, isObjectPrototypeKey } from 'ontime-utils'; import type { ErrorEmitter } from '../../utils/parserUtils.js'; @@ -45,6 +45,7 @@ export function sanitiseCustomFields(data: object): CustomFields { 'type' in data && (data.type === 'text' || data.type === 'image') && checkRegex.isAlphanumericWithSpace(data.label) && + !isObjectPrototypeKey(key) && key === customFieldLabelToKey(data.label) ); } diff --git a/apps/server/src/api-data/custom-fields/customFields.validation.ts b/apps/server/src/api-data/custom-fields/customFields.validation.ts index 9d374208b..ad27506fd 100644 --- a/apps/server/src/api-data/custom-fields/customFields.validation.ts +++ b/apps/server/src/api-data/custom-fields/customFields.validation.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { checkRegex } from 'ontime-utils'; +import { checkRegex, customFieldLabelToKey, isObjectPrototypeKey } from 'ontime-utils'; import { requestValidationFunction } from '../validation-utils/validationFunction.js'; @@ -9,7 +9,7 @@ export const validateCustomField = [ .trim() .notEmpty() .custom((value) => { - return checkRegex.isAlphanumericWithSpace(value); + return checkRegex.isAlphanumericWithSpace(value) && !isObjectPrototypeKey(customFieldLabelToKey(value)); }), body('type').isIn(['text', 'image']), body('colour').isString().trim(), @@ -24,7 +24,7 @@ export const validateEditCustomField = [ .trim() .notEmpty() .custom((value) => { - return checkRegex.isAlphanumericWithSpace(value); + return checkRegex.isAlphanumericWithSpace(value) && !isObjectPrototypeKey(customFieldLabelToKey(value)); }), body('type').isIn(['text', 'image']), body('colour').isString().trim(), diff --git a/apps/server/src/api-data/rundown/rundown.parser.ts b/apps/server/src/api-data/rundown/rundown.parser.ts index 332ed29c7..17795f5a9 100644 --- a/apps/server/src/api-data/rundown/rundown.parser.ts +++ b/apps/server/src/api-data/rundown/rundown.parser.ts @@ -188,7 +188,7 @@ export function parseRundown( */ export function sanitiseCustomFields(customFields: CustomFields, entry: OntimeEvent | OntimeMilestone | OntimeGroup) { for (const field in entry.custom) { - if (field in customFields) continue; + if (Object.hasOwn(customFields, field)) continue; delete entry.custom[field]; } return entry; diff --git a/apps/server/src/api-data/rundown/rundown.service.ts b/apps/server/src/api-data/rundown/rundown.service.ts index 48a8873e5..dc2b82907 100644 --- a/apps/server/src/api-data/rundown/rundown.service.ts +++ b/apps/server/src/api-data/rundown/rundown.service.ts @@ -16,7 +16,7 @@ import { isOntimeEvent, isOntimeGroup, } from 'ontime-types'; -import { customFieldLabelToKey, getInsertAfterId, resolveInsertParent } from 'ontime-utils'; +import { customFieldLabelToKey, getInsertAfterId, isObjectPrototypeKey, resolveInsertParent } from 'ontime-utils'; import { sendRefetch } from '../../adapters/WebsocketAdapter.js'; import { getDataProvider } from '../../classes/data-provider/DataProvider.js'; @@ -425,6 +425,9 @@ export async function createCustomField(customField: CustomField): Promise