Compare commits

...

17 Commits

Author SHA1 Message Date
858da35539 Bump NBitcoin 2021-10-12 15:43:17 +09:00
ed52a1012a bump nbx 2021-10-06 19:26:33 +09:00
8ee0a97e9c Replace pruning wallet by wipe transactions 2021-10-06 19:19:01 +09:00
02195ed096 bump 2021-09-26 14:32:16 +09:00
562b96cf1f Update Changelog 2021-09-26 14:22:08 +09:00
f44b54bc59 Sanitize UrlRoot in PayButton 2021-09-26 14:06:52 +09:00
1889dec567 Fix pay button CSP issue when using modal (#2872)
* Fix pay button CSP issue when using modal

Fixes #2864.

* Use event handler, refactor csp tags

* Fix script indentation

* Fix onsubmit event handler integration

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2021-09-26 14:06:49 +09:00
aa953ad6a6 bump redoc 2021-09-26 14:05:48 +09:00
6a7d434c89 Make CSP more specific for docs 2021-09-26 14:05:48 +09:00
cc812f96c9 Reset "all stores" values to default 2021-09-26 14:03:02 +09:00
6a4cb0e95c Don't adjust store mode if user is performing an action 2021-09-26 14:02:48 +09:00
4c7149de95 Adjust view 2021-09-26 14:02:36 +09:00
532e847cdd [WIP] Fix issues with Authorization Request page
closes #2858
2021-09-26 14:02:17 +09:00
3e3d4aea03 Fix documentation page broken by CSP 2021-09-26 14:01:54 +09:00
55bfecd4ed Attempt cover scenarios of switching back to Bitcoin only after taint (#2881) 2021-09-26 14:01:12 +09:00
2e4d1f6d37 Should not be able to activate a payment method on an invoice which is not new 2021-09-26 14:01:01 +09:00
a3338b6f80 Fix infinite loop happening if payment method unavailable with on invoices with lazy activation (#2914) 2021-09-26 14:00:45 +09:00
22 changed files with 222 additions and 82 deletions

View File

@ -27,7 +27,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NBitcoin" Version="6.0.8" />
<PackageReference Include="NBitcoin" Version="6.0.12" />
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

View File

@ -4,7 +4,7 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NBXplorer.Client" Version="4.1.0" />
<PackageReference Include="NBXplorer.Client" Version="4.1.2" />
</ItemGroup>
<ItemGroup Condition="'$(Altcoins)' != 'true'">
<Compile Remove="Altcoins\**\*.cs"></Compile>

View File

@ -505,9 +505,11 @@ namespace BTCPayServer.Controllers
var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
if (!paymentMethodDetails.Activated)
{
await _InvoiceRepository.ActivateInvoicePaymentMethod(_EventAggregator, _NetworkProvider,
_paymentMethodHandlerDictionary, store, invoice, paymentMethod.GetId());
return await GetInvoiceModel(invoiceId, paymentMethodId, lang);
if (await _InvoiceRepository.ActivateInvoicePaymentMethod(_EventAggregator, _NetworkProvider,
_paymentMethodHandlerDictionary, store, invoice, paymentMethod.GetId()))
{
return await GetInvoiceModel(invoiceId, paymentMethodId, lang);
}
}
var dto = invoice.EntityToDTO();
var storeBlob = store.GetStoreBlob();

View File

@ -195,7 +195,25 @@ namespace BTCPayServer.Controllers
private void AdjustVMForAuthorization(AuthorizeApiKeysViewModel vm)
{
var parsedPermissions = Permission.ToPermissions(vm.Permissions?.Split(';')??Array.Empty<string>()).GroupBy(permission => permission.Policy);
var permissions = vm.Permissions?.Split(';') ?? Array.Empty<string>();
var permissionsWithStoreIDs = new List<string>();
/**
* Go over each permission and associated store IDs and
* join them so that permission for a specific store is parsed correctly
*/
for (var i = 0; i < permissions.Length; i++) {
var currPerm = permissions[i];
var storeIds = vm.PermissionValues[i].SpecificStores.ToArray();
if (storeIds.Length > 0) {
for (var x = 0; x < storeIds.Length; x++) {
permissionsWithStoreIDs.Add($"{currPerm}:{storeIds[x]}");
}
} else {
permissionsWithStoreIDs.Add(currPerm);
}
}
var parsedPermissions = Permission.ToPermissions(permissionsWithStoreIDs.ToArray()).GroupBy(permission => permission.Policy);
for (var index = vm.PermissionValues.Count - 1; index >= 0; index--)
{
@ -210,6 +228,14 @@ namespace BTCPayServer.Controllers
}
else if (wanted?.Any() ?? false)
{
var commandParts = vm.Command?.Split(':', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
var command = commandParts.Length > 1 ? commandParts[1] : null;
var isPerformingAnAction = command == "change-store-mode" || command == "add-store";
// Don't want to accidentally change mode for the user if they are explicitly performing some action
if (isPerformingAnAction) {
continue;
}
if (vm.SelectiveStores && Policies.IsStorePolicy(permissionValue.Permission) &&
wanted.Any(permission => !string.IsNullOrEmpty(permission.Scope)))
{
@ -358,6 +384,12 @@ namespace BTCPayServer.Controllers
permissionValueItem.StoreMode = permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific
? AddApiKeyViewModel.ApiKeyStoreMode.AllStores
: AddApiKeyViewModel.ApiKeyStoreMode.Specific;
// Reset values for "all stores" option to their original values
if (permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
{
permissionValueItem.SpecificStores = new List<string>();
permissionValueItem.Value = true;
}
if (permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific &&
!permissionValueItem.SpecificStores.Any() && viewModel.Stores.Any())

View File

@ -1154,20 +1154,19 @@ namespace BTCPayServer.Controllers
TempData[WellKnownTempData.SuccessMessage] = "Wallet settings updated";
return RedirectToAction(nameof(WalletSettings));
}
else if (command == "prune")
else if (command == "clear")
{
var result = await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode)
.PruneAsync(derivationScheme.AccountDerivation, new PruneRequest(), cancellationToken);
if (result.TotalPruned == 0)
if (Version.TryParse(_dashboard.Get(walletId.CryptoCode)?.Status?.Version ?? "0.0.0.0", out var v) &&
v < new Version(2, 2, 4))
{
TempData[WellKnownTempData.SuccessMessage] = $"The wallet is already pruned";
TempData[WellKnownTempData.ErrorMessage] = $"This version of NBXplorer doesn't support this operation, please upgrade to 2.2.4 or above";
}
else
{
TempData[WellKnownTempData.SuccessMessage] =
$"The wallet has been successfully pruned ({result.TotalPruned} transactions have been removed from the history)";
await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode)
.WipeAsync(derivationScheme.AccountDerivation, cancellationToken);
TempData[WellKnownTempData.SuccessMessage] = $"The transactions have been wiped out, to restore your balance, rescan the wallet.";
}
return RedirectToAction(nameof(WalletSettings));
}
else if (command == "view-seed" && await CanUseHotWallet())

View File

@ -52,7 +52,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
public bool CanHandle(PaymentMethodId paymentMethod)
{
return paymentMethod.PaymentType == BitcoinPaymentType.Instance &&
return paymentMethod?.PaymentType == BitcoinPaymentType.Instance &&
_btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethod.CryptoCode)?.ReadonlyWallet is false;
}
@ -89,6 +89,10 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
if (payout?.Proof is null)
return null;
var paymentMethodId = payout.GetPaymentMethodId();
if (paymentMethodId is null)
{
return null;
}
var raw = JObject.Parse(Encoding.UTF8.GetString(payout.Proof));
if (raw.TryGetValue("proofType", StringComparison.InvariantCultureIgnoreCase, out var proofType) &&
proofType.Value<string>() == ManualPayoutProof.Type)
@ -161,7 +165,9 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
.Include(p => p.PullPaymentData.StoreData)
.Where(p => payoutIds.Contains(p.Id))
.Where(p => p.PullPaymentData.StoreId == storeId && !p.PullPaymentData.Archived && p.State == PayoutState.AwaitingPayment)
.ToListAsync()).Where(data => CanHandle(PaymentMethodId.Parse(data.PaymentMethodId)))
.ToListAsync()).Where(data =>
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
CanHandle(paymentMethodId))
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple=> tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == false);
foreach (var valueTuple in payouts)
{
@ -185,7 +191,9 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
.Include(p => p.PullPaymentData.StoreData)
.Where(p => payoutIds.Contains(p.Id))
.Where(p => p.PullPaymentData.StoreId == storeId && !p.PullPaymentData.Archived && p.State == PayoutState.AwaitingPayment)
.ToListAsync()).Where(data => CanHandle(PaymentMethodId.Parse(data.PaymentMethodId)))
.ToListAsync()).Where(data =>
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
CanHandle(paymentMethodId))
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple=> tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == true);
foreach (var valueTuple in payouts)
{

View File

@ -28,7 +28,7 @@ namespace BTCPayServer.Data
public static PaymentMethodId GetPaymentMethodId(this PayoutData data)
{
return PaymentMethodId.Parse(data.PaymentMethodId);
return PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId)? paymentMethodId : null;
}
public static PayoutBlob GetBlob(this PayoutData data, BTCPayNetworkJsonSerializerSettings serializers)
{

View File

@ -171,7 +171,8 @@ namespace BTCPayServer.Data
#pragma warning disable CS0618 // Type or member is obsolete
if (ExcludedPaymentMethods == null || ExcludedPaymentMethods.Length == 0)
return PaymentFilter.Never();
return PaymentFilter.Any(ExcludedPaymentMethods.Select(p => PaymentFilter.WhereIs(PaymentMethodId.Parse(p))).ToArray());
return PaymentFilter.Any(ExcludedPaymentMethods.ParsePaymentMethodIds().Select(PaymentFilter.WhereIs).ToArray());
#pragma warning restore CS0618 // Type or member is obsolete
}

