mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
77 lines
3.1 KiB
TypeScript
77 lines
3.1 KiB
TypeScript
import { readFile } from 'node:fs/promises'
|
|
import { describe, expect, it } from 'vitest'
|
|
import type { CapturedNetworkRequest } from 'posthog-js'
|
|
import { redactInviteNetworkRequest, redactInvitePaths } from './providers'
|
|
|
|
const pageViewPath = new URL('./PostHogPageView.tsx', import.meta.url)
|
|
const providerPath = new URL('./providers.tsx', import.meta.url)
|
|
|
|
describe('PostHog invite privacy boundary', () => {
|
|
it('does not send a global page view for invite routes', async () => {
|
|
const source = await readFile(pageViewPath, 'utf8')
|
|
|
|
expect(source).toContain("pathname?.startsWith('/join/')")
|
|
expect(source.indexOf("pathname?.startsWith('/join/')")).toBeLessThan(source.indexOf("posthog.capture('$pageview'"))
|
|
})
|
|
|
|
it('disables automatic page-leave capture and redacts join URLs', async () => {
|
|
const source = await readFile(providerPath, 'utf8')
|
|
|
|
expect(source).toContain('capture_pageview: false')
|
|
expect(source).toContain('capture_pageleave: false')
|
|
expect(source).toContain('get_current_url: redactInviteUrl')
|
|
expect(source).toContain('before_send: redactInvitePathsBeforeSend')
|
|
expect(source).toContain('maskCapturedNetworkRequestFn: redactInviteNetworkRequest')
|
|
expect(source).toContain("url.pathname.startsWith('/join/')")
|
|
expect(source).toContain('/join/[invite]')
|
|
})
|
|
|
|
it('removes an invite code from every serialized URL or path property', () => {
|
|
const inviteCode = 'A4K9BT2Q'
|
|
const event = {
|
|
event: 'join_page_viewed',
|
|
properties: {
|
|
$current_url: `https://sauci.app/join/${inviteCode}`,
|
|
$pathname: `/join/${inviteCode}`,
|
|
$referrer: `https://sauci.app/join/${inviteCode}?source=message`,
|
|
$session_entry_url: `https://sauci.app/join/${inviteCode}`,
|
|
nested: { previous_path: `/join/${inviteCode}` },
|
|
},
|
|
}
|
|
|
|
const serialized = JSON.stringify(redactInvitePaths(event))
|
|
|
|
expect(serialized).not.toContain(inviteCode)
|
|
expect(serialized).toContain('/join/[invite]')
|
|
expect(serialized).toContain('join_page_viewed')
|
|
})
|
|
|
|
it('redacts invite codes from replay network request URLs, headers, and bodies without mutating the capture', () => {
|
|
const inviteCode = 'A4K9BT2Q'
|
|
const request = {
|
|
name: `https://sauci.app/join/${inviteCode}`,
|
|
entryType: 'resource',
|
|
startTime: 1,
|
|
duration: 2,
|
|
requestHeaders: {
|
|
referer: `https://sauci.app/join/${inviteCode}`,
|
|
},
|
|
requestBody: JSON.stringify({ invite_path: `/join/${inviteCode}` }),
|
|
responseHeaders: {
|
|
location: `https://sauci.app/join/${inviteCode}`,
|
|
},
|
|
responseBody: JSON.stringify({ referrer: `https://sauci.app/join/${inviteCode}` }),
|
|
} as CapturedNetworkRequest
|
|
|
|
const redacted = redactInviteNetworkRequest(request)
|
|
const serialized = JSON.stringify(redacted)
|
|
|
|
expect(redacted).not.toBe(request)
|
|
expect(redacted.requestHeaders).not.toBe(request.requestHeaders)
|
|
expect(redacted.responseHeaders).not.toBe(request.responseHeaders)
|
|
expect(serialized).not.toContain(inviteCode)
|
|
expect(serialized).toContain('/join/[invite]')
|
|
expect(JSON.stringify(request)).toContain(inviteCode)
|
|
})
|
|
})
|