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

Using ui.download from the auto index page with multiple browsers / clients connected causes the file to download to on all connected clients and fail on some #4016

Open
PeterQuinn396 opened this issue Nov 20, 2024 · 0 comments

Comments

@PeterQuinn396
Copy link

Description

First off, NiceGUI is a great tool and I love working with it, thank you so much for making it and supporting it!

Problem

I've come across a strange behavior that I believe is potentially problematic.

I am using a nicegui webpage to monitor and control the state of a piece of hardware, using the auto index page to ensure that anyone viewing the webpage sees the same state of the hardware.

When using the auto index page with multiple clients (either different web browers on the same machine, or browsers on different machines), when one user clicks a ui.button element which triggers a ui.download call, the download is initiated on all connected clients.

This triggers a race condition between the browsers / clients to download the file. The first client to access it receives the file, all the others show a download failed error, as the file download url can only be used once:

src = core.app.add_static_file(local_file=src, single_use=True)

The situation can arise where the user who clicked the download button does not receive the file, but it goes to a user on a different machine, who also happens to be viewing the auto index page.

This seems like a potentially problematic situation, as arbitrary files can be downloaded onto another persons machine, and the desired files don't up on the correct target machine.

Minimal working example

Using ubuntu 22 and python 3.10, and latest version of nicegui==2.5.0

  1. Create a small file to download later

    echo "sample text" >> small_file.txt
    
  2. Run the following code from the same folder

    from nicegui import ui
    
    def main():
        ui.button("Download", on_click=lambda: ui.download('small_file.txt'))
        ui.run(reload=False)
    
    if __name__ == "__main__":
        main()
  3. Open the URL from the terminal in two different webbrowers on the same machine (ex chrome and firefox)

  4. Press the download button, both will attempt to download the file, but one will fail. It may be the browser that requested the download, or the other one, its not very predictable

    Closing one of the browsers fixes allows the single one to consistently download the file as expected

Attempted fixes / investigation so far

I couldn't see anyway to work with ui.download function arguments to change this behaviour, so I looked for alternatives to have only the user that initiated the download receive the file.

My understanding with NiceGUI is that per user interactions are done with the ui.page() decorator. So I tried a small example below:

from nicegui import ui
from nicegui.events import ClickEventArguments

def main():

    # create a private page to handle the download, so it only triggers for the current user?
    def download(click_event_args: ClickEventArguments):
        @ui.page('/download')
        async def _private_download_page():
            ui.download('small_file.txt')
            ui.run_javascript('window.close();')

        ui.navigate.to('/download', new_tab=True)

    ui.button("Download", on_click=download)
    ui.run(reload=False, dark=True)

if __name__ == "__main__":
    main()

However, it looks like the ui.navigate.to() function uses a similar mechanism as ui.download(), so all the clients connected to the webpage try to navigate to the new page.

The documentation for the navigate.to function here seems to suggest there is a way to make the function only run for the triggering client when using the auto page index, but I have not been able to figure out how to make that work. The navigate.to function does not provide an explicit socket parameter suggested by the note, and the ClickEventArguments fields don't seem to expose a socket parameter.

Separately, while reading through the code, I also came across this function, which seems related to controlling responses to clients connected to the webpage, but I wasn't able to find much documentation for how to use it.

def individual_target(self, socket_id: str) -> Iterator[None]:

Conclusion

I just wanted to make the team aware of what could be seen as a potential security issue of downloading files onto another machine remotely.

As for resolving my current issue, I think my question boils down to:

  • When using the auto index page, is there a supported mechanism for restricting certain user interactions with the UI to the user triggered it? (ex ui.download, ui.navigate.to() )

If anyone has any ideas about how that might be achievable, I would really appreciate any insight here.

Apologies for the long post, I just wanted to make sure it was documented as clearly as possible.

Thank you very much!

@PeterQuinn396 PeterQuinn396 changed the title Using ui.download from the auto index page with multiple browsers / clients connected Using ui.download from the auto index page with multiple browsers / clients connected causes the file to download to on all connected clients and fail on some Nov 20, 2024
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

No branches or pull requests

1 participant