View File

@ -25,7 +25,8 @@ namespace BTCPayServer.Data
public static PaymentMethodId[] GetEnabledPaymentIds(this StoreData storeData, BTCPayNetworkProvider networks)
{
var excludeFilter = storeData.GetStoreBlob().GetExcludedPaymentMethods();
var paymentMethodIds = storeData.GetSupportedPaymentMethods(networks).Select(p => p.PaymentId)
var paymentMethodIds = storeData.GetSupportedPaymentMethods(networks)
.Select(p => p.PaymentId)
.Where(a => !excludeFilter.Match(a))
.OrderByDescending(a => a.CryptoCode == "BTC")
.ThenBy(a => a.CryptoCode)

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@ -482,6 +483,15 @@ namespace BTCPayServer
"Supported chains: " + String.Join(',', supportedChains.ToArray()));
return result;
}
public static PaymentMethodId[] ParsePaymentMethodIds(this string[] paymentMethods)
{
return paymentMethods.Select(s =>
{
PaymentMethodId.TryParse(s, out var parsed);
return parsed;
}).Where(id => id != null).ToArray();
}
public static DataDirectories Configure(this DataDirectories dataDirectories, IConfiguration configuration)
{

View File

@ -286,8 +286,13 @@ namespace BTCPayServer.HostedServices
req.Completion.SetResult(PayoutApproval.Result.OldRevision);
return;
}
if (!PaymentMethodId.TryParse(payout.PaymentMethodId, out var paymentMethod))
{
req.Completion.SetResult(PayoutApproval.Result.NotFound);
return;
}
payout.State = PayoutState.AwaitingPayment;
var paymentMethod = PaymentMethodId.Parse(payout.PaymentMethodId);
if (paymentMethod.CryptoCode == payout.PullPaymentData.GetBlob().Currency)
req.Rate = 1.0m;
var cryptoAmount = payoutBlob.Amount / req.Rate;

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NBitcoin.Crypto;
namespace BTCPayServer.Security
{
@ -69,6 +70,35 @@ namespace BTCPayServer.Security
}
readonly HashSet<ConsentSecurityPolicy> _Policies = new HashSet<ConsentSecurityPolicy>();
/// <summary>
/// Allow a specific script as event handler
/// </summary>
/// <param name="script"></param>
public void AllowUnsafeHashes(string script)
{
if (script is null)
throw new ArgumentNullException(nameof(script));
var sha = GetSha256(script);
Add("script-src", $"'unsafe-hashes'");
Add("script-src", $"'sha256-{sha}'");
}
/// <summary>
/// Allow the injection of script tag with the following script
/// </summary>
/// <param name="script"></param>
public void AllowInline(string script)
{
if (script is null)
throw new ArgumentNullException(nameof(script));
var sha = GetSha256(script);
Add("script-src", $"'sha256-{sha}'");
}
static string GetSha256(string script)
{
return Convert.ToBase64String(Hashes.SHA256(Encoding.UTF8.GetBytes(script.Replace("\r\n", "\n", StringComparison.Ordinal))));
}
public void Add(string name, string value)
{
Add(new ConsentSecurityPolicy(name, value));

View File

@ -289,7 +289,10 @@ namespace BTCPayServer.Services.Invoices
JObject strategies = JObject.Parse(DerivationStrategies);
foreach (var strat in strategies.Properties())
{
var paymentMethodId = PaymentMethodId.Parse(strat.Name);
if (!PaymentMethodId.TryParse(strat.Name, out var paymentMethodId))
{
continue;
}
var network = Networks.GetNetwork<BTCPayNetworkBase>(paymentMethodId.CryptoCode);
if (network != null)
{

View File

@ -10,11 +10,13 @@ namespace BTCPayServer.Services.Invoices
{
public static class InvoiceExtensions
{
public static async Task ActivateInvoicePaymentMethod(this InvoiceRepository invoiceRepository,
public static async Task<bool> ActivateInvoicePaymentMethod(this InvoiceRepository invoiceRepository,
EventAggregator eventAggregator, BTCPayNetworkProvider btcPayNetworkProvider, PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
StoreData store,InvoiceEntity invoice, PaymentMethodId paymentMethodId)
{
if (invoice.GetInvoiceState().Status != InvoiceStatusLegacy.New)
return false;
bool success = false;
var eligibleMethodToActivate = invoice.GetPaymentMethod(paymentMethodId);
if (!eligibleMethodToActivate.GetPaymentMethodDetails().Activated)
{
@ -34,6 +36,8 @@ namespace BTCPayServer.Services.Invoices
eligibleMethodToActivate.SetPaymentMethodDetails(newDetails);
await invoiceRepository.UpdateInvoicePaymentMethod(invoice.Id, eligibleMethodToActivate);
eventAggregator.Publish(new InvoicePaymentMethodActivated(paymentMethodId, invoice));
eventAggregator.Publish(new InvoiceNeedUpdateEvent(invoice.Id));
success = true;
}
catch (PaymentMethodUnavailableException ex)
{
@ -45,8 +49,8 @@ namespace BTCPayServer.Services.Invoices
}
await invoiceRepository.AddInvoiceLogs(invoice.Id, logs);
eventAggregator.Publish(new InvoiceNeedUpdateEvent(invoice.Id));
}
return success;
}
}

View File

@ -72,7 +72,6 @@ namespace BTCPayServer.TagHelpers
[HtmlTargetElement(Attributes = "onkeypress")]
[HtmlTargetElement(Attributes = "onchange")]
[HtmlTargetElement(Attributes = "onsubmit")]
[HtmlTargetElement(Attributes = "href")]
public class CSPEventTagHelper : TagHelper
{
public const string EventNames = "onclick,onkeypress,onchange,onsubmit";
@ -91,29 +90,55 @@ namespace BTCPayServer.TagHelpers
var n = attr.Name.ToLowerInvariant();
if (EventSet.Contains(n))
{
Allow(attr.Value.ToString());
}
else if (n == "href")
{
var v = attr.Value.ToString();
if (v.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
{
Allow(v);
}
_csp.AllowUnsafeHashes(attr.Value.ToString());
}
}
}
}
private void Allow(string v)
/// <summary>
/// Add sha256- to allow inline event handlers in CSP
/// </summary>
[HtmlTargetElement("template", Attributes = "csp-allow")]
public class CSPTemplate : TagHelper
{
private readonly ContentSecurityPolicies _csp;
public CSPTemplate(ContentSecurityPolicies csp)
{
var sha = GetSha256(v);
_csp.Add("script-src", $"'unsafe-hashes'");
_csp.Add("script-src", $"'sha256-{sha}'");
_csp = csp;
}
public static string GetSha256(string script)
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
return Convert.ToBase64String(Hashes.SHA256(Encoding.UTF8.GetBytes(script.Replace("\r\n", "\n", StringComparison.Ordinal))));
output.Attributes.RemoveAll("csp-allow");
var childContent = await output.GetChildContentAsync();
var content = childContent.GetContent();
_csp.AllowInline(content);
}
}
/// <summary>
/// Add sha256- to allow inline event handlers in a:href=javascript:
/// </summary>
[HtmlTargetElement("a", Attributes = "csp-allow")]
public class CSPA : TagHelper
{
private readonly ContentSecurityPolicies _csp;
public CSPA(ContentSecurityPolicies csp)
{
_csp = csp;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.RemoveAll("csp-allow");
if (output.Attributes.TryGetAttribute("href", out var attr))
{
var v = attr.Value.ToString();
if (v.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
{
_csp.AllowInline(v);
}
}
}
}
}

View File

@ -1,5 +1,8 @@
@inject BTCPayServer.Security.ContentSecurityPolicies csp
@{
Layout = null;
csp.Add("script-src", "https://cdn.jsdelivr.net");
csp.Add("worker-src", "blob:");
}
<!DOCTYPE html>
<html>
@ -25,6 +28,6 @@
@*Ignore this, this is for making the test ClickOnAllSideMenus happy*@
<div class="navbar-brand" style="visibility:collapse;"></div>
<redoc spec-url="@Url.ActionLink("Swagger")"></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.45/bundles/redoc.standalone.js" integrity="sha384-RC31+q3tyqdcilXYaU++ii/FAByqeZ+sjKUHMJ8hMzIY5k4kzNqi4Ett88EZ/4lq" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.54/bundles/redoc.standalone.js" integrity="sha384-pxWFJkxrlfignEDb+sJ8XrdnJQ+V2bsiRqgPnfmOk1i3KKSubbydbolVZJeKisNY" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -64,7 +64,7 @@
@if (Model.PermissionValues[i].StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
{
<div class="list-group-item form-group">
<div class="form-check">
<div class="form-check d-flex">
@if (Model.Strict || Model.PermissionValues[i].Forbidden)
{
<input id="@Model.PermissionValues[i].Permission" type="hidden" asp-for="PermissionValues[i].Value"/>
@ -74,10 +74,10 @@
{
<input id="@Model.PermissionValues[i].Permission" type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-input"/>
}
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label">@Model.PermissionValues[i].Title</label>
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label m-0 me-4 ms-2">@Model.PermissionValues[i].Title</label>
@if (Model.SelectiveStores)
{
<button type="submit" class="btn btn-link" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">select specific stores...</button>
<button type="submit" class="btn btn-link p-0 me-4" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">select specific stores...</button>
}
@if (Model.PermissionValues[i].Forbidden)

View File

@ -1,7 +1,9 @@
@inject BTCPayServer.Security.ContentSecurityPolicies csp
@model PayButtonViewModel
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().StoreName);
csp.AllowUnsafeHashes("onBTCPayFormSubmit(event);return false");
}
@section PageHeadContent {
@ -14,6 +16,24 @@
<script src="~/vendor/vuejs-vee-validate/vee-validate.js" asp-append-version="true"></script>
<script src="~/vendor/clipboard.js/clipboard.js" asp-append-version="true"></script>
<script src="~/paybutton/paybutton.js" asp-append-version="true"></script>
<template id="template-get-scripts" csp-allow>
if (!window.btcpay) {
var script = document.createElement('script');
script.src=@(Safe.Json(Model.UrlRoot + "modal/btcpay.js"));
document.getElementsByTagName('head')[0].append(script);
}
function onBTCPayFormSubmit(event) {
event.preventDefault();
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200 && this.responseText) {
window.btcpay.showInvoice(JSON.parse(this.responseText).invoiceId);
}
};
xhttp.open('POST', event.target.getAttribute('action'), true);
xhttp.send(new FormData(event.target));
}
</template>
<script>
var srvModel = @Safe.Json(Model);

View File

@ -1,4 +1,4 @@
@using Newtonsoft.Json
@using Newtonsoft.Json
@using System.Text
@using NBitcoin.DataEncoders
@model WalletSettingsViewModel
@ -70,7 +70,7 @@
Other actions...
</button>
<div class="dropdown-menu" aria-labelledby="OtherActionsDropdownToggle">
<button name="command" type="submit" class="dropdown-item" value="prune">Prune old transactions from history</button>
<button name="command" type="submit" class="dropdown-item" value="clear">Clear all transactions from history</button>
@if (Model.NBXSeedAvailable)
{
<button name="command" type="submit" class="dropdown-item" value="view-seed">View seed</button>

View File

@ -42,33 +42,11 @@ function getStyles (styles) {
}
function getScripts(srvModel) {
return ""+
"<script>" +
"if(!window.btcpay){ " +
" var head = document.getElementsByTagName('head')[0];" +
" var script = document.createElement('script');" +
" script.src='"+esc(srvModel.urlRoot)+"modal/btcpay.js';" +
" script.type = 'text/javascript';" +
" head.append(script);" +
"}" +
"function onBTCPayFormSubmit(event){" +
" var xhttp = new XMLHttpRequest();" +
" xhttp.onreadystatechange = function() {" +
" if (this.readyState == 4 && this.status == 200) {" +
" if(this.status == 200 && this.responseText){" +
" var response = JSON.parse(this.responseText);" +
" window.btcpay.showInvoice(response.invoiceId);" +
" }" +
" }" +
" };" +
" xhttp.open(\"POST\", event.target.getAttribute('action'), true);" +
" xhttp.send(new FormData( event.target ));" +
"}" +
"</script>";
if (!srvModel.useModal) return ''
const template = document.getElementById('template-get-scripts')
return template.innerHTML.replace(/&amp;/g, '&')
}
function inputChanges(event, buttonSize) {
if (buttonSize !== null && buttonSize !== undefined) {
srvModel.buttonSize = buttonSize;
@ -115,12 +93,10 @@ function inputChanges(event, buttonSize) {
}
var html =
//Scripts
(srvModel.useModal? getScripts(srvModel) :"") +
// Styles
getStyles('template-paybutton-styles') + (isSlider ? getStyles('template-slider-styles') : '') +
// Form
'<form method="POST" '+ ( srvModel.useModal? ' onsubmit="onBTCPayFormSubmit(event);return false" ' : '' )+' action="' + esc(srvModel.urlRoot) + actionUrl + '" class="btcpay-form btcpay-form--' + (srvModel.fitButtonInline ? 'inline' : 'block') +'">\n' +
'<form method="POST"' + (srvModel.useModal ? ' onsubmit="onBTCPayFormSubmit(event);return false"' : '') + ' action="' + esc(srvModel.urlRoot) + actionUrl + '" class="btcpay-form btcpay-form--' + (srvModel.fitButtonInline ? 'inline' : 'block') +'">\n' +
addInput("storeId", srvModel.storeId);
if(app){
@ -144,7 +120,6 @@ function inputChanges(event, buttonSize) {
if (srvModel.checkoutQueryString) html += addInput("checkoutQueryString", srvModel.checkoutQueryString);
}
// Fixed amount: Add price and currency as hidden inputs
if (isFixedAmount) {
@ -192,10 +167,21 @@ function inputChanges(event, buttonSize) {
'</button>'
}
html += '</form>';
// Scripts
var scripts = getScripts(srvModel);
var code = html + (scripts ? `\n<script>\n ${scripts.trim()}\n</script>` : '')
$("#mainCode").text(html).html();
$("#preview").html(html);
var form = document.querySelector("#preview form");
$("#mainCode").text(code).html();
var preview = document.getElementById('preview');
preview.innerHTML = html;
if (scripts) {
// script needs to be inserted as node, otherwise it won't get executed
var script = document.createElement('script');
script.innerHTML = scripts
preview.appendChild(script)
}
var form = preview.querySelector("form");
var url = new URL(form.getAttribute("action"));
var formData = new FormData(form);
formData.forEach((value, key) => {

View File

@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>1.2.3</Version>
<Version>1.2.4</Version>
</PropertyGroup>
</Project>

View File

@ -1,12 +1,23 @@
# Changelog
## 1.2.4
Minor bug fixes release, update recommended for shared hosting.
### Bug fixes
* If `Only enable the payment method after user explicitly chooses it` is enabled for a store and a payment method is unavailable, the server could become unresponsive. @NicolasDorier
* Authorize API key page was broken when trying to select specific stores (#2858) @ubolator
* The /docs page was broken in 1.2.3 due to CSP @NicolasDorier
* Fixing crashes happening when someone migrate from BTCPay Server altcoins edition back to bitcoin only @Kukks
## 1.2.3
This release fixes three XSS vulnerabilities. Those vulnerabilities only impacts shared BTCPay instances.
Special thanks to Ajmal "@b3ef" Aboobacker and Abdul "@b1nslashsh" muhaimin for finding them who contacted us through @huntrdev.
See [1](https://huntr.dev/bounties/ffabdac8-7280-4806-b70c-9b0d1aafbb6e/), [2](https://www.huntr.dev/bounties/32e30ecf-31fa-45f6-8552-47250ef0e613/) and [3](https://huntr.dev/bounties/0fcdee5f-1f07-47ce-b650-ea8b4a7d35d8/).
# Bug fixes:
### Bug fixes:
* Use CSP to prevent future XSS vulnerabilities. (#2856, #2863) @NicolasDorier
* Fix XSS vulnerabilities in summernote, the rich text editor (#2859) @dennisreimann