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

feat: Support opening shinylive apps locally from a vscode:// or positron:// URL #70

Merged
merged 5 commits into from
Aug 23, 2024

Conversation

gadenbuie
Copy link
Collaborator

@gadenbuie gadenbuie commented Aug 23, 2024

Closes #58

Adds support for positron://, vscode://, vscode-insiders://, or cursor:// links of the form

vscode://posit.shiny/shinylive?url={encodeURIComponent(shinylive_url)}

Clicking a link like this will open Positron/VS Code/Cursor and prompt the user to choose a location to save the file, building on the existing "Save App from Shinylive Link" command.

Kapture.2024-08-23.at.08.01.20.mp4

These URLs are easy to construct:

const shinyliveUrl = "https://shinylive.io/py/editor/#code=NobwRAdghgtgpmAXGKAHVA6VBPMAaMAYwHsIAXOcpMAMwCdiYACAZwAsBLCbDOAD1R04LFkw4xUxOmTERUAVzJ4mQiABM4dZfI4AdCPp0Y2AJgAUusAAk4AG1vFlAdym21AQksBKQxwxcFMgB9FlsODToLSEtlSwA5GKYABmUARiSUphMknwM8gAFVCIwSDX0NGiYyPjIzL0R9JiaVODJ5OggmGksIACoTMVEQAMUMCDqmXqyAX0t9MGmAXSA"
const url = new URL("positron://Posit.shiny/shinylive/")
url.searchParams.set("url", encodeURIComponent(shinyliveUrl))
url.toString()

// positron://Posit.shiny/shinylive/?url=https%253A%252F%252Fshinylive.io%252Fpy%252Feditor%252F%2523code%253DNobwRAdghgtgpmAXGKAHVA6VBPMAaMAYwHsIAXOcpMAMwCdiYACAZwAsBLCbDOAD1R04LFkw4xUxOmTERUAVzJ4mQiABM4dZfI4AdCPp0Y2AJgAUusAAk4AG1vFlAdym21AQksBKQxwxcFMgB9FlsODToLSEtlSwA5GKYABmUARiSUphMknwM8gAFVCIwSDX0NGiYyPjIzL0R9JiaVODJ5OggmGksIACoTMVEQAMUMCDqmXqyAX0t9MGmAXSA
// vscode://Posit.shiny/shinylive/?url=https%253A%252F%252Fshinylive.io%252Fpy%252Feditor%252F%2523code%253DNobwRAdghgtgpmAXGKAHVA6VBPMAaMAYwHsIAXOcpMAMwCdiYACAZwAsBLCbDOAD1R04LFkw4xUxOmTERUAVzJ4mQiABM4dZfI4AdCPp0Y2AJgAUusAAk4AG1vFlAdym21AQksBKQxwxcFMgB9FlsODToLSEtlSwA5GKYABmUARiSUphMknwM8gAFVCIwSDX0NGiYyPjIzL0R9JiaVODJ5OggmGksIACoTMVEQAMUMCDqmXqyAX0t9MGmAXSA

@gadenbuie gadenbuie marked this pull request as ready for review August 23, 2024 15:17
@gadenbuie gadenbuie requested a review from jcheng5 August 23, 2024 15:17
* @export
* @async
*/
export async function shinyliveSaveAppFromUrl(): Promise<void> {
const url = await askUserForUrl();
export async function shinyliveSaveAppFromUrl(
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to change this for this PR, but I found the way single file vs. multiple file shinylive projects are handled, kind of confusing; there are several points in this function and in askUserForOutputLocation() where the logic varies depending on whether you're doing single vs. multiple that is a bit hard for me to reason about. I suspect it would make more sense to me if e.g. there were separate askUserForOutputDir and askUserForOutputFile functions, and a single conditional in this function with slightly heftier branches.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah that makes a lot of sense. I definitely have a similar feeling coming back to that code. I'll refactor this in a separate PR.

@jcheng5
Copy link
Collaborator

jcheng5 commented Aug 23, 2024

Trying to think of security implications here...

Is it possible to have a maliciously crafted URL that contains ../../../ in the filename?

Do we need to have an interstitial modal that tells people what they clicked and what is about to happen if they proceed?

@jcheng5
Copy link
Collaborator

jcheng5 commented Aug 23, 2024

Should we have a denylist of extensions that we refuse to write? (.exe, .bat, .com, .cmd, others...?)

@gadenbuie
Copy link
Collaborator Author

gadenbuie commented Aug 23, 2024

Trying to think of security implications here...

I'm glad you are, but just FYI the shinylive -> local files isn't a new feature. The only new bit is the ability to trigger the worfklow from a vscode://posit.shiny or positron://posit.shiny URI.

Is it possible to have a maliciously crafted URL that contains ../../../ in the filename?

We check this and refuse to save apps that somehow escape the user-selected folder.

Do we need to have an interstitial modal that tells people what they clicked and what is about to happen if they proceed?

We could. I didn't do this because VS Code has its own interstitial (at 00:05 in the video above) asking for confirmation. Users might dismiss these entirely, at which point we're relying on the link being clear, e.g. "Open in Positron...".

@gadenbuie
Copy link
Collaborator Author

gadenbuie commented Aug 23, 2024

I suppose an interstitial of our own would give us a chance to give the user the option to

  • Open the app on shinylive to review the contents of the app
  • Cancel
  • Move forward

@jcheng5
Copy link
Collaborator

jcheng5 commented Aug 23, 2024

The only new bit is the ability to trigger the worfklow from a vscode://posit.shiny or positron://posit.shiny URI.

Yeah, this is what makes it more dangerous security-wise because that link could be disguised as anything, anywhere HTML can be displayed, whereas the existing ways to trigger the feature are totally unambiguous.

Copy link
Collaborator

@jcheng5 jcheng5 left a comment

Choose a reason for hiding this comment

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

Perfect, thanks!

@gadenbuie
Copy link
Collaborator Author

Here's a quick video of the new interstitial flow

Kapture.2024-08-23.at.12.40.29.mp4

@gadenbuie gadenbuie merged commit dc85119 into main Aug 23, 2024
3 checks passed
@gadenbuie gadenbuie deleted the feat/on-uri-handler branch August 23, 2024 19:50
@jcheng5
Copy link
Collaborator

jcheng5 commented Aug 23, 2024

It's a lot of dialogs to get through, but on balance, I think it's an appropriately conservative starting point.

Oh, I just noticed that the interstitial actually has two Cancel buttons...

@gadenbuie
Copy link
Collaborator Author

Oh, I just noticed that the interstitial actually has two Cancel buttons...

Good catch, I'll get rid of the extra one 😄

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.

Add shinylive onUri handler
2 participants