@oneuptime/common 12.0.11 → 12.0.13

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 (251) hide show
  1. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
  2. package/Models/DatabaseModels/NetworkDevice.ts +64 -0
  3. package/Models/DatabaseModels/StatusPage.ts +59 -0
  4. package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
  5. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
  6. package/Server/API/AIChatAPI.ts +55 -56
  7. package/Server/API/CommonAPI.ts +89 -0
  8. package/Server/API/MicrosoftTeamsAPI.ts +20 -9
  9. package/Server/API/SlackAPI.ts +13 -10
  10. package/Server/API/StatusPageAPI.ts +2197 -2128
  11. package/Server/API/TelemetryAPI.ts +72 -3
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  15. package/Server/Middleware/UserAuthorization.ts +11 -2
  16. package/Server/Services/ApiKeyPermissionService.ts +116 -2
  17. package/Server/Services/DatabaseService.ts +17 -6
  18. package/Server/Services/LogAggregationService.ts +34 -0
  19. package/Server/Services/LogService.ts +21 -0
  20. package/Server/Services/NetworkSiteService.ts +12 -0
  21. package/Server/Services/SpanService.ts +21 -0
  22. package/Server/Services/TraceAggregationService.ts +15 -0
  23. package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
  24. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  25. package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
  26. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +9 -0
  27. package/Server/Types/Workflow/Components/JavaScript.ts +9 -0
  28. package/Server/Utils/APIKey/AccessPermission.ts +16 -40
  29. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
  30. package/Server/Utils/LogRedaction.ts +576 -0
  31. package/Server/Utils/Logger.ts +123 -46
  32. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
  33. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +12 -0
  34. package/Server/Utils/Monitor/MonitorResource.ts +29 -9
  35. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
  36. package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
  37. package/Server/Utils/StartServer.ts +30 -9
  38. package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
  39. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
  40. package/Server/Utils/VM/VMRunner.ts +693 -874
  41. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
  42. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
  43. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
  44. package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
  45. package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
  46. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
  47. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
  48. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
  49. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
  50. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
  51. package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
  52. package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
  53. package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
  54. package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
  55. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
  56. package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
  57. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
  58. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
  59. package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
  60. package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
  61. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
  62. package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
  63. package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
  64. package/Tests/Server/Types/Workflow/Components/CustomCodeScriptError.test.ts +121 -0
  65. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
  66. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
  67. package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
  68. package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
  69. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
  70. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorJavascriptExpression.test.ts +92 -0
  71. package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
  72. package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
  73. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
  74. package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
  75. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
  76. package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
  77. package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
  78. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
  79. package/Tests/Server/Utils/VM/VMRunnerIsolation.test.ts +385 -0
  80. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +3 -0
  81. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
  82. package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
  83. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
  84. package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
  85. package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
  86. package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
  87. package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
  88. package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
  89. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
  90. package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
  91. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
  92. package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
  93. package/Tests/UI/Components/MasterPage.test.tsx +36 -1
  94. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
  95. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
  96. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
  97. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
  98. package/Types/API/Hostname.ts +79 -0
  99. package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
  100. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
  101. package/Types/Database/DatabaseProperty.ts +38 -0
  102. package/Types/Database/TableColumn.ts +34 -7
  103. package/Types/Events/Recurring.ts +16 -1
  104. package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +14 -2
  105. package/Types/Monitor/MonitorStep.ts +13 -1
  106. package/Types/Monitor/MonitorType.ts +345 -40
  107. package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
  108. package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
  109. package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
  110. package/UI/Components/CardSelect/CardSelect.tsx +714 -105
  111. package/UI/Components/EmptyState/EmptyState.tsx +8 -1
  112. package/UI/Components/Footer/Footer.tsx +32 -6
  113. package/UI/Components/Forms/Fields/FormField.tsx +3 -0
  114. package/UI/Components/Forms/Types/Field.ts +8 -0
  115. package/UI/Components/Loader/PageLoader.tsx +29 -8
  116. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
  117. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
  118. package/UI/Components/MasterPage/MasterPage.tsx +24 -11
  119. package/Utils/Monitor/MonitorMetricType.ts +16 -1
  120. package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
  121. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
  122. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
  123. package/Utils/Telemetry/CrossSignalScope.ts +75 -4
  124. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
  125. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  126. package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
  127. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  128. package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
  129. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  130. package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
  131. package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
  132. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  133. package/build/dist/Server/API/AIChatAPI.js +48 -35
  134. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  135. package/build/dist/Server/API/CommonAPI.js +59 -0
  136. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  137. package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
  138. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  139. package/build/dist/Server/API/SlackAPI.js +13 -10
  140. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  141. package/build/dist/Server/API/StatusPageAPI.js +745 -673
  142. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  143. package/build/dist/Server/API/TelemetryAPI.js +34 -3
  144. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  145. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
  146. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
  147. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
  148. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
  149. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  150. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  151. package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
  152. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  153. package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
  154. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  155. package/build/dist/Server/Services/DatabaseService.js +15 -6
  156. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  157. package/build/dist/Server/Services/LogAggregationService.js +2 -0
  158. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  159. package/build/dist/Server/Services/LogService.js +16 -0
  160. package/build/dist/Server/Services/LogService.js.map +1 -1
  161. package/build/dist/Server/Services/NetworkSiteService.js +12 -0
  162. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  163. package/build/dist/Server/Services/SpanService.js +16 -0
  164. package/build/dist/Server/Services/SpanService.js.map +1 -1
  165. package/build/dist/Server/Services/TraceAggregationService.js +2 -0
  166. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  167. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
  168. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  169. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  170. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  171. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
  172. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
  173. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +8 -0
  174. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  175. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +8 -0
  176. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  177. package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
  178. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  179. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
  180. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  181. package/build/dist/Server/Utils/LogRedaction.js +449 -0
  182. package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
  183. package/build/dist/Server/Utils/Logger.js +98 -45
  184. package/build/dist/Server/Utils/Logger.js.map +1 -1
  185. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
  186. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
  187. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +12 -1
  188. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  189. package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
  190. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  191. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
  192. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  193. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
  194. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
  195. package/build/dist/Server/Utils/StartServer.js +10 -7
  196. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  197. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
  198. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
  199. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
  200. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
  201. package/build/dist/Server/Utils/VM/VMRunner.js +402 -525
  202. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  203. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
  204. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  205. package/build/dist/Types/API/Hostname.js +63 -0
  206. package/build/dist/Types/API/Hostname.js.map +1 -1
  207. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
  208. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  209. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
  210. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  211. package/build/dist/Types/Database/DatabaseProperty.js +38 -0
  212. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  213. package/build/dist/Types/Database/TableColumn.js +24 -6
  214. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  215. package/build/dist/Types/Events/Recurring.js +13 -1
  216. package/build/dist/Types/Events/Recurring.js.map +1 -1
  217. package/build/dist/Types/Monitor/MonitorStep.js +9 -1
  218. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  219. package/build/dist/Types/Monitor/MonitorType.js +330 -31
  220. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  221. package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
  222. package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
  223. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
  224. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
  225. package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
  226. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  227. package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
  228. package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
  229. package/build/dist/UI/Components/Footer/Footer.js +7 -4
  230. package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
  231. package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
  232. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  233. package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
  234. package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
  235. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
  236. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  237. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
  238. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  239. package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
  240. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  241. package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
  242. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  243. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
  244. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  245. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
  246. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
  247. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
  248. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  249. package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
  250. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  251. package/package.json +1 -1
