Tusflow

File Validation

The file validation middleware ensures that uploaded files meet security requirements and content restrictions. It validates file types and sizes during the upload process.

Configuration

The file validation middleware uses a configuration object defined in @/config:

export const FILE_VALIDATION = {
    // Enable/disable file type validation
    ENABLE_TYPE_VALIDATION: true,
    // Allowed file extensions (set to null or empty array to allow all types)
    ALLOWED_FILE_TYPES: [
        '.pdf',
        '.doc',
        '.docx',
        '.txt',
        '.jpg',
        '.jpeg',
        '.png',
        '.mp4',
    ],
    // File size limits
    MAX_FILE_SIZE: 100 * 1024 * 1024, // 100MB
    MIN_FILE_SIZE: 1024, // 1KB
};

Implementation

The middleware is implemented as a Hono middleware function that validates file uploads:

import { ERROR_MESSAGES, FILE_VALIDATION } from '@/config';
import { ValidationError } from '@/utils/error/customErrors';
import { type Context, type Next } from 'hono';
import path from 'path';
 
export const validateFileUpload = () => {
    return async (c: Context, next: Next) => {
        if (c.req.method === 'POST') {
            const metadata = c.req.header('Upload-Metadata');
            const uploadLength = c.req.header('Upload-Length');
            const fileSize = uploadLength ? parseInt(uploadLength) : null;
 
            // Validate metadata and file type
            if (metadata) {
                const metadataPairs = metadata.split(',').reduce(
                    (acc, pair) => {
                        const [key, value] = pair.trim().split(' ');
                        acc[key] = Buffer.from(value, 'base64').toString();
                        return acc;
                    },
                    {} as Record<string, string>
                );
 
                const filename = metadataPairs['filename'];
 
                // File type validation
                if (FILE_VALIDATION.ENABLE_TYPE_VALIDATION && filename) {
                    const fileExt = path.extname(filename).toLowerCase();
                    const allowedTypes = FILE_VALIDATION.ALLOWED_FILE_TYPES;
 
                    if (
                        allowedTypes &&
                        allowedTypes.length > 0 &&
                        !allowedTypes.includes(fileExt)
                    ) {
                        throw new ValidationError(
                            ERROR_MESSAGES.FILE_VALIDATION.INVALID_FILE_TYPE,
                            {
                                statusCode: 415,
                                errorCode: 'INVALID_FILE_TYPE',
                                details: {
                                    allowedTypes:
                                        FILE_VALIDATION.ALLOWED_FILE_TYPES,
                                    providedType: fileExt,
                                },
                            }
                        );
                    }
                }
            }
 
            // File size validation
            if (fileSize !== null) {
                const maxSize = FILE_VALIDATION.MAX_FILE_SIZE;
                const minSize = FILE_VALIDATION.MIN_FILE_SIZE;
 
                if (maxSize && fileSize > maxSize) {
                    throw new ValidationError(
                        ERROR_MESSAGES.FILE_VALIDATION.FILE_SIZE_EXCEEDED,
                        {
                            statusCode: 413,
                            errorCode: 'FILE_TOO_LARGE',
                            details: {
                                maxSize,
                                providedSize: fileSize,
                            },
                        }
                    );
                }
 
                if (minSize && fileSize < minSize) {
                    throw new ValidationError(
                        ERROR_MESSAGES.FILE_VALIDATION.FILE_SIZE_BELOW,
                        {
                            statusCode: 413,
                            errorCode: 'FILE_TOO_SMALL',
                            details: {
                                minSize,
                                providedSize: fileSize,
                            },
                        }
                    );
                }
            }
        }
 
        await next();
    };
};

Usage

The middleware is applied globally in the application:

import { validateFileUpload } from '@/middleware';
 
const app = new Hono<{ Bindings: Bindings }>();
 
// Apply file validation middleware
app.use('*', validateFileUpload());

Validation Checks

File Type Validation

  • Validates file extensions against the allowed types list
  • Can be enabled/disabled via ENABLE_TYPE_VALIDATION
  • Returns 415 status code for invalid file types

File Size Validation

  • Enforces minimum and maximum file size limits
  • Validates against MAX_FILE_SIZE and MIN_FILE_SIZE
  • Returns 413 status code for size violations

Error Responses

The middleware returns standardized error responses:

Invalid File Type

{
    "error": {
        "message": "Invalid file type",
        "statusCode": 415,
        "errorCode": "INVALID_FILE_TYPE",
        "details": {
            "allowedTypes": [".pdf", ".doc", ".docx", ...],
            "providedType": ".exe"
        }
    }
}

File Size Exceeded

{
    "error": {
        "message": "File size exceeded",
        "statusCode": 413,
        "errorCode": "FILE_TOO_LARGE",
        "details": {
            "maxSize": 104857600,
            "providedSize": 157286400
        }
    }
}

Best Practices

  1. Configuration

    • Keep file type restrictions up to date
    • Set appropriate size limits based on your storage capacity
    • Consider your application's specific needs when enabling/disabling validations
  2. Security

    • Always validate both file size and type
    • Keep the allowed file types list as restrictive as possible
    • Consider adding additional validation for file content
  3. Error Handling

    • Use descriptive error messages
    • Include detailed error information in responses
    • Log validation failures for monitoring
  4. Performance

    • Validate file metadata before processing uploads
    • Use early returns to fail fast
    • Keep validation logic simple and efficient
Edit on GitHub

Last updated on

On this page