diff --git a/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor b/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor index 65e3cea58..9eb6f37f3 100644 --- a/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor +++ b/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor @@ -25,7 +25,7 @@ @if (!string.IsNullOrEmpty(GetSetting("Head"))) { - @((MarkupString)GetSetting("Head")) + @((MarkupString)SanitizeHeadContent(GetSetting("Head"))) } @foreach (var stylesheet in Stylesheets) diff --git a/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor.cs b/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor.cs index 580066ef4..cdd5b2d1a 100644 --- a/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor.cs +++ b/src/Frontend/FluentCMS.Web.UI/Components/PageHead.razor.cs @@ -1,3 +1,5 @@ +using Ganss.Xss; + namespace FluentCMS.Web.UI; public partial class PageHead : IAsyncDisposable @@ -7,6 +9,39 @@ public partial class PageHead : IAsyncDisposable private List Stylesheets { get; set; } = []; + private static readonly HtmlSanitizer _headSanitizer = CreateHeadSanitizer(); + + private static HtmlSanitizer CreateHeadSanitizer() + { + var sanitizer = new HtmlSanitizer(); + sanitizer.AllowedTags.Clear(); + sanitizer.AllowedTags.Add("meta"); + sanitizer.AllowedTags.Add("link"); + sanitizer.AllowedTags.Add("style"); + sanitizer.AllowedTags.Add("noscript"); + sanitizer.AllowedAttributes.Clear(); + sanitizer.AllowedAttributes.Add("name"); + sanitizer.AllowedAttributes.Add("content"); + sanitizer.AllowedAttributes.Add("rel"); + sanitizer.AllowedAttributes.Add("href"); + sanitizer.AllowedAttributes.Add("type"); + sanitizer.AllowedAttributes.Add("media"); + sanitizer.AllowedAttributes.Add("charset"); + sanitizer.AllowedAttributes.Add("property"); + sanitizer.AllowedAttributes.Add("http-equiv"); + sanitizer.AllowedAttributes.Add("sizes"); + sanitizer.AllowedAttributes.Add("hreflang"); + sanitizer.AllowedAttributes.Add("crossorigin"); + sanitizer.AllowedAttributes.Add("integrity"); + sanitizer.AllowedCssProperties.Clear(); + return sanitizer; + } + + private static string SanitizeHeadContent(string content) + { + return _headSanitizer.Sanitize(content); + } + private string GetRobots() { ViewState.Page.Settings.TryGetValue("Index", out var index); @@ -36,9 +71,16 @@ private string GetSetting(string key) return pageValue ?? siteValue ?? string.Empty; } + private static readonly System.Text.RegularExpressions.Regex _googleTagsIdRegex = + new(@"^[A-Za-z0-9\-]+$", System.Text.RegularExpressions.RegexOptions.Compiled); + private string GetGoogleTagsScript() { - return $"\n"; + var tagId = GetSetting("GoogleTagsId"); + if (string.IsNullOrEmpty(tagId) || !_googleTagsIdRegex.IsMatch(tagId)) + return string.Empty; + + return $"\n"; } private async void OnStateChanged(object? sender, EventArgs e) diff --git a/src/Frontend/FluentCMS.Web.UI/FluentCMS.Web.UI.csproj b/src/Frontend/FluentCMS.Web.UI/FluentCMS.Web.UI.csproj index 067344a5f..4007a89bb 100644 --- a/src/Frontend/FluentCMS.Web.UI/FluentCMS.Web.UI.csproj +++ b/src/Frontend/FluentCMS.Web.UI/FluentCMS.Web.UI.csproj @@ -22,6 +22,7 @@ +