Files
struts/docs/superpowers/specs
Lukasz Lenart 833220346c WW-5474 Count files only for maxFiles, add maxParameterCount (#1806)
* WW-5474 docs(multipart): design for files-only maxFiles + maxParameterCount

Spec for correcting struts.multipart.maxFiles to count file parts only
(consistently across the jakarta and jakarta-stream parsers) and adding
struts.multipart.maxParameterCount to cap non-file form fields, restoring
the DoS guard the old accidental total-part cap provided. Fail-closed on
breach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 docs(multipart): implementation plan for maxFiles/maxParameterCount

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 fix(multipart): count files only for maxFiles, add maxParameterCount (jakarta)

The jakarta parser passed maxFiles to commons-fileupload2 setMaxFileCount,
which counts every part (fields + files), so maxFiles wrongly limited total
parameters. Enforce a files-only count and non-file field count in Struts,
failing closed on breach; keep a total-parts commons backstop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 fix(multipart): honor -1 unlimited sentinel in total-parts backstop

prepareServletFileUpload applied the total-parts backstop whenever both
maxFiles and maxParameterCount were non-null, without checking for the
-1 "unlimited" sentinel already honored by enforceMaxFiles/enforceMaxParameterCount.
With maxFiles=-1 and maxParameterCount=256, maxParts computed to 255 and
was passed to commons-fileupload2's setMaxFileCount (which counts ALL
parts), wrongly rejecting large file-only uploads. Only apply the
backstop when both limits are finite (non-null and >= 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 fix(multipart): apply files-only maxFiles + maxParameterCount to stream parser

Replace the field-name-based exceedsMaxFiles with the shared files-only
enforcement and add parameter-count enforcement, matching the jakarta parser
and failing closed on breach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 fix(multipart): track all parsed items to avoid temp-file leak on fail-closed breach

servletFileUpload.parseRequest() fully materializes every part - spilling
large ones to disk - before processUpload() iterates over the result. The
loop only added each DiskFileItem to diskFileItems as it was reached, so
when enforceMaxFiles/enforceMaxParameterCount threw mid-loop on a breach,
every item positioned after the breaching one was never registered for
cleanup. With no FileCleaningTracker on the factory, cleanUp() had no way
to reclaim those temp files, leaking disk space on the hardening path.

Materialize the parsed list once and register all items for cleanup
before processing so cleanUp() reclaims every temp file regardless of
where enforcement aborts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 fix(multipart): guard debug logging in enforce helpers (Sonar S2629)

Wrap the LOG.debug calls in enforceMaxFiles/enforceMaxParameterCount with
isDebugEnabled() so normalizeSpace() is not evaluated when debug is disabled,
matching the exceedsMaxStringLength pattern in the same class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 fix(multipart): address Copilot review - parser parity + overflow guard

- JakartaMultiPartRequest: only count/enforce a file part toward maxFiles when it
  has a non-null field name, matching JakartaStreamMultiPartRequest's accept criteria
  (defensive: commons-fileupload2 already drops parts without a name attribute before
  parseRequest returns, so the two parsers stay consistent regardless).
- AbstractMultiPartRequest: compute the total-parts backstop with Math.addExact and
  clamp to Long.MAX_VALUE on overflow, so extremely large configured limits cannot
  wrap negative and silently disable the commons backstop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-5474 fix(multipart): log "processing a form field" only for form fields

Move the debug log into the isFormField branch so file parts are not
mislabelled; the file branch already logs "Processing a file".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 04:57:19 +02:00
..