99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
// @vitest-environment node
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { checkAccess, readAccessConfig } from './policy.server'
|
|
|
|
const config = {
|
|
origin: 'https://deck.invalid',
|
|
allowedLogin: '[email protected]',
|
|
}
|
|
|
|
afterEach(() => vi.unstubAllEnvs())
|
|
|
|
describe('Serve access boundary', () => {
|
|
it('fails closed when the deployment is not configured', () => {
|
|
vi.stubEnv('TWITTER_LITE_ORIGIN', '')
|
|
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', '')
|
|
expect(readAccessConfig()).toBeNull()
|
|
expect(checkAccess(new Request(config.origin), null)?.status).toBe(503)
|
|
})
|
|
|
|
it.each([
|
|
'',
|
|
'https://deck.invalid/path',
|
|
'http://deck.invalid',
|
|
'not a URL',
|
|
])('rejects an invalid configured origin: %s', (origin) => {
|
|
vi.stubEnv('TWITTER_LITE_ORIGIN', origin)
|
|
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', config.allowedLogin)
|
|
expect(readAccessConfig()).toBeNull()
|
|
})
|
|
|
|
it.each([
|
|
'https://deck.invalid',
|
|
'http://127.0.0.1:4173',
|
|
])('accepts an explicit deployment origin: %s', (origin) => {
|
|
vi.stubEnv('TWITTER_LITE_ORIGIN', origin)
|
|
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', config.allowedLogin)
|
|
expect(readAccessConfig()).toEqual({ ...config, origin })
|
|
})
|
|
|
|
it.each([
|
|
undefined,
|
|
'[email protected]',
|
|
'[email protected], [email protected]',
|
|
])('rejects absent, foreign, or ambiguous identities: %s', (login) => {
|
|
const headers = new Headers()
|
|
if (login) headers.set('Tailscale-User-Login', login)
|
|
expect(
|
|
checkAccess(new Request(config.origin, { headers }), config)?.status,
|
|
).toBe(403)
|
|
})
|
|
|
|
it('permits owner navigation back from an OAuth provider without Origin', () => {
|
|
const request = new Request(
|
|
`${config.origin}/oauth/mastodon/callback?code=code`,
|
|
{
|
|
headers: {
|
|
'Tailscale-User-Login': config.allowedLogin,
|
|
'Sec-Fetch-Site': 'cross-site',
|
|
},
|
|
},
|
|
)
|
|
expect(checkAccess(request, config)).toBeNull()
|
|
})
|
|
|
|
it.each([
|
|
'POST',
|
|
'PUT',
|
|
'PATCH',
|
|
'DELETE',
|
|
])('requires exact Origin for %s even with same-origin Fetch Metadata', (method) => {
|
|
const headers = {
|
|
'Tailscale-User-Login': config.allowedLogin,
|
|
'Sec-Fetch-Site': 'same-origin',
|
|
}
|
|
expect(
|
|
checkAccess(new Request(config.origin, { method, headers }), config)
|
|
?.status,
|
|
).toBe(403)
|
|
expect(
|
|
checkAccess(
|
|
new Request(config.origin, {
|
|
method,
|
|
headers: { ...headers, Origin: 'https://other.invalid' },
|
|
}),
|
|
config,
|
|
)?.status,
|
|
).toBe(403)
|
|
expect(
|
|
checkAccess(
|
|
new Request(config.origin, {
|
|
method,
|
|
headers: { ...headers, Origin: config.origin },
|
|
}),
|
|
config,
|
|
),
|
|
).toBeNull()
|
|
})
|
|
})
|