cloudinary / cloudinary/cloudinary_gem
Images uploaded with wrong extension trigger ActiveStorage::IntegrityError
- Vorherrschende Sprache
- Ruby
- Sterne
- 420
- Forks
- 285
- PR-Merge-Kennzahlen
- Keine gemergten PRs in 30 T.
Beschreibung
## Describe the bug in a sentence or two.
When using Rails [Direct Uploads](https://guides.rubyonrails.org/active_storage_overview.html#direct-uploads), we can't analyze a file before it reaches Cloudinary. If a user uploads an image with the wrong extension (say a JPEG named `image.png`), then Cloudinary will return an URL ending with `.png`, which requests a transformation of the image to PNG, resulting in a checksum that does not match the one stored in the associated `ActiveStorage::Blob`, which triggers `ActiveStorage::IntegrityError`.
## Issue Type (Can be multiple)
- [ ] Build - Cannot install or import the SDK
- [ ] Performance - Performance issues
- [x] Behaviour - Functions are not working as expected (such as generate URL)
- [ ] Documentation - Inconsistency between the docs and behaviour
- [ ] Other (Specify)
## Operating System
- [ ] Linux
- [ ] Windows
- [ ] macOS
- [x] All
## Environment and Libraries (fill in the version numbers)
- Cloudinary Ruby SDK version - 2.4.4
- Ruby Version - 3.4.10
- Rails Version - 8.1.3
## Current workaround
In our codebase, we monkey patched `ActiveStorage::Service::CloudinaryService#ext_for_file` like this as a workaround:
```
# The original ext_for_file method takes the extension from the filename and only falls back to the
# content type when the filename carries none. What this patch does is let Rails determine the real
# content type of the file, and if it matches the filename extension return this extension, otherwise
# return an extension that really matches the content_type.
#
# This logic does not apply to "raw" resource types, as these are never transcoded by cloudinary.
def ext_for_file(key, filename = nil, content_type = nil)
attributes = key.is_a?(ActiveStorage::BlobKey) ? key.attributes : {}
content_type = content_type.presence || attributes[:content_type]
return super if content_type_to_resource_type(content_type).eql?('raw')
# { 'identified' => true } in metadata means Rails has analyzed the file and determined its true
# content type. It does that by downloading the file from the provided url, at the moment of
# attaching the blob to the record. Before that, the content type is what the client said it was,
# which can be wrong. Returning nil before identification forces no extension and therefore no
# conversion on the Cloudinary side. That allows Rails to download and analyze the original file
# intead of a converted one.
return nil unless attributes.dig(:metadata, 'identified')
filename = ActiveStorage::Filename.wrap(filename.presence || attributes[:filename])
filename_extension = filename.extension_without_delimiter
content_type_extensions = Marcel::TYPE_EXTS[content_type]
return filename_extension if content_type_extensions.blank?
content_type_extensions.find { it == filename_extension } || content_type_extensions.first
end
```
Beitragsleitfaden
Rechercherichtung
Beginne mit ActiveStorage::Service::CloudinaryService#ext_for_file und vergleiche die Auswahl der Dateierweiterung mit dem gemeldeten Monkey Patch. Reproduziere einen Rails Direct Upload mit einem JPEG, dessen Dateiname die Erweiterung .png hat, und verfolge anschließend die Cloudinary-URL, den Blob-Checksum und den Pfad zu ActiveStorage::IntegrityError. Als erledigt gilt die Aufgabe, wenn das ursprünglich hochgeladene Bild ohne Checksum-Abweichung oder unbeabsichtigte Formatumwandlung heruntergeladen und analysiert werden kann.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- rails, ruby
- Bereich
- backend, cloud
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 55/100