@jmruthers/pace-core 0.5.135 → 0.5.137
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/dist/{DataTable-A36PJG6N.js → DataTable-6M4L6BI2.js} +26 -13
- package/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
- package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-rFL_kRjk.d.ts} +123 -135
- package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
- package/dist/{UnifiedAuthProvider-CQDZRJIS.js → UnifiedAuthProvider-XIQQ7LVU.js} +5 -5
- package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
- package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
- package/dist/{chunk-F64FFPOZ.js → chunk-22WKWKRX.js} +26 -20
- package/dist/chunk-22WKWKRX.js.map +1 -0
- package/dist/{chunk-VZ5OR6HD.js → chunk-4C7EXCAR.js} +62 -150
- package/dist/chunk-4C7EXCAR.js.map +1 -0
- package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
- package/dist/chunk-56XJ3TU6.js.map +1 -0
- package/dist/{chunk-CTJRBUX2.js → chunk-6LAAY47Q.js} +2 -2
- package/dist/{chunk-UJI6WSMD.js → chunk-7QCC6MCP.js} +90 -3
- package/dist/chunk-7QCC6MCP.js.map +1 -0
- package/dist/{chunk-66C4BSAY.js → chunk-ANBQRTPX.js} +9 -2
- package/dist/chunk-ANBQRTPX.js.map +1 -0
- package/dist/{chunk-CQZU6TFE.js → chunk-BCIBECNB.js} +100 -62
- package/dist/chunk-BCIBECNB.js.map +1 -0
- package/dist/{chunk-GKHF54DI.js → chunk-BESYRHQM.js} +10 -4
- package/dist/chunk-BESYRHQM.js.map +1 -0
- package/dist/chunk-BJPBT3CU.js +21 -0
- package/dist/chunk-BJPBT3CU.js.map +1 -0
- package/dist/{chunk-BYXRHAIF.js → chunk-BLCXZEYF.js} +23 -14
- package/dist/chunk-BLCXZEYF.js.map +1 -0
- package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
- package/dist/chunk-BVYWGZVV.js.map +1 -0
- package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
- package/dist/chunk-ERISIBYU.js.map +1 -0
- package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-GVDR7WNV.js → chunk-HAWZXGR2.js} +334 -614
- package/dist/chunk-HAWZXGR2.js.map +1 -0
- package/dist/{chunk-ZV77RZMU.js → chunk-INQLMHPF.js} +2 -2
- package/dist/chunk-JISYG63F.js +70 -0
- package/dist/chunk-JISYG63F.js.map +1 -0
- package/dist/{chunk-HMNOSGVA.js → chunk-KYRHUBIU.js} +576 -767
- package/dist/chunk-KYRHUBIU.js.map +1 -0
- package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
- package/dist/chunk-LS353YLY.js.map +1 -0
- package/dist/{chunk-TGIY2AR2.js → chunk-MA6EPSGZ.js} +4 -3
- package/dist/{chunk-TGIY2AR2.js.map → chunk-MA6EPSGZ.js.map} +1 -1
- package/dist/chunk-OWAG3GSU.js +58 -0
- package/dist/chunk-OWAG3GSU.js.map +1 -0
- package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
- package/dist/chunk-Q5QRDWKI.js.map +1 -0
- package/dist/chunk-S5OFRT4M.js +94 -0
- package/dist/chunk-S5OFRT4M.js.map +1 -0
- package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
- package/dist/chunk-SBVILCCA.js.map +1 -0
- package/dist/{chunk-ZYZCRSBD.js → chunk-T6JN6LH6.js} +16 -11
- package/dist/chunk-T6JN6LH6.js.map +1 -0
- package/dist/chunk-XDNLUEXI.js +138 -0
- package/dist/chunk-XDNLUEXI.js.map +1 -0
- package/dist/{chunk-3CG5L6RN.js → chunk-YCWDTTUK.js} +90 -75
- package/dist/chunk-YCWDTTUK.js.map +1 -0
- package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
- package/dist/chunk-ZZ2SS7NI.js.map +1 -0
- package/dist/components.d.ts +7 -287
- package/dist/components.js +27 -157
- package/dist/components.js.map +1 -1
- package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
- package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +21 -16
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +101 -9
- package/dist/index.js +44 -31
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/rbac/index.js +12 -12
- package/dist/schema-DTDZQe2u.d.ts +28 -0
- package/dist/styles/index.js +2 -1
- package/dist/theming/runtime.d.ts +2 -19
- package/dist/theming/runtime.js +2 -1
- package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
- package/dist/types.d.ts +153 -4
- package/dist/types.js +51 -16
- package/dist/types.js.map +1 -1
- package/dist/{useInactivityTracker-MRUU55XI.js → useInactivityTracker-TO6ZOF35.js} +3 -2
- package/dist/{usePublicRouteParams-Dyt1tzI9.d.ts → usePublicRouteParams-B7PabvuH.d.ts} +1 -1
- package/dist/utils.d.ts +221 -173
- package/dist/utils.js +185 -225
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +24 -115
- package/dist/validation.js +19 -474
- package/dist/validation.js.map +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +6 -6
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +6 -6
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +7 -7
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- package/docs/api/interfaces/BadgeProps.md +27 -0
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +29 -4
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +18 -18
- package/docs/api/interfaces/DataTableColumn.md +61 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +14 -14
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +152 -0
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- package/docs/api/interfaces/FileDisplayProps.md +15 -15
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.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/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 +10 -10
- 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 +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +4 -4
- package/docs/api/interfaces/PermissionEnforcerProps.md +11 -11
- package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.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/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.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/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +21 -0
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.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/UseInactivityTrackerOptions.md +9 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +9 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- 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 +591 -220
- package/docs/api-reference/components.md +106 -26
- package/docs/architecture/README.md +0 -3
- package/docs/implementation-guides/data-tables.md +277 -13
- package/docs/implementation-guides/forms.md +1 -16
- package/docs/implementation-guides/permission-enforcement.md +8 -2
- package/docs/styles/README.md +0 -2
- package/examples/README.md +30 -14
- package/examples/STRUCTURE.md +125 -0
- package/examples/components 2/DataTable/HierarchicalActionsExample.tsx +421 -0
- package/examples/components 2/DataTable/HierarchicalExample.tsx +475 -0
- package/examples/components 2/DataTable/InitialPageSizeExample.tsx +177 -0
- package/examples/components 2/DataTable/PerformanceExample.tsx +506 -0
- package/examples/components 2/DataTable/index.ts +13 -0
- package/examples/components 2/Dialog/BasicHtmlTest.tsx +55 -0
- package/examples/components 2/Dialog/DebugHtmlExample.tsx +68 -0
- package/examples/components 2/Dialog/HtmlDialogExample.tsx +202 -0
- package/examples/components 2/Dialog/ScrollableDialogExample.tsx +290 -0
- package/examples/components 2/Dialog/SimpleHtmlTest.tsx +61 -0
- package/examples/components 2/Dialog/SmartDialogExample.tsx +322 -0
- package/examples/components 2/Dialog/index.ts +15 -0
- package/examples/components 2/index.ts +11 -0
- package/examples/features/index.ts +12 -0
- package/{src/examples → examples/features/public-pages}/CorrectPublicPageImplementation.tsx +14 -17
- package/{src/examples → examples/features/public-pages}/PublicEventPage.tsx +14 -27
- package/{src/examples → examples/features/public-pages}/PublicPageApp.tsx +15 -28
- package/{src/examples → examples/features/public-pages}/PublicPageUsageExample.tsx +8 -10
- package/examples/features/public-pages/index.ts +14 -0
- package/examples/features/rbac/CompleteRBACExample.tsx +324 -0
- package/examples/features/rbac/EventBasedApp.tsx +239 -0
- package/examples/features/rbac/PermissionExample.tsx +151 -0
- package/examples/features/rbac/index.ts +13 -0
- package/examples/index.ts +11 -3
- package/package.json +30 -19
- package/src/__tests__/TEST_STANDARD.md +92 -0
- package/src/components/Alert/Alert.tsx +1 -1
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Badge/Badge.test.tsx +314 -0
- package/src/components/Badge/Badge.tsx +304 -0
- package/src/components/Badge/index.ts +3 -0
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +1 -1
- package/src/components/DataTable/DataTable.tsx +1 -30
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
- package/src/components/DataTable/__tests__/styles.test.ts +3 -3
- package/src/components/DataTable/components/ActionButtons.tsx +0 -15
- package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
- package/src/components/DataTable/components/DataTableBody.tsx +461 -0
- package/src/components/DataTable/components/DataTableCore.tsx +4 -185
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
- package/src/components/DataTable/components/DataTableModals.tsx +1 -27
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/FilterRow.tsx +9 -3
- package/src/components/DataTable/components/ImportModal.tsx +2 -14
- package/src/components/DataTable/components/PaginationControls.tsx +2 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
- package/src/components/DataTable/core/ActionManager.ts +235 -0
- package/src/components/DataTable/core/ColumnManager.ts +205 -0
- package/src/components/DataTable/core/DataManager.ts +188 -0
- package/src/components/DataTable/core/DataTableContext.tsx +181 -0
- package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
- package/src/components/DataTable/core/PluginRegistry.ts +229 -0
- package/src/components/DataTable/core/StateManager.ts +311 -0
- package/src/components/DataTable/core/interfaces.ts +338 -0
- package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
- package/src/components/DataTable/index.ts +4 -0
- package/src/components/DataTable/styles.ts +28 -7
- package/src/components/DataTable/types.ts +13 -0
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
- package/src/components/DataTable/utils/columnUtils.ts +40 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.ts +1 -11
- package/src/components/DataTable/utils/index.ts +2 -0
- package/src/components/DataTable/utils/paginationUtils.ts +1 -1
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +8 -1
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
- package/src/components/EventSelector/EventSelector.tsx +3 -2
- package/src/components/FileDisplay/FileDisplay.tsx +2 -36
- package/src/components/FileUpload/FileUpload.test.tsx +2 -2
- package/src/components/FileUpload/FileUpload.tsx +2 -2
- package/src/components/Footer/Footer.test.tsx +1 -1
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +5 -510
- package/src/components/Form/Form.tsx +1 -1
- package/src/components/Form/FormField.tsx +1 -1
- package/src/components/Form/index.ts +0 -12
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +1 -1
- package/src/components/LoginForm/LoginForm.tsx +1 -1
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
- package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +14 -13
- package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
- package/src/components/PasswordReset/index.ts +0 -2
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
- package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
- package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
- package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
- package/src/components/PublicLayout/index.ts +4 -2
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +21 -9
- package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
- package/src/components/SessionRestorationLoader/index.ts +3 -0
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Table/__tests__/Table.test.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/index.ts +7 -10
- package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
- package/src/hooks/public/usePublicEvent.ts +7 -6
- package/src/hooks/public/usePublicEventLogo.ts +7 -4
- package/src/hooks/public/usePublicFileDisplay.ts +6 -150
- package/src/hooks/useComponentPerformance.ts +4 -1
- package/src/hooks/useDataTablePerformance.ts +4 -3
- package/src/hooks/useEventTheme.test.ts +18 -5
- package/src/hooks/useEventTheme.ts +4 -1
- package/src/hooks/useEvents.ts +2 -0
- package/src/hooks/useFileDisplay.ts +9 -8
- package/src/hooks/useFileReference.ts +4 -1
- package/src/hooks/useFileUrl.ts +4 -1
- package/src/hooks/useInactivityTracker.ts +5 -4
- package/src/hooks/useOrganisationSecurity.test.ts +33 -12
- package/src/hooks/useOrganisationSecurity.ts +8 -7
- package/src/hooks/usePerformanceMonitor.ts +6 -3
- package/src/hooks/usePermissionCache.ts +13 -6
- package/src/hooks/useSecureDataAccess.test.ts +2 -2
- package/src/hooks/useSecureDataAccess.ts +9 -8
- package/src/hooks/useSessionRestoration.ts +4 -1
- package/src/hooks/useStorage.ts +4 -1
- package/src/index.ts +20 -7
- package/src/providers/services/AuthServiceProvider.tsx +3 -2
- package/src/providers/services/EventServiceProvider.tsx +2 -1
- package/src/providers/services/InactivityServiceProvider.tsx +2 -1
- package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
- package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
- package/src/rbac/api.ts +5 -2
- package/src/rbac/audit-enhanced.ts +6 -6
- package/src/rbac/audit.test.ts +60 -38
- package/src/rbac/audit.ts +8 -8
- package/src/rbac/cache-invalidation.ts +7 -4
- package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
- package/src/rbac/components/NavigationGuard.tsx +7 -3
- package/src/rbac/components/NavigationProvider.tsx +6 -3
- package/src/rbac/components/PagePermissionGuard.tsx +28 -16
- package/src/rbac/components/PagePermissionProvider.tsx +4 -1
- package/src/rbac/components/PermissionEnforcer.tsx +9 -3
- package/src/rbac/components/RoleBasedRouter.tsx +3 -1
- package/src/rbac/components/SecureDataProvider.tsx +7 -3
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
- package/src/rbac/engine.ts +15 -7
- package/src/rbac/hooks/usePermissions.ts +7 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
- package/src/rbac/hooks/useResolvedScope.ts +10 -7
- package/src/rbac/permissions.ts +5 -2
- package/src/rbac/security.test.ts +27 -16
- package/src/rbac/security.ts +5 -4
- package/src/services/AuthService.ts +22 -21
- package/src/services/EventService.ts +12 -12
- package/src/services/InactivityService.ts +5 -4
- package/src/services/OrganisationService.ts +26 -25
- package/src/services/__tests__/AuthService.test.ts +51 -19
- package/src/services/__tests__/EventService.test.ts +37 -5
- package/src/services/__tests__/InactivityService.test.ts +38 -4
- package/src/services/__tests__/OrganisationService.test.ts +3 -8
- package/src/services/base/BaseService.ts +3 -1
- package/src/styles/core.css +3 -0
- package/src/theming/__tests__/runtime.test.ts +21 -12
- package/src/theming/parseEventColours.ts +5 -19
- package/src/theming/runtime.ts +8 -4
- package/src/types/validation.ts +2 -29
- package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
- package/src/utils/__tests__/audit.unit.test.ts +1 -1
- package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
- package/src/utils/__tests__/cn.unit.test.ts +1 -1
- package/src/utils/__tests__/debugLogger.test.ts +1 -1
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/formatting.unit.test.ts +1 -1
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
- package/src/utils/__tests__/logger.unit.test.ts +1 -1
- package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
- package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
- package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
- package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
- package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
- package/src/utils/__tests__/security.unit.test.ts +40 -18
- package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
- package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
- package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
- package/src/utils/app/appConfig.ts +47 -0
- package/src/utils/app/appIdResolver.test.ts +497 -0
- package/src/utils/app/appIdResolver.ts +133 -0
- package/src/utils/app/appNameResolver.simple.test.ts +212 -0
- package/src/utils/app/appNameResolver.test.ts +121 -0
- package/src/utils/app/appNameResolver.ts +195 -0
- package/src/utils/audit/audit.ts +127 -0
- package/src/utils/context/organisationContext.test.ts +322 -0
- package/src/utils/context/organisationContext.ts +156 -0
- package/src/utils/context/sessionTracking.ts +125 -0
- package/src/utils/core/cn.ts +7 -0
- package/src/utils/core/debugLogger.ts +67 -0
- package/src/utils/core/logger.ts +181 -0
- package/src/utils/device/deviceFingerprint.ts +215 -0
- package/src/utils/dynamic/dynamicUtils.ts +105 -0
- package/src/utils/dynamic/lazyLoad.tsx +44 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +788 -0
- package/src/utils/file-reference/index.ts +501 -0
- package/src/utils/formatting/formatDate.test.ts +237 -0
- package/src/utils/formatting/formatting.ts +133 -0
- package/src/utils/index.ts +39 -54
- package/src/utils/performance/bundleAnalysis.ts +129 -0
- package/src/utils/performance/performanceBenchmark.ts +64 -0
- package/src/utils/performance/performanceBudgets.ts +110 -0
- package/src/utils/permissions/permissionTypes.ts +37 -0
- package/src/utils/permissions/permissionUtils.test.ts +393 -0
- package/src/utils/permissions/permissionUtils.ts +34 -0
- package/src/utils/security/auth-utils.ts +96 -0
- package/src/utils/security/secureDataAccess.test.ts +711 -0
- package/src/utils/security/secureDataAccess.ts +377 -0
- package/src/utils/security/secureErrors.ts +82 -0
- package/src/utils/security/secureStorage.ts +244 -0
- package/src/utils/security/security.ts +159 -0
- package/src/utils/security/securityMonitor.ts +45 -0
- package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
- package/src/utils/storage/helpers.ts +15 -8
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +598 -0
- package/src/{validation → utils/validation}/csrf.ts +1 -1
- package/src/utils/validation/htmlSanitization.ts +184 -0
- package/src/utils/validation/index.ts +79 -0
- package/src/utils/validation/sanitization.ts +333 -0
- package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
- package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
- package/src/utils/validation/validation.ts +111 -0
- package/src/utils/validation/validationUtils.ts +123 -0
- package/src/validation/index.ts +3 -34
- package/dist/chunk-24MKLB7U.js +0 -81
- package/dist/chunk-24MKLB7U.js.map +0 -1
- package/dist/chunk-3CG5L6RN.js.map +0 -1
- package/dist/chunk-3DBFLLLU.js.map +0 -1
- package/dist/chunk-5F3NDPJV.js.map +0 -1
- package/dist/chunk-66C4BSAY.js.map +0 -1
- package/dist/chunk-BDZUMRBD.js +0 -87
- package/dist/chunk-BDZUMRBD.js.map +0 -1
- package/dist/chunk-BYXRHAIF.js.map +0 -1
- package/dist/chunk-CDQ3PX7L.js +0 -18
- package/dist/chunk-CDQ3PX7L.js.map +0 -1
- package/dist/chunk-CQZU6TFE.js.map +0 -1
- package/dist/chunk-F64FFPOZ.js.map +0 -1
- package/dist/chunk-GEVIB2UB.js.map +0 -1
- package/dist/chunk-GKHF54DI.js.map +0 -1
- package/dist/chunk-GVDR7WNV.js.map +0 -1
- package/dist/chunk-HMNOSGVA.js.map +0 -1
- package/dist/chunk-JCQZ6LA7.js.map +0 -1
- package/dist/chunk-M6DDYFUD.js.map +0 -1
- package/dist/chunk-O3NWNXDY.js.map +0 -1
- package/dist/chunk-PYUXFQJ3.js.map +0 -1
- package/dist/chunk-UJI6WSMD.js.map +0 -1
- package/dist/chunk-VZ5OR6HD.js.map +0 -1
- package/dist/chunk-WP5I5GLN.js.map +0 -1
- package/dist/chunk-ZYZCRSBD.js.map +0 -1
- package/dist/validation-DnhrNMju.d.ts +0 -159
- package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
- package/src/validation/__tests__/common.unit.test.ts +0 -101
- package/src/validation/__tests__/csrf.unit.test.ts +0 -365
- package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
- package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
- package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
- package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
- package/src/validation/__tests__/user.unit.test.ts +0 -440
- package/src/validation/sanitization.ts +0 -96
- /package/dist/{DataTable-A36PJG6N.js.map → DataTable-6M4L6BI2.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
- /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
- /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
- /package/dist/{chunk-CTJRBUX2.js.map → chunk-6LAAY47Q.js.map} +0 -0
- /package/dist/{chunk-ZV77RZMU.js.map → chunk-INQLMHPF.js.map} +0 -0
- /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
- /package/src/{validation → utils/validation}/common.ts +0 -0
- /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
- /package/src/{validation → utils/validation}/user.ts +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file HTML Sanitization Utilities
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Utils/Validation/HTMLSanitization
|
|
5
|
+
* @since 0.4.36
|
|
6
|
+
*
|
|
7
|
+
* Utilities for safely rendering HTML content.
|
|
8
|
+
* Provides sanitization and validation for basic HTML elements.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Allowed HTML tags for safe rendering
|
|
13
|
+
*/
|
|
14
|
+
const ALLOWED_TAGS = [
|
|
15
|
+
'p', 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
16
|
+
'strong', 'em', 'b', 'i', 'u', 's', 'mark', 'small', 'sub', 'sup',
|
|
17
|
+
'ul', 'ol', 'li', 'dl', 'dt', 'dd',
|
|
18
|
+
'blockquote', 'pre', 'code', 'kbd', 'samp',
|
|
19
|
+
'a', 'br', 'hr',
|
|
20
|
+
'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',
|
|
21
|
+
'img', 'figure', 'figcaption',
|
|
22
|
+
'section', 'article', 'aside', 'header', 'footer', 'main', 'nav',
|
|
23
|
+
'details', 'summary'
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Allowed HTML attributes for safe rendering
|
|
28
|
+
*/
|
|
29
|
+
const ALLOWED_ATTRIBUTES = [
|
|
30
|
+
'id', 'class', 'style', 'title', 'lang', 'dir',
|
|
31
|
+
'href', 'target', 'rel', 'download',
|
|
32
|
+
'src', 'alt', 'width', 'height', 'loading',
|
|
33
|
+
'colspan', 'rowspan', 'scope', 'headers',
|
|
34
|
+
'open', 'datetime', 'cite'
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Basic HTML sanitization function using regex-based approach
|
|
39
|
+
* Removes potentially dangerous elements and attributes while preserving basic formatting
|
|
40
|
+
* This approach is more reliable in SSR environments and doesn't require DOM manipulation
|
|
41
|
+
*
|
|
42
|
+
* @param html - The HTML string to sanitize
|
|
43
|
+
* @returns Sanitized HTML string safe for rendering
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* const safeHtml = sanitizeHtml('<p>Hello <strong>world</strong>!</p>');
|
|
48
|
+
* // Returns: '<p>Hello <strong>world</strong>!</p>'
|
|
49
|
+
*
|
|
50
|
+
* const dangerousHtml = sanitizeHtml('<script>alert("xss")</script><p>Safe content</p>');
|
|
51
|
+
* // Returns: '<p>Safe content</p>'
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function sanitizeHtml(html: string): string {
|
|
55
|
+
if (!html || typeof html !== 'string') {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Basic safety: just remove script tags and dangerous attributes
|
|
60
|
+
let sanitized = html
|
|
61
|
+
// Remove script tags (including self-closing and malformed)
|
|
62
|
+
.replace(/<script\b[^>]*>.*?<\/script>/gi, '')
|
|
63
|
+
.replace(/<script\b[^>]*\/>/gi, '')
|
|
64
|
+
// Remove iframe tags (including self-closing and malformed)
|
|
65
|
+
.replace(/<iframe\b[^>]*>.*?<\/iframe>/gi, '')
|
|
66
|
+
.replace(/<iframe\b[^>]*\/>/gi, '')
|
|
67
|
+
// Remove object tags (including self-closing and malformed)
|
|
68
|
+
.replace(/<object\b[^>]*>.*?<\/object>/gi, '')
|
|
69
|
+
.replace(/<object\b[^>]*\/>/gi, '')
|
|
70
|
+
// Remove embed tags (including self-closing)
|
|
71
|
+
.replace(/<embed\b[^>]*\/?>/gi, '')
|
|
72
|
+
// Remove form tags (including self-closing and malformed)
|
|
73
|
+
.replace(/<form\b[^>]*>.*?<\/form>/gi, '')
|
|
74
|
+
.replace(/<form\b[^>]*\/>/gi, '')
|
|
75
|
+
// Remove input tags (including self-closing)
|
|
76
|
+
.replace(/<input\b[^>]*\/?>/gi, '')
|
|
77
|
+
// Remove button tags (including self-closing and malformed)
|
|
78
|
+
.replace(/<button\b[^>]*>.*?<\/button>/gi, '')
|
|
79
|
+
.replace(/<button\b[^>]*\/>/gi, '')
|
|
80
|
+
// Remove event handlers from any remaining tags - correct pattern
|
|
81
|
+
.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi, '')
|
|
82
|
+
// Remove javascript: protocols - correct pattern
|
|
83
|
+
.replace(/javascript:[^"'\s>]*/gi, '')
|
|
84
|
+
// Remove data: protocols - correct pattern
|
|
85
|
+
.replace(/data:[^"'\s>]*/gi, '');
|
|
86
|
+
|
|
87
|
+
return sanitized;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Validates if HTML content is safe for rendering
|
|
92
|
+
*
|
|
93
|
+
* @param html - The HTML string to validate
|
|
94
|
+
* @returns Object with validation result and any warnings
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```tsx
|
|
98
|
+
* const validation = validateHtml('<p>Safe content</p>');
|
|
99
|
+
* console.log(validation.isValid); // true
|
|
100
|
+
* console.log(validation.warnings); // []
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export function validateHtml(html: string): { isValid: boolean; warnings: string[] } {
|
|
104
|
+
const warnings: string[] = [];
|
|
105
|
+
|
|
106
|
+
if (!html || typeof html !== 'string') {
|
|
107
|
+
return { isValid: false, warnings: ['HTML content must be a non-empty string'] };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check for potentially dangerous patterns
|
|
111
|
+
const dangerousPatterns = [
|
|
112
|
+
{ pattern: /<script\b[^>]*>.*?<\/script>/gi, message: 'Script tags are not allowed' },
|
|
113
|
+
{ pattern: /<script\b[^>]*\/>/gi, message: 'Script tags are not allowed' },
|
|
114
|
+
{ pattern: /<iframe\b[^>]*>.*?<\/iframe>/gi, message: 'Iframe tags are not allowed' },
|
|
115
|
+
{ pattern: /<iframe\b[^>]*\/>/gi, message: 'Iframe tags are not allowed' },
|
|
116
|
+
{ pattern: /<object\b[^>]*>.*?<\/object>/gi, message: 'Object tags are not allowed' },
|
|
117
|
+
{ pattern: /<object\b[^>]*\/>/gi, message: 'Object tags are not allowed' },
|
|
118
|
+
{ pattern: /<embed\b[^>]*\/?>/gi, message: 'Embed tags are not allowed' },
|
|
119
|
+
{ pattern: /<form\b[^>]*>.*?<\/form>/gi, message: 'Form tags are not allowed' },
|
|
120
|
+
{ pattern: /<form\b[^>]*\/>/gi, message: 'Form tags are not allowed' },
|
|
121
|
+
{ pattern: /<input\b[^>]*\/?>/gi, message: 'Input tags are not allowed' },
|
|
122
|
+
{ pattern: /<button\b[^>]*>.*?<\/button>/gi, message: 'Button tags are not allowed' },
|
|
123
|
+
{ pattern: /<button\b[^>]*\/>/gi, message: 'Button tags are not allowed' },
|
|
124
|
+
{ pattern: /on\w+\s*=/gi, message: 'Event handlers are not allowed' },
|
|
125
|
+
{ pattern: /javascript:/gi, message: 'JavaScript protocols are not allowed' },
|
|
126
|
+
{ pattern: /data:/gi, message: 'Data protocols are not allowed' }
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
dangerousPatterns.forEach(({ pattern, message }) => {
|
|
130
|
+
if (pattern.test(html)) {
|
|
131
|
+
warnings.push(message);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
isValid: warnings.length === 0,
|
|
137
|
+
warnings
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Safely renders HTML content with sanitization
|
|
143
|
+
*
|
|
144
|
+
* @param html - The HTML string to render
|
|
145
|
+
* @param options - Rendering options
|
|
146
|
+
* @returns Object with sanitized HTML and validation info
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```tsx
|
|
150
|
+
* const result = renderSafeHtml('<p>Hello <strong>world</strong>!</p>');
|
|
151
|
+
* console.log(result.html); // Sanitized HTML
|
|
152
|
+
* console.log(result.isValid); // true
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export function renderSafeHtml(
|
|
156
|
+
html: string,
|
|
157
|
+
options: {
|
|
158
|
+
strict?: boolean;
|
|
159
|
+
logWarnings?: boolean;
|
|
160
|
+
} = {}
|
|
161
|
+
): {
|
|
162
|
+
html: string;
|
|
163
|
+
isValid: boolean;
|
|
164
|
+
warnings: string[]
|
|
165
|
+
} {
|
|
166
|
+
const { strict = true, logWarnings = false } = options;
|
|
167
|
+
|
|
168
|
+
const validation = validateHtml(html);
|
|
169
|
+
const sanitizedHtml = sanitizeHtml(html);
|
|
170
|
+
|
|
171
|
+
if (logWarnings && validation.warnings.length > 0) {
|
|
172
|
+
// Use logger if needed, but this is controlled by logWarnings option
|
|
173
|
+
// For now, keep console.warn as it's only called when explicitly requested
|
|
174
|
+
// via logWarnings option, which is typically for debugging
|
|
175
|
+
console.warn('HTML content warnings:', validation.warnings);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
html: sanitizedHtml,
|
|
180
|
+
isValid: validation.isValid,
|
|
181
|
+
warnings: validation.warnings
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Validation Module Exports
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Utils/Validation
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Consolidated validation utilities and schemas for the PACE Core library.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Schema utilities
|
|
11
|
+
export { pickSchema, combineSchemas } from './schema';
|
|
12
|
+
|
|
13
|
+
// Common validation functions
|
|
14
|
+
export {
|
|
15
|
+
isValidEmail,
|
|
16
|
+
isEmpty,
|
|
17
|
+
isStrongPassword,
|
|
18
|
+
isValidUrl,
|
|
19
|
+
isValidDate,
|
|
20
|
+
isWithinRange,
|
|
21
|
+
matchesPattern,
|
|
22
|
+
deepMerge,
|
|
23
|
+
isObject
|
|
24
|
+
} from './validation';
|
|
25
|
+
|
|
26
|
+
// Enhanced validation with sanitization
|
|
27
|
+
export {
|
|
28
|
+
validateUserInput,
|
|
29
|
+
sanitizeUserInput_deprecated,
|
|
30
|
+
emailSchema as enhancedEmailSchema,
|
|
31
|
+
passwordSchema as enhancedPasswordSchema,
|
|
32
|
+
usernameSchema,
|
|
33
|
+
nameSchema as enhancedNameSchema,
|
|
34
|
+
phoneSchema as enhancedPhoneSchema,
|
|
35
|
+
urlSchema as enhancedUrlSchema
|
|
36
|
+
} from './validationUtils';
|
|
37
|
+
|
|
38
|
+
// Sanitization utilities
|
|
39
|
+
export {
|
|
40
|
+
sanitizeUserInput,
|
|
41
|
+
sanitizeFormData,
|
|
42
|
+
sanitizeHtml,
|
|
43
|
+
type SanitizationOptions
|
|
44
|
+
} from './sanitization';
|
|
45
|
+
|
|
46
|
+
// HTML sanitization utilities (allows safe HTML tags)
|
|
47
|
+
export {
|
|
48
|
+
sanitizeHtml as sanitizeHtmlAdvanced,
|
|
49
|
+
validateHtml,
|
|
50
|
+
renderSafeHtml
|
|
51
|
+
} from './htmlSanitization';
|
|
52
|
+
|
|
53
|
+
// Common validation schemas
|
|
54
|
+
export {
|
|
55
|
+
emailSchema,
|
|
56
|
+
nameSchema,
|
|
57
|
+
phoneSchema,
|
|
58
|
+
urlSchema,
|
|
59
|
+
dateSchema
|
|
60
|
+
} from './common';
|
|
61
|
+
|
|
62
|
+
// Security validation
|
|
63
|
+
export * from './csrf';
|
|
64
|
+
export * from './sqlInjectionProtection';
|
|
65
|
+
export * from './passwordSchema';
|
|
66
|
+
export * from './user';
|
|
67
|
+
|
|
68
|
+
// Re-export schemas from types for convenience (these are the canonical schemas)
|
|
69
|
+
export {
|
|
70
|
+
loginSchema,
|
|
71
|
+
registrationSchema,
|
|
72
|
+
secureLoginSchema,
|
|
73
|
+
passwordResetSchema,
|
|
74
|
+
changePasswordSchema,
|
|
75
|
+
userProfileSchema,
|
|
76
|
+
contactFormSchema,
|
|
77
|
+
securePasswordSchema
|
|
78
|
+
} from '../../types/validation';
|
|
79
|
+
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Input Sanitization Layer
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Utils/Validation/Sanitization
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive input sanitization utilities to prevent XSS, injection attacks,
|
|
8
|
+
* and other security vulnerabilities.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Sanitization options for different contexts
|
|
15
|
+
*/
|
|
16
|
+
export interface SanitizationOptions {
|
|
17
|
+
allowHtml?: boolean;
|
|
18
|
+
allowedTags?: string[];
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
trim?: boolean;
|
|
21
|
+
removeScripts?: boolean;
|
|
22
|
+
removeEvents?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default sanitization options
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_OPTIONS: SanitizationOptions = {
|
|
29
|
+
allowHtml: false,
|
|
30
|
+
allowedTags: [],
|
|
31
|
+
maxLength: 1000,
|
|
32
|
+
trim: true,
|
|
33
|
+
removeScripts: true,
|
|
34
|
+
removeEvents: true
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sanitizes user input by removing potentially dangerous characters and patterns
|
|
39
|
+
*/
|
|
40
|
+
export function sanitizeUserInput(input: string, options: SanitizationOptions = {}): string {
|
|
41
|
+
if (typeof input !== 'string') {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
46
|
+
let sanitized = input;
|
|
47
|
+
|
|
48
|
+
// Trim whitespace if requested
|
|
49
|
+
if (opts.trim) {
|
|
50
|
+
sanitized = sanitized.trim();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Enforce maximum length
|
|
54
|
+
if (opts.maxLength && sanitized.length > opts.maxLength) {
|
|
55
|
+
sanitized = sanitized.substring(0, opts.maxLength);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Remove or escape HTML if not allowed
|
|
59
|
+
if (!opts.allowHtml) {
|
|
60
|
+
sanitized = sanitized
|
|
61
|
+
.replace(/</g, '<')
|
|
62
|
+
.replace(/>/g, '>')
|
|
63
|
+
.replace(/"/g, '"')
|
|
64
|
+
.replace(/'/g, ''')
|
|
65
|
+
.replace(/\//g, '/');
|
|
66
|
+
} else if (opts.allowedTags && opts.allowedTags.length > 0) {
|
|
67
|
+
// If HTML is allowed, only permit specific tags
|
|
68
|
+
const allowedTagsRegex = new RegExp(`<(?!\/?(?:${opts.allowedTags.join('|')})\s*\/?>)[^>]+>`, 'gi');
|
|
69
|
+
sanitized = sanitized.replace(allowedTagsRegex, '');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Remove script tags and javascript: protocols
|
|
73
|
+
if (opts.removeScripts) {
|
|
74
|
+
sanitized = sanitized
|
|
75
|
+
.replace(/<script[^>]*>.*?<\/script>/gi, '')
|
|
76
|
+
.replace(/javascript:/gi, '')
|
|
77
|
+
.replace(/vbscript:/gi, '')
|
|
78
|
+
.replace(/data:/gi, '');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Remove event handlers
|
|
82
|
+
if (opts.removeEvents) {
|
|
83
|
+
sanitized = sanitized.replace(/on\w+\s*=/gi, '');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return sanitized;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Sanitizes email addresses
|
|
91
|
+
*/
|
|
92
|
+
export function sanitizeEmail(email: string): string {
|
|
93
|
+
if (typeof email !== 'string') {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return email
|
|
98
|
+
.trim()
|
|
99
|
+
.toLowerCase()
|
|
100
|
+
.replace(/[^\w@.-]/g, ''); // Only allow word characters, @, ., and -
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Sanitizes phone numbers
|
|
105
|
+
*/
|
|
106
|
+
export function sanitizePhoneNumber(phone: string): string {
|
|
107
|
+
if (typeof phone !== 'string') {
|
|
108
|
+
return '';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return phone.replace(/[^\d+\-\s()]/g, '').trim();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Sanitizes URLs
|
|
116
|
+
*/
|
|
117
|
+
export function sanitizeUrl(url: string): string {
|
|
118
|
+
if (typeof url !== 'string') {
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const sanitized = url.trim();
|
|
123
|
+
|
|
124
|
+
// Only allow http(s) and ftp protocols
|
|
125
|
+
if (!/^https?:\/\/|^ftp:\/\//i.test(sanitized)) {
|
|
126
|
+
return '';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Remove javascript: and other dangerous protocols
|
|
130
|
+
if (/javascript:|data:|vbscript:/i.test(sanitized)) {
|
|
131
|
+
return '';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return sanitized;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Sanitizes file names
|
|
139
|
+
*/
|
|
140
|
+
export function sanitizeFileName(fileName: string): string {
|
|
141
|
+
if (typeof fileName !== 'string') {
|
|
142
|
+
return '';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return fileName
|
|
146
|
+
.trim()
|
|
147
|
+
.replace(/[<>:"/\\|?*]/g, '') // Remove invalid file name characters
|
|
148
|
+
.replace(/\.\./g, '') // Remove directory traversal attempts
|
|
149
|
+
.substring(0, 255); // Limit length
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Sanitizes SQL input to prevent injection
|
|
154
|
+
*/
|
|
155
|
+
export function sanitizeSqlInput(input: string): string {
|
|
156
|
+
if (typeof input !== 'string') {
|
|
157
|
+
return '';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return input
|
|
161
|
+
.replace(/['";\\]/g, '') // Remove SQL special characters
|
|
162
|
+
.replace(/--.*$/gm, '') // Remove SQL comments
|
|
163
|
+
.replace(/\/\*.*?\*\//g, '') // Remove SQL block comments
|
|
164
|
+
.trim();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Validates and sanitizes form data using Zod schemas
|
|
169
|
+
*/
|
|
170
|
+
export function sanitizeFormData<T>(
|
|
171
|
+
data: unknown,
|
|
172
|
+
schema: z.ZodSchema<T>,
|
|
173
|
+
sanitizationRules?: Record<string, SanitizationOptions>
|
|
174
|
+
): { success: boolean; data?: T; error?: string } {
|
|
175
|
+
try {
|
|
176
|
+
// First, sanitize string fields if rules are provided
|
|
177
|
+
if (sanitizationRules && typeof data === 'object' && data !== null) {
|
|
178
|
+
const sanitizedData = { ...data } as Record<string, unknown>;
|
|
179
|
+
|
|
180
|
+
Object.entries(sanitizationRules).forEach(([field, options]) => {
|
|
181
|
+
if (typeof sanitizedData[field] === 'string') {
|
|
182
|
+
sanitizedData[field] = sanitizeUserInput(sanitizedData[field] as string, options);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
data = sanitizedData;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Then validate with Zod schema
|
|
190
|
+
const result = schema.parse(data);
|
|
191
|
+
return { success: true, data: result };
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (error instanceof z.ZodError) {
|
|
194
|
+
return {
|
|
195
|
+
success: false,
|
|
196
|
+
error: error.errors.map(e => e.message).join(', ')
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
error: 'Validation failed'
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Re-export HTML sanitization from the consolidated module
|
|
207
|
+
// The new implementation allows safe HTML tags while removing dangerous ones
|
|
208
|
+
export { sanitizeHtml } from './htmlSanitization';
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Content Security Policy (CSP) utilities
|
|
212
|
+
*/
|
|
213
|
+
export const CSP_DIRECTIVES = {
|
|
214
|
+
default: "default-src 'self'",
|
|
215
|
+
script: "script-src 'self' 'unsafe-inline'",
|
|
216
|
+
style: "style-src 'self' 'unsafe-inline'",
|
|
217
|
+
img: "img-src 'self' data: https:",
|
|
218
|
+
font: "font-src 'self'",
|
|
219
|
+
connect: "connect-src 'self'",
|
|
220
|
+
frame: "frame-src 'none'"
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export function generateCSPHeader(customDirectives?: Partial<typeof CSP_DIRECTIVES>): string {
|
|
224
|
+
const directives = { ...CSP_DIRECTIVES, ...customDirectives };
|
|
225
|
+
return Object.values(directives).join('; ');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Rate limiting utilities
|
|
230
|
+
*/
|
|
231
|
+
export class RateLimiter {
|
|
232
|
+
private attempts: Map<string, { count: number; resetTime: number }> = new Map();
|
|
233
|
+
|
|
234
|
+
constructor(
|
|
235
|
+
private maxAttempts: number = 5,
|
|
236
|
+
private windowMs: number = 15 * 60 * 1000 // 15 minutes
|
|
237
|
+
) {}
|
|
238
|
+
|
|
239
|
+
isAllowed(identifier: string): boolean {
|
|
240
|
+
const now = Date.now();
|
|
241
|
+
const record = this.attempts.get(identifier);
|
|
242
|
+
|
|
243
|
+
if (!record || now > record.resetTime) {
|
|
244
|
+
this.attempts.set(identifier, { count: 1, resetTime: now + this.windowMs });
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (record.count >= this.maxAttempts) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
record.count++;
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
getRemainingAttempts(identifier: string): number {
|
|
257
|
+
const record = this.attempts.get(identifier);
|
|
258
|
+
if (!record || Date.now() > record.resetTime) {
|
|
259
|
+
return this.maxAttempts;
|
|
260
|
+
}
|
|
261
|
+
return Math.max(0, this.maxAttempts - record.count);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
reset(identifier: string): void {
|
|
265
|
+
this.attempts.delete(identifier);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Validation schemas (kept from previous version)
|
|
270
|
+
/**
|
|
271
|
+
* Enhanced email schema with security checks
|
|
272
|
+
*/
|
|
273
|
+
export const secureEmailSchema = z
|
|
274
|
+
.string()
|
|
275
|
+
.min(1, 'Email is required')
|
|
276
|
+
.email('Invalid email format')
|
|
277
|
+
.max(254, 'Email too long')
|
|
278
|
+
.refine(
|
|
279
|
+
(email) => {
|
|
280
|
+
if (!email || typeof email !== 'string') return false;
|
|
281
|
+
// Basic domain validation
|
|
282
|
+
const domain = email.split('@')[1];
|
|
283
|
+
return domain && domain.includes('.') && domain.length > 3;
|
|
284
|
+
},
|
|
285
|
+
'Invalid email domain'
|
|
286
|
+
)
|
|
287
|
+
.transform((email) => sanitizeEmail(email));
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Basic email schema for common use
|
|
291
|
+
*/
|
|
292
|
+
export const emailSchema = z
|
|
293
|
+
.string()
|
|
294
|
+
.min(1, 'Email is required')
|
|
295
|
+
.email('Invalid email format');
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Name validation schema
|
|
299
|
+
*/
|
|
300
|
+
export const nameSchema = z
|
|
301
|
+
.string()
|
|
302
|
+
.min(1, 'Name is required')
|
|
303
|
+
.max(100, 'Name too long')
|
|
304
|
+
.regex(/^[a-zA-Z\s'-]+$/, 'Name contains invalid characters');
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Phone validation schema
|
|
308
|
+
*/
|
|
309
|
+
export const phoneSchema = z
|
|
310
|
+
.string()
|
|
311
|
+
.regex(/^[\+]?[1-9][\d]{0,15}$/, 'Invalid phone number format');
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* URL validation schema
|
|
315
|
+
*/
|
|
316
|
+
export const urlSchema = z
|
|
317
|
+
.string()
|
|
318
|
+
.url('Invalid URL format');
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Date validation schema
|
|
322
|
+
*/
|
|
323
|
+
export const dateSchema = z
|
|
324
|
+
.string()
|
|
325
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Invalid date format (YYYY-MM-DD)');
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Secure login schema
|
|
329
|
+
*/
|
|
330
|
+
export const secureLoginSchema = z.object({
|
|
331
|
+
email: secureEmailSchema,
|
|
332
|
+
password: z.string().min(1, 'Password is required'),
|
|
333
|
+
});
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
2
1
|
/**
|
|
3
2
|
* @file Schema utility functions
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Utils/Validation/Schema
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Utility functions for working with Zod schemas.
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
import { z } from 'zod';
|
|
@@ -15,15 +19,15 @@ import { z } from 'zod';
|
|
|
15
19
|
export function pickSchema<T extends z.ZodObject<any, any, any>, K extends keyof z.infer<T>>(
|
|
16
20
|
schema: T,
|
|
17
21
|
keys: K[]
|
|
18
|
-
): z.ZodObject<
|
|
22
|
+
): z.ZodObject<Pick<z.infer<T>, K>> {
|
|
19
23
|
const shape = Object.entries(schema.shape)
|
|
20
24
|
.filter(([key]) => keys.includes(key as K))
|
|
21
25
|
.reduce((acc, [key, value]) => {
|
|
22
|
-
acc[key] = value as
|
|
26
|
+
(acc as Record<string, unknown>)[key] = value as unknown;
|
|
23
27
|
return acc;
|
|
24
|
-
}, {} as Record<string,
|
|
28
|
+
}, {} as Record<string, unknown>);
|
|
25
29
|
|
|
26
|
-
return z.object(shape)
|
|
30
|
+
return z.object(shape as Record<string, z.ZodTypeAny>) as z.ZodObject<Pick<z.infer<T>, K>>;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
/**
|
|
@@ -34,9 +38,10 @@ export function pickSchema<T extends z.ZodObject<any, any, any>, K extends keyof
|
|
|
34
38
|
*/
|
|
35
39
|
export function combineSchemas<T extends z.ZodObject<any, any, any>[]>(
|
|
36
40
|
schemas: T
|
|
37
|
-
): z.ZodObject<any> {
|
|
41
|
+
): z.ZodObject<any, any, any> {
|
|
38
42
|
return schemas.reduce(
|
|
39
43
|
(merged, schema) => merged.merge(schema),
|
|
40
44
|
z.object({})
|
|
41
45
|
);
|
|
42
46
|
}
|
|
47
|
+
|
|
@@ -110,6 +110,8 @@ export function sanitizeFilters(filters: Record<string, unknown>): Record<string
|
|
|
110
110
|
const keyValidation = sqlIdentifierSchema.safeParse(key);
|
|
111
111
|
if (!keyValidation.success) {
|
|
112
112
|
// Log warning for invalid filter keys
|
|
113
|
+
// Note: Using console.warn here is intentional for security events
|
|
114
|
+
// This should eventually use the security audit system
|
|
113
115
|
console.warn(`[SECURITY] Invalid filter key detected and removed: ${key}`);
|
|
114
116
|
continue;
|
|
115
117
|
}
|