import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; type Check = { name: string; file: string; patterns: string[]; }; const checks: Check[] = [ { name: 'dashboard route uses CRM security context', file: 'src/app/api/crm/dashboard/route.ts', patterns: ['buildCrmSecurityContext', 'getCrmDashboardData('] }, { name: 'dashboard export blocks revenue export without pricing visibility', file: 'src/app/api/crm/dashboard/export/route.ts', patterns: ['canViewQuotationPricing', "section === 'revenue-analytics'"] }, { name: 'quotation list route passes resolved scope context', file: 'src/app/api/crm/quotations/route.ts', patterns: ['buildCrmSecurityContext', 'listQuotations(organization.id', 'accessContext'] }, { name: 'quotation detail route passes resolved scope context', file: 'src/app/api/crm/quotations/[id]/route.ts', patterns: ['buildCrmSecurityContext', 'getQuotationDetail(id, organization.id, accessContext)'] }, { name: 'quotation PDF preview is pricing-gated', file: 'src/app/api/crm/quotations/[id]/pdf-preview/route.ts', patterns: ['canViewQuotationPricing', "action: 'pricing_hidden'"] }, { name: 'quotation PDF download is pricing-gated', file: 'src/app/api/crm/quotations/[id]/pdf-download/route.ts', patterns: ['canViewQuotationPricing', "action: 'pricing_hidden'"] }, { name: 'approved PDF route is pricing-gated', file: 'src/app/api/crm/quotations/[id]/approved-pdf/route.ts', patterns: ['canViewQuotationPricing', "action: 'pricing_hidden'"] }, { name: 'artifact download route protects approved quotation artifacts', file: 'src/app/api/crm/document-artifacts/[id]/download/route.ts', patterns: ['getArtifact', "artifact.artifactType === 'approved_pdf'", 'canViewQuotationPricing'] }, { name: 'approval step actor resolution uses resolved CRM access', file: 'src/features/foundation/approval/server/service.ts', patterns: ['resolveCrmMembershipAccess', 'resolvedAccess.businessRoles.includes(roleCode)'] }, { name: 'quotation service applies scoped record access', file: 'src/features/crm/quotations/server/service.ts', patterns: ['canAccessScopedCrmRecord', 'buildQuotationAccessScopedFilters', 'Forbidden branch scope'] }, { name: 'customer route passes resolved scope context', file: 'src/app/api/crm/customers/route.ts', patterns: ['buildCrmSecurityContext', 'listCustomers(organization.id', 'customer_scope_violation'] }, { name: 'customer detail route passes resolved scope context', file: 'src/app/api/crm/customers/[id]/route.ts', patterns: ['buildCrmSecurityContext', 'getCustomerDetail(id, organization.id, accessContext)'] }, { name: 'customer contact routes pass resolved scope context', file: 'src/app/api/crm/customers/[id]/contacts/route.ts', patterns: ['buildCrmSecurityContext', 'listCustomerContacts(id, organization.id, accessContext)'] }, { name: 'customer service applies access-scoped ownership filtering', file: 'src/features/crm/customers/server/service.ts', patterns: ['resolveAccessibleCustomerRelationSets', 'canAccessCustomerRow', 'ownershipScope === \'monitor\''] }, { name: 'approval route passes resolved scope context', file: 'src/app/api/crm/approvals/route.ts', patterns: ['buildCrmSecurityContext', 'listApprovalRequests(organization.id', 'approval_scope_violation'] }, { name: 'approval detail route passes resolved scope context', file: 'src/app/api/crm/approvals/[id]/route.ts', patterns: ['buildCrmSecurityContext', 'getApprovalRequest(id, organization.id, accessContext)'] }, { name: 'approval service enforces current-actor or quotation visibility', file: 'src/features/foundation/approval/server/service.ts', patterns: ['canCurrentActorAccessApproval', 'canAccessQuotationForApproval', 'visibleItems'] } ]; let hasFailure = false; for (const check of checks) { const absolutePath = resolve(process.cwd(), check.file); const content = readFileSync(absolutePath, 'utf8'); const missing = check.patterns.filter((pattern) => !content.includes(pattern)); if (missing.length > 0) { hasFailure = true; console.error(`FAIL ${check.name}`); console.error(` File: ${check.file}`); console.error(` Missing: ${missing.join(', ')}`); continue; } console.info(`PASS ${check.name}`); } if (hasFailure) { process.exit(1); }