Skip to content

Commit

Permalink
Validation of file types (#573)
Browse files Browse the repository at this point in the history
* Add validation of file types

* Add more file and mime types

* Fix function description

* Add pdf type

---------

Co-authored-by: igoychev <[email protected]>
  • Loading branch information
Nnachevvv and igoychev authored Nov 13, 2023
1 parent fcab466 commit 3941038
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
ImportStatus,
} from '../bank-transactions-file/dto/bank-transactions-import-status.dto'
import { CreateBankPaymentDto } from '../donations/dto/create-bank-payment.dto'
import { validateFileType } from '../common/files'

@ApiTags('bank-transactions-file')
@Controller('bank-transactions-file')
Expand All @@ -45,7 +46,14 @@ export class BankTransactionsFileController {
) {}

@Post(':bank_transactions_file_id')
@UseInterceptors(FilesInterceptor('file', 5, { limits: { fileSize: 10485760 } }))
@UseInterceptors(
FilesInterceptor('file', 5, {
limits: { fileSize: 1024 * 1024 * 10 }, //limit uploaded files to 5 at once and 10MB each
fileFilter: (_req: Request, file, cb) => {
validateFileType(file, cb)
},
}),
)
async create(
@Param('bank_transactions_file_id') bankTransactionsFileId: string,
@Body() body: FilesTypesDto,
Expand Down
10 changes: 9 additions & 1 deletion apps/api/src/campaign-file/campaign-file.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { CampaignFileService } from './campaign-file.service'
import { CampaignService } from '../campaign/campaign.service'
import { KeycloakTokenParsed, isAdmin } from '../auth/keycloak'
import { ApiTags } from '@nestjs/swagger'
import { validateFileType } from '../common/files'

@ApiTags('campaign-file')
@Controller('campaign-file')
Expand All @@ -35,7 +36,14 @@ export class CampaignFileController {
) {}

@Post(':campaign_id')
@UseInterceptors(FilesInterceptor('file', 10, { limits: { fileSize: 20485760 } })) //limit uploaded files to 5 at once and 10MB each
@UseInterceptors(
FilesInterceptor('file', 10, {
limits: { fileSize: 1024 * 1024 * 10 }, //limit uploaded files to 10 at once and 10MB each
fileFilter: (_req: Request, file, cb) => {
validateFileType(file, cb)
},
}),
)
async create(
@Param('campaign_id') campaignId: string,
@Body() body: FilesRoleDto,
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/campaign-file/campaign-file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class CampaignFileService {
filename: encodeURIComponent(file.filename),
mimetype: file.mimetype,
stream: await this.s3.streamFile(this.bucketName, id),
role: file.role
role: file.role,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { FilesRoleDto } from './dto/files-role.dto'
import { CampaignNewsFileService } from './campaign-news-file.service'
import { KeycloakTokenParsed, isAdmin } from '../auth/keycloak'
import { ApiTags } from '@nestjs/swagger'
import { validateFileType } from '../common/files'

@ApiTags('campaign-news-file')
@Controller('campaign-news-file')
Expand All @@ -34,7 +35,14 @@ export class CampaignNewsFileController {
) {}

@Post(':article_id')
@UseInterceptors(FilesInterceptor('file', 10, { limits: { fileSize: 20485760 } })) //limit uploaded files to 5 at once and 10MB each
@UseInterceptors(
FilesInterceptor('file', 10, {
limits: { fileSize: 1024 * 1024 * 10 }, //limit uploaded files to 10 at once and 10MB each
fileFilter: (_req: Request, file, cb) => {
validateFileType(file, cb)
},
}),
)
async create(
@Param('article_id') articleId: string,
@Body() body: FilesRoleDto,
Expand Down
48 changes: 48 additions & 0 deletions apps/api/src/common/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import path from 'path'

interface File {
fieldname: string
originalname: string
encoding: string
mimetype: string
size: number
destination: string
filename: string
path: string
buffer: Buffer
}

/**
* The function is used to validate the type of a file using the file extension and MIME type
* @param file object that represent file
* @param cb callback function which indicates whether file is accepted or not
*/
export function validateFileType(
file: File,
cb: (error: Error | null, acceptFile: boolean) => void,
) {
const mimeAllowlist = [
'text/plain',
'application/json',
'application/pdf',
'image/png',
'image/jpeg',
'application/xml',
'text/xml',
'application/msword',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
]
if (!mimeAllowlist.includes(file.mimetype)) {
return cb(new Error('File mime type is not allowed'), false)
}

const allowedExtensions = /txt|json|pdf|jpeg|jpg|png|xml|xlsx|xls|docx/

const isExtensionSupported = allowedExtensions.test(path.extname(file.originalname).toLowerCase())
if (!isExtensionSupported) {
return cb(new Error('File extension is not allowed'), false)
}

cb(null, true)
}
11 changes: 9 additions & 2 deletions apps/api/src/expenses/expenses.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { UpdateExpenseDto } from './dto/update-expense.dto'
import { ApiTags } from '@nestjs/swagger'
import { UseInterceptors, UploadedFiles } from '@nestjs/common'
import { FilesInterceptor } from '@nestjs/platform-express'
import { validateFileType } from '../common/files'

@ApiTags('expenses')
@Controller('expenses')
Expand All @@ -37,7 +38,6 @@ export class ExpensesController {
}

@Post('create-expense')
@UseInterceptors(FilesInterceptor('file', 5, { limits: { fileSize: 10485760 } })) //limit uploaded files to 5 at once and 10MB each
async create(
@AuthenticatedUser() user: KeycloakTokenParsed,
@Body() createExpenseDto: CreateExpenseDto,
Expand Down Expand Up @@ -69,7 +69,14 @@ export class ExpensesController {
}

@Post(':expenseId/files')
@UseInterceptors(FilesInterceptor('file', 5, { limits: { fileSize: 10485760 } })) //limit uploaded files to 5 at once and 10MB each
@UseInterceptors(
FilesInterceptor('file', 5, {
limits: { fileSize: 1024 * 1024 * 10 }, //limit uploaded files to 5 at once and 10MB each
fileFilter: (_req: Request, file, cb) => {
validateFileType(file, cb)
},
}),
)
async uploadFiles(
@Param('expenseId') expenseId: string,
@UploadedFiles() files: Express.Multer.File[],
Expand Down
12 changes: 10 additions & 2 deletions apps/api/src/irregularity-file/irregularity-file.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import { PersonService } from '../person/person.service'
import { IrregularityFileService } from './irregularity-file.service'
import { RealmViewContactRequests, ViewContactRequests } from '@podkrepi-bg/podkrepi-types'
import { IrregularityService } from '../irregularity/irregularity.service'
import { ApiTags } from '@nestjs/swagger';
import { ApiTags } from '@nestjs/swagger'
import { validateFileType } from '../common/files'

@ApiTags('irregularity-file')
@Controller('irregularity-file')
Expand All @@ -33,7 +34,14 @@ export class IrregularityFileController {

@Post(':irregularity_id')
@Public()
@UseInterceptors(FilesInterceptor('file', 5, { limits: { fileSize: 10485760 } })) //limit uploaded files to 5 at once and 10MB each
@UseInterceptors(
FilesInterceptor('file', 5, {
limits: { fileSize: 1024 * 1024 * 10 }, //limit uploaded files to 5 at once and 10MB each
fileFilter: (_req: Request, file, cb) => {
validateFileType(file, cb)
},
}),
)
async create(
@Param('irregularity_id') irregularityId: string,
@UploadedFiles() files: Express.Multer.File[],
Expand Down

0 comments on commit 3941038

Please sign in to comment.