Header Image How to Pass a Penetration Test File Upload Check

How to Pass a Penetration Test File Upload Check

Your application just failed a penetration test. The finding reads something like: "The application accepted a known malicious file without detection." The tester uploaded an EICAR test file, your app stored it without complaint, and now there's a line item in the report that needs fixing before the next retest.

This is one of the most common findings we see. It comes straight from the OWASP Web Security Testing Guide, specifically section 10.09: "Test Upload of Malicious Files." The good news is it's straightforward to fix.

What the Tester Actually Did

Penetration testers follow a standard playbook for file upload checks. Here's what they typically test:

  1. Upload a known malicious file — usually the EICAR test file, a harmless file that every antivirus engine agrees to detect
  2. Upload files with mismatched extensions — rename a .exe to .jpg and see if the app accepts it
  3. Upload files with embedded payloads — Office documents with macros, PDFs with JavaScript, archives containing executables
  4. Bypass client-side validation — disable JavaScript or modify the request to skip any browser-based checks
  5. Check if uploaded files are served to other users — can another user download the malicious file?

If your application accepted any of these without flagging them, that's a finding. The severity depends on whether the file can reach other users or systems.

Why File Type Validation Isn't Enough

The first instinct is usually to add more validation — check MIME types, verify file extensions, maybe inspect magic bytes. This helps, but it doesn't address the core finding.

Pen testers aren't testing whether your app checks file extensions. They're testing whether your app detects known malware. A .pdf that passes every type check can still contain an exploit. A .docx with valid headers can carry a macro payload. The EICAR file itself is a valid DOS executable — its file type is legitimate.

You need actual malware scanning: running files against detection engines that recognise malicious content regardless of the file's type or extension.

The Fix: Add Malware Scanning

The fix is to scan every uploaded file against malware detection engines before it's stored or served. With AttachmentScanner, this is a single API call:

curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.attachmentscanner.com/eicar.com"}' \
  -XPOST https://YOUR_API_URL/v1.0/scans
{
  "status": "found",
  "filename": "eicar.com",
  "matches": ["Eicar-Test-File-Signature"]
}

A status of found means the file was detected. Your application blocks it, the tester's check passes, and the finding is resolved.

For a full walkthrough of integrating scanning into your upload flow — including sync vs async patterns and staging areas — see our complete guide to scanning user uploads.

Setting Up for the Retest

To make sure you pass the retest, you need to verify your integration catches the same files the tester will use. Here's a checklist:

1. Test with EICAR

Every pen tester uses the EICAR test file. It's the standard — if your scanning doesn't catch EICAR, it won't catch anything.

We host a copy at https://www.attachmentscanner.com/eicar.com. Scan it through your application the same way a user would upload a file:

curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.attachmentscanner.com/eicar.com"}' \
  -XPOST https://YOUR_API_URL/v1.0/scans

You should get "status": "found". If you get "status": "ok", your integration isn't wired up correctly.

2. Test with EICAR in Archives

Testers often embed the EICAR string inside ZIP files, GZIP archives, or nested containers to see if your scanning handles compressed content. AttachmentScanner's engines detect EICAR inside archives automatically — but test it yourself to be sure.

3. Test with Mismatched Extensions

Rename a file and upload it. Your scanning should detect malicious content regardless of the file extension. This is about the scanning engines inspecting the actual content, not trusting the filename.

4. Verify Files Are Blocked or Quarantined

It's not enough to scan — your application needs to act on the result. The tester will check whether the malicious file is accessible after upload. Make sure:

  • Files with status: "found" are rejected or quarantined
  • Files with status: "pending" are not served until scanning completes
  • Files with status: "warning" (macros, encrypted archives) are handled according to your policy

5. Test the User-Facing Flow

Don't just test the API — test the full upload flow through your application's UI. The tester will use your application the way a user would. Make sure scanning is triggered regardless of how the file arrives.

For pen test compliance, we recommend async scanning with a staging area. Something like this:

User uploads Staging (pending) Scan Clean Move to uploads Malicious Quarantine + alert
// Upload handler — returns immediately
async function handleUpload(req, res) {
  const stagedPath = await storeInStaging(req.file);

  await fetch(`https://${API_URL}/v1.0/scans`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: presignedUrlFor(stagedPath),
      callback: "https://your-app.com/webhooks/scan-complete",
      async: true,
    }),
  });

  return res.status(202).json({ status: "processing" });
}

// Callback handler
async function handleScanCallback(req, res) {
  const result = req.body;

  if (result.status === "ok") {
    await moveToUploads(result.url);
  } else if (result.status === "found") {
    await moveToQuarantine(result.url, result.matches);
    await notifyAdmin(result);
  } else if (result.status === "warning") {
    await flagForReview(result.url, result.matches);
  }

  return res.status(200).end();
}

This approach gives you:

  • No unscanned files served — files stay in staging until confirmed clean
  • Audit trail — quarantined files can be reviewed and reported on
  • Evidence for the tester — you can demonstrate that malicious uploads are caught and quarantined
  • Non-blocking uploads — your app isn't tied up waiting for scan results

Common Pen Test Findings and How to Address Them

"Application accepted EICAR test file"

Fix: Add malware scanning to your upload flow. Scan every file before storing or serving it.

"Malicious file was accessible to other users after upload"

Fix: Files must not be served until scanning completes. Use a staging area and only move files to the public location after a clean scan result.

"Application only performs client-side validation"

Fix: Client-side validation is easily bypassed. All scanning and validation must happen server-side.

"Application accepts files with mismatched extensions"

Fix: Don't rely on file extensions for security. Malware scanning inspects actual file content regardless of the filename.

"No scanning of archived/compressed content"

Fix: AttachmentScanner's engines scan inside archives (ZIP, GZIP, etc.) automatically. Verify this with an EICAR file inside a ZIP.

"Uploaded macros/scripts not detected"

Fix: AttachmentScanner returns a warning status for files containing macros or encrypted archives. Decide on a policy — block them, quarantine them for review, or allow them based on your use case.

What to Include in Your Retest Evidence

When the tester comes back, having documentation ready speeds things up:

  • Architecture diagram showing the scanning flow (staging → scan → move/quarantine)
  • Test results demonstrating EICAR detection through your application
  • Code showing how scan results are handled (reject, quarantine, etc.)
  • Logs showing quarantined files from your own testing
  • Policy documentation for how you handle warning status files

Getting Started

  1. Sign up for an account — you'll get an API token and endpoint URL
  2. Test with the EICAR test file to confirm detection works
  3. Integrate async scanning with callbacks into your upload flow
  4. Run through the retest checklist before the tester returns

Most developers have this fixed within an hour. If you've got a retest coming up and need help, get in touch — we're happy to help you get it sorted.

2026-03-03
Profile Image: AttachmentScanner Team AttachmentScanner Team

Other Articles