Skip to content

feat: Notes upload workflow has no file type validation or size limit β€” any file can be uploaded to Cloudinary, causing storage abuse and broken PDF extractionΒ #456

Description

@divyanshim27

🚨 Problem Statement

VidyaSetu supports a Notes upload and extraction workflow using Cloudinary. The current upload implementation in src/ sends files directly to Cloudinary without validating:

  1. File type β€” non-PDF files (images, executables, ZIP archives) can be uploaded and will fail the PDF extraction step silently
  2. File size β€” there is no documented maximum file size; large files (100MB+) can exhaust Cloudinary free-tier storage and cause slow uploads that time out
  3. File count per user β€” no limit on how many notes a single user can upload, enabling storage abuse

Current Impact

  • A student uploading a .jpg file as notes gets no immediate error β€” the extraction pipeline fails silently later
  • Storage costs scale unpredictably with no per-user cap
  • The Cloudinary free tier (25GB storage, 25GB bandwidth/month) can be exhausted by a small number of bad uploads

Proposed Fix

In the upload API route (src/app/api/notes/upload/route.ts or equivalent):

const ALLOWED_MIME_TYPES = ['application/pdf'];
const MAX_FILE_SIZE_MB = 10;
const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024;

export async function POST(req: Request) {
  const formData = await req.formData();
  const file = formData.get('file') as File | null;

  if (!file) {
    return Response.json({ error: 'No file provided' }, { status: 400 });
  }

  // Validate MIME type
  if (!ALLOWED_MIME_TYPES.includes(file.type)) {
    return Response.json(
      { error: `Invalid file type: ${file.type}. Only PDF files are accepted.` },
      { status: 415 }
    );
  }

  // Validate file size
  if (file.size > MAX_FILE_SIZE_BYTES) {
    return Response.json(
      { error: `File too large: ${(file.size / 1024 / 1024).toFixed(1)}MB. Maximum allowed: ${MAX_FILE_SIZE_MB}MB.` },
      { status: 413 }
    );
  }

  // Proceed with Cloudinary upload...
}

Also validate MIME type from magic bytes (not just the Content-Type header, which can be spoofed):

import { fileTypeFromBuffer } from 'file-type';

const buffer = Buffer.from(await file.arrayBuffer());
const detected = await fileTypeFromBuffer(buffer);

if (!detected || detected.mime !== 'application/pdf') {
  return Response.json({ error: 'File content does not match a valid PDF.' }, { status: 415 });
}

Add file-type to package.json:

"file-type": "^19.0.0"

Files to Modify

File Change
src/app/api/notes/upload/route.ts Add MIME type + magic byte + size validation
package.json Add file-type dependency
src/ (upload UI component) Show client-side error for wrong file type before upload

πŸ“Š Impact

Without validation, any file can be uploaded to the shared Cloudinary account. A single user can upload 50 Γ— 100MB files and exhaust the storage quota for all VidyaSetu users. The fix is self-contained to the upload route and prevents both the abuse vector and the silent pipeline failure for non-PDF files.

Suggested labels: bug, security, backend, gssoc:level2

I would like to work on this. Could you please assign it to me?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions