What changed, and why it matters
This commit updates the HtmlSanitizer library to a newer patch version and reorganizes some account email-change tests. The library bump could fix a security bug in how user-supplied HTML is cleaned, but the commit itself does not say it fixes a security issue or describe any specific vulnerability. The test changes appear to be routine refactoring of how the application verifies email address changes.
Review the HtmlSanitizer 9.2.1039 release notes to determine whether this patch fixes a security-relevant sanitization bypass. If it does, assess whether BTCPay Server passes attacker-controlled HTML through the library and prioritize deployment accordingly. Treat the test changes as non-security refactoring unless additional context indicates otherwise.
Security signals we found
Dependency version bump of an HTML-sanitization library (HtmlSanitizer)
Test-only reorganization around account email change flows
No explicit security advisory, CVE, or vulnerability description in commit message
Evidence from the diff
The diff bumps the HtmlSanitizer NuGet package from 9.2.995 to 9.2.1039 in BTCPayServer.Abstractions.csproj. HtmlSanitizer is used to sanitize untrusted HTML input; newer patch versions often address bypasses or sanitization defects. The same commit removes the standalone CanChangeUserMail test and merges its logic into the existing ChangingAccountEmailRequiresCurrentPassword test, now renamed CanChangeUserMail. The merged test still checks that duplicate emails are rejected and that successful email changes update both UserName and Email. No code changes to the sanitizer invocation or to the email-change controller are present in the diff.
Changed components
BTCPayServer.Abstractions/BTCPayServer.Abstractions.csprojHtmlSanitizer dependency (9.2.995 -> 9.2.1039)BTCPayServer.Tests/PlaywrightTests.csInspect captured patch +17 / −33
### BTCPayServer.Abstractions/BTCPayServer.Abstractions.csproj
@@ -31,7 +31,7 @@
<None Include="icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
- <PackageReference Include="HtmlSanitizer" Version="9.2.995" />
+ <PackageReference Include="HtmlSanitizer" Version="9.2.1039" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="10.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.11" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
### BTCPayServer.Tests/PlaywrightTests.cs
@@ -303,37 +303,6 @@ await Expect(s.Page.Locator(".only-for-js[data-test='status']"))
}
- [Fact]
- public async Task CanChangeUserMail()
- {
- await using var s = CreatePlaywrightTester();
- await s.StartAsync();
- var tester = s.Server;
- var u1 = tester.NewAccount();
- await u1.GrantAccessAsync();
- await u1.MakeAdmin(false);
- var u2 = tester.NewAccount();
- await u2.GrantAccessAsync();
- await u2.MakeAdmin(false);
- await s.GoToLogin();
- await s.LogIn(u1.RegisterDetails.Email, u1.RegisterDetails.Password);
- await s.GoToProfile();
- await s.Page.Locator("#Email").ClearAsync();
- await s.Page.FillAsync("#Email", u2.RegisterDetails.Email);
- await s.ClickPagePrimary();
- await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error, partialText: "The email address is already in use with an other account.");
- await s.GoToProfile();
- await s.Page.Locator("#Email").ClearAsync();
- var changedEmail = Guid.NewGuid() + "@lol.com";
- await s.Page.FillAsync("#Email", changedEmail);
- await s.ClickPagePrimary();
- await s.FindAlertMessage();
- using var scope = tester.PayTester.ServiceProvider.CreateScope();
- var manager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
- Assert.NotNull(await manager.FindByNameAsync(changedEmail));
- Assert.NotNull(await manager.FindByEmailAsync(changedEmail));
- }
-
[Fact]
[Trait("Playwright", "Playwright")]
[Trait("Lightning", "Lightning")]
@@ -990,12 +959,15 @@ public async Task NewUserLogin()
}
[Fact]
- public async Task ChangingAccountEmailRequiresCurrentPassword()
+ public async Task CanChangeUserMail()
{
await using var s = CreatePlaywrightTester();
await s.StartAsync();
var oldEmail = await s.RegisterNewUser();
var newEmail = $"{RandomUtils.GetUInt256().ToString()[..20]}@example.com";
+ var otherUser = s.Server.NewAccount();
+ await otherUser.GrantAccessAsync();
+ await otherUser.MakeAdmin(false);
await s.SkipWizard();
await s.GoToUrl("/account");
@@ -1010,6 +982,13 @@ public async Task ChangingAccountEmailRequiresCurrentPassword()
await s.ClickPagePrimary();
await Expect(s.Page.Locator("[data-valmsg-for=CurrentPassword]")).ToContainTextAsync("The current password is not correct.");
+ await s.GoToUrl("/account");
+ await Expect(s.Page.Locator("#Email")).ToHaveValueAsync(oldEmail);
+ await s.Page.FillAsync("#Email", otherUser.RegisterDetails.Email);
+ await s.Page.FillAsync("#CurrentPassword", "123456");
+ await s.ClickPagePrimary();
+ await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error, partialText: "The email address is already in use with an other account.");
+
await s.GoToUrl("/account");
await Expect(s.Page.Locator("#Email")).ToHaveValueAsync(oldEmail);
await s.Page.FillAsync("#Email", newEmail);
@@ -1018,6 +997,11 @@ public async Task ChangingAccountEmailRequiresCurrentPassword()
await s.FindAlertMessage(partialText: "Your profile has been updated");
await Expect(s.Page.Locator("#Email")).ToHaveValueAsync(newEmail);
+ using var scope = s.Server.PayTester.ServiceProvider.CreateScope();
+ var manager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
+ Assert.NotNull(await manager.FindByNameAsync(newEmail));
+ Assert.NotNull(await manager.FindByEmailAsync(newEmail));
+
await s.Logout();
await s.LogIn(newEmail, "123456");
await s.Page.AssertNoError();Why this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.