diff --git a/HellionChat/Branding/BrandingLinks.cs b/HellionChat/Branding/BrandingLinks.cs index c6eec3c..f3f3a08 100644 --- a/HellionChat/Branding/BrandingLinks.cs +++ b/HellionChat/Branding/BrandingLinks.cs @@ -1,3 +1,6 @@ +using System.Runtime.CompilerServices; +using HellionChat.Util; + namespace HellionChat.Branding; // Centralised — a future invite/URL rotation only touches this file. @@ -9,4 +12,22 @@ internal static class BrandingLinks "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat"; public const string HellionForgeWebsite = "https://hellion-forge.cloud"; public const string HellionMediaWebsite = "https://hellion-media.de/de"; + + // CA2255 warns against [ModuleInitializer] in library code, but Dalamud + // loads the plugin DLL directly so the module-init pass is the right hook + // for a one-shot URL sanity check at plugin load. +#pragma warning disable CA2255 + [ModuleInitializer] +#pragma warning restore CA2255 + internal static void ValidateUrls() + { + UrlValidation.ValidateAll( + nameof(BrandingLinks), + HellionForgeDiscordInvite, + HellionForgeGitea, + HellionChatRepo, + HellionForgeWebsite, + HellionMediaWebsite + ); + } } diff --git a/HellionChat/Integrations/IntegrationLinks.cs b/HellionChat/Integrations/IntegrationLinks.cs index 84253a7..65bb5a6 100644 --- a/HellionChat/Integrations/IntegrationLinks.cs +++ b/HellionChat/Integrations/IntegrationLinks.cs @@ -1,3 +1,6 @@ +using System.Runtime.CompilerServices; +using HellionChat.Util; + namespace HellionChat.Integrations; // Third-party plugin URLs — separate from BrandingLinks (Hellion-owned URLs). @@ -5,4 +8,13 @@ internal static class IntegrationLinks { public const string HonorificRepo = "https://github.com/Caraxi/Honorific"; public const string HonorificAuthor = "https://github.com/Caraxi"; + + // See BrandingLinks.ValidateUrls for the CA2255 rationale. +#pragma warning disable CA2255 + [ModuleInitializer] +#pragma warning restore CA2255 + internal static void ValidateUrls() + { + UrlValidation.ValidateAll(nameof(IntegrationLinks), HonorificRepo, HonorificAuthor); + } } diff --git a/HellionChat/Util/UrlValidation.cs b/HellionChat/Util/UrlValidation.cs new file mode 100644 index 0000000..98d49b7 --- /dev/null +++ b/HellionChat/Util/UrlValidation.cs @@ -0,0 +1,23 @@ +namespace HellionChat.Util; + +internal static class UrlValidation +{ + // Used by BrandingLinks/IntegrationLinks at module init. A typo in a URL + // rotation throws loudly at plugin load instead of silently failing when + // a user clicks the broken button. + public static void ValidateAll(string source, params string[] urls) + { + foreach (var url in urls) + { + if ( + !Uri.TryCreate(url, UriKind.Absolute, out var uri) + || (uri.Scheme is not "https" and not "http") + ) + { + throw new InvalidOperationException( + $"{source} contains malformed URL: {url}" + ); + } + } + } +}