@oneuptime/common 12.0.2 → 12.0.4
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/Models/DatabaseModels/CephCluster.ts +14 -0
- package/Models/DatabaseModels/CodeRepository.ts +14 -6
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/NetworkDevice.ts +14 -0
- package/Models/DatabaseModels/NetworkInterface.ts +18 -0
- package/Models/DatabaseModels/Project.ts +16 -2
- package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
- package/Models/DatabaseModels/Service.ts +30 -0
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +31 -0
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +458 -12
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +38 -4
- package/Server/Infrastructure/GlobalCache.ts +152 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/AnalyticsDatabaseService.ts +72 -1
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +296 -2
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/OpenTelemetryIngestService.ts +230 -26
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Services/UserService.ts +184 -0
- package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
- package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/PasswordHash.ts +306 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
- package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
- package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
- package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
- package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
- package/Tests/Server/API/Helpers.ts +6 -1
- package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
- package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
- package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
- package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
- package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
- package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/ColumnLength.test.ts +7 -1
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/ColumnLength.ts +9 -1
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/Decimal.ts +16 -4
- package/Types/HashedString.ts +139 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/UI/Components/Navbar/NavBar.tsx +12 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
- package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/Utils/TeamMembersByProject.ts +237 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +16 -2
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +28 -1
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +32 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +343 -10
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +94 -167
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +187 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +34 -4
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +246 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +165 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
- package/build/dist/Server/Utils/PasswordHash.js +237 -0
- package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
- package/build/dist/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
- package/build/dist/Types/Database/ColumnLength.js +9 -1
- package/build/dist/Types/Database/ColumnLength.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
- package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/HashedString.js +95 -5
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { WidenHashedStringColumnsForScrypt1786023262402 } from "../../../Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt";
|
|
2
|
+
import SchemaMigrations from "../../../Server/Infrastructure/Postgres/SchemaMigrations/Index";
|
|
3
|
+
import ColumnLength from "../../../Types/Database/ColumnLength";
|
|
4
|
+
import { QueryRunner } from "typeorm";
|
|
5
|
+
import { describe, expect, test } from "@jest/globals";
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* The migration that widens every HashedString column so a scrypt hash, which
|
|
9
|
+
* carries its own cost parameters, fits.
|
|
10
|
+
*
|
|
11
|
+
* THE REASON THIS FILE EXISTS.
|
|
12
|
+
*
|
|
13
|
+
* `typeorm migration:generate` does not express a varchar length change as
|
|
14
|
+
* ALTER COLUMN TYPE. It emits DROP COLUMN followed by ADD COLUMN — which for
|
|
15
|
+
* these six columns means deleting every password, every status page and
|
|
16
|
+
* dashboard master password, and every active session in the instance. The
|
|
17
|
+
* generated file was replaced by hand for exactly that reason.
|
|
18
|
+
*
|
|
19
|
+
* Anyone who regenerates this migration (or writes the next widening the same
|
|
20
|
+
* way) gets the destructive version back, and it looks perfectly ordinary in
|
|
21
|
+
* review. The first test below is the guard: no statement in this migration
|
|
22
|
+
* may drop a column.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
type MakeQueryRunnerResult = {
|
|
26
|
+
runner: QueryRunner;
|
|
27
|
+
statements: Array<string>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type MakeQueryRunnerFunction = () => MakeQueryRunnerResult;
|
|
31
|
+
|
|
32
|
+
const makeQueryRunner: MakeQueryRunnerFunction = (): MakeQueryRunnerResult => {
|
|
33
|
+
const statements: Array<string> = [];
|
|
34
|
+
|
|
35
|
+
const query: (...args: Array<unknown>) => Promise<undefined> = (
|
|
36
|
+
...args: Array<unknown>
|
|
37
|
+
): Promise<undefined> => {
|
|
38
|
+
statements.push(String(args[0]));
|
|
39
|
+
return Promise.resolve(undefined);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
runner: { query } as unknown as QueryRunner,
|
|
44
|
+
statements,
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const WIDENED: ReadonlyArray<[string, string]> = [
|
|
49
|
+
["User", "password"],
|
|
50
|
+
["StatusPagePrivateUser", "password"],
|
|
51
|
+
["StatusPage", "masterPassword"],
|
|
52
|
+
["Dashboard", "masterPassword"],
|
|
53
|
+
["UserSession", "refreshToken"],
|
|
54
|
+
["StatusPagePrivateUserSession", "refreshToken"],
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
describe("WidenHashedStringColumnsForScrypt — must not destroy data", () => {
|
|
58
|
+
test("up() never drops a column", async () => {
|
|
59
|
+
/*
|
|
60
|
+
* The whole point. A DROP COLUMN here silently deletes every credential
|
|
61
|
+
* in the database, and the migration would still 'succeed'.
|
|
62
|
+
*/
|
|
63
|
+
const { runner, statements } = makeQueryRunner();
|
|
64
|
+
|
|
65
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
|
|
66
|
+
|
|
67
|
+
for (const statement of statements) {
|
|
68
|
+
expect(statement).not.toMatch(/DROP COLUMN/i);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("down() never drops a column either", async () => {
|
|
73
|
+
const { runner, statements } = makeQueryRunner();
|
|
74
|
+
|
|
75
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().down(runner);
|
|
76
|
+
|
|
77
|
+
for (const statement of statements) {
|
|
78
|
+
expect(statement).not.toMatch(/DROP COLUMN/i);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("up() is nothing but ALTER COLUMN TYPE statements", async () => {
|
|
83
|
+
const { runner, statements } = makeQueryRunner();
|
|
84
|
+
|
|
85
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
|
|
86
|
+
|
|
87
|
+
for (const statement of statements) {
|
|
88
|
+
expect(statement).toMatch(
|
|
89
|
+
/^ALTER TABLE "[A-Za-z]+" ALTER COLUMN "[A-Za-z]+" TYPE character varying\(\d+\)$/,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("it does not touch constraints or indexes", async () => {
|
|
95
|
+
/*
|
|
96
|
+
* The generated version dropped and recreated the refresh-token unique
|
|
97
|
+
* constraints and indexes purely to work around its own DROP COLUMN.
|
|
98
|
+
* Widening a varchar needs no table rewrite, so the indexes survive
|
|
99
|
+
* untouched and none of that churn is necessary.
|
|
100
|
+
*/
|
|
101
|
+
const { runner, statements } = makeQueryRunner();
|
|
102
|
+
|
|
103
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
|
|
104
|
+
|
|
105
|
+
for (const statement of statements) {
|
|
106
|
+
expect(statement).not.toMatch(/CONSTRAINT/i);
|
|
107
|
+
expect(statement).not.toMatch(/INDEX/i);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("WidenHashedStringColumnsForScrypt — SQL contract", () => {
|
|
113
|
+
test("up() widens exactly the six HashedString columns", async () => {
|
|
114
|
+
const { runner, statements } = makeQueryRunner();
|
|
115
|
+
|
|
116
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
|
|
117
|
+
|
|
118
|
+
expect(statements).toHaveLength(WIDENED.length);
|
|
119
|
+
|
|
120
|
+
for (const [table, column] of WIDENED) {
|
|
121
|
+
expect(statements).toContain(
|
|
122
|
+
`ALTER TABLE "${table}" ALTER COLUMN "${column}" TYPE character varying(255)`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("it widens to whatever ColumnLength.HashedString says", async () => {
|
|
128
|
+
/*
|
|
129
|
+
* The entity metadata and the migration have to agree, or the drift check
|
|
130
|
+
* fails and the next generated migration tries to 'fix' it.
|
|
131
|
+
*/
|
|
132
|
+
const { runner, statements } = makeQueryRunner();
|
|
133
|
+
|
|
134
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
|
|
135
|
+
|
|
136
|
+
for (const statement of statements) {
|
|
137
|
+
expect(statement).toContain(
|
|
138
|
+
`character varying(${ColumnLength.HashedString})`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("down() returns the same six columns to 64", async () => {
|
|
144
|
+
const { runner, statements } = makeQueryRunner();
|
|
145
|
+
|
|
146
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().down(runner);
|
|
147
|
+
|
|
148
|
+
expect(statements).toHaveLength(WIDENED.length);
|
|
149
|
+
|
|
150
|
+
for (const [table, column] of WIDENED) {
|
|
151
|
+
expect(statements).toContain(
|
|
152
|
+
`ALTER TABLE "${table}" ALTER COLUMN "${column}" TYPE character varying(64)`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("down() reverses up()'s order", async () => {
|
|
158
|
+
const up: MakeQueryRunnerResult = makeQueryRunner();
|
|
159
|
+
const down: MakeQueryRunnerResult = makeQueryRunner();
|
|
160
|
+
|
|
161
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().up(up.runner);
|
|
162
|
+
await new WidenHashedStringColumnsForScrypt1786023262402().down(
|
|
163
|
+
down.runner,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
type ColumnsFunction = (statements: Array<string>) => Array<string>;
|
|
167
|
+
|
|
168
|
+
const columns: ColumnsFunction = (
|
|
169
|
+
statements: Array<string>,
|
|
170
|
+
): Array<string> => {
|
|
171
|
+
return statements.map((statement: string) => {
|
|
172
|
+
return statement
|
|
173
|
+
.match(/ALTER TABLE "([^"]+)" ALTER COLUMN "([^"]+)"/)!
|
|
174
|
+
.slice(1, 3)
|
|
175
|
+
.join(".");
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
expect(columns(down.statements)).toEqual(columns(up.statements).reverse());
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("the widened column holds a scrypt hash with room to spare", () => {
|
|
183
|
+
// `scrypt$N=16384,r=8,p=1$` + 64 hex characters.
|
|
184
|
+
const worstCaseToday: number = "scrypt$N=1048576,r=32,p=16$".length + 64;
|
|
185
|
+
|
|
186
|
+
expect(ColumnLength.HashedString).toBeGreaterThan(worstCaseToday);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("WidenHashedStringColumnsForScrypt — registration", () => {
|
|
191
|
+
test("is registered in SchemaMigrations/Index.ts", () => {
|
|
192
|
+
expect(SchemaMigrations).toContain(
|
|
193
|
+
WidenHashedStringColumnsForScrypt1786023262402,
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("appears exactly once", () => {
|
|
198
|
+
const occurrences: number = SchemaMigrations.filter(
|
|
199
|
+
(migration: { name: string }) => {
|
|
200
|
+
return (
|
|
201
|
+
migration.name === "WidenHashedStringColumnsForScrypt1786023262402"
|
|
202
|
+
);
|
|
203
|
+
},
|
|
204
|
+
).length;
|
|
205
|
+
|
|
206
|
+
expect(occurrences).toBe(1);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("runs after the migration that added the salt columns", () => {
|
|
210
|
+
const names: Array<string> = SchemaMigrations.map(
|
|
211
|
+
(migration: { name: string }) => {
|
|
212
|
+
return migration.name;
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expect(
|
|
217
|
+
names.indexOf("WidenHashedStringColumnsForScrypt1786023262402"),
|
|
218
|
+
).toBeGreaterThan(names.indexOf("AddPerUserPasswordSalt1786018109307"));
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -15,7 +15,8 @@ import CodeRepositoryModel from "../../../../Models/DatabaseModels/CodeRepositor
|
|
|
15
15
|
import TelemetryException from "../../../../Models/DatabaseModels/TelemetryException";
|
|
16
16
|
import CodeRepositoryType from "../../../../Types/CodeRepository/CodeRepositoryType";
|
|
17
17
|
import ObjectID from "../../../../Types/ObjectID";
|
|
18
|
-
import
|
|
18
|
+
import GitHubInstallationBinding from "../../../../Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding";
|
|
19
|
+
import { describe, expect, test, afterEach, beforeEach } from "@jest/globals";
|
|
19
20
|
|
|
20
21
|
/*
|
|
21
22
|
* The code tools are the bridge from a telemetry signal to the source that
|
|
@@ -86,6 +87,19 @@ function mockFileContent(content: string): void {
|
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/*
|
|
91
|
+
* findReadableRepositories now also proves each row's GitHub App installation
|
|
92
|
+
* belongs to the row's project (GHSA-xx95-gmcf-7q86), which is a database
|
|
93
|
+
* read. That guard has its own tests; here it would just be a Postgres
|
|
94
|
+
* connection these tool tests do not need, so treat every fixture row as
|
|
95
|
+
* bound.
|
|
96
|
+
*/
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
jest
|
|
99
|
+
.spyOn(GitHubInstallationBinding, "getBoundInstallationId")
|
|
100
|
+
.mockResolvedValue("12345");
|
|
101
|
+
});
|
|
102
|
+
|
|
89
103
|
afterEach(() => {
|
|
90
104
|
jest.restoreAllMocks();
|
|
91
105
|
});
|
|
@@ -119,6 +133,38 @@ describe("list_code_repositories", () => {
|
|
|
119
133
|
expect(result.rowCount).toBe(1);
|
|
120
134
|
});
|
|
121
135
|
|
|
136
|
+
/*
|
|
137
|
+
* GHSA-xx95-gmcf-7q86. A row can be in this project and still name a GitHub
|
|
138
|
+
* App installation the project does not own — that combination used to be
|
|
139
|
+
* creatable, and these tools would then read, commit and open pull requests
|
|
140
|
+
* in another tenant's private repositories. Being in the project is not the
|
|
141
|
+
* same as owning the installation, so an unbound row must be invisible here
|
|
142
|
+
* exactly like an uninstalled one.
|
|
143
|
+
*/
|
|
144
|
+
test("hides a repository whose installation is not bound to the project", async () => {
|
|
145
|
+
jest
|
|
146
|
+
.spyOn(GitHubInstallationBinding, "getBoundInstallationId")
|
|
147
|
+
.mockResolvedValue("12345");
|
|
148
|
+
|
|
149
|
+
mockRepositories([
|
|
150
|
+
buildRepository({ name: "our-repo", installationId: "12345" }),
|
|
151
|
+
buildRepository({
|
|
152
|
+
id: ObjectID.generate(),
|
|
153
|
+
name: "someone-elses-repo",
|
|
154
|
+
installationId: "99999999",
|
|
155
|
+
}),
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
const result: ToolExecutionResult = await ListCodeRepositoriesTool.execute(
|
|
159
|
+
{},
|
|
160
|
+
ctx,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
expect(result.dataForLlm).toContain("our-repo");
|
|
164
|
+
expect(result.dataForLlm).not.toContain("someone-elses-repo");
|
|
165
|
+
expect(result.rowCount).toBe(1);
|
|
166
|
+
});
|
|
167
|
+
|
|
122
168
|
test("says plainly when no repository is connected", async () => {
|
|
123
169
|
mockRepositories([]);
|
|
124
170
|
|
|
@@ -23,6 +23,7 @@ import AIAgentTaskPullRequest from "../../../../Models/DatabaseModels/AIAgentTas
|
|
|
23
23
|
import CodeRepositoryType from "../../../../Types/CodeRepository/CodeRepositoryType";
|
|
24
24
|
import PullRequestState from "../../../../Types/CodeRepository/PullRequestState";
|
|
25
25
|
import ObjectID from "../../../../Types/ObjectID";
|
|
26
|
+
import GitHubInstallationBinding from "../../../../Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding";
|
|
26
27
|
import { describe, expect, test, afterEach, beforeEach } from "@jest/globals";
|
|
27
28
|
|
|
28
29
|
/*
|
|
@@ -68,6 +69,15 @@ beforeEach(() => {
|
|
|
68
69
|
.spyOn(CodeRepositoryService, "findBy")
|
|
69
70
|
.mockResolvedValue([buildRepository()] as never);
|
|
70
71
|
jest.spyOn(GitHubUtil, "getDefaultBranchName").mockResolvedValue("main");
|
|
72
|
+
|
|
73
|
+
/*
|
|
74
|
+
* resolveTargetRepository now proves each row's GitHub App installation
|
|
75
|
+
* belongs to the row's project (GHSA-xx95-gmcf-7q86), which is a database
|
|
76
|
+
* read. That guard has its own tests; treat the fixture row as bound.
|
|
77
|
+
*/
|
|
78
|
+
jest
|
|
79
|
+
.spyOn(GitHubInstallationBinding, "getBoundInstallationId")
|
|
80
|
+
.mockResolvedValue("12345");
|
|
71
81
|
});
|
|
72
82
|
|
|
73
83
|
afterEach(() => {
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAll,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
it,
|
|
7
|
+
jest,
|
|
8
|
+
} from "@jest/globals";
|
|
9
|
+
import APIKeyAccessPermission from "../../../../Server/Utils/APIKey/AccessPermission";
|
|
10
|
+
import ApiKeyPermissionService from "../../../../Server/Services/ApiKeyPermissionService";
|
|
11
|
+
import APIKeyPermission from "../../../../Models/DatabaseModels/ApiKeyPermission";
|
|
12
|
+
import Label from "../../../../Models/DatabaseModels/Label";
|
|
13
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
14
|
+
import Permission, {
|
|
15
|
+
UserGlobalAccessPermission,
|
|
16
|
+
UserPermission,
|
|
17
|
+
UserTenantAccessPermission,
|
|
18
|
+
} from "../../../../Types/Permission";
|
|
19
|
+
import { getJestSpyOn } from "../../../Spy";
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* This class turns an API key into the permission set a request runs under, so
|
|
23
|
+
* every method here is a privilege-granting decision. The security invariant
|
|
24
|
+
* that must hold across all of them: a *default* (ordinary) API key is never
|
|
25
|
+
* silently handed ProjectOwner, while a *master* key deliberately is. A
|
|
26
|
+
* regression that leaks ProjectOwner into the default path would give every API
|
|
27
|
+
* key full control of the project; one that dropped it from the master path
|
|
28
|
+
* would break trusted internal automation. Both directions are pinned below.
|
|
29
|
+
*
|
|
30
|
+
* getApiTenantAccessPermission also reshapes stored permission rows into the
|
|
31
|
+
* runtime UserPermission shape (labels -> labelIds, block flag preserved). That
|
|
32
|
+
* mapping is where a dropped label would quietly widen a scoped permission, so
|
|
33
|
+
* it is tested against a spied service rather than a live database.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
37
|
+
const apiKeyId: ObjectID = ObjectID.generate();
|
|
38
|
+
|
|
39
|
+
const permissionValues: (
|
|
40
|
+
permissions: Array<UserPermission>,
|
|
41
|
+
) => Array<Permission> = (
|
|
42
|
+
permissions: Array<UserPermission>,
|
|
43
|
+
): Array<Permission> => {
|
|
44
|
+
return permissions.map((p: UserPermission) => {
|
|
45
|
+
return p.permission;
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
describe("APIKeyAccessPermission.getDefaultApiGlobalPermission", () => {
|
|
50
|
+
it("grants Public, User and CurrentUser for the project", async () => {
|
|
51
|
+
const perm: UserGlobalAccessPermission =
|
|
52
|
+
await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
|
|
53
|
+
|
|
54
|
+
expect(perm._type).toBe("UserGlobalAccessPermission");
|
|
55
|
+
expect(
|
|
56
|
+
perm.projectIds.map((p: ObjectID) => {
|
|
57
|
+
return p.toString();
|
|
58
|
+
}),
|
|
59
|
+
).toEqual([projectId.toString()]);
|
|
60
|
+
expect(perm.globalPermissions).toEqual([
|
|
61
|
+
Permission.Public,
|
|
62
|
+
Permission.User,
|
|
63
|
+
Permission.CurrentUser,
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("does NOT grant ProjectOwner to an ordinary key", async () => {
|
|
68
|
+
// The core boundary: a default key must never be an owner.
|
|
69
|
+
const perm: UserGlobalAccessPermission =
|
|
70
|
+
await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
|
|
71
|
+
expect(perm.globalPermissions).not.toContain(Permission.ProjectOwner);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("APIKeyAccessPermission.getMasterKeyApiGlobalPermission", () => {
|
|
76
|
+
it("grants everything the default key has, plus ProjectOwner", async () => {
|
|
77
|
+
const master: UserGlobalAccessPermission =
|
|
78
|
+
await APIKeyAccessPermission.getMasterKeyApiGlobalPermission(projectId);
|
|
79
|
+
const def: UserGlobalAccessPermission =
|
|
80
|
+
await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
|
|
81
|
+
|
|
82
|
+
// Superset: no default grant is lost when elevating to master.
|
|
83
|
+
for (const p of def.globalPermissions) {
|
|
84
|
+
expect(master.globalPermissions).toContain(p);
|
|
85
|
+
}
|
|
86
|
+
// The one deliberate elevation.
|
|
87
|
+
expect(master.globalPermissions).toContain(Permission.ProjectOwner);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("scopes to exactly the one project", async () => {
|
|
91
|
+
const master: UserGlobalAccessPermission =
|
|
92
|
+
await APIKeyAccessPermission.getMasterKeyApiGlobalPermission(projectId);
|
|
93
|
+
expect(
|
|
94
|
+
master.projectIds.map((p: ObjectID) => {
|
|
95
|
+
return p.toString();
|
|
96
|
+
}),
|
|
97
|
+
).toEqual([projectId.toString()]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("APIKeyAccessPermission.getMasterApiTenantAccessPermission", () => {
|
|
102
|
+
it("elevates the default tenant permission with ProjectOwner", async () => {
|
|
103
|
+
const perm: UserTenantAccessPermission =
|
|
104
|
+
await APIKeyAccessPermission.getMasterApiTenantAccessPermission(
|
|
105
|
+
projectId,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(perm._type).toBe("UserTenantAccessPermission");
|
|
109
|
+
expect(perm.projectId.toString()).toBe(projectId.toString());
|
|
110
|
+
expect(permissionValues(perm.permissions)).toContain(
|
|
111
|
+
Permission.ProjectOwner,
|
|
112
|
+
);
|
|
113
|
+
// The default tenant baseline is retained alongside the elevation.
|
|
114
|
+
expect(permissionValues(perm.permissions)).toContain(
|
|
115
|
+
Permission.CurrentUser,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("APIKeyAccessPermission.getApiTenantAccessPermission", () => {
|
|
121
|
+
const spyFindBy: jest.SpyInstance = getJestSpyOn(
|
|
122
|
+
ApiKeyPermissionService,
|
|
123
|
+
"findBy",
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
jest.clearAllMocks();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
afterAll(() => {
|
|
131
|
+
jest.restoreAllMocks();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const makeRow: (data: {
|
|
135
|
+
permission: Permission;
|
|
136
|
+
labelIds?: Array<ObjectID>;
|
|
137
|
+
isBlockPermission?: boolean;
|
|
138
|
+
}) => APIKeyPermission = (data: {
|
|
139
|
+
permission: Permission;
|
|
140
|
+
labelIds?: Array<ObjectID>;
|
|
141
|
+
isBlockPermission?: boolean;
|
|
142
|
+
}): APIKeyPermission => {
|
|
143
|
+
const row: APIKeyPermission = new APIKeyPermission();
|
|
144
|
+
row.permission = data.permission;
|
|
145
|
+
if (data.labelIds) {
|
|
146
|
+
row.labels = data.labelIds.map((id: ObjectID) => {
|
|
147
|
+
const label: Label = new Label();
|
|
148
|
+
label.id = id;
|
|
149
|
+
return label;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
row.isBlockPermission = data.isBlockPermission ?? false;
|
|
153
|
+
return row;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
it("queries the permissions for exactly this api key", async () => {
|
|
157
|
+
spyFindBy.mockResolvedValue([] as never);
|
|
158
|
+
|
|
159
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
160
|
+
projectId,
|
|
161
|
+
apiKeyId,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(spyFindBy).toHaveBeenCalledTimes(1);
|
|
165
|
+
const arg: { query: { apiKeyId: ObjectID } } = spyFindBy.mock
|
|
166
|
+
.calls[0]![0] as { query: { apiKeyId: ObjectID } };
|
|
167
|
+
expect(arg.query.apiKeyId.toString()).toBe(apiKeyId.toString());
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("returns just the default baseline when the key has no permissions", async () => {
|
|
171
|
+
spyFindBy.mockResolvedValue([] as never);
|
|
172
|
+
|
|
173
|
+
const perm: UserTenantAccessPermission =
|
|
174
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
175
|
+
projectId,
|
|
176
|
+
apiKeyId,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// No stored rows -> no ProjectOwner. An empty key is not an owner.
|
|
180
|
+
expect(permissionValues(perm.permissions)).not.toContain(
|
|
181
|
+
Permission.ProjectOwner,
|
|
182
|
+
);
|
|
183
|
+
expect(permissionValues(perm.permissions)).toContain(
|
|
184
|
+
Permission.CurrentUser,
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("maps each stored permission row onto the tenant permission", async () => {
|
|
189
|
+
const labelId: ObjectID = ObjectID.generate();
|
|
190
|
+
spyFindBy.mockResolvedValue([
|
|
191
|
+
makeRow({
|
|
192
|
+
permission: Permission.CreateProjectIncident,
|
|
193
|
+
labelIds: [labelId],
|
|
194
|
+
isBlockPermission: false,
|
|
195
|
+
}),
|
|
196
|
+
] as never);
|
|
197
|
+
|
|
198
|
+
const perm: UserTenantAccessPermission =
|
|
199
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
200
|
+
projectId,
|
|
201
|
+
apiKeyId,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const mapped: UserPermission | undefined = perm.permissions.find(
|
|
205
|
+
(p: UserPermission) => {
|
|
206
|
+
return p.permission === Permission.CreateProjectIncident;
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
expect(mapped).toBeDefined();
|
|
210
|
+
/*
|
|
211
|
+
* The label scope must survive the mapping, or the permission silently
|
|
212
|
+
* widens from "these labels" to "all".
|
|
213
|
+
*/
|
|
214
|
+
expect(
|
|
215
|
+
mapped!.labelIds.map((l: ObjectID) => {
|
|
216
|
+
return l.toString();
|
|
217
|
+
}),
|
|
218
|
+
).toEqual([labelId.toString()]);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("preserves the block flag so a deny row stays a deny row", async () => {
|
|
222
|
+
spyFindBy.mockResolvedValue([
|
|
223
|
+
makeRow({
|
|
224
|
+
permission: Permission.CreateProjectIncident,
|
|
225
|
+
labelIds: [],
|
|
226
|
+
isBlockPermission: true,
|
|
227
|
+
}),
|
|
228
|
+
] as never);
|
|
229
|
+
|
|
230
|
+
const perm: UserTenantAccessPermission =
|
|
231
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
232
|
+
projectId,
|
|
233
|
+
apiKeyId,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const mapped: UserPermission | undefined = perm.permissions.find(
|
|
237
|
+
(p: UserPermission) => {
|
|
238
|
+
return p.permission === Permission.CreateProjectIncident;
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
// Flipping a block permission to an allow would invert an admin's intent.
|
|
242
|
+
expect(mapped!.isBlockPermission).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("treats a row with no labels as an empty (unscoped) label set", async () => {
|
|
246
|
+
const row: APIKeyPermission = new APIKeyPermission();
|
|
247
|
+
row.permission = Permission.CreateProjectIncident;
|
|
248
|
+
// labels intentionally left undefined.
|
|
249
|
+
row.isBlockPermission = false;
|
|
250
|
+
spyFindBy.mockResolvedValue([row] as never);
|
|
251
|
+
|
|
252
|
+
const perm: UserTenantAccessPermission =
|
|
253
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
254
|
+
projectId,
|
|
255
|
+
apiKeyId,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const mapped: UserPermission | undefined = perm.permissions.find(
|
|
259
|
+
(p: UserPermission) => {
|
|
260
|
+
return p.permission === Permission.CreateProjectIncident;
|
|
261
|
+
},
|
|
262
|
+
);
|
|
263
|
+
expect(mapped).toBeDefined();
|
|
264
|
+
// No crash on undefined labels, and it becomes [] rather than undefined.
|
|
265
|
+
expect(mapped!.labelIds).toEqual([]);
|
|
266
|
+
});
|
|
267
|
+
});
|