java-topology/whitepaper/outreach/btcpayserver-0002.md
russell@unturf.com 9a78d1afbe feat: add 15 outreach docs (15 defects) for 1-patch projects
0ad (4), aranym, ardour, argo-cd, aria2, azahar, bcoin, bind9,
btcpayserver (3), bullet3. Mix of CWE-407 and CWE-312.
2026-04-14 14:33:17 -04:00

2.4 KiB
Raw Blame History

BTCPay Server — CWE-407 Disclosure Brief (btcpayserver-0002)

2026-04-13 · Patch available — awaiting upstream merge

Finding

One O(D*S) defect in BTCPay Server's app sales statistics. GetSalesStats uses series.All(e => e.Date != date) inside a loop over date ranges, making gap-filling quadratic in the number of days times existing series entries.

The Defect

btcpayserver-0002 (PATCHED — MEDIUM): BTCPayServer/Services/Apps/AppService.cs:113

// In AppService.GetSalesStats() — fill date gaps:
foreach (var i in Enumerable.Range(0, numberOfDays))
{
    var date = (DateTimeOffset.UtcNow - TimeSpan.FromDays(i)).Date;
    if (series.All(e => e.Date != date))
    {
        series.Add(new AppSalesStatsItem { Date = date, ... });
    }
}

series.All() scans all S existing series entries for each of D days in the range. Total cost: O(D*S). As series grows (via Add), subsequent All() calls scan an ever-larger list.

Complexity Proof

At D=365 days, S=200 initial series entries:

  • Defective: 365 × ~280 average = ~102,000 comparisons
  • Fixed: S=200 set construction + 365 O(1) lookups = ~565 operations
  • ~180× op reduction.

Impact

BTCPay Server displays sales statistics on merchant dashboards. Requesting a year of sales data triggers the quadratic gap-filling loop. Merchants viewing annual reports or dashboards with large date ranges experience slow page loads.

The Fix

Build a HashSet<DateTime> for O(1) date membership testing:

// Before
if (series.All(e => e.Date != date))

// After
// CWE-407 fix: HashSet for O(1) lookup instead of O(S) All() scan.
var existingDates = new HashSet<DateTime>(series.Select(e => e.Date));
if (!existingDates.Contains(date))
{
    series.Add(...);
    existingDates.Add(date);
}

Patch

Fix available: defects/btcpayserver-0002/patch/btcpayserver-0002.patch

Single-file patch on AppService.cs. Adds HashSet construction before the gap-filling loop and maintains the set on insertion.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (btcpayserver/btcpayserver).
  2. Assess severity — fires when rendering sales statistics over large date ranges.
  3. Coordinate a disclosure date — we target 90 days from first contact.
  4. We will credit the BTCPay Server team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.