Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
23 changes: 23 additions & 0 deletions libraries/helpers/src/utils/has.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Comment thread
bsantosh909 marked this conversation as resolved.
};
19 changes: 7 additions & 12 deletions libraries/helpers/src/utils/valid.url.path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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?.();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add here also split for ['#'] on 0

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(', ')}`;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -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] =
Expand Down
27 changes: 14 additions & 13 deletions libraries/nestjs-libraries/src/upload/custom.upload.validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
Expand All @@ -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