Skip to content

Commit a5d7908

Browse files
committed
chore: merge main into release for new releases
2 parents ad9f5df + c1bbcfe commit a5d7908

File tree

7 files changed

+361
-145
lines changed

7 files changed

+361
-145
lines changed

apps/api/src/attachments/attachments.service.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,69 @@ export class AttachmentsService {
5151
userId?: string,
5252
): Promise<AttachmentResponseDto> {
5353
try {
54+
// Blocked file extensions for security
55+
const BLOCKED_EXTENSIONS = [
56+
'exe',
57+
'bat',
58+
'cmd',
59+
'com',
60+
'scr',
61+
'msi', // Windows executables
62+
'js',
63+
'vbs',
64+
'vbe',
65+
'wsf',
66+
'wsh',
67+
'ps1', // Scripts
68+
'sh',
69+
'bash',
70+
'zsh', // Shell scripts
71+
'dll',
72+
'sys',
73+
'drv', // System files
74+
'app',
75+
'deb',
76+
'rpm', // Application packages
77+
'jar', // Java archives (can execute)
78+
'pif',
79+
'lnk',
80+
'cpl', // Shortcuts and control panel
81+
'hta',
82+
'reg', // HTML apps and registry
83+
];
84+
85+
// Blocked MIME types for security
86+
const BLOCKED_MIME_TYPES = [
87+
'application/x-msdownload', // .exe
88+
'application/x-msdos-program',
89+
'application/x-executable',
90+
'application/x-sh', // Shell scripts
91+
'application/x-bat', // Batch files
92+
'text/x-sh',
93+
'text/x-python',
94+
'text/x-perl',
95+
'text/x-ruby',
96+
'application/x-httpd-php', // PHP files
97+
'application/x-javascript', // Executable JS (not JSON)
98+
'application/javascript',
99+
'text/javascript',
100+
];
101+
102+
// Validate file extension
103+
const fileExt = uploadDto.fileName.split('.').pop()?.toLowerCase();
104+
if (fileExt && BLOCKED_EXTENSIONS.includes(fileExt)) {
105+
throw new BadRequestException(
106+
`File extension '.${fileExt}' is not allowed for security reasons`,
107+
);
108+
}
109+
110+
// Validate MIME type
111+
if (BLOCKED_MIME_TYPES.includes(uploadDto.fileType.toLowerCase())) {
112+
throw new BadRequestException(
113+
`File type '${uploadDto.fileType}' is not allowed for security reasons`,
114+
);
115+
}
116+
54117
// Validate file size
55118
const fileBuffer = Buffer.from(uploadDto.fileData, 'base64');
56119
if (fileBuffer.length > this.MAX_FILE_SIZE_BYTES) {

apps/api/src/attachments/upload-attachment.dto.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,28 @@ import { ApiProperty } from '@nestjs/swagger';
22
import { Transform } from 'class-transformer';
33
import {
44
IsBase64,
5-
IsIn,
65
IsNotEmpty,
76
IsOptional,
87
IsString,
98
MaxLength,
9+
Matches,
1010
} from 'class-validator';
1111

12-
const ALLOWED_FILE_TYPES = [
13-
'image/jpeg',
14-
'image/png',
15-
'image/gif',
16-
'image/webp',
17-
'application/pdf',
18-
'text/plain',
19-
'application/msword',
20-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
21-
'application/vnd.ms-excel',
22-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
12+
// Block dangerous MIME types that could execute code
13+
const BLOCKED_MIME_TYPES = [
14+
'application/x-msdownload', // .exe
15+
'application/x-msdos-program',
16+
'application/x-executable',
17+
'application/x-sh', // Shell scripts
18+
'application/x-bat', // Batch files
19+
'text/x-sh',
20+
'text/x-python',
21+
'text/x-perl',
22+
'text/x-ruby',
23+
'application/x-httpd-php', // PHP files
24+
'application/x-javascript', // Executable JS (not JSON)
25+
'application/javascript',
26+
'text/javascript',
2327
];
2428

2529
export class UploadAttachmentDto {
@@ -37,11 +41,11 @@ export class UploadAttachmentDto {
3741
@ApiProperty({
3842
description: 'MIME type of the file',
3943
example: 'application/pdf',
40-
enum: ALLOWED_FILE_TYPES,
4144
})
4245
@IsString()
43-
@IsIn(ALLOWED_FILE_TYPES, {
44-
message: `File type must be one of: ${ALLOWED_FILE_TYPES.join(', ')}`,
46+
@IsNotEmpty()
47+
@Matches(/^[a-zA-Z0-9\-]+\/[a-zA-Z0-9\-\+\.]+$/, {
48+
message: 'Invalid MIME type format',
4549
})
4650
fileType: string;
4751

apps/api/src/tasks/attachments.service.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,69 @@ export class AttachmentsService {
4747
userId?: string,
4848
): Promise<AttachmentResponseDto> {
4949
try {
50+
// Blocked file extensions for security
51+
const BLOCKED_EXTENSIONS = [
52+
'exe',
53+
'bat',
54+
'cmd',
55+
'com',
56+
'scr',
57+
'msi', // Windows executables
58+
'js',
59+
'vbs',
60+
'vbe',
61+
'wsf',
62+
'wsh',
63+
'ps1', // Scripts
64+
'sh',
65+
'bash',
66+
'zsh', // Shell scripts
67+
'dll',
68+
'sys',
69+
'drv', // System files
70+
'app',
71+
'deb',
72+
'rpm', // Application packages
73+
'jar', // Java archives (can execute)
74+
'pif',
75+
'lnk',
76+
'cpl', // Shortcuts and control panel
77+
'hta',
78+
'reg', // HTML apps and registry
79+
];
80+
81+
// Blocked MIME types for security
82+
const BLOCKED_MIME_TYPES = [
83+
'application/x-msdownload', // .exe
84+
'application/x-msdos-program',
85+
'application/x-executable',
86+
'application/x-sh', // Shell scripts
87+
'application/x-bat', // Batch files
88+
'text/x-sh',
89+
'text/x-python',
90+
'text/x-perl',
91+
'text/x-ruby',
92+
'application/x-httpd-php', // PHP files
93+
'application/x-javascript', // Executable JS (not JSON)
94+
'application/javascript',
95+
'text/javascript',
96+
];
97+
98+
// Validate file extension
99+
const fileExt = uploadDto.fileName.split('.').pop()?.toLowerCase();
100+
if (fileExt && BLOCKED_EXTENSIONS.includes(fileExt)) {
101+
throw new BadRequestException(
102+
`File extension '.${fileExt}' is not allowed for security reasons`,
103+
);
104+
}
105+
106+
// Validate MIME type
107+
if (BLOCKED_MIME_TYPES.includes(uploadDto.fileType.toLowerCase())) {
108+
throw new BadRequestException(
109+
`File type '${uploadDto.fileType}' is not allowed for security reasons`,
110+
);
111+
}
112+
50113
// Validate file size
51114
const fileBuffer = Buffer.from(uploadDto.fileData, 'base64');
52115
if (fileBuffer.length > this.MAX_FILE_SIZE_BYTES) {

apps/api/src/tasks/dto/upload-attachment.dto.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,28 @@ import { ApiProperty } from '@nestjs/swagger';
22
import { Transform } from 'class-transformer';
33
import {
44
IsBase64,
5-
IsIn,
65
IsNotEmpty,
76
IsOptional,
87
IsString,
98
MaxLength,
9+
Matches,
1010
} from 'class-validator';
1111

12-
const ALLOWED_FILE_TYPES = [
13-
'image/jpeg',
14-
'image/png',
15-
'image/gif',
16-
'image/webp',
17-
'application/pdf',
18-
'text/plain',
19-
'application/msword',
20-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
21-
'application/vnd.ms-excel',
22-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
12+
// Block dangerous MIME types that could execute code
13+
const BLOCKED_MIME_TYPES = [
14+
'application/x-msdownload', // .exe
15+
'application/x-msdos-program',
16+
'application/x-executable',
17+
'application/x-sh', // Shell scripts
18+
'application/x-bat', // Batch files
19+
'text/x-sh',
20+
'text/x-python',
21+
'text/x-perl',
22+
'text/x-ruby',
23+
'application/x-httpd-php', // PHP files
24+
'application/x-javascript', // Executable JS (not JSON)
25+
'application/javascript',
26+
'text/javascript',
2327
];
2428

2529
export class UploadAttachmentDto {
@@ -37,11 +41,11 @@ export class UploadAttachmentDto {
3741
@ApiProperty({
3842
description: 'MIME type of the file',
3943
example: 'application/pdf',
40-
enum: ALLOWED_FILE_TYPES,
4144
})
4245
@IsString()
43-
@IsIn(ALLOWED_FILE_TYPES, {
44-
message: `File type must be one of: ${ALLOWED_FILE_TYPES.join(', ')}`,
46+
@IsNotEmpty()
47+
@Matches(/^[a-zA-Z0-9\-]+\/[a-zA-Z0-9\-\+\.]+$/, {
48+
message: 'Invalid MIME type format',
4549
})
4650
fileType: string;
4751

0 commit comments

Comments
 (0)