From fc0fbc0fce25190cbf4e24bbe804bc0edeb48116 Mon Sep 17 00:00:00 2001 From: mijinummi Date: Sun, 18 Oct 2026 17:32:11 +0100 Subject: [PATCH 1/4] fix(validation): verify the checksum of Stellar addresses --- .../validation/stellar-address.validator.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/common/validation/stellar-address.validator.ts diff --git a/src/common/validation/stellar-address.validator.ts b/src/common/validation/stellar-address.validator.ts new file mode 100644 index 0000000..a4c72f0 --- /dev/null +++ b/src/common/validation/stellar-address.validator.ts @@ -0,0 +1,16 @@ +import { z } from 'zod'; +import { StrKey } from '@stellar/stellar-sdk'; + +export const stellarAddressSchema = z + .string() + .min(1, { message: 'Stellar address is required' }) + .refine( + (val) => { + try { + return StrKey.isValidEd25519PublicKey(val); + } catch { + return false; + } + }, + { message: 'Invalid Stellar public key or bad checksum' } + ); \ No newline at end of file From d2d05a36f6152b3eddd8d835d5651c345a9cde0d Mon Sep 17 00:00:00 2001 From: mijinummi Date: Sun, 18 Oct 2026 17:32:46 +0100 Subject: [PATCH 2/4] fix(notifications): don't log the webhook URL path --- src/notifications/notifications.service.ts | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/notifications/notifications.service.ts diff --git a/src/notifications/notifications.service.ts b/src/notifications/notifications.service.ts new file mode 100644 index 0000000..89224ef --- /dev/null +++ b/src/notifications/notifications.service.ts @@ -0,0 +1,37 @@ +import { Logger } from '@nestjs/common'; + +export class NotificationsService { + private readonly logger = new Logger(NotificationsService.name); + + async notifyWebhook(webhookUrl: string, payload: any): Promise { + try { + const response = await fetch(webhookUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + // Redact URL path to prevent leaking Slack/Discord webhook secrets + let redactedOrigin = 'unknown-origin'; + try { + const parsedUrl = new URL(webhookUrl); + redactedOrigin = parsedUrl.origin; + } catch { + redactedOrigin = '[invalid-url]'; + } + + this.logger.error( + `webhook notification failed with status ${response.status} for ${redactedOrigin}` + ); + } + } catch (error: any) { + let redactedOrigin = 'unknown-origin'; + try { + redactedOrigin = new URL(webhookUrl).origin; + } catch {} + + this.logger.error(`webhook notification error for ${redactedOrigin}: ${error.message}`); + } + } +} \ No newline at end of file From ac2a4d082397aa4622c10c5f9c051e0b85b25276 Mon Sep 17 00:00:00 2001 From: mijinummi Date: Sun, 18 Oct 2026 17:33:33 +0100 Subject: [PATCH 3/4] fix(impact): compute totals in SQL --- src/impacts/impact.service.ts | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/impacts/impact.service.ts diff --git a/src/impacts/impact.service.ts b/src/impacts/impact.service.ts new file mode 100644 index 0000000..5f0ee0c --- /dev/null +++ b/src/impacts/impact.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; // adjust path as needed + +@Injectable() +export class ImpactService { + constructor(private readonly prisma: PrismaService) {} + + async getPlatformImpact() { + // Use SQL aggregation instead of loading all streams into memory + const result = await this.prisma.$queryRaw<{ totalAmount: number; totalStreams: number }[]>` + SELECT + COALESCE(SUM(amount), 0) as "totalAmount", + COUNT(id) as "totalStreams" + FROM "Stream"; + `; + + return { + totalAmount: Number(result[0]?.totalAmount || 0), + totalStreams: Number(result[0]?.totalStreams || 0), + }; + } + + async getNgoImpact(ngoId: string) { + // Aggregate platform total and NGO-specific total in SQL + const [platformResult, ngoResult] = await Promise.all([ + this.prisma.$queryRaw<{ totalAmount: number }[]>` + SELECT COALESCE(SUM(amount), 0) as "totalAmount" FROM "Stream"; + `, + this.prisma.$queryRaw<{ totalAmount: number; totalStreams: number }[]>` + SELECT + COALESCE(SUM(amount), 0) as "totalAmount", + COUNT(id) as "totalStreams" + FROM "Stream" + WHERE "ngoId" = ${ngoId}; + `, + ]); + + return { + ngoId, + ngoTotalAmount: Number(ngoResult[0]?.totalAmount || 0), + ngoTotalStreams: Number(ngoResult[0]?.totalStreams || 0), + platformTotalAmount: Number(platformResult[0]?.totalAmount || 0), + }; + } +} \ No newline at end of file From 1a1d8c5029eb32c465af5f35f04c5bc97d61665c Mon Sep 17 00:00:00 2001 From: mijinummi Date: Sun, 18 Oct 2026 17:34:20 +0100 Subject: [PATCH 4/4] fix(db): require DATABASE_URL in production --- src/db.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/db.ts b/src/db.ts index a148246..d3871df 100644 --- a/src/db.ts +++ b/src/db.ts @@ -26,3 +26,23 @@ export async function setNgoVerified(id: string, verified: boolean): Promise