Skip to content

[DEFECT] /dA/ shorty URLs treat filenames like 500w.jpg or 533637w.pdf as resize filters; on libvips an uncapped PDF resize can OOM the pod #38003

Open

Description

@danielsolis-dotcms

Problem Statement

ShortyServlet applies its transform regexes (/(\d+)w\b, h, cw, ch, q, maxw, minw, …) to the whole lowercased URI, and everything after the field name is treated as a filter. A real filename that happens to look like a token is therefore parsed as a transform:

  1. Wrong bytes served. An image named 500w.jpg requested via its direct link comes back resized to 500px wide and re-encoded. The visitor never gets the file they asked for.
  2. Non-images enter the image pipeline. A PDF named 300w.pdf is routed to /contentAsset/image because width > 0 sets isImage, and is rasterized to a PNG instead of served raw. ImageUriPathExpander re-applies the same patterns to each token, filename included, and appends /resize_w/N.
  3. Uncapped libvips resize can crash the pod. VipsResizeImageFilter sizes the output with ResizeCalc and calls thumbnailImage(..., SIZE_FORCE) without the IMAGE_MAX_PIXEL_SIZE (default 5000) clamp that VipsImageFilterApiImpl and the legacy ImageFilterApiImpl apply. A name like 533637w.pdf requests a resize to 533,637px wide on a PDF rendered to an image. libvips allocates natively outside the JVM heap, so the container hits its memory limit and the kernel OOM-kills the process. The libvips→legacy fallback never runs. IMAGE_GENERATION_SIMULTANEOUS_REQUESTS still allows several concurrent renders.

Impact: An environment on libvips can go into repeated OOM crash loops. A request that exceeds the load balancer timeout gets retried by the CDN, so renders pile up. Workarounds are renaming the asset or disabling libvips (DOT_IMAGE_API_USE_LIBVIPS=false), and the second one removes AVIF output. The h, cw, ch, maxw and minw patterns are affected the same way.

Steps to Reproduce

Local build 1.0.0-SNAPSHOT (built 2026-10-10), default image engine settings. The production environment ran 26.09.02.

  1. Create a content type with two binary fields (file1, diagram1).
  2. Upload a 1200×800 JPEG named 500w.jpg to diagram1, and a 1-page PDF named 300w.pdf to file1. Upload a control PDF named plain.pdf to file1. Publish all three.
  3. Request each direct link with no transform tokens:
    • /dA/<id>/diagram1/500w.jpg → image/png, 500×333, 215,826 bytes. The original is a 52,974-byte, 1200×800 JPEG.
    • /dA/<id>/file1/300w.pdf → image/png, 300×200. The original is a PDF.
    • /dA/<id>/file1/plain.pdf → application/pdf, hash identical to the upload. This is the control.
  4. (Memory impact) On libvips, request a PDF with a very large leading number, e.g. /dA/<id>/file1/533637w.pdf. A render of this kind exhausts native memory. Production thread dumps show the request threads in VipsResizeImageFilter → vips_cache_operation_build, with a heap of 0.7 GiB and a container at the 14 GiB limit.

Expected: a filename is never parsed as a transform token, and non-image assets are served raw. Transform size is always clamped, whichever engine is configured.
Actual: as in step 3.

Acceptance Criteria

  • An asset whose filename matches a token pattern (500w.jpg, 533637w.pdf, 300h.png, and the cw, ch, q, maxw, minw, ro forms), requested through its direct /dA/<id>/<field>/<filename> link, is served untransformed, with the original bytes and content type.
  • A non-image binary (e.g. a PDF) is never routed to /contentAsset/image because of its filename.
  • Every documented transform URL form still behaves as before (/500w/photo.jpg, /webp, /jpeg, /jpegp, /avif, /smart, NNw, NNh, NNq, NNcw, NNch, NNmaxw, NNmaxh, NNminw, NNminh, NNro, fp), byte-for-byte for CDN-cached URLs.
  • VipsResizeImageFilter (and any other libvips resize path) honours IMAGE_MAX_PIXEL_SIZE. A single request with an oversized target cannot allocate unbounded native memory.
  • Behaviour is identical under the default libvips engine and IMAGE_API_USE_LIBVIPS=false.
  • Regression tests cover a filename/token collision for numeric tokens, alongside the format-token cases in [DEFECT] Asset filenames starting with an image format token (webp-, jpeg-, avif-) are silently converted on /dA/ links #37534.

dotCMS Version

Reproduced on a local 1.0.0-SNAPSHOT build (2026-10-10). Observed in production on 26.09.02 and, per infrastructure metrics, already present on 26.08.19-04. Code is present on main.

Severity

High - Major functionality broken

Notes for whoever picks this up

  • Relevant code: ShortyServlet.java (the widthPattern / heightPattern definitions and isImage at ~L358), ImageUriPathExpander.java, vips/VipsResizeImageFilter.java.
  • Possible directions, none prescribed: treat a trailing segment that has a file extension as the filename, never as a filter (/500w/photo.jpg still works); only route to the image path for image binaries; clamp libvips resizes and limit concurrency. A change that breaks the /<field>/NNw form would need a migration path, because those URLs are public and CDN-cached.
  • Byte-range requests skip the resize path, which is why partial PDF downloads kept working in production.

Links

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions