Make HtmlTags safer, properly sanitize in the view as well

This commit is contained in:
nicolas.dorier
2025-01-19 10:59:28 +09:00
parent 1830d398a4
commit d3d6b2d15c
6 changed files with 34 additions and 25 deletions

View File

@@ -35,33 +35,35 @@ namespace BTCPayServer.Abstractions.Services
return _htmlHelper.Raw(_jsonHelper.Serialize(model));
}
public IHtmlContent Meta(string inputHtml) => _htmlHelper.Raw(RawMeta(inputHtml, out _));
public string RawMeta(string inputHtml, out bool isHtmlModified)
{
bool bHtmlModified;
HtmlSanitizer _metaSanitizer = new HtmlSanitizer();
HtmlSanitizer sane = new HtmlSanitizer();
_metaSanitizer.AllowedTags.Clear();
_metaSanitizer.AllowedTags.Add("meta");
sane.AllowedTags.Clear();
sane.AllowedTags.Add("meta");
_metaSanitizer.AllowedAttributes.Clear();
_metaSanitizer.AllowedAttributes.Add("name");
_metaSanitizer.AllowedAttributes.Add("http-equiv");
_metaSanitizer.AllowedAttributes.Add("content");
_metaSanitizer.AllowedAttributes.Add("value");
_metaSanitizer.AllowedAttributes.Add("property");
sane.AllowedAttributes.Clear();
sane.AllowedAttributes.Add("name");
sane.AllowedAttributes.Add("http-equiv");
sane.AllowedAttributes.Add("content");
sane.AllowedAttributes.Add("value");
sane.AllowedAttributes.Add("property");
_metaSanitizer.AllowDataAttributes = false;
sane.AllowDataAttributes = false;
_metaSanitizer.RemovingTag += (sender, e) => bHtmlModified = true;
_metaSanitizer.RemovingAtRule += (sender, e) => bHtmlModified = true;
_metaSanitizer.RemovingAttribute += (sender, e) => bHtmlModified = true;
_metaSanitizer.RemovingComment += (sender, e) => bHtmlModified = true;
_metaSanitizer.RemovingCssClass += (sender, e) => bHtmlModified = true;
_metaSanitizer.RemovingStyle += (sender, e) => bHtmlModified = true;
sane.RemovingTag += (sender, e) => bHtmlModified = true;
sane.RemovingAtRule += (sender, e) => bHtmlModified = true;
sane.RemovingAttribute += (sender, e) => bHtmlModified = true;
sane.RemovingComment += (sender, e) => bHtmlModified = true;
sane.RemovingCssClass += (sender, e) => bHtmlModified = true;
sane.RemovingStyle += (sender, e) => bHtmlModified = true;
bHtmlModified = false;
var sRet = _metaSanitizer.Sanitize(inputHtml);
var sRet = sane.Sanitize(inputHtml);
isHtmlModified = bHtmlModified;
return sRet;

View File

@@ -6,6 +6,7 @@ using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Abstractions.Services;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
@@ -38,13 +39,16 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly UserManager<ApplicationUser> _userManager;
private readonly IFileService _fileService;
public Safe Safe { get; }
public GreenfieldAppsController(
AppService appService,
UriResolver uriResolver,
StoreRepository storeRepository,
CurrencyNameTable currencies,
IFileService fileService,
UserManager<ApplicationUser> userManager
UserManager<ApplicationUser> userManager,
Safe safe
)
{
_appService = appService;
@@ -53,6 +57,7 @@ namespace BTCPayServer.Controllers.Greenfield
_currencies = currencies;
_fileService = fileService;
_userManager = userManager;
Safe = safe;
}
[HttpPost("~/api/v1/stores/{storeId}/apps/crowdfund")]
@@ -305,7 +310,8 @@ namespace BTCPayServer.Controllers.Greenfield
var parsedSounds = ValidateStringArray(request.Sounds);
var parsedColors = ValidateStringArray(request.AnimationColors);
Enum.TryParse<BTCPayServer.Services.Apps.CrowdfundResetEvery>(request.ResetEvery.ToString(), true, out var resetEvery);
if (request.HtmlMetaTags is not null)
request.HtmlMetaTags = Safe.RawMeta(request.HtmlMetaTags, out _);
return new CrowdfundSettings
{
Title = request.Title?.Trim() ?? request.AppName,
@@ -342,6 +348,8 @@ namespace BTCPayServer.Controllers.Greenfield
private PointOfSaleSettings ToPointOfSaleSettings(PointOfSaleAppRequest request, PointOfSaleSettings settings)
{
Enum.TryParse<BTCPayServer.Plugins.PointOfSale.PosViewType>(request.DefaultView.ToString(), true, out var defaultView);
if (request.HtmlMetaTags is not null)
request.HtmlMetaTags = Safe.RawMeta(request.HtmlMetaTags, out _);
return new PointOfSaleSettings
{

View File

@@ -588,7 +588,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
});
if (wasHtmlModified)
{
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Only meta tags are allowed in HTML headers. Your HTML code has been cleaned up accordingly."].Value;
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Only meta tags are allowed in HTML headers. Your HTML code has been cleaned up accordingly."].Value;
}
else
{

View File

@@ -729,7 +729,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
await _appService.UpdateOrCreateApp(app);
if (wasHtmlModified)
{
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Only meta tags are allowed in HTML headers. Your HTML code has been cleaned up accordingly."].Value;
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Only meta tags are allowed in HTML headers. Your HTML code has been cleaned up accordingly."].Value;
} else {
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["App updated"].Value;
}

View File

@@ -35,8 +35,7 @@
object-fit: scale-down;
}
</style>
@* Html.Raw OK here since Html has been cleaned before in controller *@
@Html.Raw(Model.HtmlMetaTags)
@this.Safe.Meta(Model.HtmlMetaTags)
<vc:ui-extension-point location="crowdfund-head" model="@Model"/>
</head>
<body class="min-vh-100 p-2">

View File

@@ -5,6 +5,7 @@
@inject IWebHostEnvironment WebHostEnvironment
@inject SettingsRepository SettingsRepository
@inject BTCPayServerEnvironment Env
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{
ViewData["Title"] = string.IsNullOrEmpty(Model.Title) ? Model.StoreName : Model.Title;
@@ -40,8 +41,7 @@
<link rel="apple-touch-startup-image" href="~/img/splash.png">
<link rel="manifest" href="@(await GetDynamicManifest(ViewData["Title"]!.ToString()))">
<link href="~/pos/common.css" asp-append-version="true" rel="stylesheet" />
@* Html.Raw OK here since Html has been cleaned before in controller *@
@Html.Raw(Model.HtmlMetaTags)
@this.Safe.Meta(Model.HtmlMetaTags)
@await RenderSectionAsync("PageHeadContent", false)
</head>
<body class="min-vh-100">