fix(entitlements): close paywall leaks on interactive bank, SKV, and email routes (#910)
* fix(entitlements): close paywall leaks on interactive bank, SKV, and email routes The capability gate covered crons, MCP tools, and invoice send, but four interactive server paths still bypassed it ahead of the 2026-07-07 trial cutover: - enable-banking POST /connect and /sync (bank_sync) - skatteverket authorize/validate/draft/lock/submit/spara/las/kontrollera/ skattekonto-sync (skatteverket); unlock and all reads stay free, and the AGI/VAT file download path remains ungated per the manual-filing decision - salary payslip email send (email_send) - recurring-invoice auto-send (email_send); the invoice is still created, only the email is withheld (freeze-and-retain) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(entitlements): pin unlock routes as paywall-free recovery paths Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
573feea890
commit
7f24ede6c0
@@ -14,6 +14,10 @@ vi.mock('../lib/sync', () => ({
|
||||
syncAccountTransactions: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/entitlements/has-capability', () => ({
|
||||
requireCapability: vi.fn().mockResolvedValue(null),
|
||||
}))
|
||||
|
||||
import {
|
||||
isSessionExpiredResponse,
|
||||
SessionExpiredError,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
vi.mock('@/lib/entitlements/has-capability', () => ({
|
||||
requireCapability: vi.fn().mockResolvedValue(null),
|
||||
}))
|
||||
|
||||
import { enableBankingExtension } from '../index'
|
||||
import type { ExtensionContext } from '@/lib/extensions/types'
|
||||
import type { StoredAccount } from '../types'
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
DEFAULT_UNATTENDED_CONFIDENCE_THRESHOLD,
|
||||
} from '@/lib/reconciliation/bank-reconciliation'
|
||||
import { checkRateLimit } from '@/lib/auth/rate-limit-http'
|
||||
import { requireCapability } from '@/lib/entitlements/has-capability'
|
||||
import { CAPABILITY } from '@/lib/entitlements/keys'
|
||||
import type { StoredAccount } from './types'
|
||||
import type { Transaction } from '@/types'
|
||||
|
||||
@@ -107,6 +109,9 @@ export const enableBankingExtension: Extension = {
|
||||
}
|
||||
const companyId = ctx.companyId
|
||||
|
||||
const blocked = await requireCapability(supabase, companyId, CAPABILITY.bank_sync)
|
||||
if (blocked) return blocked
|
||||
|
||||
const { aspsp_name, aspsp_country, psu_type: explicitPsuType, connection_id: reconnectId } = await request.json()
|
||||
|
||||
// Reconnect mode: re-authorize an EXISTING connection in place (no
|
||||
@@ -410,6 +415,9 @@ export const enableBankingExtension: Extension = {
|
||||
}
|
||||
const companyId = ctx.companyId
|
||||
|
||||
const blocked = await requireCapability(supabase, companyId, CAPABILITY.bank_sync)
|
||||
if (blocked) return blocked
|
||||
|
||||
const rl = await checkRateLimit({
|
||||
prefix: 'enable-banking:sync',
|
||||
identifier: user.id,
|
||||
|
||||
Reference in New Issue
Block a user