@jmruthers/pace-core 0.5.189 → 0.5.191
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/core-usage-manifest.json +0 -4
- package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
- package/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
- package/dist/{DataTable-GUFUNZ3N.js → DataTable-WKRZD47S.js} +8 -8
- package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +84 -25
- package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
- package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-FTSG5XH7.js} +4 -2
- package/dist/{api-YP7XD5L6.js → api-IHKALJZD.js} +4 -2
- package/dist/{chunk-VGZZXKBR.js → chunk-6LTQQAT6.js} +351 -157
- package/dist/chunk-6LTQQAT6.js.map +1 -0
- package/dist/{chunk-MX64ZF6I.js → chunk-6TQDD426.js} +15 -15
- package/dist/chunk-6TQDD426.js.map +1 -0
- package/dist/{chunk-YHCN776L.js → chunk-G37KK66H.js} +2 -75
- package/dist/chunk-G37KK66H.js.map +1 -0
- package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
- package/dist/chunk-HW3OVDUF.js.map +1 -0
- package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
- package/dist/chunk-I7PSE6JW.js.map +1 -0
- package/dist/{chunk-IM4QE42D.js → chunk-LOMZXPSN.js} +141 -326
- package/dist/chunk-LOMZXPSN.js.map +1 -0
- package/dist/chunk-OETXORNB.js +614 -0
- package/dist/chunk-OETXORNB.js.map +1 -0
- package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
- package/dist/{chunk-HEHYGYOX.js → chunk-ROXMHMY2.js} +403 -46
- package/dist/chunk-ROXMHMY2.js.map +1 -0
- package/dist/{chunk-2UUZZJFT.js → chunk-ULHIJK66.js} +228 -177
- package/dist/{chunk-2UUZZJFT.js.map → chunk-ULHIJK66.js.map} +1 -1
- package/dist/{chunk-YGPFYGA6.js → chunk-VKB2CO4Z.js} +838 -503
- package/dist/chunk-VKB2CO4Z.js.map +1 -0
- package/dist/{chunk-3GOZZZYH.js → chunk-VRGWKHDB.js} +238 -301
- package/dist/chunk-VRGWKHDB.js.map +1 -0
- package/dist/{chunk-UCQSRW7Z.js → chunk-XNYQOL3Z.js} +431 -384
- package/dist/chunk-XNYQOL3Z.js.map +1 -0
- package/dist/{chunk-DDM4CCYT.js → chunk-XYXSXPUK.js} +79 -59
- package/dist/chunk-XYXSXPUK.js.map +1 -0
- package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
- package/dist/chunk-ZSAAAMVR.js.map +1 -0
- package/dist/components.d.ts +5 -6
- package/dist/components.js +19 -19
- package/dist/components.js.map +1 -1
- package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
- package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
- package/dist/hooks.d.ts +20 -15
- package/dist/hooks.js +14 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +17 -15
- package/dist/index.js +86 -81
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +3 -1
- package/dist/rbac/index.d.ts +77 -13
- package/dist/rbac/index.js +12 -9
- package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +17 -10
- package/dist/utils.d.ts +8 -8
- package/dist/utils.js +16 -16
- package/docs/README.md +2 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +2 -2
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +2 -2
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +5 -5
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +2 -2
- package/docs/api/classes/SecureSupabaseClient.md +25 -20
- package/docs/api/classes/StorageUtils.md +7 -4
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +20 -6
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +62 -16
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +2 -2
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +26 -12
- package/docs/api/interfaces/FileUploadProps.md +30 -19
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +7 -7
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +2 -2
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +3 -11
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +10 -10
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +4 -4
- package/docs/api/interfaces/StorageFileInfo.md +7 -7
- package/docs/api/interfaces/StorageFileMetadata.md +25 -14
- package/docs/api/interfaces/StorageListOptions.md +22 -9
- package/docs/api/interfaces/StorageListResult.md +4 -4
- package/docs/api/interfaces/StorageUploadOptions.md +21 -8
- package/docs/api/interfaces/StorageUploadResult.md +6 -6
- package/docs/api/interfaces/StorageUrlOptions.md +19 -6
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +5 -5
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +165 -106
- package/docs/api-reference/components.md +15 -7
- package/docs/api-reference/providers.md +2 -2
- package/docs/api-reference/rpc-functions.md +1 -0
- package/docs/best-practices/README.md +1 -1
- package/docs/best-practices/deployment.md +8 -8
- package/docs/getting-started/examples/README.md +2 -2
- package/docs/getting-started/installation-guide.md +4 -4
- package/docs/getting-started/quick-start.md +3 -3
- package/docs/migration/MIGRATION_GUIDE.md +3 -3
- package/docs/migration/README.md +18 -0
- package/docs/migration/database-changes-december-2025.md +767 -0
- package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
- package/docs/rbac/compliance/compliance-guide.md +2 -2
- package/docs/rbac/event-based-apps.md +2 -2
- package/docs/rbac/getting-started.md +2 -2
- package/docs/rbac/quick-start.md +2 -2
- package/docs/security/README.md +4 -4
- package/docs/standards/07-rbac-and-rls-standard.md +430 -7
- package/docs/troubleshooting/README.md +2 -2
- package/docs/troubleshooting/migration.md +3 -3
- package/package.json +1 -3
- package/scripts/check-pace-core-compliance.cjs +1 -1
- package/scripts/check-pace-core-compliance.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +301 -0
- package/src/__tests__/public-recipe-view.test.ts +19 -19
- package/src/__tests__/rls-policies.test.ts +210 -74
- package/src/components/AddressField/AddressField.test.tsx +42 -0
- package/src/components/AddressField/AddressField.tsx +71 -60
- package/src/components/AddressField/README.md +7 -6
- package/src/components/Alert/Alert.test.tsx +50 -10
- package/src/components/Alert/Alert.tsx +5 -3
- package/src/components/Avatar/Avatar.test.tsx +95 -43
- package/src/components/Avatar/Avatar.tsx +16 -16
- package/src/components/Button/Button.test.tsx +2 -1
- package/src/components/Button/Button.tsx +3 -3
- package/src/components/Calendar/Calendar.test.tsx +53 -37
- package/src/components/Calendar/Calendar.tsx +409 -82
- package/src/components/Card/Card.test.tsx +7 -4
- package/src/components/Card/Card.tsx +3 -6
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +5 -5
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
- package/src/components/DataTable/components/DataTableBody.tsx +12 -12
- package/src/components/DataTable/components/DataTableCore.tsx +3 -3
- package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
- package/src/components/DataTable/components/EditableRow.tsx +2 -2
- package/src/components/DataTable/components/EmptyState.tsx +3 -3
- package/src/components/DataTable/components/GroupHeader.tsx +2 -2
- package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
- package/src/components/DataTable/components/ImportModal.tsx +4 -4
- package/src/components/DataTable/components/LoadingState.tsx +1 -1
- package/src/components/DataTable/components/PaginationControls.tsx +11 -11
- package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
- package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
- package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/EventSelector/EventSelector.tsx +7 -7
- package/src/components/FileDisplay/FileDisplay.tsx +291 -179
- package/src/components/FileUpload/FileUpload.tsx +7 -4
- package/src/components/Header/Header.test.tsx +28 -0
- package/src/components/Header/Header.tsx +22 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +42 -22
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
- package/src/components/PaceAppLayout/test-setup.tsx +1 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
- package/src/components/Progress/Progress.test.tsx +18 -19
- package/src/components/Progress/Progress.tsx +31 -32
- package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
- package/src/components/Select/Select.test.tsx +4 -1
- package/src/components/Select/Select.tsx +65 -20
- package/src/components/Switch/Switch.test.tsx +2 -1
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.test.tsx +8 -2
- package/src/components/UserMenu/UserMenu.tsx +3 -3
- package/src/eslint-rules/pace-core-compliance.cjs +0 -2
- package/src/eslint-rules/pace-core-compliance.js +0 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
- package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
- package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
- package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +1 -1
- package/src/hooks/__tests__/usePublicEvent.test.ts +608 -0
- package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +67 -24
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEvent.ts +10 -10
- package/src/hooks/public/usePublicFileDisplay.ts +173 -87
- package/src/hooks/useAppConfig.ts +24 -5
- package/src/hooks/useFileDisplay.ts +298 -36
- package/src/hooks/useFileReference.ts +56 -11
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useInactivityTracker.ts +16 -7
- package/src/hooks/usePermissionCache.test.ts +85 -8
- package/src/hooks/useQueryCache.ts +27 -6
- package/src/hooks/useSecureDataAccess.test.ts +87 -42
- package/src/hooks/useSecureDataAccess.ts +95 -48
- package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
- package/src/providers/services/EventServiceProvider.tsx +37 -17
- package/src/providers/services/InactivityServiceProvider.tsx +4 -4
- package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
- package/src/rbac/api.ts +240 -36
- package/src/rbac/cache-invalidation.ts +21 -7
- package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
- package/src/rbac/components/NavigationGuard.tsx +23 -63
- package/src/rbac/components/NavigationProvider.test.tsx +52 -23
- package/src/rbac/components/NavigationProvider.tsx +13 -11
- package/src/rbac/components/PagePermissionGuard.tsx +77 -203
- package/src/rbac/components/PagePermissionProvider.tsx +13 -11
- package/src/rbac/components/PermissionEnforcer.tsx +24 -62
- package/src/rbac/components/RoleBasedRouter.tsx +14 -12
- package/src/rbac/components/SecureDataProvider.tsx +13 -11
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
- package/src/rbac/engine.ts +4 -2
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
- package/src/rbac/hooks/index.ts +3 -0
- package/src/rbac/hooks/useCan.test.ts +101 -53
- package/src/rbac/hooks/usePermissions.ts +108 -41
- package/src/rbac/hooks/useRBAC.test.ts +11 -3
- package/src/rbac/hooks/useRBAC.ts +83 -40
- package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
- package/src/rbac/hooks/useResolvedScope.ts +128 -70
- package/src/rbac/hooks/useSecureSupabase.ts +36 -19
- package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
- package/src/rbac/request-deduplication.ts +1 -1
- package/src/rbac/secureClient.ts +72 -12
- package/src/rbac/security.ts +29 -23
- package/src/rbac/types.ts +10 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
- package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +8 -3
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +74 -12
- package/src/rbac/utils/contextValidator.ts +288 -0
- package/src/rbac/utils/eventContext.ts +52 -3
- package/src/services/AuthService.ts +37 -8
- package/src/services/EventService.ts +165 -21
- package/src/services/OrganisationService.ts +125 -137
- package/src/services/__tests__/EventService.test.ts +26 -21
- package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
- package/src/services/__tests__/OrganisationService.test.ts +218 -86
- package/src/types/database.generated.ts +166 -201
- package/src/types/file-reference.ts +13 -10
- package/src/types/supabase.ts +2 -2
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
- package/src/utils/app/appNameResolver.test.ts +346 -73
- package/src/utils/context/superAdminOverride.ts +58 -0
- package/src/utils/file-reference/index.ts +65 -37
- package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
- package/src/utils/google-places/googlePlacesUtils.ts +1 -1
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
- package/src/utils/google-places/types.ts +1 -1
- package/src/utils/request-deduplication.ts +4 -4
- package/src/utils/security/secureDataAccess.test.ts +1 -1
- package/src/utils/security/secureDataAccess.ts +7 -4
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.test.ts +1 -1
- package/src/utils/storage/helpers.ts +38 -19
- package/src/utils/storage/types.ts +15 -8
- package/src/utils/validation/__tests__/csrf.test.ts +105 -0
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
- package/src/vite-env.d.ts +2 -2
- package/dist/chunk-3GOZZZYH.js.map +0 -1
- package/dist/chunk-DDM4CCYT.js.map +0 -1
- package/dist/chunk-E7UAOUMY.js +0 -75
- package/dist/chunk-E7UAOUMY.js.map +0 -1
- package/dist/chunk-F2IMUDXZ.js.map +0 -1
- package/dist/chunk-HEHYGYOX.js.map +0 -1
- package/dist/chunk-IM4QE42D.js.map +0 -1
- package/dist/chunk-MX64ZF6I.js.map +0 -1
- package/dist/chunk-SAUPYVLF.js.map +0 -1
- package/dist/chunk-THRPYOFK.js.map +0 -1
- package/dist/chunk-UCQSRW7Z.js.map +0 -1
- package/dist/chunk-VGZZXKBR.js.map +0 -1
- package/dist/chunk-YGPFYGA6.js.map +0 -1
- package/dist/chunk-YHCN776L.js.map +0 -1
- /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-WKRZD47S.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
- /package/dist/{api-YP7XD5L6.js.map → api-IHKALJZD.js.map} +0 -0
- /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
|
@@ -18,33 +18,37 @@ import { createLogger } from '../core/logger';
|
|
|
18
18
|
const log = createLogger('StorageHelpers');
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Generate a file path based on organization-first structure
|
|
21
|
+
* Generate a file path based on organization-first or user-first structure
|
|
22
|
+
* - If orgId is provided: {orgId}/{folder}/filename
|
|
23
|
+
* - If userId is provided (and orgId is not): users/{userId}/{folder}/filename
|
|
22
24
|
*/
|
|
23
25
|
export function generateFilePath(options: StorageUploadOptions, fileName: string): string {
|
|
24
|
-
const { orgId, isPublic = false, customPath } = options;
|
|
26
|
+
const { orgId, userId, isPublic = false, customPath } = options;
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
throw new Error('orgId is required for file path generation');
|
|
28
|
+
// Validate that either orgId or userId is provided
|
|
29
|
+
if (!orgId && !userId) {
|
|
30
|
+
throw new Error('Either orgId or userId is required for file path generation');
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
// Determine base path: organisation-based or user-based
|
|
34
|
+
const basePath = orgId ? orgId : `users/${userId}`;
|
|
35
|
+
|
|
32
36
|
if (isPublic) {
|
|
33
|
-
// Public files go to {
|
|
37
|
+
// Public files go to {basePath}/{folder}/filename
|
|
34
38
|
if (customPath) {
|
|
35
|
-
return `${
|
|
39
|
+
return `${basePath}/${customPath}/${fileName}`;
|
|
36
40
|
}
|
|
37
|
-
return `${
|
|
41
|
+
return `${basePath}/public/${fileName}`;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
// Organization-first structure: {
|
|
44
|
+
// Organization-first or user-first structure: {basePath}/{folder}/filename
|
|
41
45
|
if (customPath) {
|
|
42
|
-
return `${
|
|
46
|
+
return `${basePath}/${customPath}/${fileName}`;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
// Use customPath if available, otherwise default to files
|
|
46
50
|
const pathFolder = customPath || 'files';
|
|
47
|
-
return `${
|
|
51
|
+
return `${basePath}/${pathFolder}/${fileName}`;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
/**
|
|
@@ -86,7 +90,8 @@ export async function extractFileMetadata(
|
|
|
86
90
|
const metadata: StorageFileMetadata = {
|
|
87
91
|
mimeType: file.type,
|
|
88
92
|
size: file.size,
|
|
89
|
-
orgId: options.orgId,
|
|
93
|
+
...(options.orgId && { orgId: options.orgId }),
|
|
94
|
+
...(options.userId && { userId: options.userId }),
|
|
90
95
|
appName: options.appName || 'pace-core',
|
|
91
96
|
uploadedBy,
|
|
92
97
|
uploadedAt: new Date().toISOString(),
|
|
@@ -608,8 +613,14 @@ export async function listFiles(
|
|
|
608
613
|
// Select bucket based on isPublic flag (default to private files bucket)
|
|
609
614
|
const bucketName = getBucketName(options.isPublic || false);
|
|
610
615
|
|
|
611
|
-
//
|
|
612
|
-
|
|
616
|
+
// Validate that either orgId or userId is provided
|
|
617
|
+
if (!options.orgId && !options.userId) {
|
|
618
|
+
throw new Error('Either orgId or userId is required for listing files');
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Organization-first or user-first structure
|
|
622
|
+
const basePath = options.orgId ? options.orgId : `users/${options.userId}`;
|
|
623
|
+
const pathPrefix = `${basePath}/`;
|
|
613
624
|
const searchPath = options.pathPrefix ? `${pathPrefix}${options.pathPrefix}` : pathPrefix;
|
|
614
625
|
|
|
615
626
|
const { data, error } = await supabase.storage
|
|
@@ -634,7 +645,8 @@ export async function listFiles(
|
|
|
634
645
|
metadata: {
|
|
635
646
|
mimeType: item.metadata?.mimetype || 'application/octet-stream',
|
|
636
647
|
size: item.metadata?.size || 0,
|
|
637
|
-
orgId: options.orgId,
|
|
648
|
+
...(options.orgId && { orgId: options.orgId }),
|
|
649
|
+
...(options.userId && { userId: options.userId }),
|
|
638
650
|
appName: options.appName,
|
|
639
651
|
uploadedBy: 'unknown',
|
|
640
652
|
uploadedAt: item.created_at || new Date().toISOString(),
|
|
@@ -716,13 +728,20 @@ export async function downloadFile(
|
|
|
716
728
|
export async function archiveFile(
|
|
717
729
|
supabase: SupabaseClient,
|
|
718
730
|
path: string,
|
|
719
|
-
options: { appName: string; orgId
|
|
731
|
+
options: { appName: string; orgId?: string; userId?: string; isPublic?: boolean }
|
|
720
732
|
): Promise<{ success: boolean; error?: string }> {
|
|
721
733
|
try {
|
|
722
734
|
const bucketName = getBucketName(options.isPublic || false);
|
|
723
735
|
|
|
724
|
-
// Generate archived path for organization-first structure
|
|
725
|
-
|
|
736
|
+
// Generate archived path for organization-first or user-first structure
|
|
737
|
+
let archivedPath: string;
|
|
738
|
+
if (options.orgId) {
|
|
739
|
+
archivedPath = path.replace(`${options.orgId}/`, `archived/${options.orgId}/`);
|
|
740
|
+
} else if (options.userId) {
|
|
741
|
+
archivedPath = path.replace(`users/${options.userId}/`, `archived/users/${options.userId}/`);
|
|
742
|
+
} else {
|
|
743
|
+
throw new Error('Either orgId or userId is required for archiving files');
|
|
744
|
+
}
|
|
726
745
|
|
|
727
746
|
// Copy file to archived location
|
|
728
747
|
const { error: copyError } = await supabase.storage
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
export interface StorageUploadOptions {
|
|
6
6
|
/** The app name from rbac_apps */
|
|
7
7
|
appName: string;
|
|
8
|
-
/** Organisation ID for scoping */
|
|
9
|
-
orgId
|
|
8
|
+
/** Organisation ID for scoping (required if userId not provided) */
|
|
9
|
+
orgId?: string;
|
|
10
|
+
/** User ID for user-scoped files (required if orgId not provided) */
|
|
11
|
+
userId?: string;
|
|
10
12
|
/** Whether the file should be publicly accessible */
|
|
11
13
|
isPublic?: boolean;
|
|
12
14
|
/** Optional tags for categorisation */
|
|
13
15
|
tags?: string[];
|
|
14
|
-
/** Optional custom path within the app/org structure */
|
|
16
|
+
/** Optional custom path within the app/org/user structure */
|
|
15
17
|
customPath?: string;
|
|
16
18
|
/** Optional metadata to store with the file */
|
|
17
19
|
metadata?: Record<string, any>;
|
|
@@ -23,7 +25,8 @@ export interface StorageFileMetadata {
|
|
|
23
25
|
width?: number;
|
|
24
26
|
height?: number;
|
|
25
27
|
hash?: string;
|
|
26
|
-
orgId
|
|
28
|
+
orgId?: string;
|
|
29
|
+
userId?: string;
|
|
27
30
|
appName: string;
|
|
28
31
|
uploadedBy: string;
|
|
29
32
|
uploadedAt: string;
|
|
@@ -43,8 +46,10 @@ export interface StorageUploadResult {
|
|
|
43
46
|
export interface StorageUrlOptions {
|
|
44
47
|
/** The app name from rbac_apps */
|
|
45
48
|
appName: string;
|
|
46
|
-
/** Organisation ID for scoping */
|
|
47
|
-
orgId
|
|
49
|
+
/** Organisation ID for scoping (required if userId not provided) */
|
|
50
|
+
orgId?: string;
|
|
51
|
+
/** User ID for user-scoped files (required if orgId not provided) */
|
|
52
|
+
userId?: string;
|
|
48
53
|
/** Expiry time in seconds for signed URLs (default: 3600) */
|
|
49
54
|
expiresIn?: number;
|
|
50
55
|
}
|
|
@@ -52,8 +57,10 @@ export interface StorageUrlOptions {
|
|
|
52
57
|
export interface StorageListOptions {
|
|
53
58
|
/** The app name from rbac_apps */
|
|
54
59
|
appName: string;
|
|
55
|
-
/** Organisation ID for scoping */
|
|
56
|
-
orgId
|
|
60
|
+
/** Organisation ID for scoping (required if userId not provided) */
|
|
61
|
+
orgId?: string;
|
|
62
|
+
/** User ID for user-scoped files (required if orgId not provided) */
|
|
63
|
+
userId?: string;
|
|
57
64
|
/** Optional path prefix to filter by */
|
|
58
65
|
pathPrefix?: string;
|
|
59
66
|
/** Optional tags to filter by */
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
csrfManager,
|
|
4
|
+
generateCSRFToken,
|
|
5
|
+
validateCSRFToken,
|
|
6
|
+
getCSRFToken,
|
|
7
|
+
} from '../csrf';
|
|
8
|
+
import { secureStorage } from '../../security/secureStorage';
|
|
9
|
+
|
|
10
|
+
const getTokenCache = () => (csrfManager as unknown as { tokenCache: Map<string, unknown> }).tokenCache;
|
|
11
|
+
|
|
12
|
+
describe('csrf protection', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.restoreAllMocks();
|
|
15
|
+
vi.useRealTimers();
|
|
16
|
+
localStorage.clear();
|
|
17
|
+
const cache = getTokenCache();
|
|
18
|
+
cache?.clear();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('generates secure tokens and persists them', async () => {
|
|
22
|
+
const setItemSpy = vi.spyOn(secureStorage, 'setItem').mockResolvedValue();
|
|
23
|
+
const token = await generateCSRFToken('session-1');
|
|
24
|
+
|
|
25
|
+
expect(token).toMatch(/^[a-f0-9]{64}$/);
|
|
26
|
+
expect(setItemSpy).toHaveBeenCalledWith(
|
|
27
|
+
'csrf_tokens',
|
|
28
|
+
expect.stringContaining('session-1'),
|
|
29
|
+
{ encrypt: true, expiry: expect.any(Number) }
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const cache = getTokenCache();
|
|
33
|
+
expect(cache.get(token)).toMatchObject({ sessionId: 'session-1', used: false });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('validates tokens only once and marks them as used', async () => {
|
|
37
|
+
vi.spyOn(secureStorage, 'setItem').mockResolvedValue();
|
|
38
|
+
const token = await generateCSRFToken('session-2');
|
|
39
|
+
|
|
40
|
+
await expect(validateCSRFToken(token, 'session-2')).resolves.toBe(true);
|
|
41
|
+
await expect(validateCSRFToken(token, 'session-2')).resolves.toBe(false);
|
|
42
|
+
|
|
43
|
+
const cache = getTokenCache();
|
|
44
|
+
expect((cache.get(token) as any).used).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('rejects expired tokens and persists cleanup', async () => {
|
|
48
|
+
const persistSpy = vi.spyOn(secureStorage, 'setItem').mockResolvedValue();
|
|
49
|
+
const cache = getTokenCache();
|
|
50
|
+
|
|
51
|
+
cache.set('expired-token', {
|
|
52
|
+
token: 'expired-token',
|
|
53
|
+
sessionId: 'expired-session',
|
|
54
|
+
timestamp: Date.now() - 31 * 60 * 1000,
|
|
55
|
+
used: false,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const result = await validateCSRFToken('expired-token', 'expired-session');
|
|
59
|
+
expect(result).toBe(false);
|
|
60
|
+
expect(cache.has('expired-token')).toBe(false);
|
|
61
|
+
expect(persistSpy).toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('loads stored tokens when cache is empty and returns current token', async () => {
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
vi.spyOn(secureStorage, 'getItem').mockResolvedValue(
|
|
67
|
+
JSON.stringify([
|
|
68
|
+
[
|
|
69
|
+
'valid-token',
|
|
70
|
+
{ token: 'valid-token', sessionId: 'session-load', timestamp: now, used: false },
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
'stale-token',
|
|
74
|
+
{ token: 'stale-token', sessionId: 'session-load', timestamp: now - 40 * 60 * 1000, used: false },
|
|
75
|
+
],
|
|
76
|
+
])
|
|
77
|
+
);
|
|
78
|
+
const persistSpy = vi.spyOn(secureStorage, 'setItem').mockResolvedValue();
|
|
79
|
+
|
|
80
|
+
const token = await getCSRFToken('session-load');
|
|
81
|
+
|
|
82
|
+
expect(token).toBe('valid-token');
|
|
83
|
+
expect(persistSpy).toHaveBeenCalled();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('returns false for invalid or mismatched tokens', async () => {
|
|
87
|
+
vi.spyOn(secureStorage, 'getItem').mockResolvedValue(null);
|
|
88
|
+
await expect(validateCSRFToken('unknown', 'session-x')).resolves.toBe(false);
|
|
89
|
+
|
|
90
|
+
vi.spyOn(secureStorage, 'setItem').mockResolvedValue();
|
|
91
|
+
const token = await generateCSRFToken('session-y');
|
|
92
|
+
await expect(validateCSRFToken(token, 'other-session')).resolves.toBe(false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('fails gracefully when token generation throws', async () => {
|
|
96
|
+
const randomSpy = vi
|
|
97
|
+
.spyOn(globalThis.crypto, 'getRandomValues')
|
|
98
|
+
.mockImplementation(() => {
|
|
99
|
+
throw new Error('no entropy');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await expect(generateCSRFToken('session-error')).rejects.toThrow('CSRF token generation failed');
|
|
103
|
+
expect(randomSpy).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
buildSafeQueryParams,
|
|
4
|
+
detectSQLInjection,
|
|
5
|
+
escapeLikeQuery,
|
|
6
|
+
sanitizeFilters,
|
|
7
|
+
sanitizeSearchQuery,
|
|
8
|
+
searchQuerySchema,
|
|
9
|
+
sqlIdentifierSchema,
|
|
10
|
+
orderBySchema,
|
|
11
|
+
limitOffsetSchema,
|
|
12
|
+
} from '../sqlInjectionProtection';
|
|
13
|
+
|
|
14
|
+
describe('sqlInjectionProtection', () => {
|
|
15
|
+
it('sanitizes search queries by stripping dangerous characters and trimming length', () => {
|
|
16
|
+
const longQuery = `${'DROP TABLE users; '.repeat(40)}'%evil`;
|
|
17
|
+
const sanitized = sanitizeSearchQuery(longQuery);
|
|
18
|
+
|
|
19
|
+
expect(sanitized.includes('DROP')).toBe(true);
|
|
20
|
+
expect(sanitized).not.toContain("'");
|
|
21
|
+
expect(sanitized).not.toContain('%');
|
|
22
|
+
expect(sanitized.length).toBeLessThanOrEqual(500);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('validates search query schema and transforms input', () => {
|
|
26
|
+
const parsed = searchQuerySchema.parse(' abc ');
|
|
27
|
+
expect(parsed).toBe('abc');
|
|
28
|
+
expect(() => searchQuerySchema.parse('SELECT * FROM users')).toThrow('Invalid characters detected in search query');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('validates identifiers and ordering formats', () => {
|
|
32
|
+
expect(sqlIdentifierSchema.parse('valid_name')).toBe('valid_name');
|
|
33
|
+
expect(() => sqlIdentifierSchema.parse('SELECT')).toThrow('Identifier cannot be a reserved SQL keyword');
|
|
34
|
+
expect(orderBySchema.parse('created_at DESC')).toBe('created_at DESC');
|
|
35
|
+
expect(() => orderBySchema.parse('invalid order by')).toThrow('Invalid order by format');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('validates numeric limit and offset values', () => {
|
|
39
|
+
expect(limitOffsetSchema.parse(25)).toBe(25);
|
|
40
|
+
expect(() => limitOffsetSchema.parse(-1)).toThrow('Must be non-negative');
|
|
41
|
+
expect(() => limitOffsetSchema.parse(10000)).toThrow('Limit too large');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('escapes like queries safely', () => {
|
|
45
|
+
expect(escapeLikeQuery('100% match_')).toBe('100\\% match\\_');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('sanitizes filters and omits invalid keys while logging', () => {
|
|
49
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
50
|
+
const sanitized = sanitizeFilters({
|
|
51
|
+
valid: 'abc',
|
|
52
|
+
'1invalid': 'DROP TABLE',
|
|
53
|
+
numbers: [1, 'two', '3;DELETE'],
|
|
54
|
+
malicious: "UNION SELECT password",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(sanitized.valid).toBe('abc');
|
|
58
|
+
expect(sanitized).not.toHaveProperty('1invalid');
|
|
59
|
+
expect(sanitized.malicious).toBeUndefined();
|
|
60
|
+
expect(sanitized.numbers).toEqual([1, 'two', '3DELETE']);
|
|
61
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('builds safe query params only including validated parts', () => {
|
|
65
|
+
const safe = buildSafeQueryParams({
|
|
66
|
+
select: 'id, name, DROP',
|
|
67
|
+
filters: { id: 1, unsafe: 'UNION SELECT *' },
|
|
68
|
+
orderBy: 'created_at DESC',
|
|
69
|
+
limit: 25,
|
|
70
|
+
offset: 5,
|
|
71
|
+
search: " ';DROP",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(safe.select).toBe('id, name');
|
|
75
|
+
expect(safe.filters).toEqual({ id: 1 });
|
|
76
|
+
expect(safe.orderBy).toBe('created_at DESC');
|
|
77
|
+
expect(safe.limit).toBe(25);
|
|
78
|
+
expect(safe.offset).toBe(5);
|
|
79
|
+
expect(safe.search).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('detects SQL injection patterns with risk levels', () => {
|
|
83
|
+
const injection = detectSQLInjection("'; UNION SELECT password FROM users --");
|
|
84
|
+
expect(injection.isSuspicious).toBe(true);
|
|
85
|
+
expect(injection.riskLevel).toBe('critical');
|
|
86
|
+
expect(injection.patterns.length).toBeGreaterThan(0);
|
|
87
|
+
|
|
88
|
+
const benign = detectSQLInjection('12345');
|
|
89
|
+
expect(benign.isSuspicious).toBe(false);
|
|
90
|
+
expect(benign.riskLevel).toBe('low');
|
|
91
|
+
});
|
|
92
|
+
});
|
package/src/vite-env.d.ts
CHANGED
|
@@ -9,8 +9,8 @@ interface ImportMetaEnv {
|
|
|
9
9
|
// Add other environment variables as needed
|
|
10
10
|
readonly VITE_APP_NAME?: string
|
|
11
11
|
readonly VITE_SUPABASE_URL?: string
|
|
12
|
-
//
|
|
13
|
-
readonly
|
|
12
|
+
// Publishable key (sb_publishable_...)
|
|
13
|
+
readonly VITE_SUPABASE_PUBLISHABLE_KEY?: string
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
interface ImportMeta {
|