@oneuptime/common 12.0.30 → 12.0.31

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 (489) hide show
  1. package/Models/AnalyticsModels/SecurityEvent.ts +48 -16
  2. package/Models/AnalyticsModels/ThreatIntelIndicator.ts +10 -5
  3. package/Models/DatabaseModels/AlertSeverity.ts +44 -0
  4. package/Models/DatabaseModels/DetectionRule.ts +16 -5
  5. package/Models/DatabaseModels/GoogleSecOpsConnection.ts +14 -5
  6. package/Models/DatabaseModels/IncidentSeverity.ts +44 -0
  7. package/Models/DatabaseModels/Index.ts +2 -0
  8. package/Models/DatabaseModels/NetworkDevice.ts +113 -3
  9. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +88 -0
  10. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +22 -0
  11. package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +499 -0
  12. package/Models/DatabaseModels/ThreatIntelFeed.ts +16 -5
  13. package/Server/API/GlobalConfigAPI.ts +362 -183
  14. package/Server/API/TelemetryAPI.ts +42 -4
  15. package/Server/EnvironmentConfig.ts +112 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.ts +59 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.ts +25 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  19. package/Server/Middleware/MultipartFormData.ts +118 -32
  20. package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +9 -1
  21. package/Server/Services/AlertEpisodeOnCallRuleEngineService.ts +9 -1
  22. package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +9 -1
  23. package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +9 -1
  24. package/Server/Services/AlertGroupingEngineService.ts +9 -1
  25. package/Server/Services/AlertLabelRuleEngineService.ts +9 -1
  26. package/Server/Services/AlertOnCallRuleEngineService.ts +9 -1
  27. package/Server/Services/AlertOwnerRuleEngineService.ts +9 -1
  28. package/Server/Services/AlertPrivacyRuleEngineService.ts +9 -1
  29. package/Server/Services/AlertReminderRuleService.ts +9 -1
  30. package/Server/Services/AutoRemediationRuleEngineService.ts +9 -1
  31. package/Server/Services/CephClusterLabelRuleEngineService.ts +9 -1
  32. package/Server/Services/CephClusterOwnerRuleEngineService.ts +9 -1
  33. package/Server/Services/CloudResourceLabelRuleEngineService.ts +9 -1
  34. package/Server/Services/CloudResourceOwnerRuleEngineService.ts +9 -1
  35. package/Server/Services/DashboardLabelRuleEngineService.ts +9 -1
  36. package/Server/Services/DashboardOwnerRuleEngineService.ts +9 -1
  37. package/Server/Services/DockerHostLabelRuleEngineService.ts +9 -1
  38. package/Server/Services/DockerHostOwnerRuleEngineService.ts +9 -1
  39. package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +9 -1
  40. package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +9 -1
  41. package/Server/Services/HostLabelRuleEngineService.ts +9 -1
  42. package/Server/Services/HostOwnerRuleEngineService.ts +9 -1
  43. package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +9 -1
  44. package/Server/Services/IncidentEpisodeOnCallRuleEngineService.ts +9 -1
  45. package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +9 -1
  46. package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +9 -1
  47. package/Server/Services/IncidentGroupingEngineService.ts +9 -1
  48. package/Server/Services/IncidentLabelRuleEngineService.ts +9 -1
  49. package/Server/Services/IncidentOnCallRuleEngineService.ts +9 -1
  50. package/Server/Services/IncidentOwnerRuleEngineService.ts +9 -1
  51. package/Server/Services/IncidentPrivacyRuleEngineService.ts +9 -1
  52. package/Server/Services/IncidentReminderRuleService.ts +9 -1
  53. package/Server/Services/IncidentSlaRuleService.ts +9 -1
  54. package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +9 -1
  55. package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +9 -1
  56. package/Server/Services/Index.ts +2 -0
  57. package/Server/Services/IoTFleetLabelRuleEngineService.ts +9 -1
  58. package/Server/Services/IoTFleetOwnerRuleEngineService.ts +9 -1
  59. package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +9 -1
  60. package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +9 -1
  61. package/Server/Services/KubernetesResourceService.ts +5 -1
  62. package/Server/Services/MonitorLabelRuleEngineService.ts +9 -1
  63. package/Server/Services/MonitorOwnerRuleEngineService.ts +9 -1
  64. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +35 -0
  65. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +96 -0
  66. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +9 -1
  67. package/Server/Services/NetworkDeviceOidTemplateService.ts +201 -0
  68. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +9 -1
  69. package/Server/Services/NetworkDeviceService.ts +209 -1
  70. package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +9 -1
  71. package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +9 -1
  72. package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +9 -1
  73. package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +9 -1
  74. package/Server/Services/PodmanHostLabelRuleEngineService.ts +9 -1
  75. package/Server/Services/PodmanHostOwnerRuleEngineService.ts +9 -1
  76. package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +9 -1
  77. package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +9 -1
  78. package/Server/Services/RumApplicationLabelRuleEngineService.ts +9 -1
  79. package/Server/Services/RumApplicationOwnerRuleEngineService.ts +9 -1
  80. package/Server/Services/RunbookLabelRuleEngineService.ts +9 -1
  81. package/Server/Services/RunbookOwnerRuleEngineService.ts +9 -1
  82. package/Server/Services/RunbookRuleEngineService.ts +9 -1
  83. package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +9 -1
  84. package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +9 -1
  85. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +9 -1
  86. package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +9 -1
  87. package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +9 -1
  88. package/Server/Services/ServiceLabelRuleEngineService.ts +9 -1
  89. package/Server/Services/ServiceOwnerRuleEngineService.ts +9 -1
  90. package/Server/Services/StatusPageLabelRuleEngineService.ts +9 -1
  91. package/Server/Services/StatusPageMonitorRuleEngineService.ts +7 -0
  92. package/Server/Services/StatusPageOwnerRuleEngineService.ts +9 -1
  93. package/Server/Services/TelemetrySourceMapService.ts +266 -23
  94. package/Server/Services/UserService.ts +27 -0
  95. package/Server/Services/WorkflowLabelRuleEngineService.ts +9 -1
  96. package/Server/Services/WorkflowOwnerRuleEngineService.ts +9 -1
  97. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +186 -0
  98. package/Server/Utils/Monitor/CapturedMetricAttributeUtil.ts +245 -0
  99. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +21 -2
  100. package/Server/Utils/Monitor/MonitorMetricUtil.ts +55 -21
  101. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +32 -0
  102. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +15 -2
  103. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +18 -1
  104. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +10 -0
  105. package/Server/Utils/Monitor/SeriesResourceLabels.ts +34 -0
  106. package/Server/Utils/Rules/RuleEngineRuleRead.ts +48 -0
  107. package/Server/Utils/Telemetry/ExceptionSanitizer.ts +14 -1
  108. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +14 -2
  109. package/Server/Utils/Telemetry/SourceMapResolver.ts +16 -5
  110. package/Server/Utils/Telemetry/StackTraceParser.ts +399 -0
  111. package/Tests/App/Dashboard/EventOverlayHook.test.tsx +125 -0
  112. package/Tests/App/Dashboard/HeaderImageAssets.test.tsx +93 -0
  113. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3 -3
  114. package/Tests/App/Dashboard/UserElementEmail.test.tsx +12 -4
  115. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +1 -1
  116. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +14 -0
  117. package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +469 -0
  118. package/Tests/Models/SecurityDomainAccessControl.test.ts +429 -0
  119. package/Tests/Server/API/GlobalConfigLicense.test.ts +657 -0
  120. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +84 -6
  121. package/Tests/Server/API/TelemetryResolveStackTraceAPI.test.ts +805 -0
  122. package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +32 -7
  123. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +1144 -0
  124. package/Tests/Server/Middleware/MultipartFormData.test.ts +618 -3
  125. package/Tests/Server/Services/MonitorLabelRuleEngineScale.test.ts +520 -0
  126. package/Tests/Server/Services/NetworkDeviceOidTemplateService.test.ts +332 -0
  127. package/Tests/Server/Services/NetworkDeviceOidTemplateWriteGuards.test.ts +409 -0
  128. package/Tests/Server/Services/RuleEngineRuleFetchLimit.test.ts +239 -0
  129. package/Tests/Server/Services/TelemetrySourceMapService.test.ts +1210 -2
  130. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +182 -0
  131. package/Tests/Server/Types/AnalyticsDatabase/SecurityEventModelPermission.test.ts +255 -0
  132. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +212 -0
  133. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +413 -0
  134. package/Tests/Server/Utils/CodeRepository.test.ts +689 -0
  135. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +495 -0
  136. package/Tests/Server/Utils/ExceptionSanitizer.test.ts +77 -0
  137. package/Tests/Server/Utils/Monitor/CapturedMetricAttributeUtil.test.ts +284 -0
  138. package/Tests/Server/Utils/Monitor/MonitorCustomMetricAttributes.test.ts +503 -0
  139. package/Tests/Server/Utils/Monitor/MonitorTemplatePrototypePollution.test.ts +106 -0
  140. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkLogWritePath.test.ts +33 -4
  141. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +14 -4
  142. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +44 -0
  143. package/Tests/Server/Utils/OpenAPISpec.test.ts +536 -0
  144. package/Tests/Server/Utils/Rules/RuleEngineRuleRead.test.ts +115 -0
  145. package/Tests/Server/Utils/StackTraceParser.test.ts +610 -0
  146. package/Tests/Server/Utils/Telemetry/LogExceptionExtractor.test.ts +0 -0
  147. package/Tests/Server/Utils/Telemetry/SourceMapLimits.test.ts +656 -0
  148. package/Tests/Server/Utils/Telemetry/SourceMapResolver.test.ts +100 -0
  149. package/Tests/Types/Monitor/SnmpMonitor/SnmpOidListUtil.test.ts +454 -0
  150. package/Tests/Types/SecurityRolePermissions.test.ts +193 -0
  151. package/Tests/UI/Components/Charts/AnnotationLayout.test.ts +773 -0
  152. package/Tests/UI/Components/Charts/ChartAnnotationRail.test.tsx +789 -0
  153. package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +254 -0
  154. package/Tests/UI/Components/Charts/TimeAnnotation.test.ts +471 -0
  155. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +405 -0
  156. package/Tests/UI/Components/ErrorMessageRefreshControl.test.tsx +83 -0
  157. package/Tests/UI/Components/MarkdownConverters.test.ts +686 -0
  158. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteAndEmptyStates.test.tsx +341 -0
  159. package/Tests/UI/Components/OrderedStatesList.test.tsx +5 -5
  160. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +1 -1
  161. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +195 -0
  162. package/Tests/UI/Components/XAxis.test.ts +270 -5
  163. package/Tests/UI/Components/XAxisDaylightSaving.test.ts +113 -0
  164. package/Tests/UI/Styles/ChartFocusRing.test.ts +153 -0
  165. package/Tests/UI/Styles/ChartFocusRingCoverage.test.tsx +377 -0
  166. package/Tests/UI/Styles/ThemeStylesheet.ts +162 -0
  167. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +507 -0
  168. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +951 -1
  169. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +238 -0
  170. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +1056 -0
  171. package/Tests/Utils/NetworkDiscovery/ReverseDnsNameUtil.test.ts +882 -0
  172. package/Types/Date.ts +39 -0
  173. package/Types/Monitor/SnmpMonitor/SnmpOidListUtil.ts +333 -0
  174. package/Types/Permission.ts +96 -0
  175. package/Types/Rum/SessionReplay.ts +21 -0
  176. package/Types/Telemetry/SourceMap.ts +8 -0
  177. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.tsx +176 -0
  178. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.ts +745 -0
  179. package/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.tsx +576 -0
  180. package/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.tsx +204 -0
  181. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +491 -544
  182. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +324 -372
  183. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +487 -540
  184. package/UI/Components/Charts/ChartLibrary/Types/FormattedReferenceRegion.ts +7 -3
  185. package/UI/Components/Charts/ChartLibrary/Types/FormattedTimeReferenceLine.ts +14 -3
  186. package/UI/Components/Charts/Types/ChartEventKind.ts +13 -0
  187. package/UI/Components/Charts/Types/ReferenceRegionProps.ts +6 -0
  188. package/UI/Components/Charts/Types/TimeReferenceLineProps.ts +20 -3
  189. package/UI/Components/Charts/Utils/TimeAnnotation.ts +69 -35
  190. package/UI/Components/Charts/Utils/XAxis.ts +125 -18
  191. package/UI/Components/EditionLabel/EditionLabel.tsx +280 -26
  192. package/UI/Components/ErrorMessage/ErrorMessage.tsx +21 -10
  193. package/UI/Components/Feed/FeedItem.tsx +4 -5
  194. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -2
  195. package/UI/Components/Header/UserProfile/UserProfile.tsx +1 -2
  196. package/UI/Components/Image/Image.tsx +20 -3
  197. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  198. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +1 -2
  199. package/UI/Components/ModelTable/BaseModelTable.tsx +150 -24
  200. package/UI/Components/Table/TableHeader.tsx +64 -48
  201. package/UI/Components/Table/TableRow.tsx +13 -3
  202. package/UI/Styles/Theme.css +38 -0
  203. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +271 -0
  204. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +6 -0
  205. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +141 -10
  206. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +55 -3
  207. package/Utils/NetworkDiscovery/ReverseDnsNameUtil.ts +151 -0
  208. package/Utils/Rules/RuleEngineLimits.ts +32 -0
  209. package/build/dist/Models/AnalyticsModels/SecurityEvent.js +48 -16
  210. package/build/dist/Models/AnalyticsModels/SecurityEvent.js.map +1 -1
  211. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js +10 -5
  212. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/AlertSeverity.js +46 -1
  214. package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/DetectionRule.js +16 -5
  216. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +14 -5
  218. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -1
  219. package/build/dist/Models/DatabaseModels/IncidentSeverity.js +46 -1
  220. package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
  221. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  222. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  223. package/build/dist/Models/DatabaseModels/NetworkDevice.js +115 -3
  224. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  225. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +88 -0
  226. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
  227. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  228. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +519 -0
  229. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -0
  230. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js +16 -5
  231. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js.map +1 -1
  232. package/build/dist/Server/API/GlobalConfigAPI.js +293 -176
  233. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  234. package/build/dist/Server/API/TelemetryAPI.js +31 -3
  235. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  236. package/build/dist/Server/EnvironmentConfig.js +83 -0
  237. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js +26 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js.map +1 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js +14 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js.map +1 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  244. package/build/dist/Server/Middleware/MultipartFormData.js +93 -28
  245. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  246. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +8 -1
  247. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
  248. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js +8 -1
  249. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js.map +1 -1
  250. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +8 -1
  251. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
  252. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +8 -1
  253. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
  254. package/build/dist/Server/Services/AlertGroupingEngineService.js +8 -1
  255. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
  256. package/build/dist/Server/Services/AlertLabelRuleEngineService.js +8 -1
  257. package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
  258. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js +8 -1
  259. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js.map +1 -1
  260. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +8 -1
  261. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
  262. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +8 -1
  263. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
  264. package/build/dist/Server/Services/AlertReminderRuleService.js +8 -1
  265. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  266. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +8 -1
  267. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  268. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +8 -1
  269. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
  270. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +8 -1
  271. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
  272. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +8 -1
  273. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
  274. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +8 -1
  275. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
  276. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +8 -1
  277. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
  278. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +8 -1
  279. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
  280. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +8 -1
  281. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
  282. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +8 -1
  283. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
  284. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +8 -1
  285. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
  286. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +8 -1
  287. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
  288. package/build/dist/Server/Services/HostLabelRuleEngineService.js +8 -1
  289. package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
  290. package/build/dist/Server/Services/HostOwnerRuleEngineService.js +8 -1
  291. package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
  292. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +8 -1
  293. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
  294. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js +8 -1
  295. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js.map +1 -1
  296. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +8 -1
  297. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
  298. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +8 -1
  299. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
  300. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -1
  301. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  302. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +8 -1
  303. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
  304. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js +8 -1
  305. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js.map +1 -1
  306. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +8 -1
  307. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
  308. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +8 -1
  309. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
  310. package/build/dist/Server/Services/IncidentReminderRuleService.js +8 -1
  311. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  312. package/build/dist/Server/Services/IncidentSlaRuleService.js +8 -1
  313. package/build/dist/Server/Services/IncidentSlaRuleService.js.map +1 -1
  314. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +8 -1
  315. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
  316. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +8 -1
  317. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
  318. package/build/dist/Server/Services/Index.js +2 -0
  319. package/build/dist/Server/Services/Index.js.map +1 -1
  320. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +8 -1
  321. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
  322. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +8 -1
  323. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
  324. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +8 -1
  325. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
  326. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +8 -1
  327. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
  328. package/build/dist/Server/Services/KubernetesResourceService.js +5 -1
  329. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  330. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +8 -1
  331. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
  332. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +8 -1
  333. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
  334. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +30 -21
  335. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  336. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +64 -0
  337. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  338. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +8 -1
  339. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  340. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js +180 -0
  341. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js.map +1 -0
  342. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +8 -1
  343. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
  344. package/build/dist/Server/Services/NetworkDeviceService.js +154 -1
  345. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  346. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +8 -1
  347. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
  348. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +8 -1
  349. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
  350. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +8 -1
  351. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
  352. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +8 -1
  353. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
  354. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +8 -1
  355. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
  356. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +8 -1
  357. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
  358. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +8 -1
  359. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
  360. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +8 -1
  361. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
  362. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +8 -1
  363. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
  364. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +8 -1
  365. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
  366. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +8 -1
  367. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
  368. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +8 -1
  369. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
  370. package/build/dist/Server/Services/RunbookRuleEngineService.js +8 -1
  371. package/build/dist/Server/Services/RunbookRuleEngineService.js.map +1 -1
  372. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +8 -1
  373. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
  374. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +8 -1
  375. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
  376. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +8 -1
  377. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  378. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +8 -1
  379. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
  380. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +8 -1
  381. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
  382. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +8 -1
  383. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
  384. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +8 -1
  385. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
  386. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +8 -1
  387. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
  388. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +6 -0
  389. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -1
  390. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +8 -1
  391. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
  392. package/build/dist/Server/Services/TelemetrySourceMapService.js +213 -19
  393. package/build/dist/Server/Services/TelemetrySourceMapService.js.map +1 -1
  394. package/build/dist/Server/Services/UserService.js +25 -0
  395. package/build/dist/Server/Services/UserService.js.map +1 -1
  396. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +8 -1
  397. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
  398. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +8 -1
  399. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
  400. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +145 -0
  401. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +1 -0
  402. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js +201 -0
  403. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js.map +1 -0
  404. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +19 -2
  405. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  406. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +42 -23
  407. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  408. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +29 -0
  409. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  410. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +11 -2
  411. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  412. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +16 -1
  413. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  414. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +10 -0
  415. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  416. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +33 -0
  417. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  418. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js +37 -0
  419. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js.map +1 -0
  420. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +12 -1
  421. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -1
  422. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +14 -2
  423. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  424. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js +15 -5
  425. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js.map +1 -1
  426. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js +307 -0
  427. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js.map +1 -1
  428. package/build/dist/Types/Date.js +27 -0
  429. package/build/dist/Types/Date.js.map +1 -1
  430. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js +253 -0
  431. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js.map +1 -0
  432. package/build/dist/Types/Permission.js +86 -0
  433. package/build/dist/Types/Permission.js.map +1 -1
  434. package/build/dist/Types/Rum/SessionReplay.js +19 -0
  435. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  436. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js +83 -0
  437. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js.map +1 -0
  438. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js +531 -0
  439. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js.map +1 -0
  440. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js +264 -0
  441. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js.map +1 -0
  442. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js +122 -0
  443. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js.map +1 -0
  444. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +213 -233
  445. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  446. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +139 -152
  447. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  448. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +212 -232
  449. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  450. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js +14 -0
  451. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js.map +1 -0
  452. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +57 -28
  453. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
  454. package/build/dist/UI/Components/Charts/Utils/XAxis.js +103 -17
  455. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  456. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +198 -23
  457. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  458. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +15 -5
  459. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
  460. package/build/dist/UI/Components/Feed/FeedItem.js +4 -5
  461. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  462. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -2
  463. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  464. package/build/dist/UI/Components/Header/UserProfile/UserProfile.js.map +1 -1
  465. package/build/dist/UI/Components/Image/Image.js +6 -1
  466. package/build/dist/UI/Components/Image/Image.js.map +1 -1
  467. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  468. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  469. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +1 -2
  470. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  471. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +104 -23
  472. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  473. package/build/dist/UI/Components/Table/TableHeader.js +20 -22
  474. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  475. package/build/dist/UI/Components/Table/TableRow.js +13 -4
  476. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  477. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +153 -0
  478. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -0
  479. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  480. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +127 -7
  481. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  482. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +50 -3
  483. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  484. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js +132 -0
  485. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js.map +1 -0
  486. package/build/dist/Utils/Rules/RuleEngineLimits.js +31 -0
  487. package/build/dist/Utils/Rules/RuleEngineLimits.js.map +1 -0
  488. package/jest.config.json +1 -0
  489. package/package.json +2 -2
