CVE-2023-1270: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting in GitHub repository btcpayserver/btcpayserver prior to 1.8.3.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2023-03-08T14:15Z
Repository: https://github.com/btcpayserver/btcpayserver
Language: Other
Files changed: BTCPayServer.Abstractions/Security/ContentSecurityPolicies.cs, BTCPayServer/Filters/ContentSecurityPolicyAttribute.cs, BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml, BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml, BTCPayServer/Views/Shared/PayButton/PayButton.cshtml, BTCPayServer/Views/Shared/PointOfSale/Public/Light.cshtml, BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml, BTCPayServer/Views/Shared/PosData.cshtml, BTCPayServer/Views/Shared/TemplateEditor.cshtml, BTCPayServer/Views/Shared/_Layout.cshtml, BTCPayServer/Views/Shared/_LayoutSignedOut.cshtml, BTCPayServer/Views/Shared/_StatusMessage.cshtml, BTCPayServer/Views/Shared/_StoreHeader.cshtml, BTCPayServer/Views/UIAccount/Login.cshtml, BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml, BTCPayServer/Views/UIInvoice/Checkout.cshtml, BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml, BTCPayServer/Views/UIManage/APIKeys.cshtml, BTCPayServer/Views/UIPaymentRequest/ViewPaymentRequest.cshtml, BTCPayServer/Views/UIPullPayment/ViewPullPayment.cshtml, BTCPayServer/Views/UIStores/ImportWallet/Scan.cshtml, BTCPayServer/Views/UIStores/WalletSettings.cshtml, BTCPayServer/Views/UIWallets/WalletPSBT.cshtml, BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml, BTCPayServer/Views/UIWallets/WalletSend.cshtml
Commit date: 2023-03-08 17:57:36 +0900
Commit message: CSP: Remove unsafe-eval when vue isn't used (#4747)

* CSP: Remove unsafe-eval when vue isn't used

* Prevent XSS injection via VueJS
Diff: diff --git a/BTCPayServer.Abstractions/Security/ContentSecurityPolicies.cs b/BTCPayServer.Abstractions/Security/ContentSecurityPolicies.cs
index 27edb825b..3f56bad90 100644
--- a/BTCPayServer.Abstractions/Security/ContentSecurityPolicies.cs
+++ b/BTCPayServer.Abstractions/Security/ContentSecurityPolicies.cs
@@ -112,10 +112,15 @@ namespace BTCPayServer.Security
         public void Add(ConsentSecurityPolicy policy)
         {
             _Policies.Add(policy);
         }
 
+        public void UnsafeEval()
+        {
+            Add("script-src", "'unsafe-eval'");
+        }
+
         public IEnumerable<ConsentSecurityPolicy> Rules => _Policies;
         public bool HasRules => _Policies.Count != 0;
 
         public override string ToString()
         {
diff --git a/BTCPayServer/Filters/ContentSecurityPolicyAttribute.cs b/BTCPayServer/Filters/ContentSecurityPolicyAttribute.cs
index b2973b003..f0ec77eb1 100644
--- a/BTCPayServer/Filters/ContentSecurityPolicyAttribute.cs
+++ b/BTCPayServer/Filters/ContentSecurityPolicyAttribute.cs
@@ -22,11 +22,11 @@ namespace BTCPayServer.Filters
             if (template == CSPTemplate.AntiXSS)
             {
                 AutoSelf = false;
                 FixWebsocket = false;
                 UnsafeInline = false;
-                ScriptSrc = "'self' 'unsafe-eval'"; // unsafe-eval needed for vue
+                ScriptSrc = "'self'";
             }
         }
 
         public void OnActionExecuted(ActionExecutedContext context)
         {
diff --git a/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml b/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml
index 981fcabe3..5f3b3dd05 100644
--- a/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml
+++ b/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml
@@ -1,17 +1,18 @@
 @model BTCPayServer.Plugins.Crowdfund.Models.ViewCrowdfundViewModel
 @using BTCPayServer.Plugins.Crowdfund.Models
 @inject BTCPayServer.Services.BTCPayServerEnvironment Env
 @inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @{
-    ViewData["Title"] = Model.Title;
-    Layout = null;
-    if (!string.IsNullOrEmpty(Model.DisqusShortname))
-    {
-        Csp.Add("script-src", $"https://{Model.DisqusShortname}.disqus.com");
-        Csp.Add("script-src", "https://c.disquscdn.com");
-    }
+	ViewData["Title"] = Model.Title;
+	Layout = null;
+	Csp.UnsafeEval();
+	if (!string.IsNullOrEmpty(Model.DisqusShortname))
+	{
+		Csp.Add("script-src", $"https://{Model.DisqusShortname}.disqus.com");
+		Csp.Add("script-src", "https://c.disquscdn.com");
+	}
 }
 <!DOCTYPE html>
 <html class="h-100" @(Env.IsDeveloping ? " data-devenv" : "")>
 <head>
     <partial name="LayoutHead"/>
@@ -53,17 +54,17 @@
 }
 
 <div class="public-page-wrap flex-column container" id="app" @(Model.SimpleDisplay ? "" : "v-cloak")>
     @if (!string.IsNullOrEmpty(Model.MainImageUrl))
     {
-        <img v-if="srvModel.mainImageUrl" src="@Model.MainImageUrl" :src="srvModel.mainImageUrl" alt="@Model.Title" :alt="srvModel.title" id="crowdfund-main-image" asp-append-version="true"/>
+        <img v-if="srvModel.mainImageUrl" :src="srvModel.mainImageUrl" :alt="srvModel.title" id="crowdfund-main-image" asp-append-version="true"/>
     }
     <div class="d-flex flex-column justify-content-between p-3 text-center" id="crowdfund-header-container">
