Use Safe.Raw and Safe.Json instead of Html.Raw and the JsonHelper, move sanitization at the View level (#960)

This commit is contained in:
Nicolas Dorier 2019-08-10 14:05:11 +09:00 committed by GitHub
parent 6b355cbe1b
commit be5597085b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 132 additions and 82 deletions

View file

@ -34,7 +34,7 @@
<PackageReference Include="BuildBundlerMinifier" Version="2.9.406" /> <PackageReference Include="BuildBundlerMinifier" Version="2.9.406" />
<PackageReference Include="BundlerMinifier.Core" Version="2.9.406" /> <PackageReference Include="BundlerMinifier.Core" Version="2.9.406" />
<PackageReference Include="BundlerMinifier.TagHelpers" Version="2.9.406" /> <PackageReference Include="BundlerMinifier.TagHelpers" Version="2.9.406" />
<PackageReference Include="HtmlSanitizer" Version="4.0.207" /> <PackageReference Include="HtmlSanitizer" Version="4.0.217" />
<PackageReference Include="LedgerWallet" Version="2.0.0.3" /> <PackageReference Include="LedgerWallet" Version="2.0.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
@ -47,7 +47,7 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" /> <PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" />
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" /> <PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
<PackageReference Include="NicolasDorier.RateLimits" Version="1.0.0.7" /> <PackageReference Include="NicolasDorier.RateLimits" Version="1.0.0.9" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" /> <PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" />
<PackageReference Include="OpenIddict" Version="2.0.0" /> <PackageReference Include="OpenIddict" Version="2.0.0" />

View file

@ -183,7 +183,7 @@ namespace BTCPayServer.Controllers
{ {
Version = u2fChallenge[0].version, Version = u2fChallenge[0].version,
Challenge = u2fChallenge[0].challenge, Challenge = u2fChallenge[0].challenge,
Challenges = JsonConvert.SerializeObject(u2fChallenge), Challenges = u2fChallenge,
AppId = u2fChallenge[0].appId, AppId = u2fChallenge[0].appId,
UserId = user.Id, UserId = user.Id,
RememberMe = rememberMe RememberMe = rememberMe

View file

@ -132,7 +132,7 @@ namespace BTCPayServer.Controllers
EnforceTargetAmount = vm.EnforceTargetAmount, EnforceTargetAmount = vm.EnforceTargetAmount,
StartDate = vm.StartDate?.ToUniversalTime(), StartDate = vm.StartDate?.ToUniversalTime(),
TargetCurrency = vm.TargetCurrency, TargetCurrency = vm.TargetCurrency,
Description = _htmlSanitizer.Sanitize( vm.Description), Description = vm.Description,
EndDate = vm.EndDate?.ToUniversalTime(), EndDate = vm.EndDate?.ToUniversalTime(),
TargetAmount = vm.TargetAmount, TargetAmount = vm.TargetAmount,
CustomCSSLink = vm.CustomCSSLink, CustomCSSLink = vm.CustomCSSLink,

View file

@ -30,7 +30,6 @@ namespace BTCPayServer.Controllers
EventAggregator eventAggregator, EventAggregator eventAggregator,
BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider networkProvider,
CurrencyNameTable currencies, CurrencyNameTable currencies,
HtmlSanitizer htmlSanitizer,
EmailSenderFactory emailSenderFactory, EmailSenderFactory emailSenderFactory,
AppService AppService) AppService AppService)
{ {
@ -39,7 +38,6 @@ namespace BTCPayServer.Controllers
_EventAggregator = eventAggregator; _EventAggregator = eventAggregator;
_NetworkProvider = networkProvider; _NetworkProvider = networkProvider;
_currencies = currencies; _currencies = currencies;
_htmlSanitizer = htmlSanitizer;
_emailSenderFactory = emailSenderFactory; _emailSenderFactory = emailSenderFactory;
_AppService = AppService; _AppService = AppService;
} }
@ -49,7 +47,6 @@ namespace BTCPayServer.Controllers
private readonly EventAggregator _EventAggregator; private readonly EventAggregator _EventAggregator;
private BTCPayNetworkProvider _NetworkProvider; private BTCPayNetworkProvider _NetworkProvider;
private readonly CurrencyNameTable _currencies; private readonly CurrencyNameTable _currencies;
private readonly HtmlSanitizer _htmlSanitizer;
private readonly EmailSenderFactory _emailSenderFactory; private readonly EmailSenderFactory _emailSenderFactory;
private AppService _AppService; private AppService _AppService;

View file

@ -3,6 +3,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Models;
using BTCPayServer.Models.ManageViewModels; using BTCPayServer.Models.ManageViewModels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -11,6 +12,7 @@ namespace BTCPayServer.Controllers
{ {
public partial class ManageController public partial class ManageController
{ {
private const string RecoveryCodesKey = nameof(RecoveryCodesKey);
private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
[HttpGet] [HttpGet]
@ -80,18 +82,8 @@ namespace BTCPayServer.Controllers
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
} }
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); var model = new EnableAuthenticatorViewModel();
if (string.IsNullOrEmpty(unformattedKey)) await LoadSharedKeyAndQrCodeUriAsync(user, model);
{
await _userManager.ResetAuthenticatorKeyAsync(user);
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
}
var model = new EnableAuthenticatorViewModel
{
SharedKey = FormatKey(unformattedKey),
AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey)
};
return View(model); return View(model);
} }
@ -100,32 +92,36 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> EnableAuthenticator(EnableAuthenticatorViewModel model) public async Task<IActionResult> EnableAuthenticator(EnableAuthenticatorViewModel model)
{ {
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.GetUserAsync(User); var user = await _userManager.GetUserAsync(User);
if (user == null) if (user == null)
{ {
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
} }
if (!ModelState.IsValid)
{
await LoadSharedKeyAndQrCodeUriAsync(user, model);
return View(model);
}
// Strip spaces and hypens // Strip spaces and hypens
var verificationCode = model.Code.Replace(" ", string.Empty, StringComparison.InvariantCulture) var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
.Replace("-", string.Empty, StringComparison.InvariantCulture);
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
if (!is2faTokenValid) if (!is2faTokenValid)
{ {
ModelState.AddModelError(nameof(model.Code), "Verification code is invalid."); ModelState.AddModelError("Code", "Verification code is invalid.");
await LoadSharedKeyAndQrCodeUriAsync(user, model);
return View(model); return View(model);
} }
await _userManager.SetTwoFactorEnabledAsync(user, true); await _userManager.SetTwoFactorEnabledAsync(user, true);
_logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id); _logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id);
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
TempData[RecoveryCodesKey] = recoveryCodes.ToArray();
return RedirectToAction(nameof(GenerateRecoveryCodes)); return RedirectToAction(nameof(GenerateRecoveryCodes));
} }
@ -153,7 +149,20 @@ namespace BTCPayServer.Controllers
} }
[HttpGet] [HttpGet]
public async Task<IActionResult> GenerateRecoveryCodes() public IActionResult GenerateRecoveryCodes()
{
var recoveryCodes = (string[])TempData[RecoveryCodesKey];
if (recoveryCodes == null)
{
return RedirectToAction(nameof(TwoFactorAuthentication));
}
var model = new GenerateRecoveryCodesViewModel {RecoveryCodes = recoveryCodes};
return View(model);
}
[HttpGet]
public async Task<IActionResult> GenerateRecoveryCodesWarning()
{ {
var user = await _userManager.GetUserAsync(User); var user = await _userManager.GetUserAsync(User);
if (user == null) if (user == null)
@ -163,16 +172,10 @@ namespace BTCPayServer.Controllers
if (!user.TwoFactorEnabled) if (!user.TwoFactorEnabled)
{ {
throw new ApplicationException( throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' because they do not have 2FA enabled.");
$"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled.");
} }
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); return View(nameof(GenerateRecoveryCodesWarning));
var model = new GenerateRecoveryCodesViewModel {RecoveryCodes = recoveryCodes.ToArray()};
_logger.LogInformation("User with ID {UserId} has generated new 2FA recovery codes.", user.Id);
return View(model);
} }
private string GenerateQrCodeUri(string email, string unformattedKey) private string GenerateQrCodeUri(string email, string unformattedKey)
@ -201,5 +204,19 @@ namespace BTCPayServer.Controllers
return result.ToString().ToLowerInvariant(); return result.ToString().ToLowerInvariant();
} }
private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user, EnableAuthenticatorViewModel model)
{
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
if (string.IsNullOrEmpty(unformattedKey))
{
await _userManager.ResetAuthenticatorKeyAsync(user);
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
}
model.SharedKey = FormatKey(unformattedKey);
model.AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey);
}
} }
} }