@@ -4,6 +4,7 @@ import {
4
4
  buildDeviceName,
5
5
  buildFallbackDeviceName,
6
6
  buildNetworkDeviceFromDiscoveredHost,
7
+ getDiscoveredHostDisplayName,
7
8
  DiscoveredDeviceScanSource,
8
9
  } from "../../../Utils/NetworkDiscovery/DiscoveredDeviceBuilder";
9
10
  import NetworkDevice from "../../../Models/DatabaseModels/NetworkDevice";
@@ -11,7 +12,10 @@ import { DiscoveredNetworkDevice } from "../../../Models/DatabaseModels/NetworkD
11
12
  import NetworkDeviceMonitoringMethod from "../../../Types/NetworkDevice/NetworkDeviceMonitoringMethod";
12
13
  import ObjectID from "../../../Types/ObjectID";
13
14
  import { DiscoveryScanSnmpConfig } from "../../../Utils/NetworkDiscovery/SnmpScanConfigUtil";
14
- import { describe, expect, it } from "@jest/globals";
15
+ import { MAX_REVERSE_DNS_NAME_LENGTH } from "../../../Utils/NetworkDiscovery/ReverseDnsNameUtil";
16
+ import ColumnLength from "../../../Types/Database/ColumnLength";
17
+ import Slug from "../../../Utils/Slug";
18
+ import { describe, expect, it, test } from "@jest/globals";
15
19
 
