Skip to content

Commit

Permalink
Update resource validation procedure.
Browse files Browse the repository at this point in the history
New validation procedure allows to embed base64 encoded images

Signed-off-by: Serge Arbuzov <[email protected]>
  • Loading branch information
Arbuzov authored and kavilla committed Feb 28, 2022
1 parent 8ba11e3 commit 501a82a
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 7 deletions.
23 changes: 23 additions & 0 deletions src/core/server/rendering/rendering_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
39 changes: 32 additions & 7 deletions src/core/server/rendering/rendering_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ['<svg', 'GIF8', '‰PNG'];

/** @internal */
export class RenderingService {
constructor(private readonly coreContext: CoreContext) {}
Expand Down Expand Up @@ -261,28 +267,28 @@ export class RenderingService {
opensearchDashboardsConfig: Readonly<OpenSearchDashboardsConfigType>
): Promise<BrandingValidation> => {
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');

Expand All @@ -306,6 +312,25 @@ export class RenderingService {
return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record<string, any>;
}

/**
* 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<boolean> => {
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.
Expand Down

0 comments on commit 501a82a

Please sign in to comment.