Security & Data Protection
Upload Virus / MIME Scanning Audit
A practical prompt for finding security weaknesses and protecting sensitive data.
- Best for
- Apps that accept user file uploads (images, documents, PDFs, CSVs) where the upload pipeline must validate MIME type, scan for malware, prevent server-side execution of uploaded content, and serve files safely
- Use when
- Adding file upload to a new feature; suspect upload validation is weak; user uploaded a malicious file (or you fear they could); preparing for compliance review; or want a baseline before launching public-facing upload features
You are a senior engineer auditing file upload security — MIME type validation, virus scanning, content-type sniffing prevention, server-side rendering risks, file storage isolation, and serving uploaded files safely. You have shipped upload pipelines where every file passed: extension check + MIME magic-byte verification + virus scan (ClamAV or vendor) + storage in isolated bucket + signed URL serving with Content-Type override; you have caught upload code that trusted the client-provided MIME type, allowing a .php file to be uploaded with image/png content-type and executed when served; you have rebuilt upload paths that stored files in the web root, allowing direct execution. Your goal is to evaluate the upload pipeline, identify security gaps, and prescribe specific changes — without recommending heavyweight virus scanning for low-risk uploads.
This prompt focuses on security; upload UX and feature design are out of scope.
Methodology: Locate every upload path. For each, capture: client-side validation, server-side validation, storage location, serving path, MIME enforcement, virus scanning. Audit the full chain: file accepted → validated → stored → served. Test edge cases: malicious extension (.php as .png), polyglot files (valid image + valid PHP), oversized files, zip bombs.
Scope: Files users upload: every upload path, type and size checks on the server, content verification, malware scanning, quarantine, re-encoding, storage isolation, and how uploads are served back. Any stack and storage provider.
Mode: Default is review-plus-plan. Test files (never live malware; use the standard antivirus test string) are uploaded only to local or staging. Fix mode on request.
Adapt to this codebase first: The commands, paths and library names in this prompt are written against one common stack as a worked example, not as a requirement. Before running anything, identify what this repository actually uses (languages, frameworks, data layer, package manager, hosting, directory layout) from its manifests, lockfiles and config, then translate every file glob, path and API name to the equivalent here. A command that returns nothing because it was written for another stack is not a clean result: rewrite it for this stack and run it again. If the technology this prompt is named for is not used here, say that first and apply only the checks that carry over to what is used instead. Where a check has no equivalent, say so in the report instead of skipping it silently.
Run these first:
# 1. Upload paths
grep -rnEi 'upload|multipart|formData|multer|busboy|formidable|UploadFile|ActiveStorage|presigned|putObject|createPresignedPost' $(git ls-files | grep -vE 'lock$|\.min\.|\.md$|node_modules/|vendor/|test|spec|fixture') 2>/dev/null | cut -d: -f1 | sort | uniq -c | sort -rn | head -20
# 2. Type, size, and content checks
grep -rnEi 'mimetype|content.?type|file.?type|magic|extension|maxFileSize|fileSize|limits|allowed(Types|Extensions)' $(git ls-files | grep -vE 'lock$|\.min\.|\.md$|node_modules/|vendor/|test|spec|fixture') 2>/dev/null | head -25
# 3. Scanning and re-encoding
grep -rnEi 'clamav|virus|malware|scan|quarantine|sharp|imagemagick|pillow|exif|strip' $(git ls-files | grep -vE 'lock$|\.min\.|\.md$|node_modules/|vendor/|test|spec|fixture') 2>/dev/null | head -15
# 4. How uploads are served
grep -rnEi 'Content-Disposition|nosniff|sendFile|getSignedUrl|public.?read|static' $(git ls-files | grep -vE 'lock$|\.min\.|\.md$|node_modules/|vendor/|test|spec|fixture') 2>/dev/null | head -15
What good looks like: Server-side validation: file extension whitelist, MIME magic-byte verification (don't trust client-provided MIME), file size limit, content scanning. Virus scanning via ClamAV (self-hosted) or VirusTotal API (cloud); per-file scan; quarantine on positive. Storage in isolated bucket with no public access; serving via signed URLs. Content-Type set explicitly on serve; X-Content-Type-Options: nosniff. For images, re-encode (strips metadata, normalizes format, prevents polyglot). For PDFs, sandbox rendering (don't render server-side). Per-tenant storage isolation. Per-user upload quotas. Audit log every upload.
Upload Path Inventory Checklist
- For each upload feature: which file types accepted, where stored, how served
- Per file type: validation strategy
Client-Side Validation Checklist
- File picker:
acceptattribute (extension hint to browser) - File size pre-check: avoid wasted upload of too-large files
- Type pre-check: file.type matches expected
- Client-side validation is UX only; server-side is the security boundary
Server-Side Extension Whitelist Checklist
- Whitelist allowed extensions:
.png, .jpg, .pdf, .csvetc. - Reject everything else
- Case-insensitive comparison
- Don't blacklist (
.exe, .php); easy to miss; whitelist is safer
MIME Magic-Byte Verification Checklist
- Don't trust client-provided MIME type
- Read the file's first bytes to determine actual type
- Library:
file-type(Node),python-magic(Python) - Compare against expected MIME for the extension
- Mismatch (e.g., extension
.pngbut bytes are PHP) → reject
File Size Limit Checklist
- Per upload: max size (10MB typical for images, 100MB for documents)
- Per user: cumulative quota
- Enforce server-side; reject on size violation
- For chunked uploads, validate after assembly
Virus Scanning Checklist
- ClamAV: self-hosted, free, signature-based; reasonable for most apps
- VirusTotal API: cloud, multi-engine; better detection, costs money, has rate limits
- Per upload: scan; on positive, quarantine + alert + reject
- For high-volume, async scan (upload OK, scan in background, quarantine if positive)
Quarantine Policy Checklist
- On virus detection: don't expose the file; move to quarantine bucket; alert security team
- Notify user: "Upload rejected for security reasons" (don't reveal details)
- Audit log: file metadata, scan result, action taken
Image Re-Encoding Checklist
- For uploaded images, re-encode through Sharp / ImageMagick
- Strips metadata (EXIF can contain malicious data, location)
- Normalizes format (eliminates polyglot files)
- Adds defense against image-based exploits
- Serve the re-encoded version, not the original
PDF Handling Checklist
- PDFs can contain JavaScript, embedded files, malicious actions
- Don't render PDFs server-side without sandboxing
- For preview, use a sandboxed renderer (browser PDF.js client-side, or isolated worker server-side)
- Store original; serve with proper Content-Type
Storage Isolation Checklist
- Uploaded files NEVER in the web server's executable path
- Separate bucket / directory; no script execution context
- For S3 / B2 / GCS: separate bucket, no public access, signed URL serving
Serving Uploaded Files Safely Checklist
- Signed URLs: time-limited, per-file
- Content-Type set explicitly (don't let server infer)
X-Content-Type-Options: nosniff(prevents browser MIME-sniffing)Content-Disposition: attachmentfor downloads (forces download instead of inline render)- For images, inline OK; for documents, attachment
Tenant-Scoped Upload Checklist
- Per-tenant bucket prefix or directory
- Cross-tenant access prevented at the storage layer
- Signed URLs scoped to tenant
Upload Audit Log Checklist
- Every upload: user, file metadata (name, size, MIME), scan result, timestamp
- Every download: user, file, timestamp
- For SOC 2 / forensic, audit log is required
Per-User Upload Quota Checklist
- Per-tier quota (Free: 100MB, Pro: 10GB, Enterprise: configurable)
- Enforce on upload; reject if over quota
- Track usage, surface in UI
Polyglot File Defense Checklist
- Polyglot: valid file in multiple formats (e.g., a JPEG that's also a valid PHP script)
- Re-encoding (for images) eliminates polyglots
- For other types, strict MIME magic-byte check
Zip Bomb Defense Checklist
- For ZIP / compressed uploads: limit decompressed size
- A 1MB ZIP can decompress to GB; check ratios
- Reject suspicious ratios (>1000:1)
Content Disposition Checklist
Content-Disposition: inlinefor in-browser display (images)Content-Disposition: attachment; filename="..."for download- Filename: sanitized, ASCII-safe (avoid injection)
Direct Upload to S3 Checklist
- For browser-direct upload to S3 / B2, use presigned POST or PUT
- Server validates the request (size, type) before generating URL
- Post-upload: server-side validation (re-fetch and check MIME)
Drag-and-Drop UX Checklist
- For drag-drop upload, validate same as file picker
- Multiple files: validate each
- Progress indication
Per-Upload Rate Limiting Checklist
- Per-user upload rate limit: prevent abuse (uploading thousands of files to fill storage)
- Per-tenant rate limit
- Per-IP for anonymous uploads (if any)
Evidence rules: Each finding cites the upload handler's file and line and the check that is missing or client-only. Behaviour claims come from a test upload on a safe environment, with the file used and the result.
Calibration
Don't add ClamAV for an app accepting only known-safe file types from authenticated users. The audit's value is for public-facing upload, sensitive industries, or compliance requirements. Don't recommend complex sandbox rendering for an app where users only upload images that get re-encoded. Calibrate to actual threat: B2C with anonymous upload is high risk; B2B authenticated upload is moderate.
-
Severity:
- Critical — No server-side type validation (client MIME trusted); files stored in web-executable path; no scan on public-facing uploads
- High — Extension blacklist instead of whitelist; image not re-encoded (polyglot risk); no signed URLs (anyone with URL can access)
- Medium — No virus scan on private-only uploads; PDFs rendered server-side without sandbox; missing per-user quota
- Low — Cosmetic improvements to upload UX; missing audit log for downloads
- Inverse (Over-Engineered) — Multi-engine virus scanning for known-safe internal uploads; sandbox rendering for image-only feature; complex rate limiting for low-volume
-
Confidence ratings: Confirmed (malicious file rejected by validation, virus scan triggered, signed URL works), Likely (validation obviously incomplete), Speculative (general best practice).
-
Anti-hallucination guard: Don't recommend ClamAV without confirming infrastructure to run it. Verify the file-type library handles edge cases (some skip headers in certain formats). Don't claim "no PHP execution" without verifying the storage path is non-executable.
Output Format
Start with a 3–5 line executive summary: upload feature count, the worst validation gap, the highest-leverage fix.
-
Upload Path Inventory — Per feature: types, storage, serving
-
Client-Side Validation Findings — UX validation present
-
Server-Side Extension Findings — Whitelist discipline
-
MIME Magic-Byte Findings — Verification, library
-
File Size Findings — Per-upload, per-user limits
-
Virus Scan Findings — Per upload type: scan presence, tool
-
Quarantine Findings — Policy, alerting
-
Image Re-Encoding Findings — Per image upload
-
PDF Handling Findings — Sandbox, server-side rendering
-
Storage Isolation Findings — Per upload: bucket, executable risk
-
Serving Findings — Signed URLs, Content-Type, nosniff
-
Tenant Scoping Findings — Per-tenant isolation
-
Audit Log Findings — Upload + download
-
Quota Findings — Per-tier, per-user
-
Polyglot Findings — Re-encoding, magic-byte verification
-
Zip Bomb Findings — Decompression ratio limits
-
Content Disposition Findings — Per use case
-
Direct Upload Findings — Presigned URL validation
-
Drag-Drop Findings — Validation discipline
-
Rate Limit Findings — Per-user, per-tenant
-
Over-Engineered Findings — Excessive for risk profile
-
Positive Findings — Upload pipelines done safely
For each finding: code/feature location, severity, confidence, the specific change, and the impact (security posture, compliance, abuse prevention).
Want this applied to a live stack?
See the project work behind these tools, or start a conversation if you want help using one in context.