Fix several HTML injections (#4545)

This commit is contained in:
Nicolas Dorier
2023-01-22 03:08:12 +09:00
committed by GitHub
parent 5f24b41250
commit a3203e5775
22 changed files with 59 additions and 43 deletions

View File

@@ -13,6 +13,7 @@ using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace BTCPayServer.Controllers
{
@@ -23,11 +24,13 @@ namespace BTCPayServer.Controllers
public UIAppsController(
UserManager<ApplicationUser> userManager,
StoreRepository storeRepository,
AppService appService)
AppService appService,
IHtmlHelper html)
{
_userManager = userManager;
_storeRepository = storeRepository;
_appService = appService;
Html = html;
}
private readonly UserManager<ApplicationUser> _userManager;
@@ -35,6 +38,7 @@ namespace BTCPayServer.Controllers
private readonly AppService _appService;
public string CreatedAppId { get; set; }
public IHtmlHelper Html { get; }
public class AppUpdated
{
@@ -175,7 +179,7 @@ namespace BTCPayServer.Controllers
if (app == null)
return NotFound();
return View("Confirm", new ConfirmModel("Delete app", $"The app <strong>{app.Name}</strong> and its settings will be permanently deleted. Are you sure?", "Delete"));
return View("Confirm", new ConfirmModel("Delete app", $"The app <strong>{Html.Encode(app.Name)}</strong> and its settings will be permanently deleted. Are you sure?", "Delete"));
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]

View File

@@ -2,9 +2,11 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Abstractions.Services;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Models;
@@ -41,7 +43,7 @@ namespace BTCPayServer.Controllers
return View("Confirm", new ConfirmModel
{
Title = "Delete API key",
Description = $"Any application using the API key <strong>{key.Label ?? key.Id}<strong> will immediately lose access.",
Description = $"Any application using the API key <strong>{Html.Encode(key.Label ?? key.Id)}<strong> will immediately lose access.",
Action = "Delete",
ActionName = nameof(DeleteAPIKeyPost)
});

View File

@@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
using MimeKit;
@@ -38,6 +39,7 @@ namespace BTCPayServer.Controllers
private readonly Fido2Service _fido2Service;
private readonly LinkGenerator _linkGenerator;
private readonly UserLoginCodeService _userLoginCodeService;
private readonly IHtmlHelper Html;
private readonly UserService _userService;
readonly StoreRepository _StoreRepository;
@@ -54,7 +56,8 @@ namespace BTCPayServer.Controllers
Fido2Service fido2Service,
LinkGenerator linkGenerator,
UserService userService,
UserLoginCodeService userLoginCodeService
UserLoginCodeService userLoginCodeService,
IHtmlHelper htmlHelper
)
{
_userManager = userManager;
@@ -68,6 +71,7 @@ namespace BTCPayServer.Controllers
_fido2Service = fido2Service;
_linkGenerator = linkGenerator;
_userLoginCodeService = userLoginCodeService;
Html = htmlHelper;
_userService = userService;
_StoreRepository = storeRepository;
}

View File

@@ -225,15 +225,15 @@ namespace BTCPayServer.Controllers
{
// return
return View("Confirm", new ConfirmModel("Delete admin",
$"Unable to proceed: As the user <strong>{user.Email}</strong> is the last enabled admin, it cannot be removed."));
$"Unable to proceed: As the user <strong>{Html.Encode(user.Email)}</strong> is the last enabled admin, it cannot be removed."));
}
return View("Confirm", new ConfirmModel("Delete admin",
$"The admin <strong>{user.Email}</strong> will be permanently deleted. This action will also delete all accounts, users and data associated with the server account. Are you sure?",
$"The admin <strong>{Html.Encode(user.Email)}</strong> will be permanently deleted. This action will also delete all accounts, users and data associated with the server account. Are you sure?",
"Delete"));
}
return View("Confirm", new ConfirmModel("Delete user", $"The user <strong>{user.Email}</strong> will be permanently deleted. Are you sure?", "Delete"));
return View("Confirm", new ConfirmModel("Delete user", $"The user <strong>{Html.Encode(user.Email)}</strong> will be permanently deleted. Are you sure?", "Delete"));
}
[HttpPost("server/users/{userId}/delete")]
@@ -259,9 +259,9 @@ namespace BTCPayServer.Controllers
if (!enable && await _userService.IsUserTheOnlyOneAdmin(user))
{
return View("Confirm", new ConfirmModel("Disable admin",
$"Unable to proceed: As the user <strong>{user.Email}</strong> is the last enabled admin, it cannot be disabled."));
$"Unable to proceed: As the user <strong>{Html.Encode(user.Email)}</strong> is the last enabled admin, it cannot be disabled."));
}
return View("Confirm", new ConfirmModel($"{(enable ? "Enable" : "Disable")} user", $"The user <strong>{user.Email}</strong> will be {(enable ? "enabled" : "disabled")}. Are you sure?", (enable ? "Enable" : "Disable")));
return View("Confirm", new ConfirmModel($"{(enable ? "Enable" : "Disable")} user", $"The user <strong>{Html.Encode(user.Email)}</strong> will be {(enable ? "enabled" : "disabled")}. Are you sure?", (enable ? "Enable" : "Disable")));
}
[HttpPost("server/users/{userId}/toggle")]
@@ -288,7 +288,7 @@ namespace BTCPayServer.Controllers
if (user == null)
return NotFound();
return View("Confirm", new ConfirmModel("Send verification email", $"This will send a verification email to <strong>{user.Email}</strong>.", "Send"));
return View("Confirm", new ConfirmModel("Send verification email", $"This will send a verification email to <strong>{Html.Encode(user.Email)}</strong>.", "Send"));
}
[HttpPost("server/users/{userId}/verification-email")]

