Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
CSP: Remove unsafe-eval when vue isn't used (#4747)
* CSP: Remove unsafe-eval when vue isn't used

* Prevent XSS injection via VueJS
  • Loading branch information
NicolasDorier committed Mar 8, 2023
1 parent 2010a9a commit 7b5ce8f
Show file tree
Hide file tree
Showing 25 changed files with 88 additions and 56 deletions.
5 changes: 5 additions & 0 deletions BTCPayServer.Abstractions/Security/ContentSecurityPolicies.cs
Expand Up @@ -114,6 +114,11 @@ 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;

Expand Down
2 changes: 1 addition & 1 deletion BTCPayServer/Filters/ContentSecurityPolicyAttribute.cs
Expand Up @@ -24,7 +24,7 @@ public ContentSecurityPolicyAttribute(CSPTemplate template)
AutoSelf = false;
FixWebsocket = false;
UnsafeInline = false;
ScriptSrc = "'self' 'unsafe-eval'"; // unsafe-eval needed for vue
ScriptSrc = "'self'";
}
}

Expand Down
25 changes: 12 additions & 13 deletions BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml
Expand Up @@ -3,13 +3,14 @@
@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" : "")>
Expand Down Expand Up @@ -55,13 +56,13 @@
<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)
{
Expand Down Expand Up @@ -221,7 +222,6 @@
<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">
Expand All @@ -231,7 +231,6 @@
</template>
<template v-else>
<div class="overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
@Safe.Raw(Model.Description)
</div>
</template>
</div>
Expand All @@ -246,7 +245,7 @@
</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>
Expand Down
2 changes: 2 additions & 0 deletions BTCPayServer/Views/Shared/Crowdfund/UpdateCrowdfund.cshtml
Expand Up @@ -4,9 +4,11 @@
@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 {
Expand Down
2 changes: 2 additions & 0 deletions BTCPayServer/Views/Shared/PayButton/PayButton.cshtml
@@ -1,7 +1,9 @@
@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 {
Expand Down
2 changes: 2 additions & 0 deletions BTCPayServer/Views/Shared/PointOfSale/Public/Light.cshtml
@@ -1,6 +1,8 @@
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{
Layout = "PointOfSale/Public/_Layout";
Csp.UnsafeEval();
}
@section PageHeadContent {
<style>
Expand Down
Expand Up @@ -5,10 +5,11 @@
@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);
}

Expand Down
2 changes: 1 addition & 1 deletion BTCPayServer/Views/Shared/PosData.cshtml
Expand Up @@ -8,7 +8,7 @@
}
}

<table class="table my-0">
<table class="table my-0" v-pre>
@foreach (var (key, value) in Model.Items)
{
<tr>
Expand Down
2 changes: 1 addition & 1 deletion BTCPayServer/Views/Shared/TemplateEditor.cshtml
Expand Up @@ -8,7 +8,7 @@
foreach (var error in errors.Errors)
{
<br/>
<span class="text-danger">@error.ErrorMessage</span>
<span class="text-danger" v-pre>@error.ErrorMessage</span>
}
}

Expand Down
2 changes: 1 addition & 1 deletion BTCPayServer/Views/Shared/_Layout.cshtml
Expand Up @@ -27,7 +27,7 @@
@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>
Expand Down
4 changes: 2 additions & 2 deletions BTCPayServer/Views/Shared/_LayoutSignedOut.cshtml
@@ -1,4 +1,4 @@
@{
@{
Layout = "_LayoutSimple";
ViewBag.ShowTitle ??= true;
ViewBag.ShowLeadText ??= false;
Expand Down Expand Up @@ -50,7 +50,7 @@
<div class="account-form">
@if (ViewBag.ShowTitle)
{
<h4>@ViewData["Title"]</h4>
<h4 v-pre>@ViewData["Title"]</h4>
}
@RenderBody()
</div>
Expand Down
2 changes: 1 addition & 1 deletion BTCPayServer/Views/Shared/_StatusMessage.cshtml
Expand Up @@ -5,7 +5,7 @@

@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">
Expand Down
2 changes: 1 addition & 1 deletion BTCPayServer/Views/Shared/_StoreHeader.cshtml
Expand Up @@ -7,7 +7,7 @@
? 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"/>
Expand Down
6 changes: 4 additions & 2 deletions BTCPayServer/Views/UIAccount/Login.cshtml
@@ -1,8 +1,10 @@
@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">
Expand Down
@@ -1,9 +1,11 @@
@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
Expand Down
4 changes: 3 additions & 1 deletion BTCPayServer/Views/UIInvoice/Checkout.cshtml
@@ -1,9 +1,11 @@
@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>
Expand Down
11 changes: 6 additions & 5 deletions BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml
Expand Up @@ -5,12 +5,13 @@
@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
Expand Down Expand Up @@ -49,7 +50,7 @@
<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)
{
Expand Down
4 changes: 3 additions & 1 deletion BTCPayServer/Views/UIManage/APIKeys.cshtml
@@ -1,8 +1,10 @@
@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">
Expand Down
12 changes: 6 additions & 6 deletions BTCPayServer/Views/UIPaymentRequest/ViewPaymentRequest.cshtml
Expand Up @@ -3,9 +3,11 @@
@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())
Expand Down Expand Up @@ -68,7 +70,7 @@
<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>
Expand Down Expand Up @@ -204,9 +206,7 @@
<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
{
Expand Down
2 changes: 2 additions & 0 deletions BTCPayServer/Views/UIPullPayment/ViewPullPayment.cshtml
Expand Up @@ -4,9 +4,11 @@
@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)
{
Expand Down
6 changes: 4 additions & 2 deletions BTCPayServer/Views/UIStores/ImportWallet/Scan.cshtml
@@ -1,7 +1,9 @@
@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 {
Expand Down
8 changes: 5 additions & 3 deletions BTCPayServer/Views/UIStores/WalletSettings.cshtml
Expand Up @@ -2,11 +2,13 @@
@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 {
Expand Down
12 changes: 7 additions & 5 deletions BTCPayServer/Views/UIWallets/WalletPSBT.cshtml
@@ -1,11 +1,13 @@
@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 {
Expand Down
6 changes: 4 additions & 2 deletions BTCPayServer/Views/UIWallets/WalletPSBTDecoded.cshtml
@@ -1,14 +1,16 @@
@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 {
Expand Down

0 comments on commit 7b5ce8f

Please sign in to comment.