16
20
  /*
17
21
  * Contract under test — one discovered host maps to one NetworkDevice the
@@ -555,3 +559,949 @@ describe("the name override", () => {
555
559
  expect(device.hostname).toBe("10.0.0.5");
556
560
  });
557
561
  });
562
+
563
+ /*
564
+ * OneUptime issue #3529 — "Network Discovery Scan should perform reverse DNS
565
+ * lookup and display hostnames".
566
+ *
567
+ * The reporter's Review dialog listed 10.18.166.51, .53, .54, .55 on an
568
+ * estate where every one of those addresses has a DNS record. Those rows are
569
+ * hosts with no readable SNMP: with no sysName, `sysName || ipAddress` had
570
+ * nothing left to fall back to. `dnsHostname` is the missing middle term.
571
+ *
572
+ * What these cases pin is the ORDER, and the order is a deliberate judgement
573
+ * rather than a reading of the issue text. The issue asks for the hostname to
574
+ * be "the device name"; sysName is nonetheless kept ahead of it, because
575
+ * sysName is the name the device asserts about itself, it is what every scan
576
+ * imported under before this existed, and demoting it would silently rename
577
+ * devices that already import correctly for people who never asked for
578
+ * anything to change. The PTR name lands exactly where the complaint was —
579
+ * the hosts that had no name at all — and the dialog surfaces both when they
580
+ * disagree, so nothing is hidden by the choice.
581
+ */
582
+ describe("the reverse-DNS name (issue #3529)", () => {
583
+ describe("precedence", () => {
584
+ test("a host with no sysName is named by its PTR record", () => {
585
+ // The reported case, in one line.
586
+ expect(
587
+ buildDeviceName({
588
+ ipAddress: "10.18.166.51",
589
+ dnsHostname: "core-gw.corp.example.com",
590
+ }),
591
+ ).toBe("core-gw.corp.example.com");
592
+ });
593
+
594
+ test("sysName still wins when the host has both", () => {
595
+ expect(
596
+ buildDeviceName({
597
+ ipAddress: "10.18.166.51",
598
+ sysName: "core-switch-01",
599
+ dnsHostname: "sw1.corp.example.com",
600
+ }),
601
+ ).toBe("core-switch-01");
602
+ });
603
+
604
+ test("a blank sysName falls through to the PTR name", () => {
605
+ /*
606
+ * A whitespace-only sysName is truthy and is a real thing to read off a
607
+ * device with a half-configured system group. It used to produce a
608
+ * device named " ".
609
+ */
610
+ expect(
611
+ buildDeviceName({
612
+ ipAddress: "10.18.166.51",
613
+ sysName: " ",
614
+ dnsHostname: "sw1.corp.example.com",
615
+ }),
616
+ ).toBe("sw1.corp.example.com");
617
+ });
618
+
619
+ test("a padded sysName is trimmed before it becomes the name", () => {
620
+ /*
621
+ * The whitespace-ONLY case above pins the fall-through; this pins the
622
+ * other half of the same `.trim()`, which nothing asserted. A padded
623
+ * DisplayString is ordinary on real gear, and the padding must not
624
+ * survive: leading spaces go into the varchar, into the slug, and into
625
+ * every site/label rule that matches on the name. Delete the `.trim()`
626
+ * and the sysName is still truthy, still wins the contest, and this
627
+ * reads " core-switch-01 " instead.
628
+ */
629
+ const paddedHost: DiscoveredNetworkDevice = {
630
+ ipAddress: "10.18.166.51",
631
+ sysName: " core-switch-01 ",
632
+ dnsHostname: "sw1.corp.example.com",
633
+ };
634
+
635
+ expect(getDiscoveredHostDisplayName(paddedHost)).toBe("core-switch-01");
636
+ expect(build({ host: paddedHost }).name).toBe("core-switch-01");
637
+ });
638
+
639
+ test("the address is still the last resort", () => {
640
+ expect(buildDeviceName({ ipAddress: "10.18.166.51" })).toBe(
641
+ "10.18.166.51",
642
+ );
643
+ expect(
644
+ buildDeviceName({ ipAddress: "10.18.166.51", dnsHostname: "" }),
645
+ ).toBe("10.18.166.51");
646
+ });
647
+
648
+ test("a host stored before the field existed is named exactly as it was", () => {
649
+ /*
650
+ * Back-compat, and it is not hypothetical: every scan result already in
651
+ * the database predates this field, as does every result from a probe
652
+ * that has not been upgraded yet.
653
+ */
654
+ expect(
655
+ buildDeviceName({ ipAddress: "10.0.0.5", sysName: "core-switch-01" }),
656
+ ).toBe("core-switch-01");
657
+ expect(buildDeviceName({ ipAddress: "10.0.0.5" })).toBe("10.0.0.5");
658
+ });
659
+ });
660
+
661
+ describe("the display name and the device name agree", () => {
662
+ /*
663
+ * The operator ticks a box next to a name and gets a device with that
664
+ * name. The two used to be spelled out separately — the dialog said
665
+ * `sysName || ipAddress` and the builder said the same thing again — and
666
+ * that duplication is what this shared function exists to end.
667
+ */
668
+ test("the device is created under the clamped display name", () => {
669
+ /*
670
+ * Rows 1-4 are the four naming outcomes; row 5 is what makes the loop
671
+ * mean anything. Every fixture used to be far under the 80-character
672
+ * ceiling, so `truncate` was the identity function on all of them and
673
+ * "buildDeviceName === the display name" held for ANY clamp, including
674
+ * no clamp at all. The long-PTR row is asserted to be genuinely cut at
675
+ * the bottom, so deleting the truncate() reddens this.
676
+ */
677
+ const longPtrName: string = `${"a".repeat(63)}.${"b".repeat(
678
+ 40,
679
+ )}.example.com`;
680
+
681
+ const hosts: Array<DiscoveredNetworkDevice> = [
682
+ { ipAddress: "10.0.0.1" },
683
+ { ipAddress: "10.0.0.2", dnsHostname: "gw.corp.example.com" },
684
+ { ipAddress: "10.0.0.3", sysName: "sw-3" },
685
+ {
686
+ ipAddress: "10.0.0.4",
687
+ sysName: "sw-4",
688
+ dnsHostname: "sw4.corp.example.com",
689
+ },
690
+ { ipAddress: "10.0.0.5", dnsHostname: longPtrName },
691
+ ];
692
+
693
+ for (const discoveredHost of hosts) {
694
+ const displayed: string = getDiscoveredHostDisplayName(discoveredHost);
695
+
696
+ expect(buildDeviceName(discoveredHost)).toBe(
697
+ displayed.substring(0, MAX_DEVICE_NAME_LENGTH),
698
+ );
699
+
700
+ /*
701
+ * And the DEVICE carries that name. This is the half with teeth: the
702
+ * operator ticks a box next to the displayed name, and `device.name`
703
+ * is what the create writes. An edit that gave the builder its own
704
+ * naming rule would pass the assertion above and fail this one.
705
+ */
706
+ expect(build({ host: discoveredHost }).name).toBe(
707
+ buildDeviceName(discoveredHost),
708
+ );
709
+ }
710
+
711
+ // The clamp is exercised rather than skipped: row 5 really is cut.
712
+ const clampedHost: DiscoveredNetworkDevice = hosts[
713
+ hosts.length - 1
714
+ ] as DiscoveredNetworkDevice;
715
+
716
+ expect(getDiscoveredHostDisplayName(clampedHost)).not.toBe(
717
+ buildDeviceName(clampedHost),
718
+ );
719
+ });
720
+
721
+ test("the display name is returned unclamped", () => {
722
+ /*
723
+ * The 80-character ceiling exists for the SLUG, not for the eye. A
724
+ * dialog row has its own truncation, and showing the operator a name cut
725
+ * at a different point than the device gets is a smaller problem than
726
+ * pretending the ceiling is a display concern.
727
+ */
728
+ const longName: string = `${"a".repeat(63)}.${"b".repeat(40)}.example.com`;
729
+
730
+ expect(
731
+ getDiscoveredHostDisplayName({
732
+ ipAddress: "10.0.0.1",
733
+ dnsHostname: longName,
734
+ }),
735
+ ).toBe(longName);
736
+ expect(
737
+ buildDeviceName({ ipAddress: "10.0.0.1", dnsHostname: longName }),
738
+ ).toHaveLength(MAX_DEVICE_NAME_LENGTH);
739
+ });
740
+ });
741
+
742
+ describe("an untrusted PTR record never becomes a name", () => {
743
+ /*
744
+ * `dnsHostname` is the ONE field in a scan result whose value is chosen by
745
+ * the scanned network. The probe normalises it, and normalizeDiscoveredHosts
746
+ * normalises it again on the way out of the jsonb — but this function is the
747
+ * last point before the value becomes a rendered line and a slugified device
748
+ * name, and a row written by an older probe or straight through the API
749
+ * reaches it without having passed either.
750
+ */
751
+ test("falls back to the address rather than naming a device after markup", () => {
752
+ expect(
753
+ buildDeviceName({
754
+ ipAddress: "10.18.166.51",
755
+ dnsHostname: "<script>alert(1)</script>",
756
+ }),
757
+ ).toBe("10.18.166.51");
758
+ });
759
+
760
+ test("falls back to the address for a name with whitespace in it", () => {
761
+ expect(
762
+ buildDeviceName({
763
+ ipAddress: "10.18.166.51",
764
+ dnsHostname: "core switch",
765
+ }),
766
+ ).toBe("10.18.166.51");
767
+ });
768
+
769
+ test("falls back to the address for a PTR that merely restates it", () => {
770
+ /*
771
+ * Not a name. Showing it as one would assert a resolved hostname nobody
772
+ * published, which is worse than showing the address as an address.
773
+ */
774
+ expect(
775
+ buildDeviceName({
776
+ ipAddress: "10.18.166.51",
777
+ dnsHostname: "10.18.166.51",
778
+ }),
779
+ ).toBe("10.18.166.51");
780
+ expect(
781
+ buildDeviceName({
782
+ ipAddress: "10.18.166.51",
783
+ dnsHostname: "51.166.18.10.in-addr.arpa",
784
+ }),
785
+ ).toBe("10.18.166.51");
786
+ });
787
+
788
+ test("neither throws nor stringifies on a non-string column", () => {
789
+ /*
790
+ * Two halves, because they fail against two different mutations.
791
+ *
792
+ * 51 is the jsonb-read-at-render-time case: an unguarded `.trim()` is a
793
+ * TypeError thrown inside the modal body. On its own it cannot tell a
794
+ * type-checking normaliser from a COERCING one, because String(51) is
795
+ * "51" and a single all-numeric label is refused anyway.
796
+ */
797
+ expect(
798
+ buildDeviceName({
799
+ ipAddress: "10.18.166.51",
800
+ dnsHostname: 51 as unknown as string,
801
+ }),
802
+ ).toBe("10.18.166.51");
803
+
804
+ /*
805
+ * `true` is the half that can. String(true) is "true" — a syntactically
806
+ * perfect single label that passes every content rule in
807
+ * ReverseDnsNameUtil. Swap the `typeof value !== "string"` guard for
808
+ * String(value) and this host is named "true" instead of by its
809
+ * address, and this line is the only thing in the file that notices.
810
+ */
811
+ expect(
812
+ buildDeviceName({
813
+ ipAddress: "10.18.166.51",
814
+ dnsHostname: true as unknown as string,
815
+ }),
816
+ ).toBe("10.18.166.51");
817
+ });
818
+
819
+ test("stores the normalised form, not the raw answer", () => {
820
+ expect(
821
+ buildDeviceName({
822
+ ipAddress: "10.0.0.1",
823
+ dnsHostname: " gw.corp.example.com. ",
824
+ }),
825
+ ).toBe("gw.corp.example.com");
826
+ });
827
+ });
828
+
829
+ describe("the rest of the device", () => {
830
+ test("the address stays the hostname even when the host resolved a name", () => {
831
+ /*
832
+ * The issue asks for this in as many words ("retain the IP address as
833
+ * the address/IP field"), and the system needs it: `hostname` is the
834
+ * registered-host dedup key, what the SNMP poller dials, and what a
835
+ * trap's source IP correlates to. A name here would make a device stop
836
+ * polling the day its reverse zone changed.
837
+ */
838
+ const device: NetworkDevice = buildNetworkDeviceFromDiscoveredHost({
839
+ projectId: PROJECT_ID,
840
+ host: {
841
+ ipAddress: "10.18.166.51",
842
+ dnsHostname: "core-gw.corp.example.com",
843
+ },
844
+ scan: fullScanSource(),
845
+ });
846
+
847
+ expect(device.hostname).toBe("10.18.166.51");
848
+ expect(device.name).toBe("core-gw.corp.example.com");
849
+ });
850
+
851
+ test("a ping-only host still imports as a monitor-backed device", () => {
852
+ /*
853
+ * Naming must not change WHAT a host imports as. A PTR record says
854
+ * nothing about whether the device can be SNMP-polled.
855
+ */
856
+ const device: NetworkDevice = buildNetworkDeviceFromDiscoveredHost({
857
+ projectId: PROJECT_ID,
858
+ host: {
859
+ ipAddress: "10.18.166.51",
860
+ snmpReachable: false,
861
+ dnsHostname: "cam-lobby.corp.example.com",
862
+ },
863
+ scan: fullScanSource(),
864
+ });
865
+
866
+ expect(device.name).toBe("cam-lobby.corp.example.com");
867
+ expect(device.monitoringMethod).toBe(
868
+ NetworkDeviceMonitoringMethod.Monitor,
869
+ );
870
+ expect(device.isPollingEnabled).toBe(false);
871
+ expect(device.snmpCommunityString).toBeUndefined();
872
+ });
873
+
874
+ test("the collision fallback appends the address to the PTR name", () => {
875
+ /*
876
+ * Two devices legitimately sharing a name is common — and a shared PTR
877
+ * name is if anything MORE likely than a shared sysName, because a
878
+ * wildcard reverse zone hands every address in a range the same answer.
879
+ * The address is what tells them apart.
880
+ */
881
+ expect(
882
+ buildFallbackDeviceName({
883
+ ipAddress: "10.18.166.51",
884
+ dnsHostname: "dhcp-pool.corp.example.com",
885
+ }),
886
+ ).toBe("dhcp-pool.corp.example.com (10.18.166.51)");
887
+ });
888
+ });
889
+
890
+ /*
891
+ * `sysName` is read out of the SAME verbatim jsonb blob as `dnsHostname`,
892
+ * so its declared `string | undefined` type is a description of what the
893
+ * probe SHOULD send rather than a fact about what is stored. Since
894
+ * getDiscoveredHostDisplayName became the Review dialog's name line, a
895
+ * `(42).trim()` TypeError in here is thrown DURING RENDER: React unmounts
896
+ * the subtree, so one malformed row takes out the entire modal and the
897
+ * operator cannot import any of the hosts the scan found, not just that
898
+ * one. Each of these must fall through to the next naming source instead.
899
+ */
900
+ describe("an untrusted sysName never reaches .trim()", () => {
901
+ const UNTRUSTED_SYS_NAMES: Array<{ reason: string; value: unknown }> = [
902
+ /*
903
+ * An agent whose sysName OID returned an INTEGER rather than a
904
+ * DisplayString. Discriminating despite "42" being a poor name: unlike
905
+ * `dnsHostname` there is no normaliser behind `sysName ||`, so a
906
+ * String() coercion makes "42" TRUTHY and it wins the naming contest
907
+ * outright, beating a perfectly good PTR record on the same row.
908
+ *
909
+ * (The boolean row that sat here was removed: `true` fails
910
+ * `typeof === "string"` and coerces to a truthy string in exactly the
911
+ * same way this one does, so it could not fail unless this one did.)
912
+ */
913
+ { reason: "a number", value: 42 },
914
+ // What a hand-written API row looks like when someone nests the name.
915
+ { reason: "an object", value: { name: "core-switch-01" } },
916
+ /*
917
+ * The sharpest of the four: `["sw-1"].toString()` is "sw-1", so a
918
+ * String() coercion instead of the typeof guard would quietly ACCEPT
919
+ * this and name a device after a JSON array's join.
920
+ */
921
+ { reason: "an array", value: ["sw-1"] },
922
+ /*
923
+ * jsonb stores an explicit null. The old WHY here claimed `??` would
924
+ * miss it, which is backwards — `null ?? ""` is "". The failures this
925
+ * row really pins are the other two shapes the guard could have taken:
926
+ * `host.sysName !== undefined ? host.sysName.trim() : ""` THROWS on
927
+ * null, and `String(null)` is the truthy "null", which would beat the
928
+ * PTR record on the same row and create a device called "null".
929
+ */
930
+ { reason: "null", value: null },
931
+ ];
932
+
933
+ for (const untrusted of UNTRUSTED_SYS_NAMES) {
934
+ test(`${untrusted.reason} sysName falls through to the PTR name`, () => {
935
+ const host: DiscoveredNetworkDevice = {
936
+ ipAddress: "10.18.166.51",
937
+ sysName: untrusted.value as unknown as string,
938
+ dnsHostname: "core-gw.corp.example.com",
939
+ };
940
+
941
+ expect(getDiscoveredHostDisplayName(host)).toBe(
942
+ "core-gw.corp.example.com",
943
+ );
944
+ expect(buildDeviceName(host)).toBe("core-gw.corp.example.com");
945
+ });
946
+
947
+ test(`${untrusted.reason} sysName falls through to the address`, () => {
948
+ // The same host on an estate with no reverse zone: nothing left but the IP.
949
+ const host: DiscoveredNetworkDevice = {
950
+ ipAddress: "10.18.166.51",
951
+ sysName: untrusted.value as unknown as string,
952
+ };
953
+
954
+ expect(getDiscoveredHostDisplayName(host)).toBe("10.18.166.51");
955
+ expect(buildDeviceName(host)).toBe("10.18.166.51");
956
+ });
957
+ }
958
+ });
959
+
960
+ /*
961
+ * The last resort is the last thing standing between the Review dialog and
962
+ * a blank modal, so it may not throw either — `String(...)` rather than
963
+ * `host.ipAddress.trim()` or a template read of a property.
964
+ */
965
+ describe("an untrusted address as the last resort", () => {
966
+ test("a numeric address still names the row", () => {
967
+ expect(buildDeviceName({ ipAddress: 42 as unknown as string })).toBe(
968
+ "42",
969
+ );
970
+ });
971
+
972
+ test("a null address degrades to an empty name rather than a throw", () => {
973
+ /*
974
+ * Empty is not a good name; it is a name the create path rejects with
975
+ * a validation error the operator can see, which is strictly better
976
+ * than a TypeError that removes the dialog.
977
+ */
978
+ expect(
979
+ getDiscoveredHostDisplayName({ ipAddress: null as unknown as string }),
980
+ ).toBe("");
981
+ });
982
+
983
+ test("an object address is named by its coercion, not by a throw", () => {
984
+ /*
985
+ * The assertion here was `expect(typeof name).toBe("string")` against a
986
+ * function whose declared return type IS string: the only way it could
987
+ * fail was a throw, and it passed happily with the device named
988
+ * "[object Object]". Pin the value instead.
989
+ *
990
+ * "[object Object]" is a terrible device name and that is the point: it
991
+ * is a name the operator can SEE is wrong and that the create path
992
+ * rejects on its own merits, rather than a TypeError that unmounts the
993
+ * Review dialog. Delete the String(...) coercion and the expression
994
+ * yields the object itself; replace it with a template read and this
995
+ * still holds, which is why the null case above is asserted too.
996
+ */
997
+ expect(
998
+ buildDeviceName({
999
+ ipAddress: { v4: "10.18.166.51" } as unknown as string,
1000
+ }),
1001
+ ).toBe("[object Object]");
1002
+ });
1003
+
1004
+ test("a non-string address is NAMED by coercion but ADDRESSED raw", () => {
1005
+ /*
1006
+ * BOTH readings coerce, and the hostname one matters more than the name.
1007
+ *
1008
+ * `hostname` is the registered-host dedup key:
1009
+ * NetworkDeviceService.getRegisteredHostnames matches it with
1010
+ * `Set.has()` against hostnames read back out of the database as
1011
+ * strings, and `Set.has` does not coerce. A device stored with the
1012
+ * NUMBER 42 in that column would therefore never match its own
1013
+ * registration, so the host would read as unregistered on every review
1014
+ * and import again, and again.
1015
+ *
1016
+ * The dashboard path never reached that, because normalizeDiscoveredHosts
1017
+ * stringifies the address first. The builder is also called directly by
1018
+ * the rule engine, so it coerces on its own account rather than relying
1019
+ * on a caller having been careful.
1020
+ */
1021
+ const device: NetworkDevice = build({
1022
+ host: { ipAddress: 42 as unknown as string },
1023
+ });
1024
+
1025
+ expect(device.name).toBe("42");
1026
+ expect(device.hostname).toBe("42");
1027
+ expect(typeof device.hostname).toBe("string");
1028
+ });
1029
+ });
1030
+
1031
+ /*
1032
+ * A dnsHostname that is not a string at all, alongside the number and
1033
+ * boolean cases above.
1034
+ *
1035
+ * Every fixture here is chosen so that String(value) is a name the
1036
+ * normaliser would HAPPILY ACCEPT, because that is the only kind that
1037
+ * discriminates. The plain object `{ name: "gw.corp.example.com" }` that
1038
+ * used to sit here proved nothing: String() of it is "[object Object]",
1039
+ * whose spaces and brackets LABEL_PATTERN refuses on their own, so the
1040
+ * case passed against a coercing normaliser exactly as well as against the
1041
+ * type-checking one it claimed to be about.
1042
+ */
1043
+ describe("an untrusted PTR record of the wrong TYPE", () => {
1044
+ const WRONGLY_TYPED_PTR_ANSWERS: Array<{
1045
+ reason: string;
1046
+ value: unknown;
1047
+ }> = [
1048
+ /*
1049
+ * The realistic one: dns.reverse() answers with an ARRAY of names, and
1050
+ * a probe that forgot to take [0] stores the array. `["gw.corp.
1051
+ * example.com"].toString()` is exactly "gw.corp.example.com", so a
1052
+ * String() coercion would store it verbatim as the device name.
1053
+ */
1054
+ {
1055
+ reason: "the resolver's whole answer array",
1056
+ value: ["gw.corp.example.com"],
1057
+ },
1058
+ /*
1059
+ * Not a shape JSON.parse can produce, and deliberately so: it is the
1060
+ * question in its pure form. String(value) here IS, byte for byte, the
1061
+ * hostname that would otherwise be stored. Only a real
1062
+ * `typeof value !== "string"` test rejects it — a coercion, a duck-type
1063
+ * ("does it have .trim()?") or an `instanceof String` check would all
1064
+ * name the device gw.corp.example.com off a value that is not a string.
1065
+ */
1066
+ {
1067
+ reason: "a string-like object",
1068
+ value: {
1069
+ toString: (): string => {
1070
+ return "gw.corp.example.com";
1071
+ },
1072
+ },
1073
+ },
1074
+ ];
1075
+
1076
+ for (const answer of WRONGLY_TYPED_PTR_ANSWERS) {
1077
+ test(`${answer.reason} falls back to the address`, () => {
1078
+ const hostWithBadPtr: DiscoveredNetworkDevice = {
1079
+ ipAddress: "10.18.166.51",
1080
+ dnsHostname: answer.value as unknown as string,
1081
+ };
1082
+
1083
+ expect(getDiscoveredHostDisplayName(hostWithBadPtr)).toBe(
1084
+ "10.18.166.51",
1085
+ );
1086
+ // The device, not just the name: a coercion would reach the column.
1087
+ expect(build({ host: hostWithBadPtr }).name).toBe("10.18.166.51");
1088
+ });
1089
+ }
1090
+ });
1091
+
1092
+ /*
1093
+ * THE WILDCARD REVERSE ZONE.
1094
+ *
1095
+ * A DHCP range is routinely published as one record —
1096
+ * `*.166.18.10.in-addr.arpa IN PTR dhcp-pool.corp.example.com` — so every
1097
+ * address in the range resolves to the SAME name. Nothing about that answer
1098
+ * is malformed, so normalisation keeps it, and the estate the issue came
1099
+ * from is exactly the kind that has one.
1100
+ *
1101
+ * Device names are unique per project, so the second host of such a range
1102
+ * fails its create on a duplicate name. That is what the dashboard's import
1103
+ * retry exists for, and the retry is only useful if the fallback name is
1104
+ * actually DIFFERENT per host — which is what the second half pins.
1105
+ */
1106
+ describe("a wildcard reverse zone gives many hosts one name", () => {
1107
+ const WILDCARD_PTR_NAME: string = "dhcp-pool.corp.example.com";
1108
+
1109
+ const firstPoolHost: DiscoveredNetworkDevice = {
1110
+ ipAddress: "10.18.166.51",
1111
+ dnsHostname: WILDCARD_PTR_NAME,
1112
+ };
1113
+ const secondPoolHost: DiscoveredNetworkDevice = {
1114
+ ipAddress: "10.18.166.52",
1115
+ dnsHostname: WILDCARD_PTR_NAME,
1116
+ };
1117
+
1118
+ const thirdPoolHost: DiscoveredNetworkDevice = {
1119
+ ipAddress: "10.18.166.53",
1120
+ dnsHostname: WILDCARD_PTR_NAME,
1121
+ };
1122
+
1123
+ const POOL_HOSTS: Array<DiscoveredNetworkDevice> = [
1124
+ firstPoolHost,
1125
+ secondPoolHost,
1126
+ thirdPoolHost,
1127
+ ];
1128
+
1129
+ test("three hosts in the range import as one name, three addresses", () => {
1130
+ /*
1131
+ * The second assertion used to be
1132
+ * `expect(buildDeviceName(first)).toBe(buildDeviceName(second))` over
1133
+ * two fixtures built from the same constant — a statement about the
1134
+ * test data, true of any implementation that reads the same field
1135
+ * twice. Phrased over the DEVICES the builder emits it has bite in both
1136
+ * directions: `names.size` is 1 only because the name ignores the
1137
+ * address, and `hostnames.size` is 3 only because the hostname ignores
1138
+ * the name. The tempting edit (put the resolved name in `hostname`)
1139
+ * breaks the second; an edit that disambiguated names eagerly breaks
1140
+ * the first, and would hide the collision the retry below exists for.
1141
+ */
1142
+ const devices: Array<NetworkDevice> = POOL_HOSTS.map(
1143
+ (poolHost: DiscoveredNetworkDevice) => {
1144
+ return build({ host: poolHost });
1145
+ },
1146
+ );
1147
+
1148
+ const names: Set<string> = new Set<string>(
1149
+ devices.map((device: NetworkDevice) => {
1150
+ return device.name as string;
1151
+ }),
1152
+ );
1153
+ const hostnames: Set<string> = new Set<string>(
1154
+ devices.map((device: NetworkDevice) => {
1155
+ return device.hostname as unknown as string;
1156
+ }),
1157
+ );
1158
+
1159
+ expect(names).toEqual(new Set<string>([WILDCARD_PTR_NAME]));
1160
+ expect(hostnames.size).toBe(POOL_HOSTS.length);
1161
+ });
1162
+
1163
+ test("buildFallbackDeviceName tells them apart by their own address", () => {
1164
+ expect(buildFallbackDeviceName(firstPoolHost)).not.toBe(
1165
+ buildFallbackDeviceName(secondPoolHost),
1166
+ );
1167
+ expect(buildFallbackDeviceName(firstPoolHost)).toBe(
1168
+ "dhcp-pool.corp.example.com (10.18.166.51)",
1169
+ );
1170
+ expect(buildFallbackDeviceName(secondPoolHost)).toBe(
1171
+ "dhcp-pool.corp.example.com (10.18.166.52)",
1172
+ );
1173
+ });
1174
+
1175
+ test("the retry creates the device under the fallback name", () => {
1176
+ /*
1177
+ * End to end, in the shape both import paths actually use: the create
1178
+ * fails on a duplicate name and the caller rebuilds the SAME device
1179
+ * with `name: buildFallbackDeviceName(host)`. Nothing else may move —
1180
+ * `hostname` is still the bare address, which is what the dedup key and
1181
+ * any hostname-keyed rule depend on. Nothing else in the suite passes a
1182
+ * PTR-named host's fallback name through the builder, so an edit that
1183
+ * ignored `data.name`, or that followed the name into `hostname`, would
1184
+ * only be caught here.
1185
+ */
1186
+ const device: NetworkDevice = build({
1187
+ host: secondPoolHost,
1188
+ name: buildFallbackDeviceName(secondPoolHost),
1189
+ });
1190
+
1191
+ expect(device.name).toBe("dhcp-pool.corp.example.com (10.18.166.52)");
1192
+ expect(device.hostname).toBe("10.18.166.52");
1193
+ });
1194
+
1195
+ test("an address-less host fabricates one shared fallback name", () => {
1196
+ /*
1197
+ * An address-less host gets NO SUFFIX, not a fabricated one.
1198
+ *
1199
+ * The suffix used to be composed with a raw template read —
1200
+ * ` (${host.ipAddress})` — so a missing address produced the literal
1201
+ * token "(undefined)". That is worse than useless here: every
1202
+ * address-less host produced the SAME token, so the fallback handed the
1203
+ * retry a name that collided all over again, failing on the very
1204
+ * duplicate it was retrying.
1205
+ *
1206
+ * The honest answer is that a host with no address cannot be told apart
1207
+ * from another one, so the fallback returns the base name unchanged and
1208
+ * the create fails on a real duplicate. What must never happen is a
1209
+ * name that LOOKS address-qualified while carrying no address.
1210
+ */
1211
+ const addresslessHost: DiscoveredNetworkDevice = {
1212
+ dnsHostname: WILDCARD_PTR_NAME,
1213
+ } as unknown as DiscoveredNetworkDevice;
1214
+
1215
+ const fallbackName: string = buildFallbackDeviceName(addresslessHost);
1216
+
1217
+ expect(fallbackName).toBe(WILDCARD_PTR_NAME);
1218
+ expect(fallbackName).not.toContain("undefined");
1219
+ expect(fallbackName).not.toContain("(");
1220
+ });
1221
+
1222
+ test("a null address is composed into the name as the token (null)", () => {
1223
+ /*
1224
+ * ONE value, ONE reading — which is the point of the fix.
1225
+ *
1226
+ * A null address used to be read two ways: "" by the display path
1227
+ * (`String(host.ipAddress ?? "")`) and " (null)" by the fallback path,
1228
+ * which interpolated it raw. Two readings of one field in two functions
1229
+ * that have to agree about the same host is how a retry ends up
1230
+ * composing a name nobody can act on.
1231
+ */
1232
+ const nullAddressHost: DiscoveredNetworkDevice = {
1233
+ ipAddress: null as unknown as string,
1234
+ dnsHostname: WILDCARD_PTR_NAME,
1235
+ };
1236
+
1237
+ expect(buildFallbackDeviceName(nullAddressHost)).toBe(WILDCARD_PTR_NAME);
1238
+ expect(buildFallbackDeviceName(nullAddressHost)).not.toContain("null)");
1239
+ });
1240
+ });
1241
+
1242
+ /*
1243
+ * FALLBACK NAME ARITHMETIC at both extremes at once.
1244
+ *
1245
+ * The longest name DNS can express (253 characters) combined with the
1246
+ * widest address is the worst case the composition has to survive, and a
1247
+ * PTR name is the one naming source that can hit its own ceiling with no
1248
+ * help from a misconfigured device. Two things have to hold: the composed
1249
+ * name still fits under the ceiling the slug needs, and the ADDRESS
1250
+ * survives the cut. A fallback that truncated the address away would hand
1251
+ * both hosts of a wildcard range the same name again — the exact collision
1252
+ * it is there to break.
1253
+ *
1254
+ * "Widest address" is IPv6, not 255.255.255.255. A full v6 address is 39
1255
+ * characters, so its suffix is 42 — more than half the whole ceiling, and
1256
+ * 24 characters wider than the v4 case. The v4 rows come first because
1257
+ * they are the ordinary case; the v6 rows at the end are the real extreme,
1258
+ * and the probe resolves ip6.arpa PTRs, so v6 hosts are in scope.
1259
+ */
1260
+ describe("the fallback name at the longest PTR name and the widest address", () => {
1261
+ // 63 + 1 + 63 + 1 + 63 + 1 + 61 = 253: maximal labels, maximal total.
1262
+ const MAXIMAL_PTR_NAME: string = `${"a".repeat(63)}.${"b".repeat(
1263
+ 63,
1264
+ )}.${"c".repeat(63)}.${"d".repeat(61)}`;
1265
+
1266
+ const WIDEST_ADDRESS_SUFFIX: string = " (255.255.255.255)";
1267
+
1268
+ test("the maximal name is a name this builder actually accepts", () => {
1269
+ /*
1270
+ * Anti-vacuity for everything below: if normalisation rejected a
1271
+ * 253-character name, every case here would be measuring the ADDRESS
1272
+ * fallback and would pass for the wrong reason.
1273
+ */
1274
+ expect(MAXIMAL_PTR_NAME).toHaveLength(MAX_REVERSE_DNS_NAME_LENGTH);
1275
+ expect(
1276
+ buildDeviceName({
1277
+ ipAddress: "255.255.255.255",
1278
+ dnsHostname: MAXIMAL_PTR_NAME,
1279
+ }),
1280
+ ).toBe(MAXIMAL_PTR_NAME.substring(0, MAX_DEVICE_NAME_LENGTH));
1281
+ });
1282
+
1283
+ test("the composed name uses the whole ceiling and keeps the address", () => {
1284
+ /*
1285
+ * The re-derived expectation is gone. This used to assert the base
1286
+ * equals `MAXIMAL_PTR_NAME.substring(0, MAX - suffix.length)`, which is
1287
+ * buildFallbackDeviceName's own arithmetic restated: an implementation
1288
+ * that computed the WRONG cut would compute it identically on both
1289
+ * sides and still pass. The contract instead — all 80 characters are
1290
+ * used, the suffix is the host's address in full (it is never what
1291
+ * gives way, or the wildcard range collides again), and what precedes
1292
+ * it is a genuine prefix of the PTR name rather than some other string.
1293
+ */
1294
+ const name: string = buildFallbackDeviceName({
1295
+ ipAddress: "255.255.255.255",
1296
+ dnsHostname: MAXIMAL_PTR_NAME,
1297
+ });
1298
+
1299
+ expect(name).toHaveLength(MAX_DEVICE_NAME_LENGTH);
1300
+ expect(name.endsWith(WIDEST_ADDRESS_SUFFIX)).toBe(true);
1301
+
1302
+ const base: string = name.substring(
1303
+ 0,
1304
+ name.length - WIDEST_ADDRESS_SUFFIX.length,
1305
+ );
1306
+
1307
+ expect(base.length).toBeGreaterThan(0);
1308
+ expect(MAXIMAL_PTR_NAME.startsWith(base)).toBe(true);
1309
+ });
1310
+
1311
+ test("two maximal-name hosts still get different fallback names", () => {
1312
+ /*
1313
+ * The point of the whole fallback, at the length where losing the
1314
+ * address is most tempting: these two share 253 characters of name and
1315
+ * differ only in their last octet.
1316
+ */
1317
+ expect(
1318
+ buildFallbackDeviceName({
1319
+ ipAddress: "255.255.255.255",
1320
+ dnsHostname: MAXIMAL_PTR_NAME,
1321
+ }),
1322
+ ).not.toBe(
1323
+ buildFallbackDeviceName({
1324
+ ipAddress: "255.255.255.254",
1325
+ dnsHostname: MAXIMAL_PTR_NAME,
1326
+ }),
1327
+ );
1328
+ });
1329
+
1330
+ /*
1331
+ * IPV6 — the actual worst case, and untested anywhere in the feature
1332
+ * until now. 39 characters of address means a 42-character suffix, so
1333
+ * the base is cut to 38: more than half the composed name is the address.
1334
+ * The `Math.max(1, MAX_DEVICE_NAME_LENGTH - suffix.length)` in
1335
+ * buildFallbackDeviceName is written to survive this; nothing proved it,
1336
+ * and a v4-only reading of "the widest address" would not have noticed a
1337
+ * regression that only bites past an 18-character suffix.
1338
+ */
1339
+ const FULL_IPV6_ADDRESS: string = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
1340
+ const IPV6_SUFFIX: string = ` (${FULL_IPV6_ADDRESS})`;
1341
+
1342
+ test("an IPv6 address takes over half the ceiling and still fits", () => {
1343
+ // Anti-vacuity: this really is the wider of the two suffixes.
1344
+ expect(IPV6_SUFFIX.length).toBeGreaterThan(WIDEST_ADDRESS_SUFFIX.length);
1345
+
1346
+ const name: string = buildFallbackDeviceName({
1347
+ ipAddress: FULL_IPV6_ADDRESS,
1348
+ dnsHostname: MAXIMAL_PTR_NAME,
1349
+ });
1350
+
1351
+ expect(name).toHaveLength(MAX_DEVICE_NAME_LENGTH);
1352
+ expect(name.endsWith(IPV6_SUFFIX)).toBe(true);
1353
+ expect(
1354
+ MAXIMAL_PTR_NAME.startsWith(
1355
+ name.substring(0, name.length - IPV6_SUFFIX.length),
1356
+ ),
1357
+ ).toBe(true);
1358
+
1359
+ /*
1360
+ * And it still slugs. Asserted here rather than for the v4 case
1361
+ * because the maths is genuinely different: slugify's `remove` set
1362
+ * strips the colons and the brackets, so a v6 fallback's slug base is
1363
+ * nine characters SHORTER than its name.
1364
+ */
1365
+ expect(Slug.getSlug(name).length).toBeLessThanOrEqual(ColumnLength.Slug);
1366
+ });
1367
+
1368
+ test("two IPv6 hosts in one wildcard zone still get different names", () => {
1369
+ /*
1370
+ * Where "cut the suffix instead of the base" would collide: these two
1371
+ * share 253 characters of PTR name and differ only in the last group of
1372
+ * an address that occupies 42 of the 80 characters available.
1373
+ */
1374
+ expect(
1375
+ buildFallbackDeviceName({
1376
+ ipAddress: FULL_IPV6_ADDRESS,
1377
+ dnsHostname: MAXIMAL_PTR_NAME,
1378
+ }),
1379
+ ).not.toBe(
1380
+ buildFallbackDeviceName({
1381
+ ipAddress: "2001:0db8:85a3:0000:0000:8a2e:0370:7335",
1382
+ dnsHostname: MAXIMAL_PTR_NAME,
1383
+ }),
1384
+ );
1385
+ });
1386
+
1387
+ /*
1388
+ * SLUG HEADROOM — the ceiling that actually bites.
1389
+ *
1390
+ * NetworkDevice.name is varchar(100) but @SlugifyColumn writes a slug
1391
+ * beside it, and Slug.getSlug appends a dash and ten digits into its own
1392
+ * varchar(100). So a name of 95 characters fits the name column and fails
1393
+ * the CREATE on the slug, with an error that says nothing about names.
1394
+ * MAX_DEVICE_NAME_LENGTH is 80 for this reason and nothing else, so the
1395
+ * arithmetic is asserted here against the real column length and the real
1396
+ * slug function rather than restated as a comment.
1397
+ */
1398
+ test("the ceiling is what keeps the slug inside its own column", () => {
1399
+ /*
1400
+ * This replaces three tests that were each measuring less than they
1401
+ * claimed: one asserted `MAX_DEVICE_NAME_LENGTH + "-1234567890".length
1402
+ * <= ColumnLength.Slug`, pure arithmetic over two constants that
1403
+ * invoked none of the code under test and hard-coded Faker's suffix
1404
+ * shape as a literal; the other two slugged names that slugify SHRINKS,
1405
+ * so they had headroom the real worst case does not.
1406
+ *
1407
+ * The fixture is chosen to be that worst case. Every dotted PTR name
1408
+ * loses a character per dot on the way into a slug (slugify's `remove`
1409
+ * set eats them), so a PTR-named device can never be the widest slug a
1410
+ * name of this length produces. A hyphenated sysName has nothing to
1411
+ * remove, so its slug base is the full 80 characters — and 255-octet
1412
+ * sysNames are routine on real gear, which is why the clamp exists.
1413
+ *
1414
+ * Raise MAX_DEVICE_NAME_LENGTH past what the slug column can hold and
1415
+ * this fails at the constant, rather than in production on whichever
1416
+ * device happens to have a long enough name.
1417
+ */
1418
+ const SLUG_PROBE: string = "device";
1419
+ // Measured off the real Slug, not restated: it tracks Faker's suffix.
1420
+ const slugSuffixLength: number =
1421
+ Slug.getSlug(SLUG_PROBE).length - SLUG_PROBE.length;
1422
+
1423
+ const name: string = buildDeviceName({
1424
+ ipAddress: "10.18.166.51",
1425
+ sysName: "long-sysname-".repeat(20),
1426
+ });
1427
+
1428
+ expect(name).toHaveLength(MAX_DEVICE_NAME_LENGTH);
1429
+
1430
+ const slug: string = Slug.getSlug(name);
1431
+
1432
+ // Nothing was removed: the whole clamped name is still in the slug.
1433
+ expect(slug.substring(0, slug.length - slugSuffixLength)).toBe(
1434
+ name.toLowerCase(),
1435
+ );
1436
+ expect(slug.length).toBeLessThanOrEqual(ColumnLength.Slug);
1437
+ });
1438
+ });
1439
+
1440
+ /*
1441
+ * `hostname` is the registered-host dedup key, what the SNMP poller dials,
1442
+ * and what a trap's source IP is correlated to — so it is the ADDRESS for
1443
+ * every host, whatever the host ended up being NAMED. Table-driven across
1444
+ * both naming sources because the risk is a future edit that "uses the
1445
+ * resolved name where we have one": that would look correct on five of
1446
+ * these six rows and would silently stop a device polling the day its
1447
+ * reverse zone changed.
1448
+ */
1449
+ describe("the address is the hostname for every naming combination", () => {
1450
+ const NAMING_COMBINATIONS: Array<{
1451
+ reason: string;
1452
+ host: DiscoveredNetworkDevice;
1453
+ expectedName: string;
1454
+ }> = [
1455
+ {
1456
+ reason: "sysName and a usable PTR name",
1457
+ host: {
1458
+ ipAddress: "10.18.166.51",
1459
+ sysName: "core-switch-01",
1460
+ dnsHostname: "sw1.corp.example.com",
1461
+ },
1462
+ expectedName: "core-switch-01",
1463
+ },
1464
+ {
1465
+ reason: "sysName and no PTR name",
1466
+ host: { ipAddress: "10.18.166.51", sysName: "core-switch-01" },
1467
+ expectedName: "core-switch-01",
1468
+ },
1469
+ {
1470
+ reason: "sysName and a PTR name that normalises away",
1471
+ host: {
1472
+ ipAddress: "10.18.166.51",
1473
+ sysName: "core-switch-01",
1474
+ dnsHostname: "51.166.18.10.in-addr.arpa",
1475
+ },
1476
+ expectedName: "core-switch-01",
1477
+ },
1478
+ {
1479
+ reason: "no sysName and a usable PTR name",
1480
+ host: {
1481
+ ipAddress: "10.18.166.51",
1482
+ dnsHostname: "sw1.corp.example.com",
1483
+ },
1484
+ expectedName: "sw1.corp.example.com",
1485
+ },
1486
+ {
1487
+ reason: "no sysName and no PTR name",
1488
+ host: { ipAddress: "10.18.166.51" },
1489
+ expectedName: "10.18.166.51",
1490
+ },
1491
+ {
1492
+ reason: "no sysName and a PTR name that normalises away",
1493
+ host: { ipAddress: "10.18.166.51", dnsHostname: "core switch" },
1494
+ expectedName: "10.18.166.51",
1495
+ },
1496
+ ];
1497
+
1498
+ for (const combination of NAMING_COMBINATIONS) {
1499
+ test(`${combination.reason}: named ${combination.expectedName}, addressed 10.18.166.51`, () => {
1500
+ const device: NetworkDevice = build({ host: combination.host });
1501
+
1502
+ expect(device.name).toBe(combination.expectedName);
1503
+ expect(device.hostname).toBe("10.18.166.51");
1504
+ });
1505
+ }
1506
+ });
1507
+ });