From a13c03fd2b0da83c5425352f9fcfe0dea23d9402 Mon Sep 17 00:00:00 2001 From: Jaydbrown Date: Wed, 24 Jun 2026 15:38:22 +0100 Subject: [PATCH 1/3] fix(cors): enforce HTTPS-only, no localhost, no wildcards, max 10 origins --- src/merchants/merchants.dto.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/merchants/merchants.dto.ts b/src/merchants/merchants.dto.ts index 36db806..17640d3 100644 --- a/src/merchants/merchants.dto.ts +++ b/src/merchants/merchants.dto.ts @@ -4,6 +4,7 @@ import { IsOptional, IsUrl, IsArray, + ArrayMaxSize, Matches, MaxLength, ValidateIf, @@ -56,6 +57,11 @@ export class GenerateApiKeyDto { export class SetCorsOriginsDto { @IsArray() - @IsUrl({}, { each: true }) + @ArrayMaxSize(10, { message: 'A maximum of 10 CORS origins are allowed per merchant' }) + @IsUrl( + { protocols: ['https'], require_protocol: true, allow_localhost: false }, + { each: true, message: 'Each origin must be a valid HTTPS URL without localhost' }, + ) + @Matches(/^[^*]+$/, { each: true, message: 'Wildcard origins are not permitted' }) origins: string[]; } From 6c0929124091d8106d5aa1916110e01b8a56dc4b Mon Sep 17 00:00:00 2001 From: Jaydbrown Date: Wed, 24 Jun 2026 15:39:06 +0100 Subject: [PATCH 2/3] fix(cors): add deleteCorsOrigin method to remove a single origin --- src/merchants/merchants.service.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/merchants/merchants.service.ts b/src/merchants/merchants.service.ts index 79399f0..7276dff 100644 --- a/src/merchants/merchants.service.ts +++ b/src/merchants/merchants.service.ts @@ -148,4 +148,12 @@ export class MerchantsService { await this.corsCache.invalidateMerchantCache(merchantId); return (updated.corsOrigins ?? []) as string[]; } + + async deleteCorsOrigin(merchantId: string, origin: string): Promise { + const current = await this.getCorsOrigins(merchantId); + const filtered = current.filter((o: string) => o !== origin); + if (filtered.length === current.length) return false; + await this.setCorsOrigins(merchantId, filtered); + return true; + } } From 11591dffda67316b2c91df1a3b73652316c3180a Mon Sep 17 00:00:00 2001 From: Jaydbrown Date: Wed, 24 Jun 2026 15:40:05 +0100 Subject: [PATCH 3/3] fix(cors): add DELETE /me/cors/:origin endpoint --- src/merchants/merchants.controller.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/merchants/merchants.controller.ts b/src/merchants/merchants.controller.ts index ee8e40c..3d73ba0 100644 --- a/src/merchants/merchants.controller.ts +++ b/src/merchants/merchants.controller.ts @@ -9,6 +9,7 @@ import { Param, UseGuards, Request, + NotFoundException, } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { MerchantsService } from './merchants.service'; @@ -100,4 +101,14 @@ export class MerchantsController { return this.merchants.setCorsOrigins(m.id, dto.origins); }); } + + @Delete('me/cors/:origin') + @Roles('admin', 'merchant') + async deleteCorsOrigin(@Request() req: any, @Param('origin') origin: string) { + const m = await this.merchants.findByWallet(req.user.walletAddress); + if (!m) throw new NotFoundException('Merchant not found'); + const removed = await this.merchants.deleteCorsOrigin(m.id, decodeURIComponent(origin)); + if (!removed) throw new NotFoundException('Origin not found in configured list'); + return { origins: await this.merchants.getCorsOrigins(m.id) }; + } }