Repository navigation
Download pasted RTD images in the browser instead of on the server - #227
Conversation
|
@webharerob decodeImageDataURL is added as internal api - could be moved to @webhare/upload next to getFileAsDataURL and then also used to replace dataURItoBlob in imgeditcontroller.ts. worth it? |
There was a problem hiding this comment.
🟡 Changes recommended
The paste handler can discard valid images, retain unused files and object URLs, and buffer oversized downloads before enforcing the size limit.
3 open issues
What changed in this PR
Moves pasted RTD image downloads into the browser and reuses the existing upload service, removing the unauthenticated server-side URL-fetching endpoint that enabled SSRF.
Changes:
- Adds browser-side image validation, direct data URL decoding, and clipboard-file handling.
- Preserves pasted image dimensions during upload and supports WebP/AVIF image-cache URLs.
- Fixes invalid data URL cleanup and adds paste regression tests.
| File | Description |
|---|---|
whtree/modules/webhare_testsuite/tests/tollium/richdoc/teststructured.ts |
Tests rejected images and PNG/WebP clipboard imports. |
whtree/modules/tollium/web/ui/components/richeditor/richeditor.scss |
Prevents upload spinners from stretching. |
whtree/modules/tollium/web/ui/components/richeditor/internal/structurededitor.tsx |
Prefers clipboard image files and preserves styled dimensions. |
whtree/modules/tollium/web/ui/components/richeditor/internal/editorbase.tsx |
Downloads, validates, and uploads pasted images in the browser. |
whtree/modules/publisher/lib/internal/rtd/service.whlib |
Discards invalid and non-image data URLs before storage. |
whtree/modules/publisher/lib/internal/forms/rpc.whlib |
Removes remote fetching and routes WebP/AVIF uploads through the image cache. |
whtree/jssdk/forms/src/formservice.ts |
Removes the remote-image download method from the service interface. |
Review effort: Balanced
There was a problem hiding this comment.
🤖 Auto-Approved: PR author (@unilynx) is an internal organization/repository member.
will not change that yet |
There was a problem hiding this comment.
🟡 Changes recommended
The preliminary length guard incorrectly applies base64 limits to percent-encoded payloads, causing valid images below the 20 MiB decoded-size limit to be silently removed on paste.
6 open issues
Allow for percent-encoding expansion in the image size guard · New Percent-encoded images are rejected by the base64 length limit · New Base64 length bound rejects valid percent-encoded images · New Revoke unconsumed clipboard image URLs Enforce the image size limit while streaming downloads Decode percent-encoded image data directly into bytes
Review effort: Balanced
09da0ac to
0742c19
Compare
The RTD imported pasted images by asking the server to fetch their URLs (getImgFromRemoteURL), which let any client make the server fetch arbitrary URLs. The browser now downloads pasted images itself and uploads them through getUploadedFileFinalURL, and the server-side fetch is removed. - data: URLs are decoded directly as the CSP generally blocks fetching them - When 'Copy image' offers both a remote URL and the image file, use the file - Accept png, jpeg, gif, webp and avif, max 20MB, and verify they decode - Upload placeholders take the pasted image's dimensions - Server-side RTD cleanup now properly discards invalid or non-image data: URLs
0742c19 to
a086296
Compare
There was a problem hiding this comment.
🤖 Auto-Approved: PR author (@unilynx) is an internal organization/repository member.

Why
When content with images is pasted into an RTD, the editor asked the server to download each image URL through
getImgFromRemoteURLon the publisher form service. That service is reachable without authentication, so anyone could make the server fetch arbitrary URLs (SSRF), with only weak port and credential checks in place.What changes
getUploadedFileFinalURL, the same path the image upload button uses.RPC_GetImgFromRemoteURLand its TypeScript interface entry are removed.data:URLs are decoded directly instead of fetched, because CSPconnect-src(eg. in the backend) blocksfetch()ofdata:/blob:.<img>and one image file. This avoids needing CORS on the remote URL.createImageBitmap). Fetches usecredentials: "omit"andreferrerPolicy: "strict-origin".width/heightattributes or pixelstyledimensions. The spinner isn't stretched (object-fit: none).getUploadedFileFinalURLtreats webp/avif as images (image cache URL instead of a file URL).ValidateAndCleanupImage: non-imagedata:URLs were disposed but then still stored, and invaliddata:URLs weren't handled. Both are now discarded.Known limitations
TODOmarks where the editor should tell the user how many images couldn't be copied.connect-src 'self' https://*.googleapis.com, so cross-originhttps:image URLs in pasted HTML will be dropped there unlessconnect-srcis widened. Same-origin images,data:URLs and clipboard image files work.Tests
tollium.richdoc.teststructured: unusable pasted images are rejected (non-imagedata:URL, corrupt png, 404,file:URL), and png and webp clipboard files are imported when the HTML's remote URL can't be fetched.tollium.richdoc.teststructured,tollium.richdoc.testfree,tollium.richdoc.testrteservice,wh.jscomponents.rte.teststructured,wh.jscomponents.rte.teststructured-copypaste,wh.jscomponents.rte.testbase,publisher.forms.testrteandpublisher.forms.testuploadfield.