agentggagentgg
Back to all findings
MEDIUMuncertainfile-upload-validationmissing-upload-size-limit56ceee2f8c4d

Profile image upload has no file size limit

The profile image upload handler validates MIME via magic number and uses an opaque filename, but enforces no size limit before buffering and writing the file to a publicly served directory.

Fileroutes/profileImageFileUpload.ts
Lines1646
Confidence
60%
File statusvalidated
Details

The handler in profileImageFileUpload() reads req.file.buffer (multer memory storage) and passes it to fs.writeFile at frontend/dist/frontend/assets/public/images/uploads/${loggedInUser.data.id}.${uploadedFileType.ext}. The file is then linked from the user's profileImage column and served as a static asset.

Validation present:

  • Magic‑number/MIME check via file-type.fromBuffer plus a startsWith('image') allowlist.
  • Opaque storage path (filename is the user id, not originalname), defeating path traversal and double‑extension tricks.

Validation missing:

  • No per‑file size cap is set in this handler, and the file is fully buffered in memory (req.file.buffer) before being written. Without a multer({ limits: { fileSize: ... } }) configuration on the route, an authenticated attacker can submit arbitrarily large payloads and either fill the disk under frontend/dist/frontend/assets/public/images/uploads/ or exhaust Node process memory while the buffer is held.

Because the uploaded artifact is subsequently served as a static asset, the criteria for flagging a single missing control (size limit) are met.

Note: the image/* allowlist still admits image/svg+xml if file-type recognises it, which would be stored XSS when fetched from the assets path — but the primary, certain issue here is the missing size/quantity limit.

const filePath = `frontend/dist/frontend/assets/public/images/uploads/${loggedInUser.data.id}.${uploadedFileType.ext}`
try {
  await fs.writeFile(filePath, buffer)
} catch (err) { ... }
Proof of concept
  1. Authenticate and obtain a session cookie/token.
  2. POST a multipart/form-data request to the profile-image endpoint where the file part is a very large (e.g., several GB) buffer whose first bytes are a valid PNG magic header followed by junk:

printf '\x89PNG\r\n\x1a\n' > big.png && dd if=/dev/zero bs=1M count=5000 >> big.png curl -b token=$T -F file=@big.png http://target/profile/image/file

  1. The server buffers the whole body in memory and writes it under frontend/dist/frontend/assets/public/images/uploads/<userId>.png. Repeating the request (or sending one large request per user) exhausts disk and/or Node heap.
Impact

Authenticated users can cause denial of service via disk exhaustion (files are written to a static asset directory that is also part of the built frontend) and memory pressure (multer memory storage holds the entire upload in RAM before the write). No size or request-rate guard exists on this handler.

Validation
uncertain

Size limits for multer-based uploads are configured on the middleware at route registration (e.g., multer({ limits: { fileSize: ... } })), not inside the handler shown. The detector's claim "no size limit" is based on the absence of such config in this file, but the relevant config lives in the route/server setup file, which was not examined or quoted. Without inspecting the route mount for profileImageFileUpload(), the assertion that there is no limits.fileSize is unproven, and the PoC's assumption that multer will buffer GBs of data cannot be validated. Verdict is uncertain.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Base score: 6.5 · MEDIUM

The endpoint is reachable over HTTP (AV:N) and requires only an authenticated session — security.authenticatedUsers.get(req.cookies.token) is checked but any logged-in user qualifies (PR:L). No race or special preconditions are needed (AC:L) and no victim action is required (UI:N). The handler accepts req.file.buffer from multer memory storage and calls fs.writeFile with no limits.fileSize configuration, so an attacker can submit arbitrarily large uploads to exhaust Node heap and/or fill the disk under frontend/dist/.../uploads/, yielding sustained denial of service (A:H). No confidentiality or integrity impact is demonstrated by the missing size-limit issue itself, and the impact stays within the same Node application (S:U).

References