@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,305 @@
|
|
|
1
|
+
import CodeRepositoryService from "../../../Server/Services/CodeRepositoryService";
|
|
2
|
+
import GitHubInstallationBinding from "../../../Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding";
|
|
3
|
+
import CodeRepository from "../../../Models/DatabaseModels/CodeRepository";
|
|
4
|
+
import CodeRepositoryType from "../../../Types/CodeRepository/CodeRepositoryType";
|
|
5
|
+
import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
6
|
+
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
7
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
8
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* GHSA-xx95-gmcf-7q86, defence in depth.
|
|
13
|
+
*
|
|
14
|
+
* Closing the column ACL stops requests that go through permission checks,
|
|
15
|
+
* but every writer of this column writes with `props: { isRoot: true }`, which
|
|
16
|
+
* skips those checks entirely. These hooks run regardless of isRoot, so a
|
|
17
|
+
* route that writes a repository row carrying an installation ID its project
|
|
18
|
+
* does not own is refused at the service boundary — before the row exists to
|
|
19
|
+
* be traded for a token.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
type ServiceInternals = {
|
|
23
|
+
onBeforeCreate: (createBy: CreateBy<CodeRepository>) => Promise<unknown>;
|
|
24
|
+
onBeforeUpdate: (updateBy: UpdateBy<CodeRepository>) => Promise<unknown>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const service: ServiceInternals =
|
|
28
|
+
CodeRepositoryService as unknown as ServiceInternals;
|
|
29
|
+
|
|
30
|
+
describe("CodeRepositoryService installation-to-project binding", () => {
|
|
31
|
+
let ownProjectId: ObjectID;
|
|
32
|
+
let victimInstallationId: string;
|
|
33
|
+
let ownInstallationId: string;
|
|
34
|
+
let bindingSpy: jest.SpyInstance;
|
|
35
|
+
let findBySpy: jest.SpyInstance;
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
ownProjectId = ObjectID.generate();
|
|
39
|
+
ownInstallationId = "11111111";
|
|
40
|
+
victimInstallationId = "99999999";
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* Model the real helper: bound only for (ownProjectId, ownInstallationId).
|
|
44
|
+
*/
|
|
45
|
+
bindingSpy = jest
|
|
46
|
+
.spyOn(GitHubInstallationBinding, "isInstallationBoundToProject")
|
|
47
|
+
.mockImplementation(
|
|
48
|
+
async (data: {
|
|
49
|
+
projectId: ObjectID;
|
|
50
|
+
installationId: string;
|
|
51
|
+
}): Promise<boolean> => {
|
|
52
|
+
return (
|
|
53
|
+
data.projectId.toString() === ownProjectId.toString() &&
|
|
54
|
+
data.installationId === ownInstallationId
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
findBySpy = jest.spyOn(CodeRepositoryService, "findBy");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
jest.restoreAllMocks();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function buildCreateBy(data: {
|
|
67
|
+
projectId?: ObjectID | undefined;
|
|
68
|
+
installationId?: string | undefined;
|
|
69
|
+
tenantId?: ObjectID | undefined;
|
|
70
|
+
}): CreateBy<CodeRepository> {
|
|
71
|
+
const repository: CodeRepository = new CodeRepository();
|
|
72
|
+
repository.name = "my-service";
|
|
73
|
+
repository.repositoryHostedAt = CodeRepositoryType.GitHub;
|
|
74
|
+
repository.organizationName = "my-org";
|
|
75
|
+
repository.repositoryName = "my-service";
|
|
76
|
+
|
|
77
|
+
if (data.projectId) {
|
|
78
|
+
repository.projectId = data.projectId;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (data.installationId !== undefined) {
|
|
82
|
+
repository.gitHubAppInstallationId = data.installationId;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
data: repository,
|
|
87
|
+
props: {
|
|
88
|
+
isRoot: true,
|
|
89
|
+
tenantId: data.tenantId,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
describe("onBeforeCreate", () => {
|
|
95
|
+
/*
|
|
96
|
+
* The exploit's step 2: a row in the attacker's OWN project naming the
|
|
97
|
+
* victim's installation. Every later authorization check compares the
|
|
98
|
+
* row's projectId to the caller's tenant and passes, so this is the last
|
|
99
|
+
* place the lie can be caught.
|
|
100
|
+
*/
|
|
101
|
+
test("rejects a row in your own project that names another tenant's installation", async () => {
|
|
102
|
+
await expect(
|
|
103
|
+
service.onBeforeCreate(
|
|
104
|
+
buildCreateBy({
|
|
105
|
+
projectId: ownProjectId,
|
|
106
|
+
installationId: victimInstallationId,
|
|
107
|
+
}),
|
|
108
|
+
),
|
|
109
|
+
).rejects.toBeInstanceOf(BadDataException);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("rejects even when the write is made as root", async () => {
|
|
113
|
+
const createBy: CreateBy<CodeRepository> = buildCreateBy({
|
|
114
|
+
projectId: ownProjectId,
|
|
115
|
+
installationId: victimInstallationId,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(createBy.props.isRoot).toBe(true);
|
|
119
|
+
|
|
120
|
+
await expect(service.onBeforeCreate(createBy)).rejects.toBeInstanceOf(
|
|
121
|
+
BadDataException,
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("allows the legitimate import: an installation the project owns", async () => {
|
|
126
|
+
await expect(
|
|
127
|
+
service.onBeforeCreate(
|
|
128
|
+
buildCreateBy({
|
|
129
|
+
projectId: ownProjectId,
|
|
130
|
+
installationId: ownInstallationId,
|
|
131
|
+
}),
|
|
132
|
+
),
|
|
133
|
+
).resolves.toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Repositories connected by other means (GitLab, manual) are unaffected.
|
|
137
|
+
test("allows a row with no installation id at all and never checks a binding", async () => {
|
|
138
|
+
await expect(
|
|
139
|
+
service.onBeforeCreate(buildCreateBy({ projectId: ownProjectId })),
|
|
140
|
+
).resolves.toBeDefined();
|
|
141
|
+
|
|
142
|
+
expect(bindingSpy).not.toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("checks the binding against the row's own project", async () => {
|
|
146
|
+
await service.onBeforeCreate(
|
|
147
|
+
buildCreateBy({
|
|
148
|
+
projectId: ownProjectId,
|
|
149
|
+
installationId: ownInstallationId,
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
expect(bindingSpy).toHaveBeenCalledTimes(1);
|
|
154
|
+
|
|
155
|
+
const args: { projectId: ObjectID; installationId: string } = bindingSpy
|
|
156
|
+
.mock.calls[0]![0] as {
|
|
157
|
+
projectId: ObjectID;
|
|
158
|
+
installationId: string;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
expect(args.projectId.toString()).toBe(ownProjectId.toString());
|
|
162
|
+
expect(args.installationId).toBe(ownInstallationId);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Falls back to the tenant when the row does not carry its project inline.
|
|
166
|
+
test("uses props.tenantId when the row has no projectId", async () => {
|
|
167
|
+
await expect(
|
|
168
|
+
service.onBeforeCreate(
|
|
169
|
+
buildCreateBy({
|
|
170
|
+
tenantId: ownProjectId,
|
|
171
|
+
installationId: ownInstallationId,
|
|
172
|
+
}),
|
|
173
|
+
),
|
|
174
|
+
).resolves.toBeDefined();
|
|
175
|
+
|
|
176
|
+
const args: { projectId: ObjectID } = bindingSpy.mock.calls[0]![0] as {
|
|
177
|
+
projectId: ObjectID;
|
|
178
|
+
};
|
|
179
|
+
expect(args.projectId.toString()).toBe(ownProjectId.toString());
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
/*
|
|
183
|
+
* A projectless row would otherwise skip the check entirely and leave an
|
|
184
|
+
* unattributable installation ID in the table.
|
|
185
|
+
*/
|
|
186
|
+
test("rejects an installation id with no project to check it against", async () => {
|
|
187
|
+
await expect(
|
|
188
|
+
service.onBeforeCreate(
|
|
189
|
+
buildCreateBy({ installationId: ownInstallationId }),
|
|
190
|
+
),
|
|
191
|
+
).rejects.toBeInstanceOf(BadDataException);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe("onBeforeUpdate", () => {
|
|
196
|
+
function buildUpdateBy(
|
|
197
|
+
installationId: string | null,
|
|
198
|
+
): UpdateBy<CodeRepository> {
|
|
199
|
+
return {
|
|
200
|
+
query: { projectId: ownProjectId },
|
|
201
|
+
data: {
|
|
202
|
+
gitHubAppInstallationId: installationId,
|
|
203
|
+
} as unknown as CodeRepository,
|
|
204
|
+
limit: 100,
|
|
205
|
+
skip: 0,
|
|
206
|
+
props: { isRoot: true },
|
|
207
|
+
} as unknown as UpdateBy<CodeRepository>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function mockAffectedRows(projectIds: Array<ObjectID | undefined>): void {
|
|
211
|
+
findBySpy.mockResolvedValue(
|
|
212
|
+
projectIds.map((projectId: ObjectID | undefined) => {
|
|
213
|
+
const repository: CodeRepository = new CodeRepository();
|
|
214
|
+
repository.id = ObjectID.generate();
|
|
215
|
+
if (projectId) {
|
|
216
|
+
repository.projectId = projectId;
|
|
217
|
+
}
|
|
218
|
+
return repository;
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
test("rejects repointing an existing row at another tenant's installation", async () => {
|
|
224
|
+
mockAffectedRows([ownProjectId]);
|
|
225
|
+
|
|
226
|
+
await expect(
|
|
227
|
+
service.onBeforeUpdate(buildUpdateBy(victimInstallationId)),
|
|
228
|
+
).rejects.toBeInstanceOf(BadDataException);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("allows setting the installation the row's project owns", async () => {
|
|
232
|
+
mockAffectedRows([ownProjectId]);
|
|
233
|
+
|
|
234
|
+
await expect(
|
|
235
|
+
service.onBeforeUpdate(buildUpdateBy(ownInstallationId)),
|
|
236
|
+
).resolves.toBeDefined();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
/*
|
|
240
|
+
* This is how the `installation.deleted` webhook tears a disconnected
|
|
241
|
+
* installation out of the database — it must never be blocked.
|
|
242
|
+
*/
|
|
243
|
+
test("always allows clearing the installation id", async () => {
|
|
244
|
+
await expect(
|
|
245
|
+
service.onBeforeUpdate(buildUpdateBy(null)),
|
|
246
|
+
).resolves.toBeDefined();
|
|
247
|
+
|
|
248
|
+
expect(bindingSpy).not.toHaveBeenCalled();
|
|
249
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
/*
|
|
253
|
+
* updateBy matches by query, so a single write can span rows in several
|
|
254
|
+
* projects. One unbound row is enough to leak a token, so the whole write
|
|
255
|
+
* must fail — not just the offending row.
|
|
256
|
+
*/
|
|
257
|
+
test("rejects a multi-row update when any affected row's project is unbound", async () => {
|
|
258
|
+
mockAffectedRows([ownProjectId, ObjectID.generate()]);
|
|
259
|
+
|
|
260
|
+
await expect(
|
|
261
|
+
service.onBeforeUpdate(buildUpdateBy(ownInstallationId)),
|
|
262
|
+
).rejects.toBeInstanceOf(BadDataException);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("checks every affected row, not just the first", async () => {
|
|
266
|
+
const secondProjectId: ObjectID = ObjectID.generate();
|
|
267
|
+
mockAffectedRows([ownProjectId, secondProjectId]);
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
await service.onBeforeUpdate(buildUpdateBy(ownInstallationId));
|
|
271
|
+
} catch {
|
|
272
|
+
// expected — the second row is unbound
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const checkedProjectIds: Array<string> = bindingSpy.mock.calls.map(
|
|
276
|
+
(call: Array<unknown>) => {
|
|
277
|
+
return (call[0] as { projectId: ObjectID }).projectId.toString();
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(checkedProjectIds).toContain(ownProjectId.toString());
|
|
282
|
+
expect(checkedProjectIds).toContain(secondProjectId.toString());
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("rejects when an affected row has no project", async () => {
|
|
286
|
+
mockAffectedRows([undefined]);
|
|
287
|
+
|
|
288
|
+
await expect(
|
|
289
|
+
service.onBeforeUpdate(buildUpdateBy(ownInstallationId)),
|
|
290
|
+
).rejects.toBeInstanceOf(BadDataException);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("reads the affected rows as root so the check cannot be blinded by permissions", async () => {
|
|
294
|
+
mockAffectedRows([ownProjectId]);
|
|
295
|
+
|
|
296
|
+
await service.onBeforeUpdate(buildUpdateBy(ownInstallationId));
|
|
297
|
+
|
|
298
|
+
const findArgs: { props: { isRoot?: boolean } } = findBySpy.mock
|
|
299
|
+
.calls[0]![0] as {
|
|
300
|
+
props: { isRoot?: boolean };
|
|
301
|
+
};
|
|
302
|
+
expect(findArgs.props.isRoot).toBe(true);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import CodeRepositoryService from "../../../Server/Services/CodeRepositoryService";
|
|
2
|
+
import GitHubInstallationBinding from "../../../Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding";
|
|
3
|
+
import GitHubUtil from "../../../Server/Utils/CodeRepository/GitHub/GitHub";
|
|
4
|
+
import CodeRepository from "../../../Models/DatabaseModels/CodeRepository";
|
|
5
|
+
import CodeRepositoryType from "../../../Types/CodeRepository/CodeRepositoryType";
|
|
6
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
7
|
+
import { RepoResolution } from "../../../Server/Utils/CodeRepository/StackTraceRepoResolver";
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* GHSA-xx95-gmcf-7q86, downstream guard.
|
|
12
|
+
*
|
|
13
|
+
* resolveRepositoryForException runs server-side, as root, and probes the file
|
|
14
|
+
* tree of EVERY candidate repository in a project to work out which one an
|
|
15
|
+
* exception came from. A row carrying an installation its project does not own
|
|
16
|
+
* would therefore have OneUptime read another tenant's private repository on
|
|
17
|
+
* its own initiative — no attacker request needed at the time it happens.
|
|
18
|
+
*
|
|
19
|
+
* The write boundary is closed and the quarantine migration clears rows that
|
|
20
|
+
* predate it, so on a healthy database nothing is filtered here. It stays
|
|
21
|
+
* because a database exploited before the fix would otherwise keep leaking.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
describe("resolveRepositoryForException only considers bound installations", () => {
|
|
25
|
+
let projectId: ObjectID;
|
|
26
|
+
let boundRepositoryId: ObjectID;
|
|
27
|
+
let unboundRepositoryId: ObjectID;
|
|
28
|
+
let treePathsSpy: jest.SpyInstance;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
projectId = ObjectID.generate();
|
|
32
|
+
boundRepositoryId = ObjectID.generate();
|
|
33
|
+
unboundRepositoryId = ObjectID.generate();
|
|
34
|
+
|
|
35
|
+
treePathsSpy = jest.spyOn(GitHubUtil, "getRepositoryTreePaths");
|
|
36
|
+
treePathsSpy.mockResolvedValue(["src/app.ts"]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
jest.restoreAllMocks();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function repository(data: {
|
|
44
|
+
id: ObjectID;
|
|
45
|
+
installationId: string;
|
|
46
|
+
}): CodeRepository {
|
|
47
|
+
const codeRepository: CodeRepository = new CodeRepository();
|
|
48
|
+
codeRepository.id = data.id;
|
|
49
|
+
codeRepository.projectId = projectId;
|
|
50
|
+
codeRepository.name = "service";
|
|
51
|
+
codeRepository.organizationName = "org";
|
|
52
|
+
codeRepository.repositoryName = `repo-${data.id.toString()}`;
|
|
53
|
+
codeRepository.mainBranchName = "main";
|
|
54
|
+
codeRepository.repositoryHostedAt = CodeRepositoryType.GitHub;
|
|
55
|
+
codeRepository.gitHubAppInstallationId = data.installationId;
|
|
56
|
+
return codeRepository;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function mockRepositories(repositories: Array<CodeRepository>): void {
|
|
60
|
+
const findBySpy: jest.SpyInstance = jest.spyOn(
|
|
61
|
+
CodeRepositoryService,
|
|
62
|
+
"findBy",
|
|
63
|
+
);
|
|
64
|
+
findBySpy.mockResolvedValue(repositories);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Only "11111111" genuinely belongs to this project.
|
|
68
|
+
function mockBinding(): void {
|
|
69
|
+
jest
|
|
70
|
+
.spyOn(GitHubInstallationBinding, "getBoundInstallationId")
|
|
71
|
+
.mockResolvedValue("11111111");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
test("never fetches the tree of a repository whose installation is unbound", async () => {
|
|
75
|
+
mockRepositories([
|
|
76
|
+
repository({ id: unboundRepositoryId, installationId: "99999999" }),
|
|
77
|
+
]);
|
|
78
|
+
mockBinding();
|
|
79
|
+
|
|
80
|
+
await CodeRepositoryService.resolveRepositoryForException({
|
|
81
|
+
projectId: projectId,
|
|
82
|
+
stackTrace: "at /src/app.ts:10:5",
|
|
83
|
+
serviceName: "api",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(treePathsSpy).not.toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("returns null when every candidate is unbound", async () => {
|
|
90
|
+
mockRepositories([
|
|
91
|
+
repository({ id: unboundRepositoryId, installationId: "99999999" }),
|
|
92
|
+
]);
|
|
93
|
+
mockBinding();
|
|
94
|
+
|
|
95
|
+
const resolution: RepoResolution | null =
|
|
96
|
+
await CodeRepositoryService.resolveRepositoryForException({
|
|
97
|
+
projectId: projectId,
|
|
98
|
+
stackTrace: "at /src/app.ts:10:5",
|
|
99
|
+
serviceName: "api",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(resolution).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("still resolves against a repository whose installation is bound", async () => {
|
|
106
|
+
mockRepositories([
|
|
107
|
+
repository({ id: boundRepositoryId, installationId: "11111111" }),
|
|
108
|
+
]);
|
|
109
|
+
mockBinding();
|
|
110
|
+
|
|
111
|
+
const resolution: RepoResolution | null =
|
|
112
|
+
await CodeRepositoryService.resolveRepositoryForException({
|
|
113
|
+
projectId: projectId,
|
|
114
|
+
stackTrace: "at /src/app.ts:10:5",
|
|
115
|
+
serviceName: "api",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(resolution).not.toBeNull();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
* The dangerous shape: one legitimate row plus one planted row. Resolution
|
|
123
|
+
* fans out across every candidate, so the planted one must be dropped
|
|
124
|
+
* without the legitimate one being disturbed.
|
|
125
|
+
*/
|
|
126
|
+
test("drops only the unbound row when both are present", async () => {
|
|
127
|
+
mockRepositories([
|
|
128
|
+
repository({ id: boundRepositoryId, installationId: "11111111" }),
|
|
129
|
+
repository({ id: unboundRepositoryId, installationId: "99999999" }),
|
|
130
|
+
]);
|
|
131
|
+
mockBinding();
|
|
132
|
+
|
|
133
|
+
await CodeRepositoryService.resolveRepositoryForException({
|
|
134
|
+
projectId: projectId,
|
|
135
|
+
stackTrace: "at /src/app.ts:10:5",
|
|
136
|
+
serviceName: "api",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const probedInstallationIds: Array<string> = treePathsSpy.mock.calls.map(
|
|
140
|
+
(call: Array<unknown>) => {
|
|
141
|
+
return (call[0] as { installationId: string }).installationId;
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
expect(probedInstallationIds).not.toContain("99999999");
|
|
146
|
+
});
|
|
147
|
+
});
|