[READ-ONLY] Mirror of https://github.com/thoda-dev/shhh. Self-hostable zero-knowledge pastebin for secrets that expire on their own
docker end-to-end-encryption nuxt nuxtjs pastebin secrets selft-hosted zero-knowledge
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697import { describe, expect, it } from 'vitest'import { generateInvitationToken, invitationState, isInvitedSignup, runAsInvitedSignup} from '../server/utils/invitations'
const HOUR = 60 * 60 * 1000const future = () => new Date(Date.now() + HOUR)const past = () => new Date(Date.now() - HOUR)
describe('invitationState', () => { it('is pending while the deadline is ahead', () => { expect(invitationState({ status: 'pending', expiresAt: future() })).toBe('pending') })
it('derives expired from the deadline rather than the stored status', () => { // Nothing flips the row to 'expired' on a schedule, so the rule has to hold on read. expect(invitationState({ status: 'pending', expiresAt: past() })).toBe('expired') })
it('keeps accepted and revoked whatever the deadline says', () => { expect(invitationState({ status: 'accepted', expiresAt: past() })).toBe('accepted') expect(invitationState({ status: 'revoked', expiresAt: past() })).toBe('revoked') expect(invitationState({ status: 'accepted', expiresAt: future() })).toBe('accepted') })
it('treats the exact deadline as already expired', () => { expect(invitationState({ status: 'pending', expiresAt: new Date(Date.now()) })).toBe('expired') })})
describe('generateInvitationToken', () => { it('is URL-safe, so it survives being emailed as a query parameter', () => { expect(generateInvitationToken()).toMatch(/^[A-Za-z0-9_-]+$/) })
it('carries 32 bytes of entropy', () => { // 32 random bytes in base64url, unpadded. The only thing between a stranger and an account on a closed instance, so its size is a security property. expect(generateInvitationToken()).toHaveLength(43) })
it('never repeats', () => { const tokens = new Set(Array.from({ length: 500 }, generateInvitationToken)) expect(tokens.size).toBe(500) })})
describe('invited signup context', () => { it('is off by default', () => { expect(isInvitedSignup()).toBe(false) })
it('is on inside the callback and off again afterwards', async () => { await runAsInvitedSignup('inv-1', async () => { expect(isInvitedSignup()).toBe(true) }) expect(isInvitedSignup()).toBe(false) })
it('survives awaits inside the callback', async () => { // The whole point is that it holds for the duration of an async request handler. await runAsInvitedSignup('inv-1', async () => { await new Promise(resolve => setTimeout(resolve, 10)) expect(isInvitedSignup()).toBe(true) }) })
it('does not leak into concurrent work outside it', async () => { // If the flag leaked across async contexts, one invited signup would open the registration bypass for every concurrent request. let leaked: boolean | null = null const outside = (async () => { await new Promise(resolve => setTimeout(resolve, 5)) leaked = isInvitedSignup() })()
await runAsInvitedSignup('inv-1', async () => { await new Promise(resolve => setTimeout(resolve, 20)) }) await outside
expect(leaked).toBe(false) })
it('clears the context even when the callback throws', async () => { await expect(runAsInvitedSignup('inv-1', async () => { throw new Error('signup failed') })).rejects.toThrow('signup failed') expect(isInvitedSignup()).toBe(false) })
it('returns whatever the callback returns', async () => { await expect(runAsInvitedSignup('inv-1', async () => 'created')).resolves.toBe('created') })})