mirror of
https://github.com/cpvalente/ontime.git
synced 2026-07-26 10:38:55 +00:00
Compare commits
1 Commits
v4.10.0
...
custom-fields
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a2554d32a |
@@ -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' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Custo
|
||||
if (!key) {
|
||||
throw new Error('Unable to convert label to a valid key');
|
||||
}
|
||||
if (isObjectPrototypeKey(key)) {
|
||||
throw new Error('Label conflicts with a reserved field name');
|
||||
}
|
||||
|
||||
const { customFields, commit } = createTransaction({ mutableRundown: false, mutableCustomFields: true });
|
||||
|
||||
@@ -463,7 +466,7 @@ export async function editCustomField(
|
||||
mutableCustomFields: true,
|
||||
});
|
||||
|
||||
if (!(key in customFields)) {
|
||||
if (!Object.hasOwn(customFields, key)) {
|
||||
throw new Error('Could not find label');
|
||||
}
|
||||
|
||||
@@ -472,6 +475,9 @@ export async function editCustomField(
|
||||
if (newField.type && existingField.type !== newField.type) {
|
||||
throw new Error('Change of field type is not allowed');
|
||||
}
|
||||
if (newField.label && isObjectPrototypeKey(customFieldLabelToKey(newField.label))) {
|
||||
throw new Error('Label conflicts with a reserved field name');
|
||||
}
|
||||
|
||||
const { oldKey, newKey } = customFieldMutation.edit(customFields, key, existingField, newField);
|
||||
|
||||
@@ -513,7 +519,7 @@ export async function deleteCustomField(key: CustomFieldKey, projectRundowns: Pr
|
||||
mutableRundown: true,
|
||||
mutableCustomFields: true,
|
||||
});
|
||||
if (!(key in customFields)) {
|
||||
if (!Object.hasOwn(customFields, key)) {
|
||||
return customFields;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export { checkRegex, regex } from './src/regex-utils/checkRegex.js';
|
||||
export { isColourHex } from './src/regex-utils/isColourHex.js';
|
||||
export { splitWhitespace } from './src/regex-utils/splitWhitespace.js';
|
||||
|
||||
export { customFieldLabelToKey, customKeyFromLabel } from './src/customField-utils/customFieldUtils.js';
|
||||
export { customFieldLabelToKey, customKeyFromLabel, isObjectPrototypeKey } from './src/customField-utils/customFieldUtils.js';
|
||||
|
||||
// helpers from externals
|
||||
export { deepmerge } from './src/externals/deepmerge.js';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { CustomFields } from 'ontime-types';
|
||||
|
||||
const objectPrototypeKeys = new Set(Object.getOwnPropertyNames(Object.prototype));
|
||||
|
||||
/**
|
||||
* Transforms an alphanumeric label with spaces into a valid key
|
||||
*/
|
||||
@@ -7,6 +9,13 @@ export function customFieldLabelToKey(label: string): string {
|
||||
return label.trim().replaceAll(' ', '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects keys that collide with Object prototype properties or methods
|
||||
*/
|
||||
export function isObjectPrototypeKey(key: string): boolean {
|
||||
return objectPrototypeKeys.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an object key in the CustomFields object that matches the given label
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user