View File

@@ -89,7 +89,8 @@ namespace BTCPayServer.Controllers
Logs logs,
LinkGenerator linkGenerator,
EmailSenderFactory emailSenderFactory,
IHostApplicationLifetime applicationLifetime
IHostApplicationLifetime applicationLifetime,
IHtmlHelper html
)
{
_policiesSettings = policiesSettings;
@@ -113,6 +114,7 @@ namespace BTCPayServer.Controllers
_linkGenerator = linkGenerator;
_emailSenderFactory = emailSenderFactory;
ApplicationLifetime = applicationLifetime;
Html = html;
}
[Route("server/maintenance")]
@@ -296,6 +298,7 @@ namespace BTCPayServer.Controllers
public IHttpClientFactory HttpClientFactory { get; }
public IHostApplicationLifetime ApplicationLifetime { get; }
public IHtmlHelper Html { get; }
[Route("server/policies")]
public async Task<IActionResult> Policies()
@@ -836,7 +839,7 @@ namespace BTCPayServer.Controllers
return NotFound();
return View("Confirm",
new ConfirmModel("Delete dynamic DNS service",
$"Deleting the dynamic DNS service for <strong>{hostname}</strong> means your BTCPay Server will stop updating the associated DNS record periodically.", "Delete"));
$"Deleting the dynamic DNS service for <strong>{Html.Encode(hostname)}</strong> means your BTCPay Server will stop updating the associated DNS record periodically.", "Delete"));
}
[HttpPost("server/services/dynamic-dns/{hostname}/delete")]

View File

@@ -800,9 +800,9 @@ namespace BTCPayServer.Controllers
? ""
: " or imported it into an external wallet. If you no longer have access to your private key (recovery seed), immediately replace the wallet";
return
$"<p class=\"text-danger fw-bold\">Please note that this is a <strong>{walletType} wallet</strong>!</p>" +
$"<p class=\"text-danger fw-bold\">Do not proceed if you have not backed up the wallet{additionalText}.</p>" +
$"<p class=\"text-start mb-0\">This action will erase the current wallet data from the server. {info}</p>";
$"<p class=\"text-danger fw-bold\">Please note that this is a <strong>{Html.Encode(walletType)} wallet</strong>!</p>" +
$"<p class=\"text-danger fw-bold\">Do not proceed if you have not backed up the wallet{Html.Encode(additionalText)}.</p>" +
$"<p class=\"text-start mb-0\">This action will erase the current wallet data from the server. {Html.Encode(info)}</p>";
}
private string WalletReplaceWarning(bool isHotWallet)

View File

@@ -65,7 +65,8 @@ namespace BTCPayServer.Controllers
WebhookSender webhookNotificationManager,
IDataProtectionProvider dataProtector,
IOptions<LightningNetworkOptions> lightningNetworkOptions,
IOptions<ExternalServicesOptions> externalServiceOptions)
IOptions<ExternalServicesOptions> externalServiceOptions,
IHtmlHelper html)
{
_RateFactory = rateFactory;
_Repo = repo;
@@ -89,6 +90,7 @@ namespace BTCPayServer.Controllers
_BtcpayServerOptions = btcpayServerOptions;
_BTCPayEnv = btcpayEnv;
_externalServiceOptions = externalServiceOptions;
Html = html;
}
readonly BTCPayServerOptions _BtcpayServerOptions;
@@ -113,6 +115,7 @@ namespace BTCPayServer.Controllers
public string? GeneratedPairingCode { get; set; }
public WebhookSender WebhookNotificationManager { get; }
public IHtmlHelper Html { get; }
public LightningNetworkOptions LightningNetworkOptions { get; }
public IDataProtector DataProtector { get; }
@@ -180,7 +183,7 @@ namespace BTCPayServer.Controllers
var user = await _UserManager.FindByIdAsync(userId);
if (user == null)
return NotFound();
return View("Confirm", new ConfirmModel("Remove store user", $"This action will prevent <strong>{user.Email}</strong> from accessing this store and its settings. Are you sure?", "Remove"));
return View("Confirm", new ConfirmModel("Remove store user", $"This action will prevent <strong>{Html.Encode(user.Email)}</strong> from accessing this store and its settings. Are you sure?", "Remove"));
}
[HttpPost("{storeId}/users/{userId}/delete")]
@@ -776,7 +779,7 @@ namespace BTCPayServer.Controllers
var token = await _TokenRepository.GetToken(tokenId);
if (token == null || token.StoreId != CurrentStore.Id)
return NotFound();
return View("Confirm", new ConfirmModel("Revoke the token", $"The access token with the label <strong>{token.Label}</strong> will be revoked. Do you wish to continue?", "Revoke"));
return View("Confirm", new ConfirmModel("Revoke the token", $"The access token with the label <strong>{Html.Encode(token.Label)}</strong> will be revoked. Do you wish to continue?", "Revoke"));
}
[HttpPost("{storeId}/tokens/{tokenId}/revoke")]