fix: block staging backend on production white-label hosts (#1903)
* fix: block staging backend on production brands * fix: clarify production domain classification * fix: alert on forbidden white-label backend
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { usesForbiddenWhiteLabelBackend } from '../production-white-label-backend'
|
||||
|
||||
const STAGING_URL = 'https://metjnjrhvujscngnpzdv.supabase.co'
|
||||
const PRODUCTION_URL = 'https://pwxtzglxptnnvjrpixpg.supabase.co'
|
||||
|
||||
describe('production white-label backend guard', () => {
|
||||
it.each([
|
||||
'acount.accounted.se',
|
||||
'arbore.accounted.se',
|
||||
'elma.accounted.se',
|
||||
'm360.accounted.se',
|
||||
'redovisningskompaniet.accounted.se',
|
||||
'willem.accounted.se',
|
||||
'ziffr.accounted.se',
|
||||
])('blocks %s when it uses the staging project', hostname => {
|
||||
expect(usesForbiddenWhiteLabelBackend(hostname, STAGING_URL)).toBe(true)
|
||||
})
|
||||
|
||||
it('normalizes case and a trailing dot before the exact host checks', () => {
|
||||
expect(
|
||||
usesForbiddenWhiteLabelBackend(
|
||||
'ACOUNT.ACCOUNTED.SE.',
|
||||
'https://METJNJRHVUJSCNGNPZDV.SUPABASE.CO./rest/v1',
|
||||
),
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it.each([
|
||||
'app.accounted.se',
|
||||
'accounted.se',
|
||||
'preview.vercel.app',
|
||||
'acount.accounted.se.attacker.test',
|
||||
'notacount.accounted.se',
|
||||
])('does not extend the production classification to %s', hostname => {
|
||||
expect(usesForbiddenWhiteLabelBackend(hostname, STAGING_URL)).toBe(false)
|
||||
})
|
||||
|
||||
it('allows a customer production host to use a different backend', () => {
|
||||
expect(
|
||||
usesForbiddenWhiteLabelBackend('acount.accounted.se', PRODUCTION_URL),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it.each([
|
||||
undefined,
|
||||
'',
|
||||
'not a URL',
|
||||
'https://metjnjrhvujscngnpzdv.supabase.co.attacker.test',
|
||||
])('does not mistake an unrecognized backend for the staging project', url => {
|
||||
expect(usesForbiddenWhiteLabelBackend('acount.accounted.se', url)).toBe(
|
||||
false,
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
// This is an owner-approved production classification, not an auth callback
|
||||
// allowlist. Do not derive it from NEXT_PUBLIC_WHITELABEL_DOMAINS, which can
|
||||
// also contain demo, pilot, or self-hosted domains.
|
||||
const CUSTOMER_PRODUCTION_WHITE_LABEL_HOSTS = new Set([
|
||||
'acount.accounted.se',
|
||||
'arbore.accounted.se',
|
||||
'elma.accounted.se',
|
||||
'm360.accounted.se',
|
||||
'redovisningskompaniet.accounted.se',
|
||||
'willem.accounted.se',
|
||||
'ziffr.accounted.se',
|
||||
])
|
||||
|
||||
const FORBIDDEN_STAGING_SUPABASE_HOST =
|
||||
'metjnjrhvujscngnpzdv.supabase.co'
|
||||
|
||||
function normalizeHostname(hostname: string): string {
|
||||
return hostname.trim().toLowerCase().replace(/\.$/, '')
|
||||
}
|
||||
|
||||
function parseBackendHostname(supabaseUrl: string | undefined): string | null {
|
||||
if (!supabaseUrl) return null
|
||||
|
||||
try {
|
||||
return normalizeHostname(new URL(supabaseUrl).hostname)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Block Accounted's customer-facing white-label hosts from using the staging
|
||||
* Supabase project. The request host and backend host are exact matches: this
|
||||
* is an environment safety boundary, not suffix-based domain authorization.
|
||||
*/
|
||||
export function usesForbiddenWhiteLabelBackend(
|
||||
requestHostname: string,
|
||||
supabaseUrl: string | undefined,
|
||||
): boolean {
|
||||
const hostname = normalizeHostname(requestHostname)
|
||||
if (!CUSTOMER_PRODUCTION_WHITE_LABEL_HOSTS.has(hostname)) return false
|
||||
|
||||
return parseBackendHostname(supabaseUrl) === FORBIDDEN_STAGING_SUPABASE_HOST
|
||||
}
|
||||
Reference in New Issue
Block a user