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