From 84c81081abdf18139f846de27b7567d4d2869a66 Mon Sep 17 00:00:00 2001 From: "Martin Hinshelwood NKDAgility.com" Date: Wed, 17 Sep 2025 16:16:42 -0500 Subject: [PATCH] Add hyperlink validation and refactor `CreateHyperlink` Refactored `CreateHyperlink` to improve clarity and maintainability. - Changed `sourceLinkAbsoluteUri` declaration to `string`. - Added `IsValidHyperlink` method to validate hyperlink format and protocol. - Introduced `GetAbsoluteUri` method for reusable URI parsing logic. - Enhanced logging to provide detailed warnings for invalid hyperlinks. - Improved code readability by modularizing validation and URI handling. --- .../Tools/TfsWorkItemLinkTool.cs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsWorkItemLinkTool.cs b/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsWorkItemLinkTool.cs index d1d5df510..1586e7530 100644 --- a/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsWorkItemLinkTool.cs +++ b/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsWorkItemLinkTool.cs @@ -425,12 +425,17 @@ private bool IsRelatedLink(Link item) private void CreateHyperlink(Hyperlink sourceLink, WorkItemData target) { - var sourceLinkAbsoluteUri = GetAbsoluteUri(sourceLink); + string sourceLinkAbsoluteUri = GetAbsoluteUri(sourceLink); if (string.IsNullOrEmpty(sourceLinkAbsoluteUri)) { Log.LogWarning(" [SKIP] Unable to create a hyperlink to [{0}]", sourceLink.Location); return; } + if (!IsValidHyperlink(sourceLinkAbsoluteUri)) + { + Log.LogWarning(" [SKIP] Unable to create a hyperlink to [{0}] <-- protocall no longer allowed or url format is invalid", sourceLink.Location); + return; + } var exist = (from hyperlink in target.ToWorkItem().Links.OfType() let absoluteUri = GetAbsoluteUri(hyperlink) @@ -458,6 +463,8 @@ where string.Equals(sourceLinkAbsoluteUri, absoluteUri, StringComparison.Ordinal } } + + private string GetAbsoluteUri(Hyperlink hyperlink) { try @@ -489,5 +496,28 @@ private bool IsHyperlink(Link item) return item is Hyperlink; } + private bool IsValidHyperlink(string sourceLinkAbsoluteUri) + { + if (string.IsNullOrEmpty(sourceLinkAbsoluteUri)) + { + return false; + } + + try + { + var uri = new Uri(sourceLinkAbsoluteUri); + var allowedSchemes = new[] + { + "http", "https", "ftp", "gopher", "mailto", "news", "telnet", "wais", + "vstfs", "tfs", "alm", "mtm", "mtms", "mtr", "mtrs", "mfbclient", "mfbclients", + "test-runner", "x-mvwit", "onenote", "codeflow", "file", "tel", "skype" + }; + return allowedSchemes.Contains(uri.Scheme.ToLowerInvariant()); + } + catch (UriFormatException) + { + return false; + } + } } }