View file

@ -41,7 +41,6 @@ namespace BTCPayServer.Controllers
private readonly PaymentRequestService _PaymentRequestService; private readonly PaymentRequestService _PaymentRequestService;
private readonly EventAggregator _EventAggregator; private readonly EventAggregator _EventAggregator;
private readonly CurrencyNameTable _Currencies; private readonly CurrencyNameTable _Currencies;
private readonly HtmlSanitizer _htmlSanitizer;
private readonly InvoiceRepository _InvoiceRepository; private readonly InvoiceRepository _InvoiceRepository;
public PaymentRequestController( public PaymentRequestController(
@ -52,7 +51,6 @@ namespace BTCPayServer.Controllers
PaymentRequestService paymentRequestService, PaymentRequestService paymentRequestService,
EventAggregator eventAggregator, EventAggregator eventAggregator,
CurrencyNameTable currencies, CurrencyNameTable currencies,
HtmlSanitizer htmlSanitizer,
InvoiceRepository invoiceRepository) InvoiceRepository invoiceRepository)
{ {
_InvoiceController = invoiceController; _InvoiceController = invoiceController;
@ -62,7 +60,6 @@ namespace BTCPayServer.Controllers
_PaymentRequestService = paymentRequestService; _PaymentRequestService = paymentRequestService;
_EventAggregator = eventAggregator; _EventAggregator = eventAggregator;
_Currencies = currencies; _Currencies = currencies;
_htmlSanitizer = htmlSanitizer;
_InvoiceRepository = invoiceRepository; _InvoiceRepository = invoiceRepository;
} }
@ -152,7 +149,7 @@ namespace BTCPayServer.Controllers
blob.Title = viewModel.Title; blob.Title = viewModel.Title;
blob.Email = viewModel.Email; blob.Email = viewModel.Email;
blob.Description = _htmlSanitizer.Sanitize(viewModel.Description); blob.Description = viewModel.Description;
blob.Amount = viewModel.Amount; blob.Amount = viewModel.Amount;
blob.ExpiryDate = viewModel.ExpiryDate?.ToUniversalTime(); blob.ExpiryDate = viewModel.ExpiryDate?.ToUniversalTime();
blob.Currency = viewModel.Currency; blob.Currency = viewModel.Currency;

