From 12f2322d08eb6c352b4e59d8f935259be014b3df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Wed, 15 Feb 2023 22:18:13 +0100 Subject: [PATCH] Pass allowed URI schemes to Markdown outputs and update naming --- README.md | 8 +++---- assets/js/hooks/cell.js | 7 ++++-- assets/js/hooks/markdown_renderer.js | 5 +++++ assets/js/lib/markdown.js | 10 ++++----- lib/livebook/config.ex | 22 +++++++++---------- .../live/output/markdown_component.ex | 6 +++++ lib/livebook_web/live/session_live.ex | 8 +++---- .../live/session_live/cell_component.ex | 2 +- .../live/session_live/section_component.ex | 2 +- 9 files changed, 42 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index e26128567..3eb598503 100644 --- a/README.md +++ b/README.md @@ -229,10 +229,10 @@ The following environment variables configure Livebook: iframe. Set it to "true" to enable it. If you do enable it, then the application must run with HTTPS. - * LIVEBOOK_ALLOW_URI_SCHEMES - sets addtional extra hyperlink protocols to - the Markdown content. Livebook sanitizes links in Markdown, allowing only a few - standard protocols by default (such as http and https). Set a comma-separated list of - protocols to configure additional protocols. + * LIVEBOOK_ALLOW_URI_SCHEMES - sets additional allowed hyperlink schemes to the + Markdown content. Livebook sanitizes links in Markdown, allowing only a few + standard schemes by default (such as http and https). Set it to a comma-separated + list of schemes. diff --git a/assets/js/hooks/cell.js b/assets/js/hooks/cell.js index 15d5fc7ce..3766a2c1e 100644 --- a/assets/js/hooks/cell.js +++ b/assets/js/hooks/cell.js @@ -125,7 +125,10 @@ const Cell = { "data-smart-cell-js-view-ref", null ), - protocols: getAttributeOrThrow(this.el, "data-protocols"), + allowedUriSchemes: getAttributeOrThrow( + this.el, + "data-allowed-uri-schemes" + ), }; }, @@ -217,7 +220,7 @@ const Cell = { const markdown = new Markdown(markdownContainer, source, { baseUrl: this.props.sessionPath, emptyText: "Empty markdown cell", - extraProtocol: this.props.protocols.replace(/\s+/g, "").split(","), + allowedUriSchemes: this.props.allowedUriSchemes.split(","), }); liveEditor.onChange((newSource) => { diff --git a/assets/js/hooks/markdown_renderer.js b/assets/js/hooks/markdown_renderer.js index 532f333c7..a62a6d295 100644 --- a/assets/js/hooks/markdown_renderer.js +++ b/assets/js/hooks/markdown_renderer.js @@ -15,6 +15,7 @@ const MarkdownRenderer = { const markdown = new Markdown(this.el, "", { baseUrl: this.props.sessionPath, + allowedUriSchemes: this.props.allowedUriSchemes.split(","), }); this.handleEvent( @@ -29,6 +30,10 @@ const MarkdownRenderer = { return { id: getAttributeOrThrow(this.el, "data-id"), sessionPath: getAttributeOrThrow(this.el, "data-session-path"), + allowedUriSchemes: getAttributeOrThrow( + this.el, + "data-allowed-uri-schemes" + ), }; }, }; diff --git a/assets/js/lib/markdown.js b/assets/js/lib/markdown.js index c51db5e51..f6ef10f2c 100644 --- a/assets/js/lib/markdown.js +++ b/assets/js/lib/markdown.js @@ -28,13 +28,13 @@ class Markdown { constructor( container, content, - { baseUrl = null, emptyText = "", extraProtocol = [] } = {} + { baseUrl = null, emptyText = "", allowedUriSchemes = [] } = {} ) { this.container = container; this.content = content; this.baseUrl = baseUrl; this.emptyText = emptyText; - this.extraProtocol = extraProtocol; + this.allowedUriSchemes = allowedUriSchemes; this._render(); } @@ -67,7 +67,7 @@ class Markdown { .use(remarkRehype, { allowDangerousHtml: true }) .use(rehypeRaw) .use(rehypeExpandUrls, { baseUrl: this.baseUrl }) - .use(rehypeSanitize, sanitizeSchema(this.extraProtocol)) + .use(rehypeSanitize, sanitizeSchema(this.allowedUriSchemes)) .use(rehypeKatex) .use(rehypeMermaid) .use(rehypeExternalLinks, { baseUrl: this.baseUrl }) @@ -103,7 +103,7 @@ export default Markdown; // Plugins -function sanitizeSchema(extraProtocol) { +function sanitizeSchema(allowedUriSchemes) { // Allow class and style attributes on tags for syntax highlighting, // remarkMath tags, or user-written styles @@ -115,7 +115,7 @@ function sanitizeSchema(extraProtocol) { }, protocols: { ...defaultSchema.protocols, - href: [...defaultSchema.protocols.href, ...extraProtocol], + href: [...defaultSchema.protocols.href, ...allowedUriSchemes], }, }; } diff --git a/lib/livebook/config.ex b/lib/livebook/config.ex index 8c64ccd23..7fef44807 100644 --- a/lib/livebook/config.ex +++ b/lib/livebook/config.ex @@ -195,9 +195,9 @@ defmodule Livebook.Config do end @doc """ - Return list of added uri schemes. + Return list of additional allowed hyperlink schemes. """ - @spec allowed_uri_schemes() :: [] + @spec allowed_uri_schemes() :: list(String.t()) def allowed_uri_schemes() do Application.fetch_env!(:livebook, :allowed_uri_schemes) end @@ -407,6 +407,15 @@ defmodule Livebook.Config do end end + @doc """ + Parses and validates allowed URI schemes from env. + """ + def allowed_uri_schemes!(env) do + if schemes = System.get_env(env) do + String.split(schemes, ",", trim: true) + end + end + @doc """ Returns the current version of running Livebook. """ @@ -438,13 +447,4 @@ defmodule Livebook.Config do IO.puts("\nERROR!!! [Livebook] " <> message) System.halt(1) end - - @doc """ - Parses and validates allowed URI schemes from env. - """ - def allowed_uri_schemes!(env) do - if schemes = System.get_env(env) do - String.split(schemes, ",", trim: true) - end - end end diff --git a/lib/livebook_web/live/output/markdown_component.ex b/lib/livebook_web/live/output/markdown_component.ex index 824997ec7..03d06434a 100644 --- a/lib/livebook_web/live/output/markdown_component.ex +++ b/lib/livebook_web/live/output/markdown_component.ex @@ -1,6 +1,11 @@ defmodule LivebookWeb.Output.MarkdownComponent do use LivebookWeb, :live_component + @impl true + def mount(socket) do + {:ok, assign(socket, allowed_uri_schemes: Livebook.Config.allowed_uri_schemes())} + end + @impl true def update(assigns, socket) do socket = assign(socket, assigns) @@ -20,6 +25,7 @@ defmodule LivebookWeb.Output.MarkdownComponent do phx-hook="MarkdownRenderer" data-id={@id} data-session-path={Routes.session_path(@socket, :page, @session_id)} + data-allowed-uri-schemes={Enum.join(@allowed_uri_schemes, ",")} > """ diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex index b63e317f3..ed944dfdd 100644 --- a/lib/livebook_web/live/session_live.ex +++ b/lib/livebook_web/live/session_live.ex @@ -5,7 +5,7 @@ defmodule LivebookWeb.SessionLive do import LivebookWeb.SessionHelpers import Livebook.Utils, only: [format_bytes: 1] - alias Livebook.{Config, Sessions, Session, Delta, Notebook, Runtime, LiveMarkdown, Secrets} + alias Livebook.{Sessions, Session, Delta, Notebook, Runtime, LiveMarkdown, Secrets} alias Livebook.Notebook.{Cell, ContentLoader} alias Livebook.JSInterop alias Livebook.Hubs @@ -64,7 +64,7 @@ defmodule LivebookWeb.SessionLive do saved_secrets: get_saved_secrets(), select_secret_ref: nil, select_secret_options: nil, - protocols: Config.allowed_uri_schemes() |> Enum.join(",") + allowed_uri_schemes: Livebook.Config.allowed_uri_schemes() ) |> assign_private(data: data) |> prune_outputs() @@ -271,9 +271,9 @@ defmodule LivebookWeb.SessionLive do session_id={@session.id} session_pid={@session.pid} client_id={@client_id} - protocols={@protocols} runtime={@data_view.runtime} installing?={@data_view.installing?} + allowed_uri_schemes={@allowed_uri_schemes} cell_view={@data_view.setup_cell_view} /> @@ -291,12 +291,12 @@ defmodule LivebookWeb.SessionLive do id={section_view.id} index={index} session_id={@session.id} - protocols={@protocols} session_pid={@session.pid} client_id={@client_id} runtime={@data_view.runtime} smart_cell_definitions={@data_view.smart_cell_definitions} installing?={@data_view.installing?} + allowed_uri_schemes={@allowed_uri_schemes} section_view={section_view} /> <% end %> diff --git a/lib/livebook_web/live/session_live/cell_component.ex b/lib/livebook_web/live/session_live/cell_component.ex index 281c0094a..c76bf6907 100644 --- a/lib/livebook_web/live/session_live/cell_component.ex +++ b/lib/livebook_web/live/session_live/cell_component.ex @@ -10,7 +10,6 @@ defmodule LivebookWeb.SessionLive.CellComponent do id={"cell-#{@cell_view.id}"} phx-hook="Cell" data-cell-id={@cell_view.id} - data-protocols={@protocols} data-focusable-id={@cell_view.id} data-type={@cell_view.type} data-session-path={Routes.session_path(@socket, :page, @session_id)} @@ -18,6 +17,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do data-eval-validity={get_in(@cell_view, [:eval, :validity])} data-js-empty={empty?(@cell_view.source_view)} data-smart-cell-js-view-ref={smart_cell_js_view_ref(@cell_view)} + data-allowed-uri-schemes={Enum.join(@allowed_uri_schemes, ",")} > <%= render_cell(assigns) %> diff --git a/lib/livebook_web/live/session_live/section_component.ex b/lib/livebook_web/live/session_live/section_component.ex index 76832ae9e..6c6e8dac9 100644 --- a/lib/livebook_web/live/session_live/section_component.ex +++ b/lib/livebook_web/live/session_live/section_component.ex @@ -142,9 +142,9 @@ defmodule LivebookWeb.SessionLive.SectionComponent do session_id={@session_id} session_pid={@session_pid} client_id={@client_id} - protocols={@protocols} runtime={@runtime} installing?={@installing?} + allowed_uri_schemes={@allowed_uri_schemes} cell_view={cell_view} /> <.live_component