Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow displaying SVG images securely in gr.Image and gr.Gallery components #10269

Open
wants to merge 42 commits into
base: main
Choose a base branch
from

Conversation

abidlabs
Copy link
Member

@abidlabs abidlabs commented Dec 30, 2024

Previously, we disabled displaying SVG images in gr.Image by using the content_disposition_type = "attachment" header. Now, SVG images will be displayed in the both the gr.Image input and output components, as can be verified by a simple demo:

import gradio as gr

demo = gr.Interface(lambda x:x, "image", "image")

demo.launch()

We should think through the security implications of this PR. In order to make this PR work, we need to dump the SVG contents inline instead of using the <img> tag (since the <img> tag will not display the image if the content disposition is "attachment"). (And we should keep the content disposition as is because it affects many other things e.g. what happens if a user directly pastes the SVG url in their browser). In order to provide a good experience to to Gradio users and dipslay the image preview, this PR dumps the svg contents inline, but this should be secure because we sanitize ths SVG contents first.

Closes: #10261

@gradio-pr-bot
Copy link
Collaborator

gradio-pr-bot commented Dec 30, 2024

🪼 branch checks and previews

Name Status URL
Spaces ready! Spaces preview
Website ready! Website preview
Storybook ready! Storybook preview
🦄 Changes detecting...

Install Gradio from this PR

pip install https://gradio-pypi-previews.s3.amazonaws.com/4984ba1b66eb06d778db3417f7e3ee9e66c1137d/gradio-5.9.1-py3-none-any.whl

Install Gradio Python Client from this PR

pip install "gradio-client @ git+https://github.com/gradio-app/gradio@4984ba1b66eb06d778db3417f7e3ee9e66c1137d#subdirectory=client/python"

Install Gradio JS Client from this PR

npm install https://gradio-npm-previews.s3.amazonaws.com/4984ba1b66eb06d778db3417f7e3ee9e66c1137d/gradio-client-1.8.0.tgz

Use Lite from this PR

<script type="module" src="https://gradio-lite-previews.s3.amazonaws.com/4984ba1b66eb06d778db3417f7e3ee9e66c1137d/dist/lite.js""></script>

@gradio-pr-bot
Copy link
Collaborator

gradio-pr-bot commented Dec 30, 2024

🦄 change detected

This Pull Request includes changes to the following packages.

Package Version
@gradio/gallery patch
@gradio/image patch
gradio patch
  • Maintainers can select this checkbox to manually select packages to update.

With the following changelog entry.

Allow displaying SVG images securely in gr.Image and gr.Gallery components

Maintainers or the PR author can modify the PR title to modify this entry.

Something isn't right?

  • Maintainers can change the version label to modify the version bump.
  • If the bot has failed to detect any changes, or if this pull request needs to update multiple packages to different versions or requires a more comprehensive changelog entry, maintainers can update the changelog file directly.

@abidlabs abidlabs changed the title Svg fixes SVG fixes Dec 30, 2024
@abidlabs abidlabs changed the title SVG fixes Allow displaying SVG images securely in gr.Image component Dec 30, 2024
@abidlabs abidlabs marked this pull request as ready for review December 30, 2024 22:45
Copy link
Member

@pngwn pngwn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small thing to make sure sanitization works in SSR and CSR,

@@ -1,5 +1,6 @@
<script lang="ts">
import type { HTMLImgAttributes } from "svelte/elements";
import DOMPurify from "dompurify";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have our own special sanitizer package that works in both server and client environments. See import here: https://github.com/gradio-app/gradio/blob/main/js/markdown-code/MarkdownCode.svelte#L6.

We need to

import { sanitize } from "@gradio/sanitize";

You can see the usage here:https://github.com/gradio-app/gradio/blob/main/js/markdown-code/MarkdownCode.svelte#L62

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pngwn I'll switch to that. We use dompurify directly here:

import DOMPurify from "dompurify";

Does this need to be switched out too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we should for consistency, although its less problematic in that case because we only launch toasts on the client.

@abidlabs abidlabs marked this pull request as draft January 1, 2025 01:01
@abidlabs
Copy link
Member Author

abidlabs commented Jan 1, 2025

Ok finally have a relatively clean solution. The basic idea is that for SVG files, we set the FileData.url attribute to be the actual SVG code instead of the URL. This allows SVGs to get rendered securely inside of the <img> tags without needing to change how SVG files are served at all. In the case that the SVG files are uploaded by the user, this happens in the frontend (as that's the only place we can change the FileData payload), whereas in the output case, the FileData payload is set in Image.postprocess() so that's where we make this change.

Tested and works both in SSR and SPA modes, with both kinds of demos:

import gradio as gr

def image_mod(image):
    return image

demo = gr.Interface(
    image_mod,
    gr.Image(type="filepath"),
    "image",
)

if __name__ == "__main__":
    demo.launch()
import gradio as gr

with gr.Blocks() as demo:
    gr.Image("test.svg")

demo.launch(ssr_mode=True)

@abidlabs abidlabs marked this pull request as ready for review January 1, 2025 06:48
@aliabid94
Copy link
Collaborator

Recording 2025-01-01 at 11 49 30

The example doesn't work?

import gradio as gr

demo = gr.Interface(lambda x:x, "image", "image")

demo.launch()

@abidlabs
Copy link
Member Author

abidlabs commented Jan 1, 2025

Yes if you have an Image input you need to set type=filepath to support SVGs as the docs mention. See my example above

@akedia
Copy link

akedia commented Jan 2, 2025

thanks for the quick fix! looks like the same have to be put on Gr.Gallery as well?

@abidlabs
Copy link
Member Author

abidlabs commented Jan 2, 2025

thanks for the quick fix! looks like the same have to be put on Gr.Gallery as well?

ah yes thank you, I'll fix that

@abidlabs
Copy link
Member Author

abidlabs commented Jan 2, 2025

Fixed for gr.Gallery as well, as can be seen with a simple example:

import gradio as gr 

def test(x):
    return x

gr.Interface(test, "gallery", "gallery").launch()

@abidlabs abidlabs changed the title Allow displaying SVG images securely in gr.Image component Allow displaying SVG images securely in gr.Image and gr.Gallery components Jan 2, 2025
@aliabid94
Copy link
Collaborator

Didn;t seem to work with gallery with the following code:

import gradio as gr

def image_mod(image):
    return [image, image, image]

demo = gr.Interface(
    image_mod,
    gr.Image(type="filepath"),
    gr.Gallery(),
)

if __name__ == "__main__":
    demo.launch()

screen rec below (first with png, then svg)
Recording 2025-01-02 at 10 48 58 (1)

@abidlabs
Copy link
Member Author

abidlabs commented Jan 3, 2025

Didn't seem to work with gallery with the following code:

Hmm can't reproduce this. Are you sure you've pulled the latest and built the frontend?

https://streamable.com/yycb8e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SVG does not work with Gallery and Image
5 participants