@jmruthers/pace-core 0.6.2 → 0.6.3

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 (299) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/cursor-rules/00-pace-core-compliance.mdc +34 -2
  3. package/dist/{AuthService-BPvc3Ka0.d.ts → AuthService-Cb34EQs3.d.ts} +9 -1
  4. package/dist/{DataTable-TPTKCX4D.js → DataTable-THFPBKTP.js} +9 -8
  5. package/dist/{PublicPageProvider-DC6kCaqf.d.ts → PublicPageProvider-DEMpysFR.d.ts} +45 -67
  6. package/dist/{UnifiedAuthProvider-CVcTjx-d.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +1 -8
  7. package/dist/{UnifiedAuthProvider-CH6Z342H.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
  8. package/dist/{api-MVVQZLJI.js → api-IAGWF3ZG.js} +10 -10
  9. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  10. package/dist/{chunk-SFZUDBL5.js → chunk-2T2IG7T7.js} +70 -56
  11. package/dist/chunk-2T2IG7T7.js.map +1 -0
  12. package/dist/{chunk-MMZ7JXPU.js → chunk-6Z7LTB3D.js} +13 -21
  13. package/dist/{chunk-MMZ7JXPU.js.map → chunk-6Z7LTB3D.js.map} +1 -1
  14. package/dist/{chunk-6J4GEEJR.js → chunk-CNCQDFLN.js} +53 -27
  15. package/dist/chunk-CNCQDFLN.js.map +1 -0
  16. package/dist/chunk-DGUM43GV.js +11 -0
  17. package/dist/{chunk-EHMR7VYL.js → chunk-DWUBLJJM.js} +361 -187
  18. package/dist/chunk-DWUBLJJM.js.map +1 -0
  19. package/dist/{chunk-2UOI2FG5.js → chunk-HFZBI76P.js} +4 -4
  20. package/dist/{chunk-F2IMUDXZ.js → chunk-M7MPQISP.js} +2 -2
  21. package/dist/{chunk-3XC4CPTD.js → chunk-PQBSKX33.js} +244 -5727
  22. package/dist/chunk-PQBSKX33.js.map +1 -0
  23. package/dist/chunk-QRPVRXYT.js +226 -0
  24. package/dist/chunk-QRPVRXYT.js.map +1 -0
  25. package/dist/{chunk-24UVZUZG.js → chunk-RWEBCB47.js} +129 -387
  26. package/dist/chunk-RWEBCB47.js.map +1 -0
  27. package/dist/{chunk-XWQCNGTQ.js → chunk-YDQHOZNA.js} +173 -79
  28. package/dist/chunk-YDQHOZNA.js.map +1 -0
  29. package/dist/{chunk-NECFR5MM.js → chunk-ZNIWI3UC.js} +562 -644
  30. package/dist/chunk-ZNIWI3UC.js.map +1 -0
  31. package/dist/components.d.ts +2 -2
  32. package/dist/components.js +12 -13
  33. package/dist/contextValidator-3JNZKUTX.js +9 -0
  34. package/dist/contextValidator-3JNZKUTX.js.map +1 -0
  35. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  36. package/dist/hooks.d.ts +2 -2
  37. package/dist/hooks.js +7 -6
  38. package/dist/hooks.js.map +1 -1
  39. package/dist/index.d.ts +7 -7
  40. package/dist/index.js +21 -16
  41. package/dist/index.js.map +1 -1
  42. package/dist/providers.d.ts +3 -3
  43. package/dist/providers.js +4 -3
  44. package/dist/rbac/index.d.ts +67 -27
  45. package/dist/rbac/index.js +15 -8
  46. package/dist/styles/index.js +1 -1
  47. package/dist/theming/runtime.js +1 -1
  48. package/dist/types.js +1 -1
  49. package/dist/{usePublicRouteParams-1oMokgLF.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +7 -16
  50. package/dist/utils.js +5 -7
  51. package/dist/utils.js.map +1 -1
  52. package/docs/api/README.md +14 -16
  53. package/docs/api/modules.md +3796 -2513
  54. package/docs/components/context-selector.md +126 -0
  55. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  56. package/docs/pace-mint-fix-auto-selection.md +218 -0
  57. package/docs/pace-mint-rbac-setup.md +391 -0
  58. package/docs/rbac/secure-client-protection.md +330 -0
  59. package/package.json +3 -3
  60. package/scripts/audit/core/checks/compliance.cjs +72 -0
  61. package/scripts/audit/core/checks/dependencies.cjs +559 -28
  62. package/scripts/audit/core/checks/documentation.cjs +68 -3
  63. package/scripts/audit/core/checks/environment.cjs +2 -14
  64. package/scripts/audit/core/checks/error-handling.cjs +47 -6
  65. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  66. package/src/components/ContextSelector/index.ts +3 -0
  67. package/src/components/DataTable/components/RowComponent.tsx +19 -19
  68. package/src/components/DataTable/components/UnifiedTableBody.tsx +2 -2
  69. package/src/components/DataTable/hooks/useDataTablePermissions.ts +8 -6
  70. package/src/components/Dialog/Dialog.tsx +29 -1
  71. package/src/components/FileDisplay/FileDisplay.tsx +42 -10
  72. package/src/components/Header/Header.test.tsx +43 -73
  73. package/src/components/Header/Header.tsx +44 -45
  74. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  75. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  76. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  77. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +9 -9
  78. package/src/components/PaceAppLayout/PaceAppLayout.tsx +135 -33
  79. package/src/components/PaceAppLayout/README.md +14 -17
  80. package/src/components/PaceAppLayout/test-setup.tsx +2 -2
  81. package/src/components/index.ts +5 -5
  82. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  83. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  84. package/src/hooks/useAppConfig.ts +15 -30
  85. package/src/hooks/useFileDisplay.ts +77 -50
  86. package/src/index.ts +4 -5
  87. package/src/providers/services/AuthServiceProvider.tsx +17 -7
  88. package/src/providers/services/EventServiceProvider.tsx +33 -5
  89. package/src/providers/services/UnifiedAuthProvider.tsx +90 -134
  90. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  91. package/src/rbac/adapters.tsx +2 -2
  92. package/src/rbac/api.test.ts +59 -51
  93. package/src/rbac/api.ts +178 -132
  94. package/src/rbac/components/PagePermissionGuard.tsx +38 -10
  95. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  96. package/src/rbac/hooks/permissions/useAccessLevel.ts +1 -1
  97. package/src/rbac/hooks/permissions/useCan.ts +41 -11
  98. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +1 -1
  99. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +1 -1
  100. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +1 -1
  101. package/src/rbac/hooks/useCan.test.ts +0 -9
  102. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  103. package/src/rbac/hooks/useRBAC.ts +36 -37
  104. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  105. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  106. package/src/rbac/hooks/useSecureSupabase.ts +7 -7
  107. package/src/rbac/index.ts +7 -0
  108. package/src/rbac/secureClient.test.ts +22 -18
  109. package/src/rbac/secureClient.ts +103 -16
  110. package/src/rbac/security.ts +0 -17
  111. package/src/rbac/types.ts +1 -0
  112. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  113. package/src/rbac/utils/clientSecurity.ts +93 -0
  114. package/src/rbac/utils/contextValidator.ts +77 -168
  115. package/src/services/AuthService.ts +39 -7
  116. package/src/services/EventService.ts +186 -54
  117. package/src/services/OrganisationService.ts +81 -14
  118. package/src/services/__tests__/EventService.test.ts +1 -2
  119. package/src/services/base/BaseService.ts +3 -0
  120. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  121. package/dist/chunk-24UVZUZG.js.map +0 -1
  122. package/dist/chunk-3XC4CPTD.js.map +0 -1
  123. package/dist/chunk-6J4GEEJR.js.map +0 -1
  124. package/dist/chunk-7D4SUZUM.js +0 -38
  125. package/dist/chunk-EHMR7VYL.js.map +0 -1
  126. package/dist/chunk-NECFR5MM.js.map +0 -1
  127. package/dist/chunk-SFZUDBL5.js.map +0 -1
  128. package/dist/chunk-XWQCNGTQ.js.map +0 -1
  129. package/docs/api/classes/ColumnFactory.md +0 -243
  130. package/docs/api/classes/InvalidScopeError.md +0 -73
  131. package/docs/api/classes/Logger.md +0 -178
  132. package/docs/api/classes/MissingUserContextError.md +0 -66
  133. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  134. package/docs/api/classes/PermissionDeniedError.md +0 -73
  135. package/docs/api/classes/RBACAuditManager.md +0 -297
  136. package/docs/api/classes/RBACCache.md +0 -322
  137. package/docs/api/classes/RBACEngine.md +0 -171
  138. package/docs/api/classes/RBACError.md +0 -76
  139. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  140. package/docs/api/classes/SecureSupabaseClient.md +0 -163
  141. package/docs/api/classes/StorageUtils.md +0 -328
  142. package/docs/api/enums/FileCategory.md +0 -184
  143. package/docs/api/enums/LogLevel.md +0 -54
  144. package/docs/api/enums/RBACErrorCode.md +0 -228
  145. package/docs/api/enums/RPCFunction.md +0 -118
  146. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  147. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  148. package/docs/api/interfaces/AggregateConfig.md +0 -43
  149. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  150. package/docs/api/interfaces/AvatarProps.md +0 -128
  151. package/docs/api/interfaces/BadgeProps.md +0 -34
  152. package/docs/api/interfaces/ButtonProps.md +0 -56
  153. package/docs/api/interfaces/CalendarProps.md +0 -73
  154. package/docs/api/interfaces/CardProps.md +0 -69
  155. package/docs/api/interfaces/ColorPalette.md +0 -7
  156. package/docs/api/interfaces/ColorShade.md +0 -66
  157. package/docs/api/interfaces/ComplianceResult.md +0 -30
  158. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  159. package/docs/api/interfaces/DataRecord.md +0 -11
  160. package/docs/api/interfaces/DataTableAction.md +0 -252
  161. package/docs/api/interfaces/DataTableColumn.md +0 -504
  162. package/docs/api/interfaces/DataTableProps.md +0 -625
  163. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  164. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  165. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  166. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  167. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  168. package/docs/api/interfaces/ErrorBoundaryProps.md +0 -147
  169. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +0 -36
  170. package/docs/api/interfaces/ErrorBoundaryState.md +0 -75
  171. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  172. package/docs/api/interfaces/ExportColumn.md +0 -90
  173. package/docs/api/interfaces/ExportOptions.md +0 -126
  174. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  175. package/docs/api/interfaces/FileMetadata.md +0 -129
  176. package/docs/api/interfaces/FileReference.md +0 -118
  177. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  178. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  179. package/docs/api/interfaces/FileUploadProps.md +0 -296
  180. package/docs/api/interfaces/FooterProps.md +0 -107
  181. package/docs/api/interfaces/FormFieldProps.md +0 -166
  182. package/docs/api/interfaces/FormProps.md +0 -113
  183. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  184. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  185. package/docs/api/interfaces/InputProps.md +0 -56
  186. package/docs/api/interfaces/LabelProps.md +0 -107
  187. package/docs/api/interfaces/LoggerConfig.md +0 -62
  188. package/docs/api/interfaces/LoginFormProps.md +0 -187
  189. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  190. package/docs/api/interfaces/NavigationContextType.md +0 -164
  191. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  192. package/docs/api/interfaces/NavigationItem.md +0 -120
  193. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  194. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  195. package/docs/api/interfaces/Organisation.md +0 -140
  196. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  197. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  198. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  199. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  200. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -409
  201. package/docs/api/interfaces/PaceLoginPageProps.md +0 -49
  202. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  203. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  204. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  205. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  206. package/docs/api/interfaces/PaletteData.md +0 -41
  207. package/docs/api/interfaces/ParsedAddress.md +0 -120
  208. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  209. package/docs/api/interfaces/ProgressProps.md +0 -42
  210. package/docs/api/interfaces/ProtectedRouteProps.md +0 -78
  211. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  212. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  213. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -185
  214. package/docs/api/interfaces/QuickFix.md +0 -52
  215. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  216. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  217. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  218. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  219. package/docs/api/interfaces/RBACConfig.md +0 -133
  220. package/docs/api/interfaces/RBACContext.md +0 -52
  221. package/docs/api/interfaces/RBACLogger.md +0 -112
  222. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  223. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  224. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  225. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  226. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  227. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  228. package/docs/api/interfaces/RBACResult.md +0 -58
  229. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  230. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  231. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  232. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  233. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  234. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  235. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  236. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  237. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  238. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  239. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  240. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  241. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  242. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  243. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  244. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  245. package/docs/api/interfaces/RouteConfig.md +0 -134
  246. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  247. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  248. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  249. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  250. package/docs/api/interfaces/SetupIssue.md +0 -41
  251. package/docs/api/interfaces/StorageConfig.md +0 -41
  252. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  253. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  254. package/docs/api/interfaces/StorageListOptions.md +0 -99
  255. package/docs/api/interfaces/StorageListResult.md +0 -41
  256. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  257. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  258. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  259. package/docs/api/interfaces/StyleImport.md +0 -19
  260. package/docs/api/interfaces/SwitchProps.md +0 -34
  261. package/docs/api/interfaces/TabsContentProps.md +0 -9
  262. package/docs/api/interfaces/TabsListProps.md +0 -9
  263. package/docs/api/interfaces/TabsProps.md +0 -9
  264. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  265. package/docs/api/interfaces/TextareaProps.md +0 -53
  266. package/docs/api/interfaces/ToastActionElement.md +0 -12
  267. package/docs/api/interfaces/ToastProps.md +0 -9
  268. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -823
  269. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -173
  270. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  271. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  272. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -138
  273. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  274. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  275. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -84
  276. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  277. package/docs/api/interfaces/UsePublicEventReturn.md +0 -71
  278. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  279. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -123
  280. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -97
  281. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  282. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  283. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  284. package/docs/api/interfaces/UserEventAccess.md +0 -121
  285. package/docs/api/interfaces/UserMenuProps.md +0 -88
  286. package/docs/api/interfaces/UserProfile.md +0 -63
  287. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  288. package/src/components/EventSelector/EventSelector.tsx +0 -423
  289. package/src/components/EventSelector/index.ts +0 -3
  290. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  291. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -327
  292. package/src/components/OrganisationSelector/index.ts +0 -9
  293. /package/dist/{DataTable-TPTKCX4D.js.map → DataTable-THFPBKTP.js.map} +0 -0
  294. /package/dist/{UnifiedAuthProvider-CH6Z342H.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
  295. /package/dist/{api-MVVQZLJI.js.map → api-IAGWF3ZG.js.map} +0 -0
  296. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  297. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  298. /package/dist/{chunk-2UOI2FG5.js.map → chunk-HFZBI76P.js.map} +0 -0
  299. /package/dist/{chunk-F2IMUDXZ.js.map → chunk-M7MPQISP.js.map} +0 -0
@@ -0,0 +1,330 @@
1
+ # Secure Supabase Client Protection
2
+
3
+ This document describes the multi-layered protection system to prevent consuming apps from using insecure Supabase clients that bypass organisation context and RLS policies.
4
+
5
+ ## Problem
6
+
7
+ Using `createClient()` from `@supabase/supabase-js` directly bypasses:
8
+ - Organisation context enforcement
9
+ - RLS (Row Level Security) policies
10
+ - Event and app context injection
11
+ - Security safeguards built into pace-core
12
+
13
+ This can lead to:
14
+ - **Cross-organisation data access** - Users accessing data from organisations they shouldn't
15
+ - **Security vulnerabilities** - Bypassing permission checks
16
+ - **Data leakage** - Accidental exposure of sensitive data
17
+
18
+ ## Protection Layers
19
+
20
+ ### 1. ESLint Rule: `no-direct-supabase-client`
21
+
22
+ **Location**: `packages/core/src/eslint-rules/pace-core-compliance.cjs`
23
+
24
+ **What it does**:
25
+ - Detects `createClient` imports from `@supabase/supabase-js`
26
+ - Detects `createClient()` function calls
27
+ - Reports errors with helpful suggestions
28
+
29
+ **How to use**:
30
+ ```js
31
+ // In your consuming app's eslint.config.js:
32
+ import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
33
+
34
+ export default [
35
+ ...paceCoreConfig,
36
+ // pace-core-compliance/no-direct-supabase-client is automatically enabled
37
+ ];
38
+ ```
39
+
40
+ **Example violations**:
41
+ ```tsx
42
+ // ❌ ERROR: Direct import detected
43
+ import { createClient } from '@supabase/supabase-js';
44
+
45
+ // ❌ ERROR: Direct client creation
46
+ const supabase = createClient(url, key);
47
+ ```
48
+
49
+ ### 2. Runtime Detection: `warnIfInsecureClient()`
50
+
51
+ **Location**: `packages/core/src/rbac/utils/clientSecurity.ts`
52
+
53
+ **What it does**:
54
+ - Checks if a client is marked as secure
55
+ - Warns in development mode when insecure clients are detected
56
+ - Provides helpful error messages with fix suggestions
57
+
58
+ **How to use**:
59
+ ```tsx
60
+ import { warnIfInsecureClient } from '@jmruthers/pace-core/rbac';
61
+
62
+ function MyComponent() {
63
+ const supabase = useSecureSupabase();
64
+
65
+ // Optional: Warn if client is insecure (development only)
66
+ warnIfInsecureClient(supabase, 'MyComponent');
67
+
68
+ // Use supabase...
69
+ }
70
+ ```
71
+
72
+ **Output** (development mode only):
73
+ ```
74
+ [pace-core Security Warning] Non-secure Supabase client detected in MyComponent.
75
+ You are using a Supabase client created with createClient() instead of useSecureSupabase().
76
+ This bypasses organisation context enforcement and RLS policies...
77
+ ```
78
+
79
+ ### 3. Type Safety: `isSecureClient()`
80
+
81
+ **Location**: `packages/core/src/rbac/utils/clientSecurity.ts`
82
+
83
+ **What it does**:
84
+ - Type guard to check if a client is secure
85
+ - Returns `true` only for clients created via `useSecureSupabase()` or `createSecureClient()`
86
+ - Can be used for runtime checks and TypeScript narrowing
87
+
88
+ **How to use**:
89
+ ```tsx
90
+ import { isSecureClient } from '@jmruthers/pace-core/rbac';
91
+
92
+ function MyComponent() {
93
+ const supabase = useSecureSupabase();
94
+
95
+ if (isSecureClient(supabase)) {
96
+ // TypeScript knows supabase is secure here
97
+ // Safe to use for database operations
98
+ } else {
99
+ // Client is not secure - handle error
100
+ console.error('Insecure client detected!');
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### 4. Automatic Client Marking
106
+
107
+ **Location**: `packages/core/src/rbac/secureClient.ts`
108
+
109
+ **What it does**:
110
+ - Automatically marks clients created via `SecureSupabaseClient` as secure
111
+ - Uses a Symbol (`SECURE_CLIENT_SYMBOL`) to mark secure clients
112
+ - Works transparently - no action needed from developers
113
+
114
+ **How it works**:
115
+ ```tsx
116
+ // When you use useSecureSupabase():
117
+ const supabase = useSecureSupabase();
118
+ // Client is automatically marked as secure internally
119
+
120
+ // When you use createSecureClient():
121
+ const secureClient = createSecureClient(url, key, orgId);
122
+ const supabase = secureClient.getClient();
123
+ // Client is automatically marked as secure internally
124
+ ```
125
+
126
+ ## Correct Usage
127
+
128
+ ### ✅ Use `useSecureSupabase()` Hook
129
+
130
+ ```tsx
131
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
132
+
133
+ function MyComponent() {
134
+ const supabase = useSecureSupabase();
135
+
136
+ if (!supabase) {
137
+ return <div>Loading...</div>;
138
+ }
139
+
140
+ // Organisation context is automatically enforced
141
+ const { data } = await supabase.from('users').select('*');
142
+ }
143
+ ```
144
+
145
+ ### ✅ Use `createSecureClient()` for Non-React Code
146
+
147
+ ```tsx
148
+ import { createSecureClient } from '@jmruthers/pace-core/rbac';
149
+
150
+ // For server-side or non-React code
151
+ const secureClient = createSecureClient(
152
+ url,
153
+ key,
154
+ organisationId,
155
+ eventId,
156
+ appId,
157
+ isSuperAdmin
158
+ );
159
+ const supabase = secureClient.getClient();
160
+ ```
161
+
162
+ ### ✅ Verify Client Security (Optional)
163
+
164
+ ```tsx
165
+ import { useSecureSupabase, isSecureClient, warnIfInsecureClient } from '@jmruthers/pace-core/rbac';
166
+
167
+ function MyComponent() {
168
+ const supabase = useSecureSupabase();
169
+
170
+ // Development-only warning
171
+ warnIfInsecureClient(supabase, 'MyComponent');
172
+
173
+ // Runtime check
174
+ if (!isSecureClient(supabase)) {
175
+ throw new Error('Insecure client detected!');
176
+ }
177
+
178
+ // Use supabase...
179
+ }
180
+ ```
181
+
182
+ ## Incorrect Usage (Will Be Detected)
183
+
184
+ ### ❌ Direct `createClient()` Import
185
+
186
+ ```tsx
187
+ // ESLint will report error
188
+ import { createClient } from '@supabase/supabase-js';
189
+ const supabase = createClient(url, key);
190
+ ```
191
+
192
+ ### ❌ Direct `createClient()` Call
193
+
194
+ ```tsx
195
+ // ESLint will report error
196
+ const supabase = createClient(url, key);
197
+ ```
198
+
199
+ ### ❌ Using Base Client for Queries
200
+
201
+ ```tsx
202
+ // Even if imported from config file, using for queries is wrong
203
+ import { supabase } from './lib/supabase'; // Base client
204
+ const { data } = await supabase.from('users').select('*'); // ❌ Bypasses security
205
+ ```
206
+
207
+ ## Configuration Files Exception
208
+
209
+ The ESLint rule allows `createClient()` in configuration files (files matching `supabase*.ts/js` or `*client*.ts/js`). This is intentional because:
210
+
211
+ 1. Base clients are needed for authentication setup
212
+ 2. Base clients should only be used for auth operations
213
+ 3. Base clients should be passed to `useSecureSupabase()` as fallback
214
+
215
+ **Example of acceptable config file**:
216
+ ```tsx
217
+ // supabaseClient.ts - Config file (allowed)
218
+ import { createClient } from '@supabase/supabase-js';
219
+
220
+ export const supabase = createClient(url, key);
221
+
222
+ // Then in components:
223
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
224
+ import { supabase } from './supabaseClient';
225
+
226
+ function MyComponent() {
227
+ // ✅ Correct: Pass base client as fallback
228
+ const secureSupabase = useSecureSupabase(supabase);
229
+ }
230
+ ```
231
+
232
+ ## Audit Script Detection
233
+
234
+ The pace-core audit script comprehensively detects insecure client usage:
235
+
236
+ ```bash
237
+ npm run audit
238
+ ```
239
+
240
+ The audit will report violations including:
241
+ - ✅ Direct `createClient` imports from `@supabase/supabase-js`
242
+ - ✅ Direct `createClient()` function calls
243
+ - ✅ Usage of non-secure clients for database queries (`.from()` calls)
244
+ - ✅ Files that import `createClient` but don't use `useSecureSupabase()`
245
+ - ✅ Variables created with `createClient()` that are used for queries
246
+
247
+ **Example audit output**:
248
+ ```
249
+ ❌ Direct Supabase client usage detected
250
+ File: src/components/UserList.tsx
251
+ Line: 15
252
+ Variable: supabase
253
+ Table: users
254
+ Reason: Direct Supabase client usage detected. Variable 'supabase' is created with createClient() and used for database queries. You MUST use useSecureSupabase() instead to ensure RLS policies and organisation context are enforced.
255
+ Recommendation: Replace with: import { useSecureSupabase } from '@jmruthers/pace-core/rbac'; const supabase = useSecureSupabase();
256
+ ```
257
+
258
+ The audit tool provides the same level of detection as the ESLint rule, making it useful for:
259
+ - Pre-commit checks
260
+ - CI/CD pipelines
261
+ - Code reviews
262
+ - Migration validation
263
+
264
+ ## Best Practices
265
+
266
+ 1. **Always use `useSecureSupabase()`** in React components
267
+ 2. **Use `createSecureClient()`** for server-side or non-React code
268
+ 3. **Never import `createClient`** from `@supabase/supabase-js` in component files
269
+ 4. **Verify client security** in critical code paths (optional but recommended)
270
+ 5. **Run ESLint** regularly to catch violations early
271
+ 6. **Run audit script** before deploying to catch any missed violations
272
+
273
+ ## Troubleshooting
274
+
275
+ ### "ESLint reports error but I need createClient for auth"
276
+
277
+ **Solution**: Create a config file (e.g., `supabaseClient.ts`) and use `useSecureSupabase()` with the base client as fallback:
278
+
279
+ ```tsx
280
+ // supabaseClient.ts
281
+ import { createClient } from '@supabase/supabase-js';
282
+ export const supabase = createClient(url, key);
283
+
284
+ // MyComponent.tsx
285
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
286
+ import { supabase } from './supabaseClient';
287
+
288
+ function MyComponent() {
289
+ const secureSupabase = useSecureSupabase(supabase);
290
+ // Use secureSupabase for all database operations
291
+ }
292
+ ```
293
+
294
+ ### "I'm getting runtime warnings in development"
295
+
296
+ **Solution**: Ensure you're using `useSecureSupabase()` instead of a base client:
297
+
298
+ ```tsx
299
+ // ❌ Wrong
300
+ const supabase = createClient(url, key);
301
+
302
+ // ✅ Correct
303
+ const supabase = useSecureSupabase();
304
+ ```
305
+
306
+ ### "How do I check if my client is secure?"
307
+
308
+ **Solution**: Use `isSecureClient()`:
309
+
310
+ ```tsx
311
+ import { isSecureClient } from '@jmruthers/pace-core/rbac';
312
+
313
+ if (isSecureClient(supabase)) {
314
+ console.log('Client is secure!');
315
+ } else {
316
+ console.error('Client is NOT secure!');
317
+ }
318
+ ```
319
+
320
+ ## Summary
321
+
322
+ The protection system provides:
323
+ - ✅ **ESLint rules** to catch violations at development time
324
+ - ✅ **Runtime warnings** to alert developers in development mode
325
+ - ✅ **Type safety** to verify client security
326
+ - ✅ **Automatic marking** of secure clients
327
+ - ✅ **Audit scripts** to catch violations before deployment
328
+
329
+ By following these guidelines, you ensure that all database operations respect organisation context and RLS policies, preventing security vulnerabilities and data leakage.
330
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -238,10 +238,10 @@
238
238
  "globals": "^16.3.0",
239
239
  "jsdom": "^25.0.1",
240
240
  "react-router-dom": "^6.26.2",
241
- "typedoc": "^0.25.13",
241
+ "tsup": "^8.5.0",
242
+ "typedoc": "^0.26.11",
242
243
  "typedoc-plugin-markdown": "^3.17.1",
243
244
  "typedoc-plugin-merge-modules": "^5.1.0",
244
- "tsup": "^8.5.0",
245
245
  "typescript": "^5.4.0",
246
246
  "typescript-eslint": "^8.39.0",
247
247
  "vite": "^6.0.3"
@@ -2306,6 +2306,78 @@ function scanFile(filePath, manifest, projectRoot) {
2306
2306
  }
2307
2307
  }
2308
2308
  }
2309
+
2310
+ // Check for createClient imports even without immediate query usage
2311
+ // This catches cases where createClient is imported but may be used later
2312
+ if (hasCreateClientImport && !usesSecureSupabase) {
2313
+ const isConfigFile = /config|supabase|client/i.test(relativePath) &&
2314
+ (relativePath.includes('supabase.ts') ||
2315
+ relativePath.includes('supabase.js') ||
2316
+ relativePath.includes('client.ts') ||
2317
+ relativePath.includes('client.js'));
2318
+
2319
+ if (!isConfigFile) {
2320
+ // Find the line number of the import
2321
+ const importMatch = content.match(/import\s+{\s*createClient\s*}\s+from\s+['"]@supabase\/supabase-js['"]/);
2322
+ if (importMatch) {
2323
+ const lineNumber = content.substring(0, importMatch.index).split('\n').length;
2324
+
2325
+ // Check if this violation is already reported
2326
+ const alreadyReported = violations.directSupabaseClient.some(v =>
2327
+ v.file === relativePath &&
2328
+ v.variable === 'createClient' &&
2329
+ Math.abs(v.line - lineNumber) <= 2
2330
+ );
2331
+
2332
+ if (!alreadyReported) {
2333
+ violations.directSupabaseClient.push({
2334
+ file: relativePath,
2335
+ line: lineNumber,
2336
+ variable: 'createClient',
2337
+ table: 'none',
2338
+ reason: 'Direct import of createClient from @supabase/supabase-js detected. You MUST use useSecureSupabase() from @jmruthers/pace-core/rbac instead to ensure organisation context and RLS policies are enforced.',
2339
+ recommendation: 'Remove this import and use: import { useSecureSupabase } from \'@jmruthers/pace-core/rbac\'; const supabase = useSecureSupabase();'
2340
+ });
2341
+ }
2342
+ }
2343
+ }
2344
+ }
2345
+
2346
+ // Check for createClient() calls even when variable isn't used for queries yet
2347
+ // This catches potential security issues early
2348
+ if (hasCreateClientUsage && !usesSecureSupabase) {
2349
+ const isConfigFile = /config|supabase|client/i.test(relativePath) &&
2350
+ (relativePath.includes('supabase.ts') ||
2351
+ relativePath.includes('supabase.js') ||
2352
+ relativePath.includes('client.ts') ||
2353
+ relativePath.includes('client.js'));
2354
+
2355
+ if (!isConfigFile) {
2356
+ // Find all createClient() calls
2357
+ const createClientCallPattern = /createClient\s*\(/g;
2358
+ let callMatch;
2359
+ while ((callMatch = createClientCallPattern.exec(content)) !== null) {
2360
+ const lineNumber = content.substring(0, callMatch.index).split('\n').length;
2361
+
2362
+ // Check if this violation is already reported
2363
+ const alreadyReported = violations.directSupabaseClient.some(v =>
2364
+ v.file === relativePath &&
2365
+ Math.abs(v.line - lineNumber) <= 2
2366
+ );
2367
+
2368
+ if (!alreadyReported) {
2369
+ violations.directSupabaseClient.push({
2370
+ file: relativePath,
2371
+ line: lineNumber,
2372
+ variable: 'unknown',
2373
+ table: 'none',
2374
+ reason: 'Direct createClient() call detected. You MUST use useSecureSupabase() from @jmruthers/pace-core/rbac instead to ensure organisation context and RLS policies are enforced.',
2375
+ recommendation: 'Replace with: import { useSecureSupabase } from \'@jmruthers/pace-core/rbac\'; const supabase = useSecureSupabase();'
2376
+ });
2377
+ }
2378
+ }
2379
+ }
2380
+ }
2309
2381
  }
2310
2382
 
2311
2383
  // ============================================