From fea6dfdf9258ff9855ab3aa3dee830857274d052 Mon Sep 17 00:00:00 2001 From: Tamir Gershberg <47638346+tamirGer@users.noreply.github.com> Date: Fri, 29 Dec 2023 19:12:32 +0200 Subject: [PATCH] feat(business constraint bypass): add `limit` param on `/api/products/latest` (#294) Adds a `limit` URL param to `/api/products/latest` which controls the amount of products returned via this EP. This introduces a business constraint bypass, as this EP is used on the main page to display only the 3 latest products, and the user must be logged in to access the Marketplace page where they can see the full list of products via the `/api/products` EP, which is password protected. --- README.md | 4 +++- src/products/products.controller.ts | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d91edb7b..187e1b18 100644 --- a/README.md +++ b/README.md @@ -137,4 +137,6 @@ Additionally, the endpoint PUT /api/users/one/{email}/photo accepts SVG images, * **IFrame Injection** - The `/testimonials` page a URL parameter `videosrc` which directly controls the src attribute of the IFrame at the bottom of this page. Similarly, the home page takes a URL param `maptitle` which directly controls the `title` attribute of the IFrame at the CONTACT section of this page. -* **Excessive Data Exposure** - The `/api/users/one/:email` is supposed to expose only basic user information required to be displayed on the UI, but it also returns the user's phone number which is unnecessary information. \ No newline at end of file +* **Excessive Data Exposure** - The `/api/users/one/:email` is supposed to expose only basic user information required to be displayed on the UI, but it also returns the user's phone number which is unnecessary information. + +* **Business Constraint Bypass** - The `/api/products/latest` endpoint supports a `limit` parameter, which by default is set to 3. The `/api/products` endpoint is a password protected endpoint which returns all of the products, yet if you change the `limit` param of `/api/products/latest` to be high enough you could get the same results without the need to be authenticated. \ No newline at end of file diff --git a/src/products/products.controller.ts b/src/products/products.controller.ts index 04fa8c66..f407ba82 100644 --- a/src/products/products.controller.ts +++ b/src/products/products.controller.ts @@ -6,6 +6,8 @@ import { UseGuards, Headers, InternalServerErrorException, + Query, + BadRequestException, } from '@nestjs/common'; import { ApiOperation, @@ -14,6 +16,7 @@ import { ApiForbiddenResponse, ApiInternalServerErrorResponse, ApiHeader, + ApiQuery, } from '@nestjs/swagger'; import { AuthGuard } from '../auth/auth.guard'; import { JwtProcessorType } from '../auth/auth.service'; @@ -61,6 +64,7 @@ export class ProductsController { } @Get('latest') + @ApiQuery({ name: 'limit', example: 3, required: false }) @ApiOperation({ description: API_DESC_GET_LATEST_PRODUCTS, }) @@ -68,9 +72,17 @@ export class ProductsController { type: ProductDto, isArray: true, }) - async getLatestProducts(): Promise { + async getLatestProducts( + @Query('limit') limit: number, + ): Promise { this.logger.debug('Get latest products.'); - const products = await this.productsService.findLatest(3); + if (limit && isNaN(limit)) { + throw new BadRequestException('Limit must be a number'); + } + if (limit && limit < 0) { + throw new BadRequestException('Limit must be positive'); + } + const products = await this.productsService.findLatest(limit || 3); return products.map((p: Product) => new ProductDto(p)); }