From 02070d65836cd24627929b3403efbae8de56039a Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Sat, 21 Jan 2023 19:05:41 +0900 Subject: [PATCH] Fix several HTML injections --- BTCPayServer/Controllers/UIAppsController.cs | 8 ++++++-- .../Controllers/UIManageController.APIKeys.cs | 4 +++- BTCPayServer/Controllers/UIManageController.cs | 6 +++++- BTCPayServer/Controllers/UIServerController.Users.cs | 12 ++++++------ BTCPayServer/Controllers/UIServerController.cs | 7 +++++-- .../Controllers/UIStoresController.Onchain.cs | 6 +++--- BTCPayServer/Controllers/UIStoresController.cs | 9 ++++++--- .../Views/Shared/Crowdfund/UpdateCrowdfund.cshtml | 2 +- .../Shared/PointOfSale/UpdatePointOfSale.cshtml | 2 +- BTCPayServer/Views/UIApps/ListApps.cshtml | 4 ++-- .../UICustodianAccounts/EditCustodianAccount.cshtml | 4 ++-- .../Views/UILNURL/EditLightningAddress.cshtml | 4 ++-- BTCPayServer/Views/UIManage/APIKeys.cshtml | 2 +- .../Views/UIManage/TwoFactorAuthentication.cshtml | 4 ++-- .../ConfigureStorePayoutProcessors.cshtml | 4 ++-- .../Views/UIServer/DynamicDnsServices.cshtml | 4 ++-- BTCPayServer/Views/UIServer/ListUsers.cshtml | 4 ++-- .../Views/UIStorePullPayments/PullPayments.cshtml | 4 ++-- BTCPayServer/Views/UIStores/GeneralSettings.cshtml | 2 +- BTCPayServer/Views/UIStores/ListTokens.cshtml | 2 +- BTCPayServer/Views/UIStores/StoreUsers.cshtml | 4 ++-- BTCPayServer/Views/UIStores/WalletSettings.cshtml | 4 ++-- 22 files changed, 59 insertions(+), 43 deletions(-) diff --git a/BTCPayServer/Controllers/UIAppsController.cs b/BTCPayServer/Controllers/UIAppsController.cs index ecbeee16b9..e58afe85c3 100644 --- a/BTCPayServer/Controllers/UIAppsController.cs +++ b/BTCPayServer/Controllers/UIAppsController.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; namespace BTCPayServer.Controllers { @@ -23,11 +24,13 @@ public partial class UIAppsController : Controller public UIAppsController( UserManager userManager, StoreRepository storeRepository, - AppService appService) + AppService appService, + IHtmlHelper html) { _userManager = userManager; _storeRepository = storeRepository; _appService = appService; + Html = html; } private readonly UserManager _userManager; @@ -35,6 +38,7 @@ public partial class UIAppsController : Controller private readonly AppService _appService; public string CreatedAppId { get; set; } + public IHtmlHelper Html { get; } public class AppUpdated { @@ -175,7 +179,7 @@ public IActionResult DeleteApp(string appId) if (app == null) return NotFound(); - return View("Confirm", new ConfirmModel("Delete app", $"The app {app.Name} and its settings will be permanently deleted. Are you sure?", "Delete")); + return View("Confirm", new ConfirmModel("Delete app", $"The app {Html.Encode(app.Name)} and its settings will be permanently deleted. Are you sure?", "Delete")); } [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] diff --git a/BTCPayServer/Controllers/UIManageController.APIKeys.cs b/BTCPayServer/Controllers/UIManageController.APIKeys.cs index f1b8abfec7..fa857f0b2d 100644 --- a/BTCPayServer/Controllers/UIManageController.APIKeys.cs +++ b/BTCPayServer/Controllers/UIManageController.APIKeys.cs @@ -2,9 +2,11 @@ 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 @@ public async Task DeleteAPIKey(string id) return View("Confirm", new ConfirmModel { Title = "Delete API key", - Description = $"Any application using the API key {key.Label ?? key.Id} will immediately lose access.", + Description = $"Any application using the API key {Html.Encode(key.Label ?? key.Id)} will immediately lose access.", Action = "Delete", ActionName = nameof(DeleteAPIKeyPost) }); diff --git a/BTCPayServer/Controllers/UIManageController.cs b/BTCPayServer/Controllers/UIManageController.cs index d887765109..53d57fac05 100644 --- a/BTCPayServer/Controllers/UIManageController.cs +++ b/BTCPayServer/Controllers/UIManageController.cs @@ -16,6 +16,7 @@ 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 @@ public partial class UIManageController : Controller 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 @@ public partial class UIManageController : Controller Fido2Service fido2Service, LinkGenerator linkGenerator, UserService userService, - UserLoginCodeService userLoginCodeService + UserLoginCodeService userLoginCodeService, + IHtmlHelper htmlHelper ) { _userManager = userManager; @@ -68,6 +71,7 @@ UserLoginCodeService userLoginCodeService _fido2Service = fido2Service; _linkGenerator = linkGenerator; _userLoginCodeService = userLoginCodeService; + Html = htmlHelper; _userService = userService; _StoreRepository = storeRepository; } diff --git a/BTCPayServer/Controllers/UIServerController.Users.cs b/BTCPayServer/Controllers/UIServerController.Users.cs index 816a0d1b1c..6c937158ef 100644 --- a/BTCPayServer/Controllers/UIServerController.Users.cs +++ b/BTCPayServer/Controllers/UIServerController.Users.cs @@ -225,15 +225,15 @@ public async Task DeleteUser(string userId) { // return return View("Confirm", new ConfirmModel("Delete admin", - $"Unable to proceed: As the user {user.Email} is the last enabled admin, it cannot be removed.")); + $"Unable to proceed: As the user {Html.Encode(user.Email)} is the last enabled admin, it cannot be removed.")); } return View("Confirm", new ConfirmModel("Delete admin", - $"The admin {user.Email} will be permanently deleted. This action will also delete all accounts, users and data associated with the server account. Are you sure?", + $"The admin {Html.Encode(user.Email)} 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 {user.Email} will be permanently deleted. Are you sure?", "Delete")); + return View("Confirm", new ConfirmModel("Delete user", $"The user {Html.Encode(user.Email)} will be permanently deleted. Are you sure?", "Delete")); } [HttpPost("server/users/{userId}/delete")] @@ -259,9 +259,9 @@ public async Task ToggleUser(string userId, bool enable) if (!enable && await _userService.IsUserTheOnlyOneAdmin(user)) { return View("Confirm", new ConfirmModel("Disable admin", - $"Unable to proceed: As the user {user.Email} is the last enabled admin, it cannot be disabled.")); + $"Unable to proceed: As the user {Html.Encode(user.Email)} is the last enabled admin, it cannot be disabled.")); } - return View("Confirm", new ConfirmModel($"{(enable ? "Enable" : "Disable")} user", $"The user {user.Email} will be {(enable ? "enabled" : "disabled")}. Are you sure?", (enable ? "Enable" : "Disable"))); + return View("Confirm", new ConfirmModel($"{(enable ? "Enable" : "Disable")} user", $"The user {Html.Encode(user.Email)} will be {(enable ? "enabled" : "disabled")}. Are you sure?", (enable ? "Enable" : "Disable"))); } [HttpPost("server/users/{userId}/toggle")] @@ -288,7 +288,7 @@ public async Task SendVerificationEmail(string userId) if (user == null) return NotFound(); - return View("Confirm", new ConfirmModel("Send verification email", $"This will send a verification email to {user.Email}.", "Send")); + return View("Confirm", new ConfirmModel("Send verification email", $"This will send a verification email to {Html.Encode(user.Email)}.", "Send")); } [HttpPost("server/users/{userId}/verification-email")] diff --git a/BTCPayServer/Controllers/UIServerController.cs b/BTCPayServer/Controllers/UIServerController.cs index c7089342e3..9f730d9928 100644 --- a/BTCPayServer/Controllers/UIServerController.cs +++ b/BTCPayServer/Controllers/UIServerController.cs @@ -89,7 +89,8 @@ public partial class UIServerController : Controller Logs logs, LinkGenerator linkGenerator, EmailSenderFactory emailSenderFactory, - IHostApplicationLifetime applicationLifetime + IHostApplicationLifetime applicationLifetime, + IHtmlHelper html ) { _policiesSettings = policiesSettings; @@ -113,6 +114,7 @@ IHostApplicationLifetime applicationLifetime _linkGenerator = linkGenerator; _emailSenderFactory = emailSenderFactory; ApplicationLifetime = applicationLifetime; + Html = html; } [Route("server/maintenance")] @@ -296,6 +298,7 @@ private async Task RunSSHCore(SshClient sshClient, string ssh) public IHttpClientFactory HttpClientFactory { get; } public IHostApplicationLifetime ApplicationLifetime { get; } + public IHtmlHelper Html { get; } [Route("server/policies")] public async Task Policies() @@ -836,7 +839,7 @@ public async Task DeleteDynamicDnsService(string hostname) return NotFound(); return View("Confirm", new ConfirmModel("Delete dynamic DNS service", - $"Deleting the dynamic DNS service for {hostname} means your BTCPay Server will stop updating the associated DNS record periodically.", "Delete")); + $"Deleting the dynamic DNS service for {Html.Encode(hostname)} means your BTCPay Server will stop updating the associated DNS record periodically.", "Delete")); } [HttpPost("server/services/dynamic-dns/{hostname}/delete")] diff --git a/BTCPayServer/Controllers/UIStoresController.Onchain.cs b/BTCPayServer/Controllers/UIStoresController.Onchain.cs index 55374c9e9c..8b2d8f1f8a 100644 --- a/BTCPayServer/Controllers/UIStoresController.Onchain.cs +++ b/BTCPayServer/Controllers/UIStoresController.Onchain.cs @@ -800,9 +800,9 @@ private string WalletWarning(bool isHotWallet, string info) ? "" : " or imported it into an external wallet. If you no longer have access to your private key (recovery seed), immediately replace the wallet"; return - $"

Please note that this is a {walletType} wallet!

" + - $"

Do not proceed if you have not backed up the wallet{additionalText}.

" + - $"

This action will erase the current wallet data from the server. {info}

"; + $"

Please note that this is a {Html.Encode(walletType)} wallet!

" + + $"

Do not proceed if you have not backed up the wallet{Html.Encode(additionalText)}.

" + + $"

This action will erase the current wallet data from the server. {Html.Encode(info)}

"; } private string WalletReplaceWarning(bool isHotWallet) diff --git a/BTCPayServer/Controllers/UIStoresController.cs b/BTCPayServer/Controllers/UIStoresController.cs index 0f2aae5bf0..12de5671cb 100644 --- a/BTCPayServer/Controllers/UIStoresController.cs +++ b/BTCPayServer/Controllers/UIStoresController.cs @@ -65,7 +65,8 @@ public partial class UIStoresController : Controller WebhookSender webhookNotificationManager, IDataProtectionProvider dataProtector, IOptions lightningNetworkOptions, - IOptions externalServiceOptions) + IOptions externalServiceOptions, + IHtmlHelper html) { _RateFactory = rateFactory; _Repo = repo; @@ -89,6 +90,7 @@ public partial class UIStoresController : Controller _BtcpayServerOptions = btcpayServerOptions; _BTCPayEnv = btcpayEnv; _externalServiceOptions = externalServiceOptions; + Html = html; } readonly BTCPayServerOptions _BtcpayServerOptions; @@ -113,6 +115,7 @@ public partial class UIStoresController : Controller 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 @@ public async Task DeleteStoreUser(string userId) var user = await _UserManager.FindByIdAsync(userId); if (user == null) return NotFound(); - return View("Confirm", new ConfirmModel("Remove store user", $"This action will prevent {user.Email} from accessing this store and its settings. Are you sure?", "Remove")); + return View("Confirm", new ConfirmModel("Remove store user", $"This action will prevent {Html.Encode(user.Email)} from accessing this store and its settings. Are you sure?", "Remove")); } [HttpPost("{storeId}/users/{userId}/delete")] @@ -776,7 +779,7 @@ public async Task RevokeToken(string tokenId) 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 {token.Label} will be revoked. Do you wish to continue?", "Revoke")); + return View("Confirm", new ConfirmModel("Revoke the token", $"The access token with the label {Html.Encode(token.Label)} will be revoked. Do you wish to continue?", "Revoke")); } [HttpPost("{storeId}/tokens/{tokenId}/revoke")] diff --git a/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml b/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml index 67ec09274f..e9ed99766a 100644 --- a/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml +++ b/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml @@ -334,7 +334,7 @@ diff --git a/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml b/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml index 303c0271e3..c33965f097 100644 --- a/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml +++ b/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml @@ -265,7 +265,7 @@ diff --git a/BTCPayServer/Views/UIApps/ListApps.cshtml b/BTCPayServer/Views/UIApps/ListApps.cshtml index 96b1a52b46..4f64d330f3 100644 --- a/BTCPayServer/Views/UIApps/ListApps.cshtml +++ b/BTCPayServer/Views/UIApps/ListApps.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Services.Apps +@using BTCPayServer.Services.Apps @using BTCPayServer.Abstractions.Models @model ListAppsViewModel @{ @@ -103,7 +103,7 @@ Settings - } - Delete + Delete } diff --git a/BTCPayServer/Views/UICustodianAccounts/EditCustodianAccount.cshtml b/BTCPayServer/Views/UICustodianAccounts/EditCustodianAccount.cshtml index b3f2b950aa..a32466b315 100644 --- a/BTCPayServer/Views/UICustodianAccounts/EditCustodianAccount.cshtml +++ b/BTCPayServer/Views/UICustodianAccounts/EditCustodianAccount.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Views.Apps +@using BTCPayServer.Views.Apps @using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Models @model BTCPayServer.Models.CustodianAccountViewModels.EditCustodianAccountViewModel @@ -35,7 +35,7 @@ - + Delete this custodian account diff --git a/BTCPayServer/Views/UILNURL/EditLightningAddress.cshtml b/BTCPayServer/Views/UILNURL/EditLightningAddress.cshtml index 9ca321eedd..daa0d5b874 100644 --- a/BTCPayServer/Views/UILNURL/EditLightningAddress.cshtml +++ b/BTCPayServer/Views/UILNURL/EditLightningAddress.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Views.Stores +@using BTCPayServer.Views.Stores @using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Models @model UILNURLController.EditLightningAddressVM @@ -139,7 +139,7 @@ diff --git a/BTCPayServer/Views/UIManage/APIKeys.cshtml b/BTCPayServer/Views/UIManage/APIKeys.cshtml index 7f0e462d0f..4a07dac2f4 100644 --- a/BTCPayServer/Views/UIManage/APIKeys.cshtml +++ b/BTCPayServer/Views/UIManage/APIKeys.cshtml @@ -70,7 +70,7 @@ } - Delete + Delete - diff --git a/BTCPayServer/Views/UIManage/TwoFactorAuthentication.cshtml b/BTCPayServer/Views/UIManage/TwoFactorAuthentication.cshtml index a7c51ffbed..7cd4cdd27d 100644 --- a/BTCPayServer/Views/UIManage/TwoFactorAuthentication.cshtml +++ b/BTCPayServer/Views/UIManage/TwoFactorAuthentication.cshtml @@ -119,11 +119,11 @@ @if (device.Type == Fido2Credential.CredentialType.FIDO2) { - Remove + Remove } else if (device.Type == Fido2Credential.CredentialType.LNURLAuth) { - Remove + Remove } } diff --git a/BTCPayServer/Views/UIPayoutProcessors/ConfigureStorePayoutProcessors.cshtml b/BTCPayServer/Views/UIPayoutProcessors/ConfigureStorePayoutProcessors.cshtml index 0d2e62f3a0..4b801899f5 100644 --- a/BTCPayServer/Views/UIPayoutProcessors/ConfigureStorePayoutProcessors.cshtml +++ b/BTCPayServer/Views/UIPayoutProcessors/ConfigureStorePayoutProcessors.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Views.Stores +@using BTCPayServer.Views.Stores @using Microsoft.AspNetCore.Mvc.TagHelpers @using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Models @@ -44,7 +44,7 @@ { Modify - - Remove + Remove } diff --git a/BTCPayServer/Views/UIServer/DynamicDnsServices.cshtml b/BTCPayServer/Views/UIServer/DynamicDnsServices.cshtml index 0bcd619886..9681913254 100644 --- a/BTCPayServer/Views/UIServer/DynamicDnsServices.cshtml +++ b/BTCPayServer/Views/UIServer/DynamicDnsServices.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Abstractions.Models +@using BTCPayServer.Abstractions.Models @model BTCPayServer.Models.ServerViewModels.DynamicDnsViewModel[] @{ ViewData.SetActivePage(ServerNavPages.Services, "Dynamic DNS Settings"); @@ -61,7 +61,7 @@ Edit - - Delete + Delete } diff --git a/BTCPayServer/Views/UIServer/ListUsers.cshtml b/BTCPayServer/Views/UIServer/ListUsers.cshtml index 9906b9591a..2f3b0949fb 100644 --- a/BTCPayServer/Views/UIServer/ListUsers.cshtml +++ b/BTCPayServer/Views/UIServer/ListUsers.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Abstractions.Models +@using BTCPayServer.Abstractions.Models @model UsersViewModel @{ ViewData.SetActivePage(ServerNavPages.Users); @@ -97,7 +97,7 @@ @if (!user.Verified && !user.Disabled) { - Resend verification email + Resend verification email - } Edit - Remove diff --git a/BTCPayServer/Views/UIStorePullPayments/PullPayments.cshtml b/BTCPayServer/Views/UIStorePullPayments/PullPayments.cshtml index 79d8f06b9d..75ea2d6dc6 100644 --- a/BTCPayServer/Views/UIStorePullPayments/PullPayments.cshtml +++ b/BTCPayServer/Views/UIStorePullPayments/PullPayments.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Views.Stores +@using BTCPayServer.Views.Stores @using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Models @using BTCPayServer.Client @@ -148,7 +148,7 @@ asp-route-pullPaymentId="@pp.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" - data-description="Do you really want to archive the pull payment @pp.Name?"> + data-description="Do you really want to archive the pull payment @Html.Encode(pp.Name)?"> Archive } diff --git a/BTCPayServer/Views/UIStores/GeneralSettings.cshtml b/BTCPayServer/Views/UIStores/GeneralSettings.cshtml index a08e2758e2..ecb547e1fc 100644 --- a/BTCPayServer/Views/UIStores/GeneralSettings.cshtml +++ b/BTCPayServer/Views/UIStores/GeneralSettings.cshtml @@ -138,7 +138,7 @@ {

Additional Actions

} diff --git a/BTCPayServer/Views/UIStores/ListTokens.cshtml b/BTCPayServer/Views/UIStores/ListTokens.cshtml index 477d282466..cff1e65731 100644 --- a/BTCPayServer/Views/UIStores/ListTokens.cshtml +++ b/BTCPayServer/Views/UIStores/ListTokens.cshtml @@ -48,7 +48,7 @@ @token.Label See information - - Revoke + Revoke } diff --git a/BTCPayServer/Views/UIStores/StoreUsers.cshtml b/BTCPayServer/Views/UIStores/StoreUsers.cshtml index 06f9df928e..65f31d411e 100644 --- a/BTCPayServer/Views/UIStores/StoreUsers.cshtml +++ b/BTCPayServer/Views/UIStores/StoreUsers.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Abstractions.Models +@using BTCPayServer.Abstractions.Models @model StoreUsersViewModel @{ Layout = "../Shared/_NavLayout.cshtml"; @@ -51,7 +51,7 @@ @user.Email @user.Role - Remove + Remove } diff --git a/BTCPayServer/Views/UIStores/WalletSettings.cshtml b/BTCPayServer/Views/UIStores/WalletSettings.cshtml index fcbea8507c..746eafe94c 100644 --- a/BTCPayServer/Views/UIStores/WalletSettings.cshtml +++ b/BTCPayServer/Views/UIStores/WalletSettings.cshtml @@ -52,7 +52,7 @@ data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-title="Replace @Model.CryptoCode wallet" - data-description="@ViewData["ReplaceDescription"]" + data-description="@Html.Encode(ViewData["ReplaceDescription"])" data-confirm="Setup new wallet" data-confirm-input="REPLACE"> Replace wallet @@ -64,7 +64,7 @@ data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-title="Remove @Model.CryptoCode wallet" - data-description="@ViewData["RemoveDescription"]" + data-description="@Html.Encode(ViewData["RemoveDescription"])" data-confirm="Remove" data-confirm-input="REMOVE">Remove wallet