diff --git a/.changeset/sixty-pans-kneel.md b/.changeset/sixty-pans-kneel.md new file mode 100644 index 000000000..2e8adb157 --- /dev/null +++ b/.changeset/sixty-pans-kneel.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-api': patch +--- + +Fixing host validation to work with unified admin shops diff --git a/lib/auth/get-embedded-app-url.ts b/lib/auth/get-embedded-app-url.ts index 804aee7d4..e34d8d8f7 100644 --- a/lib/auth/get-embedded-app-url.ts +++ b/lib/auth/get-embedded-app-url.ts @@ -37,7 +37,7 @@ export function getEmbeddedAppUrl(config: ConfigInterface) { export function buildEmbeddedAppUrl(config: ConfigInterface) { return (host: string): string => { - sanitizeHost(config)(host, true); + sanitizeHost()(host, true); const decodedHost = decodeHost(host); return `https://${decodedHost}/apps/${config.apiKey}`; diff --git a/lib/utils/__tests__/shop-validator.test.ts b/lib/utils/__tests__/shop-validator.test.ts index 66de346a7..434a05633 100644 --- a/lib/utils/__tests__/shop-validator.test.ts +++ b/lib/utils/__tests__/shop-validator.test.ts @@ -22,6 +22,7 @@ const VALID_HOSTS = [ 'my-other-host.myshopify.com/admin', 'my-other-other-host.myshopify.io/admin', 'admin.shopify.com/store/my-shop', + 'admin.spin.dev/store/my-shop', ].map((testhost) => { return {testhost, base64host: Buffer.from(testhost).toString('base64')}; }); diff --git a/lib/utils/index.ts b/lib/utils/index.ts index b779bca2b..8eb4279a3 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -7,7 +7,7 @@ import {versionCompatible, versionPriorTo} from './version-compatible'; export function shopifyUtils(config: ConfigInterface) { return { sanitizeShop: sanitizeShop(config), - sanitizeHost: sanitizeHost(config), + sanitizeHost: sanitizeHost(), validateHmac: validateHmac(config), versionCompatible: versionCompatible(config), versionPriorTo: versionPriorTo(config), diff --git a/lib/utils/shop-validator.ts b/lib/utils/shop-validator.ts index 531d2fe35..931dc5a33 100644 --- a/lib/utils/shop-validator.ts +++ b/lib/utils/shop-validator.ts @@ -26,15 +26,23 @@ export function sanitizeShop(config: ConfigInterface) { }; } -export function sanitizeHost(config: ConfigInterface) { +export function sanitizeHost() { return (host: string, throwOnInvalid = false): string | null => { const base64regex = /^[0-9a-zA-Z+/]+={0,2}$/; let sanitizedHost = base64regex.test(host) ? host : null; if (sanitizedHost) { - const url = new URL(`https://${decodeHost(sanitizedHost)}`); - // allow-list of origins - if (!/\.((my)?shopify\.com|myshopify\.io|spin\.dev)$/.test(url.hostname)) { + const {hostname} = new URL(`https://${decodeHost(sanitizedHost)}`); + + const originsRegex = [ + 'myshopify\\.com', + 'shopify\\.com', + 'myshopify\\.io', + 'spin\\.dev', + ]; + + const hostRegex = new RegExp(`\\.(${originsRegex.join('|')})$`); + if (!hostRegex.test(hostname)) { sanitizedHost = null; } }