Pass allowed URI schemes to Markdown outputs and update naming

This commit is contained in:
Jonatan Kłosko 2023-02-15 22:18:13 +01:00
parent 0990ab4cb2
commit 12f2322d08
9 changed files with 42 additions and 28 deletions

View file

@ -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.
<!-- Environment variables -->

View file

@ -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) => {

View file

@ -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"
),
};
},
};

View file

@ -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],
},
};
}

View file

@ -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

View file

@ -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, ",")}
>
</div>
"""

View file

@ -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}
/>
</div>
@ -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 %>

View file

@ -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) %>
</div>

View file

@ -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