Skip to content

Detect content-type for designs uploaded to LFS

What does this MR do and why?

What

This MR sets content-type property on files uploaded using the design management feature. For now the content-type detection is under the same FF (design_management_allow_dangerous_images) as the SVG file uploads, as currently SVG design uploads is the only use case for custom content-types. It impacts all uploaded design files, but changes are targeting setups with remote object storage on GCS.

Why

  • Because of the Content-Type: application/octet-stream header, the uploaded (and valid) SVGs are rendered as broken images #34279 (comment 303545297) (which blocks enabling of the SVG uploads feature)
  • GCS only uses the content-type that is stored with the file and doesn't allow it to be overwritten at the download time

Context

Screenshots or screen recordings

GCS object metadata
Before After
type-octet type-image
Image HTTP requests

Before

curl -L -vvv http://gdk.test:3000/plan-sandbox/design-management-sample/-/design_management/designs/7/4001414bbe26178cc4d5604513d82139a8766180/raw_image

> GET /plan-sandbox/design-management-sample/-/design_management/designs/7/4001414bbe26178cc4d5604513d82139a8766180/raw_image HTTP/1.1
> Host: gdk.test:3000
...
< HTTP/1.1 302 Found
< Cache-Control: max-age=60, public
< Content-Type: text/html; charset=utf-8
> Host: storage.googleapis.com
...
< HTTP/2 200
< content-type: application/octet-stream
< content-disposition: attachment; filename="gitlab-logo-100.svg"; filename*=UTF-8''gitlab-logo-100.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 990 380"><defs ...

After

curl -L -vvv http://gdk.test:3000/plan-sandbox/design-management-sample/-/design_management/designs/8/c1ad3f10531ee29dde485e85c8ccea3f7ea399d0/raw_image

> GET /plan-sandbox/design-management-sample/-/design_management/designs/8/c1ad3f10531ee29dde485e85c8ccea3f7ea399d0/raw_image HTTP/1.1
> Host: gdk.test:3000
...
< HTTP/1.1 302 Found
< Cache-Control: max-age=60, public
< Content-Type: text/html; charset=utf-8
> Host: storage.googleapis.com
...
< HTTP/2 200
< content-type: image/svg+xml
< content-disposition: attachment; filename="gitlab-logo-200.svg"; filename*=UTF-8''gitlab-logo-200.svg
...
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 990 380">...%
GitLab designs UI
Before After
Preview gl-logo-preview-broken gl-logo-preview-hidden
Full gl-logo-broken gl-logo-hidden, gl-logo-shown

As you can see in the last row, even after applying the new content-type, an SVG image is not visible as it can't identify the image dimension on frontend side (and raises a JS exception). But the image is there, after setting height & width CSS props manually it appears. That's a separate issue, I will work on it after.

How to set up and validate locally

  1. Enable the management of dangerous image formats (Feature.enable(:design_management_allow_dangerous_images))
  2. Configure remote object storage (Please ping me in case help is needed here, I can provide credentials/access to my private GCS, so you don't need to set up your own)
  3. Navigate to the issue page
  4. Upload a design (.svg format)
  5. Checkout the image attributes on GCS (Type should be image/svg+xml now)
  6. CURL image URL curl -L -vvv (content-type: image/svg+xml header should be present)
  7. Check "Designs" of issue from step 2, the uploaded images shouldn't appear as broken (more details in in GitLab designs UI section)

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #34279

Edited by Stanislav Dobrovolschii

Merge request reports