From d85c96d4082e702e09c55ccee7068c457dd59e41 Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Thu, 17 Sep 2020 16:13:22 -0500 Subject: [PATCH] Allow remote config to set valid origins (#205) --- src/ConfigContext.ts | 16 +++++++++++++--- src/Utils/MessageValidation.test.ts | 21 +++++++++++++++++++++ src/Utils/MessageValidation.ts | 11 ++++++++--- 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/Utils/MessageValidation.test.ts diff --git a/src/ConfigContext.ts b/src/ConfigContext.ts index afa40f310..5fc2d889a 100644 --- a/src/ConfigContext.ts +++ b/src/ConfigContext.ts @@ -6,7 +6,7 @@ export enum Platform { interface ConfigContext { platform: Platform; - allowedParentFrameOrigins: RegExp; + allowedParentFrameOrigins: string[]; gitSha?: string; proxyPath?: string; AAD_ENDPOINT: string; @@ -30,7 +30,12 @@ interface ConfigContext { // Default configuration let configContext: Readonly = { platform: Platform.Portal, - allowedParentFrameOrigins: /^https:\/\/portal\.azure\.com$|^https:\/\/portal\.azure\.us$|^https:\/\/portal\.azure\.cn$|^https:\/\/portal\.microsoftazure\.de$|^https:\/\/.+\.portal\.azure\.com$|^https:\/\/.+\.portal\.azure\.us$|^https:\/\/.+\.portal\.azure\.cn$|^https:\/\/.+\.portal\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.com$|^https:\/\/main\.documentdb\.ext\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.cn$|^https:\/\/main\.documentdb\.ext\.azure\.us$/, + allowedParentFrameOrigins: [ + `^https:\\/\\/cosmos.azure.(com|cn|us)$`, + `^https:\\/\\/[\\.\\w]+.portal.azure.(com|cn|us)$`, + `^https:\\/\\/[\\.\\w]+.ext.azure.(com|cn|us)$`, + `^https:\\/\\/[\\.\\w]+microsoftazure.de$` + ], // Webpack injects this at build time gitSha: process.env.GIT_SHA, hostedExplorerURL: "https://cosmos.azure.com/", @@ -73,8 +78,13 @@ export async function initializeConfiguration(): Promise { const response = await fetch("./config.json"); if (response.status === 200) { try { - const externalConfig = await response.json(); + const { allowedParentFrameOrigins, ...externalConfig } = await response.json(); Object.assign(configContext, externalConfig); + if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) { + updateConfigContext({ + allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins] + }); + } } catch (error) { console.error("Unable to parse json in config file"); console.error(error); diff --git a/src/Utils/MessageValidation.test.ts b/src/Utils/MessageValidation.test.ts new file mode 100644 index 000000000..bc205241f --- /dev/null +++ b/src/Utils/MessageValidation.test.ts @@ -0,0 +1,21 @@ +import { isInvalidParentFrameOrigin } from "./MessageValidation"; + +test.each` + domain | expected + ${"https://cosmos.azure.com"} | ${false} + ${"https://cosmos.azure.us"} | ${false} + ${"https://cosmos.azure.cn"} | ${false} + ${"https://cosmos.microsoftazure.de"} | ${false} + ${"https://subdomain.portal.azure.com"} | ${false} + ${"https://subdomain.portal.azure.us"} | ${false} + ${"https://subdomain.portal.azure.cn"} | ${false} + ${"https://subdomain.microsoftazure.de"} | ${false} + ${"https://main.documentdb.ext.azure.com"} | ${false} + ${"https://main.documentdb.ext.azure.us"} | ${false} + ${"https://main.documentdb.ext.azure.cn"} | ${false} + ${"https://main.documentdb.ext.microsoftazure.de"} | ${false} + ${"https://random.domain"} | ${true} + ${"https://malicious.cloudapp.azure.com"} | ${true} +`("returns $expected when called with $domain", ({ domain, expected }) => { + expect(isInvalidParentFrameOrigin({ origin: domain } as MessageEvent)).toBe(expected); +}); diff --git a/src/Utils/MessageValidation.ts b/src/Utils/MessageValidation.ts index e107183e5..5d7cd9931 100644 --- a/src/Utils/MessageValidation.ts +++ b/src/Utils/MessageValidation.ts @@ -4,13 +4,18 @@ export function isInvalidParentFrameOrigin(event: MessageEvent): boolean { return !isValidOrigin(configContext.allowedParentFrameOrigins, event); } -function isValidOrigin(allowedOrigins: RegExp, event: MessageEvent): boolean { +function isValidOrigin(allowedOrigins: string[], event: MessageEvent): boolean { const eventOrigin = (event && event.origin) || ""; const windowOrigin = (window && window.origin) || ""; if (eventOrigin === windowOrigin) { return true; } - const result = allowedOrigins && allowedOrigins.test(eventOrigin); - return result; + for (const origin of allowedOrigins) { + const result = new RegExp(origin).test(eventOrigin); + if (result) { + return true; + } + } + return false; }