View file

@ -126,6 +126,7 @@ namespace BTCPayServer.Hosting
}); });
services.TryAddSingleton<AppService>(); services.TryAddSingleton<AppService>();
services.TryAddTransient<Safe>();
services.TryAddSingleton<Ganss.XSS.HtmlSanitizer>(o => services.TryAddSingleton<Ganss.XSS.HtmlSanitizer>(o =>
{ {

View file

@ -4,6 +4,7 @@ using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace BTCPayServer.Models.ManageViewModels namespace BTCPayServer.Models.ManageViewModels
{ {
@ -15,9 +16,10 @@ namespace BTCPayServer.Models.ManageViewModels
[Display(Name = "Verification Code")] [Display(Name = "Verification Code")]
public string Code { get; set; } public string Code { get; set; }
[ReadOnly(true)] [BindNever]
public string SharedKey { get; set; } public string SharedKey { get; set; }
[BindNever]
public string AuthenticatorUri { get; set; } public string AuthenticatorUri { get; set; }
} }
} }

View file

@ -283,10 +283,10 @@ namespace BTCPayServer.Services.Apps
.Where(kv => kv.Value != null) .Where(kv => kv.Value != null)
.Select(c => new ViewPointOfSaleViewModel.Item() .Select(c => new ViewPointOfSaleViewModel.Item()
{ {
Description = _HtmlSanitizer.Sanitize(c.GetDetailString("description")), Description = c.GetDetailString("description"),
Id = c.Key, Id = c.Key,
Image = _HtmlSanitizer.Sanitize(c.GetDetailString("image")), Image = c.GetDetailString("image"),
Title = _HtmlSanitizer.Sanitize(c.GetDetailString("title") ?? c.Key), Title = c.GetDetailString("title") ?? c.Key,
Price = c.GetDetail("price") Price = c.GetDetail("price")
.Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice() .Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice()
{ {

View file

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ganss.XSS;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace BTCPayServer.Services
{
public class Safe
{
private readonly IHtmlHelper _htmlHelper;
private readonly IJsonHelper _jsonHelper;
private readonly HtmlSanitizer _htmlSanitizer;
public Safe(IHtmlHelper htmlHelper, IJsonHelper jsonHelper, HtmlSanitizer htmlSanitizer)
{
_htmlHelper = htmlHelper;
_jsonHelper = jsonHelper;
_htmlSanitizer = htmlSanitizer;
}
public IHtmlContent Raw(string value)
{
return _htmlHelper.Raw(_htmlSanitizer.Sanitize(value));
}
public IHtmlContent Json(object model)
{
return _htmlHelper.Raw(_jsonHelper.Serialize(model));
}
}
}

View file

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace BTCPayServer.Services.U2F.Models namespace BTCPayServer.Services.U2F.Models
@ -18,7 +19,7 @@ namespace BTCPayServer.Services.U2F.Models
public string DeviceResponse { get; set; } public string DeviceResponse { get; set; }
[Display(Name = "Challenges")] [Display(Name = "Challenges")]
public string Challenges { get; set; } public List<ServerChallenge> Challenges { get; set; }
[Display(Name = "Challenge")] [Display(Name = "Challenge")]
public string Challenge { get; set; } public string Challenge { get; set; }

View file

@ -37,9 +37,9 @@
}; };
setTimeout(function() { setTimeout(function() {
window.u2f.sign( window.u2f.sign(
"@Model.AppId", @Safe.Json(Model.AppId),
"@Model.Challenge", @Safe.Json(Model.Challenge),
@Html.Raw(@Model.Challenges), function (data) { @Safe.Json(Model.Challenges), function (data) {
if (data.errorCode) { if (data.errorCode) {
$("#error-response").text(errorMap[data.errorCode]); $("#error-response").text(errorMap[data.errorCode]);
return; return;

View file

@ -30,17 +30,17 @@
@item.Price.Value @item.Price.Value
if (item.Custom) if (item.Custom)
{ {
Html.Raw("or more"); Safe.Raw("or more");
} }
} }
else if (item.Custom) else if (item.Custom)
{ {
Html.Raw("Any amount"); Safe.Raw("Any amount");
} }
</span> </span>
</div> </div>
<p class="card-text overflow-hidden">@Html.Raw(item.Description)</p> <p class="card-text overflow-hidden">@Safe.Raw(item.Description)</p>
</div> </div>
@if (Model.ViewCrowdfundViewModel.PerkCount.ContainsKey(item.Id)) @if (Model.ViewCrowdfundViewModel.PerkCount.ContainsKey(item.Id))

View file

@ -131,7 +131,7 @@
<hr/> <hr/>
<div class="row"> <div class="row">
<div class="col-md-8 col-sm-12"> <div class="col-md-8 col-sm-12">
<div class="card-text overflow-hidden">@Html.Raw(Model.Description)</div> <div class="card-text overflow-hidden">@Safe.Raw(Model.Description)</div>
</div> </div>
<div class="col-md-4 col-sm-12"> <div class="col-md-4 col-sm-12">
<partial <partial

View file

@ -21,7 +21,7 @@
@if (!Context.Request.Query.ContainsKey("simple")) @if (!Context.Request.Query.ContainsKey("simple"))
{ {
<script type="text/javascript"> <script type="text/javascript">
var srvModel = @Html.Raw(Json.Serialize(Model)); var srvModel = @Safe.Json(Model);
</script> </script>
<bundle name="wwwroot/bundles/crowdfund-bundle-1.min.js"></bundle> <bundle name="wwwroot/bundles/crowdfund-bundle-1.min.js"></bundle>
<bundle name="wwwroot/bundles/crowdfund-bundle-2.min.js"></bundle> <bundle name="wwwroot/bundles/crowdfund-bundle-2.min.js"></bundle>
@ -33,7 +33,7 @@
@if (!string.IsNullOrEmpty(Model.EmbeddedCSS)) @if (!string.IsNullOrEmpty(Model.EmbeddedCSS))
{ {
<style> <style>
@Html.Raw(Model.EmbeddedCSS); @Safe.Raw(Model.EmbeddedCSS);
</style> </style>
} }

View file

@ -31,7 +31,7 @@
{ {
<link rel="stylesheet" href="~/cart/css/style.css"> <link rel="stylesheet" href="~/cart/css/style.css">
<script type="text/javascript"> <script type="text/javascript">
var srvModel = @Html.Raw(Json.Serialize(Model)); var srvModel = @Safe.Json(Model);
</script> </script>
<bundle name="wwwroot/bundles/cart-bundle.min.js" /> <bundle name="wwwroot/bundles/cart-bundle.min.js" />
} }

View file

@ -10,7 +10,7 @@
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<p>You need to pay <b>@Model.Amount</b> to <b>@Model.Address</b></p> <p>You need to pay <b>@Model.Amount</b> to <b>@Model.Address</b></p>
<div id="qrCode"></div> <div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.BitcoinUri)" style="margin-bottom:20px;"></div> <div id="qrCodeData" data-url="@Model.BitcoinUri" style="margin-bottom:20px;"></div>
<p> <p>
<a class="btn btn-primary" href="@Model.BitcoinUri"> <a class="btn btn-primary" href="@Model.BitcoinUri">
<span>Open in wallet</span> <span>Open in wallet</span>
@ -52,7 +52,7 @@
<script type="text/javascript"> <script type="text/javascript">
new QRCode(document.getElementById("qrCode"), new QRCode(document.getElementById("qrCode"),
{ {
text: "@Html.Raw(Model.BitcoinUri)", text: @Safe.Json(Model.BitcoinUri),
width: 200, width: 200,
height: 200, height: 200,
useSVG: true useSVG: true

View file

@ -19,7 +19,7 @@
<bundle name="wwwroot/bundles/checkout-bundle.min.css" /> <bundle name="wwwroot/bundles/checkout-bundle.min.css" />
<script type="text/javascript"> <script type="text/javascript">
var srvModel = @Html.Raw(Json.Serialize(Model)); var srvModel = @Safe.Json(Model);
</script> </script>
<bundle name="wwwroot/bundles/checkout-bundle.min.js" /> <bundle name="wwwroot/bundles/checkout-bundle.min.js" />
@ -114,8 +114,8 @@
</div> </div>
</invoice> </invoice>
<script type="text/javascript"> <script type="text/javascript">
var availableLanguages = @Html.Raw(Json.Serialize(langService.GetLanguages().Select((language) => language.Code)));; var availableLanguages = @Safe.Json(langService.GetLanguages().Select((language) => language.Code));;
var storeDefaultLang = "@Model.DefaultLang"; var storeDefaultLang = @Safe.Json(@Model.DefaultLang);
var fallbackLanguage = "en"; var fallbackLanguage = "en";
startingLanguage = computeStartingLanguage(); startingLanguage = computeStartingLanguage();
// initialization // initialization
@ -123,7 +123,7 @@
.use(window.i18nextXHRBackend) .use(window.i18nextXHRBackend)
.init({ .init({
backend: { backend: {
loadPath: '@(Model.RootPath)locales/{{lng}}.json' loadPath: @Safe.Json($"{Model.RootPath}locales/{{{{lng}}}}.json")
}, },
lng: startingLanguage, lng: startingLanguage,
fallbackLng: fallbackLanguage, fallbackLng: fallbackLanguage,

View file

@ -39,7 +39,7 @@
}; };
setTimeout(function() { setTimeout(function() {
var request = { "challenge": "@Model.Challenge", "version": "@Model.Version", "appId": "@Model.AppId" }; var request = { "challenge": @Safe.Json(Model.Challenge), "version": @Safe.Json(Model.Version), "appId": @Safe.Json(Model.AppId) };
var registerRequests = [{version: request.version, challenge: request.challenge}]; var registerRequests = [{version: request.version, challenge: request.challenge}];
u2f.register(request.appId, registerRequests, [], u2f.register(request.appId, registerRequests, [],
function(data) { function(data) {

View file

@ -20,7 +20,7 @@
<li> <li>
<p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p> <p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p>
<div id="qrCode"></div> <div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.AuthenticatorUri)"></div> <div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div>
<br /> <br />
</li> </li>
<li> <li>
@ -53,7 +53,7 @@
<script type="text/javascript"> <script type="text/javascript">
new QRCode(document.getElementById("qrCode"), new QRCode(document.getElementById("qrCode"),
{ {
text: "@Html.Raw(Model.AuthenticatorUri)", text: @Safe.Json(Model.AuthenticatorUri),
width: 200, width: 200,
height: 200, height: 200,
useSVG: true useSVG: true

View file

@ -31,7 +31,7 @@
</div> </div>
</li> </li>
</ul> </ul>
<div class="w-100 p-2">@Html.Raw(Model.Description)</div> <div class="w-100 p-2">@Safe.Raw(Model.Description)</div>
</div> </div>
<div class="col-sm-12 col-md-12 col-lg-6"> <div class="col-sm-12 col-md-12 col-lg-6">

View file

@ -22,7 +22,7 @@
@if (!Context.Request.Query.ContainsKey("simple")) @if (!Context.Request.Query.ContainsKey("simple"))
{ {
<script type="text/javascript"> <script type="text/javascript">
var srvModel = @Html.Raw(Json.Serialize(Model)); var srvModel = @Safe.Json(Model);
</script> </script>
<bundle name="wwwroot/bundles/payment-request-bundle-1.min.js"></bundle> <bundle name="wwwroot/bundles/payment-request-bundle-1.min.js"></bundle>
<bundle name="wwwroot/bundles/payment-request-bundle-2.min.js"></bundle> <bundle name="wwwroot/bundles/payment-request-bundle-2.min.js"></bundle>
@ -34,7 +34,7 @@
@if (!string.IsNullOrEmpty(Model.EmbeddedCSS)) @if (!string.IsNullOrEmpty(Model.EmbeddedCSS))
{ {
<style> <style>
@Html.Raw(Model.EmbeddedCSS); @Safe.Raw(Model.EmbeddedCSS);
</style> </style>
} }
</head> </head>

View file

@ -23,7 +23,7 @@
<bundle name="wwwroot/bundles/lightning-node-info-bundle.min.js" /> <bundle name="wwwroot/bundles/lightning-node-info-bundle.min.js" />
<script type="text/javascript"> <script type="text/javascript">
var srvModel = @Html.Raw(Json.Serialize(Model)); var srvModel = @Safe.Json(Model);
window.onload = function() { window.onload = function() {

View file

@ -54,7 +54,7 @@
{ {
<div class="form-group"> <div class="form-group">
<div id="qrCode"></div> <div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.ServiceLink)"></div> <div id="qrCodeData" data-url="@Model.ServiceLink"></div>
</div> </div>
} }
</div> </div>
@ -70,7 +70,7 @@
<script type="text/javascript"> <script type="text/javascript">
new QRCode(document.getElementById("qrCode"), new QRCode(document.getElementById("qrCode"),
{ {
text: "@Html.Raw(Model.ServiceLink)", text: @Safe.Json(Model.ServiceLink),
width: 200, width: 200,
height: 200, height: 200,
useSVG: true useSVG: true

View file

@ -90,7 +90,7 @@
{ {
<div class="form-group"> <div class="form-group">
<div id="qrCode"></div> <div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.QRCode)"></div> <div id="qrCodeData" data-url="@Model.QRCode"></div>
</div> </div>
<p>See QR Code information by clicking <a href="#detailsQR" data-toggle="collapse">here</a></p> <p>See QR Code information by clicking <a href="#detailsQR" data-toggle="collapse">here</a></p>
<div id="detailsQR" class="collapse"> <div id="detailsQR" class="collapse">
@ -184,7 +184,7 @@
<script type="text/javascript"> <script type="text/javascript">
new QRCode(document.getElementById("qrCode"), new QRCode(document.getElementById("qrCode"),
{ {
text: "@Html.Raw(Model.QRCode)", text: @Safe.Json(Model.QRCode),
width: 200, width: 200,
height: 200, height: 200,
useSVG: true useSVG: true

View file

@ -78,7 +78,7 @@
{ {
<div class="form-group"> <div class="form-group">
<div id="qrCode"></div> <div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.ServiceLink)"></div> <div id="qrCodeData" data-url="@Model.ServiceLink"></div>
</div> </div>
<p>See QR Code information by clicking <a href="#detailsQR" data-toggle="collapse">here</a></p> <p>See QR Code information by clicking <a href="#detailsQR" data-toggle="collapse">here</a></p>
<div id="detailsQR" class="collapse"> <div id="detailsQR" class="collapse">
@ -101,7 +101,7 @@
<script type="text/javascript"> <script type="text/javascript">
new QRCode(document.getElementById("qrCode"), new QRCode(document.getElementById("qrCode"),
{ {
text: "@Html.Raw(Model.ServiceLink)", text: @Safe.Json(Model.ServiceLink),
width: 200, width: 200,
height: 200, height: 200,
useSVG: true useSVG: true

View file

@ -128,14 +128,14 @@
@RenderSection("Scripts", required: false) @RenderSection("Scripts", required: false)
<script type="text/javascript"> <script type="text/javascript">
var expectedDomain = @Html.Raw(Json.Serialize(env.ExpectedHost)); var expectedDomain = @Safe.Json(env.ExpectedHost);
var expectedProtocol = @Html.Raw(Json.Serialize(env.ExpectedProtocol)); var expectedProtocol = @Safe.Json(env.ExpectedProtocol);
if (window.location.host != expectedDomain || window.location.protocol != expectedProtocol + ":") { if (window.location.host != expectedDomain || window.location.protocol != expectedProtocol + ":") {
document.getElementById("badUrl").style.display = "block"; document.getElementById("badUrl").style.display = "block";
document.getElementById("browserScheme").innerText = window.location.protocol.substr(0, window.location.protocol.length -1); document.getElementById("browserScheme").innerText = window.location.protocol.substr(0, window.location.protocol.length -1);
} }
</script> </script>
</body> </body>
</html> </html>

View file

@ -16,7 +16,7 @@
} }
@if (!string.IsNullOrEmpty(parsedModel.Html)) @if (!string.IsNullOrEmpty(parsedModel.Html))
{ {
@Html.Raw(parsedModel.Html) @Safe.Raw(parsedModel.Html)
} }
</div> </div>
} }

View file

@ -209,7 +209,7 @@
@section Scripts { @section Scripts {
<script type="text/javascript"> <script type="text/javascript">
var srvModel = @Html.Raw(Json.Serialize(Model)); var srvModel = @Safe.Json(Model);
var payButtonCtrl = new Vue({ var payButtonCtrl = new Vue({
el: '#payButtonCtrl', el: '#payButtonCtrl',

View file

@ -171,6 +171,6 @@
</div> </div>
@section Scripts { @section Scripts {
<script type="text/javascript">var defaultScript = @Html.Raw(Json.Serialize(Model.DefaultScript));</script> <script type="text/javascript">var defaultScript = @Safe.Json(Model.DefaultScript);</script>
@await Html.PartialAsync("_ValidationScriptsPartial") @await Html.PartialAsync("_ValidationScriptsPartial")
} }

View file

@ -6,4 +6,5 @@
@using BTCPayServer.Models.InvoicingModels @using BTCPayServer.Models.InvoicingModels
@using BTCPayServer.Models.ManageViewModels @using BTCPayServer.Models.ManageViewModels
@using BTCPayServer.Models.StoreViewModels @using BTCPayServer.Models.StoreViewModels
@inject BTCPayServer.Services.Safe Safe
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View file

@ -47,5 +47,5 @@
"Pay with CoinSwitch": "CoinSwitchでのお支払い", "Pay with CoinSwitch": "CoinSwitchでのお支払い",
"Pay with Changelly": "Changellyでのお支払い", "Pay with Changelly": "Changellyでのお支払い",
"Close": "閉じる", "Close": "閉じる",
"NotPaid_ExtraTransaction": "The invoice hasn't been paid in full. Please send another transaction to cover amount Due." "NotPaid_ExtraTransaction": "請求金額の全額が支払われていません。未払い分の別のトランザクションをお送りください。"
} }