Skip to content

Commit 50b1605

Browse files
Bill Leoutsakoscursoragent
authored andcommitted
fix(sso): normalize audited domain overlaps
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a499ecd commit 50b1605

5 files changed

Lines changed: 31 additions & 10 deletions

File tree

packages/db/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './connection-url'
22
export * from './db'
33
export * from './schema'
4+
export * from './sso-domain'
45
export * from './sso-lock'
56
export * from './triggers'
67
export { instrumentPoolClient, runOutsideTransactionContext } from './tx-tripwire'

packages/db/scripts/audit-sso-providers.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { drizzle } from 'drizzle-orm/postgres-js'
44
import postgres from 'postgres'
55
import { parse as parseDomain } from 'tldts'
66
import { account, session, ssoProvider } from '../schema'
7+
import { ssoDomainsOverlap } from '../sso-domain'
78

89
const connectionString = process.env.POSTGRES_URL ?? process.env.DATABASE_URL
910
if (!connectionString) {
@@ -31,10 +32,6 @@ function isRegistrableDomain(value: string): boolean {
3132
)
3233
}
3334

34-
function domainsOverlap(left: string, right: string): boolean {
35-
return left === right || left.endsWith(`.${right}`) || right.endsWith(`.${left}`)
36-
}
37-
3835
const client = postgres(connectionString, {
3936
prepare: false,
4037
max: 1,
@@ -98,7 +95,7 @@ try {
9895
if (left.organizationId && left.organizationId === right.organizationId) {
9996
findings.push(`organization ${left.organizationId} owns multiple providers`)
10097
}
101-
if (domainsOverlap(left.domain, right.domain)) {
98+
if (ssoDomainsOverlap(left.domain, right.domain)) {
10299
findings.push(`overlapping domains: ${left.domain} and ${right.domain}`)
103100
}
104101
}

packages/db/scripts/register-sso-provider.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { drizzle } from 'drizzle-orm/postgres-js'
4646
import postgres from 'postgres'
4747
import { parse as parseDomain } from 'tldts'
4848
import { member, organization, ssoProvider, user } from '../schema'
49+
import { ssoDomainsOverlap } from '../sso-domain'
4950
import { SSO_PROVIDER_MUTATION_LOCK_KEY } from '../sso-lock'
5051

5152
interface SSOMapping {
@@ -191,10 +192,6 @@ function normalizeDomain(value: string): string | null {
191192
: null
192193
}
193194

194-
function domainsOverlap(left: string, right: string): boolean {
195-
return left === right || left.endsWith(`.${right}`) || right.endsWith(`.${left}`)
196-
}
197-
198195
function buildSSOConfigFromEnv(): SSOProviderConfig | null {
199196
const enabled = process.env.SSO_ENABLED === 'true'
200197
if (!enabled) return null
@@ -700,7 +697,7 @@ async function registerSSOProvider(): Promise<boolean> {
700697
allProviders.some(
701698
(provider) =>
702699
provider.organizationId === organizationId ||
703-
domainsOverlap(provider.domain, ssoConfig.domain)
700+
ssoDomainsOverlap(provider.domain, ssoConfig.domain)
704701
)
705702
) {
706703
logger.error(

packages/db/sso-domain.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { ssoDomainsOverlap } from './sso-domain'
3+
4+
describe('ssoDomainsOverlap', () => {
5+
it.each([
6+
['acme.com', 'acme.com'],
7+
['ACME.COM', 'acme.com'],
8+
['LOGIN.ACME.COM', 'acme.com'],
9+
['acme.com', 'LOGIN.ACME.COM'],
10+
])('detects exact and parent/child overlap between %s and %s', (left, right) => {
11+
expect(ssoDomainsOverlap(left, right)).toBe(true)
12+
})
13+
14+
it('does not treat a label suffix as a subdomain', () => {
15+
expect(ssoDomainsOverlap('notacme.com', 'acme.com')).toBe(false)
16+
})
17+
})

packages/db/sso-domain.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function ssoDomainsOverlap(left: string, right: string): boolean {
2+
const normalizedLeft = left.toLowerCase()
3+
const normalizedRight = right.toLowerCase()
4+
return (
5+
normalizedLeft === normalizedRight ||
6+
normalizedLeft.endsWith(`.${normalizedRight}`) ||
7+
normalizedRight.endsWith(`.${normalizedLeft}`)
8+
)
9+
}

0 commit comments

Comments
 (0)