@@ -158,7 +158,9 @@ describe("SSLMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
158
158
  },
159
159
  );
160
160
 
161
- expect(result).toBe("SSL certificate is not valid.");
161
+ // The reason is appended so the root cause reaches whoever is paged.
162
+ expect(result).toContain("SSL certificate is not valid");
163
+ expect(result).toContain("self signed");
162
164
  });
163
165
 
164
166
  test("valid cert + False → not met", async () => {
@@ -196,7 +198,7 @@ describe("SSLMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
196
198
  },
197
199
  );
198
200
 
199
- expect(result).toBe("SSL certificate is not valid.");
201
+ expect(result).toContain("not reachable");
200
202
  });
201
203
  });
202
204
 
@@ -311,7 +313,8 @@ describe("SSLMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
311
313
  },
312
314
  );
313
315
 
314
- expect(result).toBe("SSL certificate is not valid.");
316
+ expect(result).toContain("SSL certificate is not valid");
317
+ expect(result).toContain("expired");
315
318
  });
316
319
 
317
320
  test("healthy cert + False → met", async () => {
@@ -343,7 +346,7 @@ describe("SSLMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
343
346
  },
344
347
  );
345
348
 
346
- expect(result).toBe("SSL certificate is not valid.");
349
+ expect(result).toContain("SSL certificate is not valid");
347
350
  });
