Compare commits
1 commit
main
...
fix/mcp-at
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00b59ec2f4 |
5 changed files with 63 additions and 37 deletions
|
|
@ -35,19 +35,9 @@ import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/n
|
|||
import { GetNotificationsDto } from '@gitroom/nestjs-libraries/dtos/notifications/get.notifications.dto';
|
||||
import { Readable } from 'stream';
|
||||
import { ssrfSafeDispatcher } from '@gitroom/nestjs-libraries/dtos/webhooks/ssrf.safe.dispatcher';
|
||||
import { VALID_POST_MEDIA_MIME_TYPES } from '@gitroom/helpers/utils/has.extension';
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { fromBuffer } = require('file-type');
|
||||
|
||||
const PUBLIC_API_ALLOWED_MIME = new Set<string>([
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
'image/avif',
|
||||
'image/bmp',
|
||||
'image/tiff',
|
||||
'video/mp4',
|
||||
]);
|
||||
import * as Sentry from '@sentry/nestjs';
|
||||
import {
|
||||
socialIntegrationList,
|
||||
|
|
@ -108,7 +98,7 @@ export class PublicIntegrationsController {
|
|||
}
|
||||
const buffer = Buffer.from(await response.arrayBuffer());
|
||||
const detected = await fromBuffer(buffer);
|
||||
if (!detected || !PUBLIC_API_ALLOWED_MIME.has(detected.mime)) {
|
||||
if (!detected || !VALID_POST_MEDIA_MIME_TYPES.has(detected.mime)) {
|
||||
throw new HttpException({ msg: 'Unsupported file type.' }, 400);
|
||||
}
|
||||
const mimetype = detected.mime;
|
||||
|
|
|
|||
|
|
@ -8,3 +8,26 @@ export const hasExtension = (
|
|||
const ext = extension.startsWith('.') ? extension : `.${extension}`;
|
||||
return path.toLowerCase().indexOf(ext.toLowerCase()) > -1;
|
||||
};
|
||||
|
||||
const ALLOWED_POST_MEDIA: ReadonlyArray<{ ext: string; mime: string }> = [
|
||||
{ ext: 'png', mime: 'image/png' },
|
||||
{ ext: 'jpg', mime: 'image/jpeg' },
|
||||
{ ext: 'jpeg', mime: 'image/jpeg' },
|
||||
{ ext: 'gif', mime: 'image/gif' },
|
||||
{ ext: 'webp', mime: 'image/webp' },
|
||||
{ ext: 'mp4', mime: 'video/mp4' },
|
||||
];
|
||||
|
||||
export const VALID_POST_MEDIA_EXTENSIONS = ALLOWED_POST_MEDIA.map(
|
||||
(m) => m.ext
|
||||
);
|
||||
|
||||
export const VALID_POST_MEDIA_MIME_TYPES = new Set<string>(
|
||||
ALLOWED_POST_MEDIA.map((m) => m.mime)
|
||||
);
|
||||
|
||||
export const isValidPostMediaUrl = (
|
||||
path: string | undefined | null
|
||||
): boolean => {
|
||||
return VALID_POST_MEDIA_EXTENSIONS.some((ext) => hasExtension(path, ext));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,25 +3,20 @@ import {
|
|||
ValidatorConstraintInterface,
|
||||
ValidatorConstraint,
|
||||
} from 'class-validator';
|
||||
import { VALID_POST_MEDIA_EXTENSIONS } from './has.extension';
|
||||
|
||||
@ValidatorConstraint({ name: 'checkValidExtension', async: false })
|
||||
export class ValidUrlExtension implements ValidatorConstraintInterface {
|
||||
validate(text: string, args: ValidationArguments) {
|
||||
return (
|
||||
!!text?.split?.('?')?.[0].endsWith('.png') ||
|
||||
!!text?.split?.('?')?.[0].endsWith('.jpg') ||
|
||||
!!text?.split?.('?')?.[0].endsWith('.jpeg') ||
|
||||
!!text?.split?.('?')?.[0].endsWith('.gif') ||
|
||||
!!text?.split?.('?')?.[0].endsWith('.webp') ||
|
||||
!!text?.split?.('?')?.[0].endsWith('.mp4')
|
||||
);
|
||||
const path = text?.split?.('?')?.[0]?.toLowerCase?.();
|
||||
if (!path) return false;
|
||||
return VALID_POST_MEDIA_EXTENSIONS.some((ext) => path.endsWith('.' + ext));
|
||||
}
|
||||
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
// here you can provide default error message if validation failed
|
||||
return (
|
||||
'File must have a valid extension: .png, .jpg, .jpeg, .gif, .webp, or .mp4'
|
||||
);
|
||||
return `File must have a valid extension: ${VALID_POST_MEDIA_EXTENSIONS.map(
|
||||
(ext) => '.' + ext
|
||||
).join(', ')}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ import { Integration } from '@prisma/client';
|
|||
import { checkAuth } from '@gitroom/nestjs-libraries/chat/auth.context';
|
||||
import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation';
|
||||
import { weightedLength } from '@gitroom/helpers/utils/count.length';
|
||||
import {
|
||||
isValidPostMediaUrl,
|
||||
VALID_POST_MEDIA_EXTENSIONS,
|
||||
} from '@gitroom/helpers/utils/has.extension';
|
||||
|
||||
function countCharacters(text: string, type: string): number {
|
||||
if (type !== 'x') {
|
||||
|
|
@ -130,6 +134,19 @@ If the tools return errors, you would need to rerun it with the right parameters
|
|||
).id;
|
||||
const finalOutput = [];
|
||||
|
||||
const invalidAttachment = inputData.socialPost
|
||||
.flatMap((p) => p.postsAndComments)
|
||||
.flatMap((p) => p.attachments ?? [])
|
||||
.find((url: string) => !isValidPostMediaUrl(url));
|
||||
|
||||
if (invalidAttachment) {
|
||||
return {
|
||||
errors: `Attachment "${invalidAttachment}" is not supported. Valid extensions: ${VALID_POST_MEDIA_EXTENSIONS.map(
|
||||
(ext) => '.' + ext
|
||||
).join(', ')}.`,
|
||||
};
|
||||
}
|
||||
|
||||
const integrations = {} as Record<string, Integration>;
|
||||
for (const platform of inputData.socialPost) {
|
||||
integrations[platform.integrationId] =
|
||||
|
|
|
|||
|
|
@ -3,19 +3,16 @@ import {
|
|||
Injectable,
|
||||
PipeTransform,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
VALID_POST_MEDIA_EXTENSIONS,
|
||||
VALID_POST_MEDIA_MIME_TYPES,
|
||||
} from '@gitroom/helpers/utils/has.extension';
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { fromBuffer } = require('file-type');
|
||||
|
||||
const ALLOWED_MIME_TYPES = new Set<string>([
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
'image/avif',
|
||||
'image/bmp',
|
||||
'image/tiff',
|
||||
'video/mp4',
|
||||
]);
|
||||
const ALLOWED_EXTENSIONS_MESSAGE = `Valid extensions: ${VALID_POST_MEDIA_EXTENSIONS
|
||||
.map((ext) => '.' + ext)
|
||||
.join(', ')}`;
|
||||
|
||||
@Injectable()
|
||||
export class CustomFileValidationPipe implements PipeTransform {
|
||||
|
|
@ -34,8 +31,10 @@ export class CustomFileValidationPipe implements PipeTransform {
|
|||
}
|
||||
|
||||
const detected = await fromBuffer(value.buffer);
|
||||
if (!detected || !ALLOWED_MIME_TYPES.has(detected.mime)) {
|
||||
throw new BadRequestException('Unsupported file type.');
|
||||
if (!detected || !VALID_POST_MEDIA_MIME_TYPES.has(detected.mime)) {
|
||||
throw new BadRequestException(
|
||||
`Unsupported file type. ${ALLOWED_EXTENSIONS_MESSAGE}`
|
||||
);
|
||||
}
|
||||
|
||||
const maxSize = this.getMaxSize(detected.mime);
|
||||
|
|
@ -61,7 +60,9 @@ export class CustomFileValidationPipe implements PipeTransform {
|
|||
} else if (mimeType.startsWith('video/')) {
|
||||
return 1024 * 1024 * 1024; // 1 GB
|
||||
} else {
|
||||
throw new BadRequestException('Unsupported file type.');
|
||||
throw new BadRequestException(
|
||||
`Unsupported file type. ${ALLOWED_EXTENSIONS_MESSAGE}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue