mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-06 23:09:08 +00:00
fix: sanitise project data on load
This commit is contained in:
committed by
Carlos Valente
parent
23298bbe85
commit
5e98fdb2e9
@@ -21,8 +21,9 @@ export async function getProjectData(options?: RequestOptions): Promise<ProjectD
|
|||||||
/**
|
/**
|
||||||
* HTTP request to mutate project data
|
* HTTP request to mutate project data
|
||||||
*/
|
*/
|
||||||
export async function postProjectData(data: ProjectData): Promise<AxiosResponse<ProjectData>> {
|
export async function postProjectData(data: ProjectData): Promise<ProjectData> {
|
||||||
return axios.post(projectPath, data);
|
const response = await axios.post(projectPath, data);
|
||||||
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,4 +7,25 @@ describe('parseProjectData()', () => {
|
|||||||
expect(result).toBeTypeOf('object');
|
expect(result).toBeTypeOf('object');
|
||||||
expect(errorEmitter).toHaveBeenCalledOnce();
|
expect(errorEmitter).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sanitises malformed custom project data', () => {
|
||||||
|
const errorEmitter = vi.fn();
|
||||||
|
const result = parseProjectData(
|
||||||
|
{
|
||||||
|
project: {
|
||||||
|
title: 'Demo',
|
||||||
|
description: '',
|
||||||
|
url: '',
|
||||||
|
info: '',
|
||||||
|
logo: null,
|
||||||
|
// @ts-expect-error -- checking malformed data
|
||||||
|
custom: '{"networkInterfaces":[{"name":"localhost","address":"127.0.0.1"}]}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errorEmitter,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.custom).toEqual([]);
|
||||||
|
expect(errorEmitter).toHaveBeenCalledWith('Project custom data is invalid, using defaults');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,6 +22,53 @@ export function parseProjectData(data: Partial<DatabaseModel>, emitError?: Error
|
|||||||
url: data.project.url ?? defaultProject.url,
|
url: data.project.url ?? defaultProject.url,
|
||||||
info: data.project.info ?? defaultProject.info,
|
info: data.project.info ?? defaultProject.info,
|
||||||
logo: data.project.logo ?? defaultProject.logo,
|
logo: data.project.logo ?? defaultProject.logo,
|
||||||
custom: data.project.custom ?? defaultProject.custom,
|
custom: parseCustomProjectData(data.project.custom, defaultProject.custom, emitError),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isProjectCustomEntry(entry: unknown): entry is ProjectData['custom'][number] {
|
||||||
|
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title, value, url } = entry as Record<string, unknown>;
|
||||||
|
|
||||||
|
return typeof title === 'string' && typeof value === 'string' && (url === undefined || typeof url === 'string');
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCustomProjectData(
|
||||||
|
data: unknown,
|
||||||
|
defaultCustomData: ProjectData['custom'],
|
||||||
|
emitError?: ErrorEmitter,
|
||||||
|
): ProjectData['custom'] {
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
if (data !== undefined) {
|
||||||
|
emitError?.('Project custom data is invalid, using defaults');
|
||||||
|
}
|
||||||
|
return defaultCustomData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed: ProjectData['custom'] = [];
|
||||||
|
let skippedInvalidEntry = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const entry = data[i];
|
||||||
|
|
||||||
|
if (!isProjectCustomEntry(entry)) {
|
||||||
|
skippedInvalidEntry = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed.push({
|
||||||
|
title: entry.title,
|
||||||
|
value: entry.value,
|
||||||
|
url: entry.url ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skippedInvalidEntry) {
|
||||||
|
emitError?.('Project custom data contained invalid entries, skipping them');
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user