-        <h1 class="mb-3">@Model.Title</h1>
+	  <h1 class="mb-3">{{ srvModel.title }}</h1>
         @if (!string.IsNullOrEmpty(Model.Tagline))
         {
-            <h2 class="h3 mb-3 fw-semibold" v-if="srvModel.tagline" v-text="srvModel.tagline">@Model.Tagline</h2>
+            <h2 class="h3 mb-3 fw-semibold" v-if="srvModel.tagline" v-text="srvModel.tagline"></h2>
         }
         @if (Model.TargetAmount.HasValue)
         {
             <span v-if="srvModel.targetAmount" class="mt-3" id="crowdfund-header-target">
                 <h3 class="d-inline-block">
@@ -219,21 +220,19 @@
         <div class="col-lg-7 col-sm-12" id="crowdfund-body-description-container">
             <template v-if="srvModel.disqusEnabled && srvModel.disqusShortname">
                 <b-tabs>
                     <b-tab title="Details" active>
                         <div class="overflow-hidden pt-3" v-html="srvModel.description" id="crowdfund-body-description">
-                            @Safe.Raw(Model.Description)
                         </div>
                     </b-tab>
                     <b-tab title="Discussion">
                         <div id="disqus_thread" class="mt-4"></div>
                     </b-tab>
                 </b-tabs>
             </template>
             <template v-else>
                 <div class="overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
-                    @Safe.Raw(Model.Description)
                 </div>
             </template>
         </div>
         <div class="col-lg-4 col-sm-12" id="crowdfund-body-contribution-container">
             <contribute :target-currency="srvModel.targetCurrency"
@@ -244,11 +243,11 @@
                         :in-modal="false"
                         :perks="perks">
             </contribute>
         </div>
     </div>
-    <noscript>
+	<noscript v-pre>
         <div class="row justify-content-between">
             <div class="col-md-7  col-sm-12">
                 <div class="overflow-hidden">@Safe.Raw(Model.Description)</div>
             </div>
             <div class="col-md-4 col-sm-12">
diff --git a/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml b/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml
index acb8f47cf..013d87919 100644
--- a/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml
+++ b/BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml
@@ -2,13 +2,15 @@
 @using BTCPayServer.Abstractions.Extensions
 @using BTCPayServer.Abstractions.Models
 @using BTCPayServer.TagHelpers
 @using BTCPayServer.Views.Apps
 @using Microsoft.AspNetCore.Mvc.TagHelpers
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model BTCPayServer.Plugins.Crowdfund.Models.UpdateCrowdfundViewModel
 @{
     ViewData.SetActivePage(AppsNavPages.Update, "Update Crowdfund", Model.AppId);
+	Csp.UnsafeEval();
 }
 
 @section PageHeadContent {
     <link href="~/vendor/summernote/summernote-bs5.css" rel="stylesheet" asp-append-version="true" />
     <link href="~/main/template-editor.css" rel="stylesheet" asp-append-version="true" />
diff --git a/BTCPayServer/Views/Shared/PayButton/PayButton.cshtml b/BTCPayServer/Views/Shared/PayButton/PayButton.cshtml
index ba43963d9..b64f249e1 100644
--- a/BTCPayServer/Views/Shared/PayButton/PayButton.cshtml
+++ b/BTCPayServer/Views/Shared/PayButton/PayButton.cshtml
@@ -1,9 +1,11 @@
 @using BTCPayServer.Views.Stores
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model BTCPayServer.Plugins.PayButton.Models.PayButtonViewModel
 @{
     ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
+	Csp.UnsafeEval();
 }
 
 @section PageHeadContent {
     <link rel="stylesheet" href="~/vendor/highlightjs/default.min.css" asp-append-version="true">
 }
diff --git a/BTCPayServer/Views/Shared/PointOfSale/Public/Light.cshtml b/BTCPayServer/Views/Shared/PointOfSale/Public/Light.cshtml
index 0778507b5..136e8b0aa 100644
--- a/BTCPayServer/Views/Shared/PointOfSale/Public/Light.cshtml
+++ b/BTCPayServer/Views/Shared/PointOfSale/Public/Light.cshtml
@@ -1,8 +1,10 @@
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
 @{
     Layout = "PointOfSale/Public/_Layout";
+	Csp.UnsafeEval();
 }
 @section PageHeadContent {
     <style>
         .public-page-wrap {
             max-width: 560px;
diff --git a/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml b/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml
index 8c353a5db..958851fe7 100644
--- a/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml
+++ b/BTCPayServer/Views/Shared/PointOfSale/UpdatePointOfSale.cshtml
@@ -3,14 +3,15 @@
 @using BTCPayServer.Views.Apps
 @using BTCPayServer.Abstractions.Extensions
 @using BTCPayServer.Forms
 @using BTCPayServer.Services.Stores
 @inject FormDataService FormDataService
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model BTCPayServer.Plugins.PointOfSale.Models.UpdatePointOfSaleViewModel
 @{
     ViewData.SetActivePage(AppsNavPages.Update, "Update Point of Sale", Model.Id);
-
+	Csp.UnsafeEval();
     var checkoutFormOptions = await FormDataService.GetSelect(Model.StoreId, Model.FormId);
 }
 
 <form method="post">
     <div class="sticky-header-setup"></div>
diff --git a/BTCPayServer/Views/Shared/PosData.cshtml b/BTCPayServer/Views/Shared/PosData.cshtml
index b57385823..4288b0719 100644
--- a/BTCPayServer/Views/Shared/PosData.cshtml
+++ b/BTCPayServer/Views/Shared/PosData.cshtml
@@ -6,11 +6,11 @@
         return Uri.TryCreate(source, UriKind.Absolute, out var uriResult) && 
             (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
     }
 }
 
-<table class="table my-0">
+<table class="table my-0" v-pre>
     @foreach (var (key, value) in Model.Items)
     {
         <tr>
             @if (value is string str)
             {
diff --git a/BTCPayServer/Views/Shared/TemplateEditor.cshtml b/BTCPayServer/Views/Shared/TemplateEditor.cshtml
index c045a8ab9..1dacf57f1 100644
--- a/BTCPayServer/Views/Shared/TemplateEditor.cshtml
+++ b/BTCPayServer/Views/Shared/TemplateEditor.cshtml
@@ -6,11 +6,11 @@
         @if (ViewContext.ViewData.ModelState.TryGetValue(Model.templateId, out var errors))
         {
             foreach (var error in errors.Errors)
             {
                 <br/>
-                <span class="text-danger">@error.ErrorMessage</span>
+                <span class="text-danger" v-pre>@error.ErrorMessage</span>
             }
         }
 
         <div class="bg-light card">
             <div class="card-body " v-bind:class="{ 'card-deck': items.length > 0}">
diff --git a/BTCPayServer/Views/Shared/_Layout.cshtml b/BTCPayServer/Views/Shared/_Layout.cshtml
index 142680964..070f7b4cb 100644
--- a/BTCPayServer/Views/Shared/_Layout.cshtml
+++ b/BTCPayServer/Views/Shared/_Layout.cshtml
@@ -25,11 +25,11 @@
 <head>
     <partial name="LayoutHead" />
     @await RenderSectionAsync("PageHeadContent", false)
 </head>
 <body class="d-flex flex-column flex-lg-row min-vh-100">
-<header id="mainMenu" class="btcpay-header d-flex flex-column">
+<header id="mainMenu" class="btcpay-header d-flex flex-column" v-pre>
     <div id="mainMenuHead">
         <button id="mainMenuToggle" class="mainMenuButton" type="button" data-bs-toggle="offcanvas" data-bs-target="#mainNav" aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
             <span>Menu</span>
         </button>
         <vc:store-selector />
diff --git a/BTCPayServer/Views/Shared/_LayoutSignedOut.cshtml b/BTCPayServer/Views/Shared/_LayoutSignedOut.cshtml
index a3b253177..28ea06377 100644
--- a/BTCPayServer/Views/Shared/_LayoutSignedOut.cshtml
+++ b/BTCPayServer/Views/Shared/_LayoutSignedOut.cshtml
@@ -1,6 +1,6 @@
-﻿@{
+@{
     Layout = "_LayoutSimple";
     ViewBag.ShowTitle ??= true;
     ViewBag.ShowLeadText ??= false;
 }
 
@@ -48,11 +48,11 @@
 </div>
 
 <div class="account-form">
     @if (ViewBag.ShowTitle)
     {
-        <h4>@ViewData["Title"]</h4>
+	  <h4 v-pre>@ViewData["Title"]</h4>
     }
     @RenderBody()
 </div>
 
 <div class="row justify-content-center mt-5">
diff --git a/BTCPayServer/Views/Shared/_StatusMessage.cshtml b/BTCPayServer/Views/Shared/_StatusMessage.cshtml
index f06889eca..b1f3af0ff 100644
--- a/BTCPayServer/Views/Shared/_StatusMessage.cshtml
+++ b/BTCPayServer/Views/Shared/_StatusMessage.cshtml
@@ -3,11 +3,11 @@
     var parsedModel = TempData.GetStatusMessageModel();
 }
 
 @if (parsedModel != null)
 {
-    <div class="alert alert-@parsedModel.SeverityCSS @(parsedModel.AllowDismiss? "alert-dismissible":"" ) @(ViewData["Margin"] ?? "mb-4") text-break" role="alert">
+	<div class="alert alert-@parsedModel.SeverityCSS @(parsedModel.AllowDismiss? "alert-dismissible":"" ) @(ViewData["Margin"] ?? "mb-4") text-break" role="alert" v-pre>
         @if (parsedModel.AllowDismiss)
         {
             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
                 <vc:icon symbol="close" />
             </button>
diff --git a/BTCPayServer/Views/Shared/_StoreHeader.cshtml b/BTCPayServer/Views/Shared/_StoreHeader.cshtml
index a86444734..7c6e64606 100644
--- a/BTCPayServer/Views/Shared/_StoreHeader.cshtml
+++ b/BTCPayServer/Views/Shared/_StoreHeader.cshtml
@@ -5,11 +5,11 @@
 @{
     var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
         ? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
         : null;
 }
-<header class="store-header">
+<header class="store-header" v-pre>
     @if (!string.IsNullOrEmpty(logoUrl))
     {
         <img src="@logoUrl" alt="@Model.Title" class="store-logo"/>
     }
     <h1 class="store-name">@Model.Title</h1>
diff --git a/BTCPayServer/Views/UIAccount/Login.cshtml b/BTCPayServer/Views/UIAccount/Login.cshtml
index da11f7aa5..3f2f97075 100644
--- a/BTCPayServer/Views/UIAccount/Login.cshtml
+++ b/BTCPayServer/Views/UIAccount/Login.cshtml
@@ -1,10 +1,12 @@
 @model LoginViewModel
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @inject BTCPayServer.Services.PoliciesSettings PoliciesSettings
 @{
-    ViewData["Title"] = "Sign in";
-    Layout = "_LayoutSignedOut";
+	ViewData["Title"] = "Sign in";
+	Layout = "_LayoutSignedOut";
+	Csp.UnsafeEval();
 }
 
 <form asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" id="login-form" asp-action="Login">
     <fieldset disabled="@(ViewData.ContainsKey("disabled") ? "disabled" : null)">
         <div asp-validation-summary="ModelOnly" class="text-danger"></div>
diff --git a/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml b/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml
index d0cc8076b..cd390d8d5 100644
--- a/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml
+++ b/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml
@@ -1,11 +1,13 @@
 @using BTCPayServer.Views.Apps
 @using BTCPayServer.Abstractions.Extensions
 @using BTCPayServer.Abstractions.Custodians
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model BTCPayServer.Models.CustodianAccountViewModels.ViewCustodianAccountViewModel
 @{
-    ViewData.SetActivePage(AppsNavPages.Create, "Custodian account: " + @Model?.CustodianAccount.Name);
+	ViewData.SetActivePage(AppsNavPages.Create, "Custodian account: " + @Model?.CustodianAccount.Name);
+	Csp.UnsafeEval();
 }
 
 @section PageHeadContent
 {
     <link href="~/main/qrcode.css" rel="stylesheet" asp-append-version="true"/>
diff --git a/BTCPayServer/Views/UIInvoice/Checkout.cshtml b/BTCPayServer/Views/UIInvoice/Checkout.cshtml
index 3afa6ed50..ffa68d3ef 100644
--- a/BTCPayServer/Views/UIInvoice/Checkout.cshtml
+++ b/BTCPayServer/Views/UIInvoice/Checkout.cshtml
@@ -1,11 +1,13 @@
 @inject BTCPayServer.Services.LanguageService langService
 @inject BTCPayServer.Services.BTCPayServerEnvironment env
 @inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model PaymentModel
 @{
-    Layout = null;
+	Layout = null;
+	Csp.UnsafeEval();
 }
 
 <!DOCTYPE html>
 <html>
 <head>
diff --git a/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml b/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml
index 5d6607cd0..da6aacbba 100644
--- a/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml
+++ b/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml
@@ -3,16 +3,17 @@
 @using Microsoft.AspNetCore.Mvc.TagHelpers
 @inject LanguageService LangService
 @inject BTCPayServerEnvironment Env
 @inject IEnumerable<IUIExtension> UiExtensions 
 @inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model PaymentModel
 @{
-    Layout = null;
-    ViewData["Title"] = Model.HtmlTitle;
-
-    var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
+	Layout = null;
+	ViewData["Title"] = Model.HtmlTitle;
+	Csp.UnsafeEval();
+	var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
     // Show LNURL as selectable payment method only for top up invoices + non-BIP21 case
     var displayedPaymentMethods = Model.IsUnsetTopUp && !Model.OnChainWithLnInvoiceFallback
         ? Model.AvailableCryptos
         : Model.AvailableCryptos.Where(c => c.PaymentMethodId != "BTC_LNURLPAY").ToList();
 }
@@ -47,11 +48,11 @@
                 </button>
             </nav>
             <section id="payment" v-if="isActive">
                 @if (!string.IsNullOrEmpty(Model.ItemDesc) && Model.ItemDesc != Model.StoreName)
                 {
-                    <h5 class="text-center mt-1 mb-3 fw-semibold" v-if="srvModel.itemDesc" v-text="srvModel.itemDesc">@Model.ItemDesc</h5>
+                    <h5 class="text-center mt-1 mb-3 fw-semibold" v-if="srvModel.itemDesc" v-text="srvModel.itemDesc"></h5>
                 }
                 @if (Model.IsUnsetTopUp)
                 {
                     <h2 id="AmountDue" class="mt-1 text-center" v-t="'any_amount'"></h2>
                 }
diff --git a/BTCPayServer/Views/UIManage/APIKeys.cshtml b/BTCPayServer/Views/UIManage/APIKeys.cshtml
index 4a07dac2f..232053e78 100644
--- a/BTCPayServer/Views/UIManage/APIKeys.cshtml
+++ b/BTCPayServer/Views/UIManage/APIKeys.cshtml
@@ -1,10 +1,12 @@
 @namespace BTCPayServer.Client
 @using BTCPayServer.Abstractions.Models
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model BTCPayServer.Controllers.UIManageController.ApiKeysViewModel
 @{
-    ViewData.SetActivePage(ManageNavPages.APIKeys, "API Keys");
+	ViewData.SetActivePage(ManageNavPages.APIKeys, "API Keys");
+	Csp.UnsafeEval();
 }
 
 <div class="row">
     <div class="col-xl-10 col-xxl-constrain">
         <div class="d-flex align-items-center justify-content-between mb-3">
diff --git a/BTCPayServer/Views/UIPaymentRequest/ViewPaymentRequest.cshtml b/BTCPayServer/Views/UIPaymentRequest/ViewPaymentRequest.cshtml
index 04d08583e..47d8d8d4a 100644
--- a/BTCPayServer/Views/UIPaymentRequest/ViewPaymentRequest.cshtml
+++ b/BTCPayServer/Views/UIPaymentRequest/ViewPaymentRequest.cshtml
@@ -1,13 +1,15 @@
 @using BTCPayServer.Services.Invoices
 @using BTCPayServer.Client.Models
 @using BTCPayServer.Client
 @model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
 @inject BTCPayServer.Services.BTCPayServerEnvironment Env
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @{
-    ViewData["Title"] = Model.Title;
-    Layout = null;
+	ViewData["Title"] = Model.Title;
+	Csp.UnsafeEval();
+	Layout = null;
     string StatusClass(InvoiceState state)
     {
         switch (state.Status.ToModernStatus())
         {
             case InvoiceStatus.Settled:
@@ -66,11 +68,11 @@
             <div class="container">
                 <div class="row align-items-center" style="width:calc(100% + 30px)">
                     <div class="col-12 col-md-8 col-lg-9">
                         <div class="row">
                             <div class="col col-12 col-lg-8">
-                                <h1 class="h3" v-text="srvModel.title">@Model.Title</h1>
+                                <h1 class="h3" v-text="srvModel.title"></h1>
                             </div>
                             <div class="col col-12 col-sm-6 col-lg-8 d-flex align-items-center">
                                 <span class="text-muted text-nowrap">Last Updated</span>
                                 &nbsp;
                                 <span class="text-nowrap d-print-none" v-text="lastUpdated" v-cloak>@Model.LastUpdated.ToString("g")</span>
@@ -202,13 +204,11 @@
                     <div class="col col-12 col-lg-6 mb-4">
                         <div class="bg-tile h-100 m-0 p-3 p-sm-5 rounded">
                             <h2 class="h4 mb-3">Invoice Summary</h2>
                             @if (!string.IsNullOrEmpty(Model.Description) && Model.Description != "<br>")
                             {
-                                <div v-html="srvModel.description">
-                                    @Safe.Raw(Model.Description)
-                                </div>
+                                <div v-html="srvModel.description"></div>
                             }
                             else
                             {
                                 <p class="text-muted mt-3 mb-0">No details provided.</p>
                             }
diff --git a/BTCPayServer/Views/UIPullPayment/ViewPullPayment.cshtml b/BTCPayServer/Views/UIPullPayment/ViewPullPayment.cshtml
index 229a70416..60bec013a 100644
--- a/BTCPayServer/Views/UIPullPayment/ViewPullPayment.cshtml
+++ b/BTCPayServer/Views/UIPullPayment/ViewPullPayment.cshtml
@@ -2,13 +2,15 @@
 @inject BTCPayNetworkProvider BtcPayNetworkProvider
 @using BTCPayServer.Client
 @using BTCPayServer.Components.ThemeSwitch
 @using BTCPayServer.Payments
 @model BTCPayServer.Models.ViewPullPaymentModel
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 
 @{
     ViewData["Title"] = Model.Title;
+	Csp.UnsafeEval();
     Layout = null;
     string StatusTextClass(string status)
     {
         switch (status)
         {
diff --git a/BTCPayServer/Views/UIStores/ImportWallet/Scan.cshtml b/BTCPayServer/Views/UIStores/ImportWallet/Scan.cshtml
index 74f0160f0..0ca18ee25 100644
--- a/BTCPayServer/Views/UIStores/ImportWallet/Scan.cshtml
+++ b/BTCPayServer/Views/UIStores/ImportWallet/Scan.cshtml
@@ -1,9 +1,11 @@
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model WalletSetupViewModel
 @{
-    Layout = "_LayoutWalletSetup";
-    ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Scan QR code", Context.GetStoreData().Id);
+	Layout = "_LayoutWalletSetup";
+	ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Scan QR code", Context.GetStoreData().Id);
+	Csp.UnsafeEval();
 }
 
 @section Navbar {
     <a asp-controller="UIStores" asp-action="ImportWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" asp-route-method="">
         <vc:icon symbol="back" />
diff --git a/BTCPayServer/Views/UIStores/WalletSettings.cshtml b/BTCPayServer/Views/UIStores/WalletSettings.cshtml
index f3dea05e4..d9e24b4f6 100644
--- a/BTCPayServer/Views/UIStores/WalletSettings.cshtml
+++ b/BTCPayServer/Views/UIStores/WalletSettings.cshtml
@@ -1,14 +1,16 @@
 @using NBitcoin.DataEncoders
 @using Newtonsoft.Json
 @using System.Text
 @using BTCPayServer.Abstractions.Models
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model WalletSettingsViewModel
 @{
-    Layout = "../Shared/_NavLayout.cshtml";
-    ViewData["NavPartialName"] = "../UIWallets/_Nav";
-    ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().Id);
+	Layout = "../Shared/_NavLayout.cshtml";
+	ViewData["NavPartialName"] = "../UIWallets/_Nav";
+	ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().Id);
+	Csp.UnsafeEval();
 }
 
 @section PageHeadContent {
     <script src="~/vendor/vuejs/vue.min.js" asp-append-version="true"></script>
     <script src="~/vendor/vue-qrcode/vue-qrcode.min.js" asp-append-version="true"></script>
diff --git a/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml b/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml
index 95a69e9a1..a032a9250 100644
--- a/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml
+++ b/BTCPayServer/Views/UIWallets/WalletPSBT.cshtml
@@ -1,13 +1,15 @@
 @using BTCPayServer.Controllers
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model WalletPSBTViewModel
 @{
-    var walletId = Context.GetRouteValue("walletId").ToString();
-    var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
-    var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
-    Layout = "_LayoutWizard";
-    ViewData.SetActivePage(WalletsNavPages.PSBT, "Decode PSBT", walletId);
+	var walletId = Context.GetRouteValue("walletId").ToString();
+	var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
+	var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
+	Layout = "_LayoutWizard";
+	ViewData.SetActivePage(WalletsNavPages.PSBT, "Decode PSBT", walletId);
+	Csp.UnsafeEval();
 }
 
 @section Navbar {
     @if (backUrl != null)
     {
diff --git a/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml b/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml
index 30757ad27..deea9e2d6 100644
--- a/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml
+++ b/BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml
@@ -1,16 +1,18 @@
 @using BTCPayServer.Controllers
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model WalletPSBTViewModel
 @{
 	var walletId = Context.GetRouteValue("walletId").ToString();
-    var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
-    var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
+	var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
+	var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
 	var isReady = !Model.HasErrors;
 	var isSignable = !isReady;
 	var needsExport = !isSignable && !isReady;
 	Layout = "_LayoutWizard";
 	ViewData.SetActivePage(WalletsNavPages.PSBT, isReady ? "Confirm broadcasting this transaction" : "Transaction Details", walletId);
+	Csp.UnsafeEval();
 }
 
 @section PageHeadContent {
     <link rel="stylesheet" href="~/vendor/highlightjs/default.min.css" asp-append-version="true">
     <link href="~/vendor/vue-qrcode-reader/vue-qrcode-reader.css" rel="stylesheet" asp-append-version="true"/>
diff --git a/BTCPayServer/Views/UIWallets/WalletSend.cshtml b/BTCPayServer/Views/UIWallets/WalletSend.cshtml
index eb66da83d..3e7dbb234 100644
--- a/BTCPayServer/Views/UIWallets/WalletSend.cshtml
+++ b/BTCPayServer/Views/UIWallets/WalletSend.cshtml
@@ -1,16 +1,18 @@
 @inject BTCPayServer.Security.ContentSecurityPolicies csp
 @using Microsoft.AspNetCore.Mvc.ModelBinding
 @using BTCPayServer.Controllers
+@inject BTCPayServer.Security.ContentSecurityPolicies Csp
 @model WalletSendModel
 @{
-    var walletId = Context.GetRouteValue("walletId").ToString();
-    var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
-    var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
-    Layout = "_LayoutWizard";
-    ViewData.SetActivePage(WalletsNavPages.Send, $"Send {Model.CryptoCode}", walletId);
-    csp.Add("worker-src", "blob:");
+	var walletId = Context.GetRouteValue("walletId").ToString();
+	var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
+	var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
+	Layout = "_LayoutWizard";
+	ViewData.SetActivePage(WalletsNavPages.Send, $"Send {Model.CryptoCode}", walletId);
+	csp.Add("worker-src", "blob:");
+	Csp.UnsafeEval();
 }
 
 @section Navbar {
     @if (backUrl != null)
     {

Vulnerable code:                 ScriptSrc = "'self' 'unsafe-eval'"; // unsafe-eval needed for vue
    ViewData["Title"] = Model.Title;
    Layout = null;
    if (!string.IsNullOrEmpty(Model.DisqusShortname))
    {
        Csp.Add("script-src", $"https://{Model.DisqusShortname}.disqus.com");
        Csp.Add("script-src", "https://c.disquscdn.com");
    }
        <img v-if="srvModel.mainImageUrl" src="@Model.MainImageUrl" :src="srvModel.mainImageUrl" alt="@Model.Title" :alt="srvModel.title" id="crowdfund-main-image" asp-append-version="true"/>
        <h1 class="mb-3">@Model.Title</h1>
            <h2 class="h3 mb-3 fw-semibold" v-if="srvModel.tagline" v-text="srvModel.tagline">@Model.Tagline</h2>
                            @Safe.Raw(Model.Description)
                    @Safe.Raw(Model.Description)
    <noscript>

<table class="table my-0">
                <span class="text-danger">@error.ErrorMessage</span>
<header id="mainMenu" class="btcpay-header d-flex flex-column">
﻿@{
        <h4>@ViewData["Title"]</h4>
    <div class="alert alert-@parsedModel.SeverityCSS @(parsedModel.AllowDismiss? "alert-dismissible":"" ) @(ViewData["Margin"] ?? "mb-4") text-break" role="alert">
<header class="store-header">
    ViewData["Title"] = "Sign in";
    Layout = "_LayoutSignedOut";
    ViewData.SetActivePage(AppsNavPages.Create, "Custodian account: " + @Model?.CustodianAccount.Name);
    Layout = null;
    Layout = null;
    ViewData["Title"] = Model.HtmlTitle;

    var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
                    <h5 class="text-center mt-1 mb-3 fw-semibold" v-if="srvModel.itemDesc" v-text="srvModel.itemDesc">@Model.ItemDesc</h5>
    ViewData.SetActivePage(ManageNavPages.APIKeys, "API Keys");
    ViewData["Title"] = Model.Title;
    Layout = null;
                                <h1 class="h3" v-text="srvModel.title">@Model.Title</h1>
                                <div v-html="srvModel.description">
                                    @Safe.Raw(Model.Description)
                                </div>
    Layout = "_LayoutWalletSetup";
    ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Scan QR code", Context.GetStoreData().Id);
    Layout = "../Shared/_NavLayout.cshtml";
    ViewData["NavPartialName"] = "../UIWallets/_Nav";
    ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().Id);
    var walletId = Context.GetRouteValue("walletId").ToString();
    var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
    var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
    Layout = "_LayoutWizard";
    ViewData.SetActivePage(WalletsNavPages.PSBT, "Decode PSBT", walletId);
    var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
    var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
    var walletId = Context.GetRouteValue("walletId").ToString();
    var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
    var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
    Layout = "_LayoutWizard";
    ViewData.SetActivePage(WalletsNavPages.Send, $"Send {Model.CryptoCode}", walletId);
    csp.Add("worker-src", "blob:");
Fixed code:         public void UnsafeEval()
        {
            Add("script-src", "'unsafe-eval'");
        }

                ScriptSrc = "'self'";
	ViewData["Title"] = Model.Title;
	Layout = null;
	Csp.UnsafeEval();
	if (!string.IsNullOrEmpty(Model.DisqusShortname))
	{
		Csp.Add("script-src", $"https://{Model.DisqusShortname}.disqus.com");
		Csp.Add("script-src", "https://c.disquscdn.com");
	}
        <img v-if="srvModel.mainImageUrl" :src="srvModel.mainImageUrl" :alt="srvModel.title" id="crowdfund-main-image" asp-append-version="true"/>
	  <h1 class="mb-3">{{ srvModel.title }}</h1>
            <h2 class="h3 mb-3 fw-semibold" v-if="srvModel.tagline" v-text="srvModel.tagline"></h2>
	<noscript v-pre>
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Csp.UnsafeEval();
<table class="table my-0" v-pre>
                <span class="text-danger" v-pre>@error.ErrorMessage</span>
<header id="mainMenu" class="btcpay-header d-flex flex-column" v-pre>
@{
	  <h4 v-pre>@ViewData["Title"]</h4>
	<div class="alert alert-@parsedModel.SeverityCSS @(parsedModel.AllowDismiss? "alert-dismissible":"" ) @(ViewData["Margin"] ?? "mb-4") text-break" role="alert" v-pre>
<header class="store-header" v-pre>
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	ViewData["Title"] = "Sign in";
	Layout = "_LayoutSignedOut";
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	ViewData.SetActivePage(AppsNavPages.Create, "Custodian account: " + @Model?.CustodianAccount.Name);
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Layout = null;
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Layout = null;
	ViewData["Title"] = Model.HtmlTitle;
	Csp.UnsafeEval();
	var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
                    <h5 class="text-center mt-1 mb-3 fw-semibold" v-if="srvModel.itemDesc" v-text="srvModel.itemDesc"></h5>
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	ViewData.SetActivePage(ManageNavPages.APIKeys, "API Keys");
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	ViewData["Title"] = Model.Title;
	Csp.UnsafeEval();
	Layout = null;
                                <h1 class="h3" v-text="srvModel.title"></h1>
                                <div v-html="srvModel.description"></div>
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Layout = "_LayoutWalletSetup";
	ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Scan QR code", Context.GetStoreData().Id);
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	Layout = "../Shared/_NavLayout.cshtml";
	ViewData["NavPartialName"] = "../UIWallets/_Nav";
	ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().Id);
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	var walletId = Context.GetRouteValue("walletId").ToString();
	var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
	var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
	Layout = "_LayoutWizard";
	ViewData.SetActivePage(WalletsNavPages.PSBT, "Decode PSBT", walletId);
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
	var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
	Csp.UnsafeEval();
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
	var walletId = Context.GetRouteValue("walletId").ToString();
	var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
	var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
	Layout = "_LayoutWizard";
	ViewData.SetActivePage(WalletsNavPages.Send, $"Send {Model.CryptoCode}", walletId);
	csp.Add("worker-src", "blob:");
	Csp.UnsafeEval();