@jmruthers/pace-core 0.6.6 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +12 -13
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +255 -0
  4. package/audit-tool/audits/03-architecture.cjs +196 -0
  5. package/audit-tool/audits/04-code-quality.cjs +149 -0
  6. package/audit-tool/audits/05-styling.cjs +224 -0
  7. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  9. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  10. package/audit-tool/audits/09-operations.cjs +208 -0
  11. package/audit-tool/index.cjs +291 -0
  12. package/audit-tool/utils/code-utils.cjs +218 -0
  13. package/audit-tool/utils/file-utils.cjs +230 -0
  14. package/audit-tool/utils/report-utils.cjs +241 -0
  15. package/cursor-rules/00-standards-overview.mdc +156 -0
  16. package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
  17. package/cursor-rules/02-project-structure.mdc +37 -5
  18. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
  19. package/cursor-rules/04-code-quality.mdc +419 -0
  20. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
  21. package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
  22. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  23. package/cursor-rules/08-testing-documentation.mdc +324 -0
  24. package/cursor-rules/09-operations.mdc +365 -0
  25. package/dist/DataTable-7PMH7XN7.js +15 -0
  26. package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  27. package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +26 -16
  28. package/dist/{chunk-FENMYN2U.js → chunk-5X4QLXRG.js} +1 -3
  29. package/dist/{chunk-4T7OBVTU.js → chunk-6F3IILHI.js} +1 -1
  30. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  31. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  32. package/dist/{chunk-7TYHROIV.js → chunk-BM4CQ5P3.js} +50 -8
  33. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  34. package/dist/{chunk-OHIK3MIO.js → chunk-GHYHJTYV.js} +2 -2
  35. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  36. package/dist/{chunk-LAZMKTTF.js → chunk-JGWDVX64.js} +281 -347
  37. package/dist/{chunk-MAGBIDNS.js → chunk-L4XMVJKY.js} +2 -2
  38. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  39. package/dist/{chunk-ZS5VO5JB.js → chunk-Q7Q7V5NV.js} +406 -451
  40. package/dist/{chunk-3O3WHILE.js → chunk-VBCS3DUA.js} +236 -60
  41. package/dist/{chunk-BVP2BCJF.js → chunk-ZKAWKYT4.js} +8 -8
  42. package/dist/components.d.ts +5 -4
  43. package/dist/components.js +27 -32
  44. package/dist/eslint-rules/index.cjs +22 -9
  45. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  46. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  47. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  48. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +26 -10
  49. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  50. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  51. package/dist/hooks.d.ts +5 -5
  52. package/dist/hooks.js +6 -6
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.js +18 -17
  55. package/dist/rbac/index.js +6 -6
  56. package/dist/theming/runtime.d.ts +14 -1
  57. package/dist/theming/runtime.js +1 -1
  58. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  59. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  60. package/dist/utils.d.ts +2 -2
  61. package/dist/utils.js +8 -8
  62. package/docs/README.md +1 -1
  63. package/docs/api/modules.md +47 -31
  64. package/docs/api-reference/components.md +18 -20
  65. package/docs/api-reference/hooks.md +80 -80
  66. package/docs/api-reference/types.md +1 -1
  67. package/docs/api-reference/utilities.md +1 -1
  68. package/docs/architecture/README.md +1 -1
  69. package/docs/core-concepts/events.md +3 -3
  70. package/docs/core-concepts/organisations.md +6 -6
  71. package/docs/core-concepts/permissions.md +6 -6
  72. package/docs/documentation-index.md +12 -18
  73. package/docs/getting-started/documentation-index.md +1 -1
  74. package/docs/getting-started/examples/README.md +4 -4
  75. package/docs/getting-started/examples/full-featured-app.md +1 -1
  76. package/docs/getting-started/faq.md +2 -2
  77. package/docs/getting-started/quick-reference.md +4 -4
  78. package/docs/implementation-guides/authentication.md +15 -15
  79. package/docs/implementation-guides/component-styling.md +1 -1
  80. package/docs/implementation-guides/data-tables.md +126 -33
  81. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  82. package/docs/implementation-guides/dynamic-colors.md +3 -3
  83. package/docs/implementation-guides/file-upload-storage.md +2 -2
  84. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  85. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  86. package/docs/implementation-guides/large-datasets.md +3 -2
  87. package/docs/implementation-guides/organisation-security.md +2 -2
  88. package/docs/implementation-guides/performance.md +2 -2
  89. package/docs/implementation-guides/permission-enforcement.md +1 -1
  90. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  91. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  92. package/docs/rbac/README.md +5 -5
  93. package/docs/rbac/advanced-patterns.md +6 -6
  94. package/docs/rbac/api-reference.md +20 -20
  95. package/docs/rbac/event-based-apps.md +3 -3
  96. package/docs/rbac/examples.md +41 -41
  97. package/docs/rbac/getting-started.md +37 -37
  98. package/docs/rbac/performance.md +1 -1
  99. package/docs/rbac/quick-start.md +52 -52
  100. package/docs/rbac/secure-client-protection.md +1 -1
  101. package/docs/rbac/troubleshooting.md +1 -1
  102. package/docs/security/README.md +5 -5
  103. package/docs/standards/0-standards-overview.md +220 -0
  104. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +204 -185
  105. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  106. package/docs/standards/3-architecture-standards.md +606 -0
  107. package/docs/standards/4-code-quality-standards.md +728 -0
  108. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  109. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  110. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  111. package/docs/standards/8-testing-documentation-standards.md +401 -0
  112. package/docs/standards/9-operations-standards.md +1102 -0
  113. package/docs/standards/README.md +203 -104
  114. package/docs/troubleshooting/README.md +4 -4
  115. package/docs/troubleshooting/common-issues.md +2 -2
  116. package/docs/troubleshooting/debugging.md +9 -9
  117. package/docs/troubleshooting/migration.md +4 -4
  118. package/eslint-config-pace-core.cjs +21 -10
  119. package/package.json +6 -5
  120. package/scripts/install-cursor-rules.cjs +11 -243
  121. package/scripts/install-eslint-config.cjs +284 -0
  122. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  123. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  124. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +10 -10
  125. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  126. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  127. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  128. package/src/__tests__/templates/component.test.template.tsx +18 -15
  129. package/src/components/Calendar/Calendar.tsx +201 -47
  130. package/src/components/ContextSelector/ContextSelector.tsx +137 -153
  131. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  132. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  133. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  134. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  135. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  136. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  137. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  138. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  139. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  140. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  141. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  142. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  143. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  144. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  145. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  146. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  147. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  148. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  149. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  150. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  151. package/src/components/DataTable/components/index.ts +2 -1
  152. package/src/components/DataTable/types.ts +0 -18
  153. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  154. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  155. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  156. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  157. package/src/components/Dialog/Dialog.test.tsx +1 -0
  158. package/src/components/Dialog/Dialog.tsx +25 -8
  159. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  160. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  161. package/src/components/FileUpload/FileUpload.tsx +112 -130
  162. package/src/components/Progress/Progress.tsx +2 -4
  163. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  164. package/src/components/Select/Select.tsx +86 -77
  165. package/src/components/Select/types.ts +3 -0
  166. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  167. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  168. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  169. package/src/hooks/public/usePublicEvent.ts +5 -5
  170. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  171. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  172. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  173. package/src/hooks/useAppConfig.ts +2 -2
  174. package/src/hooks/useEventTheme.test.ts +7 -7
  175. package/src/hooks/useEventTheme.ts +1 -4
  176. package/src/hooks/useFileDisplay.ts +2 -2
  177. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  178. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  179. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  180. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  181. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  182. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  183. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  184. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  185. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  186. package/src/styles/core.css +7 -0
  187. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  188. package/src/theming/parseEventColours.ts +22 -10
  189. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  190. package/src/utils/storage/README.md +1 -1
  191. package/cursor-rules/01-standards-compliance.mdc +0 -285
  192. package/cursor-rules/04-testing-standards.mdc +0 -270
  193. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  194. package/cursor-rules/06-code-quality.mdc +0 -311
  195. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  196. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  197. package/cursor-rules/11-performance-optimization.mdc +0 -169
  198. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  199. package/dist/DataTable-LRJL4IRV.js +0 -15
  200. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  201. package/dist/eslint-rules/rules/components.cjs +0 -113
  202. package/dist/eslint-rules/rules/imports.cjs +0 -102
  203. package/docs/best-practices/README.md +0 -472
  204. package/docs/best-practices/accessibility.md +0 -604
  205. package/docs/best-practices/common-patterns.md +0 -516
  206. package/docs/best-practices/deployment.md +0 -1103
  207. package/docs/best-practices/performance.md +0 -1328
  208. package/docs/best-practices/security.md +0 -940
  209. package/docs/best-practices/testing.md +0 -1034
  210. package/docs/rbac/compliance/compliance-guide.md +0 -544
  211. package/docs/standards/01-standards-compliance.md +0 -188
  212. package/docs/standards/03-solid-principles.md +0 -39
  213. package/docs/standards/04-testing-standards.md +0 -36
  214. package/docs/standards/05-bug-reports-and-features.md +0 -27
  215. package/docs/standards/06-code-quality.md +0 -34
  216. package/docs/standards/07-tech-stack-compliance.md +0 -30
  217. package/docs/standards/10-error-handling-patterns.md +0 -401
  218. package/docs/standards/11-performance-optimization.md +0 -348
  219. package/docs/standards/12-ci-cd-integration.md +0 -370
  220. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  221. package/scripts/audit/audit-compliance.cjs +0 -1295
  222. package/scripts/audit/audit-components.cjs +0 -260
  223. package/scripts/audit/audit-rbac.cjs +0 -954
  224. package/scripts/audit/audit-standards.cjs +0 -1268
  225. package/scripts/audit/index.cjs +0 -1927
  226. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  227. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  228. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  229. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  230. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  231. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  232. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  233. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  234. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  235. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  236. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  237. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  238. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  239. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  240. package/src/components/DataTable/utils/debugTools.ts +0 -514
  241. package/src/eslint-rules/index.cjs +0 -22
  242. package/src/eslint-rules/rules/components.cjs +0 -113
  243. package/src/eslint-rules/rules/imports.cjs +0 -102
  244. package/src/eslint-rules/rules/rbac.cjs +0 -790
  245. package/src/eslint-rules/utils/helpers.cjs +0 -42
  246. package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
