@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.
- package/CHANGELOG.md +45 -0
- package/cursor-rules/00-pace-core-compliance.mdc +34 -2
- package/dist/{AuthService-BPvc3Ka0.d.ts → AuthService-Cb34EQs3.d.ts} +9 -1
- package/dist/{DataTable-TPTKCX4D.js → DataTable-THFPBKTP.js} +9 -8
- package/dist/{PublicPageProvider-DC6kCaqf.d.ts → PublicPageProvider-DEMpysFR.d.ts} +45 -67
- package/dist/{UnifiedAuthProvider-CVcTjx-d.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +1 -8
- package/dist/{UnifiedAuthProvider-CH6Z342H.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
- package/dist/{api-MVVQZLJI.js → api-IAGWF3ZG.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/{chunk-SFZUDBL5.js → chunk-2T2IG7T7.js} +70 -56
- package/dist/chunk-2T2IG7T7.js.map +1 -0
- package/dist/{chunk-MMZ7JXPU.js → chunk-6Z7LTB3D.js} +13 -21
- package/dist/{chunk-MMZ7JXPU.js.map → chunk-6Z7LTB3D.js.map} +1 -1
- package/dist/{chunk-6J4GEEJR.js → chunk-CNCQDFLN.js} +53 -27
- package/dist/chunk-CNCQDFLN.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-EHMR7VYL.js → chunk-DWUBLJJM.js} +361 -187
- package/dist/chunk-DWUBLJJM.js.map +1 -0
- package/dist/{chunk-2UOI2FG5.js → chunk-HFZBI76P.js} +4 -4
- package/dist/{chunk-F2IMUDXZ.js → chunk-M7MPQISP.js} +2 -2
- package/dist/{chunk-3XC4CPTD.js → chunk-PQBSKX33.js} +244 -5727
- package/dist/chunk-PQBSKX33.js.map +1 -0
- package/dist/chunk-QRPVRXYT.js +226 -0
- package/dist/chunk-QRPVRXYT.js.map +1 -0
- package/dist/{chunk-24UVZUZG.js → chunk-RWEBCB47.js} +129 -387
- package/dist/chunk-RWEBCB47.js.map +1 -0
- package/dist/{chunk-XWQCNGTQ.js → chunk-YDQHOZNA.js} +173 -79
- package/dist/chunk-YDQHOZNA.js.map +1 -0
- package/dist/{chunk-NECFR5MM.js → chunk-ZNIWI3UC.js} +562 -644
- package/dist/chunk-ZNIWI3UC.js.map +1 -0
- package/dist/components.d.ts +2 -2
- package/dist/components.js +12 -13
- package/dist/contextValidator-3JNZKUTX.js +9 -0
- package/dist/contextValidator-3JNZKUTX.js.map +1 -0
- package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +7 -6
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +21 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +4 -3
- package/dist/rbac/index.d.ts +67 -27
- package/dist/rbac/index.js +15 -8
- package/dist/styles/index.js +1 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-1oMokgLF.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +7 -16
- package/dist/utils.js +5 -7
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +14 -16
- package/docs/api/modules.md +3796 -2513
- package/docs/components/context-selector.md +126 -0
- package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
- package/docs/pace-mint-fix-auto-selection.md +218 -0
- package/docs/pace-mint-rbac-setup.md +391 -0
- package/docs/rbac/secure-client-protection.md +330 -0
- package/package.json +3 -3
- package/scripts/audit/core/checks/compliance.cjs +72 -0
- package/scripts/audit/core/checks/dependencies.cjs +559 -28
- package/scripts/audit/core/checks/documentation.cjs +68 -3
- package/scripts/audit/core/checks/environment.cjs +2 -14
- package/scripts/audit/core/checks/error-handling.cjs +47 -6
- package/src/components/ContextSelector/ContextSelector.tsx +384 -0
- package/src/components/ContextSelector/index.ts +3 -0
- package/src/components/DataTable/components/RowComponent.tsx +19 -19
- package/src/components/DataTable/components/UnifiedTableBody.tsx +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +8 -6
- package/src/components/Dialog/Dialog.tsx +29 -1
- package/src/components/FileDisplay/FileDisplay.tsx +42 -10
- package/src/components/Header/Header.test.tsx +43 -73
- package/src/components/Header/Header.tsx +44 -45
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +9 -9
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +135 -33
- package/src/components/PaceAppLayout/README.md +14 -17
- package/src/components/PaceAppLayout/test-setup.tsx +2 -2
- package/src/components/index.ts +5 -5
- package/src/eslint-rules/pace-core-compliance.cjs +106 -0
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
- package/src/hooks/useAppConfig.ts +15 -30
- package/src/hooks/useFileDisplay.ts +77 -50
- package/src/index.ts +4 -5
- package/src/providers/services/AuthServiceProvider.tsx +17 -7
- package/src/providers/services/EventServiceProvider.tsx +33 -5
- package/src/providers/services/UnifiedAuthProvider.tsx +90 -134
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
- package/src/rbac/adapters.tsx +2 -2
- package/src/rbac/api.test.ts +59 -51
- package/src/rbac/api.ts +178 -132
- package/src/rbac/components/PagePermissionGuard.tsx +38 -10
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
- package/src/rbac/hooks/permissions/useAccessLevel.ts +1 -1
- package/src/rbac/hooks/permissions/useCan.ts +41 -11
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +1 -1
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +1 -1
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +1 -1
- package/src/rbac/hooks/useCan.test.ts +0 -9
- package/src/rbac/hooks/useRBAC.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.ts +36 -37
- package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
- package/src/rbac/hooks/useResolvedScope.ts +35 -40
- package/src/rbac/hooks/useSecureSupabase.ts +7 -7
- package/src/rbac/index.ts +7 -0
- package/src/rbac/secureClient.test.ts +22 -18
- package/src/rbac/secureClient.ts +103 -16
- package/src/rbac/security.ts +0 -17
- package/src/rbac/types.ts +1 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
- package/src/rbac/utils/clientSecurity.ts +93 -0
- package/src/rbac/utils/contextValidator.ts +77 -168
- package/src/services/AuthService.ts +39 -7
- package/src/services/EventService.ts +186 -54
- package/src/services/OrganisationService.ts +81 -14
- package/src/services/__tests__/EventService.test.ts +1 -2
- package/src/services/base/BaseService.ts +3 -0
- package/src/utils/dynamic/dynamicUtils.ts +7 -4
- package/dist/chunk-24UVZUZG.js.map +0 -1
- package/dist/chunk-3XC4CPTD.js.map +0 -1
- package/dist/chunk-6J4GEEJR.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-EHMR7VYL.js.map +0 -1
- package/dist/chunk-NECFR5MM.js.map +0 -1
- package/dist/chunk-SFZUDBL5.js.map +0 -1
- package/dist/chunk-XWQCNGTQ.js.map +0 -1
- package/docs/api/classes/ColumnFactory.md +0 -243
- package/docs/api/classes/InvalidScopeError.md +0 -73
- package/docs/api/classes/Logger.md +0 -178
- package/docs/api/classes/MissingUserContextError.md +0 -66
- package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
- package/docs/api/classes/PermissionDeniedError.md +0 -73
- package/docs/api/classes/RBACAuditManager.md +0 -297
- package/docs/api/classes/RBACCache.md +0 -322
- package/docs/api/classes/RBACEngine.md +0 -171
- package/docs/api/classes/RBACError.md +0 -76
- package/docs/api/classes/RBACNotInitializedError.md +0 -66
- package/docs/api/classes/SecureSupabaseClient.md +0 -163
- package/docs/api/classes/StorageUtils.md +0 -328
- package/docs/api/enums/FileCategory.md +0 -184
- package/docs/api/enums/LogLevel.md +0 -54
- package/docs/api/enums/RBACErrorCode.md +0 -228
- package/docs/api/enums/RPCFunction.md +0 -118
- package/docs/api/interfaces/AddressFieldProps.md +0 -241
- package/docs/api/interfaces/AddressFieldRef.md +0 -94
- package/docs/api/interfaces/AggregateConfig.md +0 -43
- package/docs/api/interfaces/AutocompleteOptions.md +0 -75
- package/docs/api/interfaces/AvatarProps.md +0 -128
- package/docs/api/interfaces/BadgeProps.md +0 -34
- package/docs/api/interfaces/ButtonProps.md +0 -56
- package/docs/api/interfaces/CalendarProps.md +0 -73
- package/docs/api/interfaces/CardProps.md +0 -69
- package/docs/api/interfaces/ColorPalette.md +0 -7
- package/docs/api/interfaces/ColorShade.md +0 -66
- package/docs/api/interfaces/ComplianceResult.md +0 -30
- package/docs/api/interfaces/DataAccessRecord.md +0 -96
- package/docs/api/interfaces/DataRecord.md +0 -11
- package/docs/api/interfaces/DataTableAction.md +0 -252
- package/docs/api/interfaces/DataTableColumn.md +0 -504
- package/docs/api/interfaces/DataTableProps.md +0 -625
- package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
- package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
- package/docs/api/interfaces/DatabaseIssue.md +0 -41
- package/docs/api/interfaces/EmptyStateConfig.md +0 -61
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
- package/docs/api/interfaces/ErrorBoundaryProps.md +0 -147
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +0 -36
- package/docs/api/interfaces/ErrorBoundaryState.md +0 -75
- package/docs/api/interfaces/EventAppRoleData.md +0 -71
- package/docs/api/interfaces/ExportColumn.md +0 -90
- package/docs/api/interfaces/ExportOptions.md +0 -126
- package/docs/api/interfaces/FileDisplayProps.md +0 -249
- package/docs/api/interfaces/FileMetadata.md +0 -129
- package/docs/api/interfaces/FileReference.md +0 -118
- package/docs/api/interfaces/FileSizeLimits.md +0 -7
- package/docs/api/interfaces/FileUploadOptions.md +0 -139
- package/docs/api/interfaces/FileUploadProps.md +0 -296
- package/docs/api/interfaces/FooterProps.md +0 -107
- package/docs/api/interfaces/FormFieldProps.md +0 -166
- package/docs/api/interfaces/FormProps.md +0 -113
- package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
- package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
- package/docs/api/interfaces/InputProps.md +0 -56
- package/docs/api/interfaces/LabelProps.md +0 -107
- package/docs/api/interfaces/LoggerConfig.md +0 -62
- package/docs/api/interfaces/LoginFormProps.md +0 -187
- package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
- package/docs/api/interfaces/NavigationContextType.md +0 -164
- package/docs/api/interfaces/NavigationGuardProps.md +0 -139
- package/docs/api/interfaces/NavigationItem.md +0 -120
- package/docs/api/interfaces/NavigationMenuProps.md +0 -221
- package/docs/api/interfaces/NavigationProviderProps.md +0 -117
- package/docs/api/interfaces/Organisation.md +0 -140
- package/docs/api/interfaces/OrganisationContextType.md +0 -388
- package/docs/api/interfaces/OrganisationMembership.md +0 -140
- package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
- package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
- package/docs/api/interfaces/PaceAppLayoutProps.md +0 -409
- package/docs/api/interfaces/PaceLoginPageProps.md +0 -49
- package/docs/api/interfaces/PageAccessRecord.md +0 -85
- package/docs/api/interfaces/PagePermissionContextType.md +0 -140
- package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
- package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
- package/docs/api/interfaces/PaletteData.md +0 -41
- package/docs/api/interfaces/ParsedAddress.md +0 -120
- package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
- package/docs/api/interfaces/ProgressProps.md +0 -42
- package/docs/api/interfaces/ProtectedRouteProps.md +0 -78
- package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
- package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
- package/docs/api/interfaces/PublicPageLayoutProps.md +0 -185
- package/docs/api/interfaces/QuickFix.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
- package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
- package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
- package/docs/api/interfaces/RBACConfig.md +0 -133
- package/docs/api/interfaces/RBACContext.md +0 -52
- package/docs/api/interfaces/RBACLogger.md +0 -112
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
- package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
- package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
- package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
- package/docs/api/interfaces/RBACResult.md +0 -58
- package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
- package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
- package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
- package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
- package/docs/api/interfaces/RBACRolesListParams.md +0 -52
- package/docs/api/interfaces/RBACRolesListResult.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
- package/docs/api/interfaces/ResourcePermissions.md +0 -155
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
- package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
- package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
- package/docs/api/interfaces/RoleManagementResult.md +0 -52
- package/docs/api/interfaces/RouteAccessRecord.md +0 -107
- package/docs/api/interfaces/RouteConfig.md +0 -134
- package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
- package/docs/api/interfaces/SecureDataContextType.md +0 -168
- package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
- package/docs/api/interfaces/SetupIssue.md +0 -41
- package/docs/api/interfaces/StorageConfig.md +0 -41
- package/docs/api/interfaces/StorageFileInfo.md +0 -74
- package/docs/api/interfaces/StorageFileMetadata.md +0 -151
- package/docs/api/interfaces/StorageListOptions.md +0 -99
- package/docs/api/interfaces/StorageListResult.md +0 -41
- package/docs/api/interfaces/StorageUploadOptions.md +0 -101
- package/docs/api/interfaces/StorageUploadResult.md +0 -63
- package/docs/api/interfaces/StorageUrlOptions.md +0 -60
- package/docs/api/interfaces/StyleImport.md +0 -19
- package/docs/api/interfaces/SwitchProps.md +0 -34
- package/docs/api/interfaces/TabsContentProps.md +0 -9
- package/docs/api/interfaces/TabsListProps.md +0 -9
- package/docs/api/interfaces/TabsProps.md +0 -9
- package/docs/api/interfaces/TabsTriggerProps.md +0 -50
- package/docs/api/interfaces/TextareaProps.md +0 -53
- package/docs/api/interfaces/ToastActionElement.md +0 -12
- package/docs/api/interfaces/ToastProps.md +0 -9
- package/docs/api/interfaces/UnifiedAuthContextType.md +0 -823
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -173
- package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
- package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -138
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -84
- package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
- package/docs/api/interfaces/UsePublicEventReturn.md +0 -71
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -123
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -97
- package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
- package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
- package/docs/api/interfaces/UserEventAccess.md +0 -121
- package/docs/api/interfaces/UserMenuProps.md +0 -88
- package/docs/api/interfaces/UserProfile.md +0 -63
- package/src/components/EventSelector/EventSelector.test.tsx +0 -720
- package/src/components/EventSelector/EventSelector.tsx +0 -423
- package/src/components/EventSelector/index.ts +0 -3
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -327
- package/src/components/OrganisationSelector/index.ts +0 -9
- /package/dist/{DataTable-TPTKCX4D.js.map → DataTable-THFPBKTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CH6Z342H.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
- /package/dist/{api-MVVQZLJI.js.map → api-IAGWF3ZG.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{chunk-2UOI2FG5.js.map → chunk-HFZBI76P.js.map} +0 -0
- /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.
|
|
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
|
-
"
|
|
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
|
// ============================================
|