Skip to content
Hidden Logic Lab

Review file upload handlers for traversal and content sniffing

Cursor or Claude Code applies a SKILL.md on upload PRs: path traversal, trusted Content-Type headers, polyglot files, and public object ACLs — with merge-blocking findings.

·Coding·35 min·Working

An upload-review SKILL.md in the repo, a PR comment stating the storage key rule and type checks, and no handler merged that writes outside the intended prefix.

  1. 1

    Write the allowed types into the skill

    One list of extensions, magic bytes, and max size. Reviewers should compare the PR to that list, not invent a new one each time.

  2. 2

    Review a real upload diff

    Include the storage key and the download route. The skill should fail a handler that trusts the browser filename.

  3. 3

    Keep public ACLs out of the default

    Document the one prefix that may be public, if any. Everything else stays private with signed reads.

The prompt to paste

Write SKILL.md for reviewing file upload handlers before merge. Focus on where bytes are stored, how the type is decided, and who can read them back. This is not an outbound URL-fetch review.

Repository context:
[upload entry points: route, signed POST, worker]
[storage: local disk, S3-compatible bucket, or both — name the prefix pattern]
[what is allowed: images, CSV, PDF — list extensions and max bytes]
[whether objects are served from the same origin as the app]

PR context (paste when using):
[PR link, changed handlers, new content types]

SKILL.md must include:
1. Purpose — a filename or content type from the client can write outside the upload prefix, or store HTML and SVG where the browser will execute them.
2. When to use — any PR that accepts multipart or raw bytes and writes an object. When not to use — fetchers that download a remote URL (use the SSRF review), or CSS-only changes.
3. Hard rules for findings:
   - Block: object key or path built from the raw client filename (../, absolute paths, null bytes); Content-Type header trusted with no server-side sniff of magic bytes; HTML, SVG, or XML stored and served with that type from the app origin; bucket or blob ACL public-read without an explicit product requirement in the PR; archive extraction (zip, tar) that writes entries with their internal paths (zip slip).
   - Warn: size limit enforced only in the browser; image libraries that follow URLs embedded in the file; original filename used in Content-Disposition without stripping; overwrite of an existing key when the caller does not own it; antivirus or type check skipped when the storage SDK returns success.
   - Note: progress-bar UX and client preview — never block merge on those alone.
4. Review steps the agent must execute:
   - Find the function that chooses the storage key. Confirm it is a generated id plus a fixed prefix, not the user filename.
   - Record the allowlist of types and the code that checks bytes, not just the header.
   - Trace the read path: auth on download, Content-Disposition, and whether the object is ever served as active content.
   - If archives are accepted, confirm extraction stays inside a fresh directory and rejects links.
   - Note the ACL or signed-URL TTL. Flag public objects that contain user documents.
5. PR comment template: key rule, type check (header vs bytes), read-path auth, ACL, findings, merge recommendation.
6. Safe patterns section: random object keys, magic-byte allowlist, Content-Disposition attachment for anything that is not a known raster image, private bucket plus short-lived signed URLs.
7. Two example prompts: add CSV import vs serve user SVG logos on the marketing origin.
8. Pitfalls: renaming .html to .png without sniffing, assuming the CDN strips scripts, testing only the happy-path JPEG, treating a signed upload as safe when the key is still client-chosen.
9. Done means: every Block resolved, storage key rule written in the PR comment, no live customer files pasted into the review.

Rules:
- Do not invent buckets or routes that are not in the diff or repository context.
- Do not include exploit file samples. Describe the check that should fail.
- Output only SKILL.md markdown.

A random key does not help if the object is served as image/svg+xml or text/html on your origin. The skill must Block active content types even when the path is safe.

Sample case written by Hidden Logic Lab. Not affiliated with Cursor or Claude Code.

More in Coding

Get new cases in your inbox

Weekly notes on new use cases and agent workflows. No product pitches.

Occasional notes on new cases. No product pitches in the first note.