From e96ad1cea4192a608a7f15896e4b690b57bf8fc9 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 29 Sep 2026 08:37:39 -0700 Subject: invoices: don't let a supplied id overwrite another user's invoice generate() writes invoice: wholesale with the caller's uid, and the old bolt11 pointer keeps resolving to that id, so passing someone else's invoice id re-pointed their invoice at the caller's account. Invoice ids aren't secret (LUD-21 verify URLs, invoice pages, receipts), so refuse an existing id that belongs to a different user, both in generate() and in POST /invoice for anonymous pay-page callers. Both fail with "invoice not found" so ids can't be probed for existence. Co-Authored-By: Claude Fable 5.1 --- routes/invoices.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'routes') diff --git a/routes/invoices.ts b/routes/invoices.ts index 9ba45af6..b8d06bba 100644 --- a/routes/invoices.ts +++ b/routes/invoices.ts @@ -36,6 +36,16 @@ export default { else invoice.own = false; try { + // A supplied id that already exists is an UPDATE of that record — + // generate() rewrites it wholesale. Only the authenticated owner may do + // that. Without this, an anonymous pay-page session passing + // body.user = merchant could rewrite the amount/memo of a merchant's + // live invoice. generate() separately refuses to move an invoice to a + // different uid. + if (invoice?.id) { + const existing = await getInvoice(invoice.id); + if (existing && existing.uid !== req.user.id) fail("invoice not found"); + } const result = await generate({ invoice, user }); res.send(result); } catch (e) { -- cgit v1.2.3