Files
zeekay 1b367e2b62 chore: rebase the tree onto upstream master
The tree was a debranded snapshot of upstream taken around 2026-02-20, and
upstream has moved about five months since. This replaces it with upstream
master, mechanically re-derived: bin/debrand does the rename, and the
pipeline and squashed migrations are restored from the shipped tree.

Not a merge. The previous import replaced the tree wholesale in a single
commit, so there is no shared content history to merge against. Reproducing
the rename on the new tree is what actually converges.

What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints,
compose files, LLM.md, and insights/migrations. The squashed initial
migration is load-bearing — the live database records it by name, so
upstream's ~918 individual migrations cannot come back.

Nine products are dropped as unreferenced. Twenty-one more that we do not run
are kept anyway because the core app still imports them; unpicking those is a
refactor and does not belong in a rebase.

This base does not build yet, and the reasons are not in the rename:
upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against
our 4.2, and the dependency manifests still name upstream packages that we
republish under our own names. Those are decisions, not renames.
2026-08-01 15:44:40 -07:00

217 lines
9.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { handleAuthorize } from '@/handlers/authorize'
import { hashKey } from '@/lib/kv'
import { createMockKV, mockKVGet } from './helpers'
const mockKV = createMockKV()
beforeEach(() => {
vi.clearAllMocks()
})
describe('handleAuthorize', () => {
it('shows region picker when no _region param', async () => {
const request = new Request(
'https://oauth.hanzo.ai/oauth/authorize/?client_id=abc&redirect_uri=http://localhost:3000/callback&response_type=code'
)
const response = await handleAuthorize(request, mockKV)
expect(response.status).toBe(200)
expect(response.headers.get('content-type')).toContain('text/html')
const html = await response.text()
expect(html).toContain('Authenticate with Insights')
expect(html).toContain('US Cloud')
expect(html).toContain('EU Cloud')
})
it('redirects to US authorize with translated client_id and proxy callback when _region=us', async () => {
const mapping = {
us_client_id: 'us_real_id',
eu_client_id: 'eu_real_id',
redirect_uris: ['http://localhost:3000/callback'],
created_at: Date.now(),
}
mockKVGet(mockKV, (_key: string, type?: unknown) => {
if (type === 'json') {
return Promise.resolve(mapping)
}
return Promise.resolve(null)
})
const request = new Request(
'https://oauth.hanzo.ai/oauth/authorize/?client_id=us_real_id&redirect_uri=http://localhost:3000/callback&response_type=code&_region=us'
)
const response = await handleAuthorize(request, mockKV)
expect(response.status).toBe(302)
const location = new URL(response.headers.get('location')!)
expect(location.origin).toBe('https://us.hanzo.ai')
expect(location.pathname).toBe('/oauth/authorize/')
expect(location.searchParams.get('client_id')).toBe('us_real_id')
expect(location.searchParams.get('redirect_uri')).toBe('https://oauth.hanzo.ai/oauth/callback/')
expect(location.searchParams.has('_region')).toBe(false)
})
it('redirects to EU authorize with translated client_id and proxy callback when _region=eu', async () => {
const mapping = {
us_client_id: 'us_real_id',
eu_client_id: 'eu_real_id',
redirect_uris: ['http://localhost:3000/callback'],
created_at: Date.now(),
}
mockKVGet(mockKV, (_key: string, type?: unknown) => {
if (type === 'json') {
return Promise.resolve(mapping)
}
return Promise.resolve(null)
})
const request = new Request(
'https://oauth.hanzo.ai/oauth/authorize/?client_id=us_real_id&redirect_uri=http://localhost:3000/callback&response_type=code&_region=eu'
)
const response = await handleAuthorize(request, mockKV)
expect(response.status).toBe(302)
const location = new URL(response.headers.get('location')!)
expect(location.origin).toBe('https://eu.hanzo.ai')
expect(location.pathname).toBe('/oauth/authorize/')
expect(location.searchParams.get('client_id')).toBe('eu_real_id')
expect(location.searchParams.get('redirect_uri')).toBe('https://oauth.hanzo.ai/oauth/callback/')
expect(location.searchParams.has('_region')).toBe(false)
})
it('rejects unregistered redirect_uri to prevent open redirects', async () => {
const mapping = {
us_client_id: 'us_id',
eu_client_id: 'eu_id',
redirect_uris: ['http://localhost:3000/callback'],
created_at: Date.now(),
}
mockKVGet(mockKV, (_key: string, type?: unknown) => {
if (type === 'json') {
return Promise.resolve(mapping)
}
return Promise.resolve(null)
})
const request = new Request(
'https://oauth.hanzo.ai/oauth/authorize/?client_id=us_id&redirect_uri=https://attacker.com/steal&response_type=code&state=abc&_region=eu'
)
const response = await handleAuthorize(request, mockKV)
expect(response.status).toBe(400)
const data = (await response.json()) as Record<string, unknown>
expect(data.error).toBe('invalid_request')
expect(data.error_description).toBe('redirect_uri is not registered for this client')
})
it('stores region selection and callback redirect_uri keyed by state and client_id', async () => {
const mapping = {
us_client_id: 'us_id',
eu_client_id: 'eu_id',
redirect_uris: ['http://localhost:3000/callback'],
created_at: Date.now(),
}
mockKVGet(mockKV, (_key: string, type?: unknown) => {
if (type === 'json') {
return Promise.resolve(mapping)
}
return Promise.resolve(null)
})
const request = new Request(
'https://oauth.hanzo.ai/oauth/authorize/?client_id=us_id&redirect_uri=http://localhost:3000/callback&response_type=code&state=abc123&_region=eu'
)
await handleAuthorize(request, mockKV)
const putCalls = vi.mocked(mockKV.put).mock.calls
const stateHash = await hashKey('abc123')
const clientHash = await hashKey('us_id')
// Region selection stored by both state and client_id
const regionByState = putCalls.find(([key]) => (key as string) === `region:${stateHash}`)
const regionByClient = putCalls.find(([key]) => (key as string) === `region:${clientHash}`)
expect(regionByState).toBeTruthy()
expect(regionByState![1]).toBe('eu')
expect(regionByClient).toBeTruthy()
expect(regionByClient![1]).toBe('eu')
// Callback redirect_uri stored by both state and client_id
const callbackByState = putCalls.find(([key]) => (key as string) === `callback:${stateHash}`)
const callbackByClient = putCalls.find(([key]) => (key as string) === `callback:${clientHash}`)
expect(callbackByState).toBeTruthy()
expect(callbackByState![1]).toBe('http://localhost:3000/callback')
expect(callbackByClient).toBeTruthy()
expect(callbackByClient![1]).toBe('http://localhost:3000/callback')
})
it('writes bounded-length KV keys even for very large state values', async () => {
const mapping = {
us_client_id: 'us_id',
eu_client_id: 'eu_id',
redirect_uris: ['http://localhost:3000/callback'],
created_at: Date.now(),
}
mockKVGet(mockKV, (_key: string, type?: unknown) => {
if (type === 'json') {
return Promise.resolve(mapping)
}
return Promise.resolve(null)
})
// A JWT-shaped state well above Cloudflare's 512-byte KV key limit.
const longState = 'x'.repeat(2000)
const request = new Request(
`https://oauth.hanzo.ai/oauth/authorize/?client_id=us_id&redirect_uri=http://localhost:3000/callback&response_type=code&state=${longState}&_region=us`
)
const response = await handleAuthorize(request, mockKV)
expect(response.status).toBe(302)
const putCalls = vi.mocked(mockKV.put).mock.calls
expect(putCalls.length).toBeGreaterThan(0)
for (const [key] of putCalls) {
expect((key as string).length).toBeLessThanOrEqual(512)
}
})
it('passes redirect_uri through without interception for legacy clients (no stored redirect_uris)', async () => {
const mapping = { us_client_id: 'us_id', eu_client_id: 'eu_id', created_at: Date.now() }
mockKVGet(mockKV, (_key: string, type?: unknown) => {
if (type === 'json') {
return Promise.resolve(mapping)
}
return Promise.resolve(null)
})
const request = new Request(
'https://oauth.hanzo.ai/oauth/authorize/?client_id=us_id&redirect_uri=http://localhost:3000/callback&response_type=code&_region=eu'
)
const response = await handleAuthorize(request, mockKV)
expect(response.status).toBe(302)
const location = new URL(response.headers.get('location')!)
expect(location.searchParams.get('redirect_uri')).toBe('http://localhost:3000/callback')
// No callback redirect_uri should be stored
const putCalls = vi.mocked(mockKV.put).mock.calls
const callbackPuts = putCalls.filter(([key]) => (key as string).startsWith('callback:'))
expect(callbackPuts).toHaveLength(0)
})
it('sets security and cache headers on region picker page', async () => {
const request = new Request('https://oauth.hanzo.ai/oauth/authorize/?client_id=abc&response_type=code')
const response = await handleAuthorize(request, mockKV)
expect(response.headers.get('x-frame-options')).toBe('DENY')
expect(response.headers.get('x-content-type-options')).toBe('nosniff')
expect(response.headers.get('referrer-policy')).toBe('no-referrer')
expect(response.headers.get('cache-control')).toBe(
'public, max-age=3600, s-maxage=86400, stale-while-revalidate=604800'
)
})
})