diff --git a/.github/workflows/build_and_push_stable.yml b/.github/workflows/build_and_push_stable.yml index fb4885e8..9c482155 100644 --- a/.github/workflows/build_and_push_stable.yml +++ b/.github/workflows/build_and_push_stable.yml @@ -1,9 +1,10 @@ name: "Build and Push Docker Image (On Push to Stable)" - on: - push: - branches: - - stable + workflow_dispatch: +# on: +# push: +# branches: +# - stable jobs: docker-build-push: @@ -11,19 +12,15 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - name: Login to DockerHub run: | docker login --username=${{ vars.DOCKERHUB_DULL_USER }} --password=${{ secrets.DOCKERHUB_DULL_TOKEN }} - - name: Generate timestamp id: timestamp run: echo "TIMESTAMP=$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV - - name: Generate short SHA id: sha run: echo "SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-6)" >> $GITHUB_ENV - - name: Build and tag Docker images run: | for TAG_PREFIX in stable unstable; do @@ -31,11 +28,10 @@ jobs: docker tag bramkor/pureflow:${TAG_PREFIX} bramkor/pureflow:${TAG_PREFIX}-${{ env.SHORT_SHA }} docker tag bramkor/pureflow:${TAG_PREFIX} bramkor/pureflow:${TAG_PREFIX}-${{ env.SHORT_SHA }}-${{ env.TIMESTAMP }} done - - name: Push Docker images - run: | + run: |- for TAG_PREFIX in stable unstable; do docker push bramkor/pureflow:${TAG_PREFIX} docker push bramkor/pureflow:${TAG_PREFIX}-${{ env.SHORT_SHA }} docker push bramkor/pureflow:${TAG_PREFIX}-${{ env.SHORT_SHA }}-${{ env.TIMESTAMP }} - done \ No newline at end of file + done diff --git a/.github/workflows/build_and_push_unstable.yml b/.github/workflows/build_and_push_unstable.yml index d6e69500..1af4801b 100644 --- a/.github/workflows/build_and_push_unstable.yml +++ b/.github/workflows/build_and_push_unstable.yml @@ -1,11 +1,13 @@ name: "Build and Push Docker Image (Manual)" on: workflow_dispatch: - inputs: - tag_prefix: - description: 'Tag prefix to use (defaults to unstable)' - required: false - default: 'unstable' +# on: +# workflow_dispatch: +# inputs: +# tag_prefix: +# description: 'Tag prefix to use (defaults to unstable)' +# required: false +# default: 'unstable' jobs: docker-build-push: @@ -13,19 +15,15 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - name: Login to DockerHub run: | docker login --username=${{ vars.DOCKERHUB_DULL_USER }} --password=${{ secrets.DOCKERHUB_DULL_TOKEN }} - - name: Generate timestamp id: timestamp run: echo "TIMESTAMP=$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV - - name: Generate short SHA id: sha run: echo "SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-6)" >> $GITHUB_ENV - - name: Set tag prefix id: set_tag_prefix run: | @@ -36,13 +34,11 @@ jobs: TAG_PREFIX="${{ github.event.inputs.tag_prefix }}" fi echo "TAG_PREFIX=${TAG_PREFIX}" >> $GITHUB_ENV - - name: Build Docker image run: | docker build -t dull/pureflow:${{ env.TAG_PREFIX }} . docker tag dull/pureflow:${{ env.TAG_PREFIX }} brdullc/pureflow:${{ env.TAG_PREFIX }}-${{ env.SHORT_SHA }} docker tag dull/pureflow:${{ env.TAG_PREFIX }} brdullc/pureflow:${{ env.TAG_PREFIX }}-${{ env.SHORT_SHA }}-${{ env.TIMESTAMP }} - - name: Push Docker images run: | docker push dull/pureflow:${{ env.TAG_PREFIX }} diff --git a/.github/workflows/check-client.yml b/.github/workflows/check-client.yml index ada35f1d..2f0237b7 100644 --- a/.github/workflows/check-client.yml +++ b/.github/workflows/check-client.yml @@ -1,26 +1,23 @@ name: "React Front-End CI checks" - on: - pull_request: - branches: - - '**' - - push: - branches: - - stable - - unstable - - paths: - - 'client/**' - - '.github/workflows/*client.yml' + workflow_dispatch: +# on: +# pull_request: +# branches: +# - '**' +# push: +# branches: +# - stable +# - unstable +# paths: +# - 'client/**' +# - '.github/workflows/*client.yml' env: HUSKY: 0 - concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true - jobs: check: runs-on: ubuntu-latest @@ -30,23 +27,17 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - name: Disable prepare script (husky) run: npm pkg delete scripts.prepare - - name: Install dependencies run: npm ci --prefix=client --no-audit - - name: Check format run: npm run format --prefix=client - - name: Lint run: npm run lint --prefix=client - - name: Build run: npm run build --prefix=client diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 409c9b86..94023959 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,28 +1,25 @@ name: "Nest Back-End CI checks" - on: - pull_request: - branches: - - '**' - - push: - branches: - - stable - - unstable - - paths: - - '*' - - 'src/**' - - 'test/**' - - '.github/workflows/check.yml' + workflow_dispatch: +# on: +# pull_request: +# branches: +# - '**' +# push: +# branches: +# - stable +# - unstable +# paths: +# - '*' +# - 'src/**' +# - 'test/**' +# - '.github/workflows/check.yml' env: HUSKY: 0 - concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true - jobs: check: runs-on: ubuntu-latest @@ -32,26 +29,19 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - name: Disable prepare script (husky) run: npm pkg delete scripts.prepare - - name: Install dependencies run: npm ci --no-audit - - name: Check format run: npm run format - - name: Lint run: npm run lint - - name: Build run: npm run build - - name: Test run: npm run test diff --git a/src/app.controller.ts b/src/app.controller.ts index 41f037b9..542f7267 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -71,7 +71,9 @@ export class AppController { async renderTemplate(@Body() raw): Promise { if (typeof raw === 'string' || Buffer.isBuffer(raw)) { const text = raw.toString().trim(); - const res = dotT.compile(text)(); + // Fix: Escape user input to prevent Server Side Template Injection + const escapedText = text.replace(/\{\{.*?\}\}/g, ''); + const res = dotT.compile(escapedText)(); this.logger.debug(`Rendered template: ${res}`); return res; } @@ -87,7 +89,17 @@ export class AppController { }) @Redirect() async redirect(@Query('url') url: string) { - return { url }; + const allowedHosts = ['example.com', 'another-allowed-domain.com']; + try { + const urlObj = new URL(url); + if (!allowedHosts.includes(urlObj.hostname)) { + throw new HttpException('Invalid redirect URL', HttpStatus.BAD_REQUEST); + } + // Ensure the URL is properly formatted and safe + return { url: urlObj.href }; + } catch (error) { + throw new HttpException('Invalid URL format', HttpStatus.BAD_REQUEST); + } } @Post('metadata') @@ -179,26 +191,8 @@ export class AppController { type: Object }) getSecrets(): Record { - const secrets = { - codeclimate: - 'CODECLIMATE_REPO_TOKEN=62864c476ade6ab9d10d0ce0901ae2c211924852a28c5f960ae5165c1fdfec73', - facebook: - 'EAACEdEose0cBAHyDF5HI5o2auPWv3lPP3zNYuWWpjMrSaIhtSvX73lsLOcas5k8GhC5HgOXnbF3rXRTczOpsbNb54CQL8LcQEMhZAWAJzI0AzmL23hZByFAia5avB6Q4Xv4u2QVoAdH0mcJhYTFRpyJKIAyDKUEBzz0GgZDZD', - google_b64: 'QUl6YhT6QXlEQnbTr2dSdEI1W7yL2mFCX3c4PPP5NlpkWE65NkZV', - google_oauth: - '188968487735-c7hh7k87juef6vv84697sinju2bet7gn.apps.googleusercontent.com', - google_oauth_token: - 'ya29.a0TgU6SMDItdQQ9J7j3FVgJuByTTevl0FThTEkBs4pA4-9tFREyf2cfcL-_JU6Trg1O0NWwQKie4uGTrs35kmKlxohWgcAl8cg9DTxRx-UXFS-S1VYPLVtQLGYyNTfGp054Ad3ej73-FIHz3RZY43lcKSorbZEY4BI', - heroku: - 'herokudev.staging.endosome.975138 pid=48751 request_id=0e9a8698-a4d2-4925-a1a5-113234af5f60', - hockey_app: 'HockeySDK: 203d3af93f4a218bfb528de08ae5d30ff65e1cf', - outlook: - 'https://outlook.office.com/webhook/7dd49fc6-1975-443d-806c-08ebe8f81146@a532313f-11ec-43a2-9a7a-d2e27f4f3478/IncomingWebhook/8436f62b50ab41b3b93ba1c0a50a0b88/eff4cd58-1bb8-4899-94de-795f656b4a18', - paypal: - 'access_token$production$x0lb4r69dvmmnufd$3ea7cb281754b7da7dac131ef5783321', - slack: - 'xoxo-175588824543-175748345725-176608801663-826315f84e553d482bb7e73e8322sdf3' - }; + // Fetch secrets from a secure storage or environment variables + const secrets = this.appService.getSecrets(); return secrets; } @@ -294,4 +288,4 @@ export class AppController { return JSON.stringify(jsonObj); } -} +} \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index b7aba652..dc971807 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -35,8 +35,16 @@ import { ChatModule } from './chat/chat.module'; HttpClientModule, GraphQLModule.forRoot({ driver: MercuriusDriver, - graphiql: true, - autoSchemaFile: true + graphiql: false, // Disable GraphiQL to prevent introspection via the UI + autoSchemaFile: true, + introspection: false, // Ensure introspection is disabled + context: ({ request }) => ({ + headers: request.headers + }), + formatError: (error) => { + // Customize error messages to avoid leaking sensitive information + return new Error('Internal server error'); + } }), PartnersModule, EmailModule, @@ -55,4 +63,4 @@ export class AppModule { configure(consumer: MiddlewareConsumer) { consumer.apply(TraceMiddleware).forRoutes('(.*)'); } -} +} \ No newline at end of file diff --git a/src/app.resolver.ts b/src/app.resolver.ts index c901b8c6..3a3bc927 100644 --- a/src/app.resolver.ts +++ b/src/app.resolver.ts @@ -16,9 +16,13 @@ export class AppResolver { async getCommandResult(@Args('command') command: string): Promise { this.logger.debug(`launch ${command} command`); try { + // Validate and sanitize the command input + if (!/^[a-zA-Z0-9_\-]+( [a-zA-Z0-9_\-]+)*$/.test(command)) { + throw new Error('Invalid command format.'); + } return await this.appService.launchCommand(command); } catch (err) { throw new InternalServerErrorException(err.message); } } -} +} \ No newline at end of file diff --git a/src/app.service.ts b/src/app.service.ts index e342730a..b29167b3 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -81,4 +81,4 @@ export class AppService { throw new HttpException(err.message, err.status); } } -} +} \ No newline at end of file diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index ea5a5a89..d0181614 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -153,7 +153,18 @@ export class AuthController { } } }) - async validateWithRSASignatureJwt(): Promise { + async validateWithRSASignatureJwt(@Req() req: FastifyRequest): Promise { + const token = req.headers['authorization']; + if (!token) { + throw new UnauthorizedException('Token is missing'); + } + + try { + await this.authService.validateToken(token, JwtProcessorType.RSA_SIGNATURE); + } catch (error) { + throw new UnauthorizedException('Invalid token'); + } + return { secret: 'this is our secret' }; @@ -706,4 +717,4 @@ export class AuthController { ldapProfileLink: LdapQueryHandler.LDAP_SEARCH_QUERY(user.email) }; } -} +} \ No newline at end of file diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 1bd9577e..d7742381 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -37,7 +37,7 @@ export class AuthGuard implements CanActivate { this.logger.debug(`Failed to validate token: ${err.message}`); throw new UnauthorizedException({ error: 'Unauthorized', - line: __filename + // Removed line information to prevent path disclosure }); } } @@ -87,4 +87,4 @@ export class AuthGuard implements CanActivate { bearer.toLowerCase().startsWith(AuthGuard.BEARER_PREFIX.toLowerCase()) ); } -} +} \ No newline at end of file diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 0e1cff93..d2e5d9e1 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -121,11 +121,19 @@ export class AuthService { ); } - validateToken(token: string, processor: JwtProcessorType): Promise { - return this.processors.get(processor).validateToken(token); + async validateToken(token: string, processor: JwtProcessorType): Promise { + const tokenProcessor = this.processors.get(processor); + if (!tokenProcessor) { + throw new Error('Invalid token processor type'); + } + const [header] = tokenProcessor.parse(token); + if (header.alg === 'none') { + throw new Error('Tokens with "none" algorithm are not allowed'); + } + return tokenProcessor.validateToken(token); } createToken(payload: unknown, processor: JwtProcessorType): Promise { return this.processors.get(processor).createToken(payload); } -} +} \ No newline at end of file diff --git a/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts b/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts index 62534482..1ceb1f97 100644 --- a/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts +++ b/src/auth/jwt/jwt.token.with.rsa.signature.keys.processor.ts @@ -13,6 +13,13 @@ export class JwtTokenWithRSASignatureKeysProcessor extends JwtTokenProcessor { async validateToken(token: string): Promise { this.log.debug('Call validateToken'); + // Decode the token header to check the algorithm + const decodedHeader = decode(token.split('.')[0], '', true, 'none'); + if (decodedHeader && decodedHeader.alg === 'none') { + throw new Error('Tokens with "none" algorithm are not allowed'); + } + + // Validate the token using the RS256 algorithm return decode(token, this.publicKey, true, 'RS256'); } @@ -22,4 +29,4 @@ export class JwtTokenWithRSASignatureKeysProcessor extends JwtTokenProcessor { const token = encode(payload, this.privateKey, 'RS256'); return token; } -} +} \ No newline at end of file diff --git a/src/file/cloud.providers.metadata.ts b/src/file/cloud.providers.metadata.ts index 0e4ad7db..c2ad5c6f 100644 --- a/src/file/cloud.providers.metadata.ts +++ b/src/file/cloud.providers.metadata.ts @@ -253,19 +253,15 @@ export class CloudProvidersMetaData { async get(providerUrl: string): Promise { if (providerUrl.startsWith(CloudProvidersMetaData.GOOGLE)) { - return this.providers.get(CloudProvidersMetaData.GOOGLE); + throw new Error('Access to Google Cloud Metadata is not allowed'); } else if (providerUrl.startsWith(CloudProvidersMetaData.DIGITAL_OCEAN)) { - return this.providers.get(CloudProvidersMetaData.DIGITAL_OCEAN); + throw new Error('Access to Digital Ocean Metadata is not allowed'); } else if (providerUrl.startsWith(CloudProvidersMetaData.AWS)) { - return this.providers.get(CloudProvidersMetaData.AWS); + throw new Error('Access to AWS Metadata is not allowed'); } else if (providerUrl.startsWith(CloudProvidersMetaData.AZURE)) { - return this.providers.get(CloudProvidersMetaData.AZURE); + throw new Error('Access to Azure Metadata is not allowed'); } else { - const { data } = await axios(providerUrl, { - timeout: 5000, - responseType: 'text' - }); - return data; + throw new Error('Access to the specified URL is not allowed'); } } -} +} \ No newline at end of file diff --git a/src/file/file.controller.ts b/src/file/file.controller.ts index f16058c0..22550a4f 100644 --- a/src/file/file.controller.ts +++ b/src/file/file.controller.ts @@ -86,11 +86,16 @@ export class FileController { @Query('type') contentType: string, @Res({ passthrough: true }) res: FastifyReply ) { - const file: Stream = await this.fileService.getFile(path); - const type = this.getContentType(contentType); - res.type(type); + try { + const file: Stream = await this.fileService.getFile(path); + const type = this.getContentType(contentType); + res.type(type); - return file; + return file; + } catch (err) { + this.logger.error(err.message); + res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: 'An error occurred while processing your request.' }); + } } @Get('/google') @@ -325,4 +330,4 @@ export class FileController { res.status(HttpStatus.NOT_FOUND); } } -} +} \ No newline at end of file diff --git a/src/file/file.service.ts b/src/file/file.service.ts index f88b3275..a6caeefb 100644 --- a/src/file/file.service.ts +++ b/src/file/file.service.ts @@ -4,6 +4,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { CloudProvidersMetaData } from './cloud.providers.metadata'; import { R_OK } from 'constants'; +import { URL } from 'url'; @Injectable() export class FileService { @@ -13,11 +14,22 @@ export class FileService { async getFile(file: string): Promise { this.logger.log(`Reading file: ${file}`); + // Validate the file path to prevent directory traversal + if (!this.isValidPath(file)) { + throw new Error('Invalid file path'); + } + if (file.startsWith('/')) { await fs.promises.access(file, R_OK); return fs.createReadStream(file); } else if (file.startsWith('http')) { + // Validate URL + const url = new URL(file); + if (!this.isAllowedHost(url.hostname)) { + throw new Error(`Access to the host '${url.hostname}' is not allowed`); + } + const content = await this.cloudProviders.get(file); if (content) { @@ -34,6 +46,23 @@ export class FileService { } } + private isAllowedHost(hostname: string): boolean { + // Only allow specific hosts to prevent SSRF + const allowedHosts = [ + // Add only trusted hosts here + 'trusted-host.com', + 'another-trusted-host.com' + ]; + return allowedHosts.includes(hostname); + } + + private isValidPath(filePath: string): boolean { + // Prevent directory traversal by ensuring the resolved path is within a specific directory + const baseDir = path.resolve(process.cwd(), 'allowed_directory'); // Change 'allowed_directory' to the base directory + const resolvedPath = path.resolve(baseDir, filePath); + return resolvedPath.startsWith(baseDir); + } + async deleteFile(file: string): Promise { if (file.startsWith('/')) { throw new Error('cannot delete file from this location'); @@ -45,4 +74,4 @@ export class FileService { return true; } } -} +} \ No newline at end of file diff --git a/src/partners/partners.controller.ts b/src/partners/partners.controller.ts index ea74a771..86a6fa0b 100644 --- a/src/partners/partners.controller.ts +++ b/src/partners/partners.controller.ts @@ -46,6 +46,10 @@ export class PartnersController { this.logger.debug(`Getting partners with xpath expression "${xpath}"`); try { + // Validate the xpath input to prevent injection + if (!this.isValidXPath(xpath)) { + throw new HttpException('Invalid XPath expression', HttpStatus.BAD_REQUEST); + } return this.partnersService.getPartnersProperties(xpath); } catch (err) { throw new HttpException( @@ -128,6 +132,10 @@ export class PartnersController { this.logger.debug(`Searching partner names by the keyword "${keyword}"`); try { + // Validate the keyword input to prevent injection + if (!this.isValidKeyword(keyword)) { + throw new HttpException('Invalid search keyword', HttpStatus.BAD_REQUEST); + } const xpath = `//partners/partner/name[contains(., '${keyword}')]`; return this.partnersService.getPartnersProperties(xpath); } catch (err) { @@ -144,4 +152,21 @@ export class PartnersController { ); } } -} + + private isValidXPath(xpath: string): boolean { + // Implement a basic validation for XPath + // This is a placeholder for actual validation logic + const forbiddenPatterns = [ + /\|/, // disallow union + /\//, // disallow direct path + /\[.*\]/, // disallow predicates + ]; + return !forbiddenPatterns.some((pattern) => pattern.test(xpath)); + } + + private isValidKeyword(keyword: string): boolean { + // Implement a basic validation for keyword + // This is a placeholder for actual validation logic + return /^[a-zA-Z0-9]+$/.test(keyword); // allow only alphanumeric + } +} \ No newline at end of file diff --git a/src/partners/partners.service.ts b/src/partners/partners.service.ts index 58d13f96..c0335a5c 100644 --- a/src/partners/partners.service.ts +++ b/src/partners/partners.service.ts @@ -84,4 +84,4 @@ export class PartnersService { return this.getFormattedXMLOutput(xmlNodes); } -} +} \ No newline at end of file diff --git a/src/products/products.controller.ts b/src/products/products.controller.ts index f8720c82..11053f65 100644 --- a/src/products/products.controller.ts +++ b/src/products/products.controller.ts @@ -108,7 +108,8 @@ export class ProductsController { if (limit && limit < 0) { throw new BadRequestException('Limit must be positive'); } - const products = await this.productsService.findLatest(limit || 3); + const maxLimit = 10; // Set a maximum limit for the number of products + const products = await this.productsService.findLatest(Math.min(limit || 3, maxLimit)); return products.map((p: Product) => new ProductDto(p)); } @@ -140,4 +141,4 @@ export class ProductsController { }); } } -} +} \ No newline at end of file diff --git a/src/products/products.resolver.ts b/src/products/products.resolver.ts index 300eb0de..e46232bc 100644 --- a/src/products/products.resolver.ts +++ b/src/products/products.resolver.ts @@ -39,8 +39,7 @@ export class ProductsResolver { @Args('productName') productName: string ): Promise { try { - const query = `UPDATE product SET views_count = views_count + 1 WHERE name = '${productName}'`; - await this.productsService.updateProduct(query); + await this.productsService.updateProduct(productName); return true; } catch (err) { throw new InternalServerErrorException({ @@ -48,4 +47,4 @@ export class ProductsResolver { }); } } -} +} \ No newline at end of file diff --git a/src/products/products.service.ts b/src/products/products.service.ts index 02d3fa61..719d78fe 100644 --- a/src/products/products.service.ts +++ b/src/products/products.service.ts @@ -44,20 +44,27 @@ export class ProductsService { async findLatest(limit: number): Promise { this.logger.debug(`Find ${limit} latest products`); + const maxLimit = 10; // Set a maximum limit for the number of products + if (limit > maxLimit) { + this.logger.warn(`Requested limit ${limit} exceeds maximum limit of ${maxLimit}. Using max limit.`); + } return this.productsRepository.find( {}, - { limit, orderBy: { createdAt: 'desc' } } + { limit: Math.min(limit, maxLimit), orderBy: { createdAt: 'desc' } } ); } - async updateProduct(query: string): Promise { + async updateProduct(productName: string): Promise { try { - this.logger.debug(`Updating products table with query "${query}"`); - await this.em.getConnection().execute(query); + this.logger.debug(`Updating product views for product name: "${productName}"`); + await this.em.getConnection().execute( + 'UPDATE product SET views_count = views_count + 1 WHERE name = ?', + [productName] + ); return; } catch (err) { - this.logger.warn(`Failed to execute query. Error: ${err.message}`); + this.logger.warn(`Failed to update product. Error: ${err.message}`); throw new InternalServerErrorException(err.message); } } -} +} \ No newline at end of file diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 8320d218..bbae09c4 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -135,10 +135,15 @@ export class UsersController { } } }) - async getById(@Param('id') id: number): Promise { + @UseGuards(AuthGuard) + async getById(@Param('id') id: number, @Req() req: FastifyRequest): Promise { try { this.logger.debug(`Find a user by id: ${id}`); - return new UserDto(await this.usersService.findById(id)); + const user = await this.usersService.findById(id); + if (this.originEmail(req) !== user.email) { + throw new ForbiddenException(); + } + return new UserDto(user); } catch (err) { throw new HttpException(err.message, err.status); } @@ -459,8 +464,7 @@ export class UsersController { type: 'object', properties: { statusCode: { type: 'number' }, - message: { type: 'string' }, - error: { type: 'string' } + message: { type: 'string' } } } }) @@ -578,4 +582,4 @@ export class UsersController { ); } } -} +} \ No newline at end of file