@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.
Files changed (384) hide show
  1. package/Models/DatabaseModels/CephCluster.ts +14 -0
  2. package/Models/DatabaseModels/CodeRepository.ts +14 -6
  3. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  4. package/Models/DatabaseModels/DockerHost.ts +14 -0
  5. package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
  6. package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
  7. package/Models/DatabaseModels/Index.ts +2 -0
  8. package/Models/DatabaseModels/IoTFleet.ts +14 -0
  9. package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
  10. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  11. package/Models/DatabaseModels/NetworkDevice.ts +14 -0
  12. package/Models/DatabaseModels/NetworkInterface.ts +18 -0
  13. package/Models/DatabaseModels/Project.ts +16 -2
  14. package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
  15. package/Models/DatabaseModels/Service.ts +30 -0
  16. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  17. package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
  18. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  19. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  20. package/Models/DatabaseModels/User.ts +30 -0
  21. package/Server/API/AIAgentDataAPI.ts +31 -0
  22. package/Server/API/BaseAPI.ts +13 -2
  23. package/Server/API/DashboardAPI.ts +458 -12
  24. package/Server/API/GitHubAPI.ts +119 -283
  25. package/Server/API/UserAPI.ts +263 -1
  26. package/Server/EnvironmentConfig.ts +38 -4
  27. package/Server/Infrastructure/GlobalCache.ts +152 -0
  28. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  36. package/Server/Services/AlertStateTimelineService.ts +20 -0
  37. package/Server/Services/AnalyticsDatabaseService.ts +72 -1
  38. package/Server/Services/CephClusterService.ts +26 -86
  39. package/Server/Services/CloudResourceService.ts +23 -83
  40. package/Server/Services/CodeRepositoryService.ts +105 -2
  41. package/Server/Services/DatabaseService.ts +296 -2
  42. package/Server/Services/DockerHostService.ts +19 -79
  43. package/Server/Services/DockerSwarmClusterService.ts +27 -88
  44. package/Server/Services/HostService.ts +46 -99
  45. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/IoTFleetService.ts +19 -79
  48. package/Server/Services/KubernetesClusterService.ts +17 -78
  49. package/Server/Services/LabelService.ts +57 -10
  50. package/Server/Services/MetricTypeService.ts +84 -0
  51. package/Server/Services/MonitorService.ts +89 -14
  52. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  53. package/Server/Services/MonitorTemplateService.ts +65 -0
  54. package/Server/Services/OpenTelemetryIngestService.ts +230 -26
  55. package/Server/Services/PodmanHostService.ts +19 -79
  56. package/Server/Services/ProxmoxClusterService.ts +23 -83
  57. package/Server/Services/RumApplicationService.ts +19 -80
  58. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  59. package/Server/Services/ServerlessFunctionService.ts +24 -85
  60. package/Server/Services/ServiceService.ts +299 -73
  61. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  62. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  63. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  64. package/Server/Services/TelemetryEntityService.ts +1 -0
  65. package/Server/Services/UserService.ts +184 -0
  66. package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
  67. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
  68. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
  69. package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
  70. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  71. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  72. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  73. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  74. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  75. package/Server/Utils/PasswordHash.ts +306 -0
  76. package/Server/Utils/SingleFlight.ts +83 -0
  77. package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
  78. package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
  79. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
  80. package/Server/Utils/Telemetry/Telemetry.ts +351 -122
  81. package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
  82. package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
  83. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  84. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  85. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  86. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  87. package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
  88. package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
  89. package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
  90. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  91. package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
  92. package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
  93. package/Tests/Server/API/Helpers.ts +6 -1
  94. package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
  95. package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
  96. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  97. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
  98. package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
  99. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
  100. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
  101. package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
  102. package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
  103. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
  104. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  105. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  106. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
  107. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  108. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  109. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  110. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
  111. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
  112. package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
  113. package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
  114. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
  115. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
  116. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
  117. package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
  118. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
  119. package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
  120. package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
  121. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
  122. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  123. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
  124. package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
  125. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  126. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  127. package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
  128. package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
  129. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  130. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  131. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  132. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  133. package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
  134. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  135. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  136. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  137. package/Tests/Types/Database/ColumnLength.test.ts +7 -1
  138. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  139. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  140. package/Tests/Types/Decimal.test.ts +37 -0
  141. package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
  142. package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
  143. package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
  144. package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
  145. package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
  146. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  147. package/Tests/Types/Port.test.ts +38 -0
  148. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
  149. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  150. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  151. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  152. package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
  153. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  154. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  155. package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
  156. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  157. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  158. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  159. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  160. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  161. package/Types/Database/ColumnLength.ts +9 -1
  162. package/Types/Database/DatabaseProperty.ts +26 -0
  163. package/Types/Database/NumericColumnValue.ts +128 -0
  164. package/Types/Database/TableColumn.ts +11 -0
  165. package/Types/Database/UnsynchronizedIndex.ts +48 -0
  166. package/Types/Decimal.ts +16 -4
  167. package/Types/HashedString.ts +139 -4
  168. package/Types/Permission.ts +48 -0
  169. package/Types/Port.ts +16 -4
  170. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
  171. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  172. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  173. package/UI/Components/Navbar/NavBar.tsx +12 -2
  174. package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
  175. package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
  176. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  177. package/Utils/Dashboard/Components/Index.ts +7 -0
  178. package/Utils/TeamMembersByProject.ts +237 -0
  179. package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
  180. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
  182. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  184. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
  186. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
  188. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
  190. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  192. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  193. package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
  194. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  195. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
  196. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  197. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  198. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  199. package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
  200. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  201. package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
  202. package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
  203. package/build/dist/Models/DatabaseModels/Project.js +16 -2
  204. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  205. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
  206. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  207. package/build/dist/Models/DatabaseModels/Service.js +28 -1
  208. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  209. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  210. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  211. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
  212. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  214. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  216. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/User.js +32 -0
  218. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/BaseAPI.js +11 -1
  222. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +343 -10
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/GitHubAPI.js +94 -167
  226. package/build/dist/Server/API/GitHubAPI.js.map +1 -1
  227. package/build/dist/Server/API/UserAPI.js +187 -2
  228. package/build/dist/Server/API/UserAPI.js.map +1 -1
  229. package/build/dist/Server/EnvironmentConfig.js +34 -4
  230. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
  232. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  234. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  249. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  250. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/CephClusterService.js +26 -76
  254. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  255. package/build/dist/Server/Services/CloudResourceService.js +23 -73
  256. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  257. package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
  258. package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
  259. package/build/dist/Server/Services/DatabaseService.js +246 -2
  260. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  261. package/build/dist/Server/Services/DockerHostService.js +19 -69
  262. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  263. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
  264. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  265. package/build/dist/Server/Services/HostService.js +58 -102
  266. package/build/dist/Server/Services/HostService.js.map +1 -1
  267. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  268. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  269. package/build/dist/Server/Services/Index.js +2 -0
  270. package/build/dist/Server/Services/Index.js.map +1 -1
  271. package/build/dist/Server/Services/IoTFleetService.js +19 -69
  272. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  273. package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
  274. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  275. package/build/dist/Server/Services/LabelService.js +42 -7
  276. package/build/dist/Server/Services/LabelService.js.map +1 -1
  277. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  278. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  279. package/build/dist/Server/Services/MonitorService.js +81 -9
  280. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  281. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  282. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  283. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  284. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  285. package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
  286. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  287. package/build/dist/Server/Services/PodmanHostService.js +19 -69
  288. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  289. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
  290. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  291. package/build/dist/Server/Services/RumApplicationService.js +19 -70
  292. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  293. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  294. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  295. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
  296. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  297. package/build/dist/Server/Services/ServiceService.js +236 -61
  298. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  299. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  300. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  301. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  302. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  303. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  304. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  305. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  306. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  307. package/build/dist/Server/Services/UserService.js +165 -0
  308. package/build/dist/Server/Services/UserService.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
  310. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
  311. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
  312. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  313. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
  314. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  315. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
  316. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
  317. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  318. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  319. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  320. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  321. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  322. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  323. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  324. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  325. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  326. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  327. package/build/dist/Server/Utils/PasswordHash.js +237 -0
  328. package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
  329. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  330. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  331. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  332. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  333. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  334. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  335. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  336. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  337. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  338. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  339. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  340. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  341. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  342. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  344. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  346. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  347. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  348. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  349. package/build/dist/Types/Database/ColumnLength.js +9 -1
  350. package/build/dist/Types/Database/ColumnLength.js.map +1 -1
  351. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  352. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  353. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  354. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  355. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  356. package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
  357. package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
  358. package/build/dist/Types/Decimal.js +14 -3
  359. package/build/dist/Types/Decimal.js.map +1 -1
  360. package/build/dist/Types/HashedString.js +95 -5
  361. package/build/dist/Types/HashedString.js.map +1 -1
  362. package/build/dist/Types/Permission.js +42 -0
  363. package/build/dist/Types/Permission.js.map +1 -1
  364. package/build/dist/Types/Port.js +15 -5
  365. package/build/dist/Types/Port.js.map +1 -1
  366. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
  367. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  368. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  369. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  370. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  371. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  372. package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
  373. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  374. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
  375. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  376. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
  377. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
  378. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  379. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  380. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  381. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  382. package/build/dist/Utils/TeamMembersByProject.js +145 -0
  383. package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
  384. 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
+ });