diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts index e4d52dc13648..56dd1b30ec3b 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/src/core/server/rendering/rendering_service.test.ts @@ -137,6 +137,29 @@ describe('RenderingService', () => { }); }); + describe('isResourceValid()', () => { + it('checks valid URL', async () => { + const result = await service.isResourceValid( + 'https://opensearch.org/assets/brand/SVG/Mark/opensearch_mark_default.svg', + 'config' + ); + expect(result).toEqual(true); + }); + + it('checks valid SVG', async () => { + const result = await service.isResourceValid( + 'data:image/svg+xml;base64,CjxpbWcgc3R5bGU9IndpZHRoOiAxMDAlOyBoZWlnaHQ6IGF1dG87IGZsb2F0OiBsZWZ0O2JhY2tncm91bmQtaW1hZ2U6IG5vbmU7IiBzcmM9Ii8vcGljLm9ubGluZXdlYmZvbnRzLmNvbS9zdmcvaW1nXzIzOTMwOC5wbmciIGFsdD0iU2VsZWN0IFNtYWxsIiB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCI+CiAg', + 'config' + ); + expect(result).toEqual(true); + }); + + it('checks invalid resource', async () => { + const result = await service.isResourceValid('some garbage', 'config'); + expect(result).toEqual(false); + }); + }); + describe('isUrlValid()', () => { it('checks valid SVG URL', async () => { const result = await service.isUrlValid( diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index 5185ef6a6154..77bb09b6f420 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -53,6 +53,12 @@ import { OpenSearchDashboardsConfigType } from '../opensearch_dashboards_config' const DEFAULT_TITLE = 'OpenSearch Dashboards'; +/** + * Magic constants for the specific mime types + * @see https://en.wikipedia.org/wiki/List_of_file_signatures + */ +const MIME_MAGIC = [' ): Promise => { const branding = opensearchDashboardsConfig.branding; - const isLogoDefaultValid = await this.isUrlValid(branding.logo.defaultUrl, 'logo default'); + const isLogoDefaultValid = await this.isResourceValid(branding.logo.defaultUrl, 'logo default'); const isLogoDarkmodeValid = darkMode - ? await this.isUrlValid(branding.logo.darkModeUrl, 'logo darkMode') + ? await this.isResourceValid(branding.logo.darkModeUrl, 'logo darkMode') : false; - const isMarkDefaultValid = await this.isUrlValid(branding.mark.defaultUrl, 'mark default'); + const isMarkDefaultValid = await this.isResourceValid(branding.mark.defaultUrl, 'mark default'); const isMarkDarkmodeValid = darkMode - ? await this.isUrlValid(branding.mark.darkModeUrl, 'mark darkMode') + ? await this.isResourceValid(branding.mark.darkModeUrl, 'mark darkMode') : false; - const isLoadingLogoDefaultValid = await this.isUrlValid( + const isLoadingLogoDefaultValid = await this.isResourceValid( branding.loadingLogo.defaultUrl, 'loadingLogo default' ); const isLoadingLogoDarkmodeValid = darkMode - ? await this.isUrlValid(branding.loadingLogo.darkModeUrl, 'loadingLogo darkMode') + ? await this.isResourceValid(branding.loadingLogo.darkModeUrl, 'loadingLogo darkMode') : false; - const isFaviconValid = await this.isUrlValid(branding.faviconUrl, 'favicon'); + const isFaviconValid = await this.isResourceValid(branding.faviconUrl, 'favicon'); const isTitleValid = this.isTitleValid(branding.applicationTitle, 'applicationTitle'); @@ -306,6 +312,25 @@ export class RenderingService { return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record; } + /** + * Validation function for resources provided for the branding. + * Checks if it`s a base64 encoded resource is it`s not check it as URL + * + * @param {string} resource + * @param {string} configName + * @returns {boolean} indicate if the provided resource can be used in the system + */ + public isResourceValid = async (resource: string, configName?: string): Promise => { + if (resource.trim().startsWith('data:image')) { + const buff = Buffer.from(resource.replace(/^data:image\/.*;base64,/, ''), 'base64'); + const extractedBytes = buff.subarray(0, 4).toString(); + if (MIME_MAGIC.includes(extractedBytes)) { + return true; + } + } + return this.isUrlValid(resource, configName); + }; + /** * Validation function for URLs. Use Axios to call URL and check validity. * Also needs to be ended with png, svg, gif, PNG, SVG and GIF.