348
351
  });
349
352
 
@@ -465,4 +468,371 @@ describe("SSLMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
465
468
 
466
469
  expect(result).toBeNull();
467
470
  });
471
+
472
+ /*
473
+ * Regression suite for https://github.com/OneUptime/oneuptime/issues/3225.
474
+ *
475
+ * The reported monitor pointed at a host with an invalid certificate and
476
+ * sat Operational forever. Two things made that possible:
477
+ *
478
+ * 1. IsValidCertificate and IsNotAValidCertificate were computed
479
+ * independently, so BOTH came out false whenever expiresAt was
480
+ * missing or unparseable. No criterion matched, and a monitor already
481
+ * at its default status writes no timeline entry when nothing
482
+ * matches - so the check left no trace at all.
483
+ *
484
+ * 2. The verdict was inferred from !isSelfSigned, and the probe labelled
485
+ * every validation failure "self signed". A certificate that failed
486
+ * for any other reason could not be described.
487
+ */
488
+ describe("certificate validity (issue #3225)", () => {
489
+ const failureModes: Array<{ name: string; response: SslMonitorResponse }> =
490
+ [
491
+ {
492
+ name: "self-signed",
493
+ response: {
494
+ isValidCertificate: false,
495
+ isSelfSigned: true,
496
+ certificateValidationErrorCode: "DEPTH_ZERO_SELF_SIGNED_CERT",
497
+ certificateValidationError: "self signed certificate",
498
+ expiresAt: dateFromNow({ days: 100 }),
499
+ },
500
+ },
501
+ {
502
+ name: "hostname mismatch",
503
+ response: {
504
+ isValidCertificate: false,
505
+ isSelfSigned: false,
506
+ certificateValidationErrorCode: "ERR_TLS_CERT_ALTNAME_INVALID",
507
+ certificateValidationError: "Hostname/IP does not match",
508
+ expiresAt: dateFromNow({ days: 100 }),
509
+ },
510
+ },
511
+ {
512
+ name: "untrusted CA",
513
+ response: {
514
+ isValidCertificate: false,
515
+ isSelfSigned: false,
516
+ certificateValidationErrorCode: "SELF_SIGNED_CERT_IN_CHAIN",
517
+ certificateValidationError: "self signed certificate in chain",
518
+ expiresAt: dateFromNow({ days: 100 }),
519
+ },
520
+ },
521
+ {
522
+ name: "expired",
523
+ response: {
524
+ isValidCertificate: false,
525
+ isSelfSigned: false,
526
+ certificateValidationErrorCode: "CERT_HAS_EXPIRED",
527
+ certificateValidationError: "certificate has expired",
528
+ expiresAt: dateFromNow({ days: -5 }),
529
+ },
530
+ },
531
+ {
532
+ name: "incomplete chain",
533
+ response: {
534
+ isValidCertificate: false,
535
+ isSelfSigned: false,
536
+ certificateValidationErrorCode: "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
537
+ certificateValidationError:
538
+ "unable to verify the first certificate",
539
+ expiresAt: dateFromNow({ days: 100 }),
540
+ },
541
+ },
542
+ ];
543
+
544
+ test.each(failureModes)(
545
+ "$name: IsNotAValidCertificate + True → met",
546
+ async ({ response }: { response: SslMonitorResponse }) => {
547
+ const result: string | null = await evaluate(
548
+ buildDataToProcess({ isOnline: true, sslResponse: response }),
549
+ {
550
+ checkOn: CheckOn.IsNotAValidCertificate,
551
+ filterType: FilterType.True,
552
+ value: undefined,
553
+ },
554
+ );
555
+
556
+ expect(result).toBeTruthy();
557
+ },
558
+ );
559
+
560
+ test.each(failureModes)(
561
+ "$name: IsValidCertificate + True → not met",
562
+ async ({ response }: { response: SslMonitorResponse }) => {
563
+ const result: string | null = await evaluate(
564
+ buildDataToProcess({ isOnline: true, sslResponse: response }),
565
+ {
566
+ checkOn: CheckOn.IsValidCertificate,
567
+ filterType: FilterType.True,
568
+ value: undefined,
569
+ },
570
+ );
571
+
572
+ expect(result).toBeNull();
573
+ },
574
+ );
575
+
576
+ test.each(failureModes)(
577
+ "$name: IsValidCertificate + False → met",
578
+ async ({ response }: { response: SslMonitorResponse }) => {
579
+ const result: string | null = await evaluate(
580
+ buildDataToProcess({ isOnline: true, sslResponse: response }),
581
+ {
582
+ checkOn: CheckOn.IsValidCertificate,
583
+ filterType: FilterType.False,
584
+ value: undefined,
585
+ },
586
+ );
587
+
588
+ expect(result).toBeTruthy();
589
+ },
590
+ );
591
+
592
+ test("the two predicates are exact complements, even with no expiry", async () => {
593
+ /*
594
+ * The silent-healthy case: a certificate the probe could not fully
595
+ * read. Before the fix BOTH of these returned null and the monitor
596
+ * recorded nothing.
597
+ */
598
+ const response: SslMonitorResponse = {
599
+ isValidCertificate: false,
600
+ isSelfSigned: false,
601
+ certificateValidationErrorCode: "UNABLE_TO_GET_ISSUER_CERT",
602
+ certificateValidationError: "unable to get issuer certificate",
603
+ // deliberately no expiresAt
604
+ };
605
+
606
+ const isValid: string | null = await evaluate(
607
+ buildDataToProcess({ isOnline: true, sslResponse: response }),
608
+ {
609
+ checkOn: CheckOn.IsValidCertificate,
610
+ filterType: FilterType.True,
611
+ value: undefined,
612
+ },
613
+ );
614
+
615
+ const isNotValid: string | null = await evaluate(
616
+ buildDataToProcess({ isOnline: true, sslResponse: response }),
617
+ {
618
+ checkOn: CheckOn.IsNotAValidCertificate,
619
+ filterType: FilterType.True,
620
+ value: undefined,
621
+ },
622
+ );
623
+
624
+ expect(isValid).toBeNull();
625
+ expect(isNotValid).toBeTruthy();
626
+ });
627
+
628
+ test("a valid certificate satisfies IsValidCertificate and not its negation", async () => {
629
+ const response: SslMonitorResponse = {
630
+ isValidCertificate: true,
631
+ isSelfSigned: false,
632
+ certificateValidationErrorCode: "",
633
+ certificateValidationError: "",
634
+ expiresAt: dateFromNow({ days: 90 }),
635
+ };
636
+
637
+ const isValid: string | null = await evaluate(
638
+ buildDataToProcess({ isOnline: true, sslResponse: response }),
639
+ {
640
+ checkOn: CheckOn.IsValidCertificate,
641
+ filterType: FilterType.True,
642
+ value: undefined,
643
+ },
644
+ );
645
+
646
+ const isNotValid: string | null = await evaluate(
647
+ buildDataToProcess({ isOnline: true, sslResponse: response }),
648
+ {
649
+ checkOn: CheckOn.IsNotAValidCertificate,
650
+ filterType: FilterType.True,
651
+ value: undefined,
652
+ },
653
+ );
654
+
655
+ expect(isValid).toBeTruthy();
656
+ expect(isNotValid).toBeNull();
657
+ });
658
+
659
+ test("the reason names the underlying validation error", async () => {
660
+ const result: string | null = await evaluate(
661
+ buildDataToProcess({
662
+ isOnline: true,
663
+ sslResponse: {
664
+ isValidCertificate: false,
665
+ isSelfSigned: false,
666
+ certificateValidationErrorCode: "ERR_TLS_CERT_ALTNAME_INVALID",
667
+ certificateValidationError: "Hostname/IP does not match",
668
+ expiresAt: dateFromNow({ days: 100 }),
669
+ },
670
+ }),
671
+ {
672
+ checkOn: CheckOn.IsNotAValidCertificate,
673
+ filterType: FilterType.True,
674
+ value: undefined,
675
+ },
676
+ );
677
+
678
+ // The root cause has to reach whoever gets paged.
679
+ expect(result).toContain("Hostname/IP does not match");
680
+ });
681
+
682
+ test("an unreachable endpoint is not a valid certificate", async () => {
683
+ const result: string | null = await evaluate(
684
+ buildDataToProcess({
685
+ isOnline: false,
686
+ sslResponse: { isValidCertificate: false },
687
+ }),
688
+ {
689
+ checkOn: CheckOn.IsNotAValidCertificate,
690
+ filterType: FilterType.True,
691
+ value: undefined,
692
+ },
693
+ );
694
+
695
+ expect(result).toBeTruthy();
696
+ });
697
+
698
+ test("only genuine self-signed responses satisfy IsSelfSignedCertificate", async () => {
699
+ const mismatch: string | null = await evaluate(
700
+ buildDataToProcess({
701
+ isOnline: true,
702
+ sslResponse: {
703
+ isValidCertificate: false,
704
+ isSelfSigned: false,
705
+ certificateValidationErrorCode: "ERR_TLS_CERT_ALTNAME_INVALID",
706
+ expiresAt: dateFromNow({ days: 100 }),
707
+ },
708
+ }),
709
+ {
710
+ checkOn: CheckOn.IsSelfSignedCertificate,
711
+ filterType: FilterType.True,
712
+ value: undefined,
713
+ },
714
+ );
715
+
716
+ /*
717
+ * Pre-fix the probe set isSelfSigned=true for a hostname mismatch, so
718
+ * this criterion fired on a certificate that was not self-signed at
719
+ * all.
720
+ */
721
+ expect(mismatch).toBeNull();
722
+ });
723
+ });
724
+
725
+ /*
726
+ * Payloads written by a probe running an older build carry no
727
+ * isValidCertificate field. They must keep evaluating exactly as before,
728
+ * or upgrading the server would flip healthy monitors to Down.
729
+ */
730
+ describe("legacy payloads without an explicit verdict", () => {
731
+ test("a healthy legacy response is still valid", async () => {
732
+ const result: string | null = await evaluate(
733
+ buildDataToProcess({
734
+ isOnline: true,
735
+ sslResponse: {
736
+ isSelfSigned: false,
737
+ expiresAt: dateFromNow({ days: 60 }),
738
+ },
739
+ }),
740
+ {
741
+ checkOn: CheckOn.IsValidCertificate,
742
+ filterType: FilterType.True,
743
+ value: undefined,
744
+ },
745
+ );
746
+
747
+ expect(result).toBeTruthy();
748
+ });
749
+
750
+ test("a legacy self-signed response is still not valid", async () => {
751
+ const result: string | null = await evaluate(
752
+ buildDataToProcess({
753
+ isOnline: true,
754
+ sslResponse: {
755
+ isSelfSigned: true,
756
+ expiresAt: dateFromNow({ days: 60 }),
757
+ },
758
+ }),
759
+ {
760
+ checkOn: CheckOn.IsNotAValidCertificate,
761
+ filterType: FilterType.True,
762
+ value: undefined,
763
+ },
764
+ );
765
+
766
+ expect(result).toBeTruthy();
767
+ });
768
+
769
+ test("a legacy expired response is still not valid", async () => {
770
+ const result: string | null = await evaluate(
771
+ buildDataToProcess({
772
+ isOnline: true,
773
+ sslResponse: {
774
+ isSelfSigned: false,
775
+ expiresAt: dateFromNow({ days: -1 }),
776
+ },
777
+ }),
778
+ {
779
+ checkOn: CheckOn.IsValidCertificate,
780
+ filterType: FilterType.True,
781
+ value: undefined,
782
+ },
783
+ );
784
+
785
+ expect(result).toBeNull();
786
+ });
787
+ });
788
+
789
+ /*
790
+ * The probe serialises Dates to ISO strings over the wire, so by the time
791
+ * criteria run, expiresAt is a string rather than a Date. Expiry maths
792
+ * must behave identically either way.
793
+ */
794
+ describe("expiresAt arriving as an ISO string", () => {
795
+ test("a future expiry read from a string is still valid", async () => {
796
+ const result: string | null = await evaluate(
797
+ buildDataToProcess({
798
+ isOnline: true,
799
+ sslResponse: {
800
+ isValidCertificate: true,
801
+ isSelfSigned: false,
802
+ expiresAt: dateFromNow({
803
+ days: 30,
804
+ }).toISOString() as unknown as Date,
805
+ },
806
+ }),
807
+ {
808
+ checkOn: CheckOn.IsValidCertificate,
809
+ filterType: FilterType.True,
810
+ value: undefined,
811
+ },
812
+ );
813
+
814
+ expect(result).toBeTruthy();
815
+ });
816
+
817
+ test("ExpiresInDays compares correctly against a string expiry", async () => {
818
+ const result: string | null = await evaluate(
819
+ buildDataToProcess({
820
+ isOnline: true,
821
+ sslResponse: {
822
+ isValidCertificate: true,
823
+ expiresAt: dateFromNow({
824
+ days: 10,
825
+ }).toISOString() as unknown as Date,
826
+ },
827
+ }),
828
+ {
829
+ checkOn: CheckOn.ExpiresInDays,
830
+ filterType: FilterType.LessThan,
831
+ value: 30,
832
+ },
833
+ );
834
+
835
+ expect(result).toBeTruthy();
836
+ });
837
+ });
468
838
  });
