@oneuptime/common 12.0.22 → 12.0.24
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/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +207 -0
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -0
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
- package/Server/Services/NetworkDeviceService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
- package/Server/Services/ProjectService.ts +29 -19
- package/Server/Services/SecurityEventService.ts +90 -13
- package/Server/Services/StatusPageResourceService.ts +223 -0
- package/Server/Services/TelemetryAttributeService.ts +17 -0
- package/Server/Services/UserService.ts +4 -6
- package/Server/Types/Markdown.ts +161 -67
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
- package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
- package/Server/Utils/Attribution.ts +4 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +39 -1
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
- package/Server/Utils/StartServer.ts +12 -32
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
- package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
- package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
- package/Server/Views/Partials/Head.ejs +2 -1
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
- package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
- package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
- package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
- package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
- package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
- package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
- package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
- package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
- package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
- package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
- package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
- package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
- package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
- package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
- package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
- package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -0
- package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
- package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
- package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
- package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
- package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +97 -1
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
- package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
- package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
- package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
- package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
- package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
- package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
- package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
- package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
- package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
- package/Tests/UI/Components/SideOver.test.tsx +1 -1
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
- package/Tests/UI/Components/Toggle.test.tsx +25 -0
- package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
- package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +516 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
- package/Types/Code/YamlSyntax.ts +396 -0
- package/Types/Exception/ExceptionCode.ts +1 -0
- package/Types/Exception/PayloadTooLargeException.ts +8 -0
- package/Types/Marketing/Attribution.ts +10 -0
- package/Types/Marketing/MarketingEvent.ts +4 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
- package/Types/NetworkAutomation/RuleRunResult.ts +84 -0
- package/Types/Permission.ts +47 -0
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmMetricConventions.ts +124 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
- package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
- package/UI/Components/Detail/Detail.tsx +10 -0
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
- package/UI/Components/Forms/Fields/FormField.tsx +31 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
- package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
- package/UI/Components/Forms/Validation.ts +46 -0
- package/UI/Components/Input/Input.tsx +3 -0
- package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
- package/UI/Components/ModelTable/Column.ts +8 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
- package/UI/Components/SideOver/SideOver.tsx +5 -5
- package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
- package/UI/Components/StatusPage/ResourceList.tsx +197 -38
- package/UI/Components/Toggle/Toggle.tsx +9 -1
- package/UI/Components/Types/FieldType.ts +1 -0
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
- package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +257 -0
- package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
- package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +76 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +76 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
- package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
- package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +206 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -45
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +57 -5
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
- package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +2 -12
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +147 -61
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +26 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -21
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- package/build/dist/Types/Code/YamlSyntax.js +212 -0
- package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
- package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
- package/build/dist/Types/Marketing/Attribution.js +10 -0
- package/build/dist/Types/Marketing/Attribution.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +24 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/Permission.js +41 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
- package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +9 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +27 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
- package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Types/FieldType.js +1 -0
- package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +149 -0
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +2 -1
- package/Tests/UI/Components/Toast.test.tsx +0 -197
- package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
- package/UI/Components/Toast/Toast.tsx +0 -189
- package/UI/Components/Toast/ToastInit.tsx +0 -110
- package/build/dist/UI/Components/Toast/Toast.js +0 -123
- package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
- package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The auto-import rule engine reads a completed scan's SNMP credentials
|
|
3
|
+
* through AUTO_IMPORT_SCAN_CREDENTIAL_SELECT and copies them onto every
|
|
4
|
+
* device it creates via DiscoveredDeviceBuilder. The probe claim endpoint
|
|
5
|
+
* (App/FeatureSet/Telemetry/API/ProbeIngest/DiscoveryScan.ts) selects the
|
|
6
|
+
* same credential columns for the same reason: an unselected column arrives
|
|
7
|
+
* undefined and is silently NOT copied/used — no error anywhere, just a
|
|
8
|
+
* device (or a sweep) that can never authenticate.
|
|
9
|
+
*
|
|
10
|
+
* That failure mode is exactly the kind that drifts in: someone adds a
|
|
11
|
+
* credential column to the scan model, wires it into ONE of the three
|
|
12
|
+
* consumers, and the other two quietly drop it. So this suite pins the list
|
|
13
|
+
* in BOTH directions against the claim endpoint's select and against the
|
|
14
|
+
* builder — read from source with fs, the
|
|
15
|
+
* DiscoveryScanClaimHookFreeSafety.test.ts technique, because the select
|
|
16
|
+
* literal in a route handler is not importable. If an assertion here fails,
|
|
17
|
+
* its variable name says which side is missing which key; add the key to
|
|
18
|
+
* that side rather than relaxing the pin.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* The engine's import closure reaches DatabaseService (and through it
|
|
23
|
+
* PasswordHash) via the three collaborating services. None of them is called
|
|
24
|
+
* here — this suite only wants the exported select constant — so the service
|
|
25
|
+
* MODULES are stubbed out before the engine is imported, keeping the suite
|
|
26
|
+
* Postgres-free and immune to the local-only PasswordHash ts-jest compile
|
|
27
|
+
* failure.
|
|
28
|
+
*/
|
|
29
|
+
jest.mock("../../../Server/Services/NetworkDeviceService", () => {
|
|
30
|
+
return {
|
|
31
|
+
__esModule: true,
|
|
32
|
+
default: {},
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
jest.mock("../../../Server/Services/NetworkDeviceDiscoveryScanService", () => {
|
|
37
|
+
return {
|
|
38
|
+
__esModule: true,
|
|
39
|
+
default: {},
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
jest.mock("../../../Server/Services/NetworkDeviceAutoImportRuleService", () => {
|
|
44
|
+
return {
|
|
45
|
+
__esModule: true,
|
|
46
|
+
default: {},
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// The engine's sweep lock would otherwise pull the Redis client in.
|
|
51
|
+
jest.mock("../../../Server/Infrastructure/Semaphore", () => {
|
|
52
|
+
return {
|
|
53
|
+
__esModule: true,
|
|
54
|
+
default: {},
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
jest.mock("../../../Server/Utils/Logger", () => {
|
|
59
|
+
return {
|
|
60
|
+
__esModule: true,
|
|
61
|
+
default: {
|
|
62
|
+
debug: jest.fn(),
|
|
63
|
+
info: jest.fn(),
|
|
64
|
+
warn: jest.fn(),
|
|
65
|
+
error: jest.fn(),
|
|
66
|
+
trace: jest.fn(),
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
import { AUTO_IMPORT_SCAN_CREDENTIAL_SELECT } from "../../../Server/Services/NetworkDeviceAutoImportRuleEngineService";
|
|
72
|
+
import { describe, expect, test } from "@jest/globals";
|
|
73
|
+
import fs from "fs";
|
|
74
|
+
import path from "path";
|
|
75
|
+
|
|
76
|
+
const REPO_ROOT: string = path.join(__dirname, "..", "..", "..", "..");
|
|
77
|
+
|
|
78
|
+
const CLAIM_ENDPOINT_SOURCE: string = fs.readFileSync(
|
|
79
|
+
path.join(
|
|
80
|
+
REPO_ROOT,
|
|
81
|
+
"App",
|
|
82
|
+
"FeatureSet",
|
|
83
|
+
"Telemetry",
|
|
84
|
+
"API",
|
|
85
|
+
"ProbeIngest",
|
|
86
|
+
"DiscoveryScan.ts",
|
|
87
|
+
),
|
|
88
|
+
"utf8",
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const BUILDER_SOURCE: string = fs.readFileSync(
|
|
92
|
+
path.join(
|
|
93
|
+
REPO_ROOT,
|
|
94
|
+
"Common",
|
|
95
|
+
"Utils",
|
|
96
|
+
"NetworkDiscovery",
|
|
97
|
+
"DiscoveredDeviceBuilder.ts",
|
|
98
|
+
),
|
|
99
|
+
"utf8",
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
* Every `snmp*: true` in the claim endpoint is a select entry — the file's
|
|
104
|
+
* only other snmp mention is the `snmpReachable !== false` responder count,
|
|
105
|
+
* which this shape can never match.
|
|
106
|
+
*/
|
|
107
|
+
function extractSelectedSnmpColumns(source: string): Array<string> {
|
|
108
|
+
return Array.from(source.matchAll(/\b(snmp\w+)\s*:\s*true\b/g)).map(
|
|
109
|
+
(match: RegExpMatchArray) => {
|
|
110
|
+
return match[1]!;
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
describe("AUTO_IMPORT_SCAN_CREDENTIAL_SELECT", () => {
|
|
116
|
+
test("carries exactly the credential columns an import copies", () => {
|
|
117
|
+
expect(Object.keys(AUTO_IMPORT_SCAN_CREDENTIAL_SELECT).sort()).toEqual(
|
|
118
|
+
[
|
|
119
|
+
"probeId",
|
|
120
|
+
"snmpVersion",
|
|
121
|
+
"snmpCommunityString",
|
|
122
|
+
"snmpPort",
|
|
123
|
+
"snmpV3SecurityLevel",
|
|
124
|
+
"snmpV3Username",
|
|
125
|
+
"snmpV3AuthProtocol",
|
|
126
|
+
"snmpV3AuthKey",
|
|
127
|
+
"snmpV3PrivProtocol",
|
|
128
|
+
"snmpV3PrivKey",
|
|
129
|
+
].sort(),
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Guard against the extraction regex rotting into a vacuous pass.
|
|
134
|
+
test("the claim endpoint's select is extractable from its source", () => {
|
|
135
|
+
const selected: Array<string> = extractSelectedSnmpColumns(
|
|
136
|
+
CLAIM_ENDPOINT_SOURCE,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(selected).toContain("snmpVersion");
|
|
140
|
+
expect(selected).toContain("snmpV3PrivKey");
|
|
141
|
+
// The responder-count read is not a select entry and must not match.
|
|
142
|
+
expect(selected).not.toContain("snmpReachable");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
/*
|
|
146
|
+
* Direction one: a credential column the probe sweeps WITH but the engine
|
|
147
|
+
* does not select would import devices that poll with less than the scan
|
|
148
|
+
* used — a v3 scan importing v2c-shaped devices, say. Any name listed in
|
|
149
|
+
* this failure is selected by the claim endpoint and missing from
|
|
150
|
+
* AUTO_IMPORT_SCAN_CREDENTIAL_SELECT.
|
|
151
|
+
*/
|
|
152
|
+
test("every snmp column the probe claim endpoint selects is in the engine's select", () => {
|
|
153
|
+
const snmpColumnsSelectedByClaimEndpointButMissingFromEngineSelect: Array<string> =
|
|
154
|
+
extractSelectedSnmpColumns(CLAIM_ENDPOINT_SOURCE).filter(
|
|
155
|
+
(column: string) => {
|
|
156
|
+
return !(column in AUTO_IMPORT_SCAN_CREDENTIAL_SELECT);
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
expect(
|
|
161
|
+
snmpColumnsSelectedByClaimEndpointButMissingFromEngineSelect,
|
|
162
|
+
).toEqual([]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* Direction two: a credential column the engine copies onto devices but
|
|
167
|
+
* the probe never receives would mean sweeps and imports disagree about
|
|
168
|
+
* what the credentials ARE. Any name listed in this failure is a key of
|
|
169
|
+
* AUTO_IMPORT_SCAN_CREDENTIAL_SELECT missing from the claim endpoint's
|
|
170
|
+
* select.
|
|
171
|
+
*/
|
|
172
|
+
test("every snmp key of the engine's select is selected by the probe claim endpoint", () => {
|
|
173
|
+
const claimSelectedColumns: Array<string> = extractSelectedSnmpColumns(
|
|
174
|
+
CLAIM_ENDPOINT_SOURCE,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const engineSnmpKeysMissingFromClaimEndpointSelect: Array<string> =
|
|
178
|
+
Object.keys(AUTO_IMPORT_SCAN_CREDENTIAL_SELECT)
|
|
179
|
+
.filter((key: string) => {
|
|
180
|
+
return key.startsWith("snmp");
|
|
181
|
+
})
|
|
182
|
+
.filter((key: string) => {
|
|
183
|
+
return !claimSelectedColumns.includes(key);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
expect(engineSnmpKeysMissingFromClaimEndpointSelect).toEqual([]);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/*
|
|
190
|
+
* And the last hop: selecting a credential the builder never copies is the
|
|
191
|
+
* same silent drop one module later. Every key of the select must appear
|
|
192
|
+
* in DiscoveredDeviceBuilder.ts — the copy is a same-named field access,
|
|
193
|
+
* so a plain occurrence check is a faithful pin. Any name listed in this
|
|
194
|
+
* failure is selected by the engine and never mentioned by the builder.
|
|
195
|
+
*/
|
|
196
|
+
test("every key of the engine's select is copied by the device builder", () => {
|
|
197
|
+
const engineSelectKeysNeverMentionedByBuilder: Array<string> = Object.keys(
|
|
198
|
+
AUTO_IMPORT_SCAN_CREDENTIAL_SELECT,
|
|
199
|
+
).filter((key: string) => {
|
|
200
|
+
return !BUILDER_SOURCE.includes(key);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
expect(engineSelectKeysNeverMentionedByBuilder).toEqual([]);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -13,8 +13,10 @@ import { describe, expect, test } from "@jest/globals";
|
|
|
13
13
|
* typo must surface to the person editing the rule, not as a cron-side
|
|
14
14
|
* lastError hours later. These tests drive the create/update hooks
|
|
15
15
|
* directly: invalid or aggregation-based Sigma is rejected, the rule name
|
|
16
|
-
* defaults from the Sigma title,
|
|
17
|
-
* whole minutes between 1 and 1440
|
|
16
|
+
* defaults from the Sigma title, the evaluation interval is clamped to
|
|
17
|
+
* whole minutes between 1 and 1440, and the match count threshold must be
|
|
18
|
+
* a whole number between 1 and 1000000 (or omitted — the column defaults
|
|
19
|
+
* to 1, so old API clients that never send it keep working).
|
|
18
20
|
*/
|
|
19
21
|
|
|
20
22
|
const PROJECT_ID: ObjectID = new ObjectID(
|
|
@@ -52,6 +54,7 @@ function buildRule(
|
|
|
52
54
|
name?: string;
|
|
53
55
|
sigmaRuleYaml?: string;
|
|
54
56
|
evaluationIntervalInMinutes?: number;
|
|
57
|
+
matchCountThreshold?: number;
|
|
55
58
|
} = {},
|
|
56
59
|
): DetectionRule {
|
|
57
60
|
const rule: DetectionRule = new DetectionRule();
|
|
@@ -66,6 +69,10 @@ function buildRule(
|
|
|
66
69
|
rule.evaluationIntervalInMinutes = options.evaluationIntervalInMinutes;
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
if (options.matchCountThreshold !== undefined) {
|
|
73
|
+
rule.matchCountThreshold = options.matchCountThreshold;
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
return rule;
|
|
70
77
|
}
|
|
71
78
|
|
|
@@ -219,6 +226,58 @@ detection:
|
|
|
219
226
|
).resolves.toBeDefined();
|
|
220
227
|
});
|
|
221
228
|
});
|
|
229
|
+
|
|
230
|
+
describe("match count threshold validation", () => {
|
|
231
|
+
const THRESHOLD_ERROR: string =
|
|
232
|
+
"Match count threshold must be a whole number between 1 and 1000000.";
|
|
233
|
+
|
|
234
|
+
test.each([1, 5, 1000000])("%d is accepted", async (threshold: number) => {
|
|
235
|
+
const rule: DetectionRule = buildRule({
|
|
236
|
+
name: "Thresholded",
|
|
237
|
+
matchCountThreshold: threshold,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await expect(
|
|
241
|
+
service.onBeforeCreate(buildCreateBy(rule)),
|
|
242
|
+
).resolves.toBeDefined();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test.each([0, -1, 1.5, 1000001])(
|
|
246
|
+
"%d is rejected with the exact message",
|
|
247
|
+
async (threshold: number) => {
|
|
248
|
+
const rule: DetectionRule = buildRule({
|
|
249
|
+
name: "Bad Threshold",
|
|
250
|
+
matchCountThreshold: threshold,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
await expect(
|
|
254
|
+
service.onBeforeCreate(buildCreateBy(rule)),
|
|
255
|
+
).rejects.toThrow(THRESHOLD_ERROR);
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
test("an omitted threshold passes — the column defaults to 1, so old API clients that never send it still create rules", async () => {
|
|
260
|
+
const rule: DetectionRule = buildRule({ name: "No Threshold" });
|
|
261
|
+
|
|
262
|
+
expect(rule.matchCountThreshold).toBeUndefined();
|
|
263
|
+
|
|
264
|
+
await expect(
|
|
265
|
+
service.onBeforeCreate(buildCreateBy(rule)),
|
|
266
|
+
).resolves.toBeDefined();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("a valid threshold does not bypass the Sigma validation that runs before it", async () => {
|
|
270
|
+
const rule: DetectionRule = buildRule({
|
|
271
|
+
name: "Still Broken YAML",
|
|
272
|
+
sigmaRuleYaml: "title: [unclosed",
|
|
273
|
+
matchCountThreshold: 5,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await expect(service.onBeforeCreate(buildCreateBy(rule))).rejects.toThrow(
|
|
277
|
+
BadDataException,
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
222
281
|
});
|
|
223
282
|
|
|
224
283
|
describe("DetectionRuleService.onBeforeUpdate", () => {
|
|
@@ -251,4 +310,77 @@ describe("DetectionRuleService.onBeforeUpdate", () => {
|
|
|
251
310
|
),
|
|
252
311
|
).rejects.toThrow(BadDataException);
|
|
253
312
|
});
|
|
313
|
+
|
|
314
|
+
test("an update with an invalid matchCountThreshold is rejected with the exact message", async () => {
|
|
315
|
+
await expect(
|
|
316
|
+
service.onBeforeUpdate(buildUpdateBy({ matchCountThreshold: 0 })),
|
|
317
|
+
).rejects.toThrow(
|
|
318
|
+
"Match count threshold must be a whole number between 1 and 1000000.",
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test("an update with a valid matchCountThreshold passes", async () => {
|
|
323
|
+
await expect(
|
|
324
|
+
service.onBeforeUpdate(buildUpdateBy({ matchCountThreshold: 42 })),
|
|
325
|
+
).resolves.toBeDefined();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("an update that omits matchCountThreshold skips the threshold validation", async () => {
|
|
329
|
+
await expect(
|
|
330
|
+
service.onBeforeUpdate(buildUpdateBy({ description: "No threshold" })),
|
|
331
|
+
).resolves.toBeDefined();
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe("DetectionRuleService field-name trimming", () => {
|
|
336
|
+
/*
|
|
337
|
+
* groupByField / distinctCountField are looked up verbatim by the
|
|
338
|
+
* evaluator — an unknown name silently becomes an attributes[] lookup
|
|
339
|
+
* yielding '' on every row, and for a distinct-count rule that is a
|
|
340
|
+
* permanent zero-fire outage with no error anywhere. Trimming at save
|
|
341
|
+
* time kills the whitespace-from-a-paste class of that failure.
|
|
342
|
+
*/
|
|
343
|
+
test("create trims whitespace from groupByField and distinctCountField", async () => {
|
|
344
|
+
const rule: DetectionRule = buildRule();
|
|
345
|
+
rule.groupByField = " principalIp ";
|
|
346
|
+
rule.distinctCountField = "principalUser\t";
|
|
347
|
+
|
|
348
|
+
const result: OnCreate<DetectionRule> = await service.onBeforeCreate(
|
|
349
|
+
buildCreateBy(rule),
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
expect(result.createBy.data.groupByField).toBe("principalIp");
|
|
353
|
+
expect(result.createBy.data.distinctCountField).toBe("principalUser");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("create leaves omitted field names undefined", async () => {
|
|
357
|
+
const result: OnCreate<DetectionRule> = await service.onBeforeCreate(
|
|
358
|
+
buildCreateBy(buildRule()),
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
expect(result.createBy.data.groupByField).toBeUndefined();
|
|
362
|
+
expect(result.createBy.data.distinctCountField).toBeUndefined();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("update trims field names and collapses whitespace-only to the cleared value", async () => {
|
|
366
|
+
const result: OnUpdate<DetectionRule> = await service.onBeforeUpdate(
|
|
367
|
+
buildUpdateBy({
|
|
368
|
+
groupByField: " ",
|
|
369
|
+
distinctCountField: " targetUser ",
|
|
370
|
+
}),
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
// '' is the documented "feature off" value, so clearing still works.
|
|
374
|
+
expect(result.updateBy.data.groupByField).toBe("");
|
|
375
|
+
expect(result.updateBy.data.distinctCountField).toBe("targetUser");
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("an update that omits the field names does not add them", async () => {
|
|
379
|
+
const result: OnUpdate<DetectionRule> = await service.onBeforeUpdate(
|
|
380
|
+
buildUpdateBy({ description: "No field names" }),
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
expect("groupByField" in result.updateBy.data).toBe(false);
|
|
384
|
+
expect("distinctCountField" in result.updateBy.data).toBe(false);
|
|
385
|
+
});
|
|
254
386
|
});
|
|
@@ -140,7 +140,13 @@ describe("discovery-scan claim hookless write safety preconditions", () => {
|
|
|
140
140
|
"startedAt",
|
|
141
141
|
"statusMessage",
|
|
142
142
|
];
|
|
143
|
-
|
|
143
|
+
/*
|
|
144
|
+
* `name` joined `cidr` here when discovery scans became nameable
|
|
145
|
+
* (issue #3391): the hook validates and normalizes it on any update that
|
|
146
|
+
* carries it. The claim payload does not, which is what keeps the
|
|
147
|
+
* hookless write honest.
|
|
148
|
+
*/
|
|
149
|
+
const columnsValidatedByHook: Array<string> = ["cidr", "name"];
|
|
144
150
|
|
|
145
151
|
for (const column of claimWriteColumns) {
|
|
146
152
|
expect(columnsValidatedByHook).not.toContain(column);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Retention windows are declared per service via hardDeleteItemsOlderThanInDays,
|
|
6
|
+
* but they only take effect once the service is listed in
|
|
7
|
+
* Server/Services/Index.ts - that array is what
|
|
8
|
+
* App/FeatureSet/Workers/Jobs/HardDelete/HardDeleteItemsInDatabase.ts iterates.
|
|
9
|
+
* A window on an unregistered service is dead configuration, and registering
|
|
10
|
+
* the service later silently arms it.
|
|
11
|
+
*
|
|
12
|
+
* That is not hypothetical: OnCallDutyPolicyFeedService sat unregistered
|
|
13
|
+
* carrying a 90 day window while every other feed kept 3 years. Commit
|
|
14
|
+
* 6bbf2f866c ("retain items for 3 years instead of 120 days") swept the feeds
|
|
15
|
+
* from 120 to 3 * 365 and missed this one precisely because it held 90 rather
|
|
16
|
+
* than 120, and nothing noticed because the service was not registered.
|
|
17
|
+
* Registering it - which the workflow component registry fix had to do, since
|
|
18
|
+
* OnCallDutyPolicyFeed is workflow-enabled - would have started hard deleting
|
|
19
|
+
* on-call policy history at 90 days on billing-enabled deployments.
|
|
20
|
+
*
|
|
21
|
+
* Feed tables are the same kind of data as each other: a user-visible activity
|
|
22
|
+
* timeline hung off a parent entity. They should expire together, whether or
|
|
23
|
+
* not they happen to be registered yet.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const SERVICES_DIRECTORY: string = path.join(
|
|
27
|
+
__dirname,
|
|
28
|
+
"..",
|
|
29
|
+
"..",
|
|
30
|
+
"..",
|
|
31
|
+
"Server",
|
|
32
|
+
"Services",
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const RETENTION_PATTERN: RegExp =
|
|
36
|
+
/hardDeleteItemsOlderThanInDays\(\s*"(\w+)"\s*,\s*([^)]+?)\s*\)/;
|
|
37
|
+
|
|
38
|
+
interface FeedRetention {
|
|
39
|
+
serviceName: string;
|
|
40
|
+
columnName: string;
|
|
41
|
+
window: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const feedServiceNames: Array<string> = fs
|
|
45
|
+
.readdirSync(SERVICES_DIRECTORY)
|
|
46
|
+
.sort()
|
|
47
|
+
.filter((fileName: string) => {
|
|
48
|
+
return fileName.endsWith("FeedService.ts");
|
|
49
|
+
})
|
|
50
|
+
.map((fileName: string) => {
|
|
51
|
+
return fileName.replace(/\.ts$/, "");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const feedRetentions: Array<FeedRetention> = [];
|
|
55
|
+
|
|
56
|
+
for (const serviceName of feedServiceNames) {
|
|
57
|
+
const source: string = fs.readFileSync(
|
|
58
|
+
path.join(SERVICES_DIRECTORY, `${serviceName}.ts`),
|
|
59
|
+
"utf8",
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const match: RegExpMatchArray | null = source.match(RETENTION_PATTERN);
|
|
63
|
+
|
|
64
|
+
if (!match) {
|
|
65
|
+
// Deliberately not skipped - see "declares a retention window at all" below.
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
feedRetentions.push({
|
|
70
|
+
serviceName: serviceName,
|
|
71
|
+
columnName: match[1]!,
|
|
72
|
+
window: match[2]!.trim(),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe("Feed service retention windows", () => {
|
|
77
|
+
test("the feed services were actually found", () => {
|
|
78
|
+
// Guards the assertions below against a silent pass on an empty list.
|
|
79
|
+
expect(feedServiceNames.length).toBeGreaterThan(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("every feed service declares a retention window at all", () => {
|
|
83
|
+
/*
|
|
84
|
+
* The window/column assertions below can only speak for services this file
|
|
85
|
+
* managed to parse. Comparing against the filenames on disk - rather than a
|
|
86
|
+
* hardcoded count - is what stops a feed service that declares no retention,
|
|
87
|
+
* or declares one in a shape the pattern misses, from being dropped from
|
|
88
|
+
* those assertions and passing in silence.
|
|
89
|
+
*/
|
|
90
|
+
const parsed: Array<string> = feedRetentions.map((entry: FeedRetention) => {
|
|
91
|
+
return entry.serviceName;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(parsed).toEqual(feedServiceNames);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("every feed service expires on the same window", () => {
|
|
98
|
+
const windows: Array<string> = [
|
|
99
|
+
...new Set(
|
|
100
|
+
feedRetentions.map((entry: FeedRetention) => {
|
|
101
|
+
return entry.window;
|
|
102
|
+
}),
|
|
103
|
+
),
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
// Reported with the owning service names so a divergent one is obvious.
|
|
107
|
+
expect({
|
|
108
|
+
windows,
|
|
109
|
+
services: feedRetentions.map((entry: FeedRetention) => {
|
|
110
|
+
return `${entry.serviceName}=${entry.window}`;
|
|
111
|
+
}),
|
|
112
|
+
}).toEqual({
|
|
113
|
+
windows: ["3 * 365"],
|
|
114
|
+
services: feedRetentions.map((entry: FeedRetention) => {
|
|
115
|
+
return `${entry.serviceName}=3 * 365`;
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("every feed service expires on the same column", () => {
|
|
121
|
+
const columns: Array<string> = [
|
|
122
|
+
...new Set(
|
|
123
|
+
feedRetentions.map((entry: FeedRetention) => {
|
|
124
|
+
return entry.columnName;
|
|
125
|
+
}),
|
|
126
|
+
),
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
expect(columns).toEqual(["createdAt"]);
|
|
130
|
+
});
|
|
131
|
+
});
|