@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.
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
- package/Models/DatabaseModels/NetworkDevice.ts +64 -0
- package/Models/DatabaseModels/StatusPage.ts +59 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
- package/Server/API/AIChatAPI.ts +55 -56
- package/Server/API/CommonAPI.ts +89 -0
- package/Server/API/MicrosoftTeamsAPI.ts +20 -9
- package/Server/API/SlackAPI.ts +13 -10
- package/Server/API/StatusPageAPI.ts +2197 -2128
- package/Server/API/TelemetryAPI.ts +72 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +11 -2
- package/Server/Services/ApiKeyPermissionService.ts +116 -2
- package/Server/Services/DatabaseService.ts +17 -6
- package/Server/Services/LogAggregationService.ts +34 -0
- package/Server/Services/LogService.ts +21 -0
- package/Server/Services/NetworkSiteService.ts +12 -0
- package/Server/Services/SpanService.ts +21 -0
- package/Server/Services/TraceAggregationService.ts +15 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +9 -0
- package/Server/Types/Workflow/Components/JavaScript.ts +9 -0
- package/Server/Utils/APIKey/AccessPermission.ts +16 -40
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
- package/Server/Utils/LogRedaction.ts +576 -0
- package/Server/Utils/Logger.ts +123 -46
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +12 -0
- package/Server/Utils/Monitor/MonitorResource.ts +29 -9
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
- package/Server/Utils/StartServer.ts +30 -9
- package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
- package/Server/Utils/VM/VMRunner.ts +693 -874
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
- package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
- package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
- package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
- package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
- package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
- package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
- package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
- package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
- package/Tests/Server/Types/Workflow/Components/CustomCodeScriptError.test.ts +121 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorJavascriptExpression.test.ts +92 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
- package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
- package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
- package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
- package/Tests/Server/Utils/VM/VMRunnerIsolation.test.ts +385 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +3 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
- package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
- package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
- package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
- package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
- package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
- package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
- package/Tests/UI/Components/MasterPage.test.tsx +36 -1
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
- package/Types/API/Hostname.ts +79 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
- package/Types/Database/DatabaseProperty.ts +38 -0
- package/Types/Database/TableColumn.ts +34 -7
- package/Types/Events/Recurring.ts +16 -1
- package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +14 -2
- package/Types/Monitor/MonitorStep.ts +13 -1
- package/Types/Monitor/MonitorType.ts +345 -40
- package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
- package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
- package/UI/Components/CardSelect/CardSelect.tsx +714 -105
- package/UI/Components/EmptyState/EmptyState.tsx +8 -1
- package/UI/Components/Footer/Footer.tsx +32 -6
- package/UI/Components/Forms/Fields/FormField.tsx +3 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Loader/PageLoader.tsx +29 -8
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
- package/UI/Components/MasterPage/MasterPage.tsx +24 -11
- package/Utils/Monitor/MonitorMetricType.ts +16 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
- package/Utils/Telemetry/CrossSignalScope.ts +75 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +48 -35
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +59 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +13 -10
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +745 -673
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +34 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +15 -6
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogService.js +16 -0
- package/build/dist/Server/Services/LogService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +12 -0
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/SpanService.js +16 -0
- package/build/dist/Server/Services/SpanService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +449 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Logger.js +98 -45
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -7
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js +402 -525
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +63 -0
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +38 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js +24 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +13 -1
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +9 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +330 -31
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/Footer/Footer.js +7 -4
- package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
- package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/package.json +1 -1
|
@@ -158,7 +158,9 @@ describe("SSLMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
|
|
|
158
158
|
},
|
|
159
159
|
);
|
|
160
160
|
|
|
161
|
-
|
|
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).
|
|
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).
|
|
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).
|
|
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
|
+
});
|