File upload testing checklist
Fourteen checks in the order the bytes travel: what the client claims, what the server decides, what the disk holds, what the next reader gets. The order matters because the first group settles which layer is supposed to decide a file's type, and every later check assumes that question has an answer. On the endpoints these procedures were written against, one 101,941-byte upload expanded to 104,857,600 bytes, a 196-byte PNG declared 900,000,000 pixels, and a payload named avatar.php.jpg passed an allowlist and came back with an image content type. None of those requests was malformed. Each check below names the layer it interrogates and the claim it refuses to take on trust.
14 checksabout 131 min end to end
What the request itself says
- How to test multipart form data
The boundary in the header and the delimiter in the body are not the same string, and a parser bug is unreadable until you have seen the raw bytes once. - How to check if file type is validated on the server
A file name, a part header and the bytes make three separate claims about type, and a 201 tells you the server believed one of the first two. - How to check file type
The signature is the only claim the client does not control, so this is the check the three that follow depend on.
The name, which is attacker-supplied text
- How to check path traversal in an uploaded file name
The same traversal has three spellings, and on Windows the backslash form reaches places the slash form does not. - How to test uploading a file with a double extension
The allowlist reads the last extension and the server that executes the file often reads an earlier one, which is the whole defect in one sentence. - How to test a unicode file name in content disposition
The part header, the parser and the disk should agree about the bytes of a name, and here busboy read every non-ASCII name as Latin-1. - How to check file name length
Characters, UTF-8 bytes, one path component and a database column are four different limits on one name, and the smallest one truncates silently.
Size, before anything is decoded
- How to test image dimension limits on upload
A byte limit passed the 196-byte file that declares 30000 by 30000 in its header; only a pixel limit refused it. - How to check archive expansion limits on upload
An upload under every byte cap expands past every memory cap, and the capped endpoint answered 413 where the uncapped one answered 201. - How to check zip file contents
The listing names the entry called ../../../../etc/cron.d/report before an extractor acts on it, which is the last cheap moment to refuse.
What survived storage
- How to check sha256 of a file
Hash before and hash after, because a size difference points at truncation and a one-byte-per-line difference points at newline translation. - How to test an interrupted file upload
A cancelled upload leaves a fragment under the final name and a temp file nothing will remove, and the server log names both byte counts. - How to check exif data is removed from an uploaded image
Read the file the service serves back rather than the local copy, since the coordinates are public only if the stored bytes kept the APP1 segment.
What the next reader is handed
- How to check content disposition header
attachment and inline decide whether the file is downloaded or rendered in the origin, and Chrome and curl disagree about the saved name.
Take it with you
The file is the same list as Markdown checkboxes, ready to paste into a release ticket or a pull request description.
Ticks are kept in this browser only. They are not sent anywhere and other people do not see them.