diff --git a/frontend/app/common/constants.config.test.ts b/frontend/app/common/constants.config.test.ts new file mode 100644 index 00000000..e928d1e5 --- /dev/null +++ b/frontend/app/common/constants.config.test.ts @@ -0,0 +1,47 @@ +import { getBaseUrl } from './constants.config'; + +describe('constants.config', () => { + let host: string; + let consoleErrorSpy: jest.SpyInstance; + + beforeAll(() => { + host = window.remark_config.host!; + }); + beforeEach(() => { + consoleErrorSpy = jest.spyOn(console, 'error').mockImplementationOnce(jest.fn()); + }); + afterEach(() => { + consoleErrorSpy.mockClear(); + window.remark_config.host = host; + }); + + describe('BASE_URL validation', () => { + beforeEach(() => { + Object.defineProperty(window, 'location', { + value: { protocol: 'https:' }, + writable: true, + }); + }); + it('should throw error if host is not defined', () => { + window.remark_config.host = undefined; + expect(() => getBaseUrl()).toThrowError(`Remark42: remark_config.host wasn't configured.`); + }); + it('should show mismatch error', () => { + expect(getBaseUrl()).toBe('http://test.com'); + expect(consoleErrorSpy).toHaveBeenCalledTimes(1); + expect(consoleErrorSpy).toHaveBeenCalledWith('Remark42: Protocol mismatch.'); + }); + it('should throw error when BASE_URL has wrong protocol', () => { + window.remark_config.host = 'data:application/json;base64'; + expect(() => getBaseUrl()).toThrowError('Remark42: Invalid host URL.'); + expect(consoleErrorSpy).toHaveBeenCalledTimes(2); + expect(consoleErrorSpy).toHaveBeenNthCalledWith(1, 'Remark42: Protocol mismatch.'); + expect(consoleErrorSpy).toHaveBeenNthCalledWith(2, 'Remark42: Wrong protocol in host URL.'); + }); + it('should throw error when BASE_URL is invalid', () => { + window.remark_config.host = 'asfasdfa!asds'; + expect(() => getBaseUrl()).toThrowError('Remark42: Invalid host URL.'); + expect(consoleErrorSpy).toHaveBeenCalledTimes(0); + }); + }); +}); diff --git a/frontend/app/common/constants.config.ts b/frontend/app/common/constants.config.ts index 8f5fb504..c84658e0 100644 --- a/frontend/app/common/constants.config.ts +++ b/frontend/app/common/constants.config.ts @@ -1,4 +1,30 @@ -export const BASE_URL = window.remark_config.host || process.env.REMARK_URL!; export const NODE_ID = process.env.REMARK_NODE!; export const API_BASE = '/api/v1'; export const COMMENT_NODE_CLASSNAME_PREFIX = 'remark42__comment-'; +export const BASE_URL = getBaseUrl(); + +export function getBaseUrl() { + const baseUrl = window.remark_config.host ?? process.env.REMARK_URL; + + if (!baseUrl) { + throw new Error(`Remark42: remark_config.host wasn't configured.`); + } + + // Validate host + try { + const { protocol } = new URL(baseUrl); + // Show error if protocol of iframe doesn't match protocol of current page + if (protocol !== window.location.protocol) { + console.error('Remark42: Protocol mismatch.'); + } + // Check if host has valid protocol and prevent XSS vurnuality + if (!protocol.startsWith('http')) { + console.error('Remark42: Wrong protocol in host URL.'); + throw new Error(); + } + } catch (e) { + throw new Error('Remark42: Invalid host URL.'); + } + + return baseUrl; +} diff --git a/frontend/app/typings/global.d.ts b/frontend/app/typings/global.d.ts index 41186937..0e09286a 100644 --- a/frontend/app/typings/global.d.ts +++ b/frontend/app/typings/global.d.ts @@ -2,7 +2,7 @@ import 'jest-fetch-mock'; import type { Theme } from 'common/types'; type RemarkConfig = { - host: string; + host?: string; site_id: string; url?: string; max_shown_comments?: number;