@@ -1,940 +0,0 @@
1
- ---
2
- lastUpdated: 2025-11-18T17:00:00+11:00
3
- version: 0.5.181
4
- reviewedBy: documentation-standards-audit
5
- ---
6
-
7
- # Security Best Practices
8
-
9
- Security is a critical aspect of any application. This guide provides comprehensive security best practices for using `@jmruthers/pace-core` in your applications.
10
-
11
- ## Overview
12
-
13
- The security model in `@jmruthers/pace-core` is built on several layers:
14
-
15
- - **Authentication**: Secure user authentication with Supabase
16
- - **Authorization**: Role-based access control (RBAC)
17
- - **Data Isolation**: Organisation-level data separation
18
- - **Input Validation**: Comprehensive input sanitization
19
- - **CSRF Protection**: Cross-site request forgery prevention
20
- - **XSS Prevention**: Cross-site scripting protection
21
-
22
- ## Authentication Security
23
-
24
- ### 1. Secure Password Requirements
25
-
26
- ```typescript
27
- import { passwordSchema, securePasswordSchema, calculatePasswordStrength } from '@jmruthers/pace-core/utils/validation';
28
- import { isStrongPassword } from '@jmruthers/pace-core/utils/validation';
29
-
30
- // Using Zod schema (recommended)
31
- function validateUserPassword(password: string) {
32
- const result = securePasswordSchema.safeParse(password);
33
- if (!result.success) {
34
- return { valid: false, errors: result.error.errors };
35
- }
36
- return { valid: true };
37
- }
38
-
39
- // Using simple validation function
40
- function checkPasswordStrength(password: string) {
41
- const strength = calculatePasswordStrength(password);
42
- return {
43
- isValid: strength.level !== 'very-weak' && strength.level !== 'weak',
44
- strength: strength.level,
45
- score: strength.score,
46
- feedback: strength.feedback
47
- };
48
- }
49
-
50
- // Basic validation
51
- const isValid = isStrongPassword(password); // Returns boolean
52
- ```
53
-
54
- ### 2. Password Strength Indicator
55
-
56
- ```typescript
57
- import { calculatePasswordStrength } from '@jmruthers/pace-core/utils/validation';
58
-
59
- function PasswordInput() {
60
- const [password, setPassword] = useState('');
61
- const strength = calculatePasswordStrength(password);
62
-
63
- return (
64
- <div>
65
- <input
66
- type="password"
67
- value={password}
68
- onChange={(e) => setPassword(e.target.value)}
69
- />
70
- <div className={`strength-${strength.level}`}>
71
- Strength: {strength.level} ({strength.score}/100)
72
- </div>
73
- {strength.feedback.length > 0 && (
74
- <ul>
75
- {strength.feedback.map((msg, i) => (
76
- <li key={i}>{msg}</li>
77
- ))}
78
- </ul>
79
- )}
80
- </div>
81
- );
82
- }
83
- ```
84
-
85
- ### 3. Session Management
86
-
87
- ```typescript
88
- import { useUnifiedAuth } from '@jmruthers/pace-core';
89
-
90
- function SessionManager() {
91
- const { session, signOut, refreshSession } = useUnifiedAuth();
92
-
93
- // Auto-refresh session before expiry
94
- useEffect(() => {
95
- if (session && session.expires_at) {
96
- const timeUntilExpiry = session.expires_at * 1000 - Date.now();
97
- const refreshTime = Math.max(timeUntilExpiry - 5 * 60 * 1000, 0); // 5 minutes before expiry
98
-
99
- const timer = setTimeout(() => {
100
- refreshSession();
101
- }, refreshTime);
102
-
103
- return () => clearTimeout(timer);
104
- }
105
- }, [session, refreshSession]);
106
-
107
- return (
108
- <div>
109
- <p>Session expires: {new Date(session?.expires_at * 1000).toLocaleString()}</p>
110
- <button onClick={signOut}>Sign Out</button>
111
- </div>
112
- );
113
- }
114
- ```
115
-
116
- ## Authorization Security
117
-
118
- ### 1. Server-Side Permission Validation
119
-
120
- Always validate permissions on the server side:
121
-
122
- ```typescript
123
- // Server-side middleware
124
- function withPermission(permission: string) {
125
- return async (req: Request, res: Response, next: NextFunction) => {
126
- const user = req.user;
127
-
128
- if (!user) {
129
- return res.status(401).json({ error: 'Unauthorized' });
130
- }
131
-
132
- const hasPermission = await checkUserPermission(user.id, permission);
133
-
134
- if (!hasPermission) {
135
- return res.status(403).json({ error: 'Insufficient permissions' });
136
- }
137
-
138
- next();
139
- };
140
- }
141
-
142
- // API route protection
143
- app.get('/api/users', withPermission('read:users'), (req, res) => {
144
- // Handle request
145
- });
146
- ```
147
-
148
- ### 2. Organisation Access Validation
149
-
150
- ```typescript
151
- // Validate organisation access
152
- function validateOrganisationAccess(userId: string, organisationId: string): Promise<boolean> {
153
- return supabase
154
- .from('organisation_members')
155
- .select('*')
156
- .eq('user_id', userId)
157
- .eq('organisation_id', organisationId)
158
- .eq('status', 'active')
159
- .single()
160
- .then(result => !!result.data);
161
- }
162
-
163
- // Use in API routes
164
- app.get('/api/organisations/:id/data', async (req, res) => {
165
- const { id } = req.params;
166
- const userId = req.user.id;
167
-
168
- const hasAccess = await validateOrganisationAccess(userId, id);
169
-
170
- if (!hasAccess) {
171
- return res.status(403).json({ error: 'Access denied' });
172
- }
173
-
174
- // Proceed with data access
175
- });
176
- ```
177
-
178
- ### 3. Row Level Security (RLS)
179
-
180
- Enable RLS on all tables and create appropriate policies:
181
-
182
- ```sql
183
- -- Enable RLS on events table
184
- ALTER TABLE events ENABLE ROW LEVEL SECURITY;
185
-
186
- -- Policy for reading events
187
- CREATE POLICY "Users can view events in their organisation" ON events
188
- FOR SELECT USING (
189
- organisation_id IN (
190
- SELECT organisation_id
191
- FROM organisation_members
192
- WHERE user_id = auth.uid() AND status = 'active'
193
- )
194
- );
195
-
196
- -- Policy for creating events
197
- CREATE POLICY "Users can create events in their organisation" ON events
198
- FOR INSERT WITH CHECK (
199
- organisation_id IN (
200
- SELECT organisation_id
201
- FROM organisation_members
202
- WHERE user_id = auth.uid() AND status = 'active'
203
- )
204
- );
205
-
206
- -- Policy for updating events
207
- CREATE POLICY "Users can update events they created" ON events
208
- FOR UPDATE USING (
209
- created_by = auth.uid() OR
210
- organisation_id IN (
211
- SELECT organisation_id
212
- FROM organisation_members
213
- WHERE user_id = auth.uid() AND role IN ('admin', 'owner')
214
- )
215
- );
216
- ```
217
-
218
- ## Input Validation and Sanitization
219
-
220
- ### 1. Form Validation
221
-
222
- ```typescript
223
- import { useZodForm } from '@jmruthers/pace-core';
224
- import { z } from 'zod';
225
-
226
- const userSchema = z.object({
227
- name: z.string()
228
- .min(1, 'Name is required')
229
- .max(100, 'Name must be less than 100 characters')
230
- .regex(/^[a-zA-Z\s]+$/, 'Name can only contain letters and spaces'),
231
-
232
- email: z.string()
233
- .email('Invalid email address')
234
- .max(255, 'Email must be less than 255 characters'),
235
-
236
- phone: z.string()
237
- .regex(/^\+?[1-9]\d{1,14}$/, 'Invalid phone number')
238
- .optional(),
239
-
240
- age: z.number()
241
- .min(13, 'Must be at least 13 years old')
242
- .max(120, 'Invalid age'),
243
- });
244
-
245
- function UserForm() {
246
- const { register, handleSubmit, errors } = useZodForm(userSchema);
247
-
248
- const onSubmit = async (data: z.infer<typeof userSchema>) => {
249
- // Data is validated and sanitized
250
- await createUser(data);
251
- };
252
-
253
- return (
254
- <form onSubmit={handleSubmit(onSubmit)}>
255
- <input {...register('name')} />
256
- {errors.name && <span>{errors.name.message}</span>}
257
-
258
- <input type="email" {...register('email')} />
259
- {errors.email && <span>{errors.email.message}</span>}
260
-
261
- <input type="tel" {...register('phone')} />
262
- {errors.phone && <span>{errors.phone.message}</span>}
263
-
264
- <input type="number" {...register('age')} />
265
- {errors.age && <span>{errors.age.message}</span>}
266
-
267
- <button type="submit">Submit</button>
268
- </form>
269
- );
270
- }
271
- ```
272
-
273
- ### 2. HTML Sanitization
274
-
275
- ```typescript
276
- import { sanitizeHtml } from '@jmruthers/pace-core';
277
-
278
- function RichTextEditor() {
279
- const [content, setContent] = useState('');
280
-
281
- const handleSave = () => {
282
- // Sanitize HTML before saving
283
- const sanitizedContent = sanitizeHtml(content, {
284
- allowedTags: ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li'],
285
- allowedAttributes: {},
286
- });
287
-
288
- saveContent(sanitizedContent);
289
- };
290
-
291
- return (
292
- <div>
293
- <textarea
294
- value={content}
295
- onChange={(e) => setContent(e.target.value)}
296
- placeholder="Enter content..."
297
- />
298
- <button onClick={handleSave}>Save</button>
299
- </div>
300
- );
301
- }
302
- ```
303
-
304
- ### 3. File Upload Security
305
-
306
- ```typescript
307
- import { validateFileUpload } from '@jmruthers/pace-core';
308
-
309
- const fileUploadConfig = {
310
- maxSize: 5 * 1024 * 1024, // 5MB
311
- allowedTypes: ['image/jpeg', 'image/png', 'image/gif'],
312
- allowedExtensions: ['.jpg', '.jpeg', '.png', '.gif'],
313
- };
314
-
315
- function FileUpload() {
316
- const handleFileUpload = async (file: File) => {
317
- try {
318
- const validation = await validateFileUpload(file, fileUploadConfig);
319
-
320
- if (!validation.isValid) {
321
- throw new Error(validation.error);
322
- }
323
-
324
- // Proceed with upload
325
- await uploadFile(file);
326
- } catch (error) {
327
- console.error('File upload failed:', error);
328
- }
329
- };
330
-
331
- return (
332
- <input
333
- type="file"
334
- accept="image/*"
335
- onChange={(e) => {
336
- const file = e.target.files?.[0];
337
- if (file) handleFileUpload(file);
338
- }}
339
- />
340
- );
341
- }
342
- ```
343
-
344
- ## CSRF Protection
345
-
346
- ### 1. CSRF Token Generation
347
-
348
- ```typescript
349
- import { generateCSRFToken, validateCSRFToken } from '@jmruthers/pace-core';
350
-
351
- // Generate CSRF token
352
- const csrfToken = generateCSRFToken();
353
-
354
- // Include in forms
355
- function SecureForm() {
356
- return (
357
- <form>
358
- <input type="hidden" name="_csrf" value={csrfToken} />
359
- {/* Form fields */}
360
- </form>
361
- );
362
- }
363
- ```
364
-
365
- ### 2. CSRF Token Validation
366
-
367
- ```typescript
368
- // Server-side validation
369
- function validateCSRFRequest(req: Request, res: Response, next: NextFunction) {
370
- const token = req.headers['x-csrf-token'] || req.body._csrf;
371
-
372
- if (!validateCSRFToken(token)) {
373
- return res.status(403).json({ error: 'Invalid CSRF token' });
374
- }
375
-
376
- next();
377
- }
378
-
379
- // Apply to sensitive routes
380
- app.post('/api/users', validateCSRFRequest, (req, res) => {
381
- // Handle request
382
- });
383
- ```
384
-
385
- ## XSS Prevention
386
-
387
- ### 1. Content Security Policy (CSP)
388
-
389
- ```typescript
390
- // Set CSP headers
391
- const cspHeaders = {
392
- 'Content-Security-Policy': [
393
- "default-src 'self'",
394
- "script-src 'self' 'unsafe-inline'",
395
- "style-src 'self' 'unsafe-inline'",
396
- "img-src 'self' data: https:",
397
- "font-src 'self'",
398
- "connect-src 'self'",
399
- "frame-ancestors 'none'",
400
- ].join('; '),
401
- };
402
-
403
- // Apply to all responses
404
- app.use((req, res, next) => {
405
- Object.entries(cspHeaders).forEach(([key, value]) => {
406
- res.setHeader(key, value);
407
- });
408
- next();
409
- });
410
- ```
411
-
412
- ### 2. Safe HTML Rendering
413
-
414
- ```typescript
415
- import { sanitizeHtml } from '@jmruthers/pace-core';
416
-
417
- function SafeContent({ content }: { content: string }) {
418
- const sanitizedContent = sanitizeHtml(content, {
419
- allowedTags: ['p', 'br', 'strong', 'em'],
420
- allowedAttributes: {},
421
- });
422
-
423
- return (
424
- <div
425
- dangerouslySetInnerHTML={{ __html: sanitizedContent }}
426
- className="safe-content"
427
- />
428
- );
429
- }
430
- ```
431
-
432
- ## API Security
433
-
434
- ### 1. Rate Limiting
435
-
436
- ```typescript
437
- import rateLimit from 'express-rate-limit';
438
-
439
- const authLimiter = rateLimit({
440
- windowMs: 15 * 60 * 1000, // 15 minutes
441
- max: 5, // limit each IP to 5 requests per windowMs
442
- message: 'Too many authentication attempts, please try again later',
443
- });
444
-
445
- const apiLimiter = rateLimit({
446
- windowMs: 15 * 60 * 1000, // 15 minutes
447
- max: 100, // limit each IP to 100 requests per windowMs
448
- });
449
-
450
- // Apply to routes
451
- app.use('/api/auth', authLimiter);
452
- app.use('/api', apiLimiter);
453
- ```
454
-
455
- ### 2. Request Validation
456
-
457
- ```typescript
458
- import { validateRequest } from '@jmruthers/pace-core';
459
-
460
- const userValidationSchema = z.object({
461
- name: z.string().min(1).max(100),
462
- email: z.string().email(),
463
- age: z.number().min(13).max(120),
464
- });
465
-
466
- function createUser(req: Request, res: Response) {
467
- const validation = validateRequest(req.body, userValidationSchema);
468
-
469
- if (!validation.isValid) {
470
- return res.status(400).json({
471
- error: 'Invalid request data',
472
- details: validation.errors
473
- });
474
- }
475
-
476
- // Proceed with user creation
477
- }
478
- ```
479
-
480
- ### 3. Secure Headers
481
-
482
- ```typescript
483
- import helmet from 'helmet';
484
-
485
- // Apply security headers
486
- app.use(helmet({
487
- contentSecurityPolicy: {
488
- directives: {
489
- defaultSrc: ["'self'"],
490
- scriptSrc: ["'self'", "'unsafe-inline'"],
491
- styleSrc: ["'self'", "'unsafe-inline'"],
492
- imgSrc: ["'self'", "data:", "https:"],
493
- },
494
- },
495
- hsts: {
496
- maxAge: 31536000,
497
- includeSubDomains: true,
498
- preload: true,
499
- },
500
- }));
501
- ```
502
-
503
- ## Data Security
504
-
505
- ### 1. Sensitive Data Encryption
506
-
507
- ```typescript
508
- import { encryptData, decryptData } from '@jmruthers/pace-core';
509
-
510
- // Encrypt sensitive data before storing
511
- const sensitiveData = {
512
- creditCard: '1234-5678-9012-3456',
513
- ssn: '123-45-6789',
514
- };
515
-
516
- const encryptedData = encryptData(JSON.stringify(sensitiveData));
517
-
518
- // Store encrypted data
519
- await supabase
520
- .from('user_sensitive_data')
521
- .insert({
522
- user_id: userId,
523
- encrypted_data: encryptedData,
524
- });
525
-
526
- // Decrypt when needed
527
- const decryptedData = JSON.parse(decryptData(encryptedData));
528
- ```
529
-
530
- ### 2. Secure Data Transmission
531
-
532
- ```typescript
533
- // Always use HTTPS in production
534
- if (process.env.NODE_ENV === 'production') {
535
- app.use((req, res, next) => {
536
- if (!req.secure) {
537
- return res.redirect(`https://${req.headers.host}${req.url}`);
538
- }
539
- next();
540
- });
541
- }
542
- ```
543
-
544
- ### 3. Audit Logging
545
-
546
- ```typescript
547
- import { createAuditLog } from '@jmruthers/pace-core';
548
-
549
- // Log security events
550
- async function logSecurityEvent(event: string, userId: string, details: any) {
551
- await createAuditLog({
552
- event,
553
- user_id: userId,
554
- details,
555
- timestamp: new Date().toISOString(),
556
- ip_address: req.ip,
557
- user_agent: req.headers['user-agent'],
558
- });
559
- }
560
-
561
- // Use in sensitive operations
562
- app.post('/api/users/:id/delete', async (req, res) => {
563
- const { id } = req.params;
564
- const userId = req.user.id;
565
-
566
- await deleteUser(id);
567
-
568
- await logSecurityEvent('user_deleted', userId, {
569
- deleted_user_id: id,
570
- reason: req.body.reason,
571
- });
572
-
573
- res.json({ success: true });
574
- });
575
- ```
576
-
577
- ## Environment Security
578
-
579
- ### 1. Environment Variables
580
-
581
- ```typescript
582
- // Validate required environment variables
583
- const requiredEnvVars = [
584
- 'SUPABASE_URL',
585
- 'SUPABASE_ANON_KEY',
586
- 'SUPABASE_SERVICE_ROLE_KEY',
587
- 'JWT_SECRET',
588
- 'ENCRYPTION_KEY',
589
- ];
590
-
591
- requiredEnvVars.forEach(varName => {
592
- if (!process.env[varName]) {
593
- throw new Error(`Missing required environment variable: ${varName}`);
594
- }
595
- });
596
- ```
597
-
598
- ### 2. Secure Configuration
599
-
600
- ```typescript
601
- // Production security settings
602
- const securityConfig = {
603
- production: {
604
- cors: {
605
- origin: process.env.ALLOWED_ORIGINS?.split(',') || [],
606
- credentials: true,
607
- },
608
- session: {
609
- secure: true,
610
- httpOnly: true,
611
- sameSite: 'strict',
612
- },
613
- rateLimit: {
614
- windowMs: 15 * 60 * 1000,
615
- max: 100,
616
- },
617
- },
618
- development: {
619
- cors: {
620
- origin: true,
621
- credentials: true,
622
- },
623
- session: {
624
- secure: false,
625
- httpOnly: true,
626
- sameSite: 'lax',
627
- },
628
- rateLimit: {
629
- windowMs: 15 * 60 * 1000,
630
- max: 1000,
631
- },
632
- },
633
- };
634
- ```
635
-
636
- ## Security Testing
637
-
638
- ### 1. Security Headers Testing
639
-
640
- ```typescript
641
- import { testSecurityHeaders } from '@jmruthers/pace-core';
642
-
643
- test('security headers are set correctly', async () => {
644
- const response = await request(app)
645
- .get('/api/health')
646
- .expect(200);
647
-
648
- expect(response.headers).toHaveProperty('x-content-type-options');
649
- expect(response.headers['x-content-type-options']).toBe('nosniff');
650
-
651
- expect(response.headers).toHaveProperty('x-frame-options');
652
- expect(response.headers['x-frame-options']).toBe('DENY');
653
-
654
- expect(response.headers).toHaveProperty('x-xss-protection');
655
- expect(response.headers['x-xss-protection']).toBe('1; mode=block');
656
- });
657
- ```
658
-
659
- ### 2. Authentication Testing
660
-
661
- ```typescript
662
- test('unauthenticated requests are rejected', async () => {
663
- await request(app)
664
- .get('/api/users')
665
- .expect(401);
666
- });
667
-
668
- test('unauthorized requests are rejected', async () => {
669
- const user = await createTestUser({ role: 'user' });
670
- const token = await generateAuthToken(user);
671
-
672
- await request(app)
673
- .get('/api/admin/users')
674
- .set('Authorization', `Bearer ${token}`)
675
- .expect(403);
676
- });
677
- ```
678
-
679
- ### 3. Input Validation Testing
680
-
681
- ```typescript
682
- test('malicious input is sanitized', async () => {
683
- const maliciousInput = '<script>alert("xss")</script>';
684
-
685
- const response = await request(app)
686
- .post('/api/users')
687
- .send({
688
- name: maliciousInput,
689
- email: 'test@example.com',
690
- })
691
- .expect(400);
692
-
693
- expect(response.body.error).toContain('Invalid input');
694
- });
695
- ```
696
-
697
- ## Security Monitoring
698
-
699
- ### 1. Security Event Monitoring
700
-
701
- ```typescript
702
- import { monitorSecurityEvents } from '@jmruthers/pace-core';
703
-
704
- // Monitor security events
705
- monitorSecurityEvents({
706
- onFailedLogin: (event) => {
707
- console.warn('Failed login attempt:', event);
708
- // Send alert to security team
709
- },
710
- onSuspiciousActivity: (event) => {
711
- console.error('Suspicious activity detected:', event);
712
- // Trigger security response
713
- },
714
- onPermissionViolation: (event) => {
715
- console.error('Permission violation:', event);
716
- // Log for investigation
717
- },
718
- });
719
- ```
720
-
721
- ### 2. Security Metrics
722
-
723
- ```typescript
724
- import { trackSecurityMetrics } from '@jmruthers/pace-core';
725
-
726
- // Track security metrics
727
- trackSecurityMetrics({
728
- failedLogins: 0,
729
- successfulLogins: 0,
730
- permissionDenials: 0,
731
- suspiciousActivities: 0,
732
- });
733
-
734
- // Export metrics for monitoring
735
- app.get('/api/security/metrics', (req, res) => {
736
- res.json(getSecurityMetrics());
737
- });
738
- ```
739
-
740
- ## Incident Response
741
-
742
- ### 1. Security Incident Handling
743
-
744
- ```typescript
745
- import { handleSecurityIncident } from '@jmruthers/pace-core';
746
-
747
- // Handle security incidents
748
- async function handleSecurityIncident(incident: SecurityIncident) {
749
- // Log the incident
750
- await logSecurityEvent('security_incident', incident.userId, incident);
751
-
752
- // Take immediate action
753
- if (incident.type === 'brute_force') {
754
- await lockUserAccount(incident.userId);
755
- await notifySecurityTeam(incident);
756
- }
757
-
758
- if (incident.type === 'data_breach') {
759
- await revokeAllSessions(incident.userId);
760
- await forcePasswordReset(incident.userId);
761
- await notifyUser(incident.userId, 'security_alert');
762
- }
763
- }
764
- ```
765
-
766
- ### 2. Security Alerts
767
-
768
- ```typescript
769
- import { createSecurityAlert } from '@jmruthers/pace-core';
770
-
771
- // Create security alerts
772
- async function createSecurityAlert(alert: SecurityAlert) {
773
- await createAuditLog({
774
- event: 'security_alert',
775
- user_id: alert.userId,
776
- details: alert,
777
- severity: alert.severity,
778
- timestamp: new Date().toISOString(),
779
- });
780
-
781
- // Send notification based on severity
782
- if (alert.severity === 'high') {
783
- await sendImmediateAlert(alert);
784
- }
785
- }
786
- ```
787
-
788
- ## Compliance and Standards
789
-
790
- ### 1. GDPR Compliance
791
-
792
- ```typescript
793
- import { gdprCompliance } from '@jmruthers/pace-core';
794
-
795
- // GDPR compliance utilities
796
- const gdprUtils = {
797
- // Right to be forgotten
798
- deleteUserData: async (userId: string) => {
799
- await supabase.from('users').delete().eq('id', userId);
800
- await supabase.from('user_data').delete().eq('user_id', userId);
801
- await logSecurityEvent('gdpr_deletion', userId, { reason: 'right_to_be_forgotten' });
802
- },
803
-
804
- // Data portability
805
- exportUserData: async (userId: string) => {
806
- const userData = await supabase
807
- .from('users')
808
- .select('*')
809
- .eq('id', userId)
810
- .single();
811
-
812
- return JSON.stringify(userData, null, 2);
813
- },
814
-
815
- // Consent management
816
- updateConsent: async (userId: string, consent: ConsentSettings) => {
817
- await supabase
818
- .from('user_consent')
819
- .upsert({
820
- user_id: userId,
821
- marketing_emails: consent.marketingEmails,
822
- analytics_tracking: consent.analyticsTracking,
823
- updated_at: new Date().toISOString(),
824
- });
825
- },
826
- };
827
- ```
828
-
829
- ### 2. SOC 2 Compliance
830
-
831
- ```typescript
832
- import { soc2Compliance } from '@jmruthers/pace-core';
833
-
834
- // SOC 2 compliance monitoring
835
- const soc2Utils = {
836
- // Access control monitoring
837
- monitorAccess: async (userId: string, resource: string, action: string) => {
838
- await createAuditLog({
839
- event: 'access_attempt',
840
- user_id: userId,
841
- details: { resource, action, timestamp: new Date().toISOString() },
842
- });
843
- },
844
-
845
- // Data integrity checks
846
- verifyDataIntegrity: async () => {
847
- // Implement data integrity verification
848
- const checksum = await calculateDataChecksum();
849
- return checksum;
850
- },
851
-
852
- // Backup verification
853
- verifyBackups: async () => {
854
- // Implement backup verification
855
- const backupStatus = await checkBackupStatus();
856
- return backupStatus;
857
- },
858
- };
859
- ```
860
-
861
- ## Security Checklist
862
-
863
- ### Pre-Deployment Checklist
864
-
865
- - [ ] All environment variables are properly set
866
- - [ ] HTTPS is enabled in production
867
- - [ ] Security headers are configured
868
- - [ ] Rate limiting is implemented
869
- - [ ] Input validation is in place
870
- - [ ] CSRF protection is enabled
871
- - [ ] XSS protection is implemented
872
- - [ ] SQL injection protection is active
873
- - [ ] File upload validation is configured
874
- - [ ] Audit logging is enabled
875
- - [ ] Error messages don't leak sensitive information
876
- - [ ] Session management is secure
877
- - [ ] Password requirements are enforced
878
- - [ ] Multi-factor authentication is available
879
- - [ ] Row-level security is enabled
880
- - [ ] API endpoints are properly protected
881
-
882
- ### Ongoing Security Maintenance
883
-
884
- - [ ] Regular security updates
885
- - [ ] Dependency vulnerability scanning
886
- - [ ] Security audit logging review
887
- - [ ] Penetration testing
888
- - [ ] Security incident response plan
889
- - [ ] User access review
890
- - [ ] Data backup verification
891
- - [ ] Compliance monitoring
892
-
893
- For more information about implementing security in your application, see the [Authentication Guide](../core-concepts/authentication.md) and [RBAC System Guide](../core-concepts/rbac-system.md).
894
-
895
- ## ♿ Accessibility
896
-
897
- Security practices should maintain accessibility:
898
-
899
- - **Error messages are accessible** - Security errors should be clearly communicated
900
- - **Authentication flows are keyboard accessible** - All security forms should support keyboard navigation
901
- - **Screen reader support** - Security messages should be properly announced
902
- - **Focus management** - Ensure focus is properly managed during security operations
903
- - **Timeouts are announced** - Session timeouts should be accessible to screen readers
904
-
905
- ### Accessibility Best Practices
906
-
907
- 1. **Test authentication with screen readers** - Verify login flows work with assistive technologies
908
- 2. **Ensure keyboard access** - All security forms should be keyboard accessible
909
- 3. **Provide clear error messages** - Security errors should be descriptive without exposing vulnerabilities
910
- 4. **Announce security state changes** - Use ARIA live regions for security status updates
911
- 5. **Test with assistive technologies** - Verify all security features work with screen readers
912
-
913
- ## ⚠️ Edge Cases
914
-
915
- ### Security vs Usability Conflicts
916
-
917
- When security measures impact usability:
918
- - Find balance between security and user experience
919
- - Provide clear guidance for security requirements
920
- - Implement security transparently
921
- - Test with real users to ensure usability
922
- - Document security decisions
923
-
924
- ### Rate Limiting Edge Cases
925
-
926
- When rate limiting causes issues:
927
- - Provide clear error messages for rate limit violations
928
- - Implement progressive backoff for retries
929
- - Consider user experience when setting limits
930
- - Monitor rate limit effectiveness
931
- - Adjust limits based on legitimate use patterns
932
-
933
- ### Session Management Edge Cases
934
-
935
- When session management fails:
936
- - Handle session expiration gracefully
937
- - Provide clear messages for session issues
938
- - Ensure proper cleanup on session end
939
- - Test with multiple concurrent sessions
940
- - Verify session security across devices