@@ -0,0 +1,92 @@
1
+ import MonitorCriteriaEvaluator from "../../../../Server/Utils/Monitor/MonitorCriteriaEvaluator";
2
+ import VMUtil from "../../../../Server/Utils/VM/VMAPI";
3
+ import Monitor from "../../../../Models/DatabaseModels/Monitor";
4
+ import MonitorStep from "../../../../Types/Monitor/MonitorStep";
5
+ import {
6
+ CheckOn,
7
+ CriteriaFilter,
8
+ } from "../../../../Types/Monitor/CriteriaFilter";
9
+ import MonitorType from "../../../../Types/Monitor/MonitorType";
10
+ import ReturnResult from "../../../../Types/IsolatedVM/ReturnResult";
11
+ import ObjectID from "../../../../Types/ObjectID";
12
+ import { describe, expect, test, afterEach } from "@jest/globals";
13
+
14
+ type EvaluatorPrivate = {
15
+ isMonitorInstanceCriteriaFilterMet: (input: {
16
+ dataToProcess: unknown;
17
+ monitorStep: MonitorStep;
18
+ monitor: Monitor;
19
+ probeApiIngestResponse: unknown;
20
+ criteriaInstance: unknown;
21
+ criteriaFilter: CriteriaFilter;
22
+ }) => Promise<string | null>;
23
+ };
24
+
25
+ const Evaluator: EvaluatorPrivate =
26
+ MonitorCriteriaEvaluator as unknown as EvaluatorPrivate;
27
+
28
+ function makeInput(): Parameters<
29
+ EvaluatorPrivate["isMonitorInstanceCriteriaFilterMet"]
30
+ >[0] {
31
+ const monitor: Monitor = new Monitor();
32
+ monitor.projectId = ObjectID.generate();
33
+ monitor.monitorType = MonitorType.Ping;
34
+
35
+ return {
36
+ dataToProcess: { isOnline: true },
37
+ monitorStep: new MonitorStep(),
38
+ monitor,
39
+ probeApiIngestResponse: {},
40
+ criteriaInstance: {},
41
+ criteriaFilter: {
42
+ checkOn: CheckOn.JavaScriptExpression,
43
+ value: "1 === 1",
44
+ } as CriteriaFilter,
45
+ };
46
+ }
47
+
48
+ afterEach(() => {
49
+ jest.restoreAllMocks();
50
+ });
51
+
52
+ describe("MonitorCriteriaEvaluator JavaScript expression criteria", () => {
53
+ test("treats a sandbox scriptError as criteria not met", async () => {
54
+ const sandboxResult: ReturnResult = {
55
+ returnValue: undefined,
56
+ logMessages: [],
57
+ capturedMetrics: [],
58
+ scriptError: new Error("expression exploded"),
59
+ };
60
+ jest.spyOn(VMUtil, "runCodeInSandbox").mockResolvedValue(sandboxResult);
61
+
62
+ const result: string | null =
63
+ await Evaluator.isMonitorInstanceCriteriaFilterMet(makeInput());
64
+
65
+ expect(result).toBeNull();
66
+ });
67
+
68
+ test("reports the criteria as met when the expression evaluates truthy", async () => {
69
+ const sandboxResult: ReturnResult = {
70
+ returnValue: true,
71
+ logMessages: [],
72
+ capturedMetrics: [],
73
+ };
74
+ jest.spyOn(VMUtil, "runCodeInSandbox").mockResolvedValue(sandboxResult);
75
+
76
+ const result: string | null =
77
+ await Evaluator.isMonitorInstanceCriteriaFilterMet(makeInput());
78
+
79
+ expect(result).toContain("evaluated to true");
80
+ });
81
+
82
+ test("treats a sandbox rejection as criteria not met", async () => {
83
+ jest
84
+ .spyOn(VMUtil, "runCodeInSandbox")
85
+ .mockRejectedValue(new Error("isolate crashed"));
86
+
87
+ const result: string | null =
88
+ await Evaluator.isMonitorInstanceCriteriaFilterMet(makeInput());
89
+
90
+ expect(result).toBeNull();
91
+ });
92
+ });