mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-13 23:16:03 +08:00
Pass allowed URI schemes to Markdown outputs and update naming
This commit is contained in:
parent
0990ab4cb2
commit
12f2322d08
9 changed files with 42 additions and 28 deletions
|
@ -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
|
iframe. Set it to "true" to enable it. If you do enable it, then the application
|
||||||
must run with HTTPS.
|
must run with HTTPS.
|
||||||
|
|
||||||
* LIVEBOOK_ALLOW_URI_SCHEMES - sets addtional extra hyperlink protocols to
|
* LIVEBOOK_ALLOW_URI_SCHEMES - sets additional allowed hyperlink schemes to the
|
||||||
the Markdown content. Livebook sanitizes links in Markdown, allowing only a few
|
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
|
standard schemes by default (such as http and https). Set it to a comma-separated
|
||||||
protocols to configure additional protocols.
|
list of schemes.
|
||||||
|
|
||||||
<!-- Environment variables -->
|
<!-- Environment variables -->
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,10 @@ const Cell = {
|
||||||
"data-smart-cell-js-view-ref",
|
"data-smart-cell-js-view-ref",
|
||||||
null
|
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, {
|
const markdown = new Markdown(markdownContainer, source, {
|
||||||
baseUrl: this.props.sessionPath,
|
baseUrl: this.props.sessionPath,
|
||||||
emptyText: "Empty markdown cell",
|
emptyText: "Empty markdown cell",
|
||||||
extraProtocol: this.props.protocols.replace(/\s+/g, "").split(","),
|
allowedUriSchemes: this.props.allowedUriSchemes.split(","),
|
||||||
});
|
});
|
||||||
|
|
||||||
liveEditor.onChange((newSource) => {
|
liveEditor.onChange((newSource) => {
|
||||||
|
|
|
@ -15,6 +15,7 @@ const MarkdownRenderer = {
|
||||||
|
|
||||||
const markdown = new Markdown(this.el, "", {
|
const markdown = new Markdown(this.el, "", {
|
||||||
baseUrl: this.props.sessionPath,
|
baseUrl: this.props.sessionPath,
|
||||||
|
allowedUriSchemes: this.props.allowedUriSchemes.split(","),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.handleEvent(
|
this.handleEvent(
|
||||||
|
@ -29,6 +30,10 @@ const MarkdownRenderer = {
|
||||||
return {
|
return {
|
||||||
id: getAttributeOrThrow(this.el, "data-id"),
|
id: getAttributeOrThrow(this.el, "data-id"),
|
||||||
sessionPath: getAttributeOrThrow(this.el, "data-session-path"),
|
sessionPath: getAttributeOrThrow(this.el, "data-session-path"),
|
||||||
|
allowedUriSchemes: getAttributeOrThrow(
|
||||||
|
this.el,
|
||||||
|
"data-allowed-uri-schemes"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,13 +28,13 @@ class Markdown {
|
||||||
constructor(
|
constructor(
|
||||||
container,
|
container,
|
||||||
content,
|
content,
|
||||||
{ baseUrl = null, emptyText = "", extraProtocol = [] } = {}
|
{ baseUrl = null, emptyText = "", allowedUriSchemes = [] } = {}
|
||||||
) {
|
) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.emptyText = emptyText;
|
this.emptyText = emptyText;
|
||||||
this.extraProtocol = extraProtocol;
|
this.allowedUriSchemes = allowedUriSchemes;
|
||||||
|
|
||||||
this._render();
|
this._render();
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ class Markdown {
|
||||||
.use(remarkRehype, { allowDangerousHtml: true })
|
.use(remarkRehype, { allowDangerousHtml: true })
|
||||||
.use(rehypeRaw)
|
.use(rehypeRaw)
|
||||||
.use(rehypeExpandUrls, { baseUrl: this.baseUrl })
|
.use(rehypeExpandUrls, { baseUrl: this.baseUrl })
|
||||||
.use(rehypeSanitize, sanitizeSchema(this.extraProtocol))
|
.use(rehypeSanitize, sanitizeSchema(this.allowedUriSchemes))
|
||||||
.use(rehypeKatex)
|
.use(rehypeKatex)
|
||||||
.use(rehypeMermaid)
|
.use(rehypeMermaid)
|
||||||
.use(rehypeExternalLinks, { baseUrl: this.baseUrl })
|
.use(rehypeExternalLinks, { baseUrl: this.baseUrl })
|
||||||
|
@ -103,7 +103,7 @@ export default Markdown;
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
|
|
||||||
function sanitizeSchema(extraProtocol) {
|
function sanitizeSchema(allowedUriSchemes) {
|
||||||
// Allow class and style attributes on tags for syntax highlighting,
|
// Allow class and style attributes on tags for syntax highlighting,
|
||||||
// remarkMath tags, or user-written styles
|
// remarkMath tags, or user-written styles
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ function sanitizeSchema(extraProtocol) {
|
||||||
},
|
},
|
||||||
protocols: {
|
protocols: {
|
||||||
...defaultSchema.protocols,
|
...defaultSchema.protocols,
|
||||||
href: [...defaultSchema.protocols.href, ...extraProtocol],
|
href: [...defaultSchema.protocols.href, ...allowedUriSchemes],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,9 +195,9 @@ defmodule Livebook.Config do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@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
|
def allowed_uri_schemes() do
|
||||||
Application.fetch_env!(:livebook, :allowed_uri_schemes)
|
Application.fetch_env!(:livebook, :allowed_uri_schemes)
|
||||||
end
|
end
|
||||||
|
@ -407,6 +407,15 @@ defmodule Livebook.Config do
|
||||||
end
|
end
|
||||||
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 """
|
@doc """
|
||||||
Returns the current version of running Livebook.
|
Returns the current version of running Livebook.
|
||||||
"""
|
"""
|
||||||
|
@ -438,13 +447,4 @@ defmodule Livebook.Config do
|
||||||
IO.puts("\nERROR!!! [Livebook] " <> message)
|
IO.puts("\nERROR!!! [Livebook] " <> message)
|
||||||
System.halt(1)
|
System.halt(1)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
defmodule LivebookWeb.Output.MarkdownComponent do
|
defmodule LivebookWeb.Output.MarkdownComponent do
|
||||||
use LivebookWeb, :live_component
|
use LivebookWeb, :live_component
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def mount(socket) do
|
||||||
|
{:ok, assign(socket, allowed_uri_schemes: Livebook.Config.allowed_uri_schemes())}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def update(assigns, socket) do
|
def update(assigns, socket) do
|
||||||
socket = assign(socket, assigns)
|
socket = assign(socket, assigns)
|
||||||
|
@ -20,6 +25,7 @@ defmodule LivebookWeb.Output.MarkdownComponent do
|
||||||
phx-hook="MarkdownRenderer"
|
phx-hook="MarkdownRenderer"
|
||||||
data-id={@id}
|
data-id={@id}
|
||||||
data-session-path={Routes.session_path(@socket, :page, @session_id)}
|
data-session-path={Routes.session_path(@socket, :page, @session_id)}
|
||||||
|
data-allowed-uri-schemes={Enum.join(@allowed_uri_schemes, ",")}
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,7 +5,7 @@ defmodule LivebookWeb.SessionLive do
|
||||||
import LivebookWeb.SessionHelpers
|
import LivebookWeb.SessionHelpers
|
||||||
import Livebook.Utils, only: [format_bytes: 1]
|
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.Notebook.{Cell, ContentLoader}
|
||||||
alias Livebook.JSInterop
|
alias Livebook.JSInterop
|
||||||
alias Livebook.Hubs
|
alias Livebook.Hubs
|
||||||
|
@ -64,7 +64,7 @@ defmodule LivebookWeb.SessionLive do
|
||||||
saved_secrets: get_saved_secrets(),
|
saved_secrets: get_saved_secrets(),
|
||||||
select_secret_ref: nil,
|
select_secret_ref: nil,
|
||||||
select_secret_options: nil,
|
select_secret_options: nil,
|
||||||
protocols: Config.allowed_uri_schemes() |> Enum.join(",")
|
allowed_uri_schemes: Livebook.Config.allowed_uri_schemes()
|
||||||
)
|
)
|
||||||
|> assign_private(data: data)
|
|> assign_private(data: data)
|
||||||
|> prune_outputs()
|
|> prune_outputs()
|
||||||
|
@ -271,9 +271,9 @@ defmodule LivebookWeb.SessionLive do
|
||||||
session_id={@session.id}
|
session_id={@session.id}
|
||||||
session_pid={@session.pid}
|
session_pid={@session.pid}
|
||||||
client_id={@client_id}
|
client_id={@client_id}
|
||||||
protocols={@protocols}
|
|
||||||
runtime={@data_view.runtime}
|
runtime={@data_view.runtime}
|
||||||
installing?={@data_view.installing?}
|
installing?={@data_view.installing?}
|
||||||
|
allowed_uri_schemes={@allowed_uri_schemes}
|
||||||
cell_view={@data_view.setup_cell_view}
|
cell_view={@data_view.setup_cell_view}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -291,12 +291,12 @@ defmodule LivebookWeb.SessionLive do
|
||||||
id={section_view.id}
|
id={section_view.id}
|
||||||
index={index}
|
index={index}
|
||||||
session_id={@session.id}
|
session_id={@session.id}
|
||||||
protocols={@protocols}
|
|
||||||
session_pid={@session.pid}
|
session_pid={@session.pid}
|
||||||
client_id={@client_id}
|
client_id={@client_id}
|
||||||
runtime={@data_view.runtime}
|
runtime={@data_view.runtime}
|
||||||
smart_cell_definitions={@data_view.smart_cell_definitions}
|
smart_cell_definitions={@data_view.smart_cell_definitions}
|
||||||
installing?={@data_view.installing?}
|
installing?={@data_view.installing?}
|
||||||
|
allowed_uri_schemes={@allowed_uri_schemes}
|
||||||
section_view={section_view}
|
section_view={section_view}
|
||||||
/>
|
/>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -10,7 +10,6 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
||||||
id={"cell-#{@cell_view.id}"}
|
id={"cell-#{@cell_view.id}"}
|
||||||
phx-hook="Cell"
|
phx-hook="Cell"
|
||||||
data-cell-id={@cell_view.id}
|
data-cell-id={@cell_view.id}
|
||||||
data-protocols={@protocols}
|
|
||||||
data-focusable-id={@cell_view.id}
|
data-focusable-id={@cell_view.id}
|
||||||
data-type={@cell_view.type}
|
data-type={@cell_view.type}
|
||||||
data-session-path={Routes.session_path(@socket, :page, @session_id)}
|
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-eval-validity={get_in(@cell_view, [:eval, :validity])}
|
||||||
data-js-empty={empty?(@cell_view.source_view)}
|
data-js-empty={empty?(@cell_view.source_view)}
|
||||||
data-smart-cell-js-view-ref={smart_cell_js_view_ref(@cell_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) %>
|
<%= render_cell(assigns) %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -142,9 +142,9 @@ defmodule LivebookWeb.SessionLive.SectionComponent do
|
||||||
session_id={@session_id}
|
session_id={@session_id}
|
||||||
session_pid={@session_pid}
|
session_pid={@session_pid}
|
||||||
client_id={@client_id}
|
client_id={@client_id}
|
||||||
protocols={@protocols}
|
|
||||||
runtime={@runtime}
|
runtime={@runtime}
|
||||||
installing?={@installing?}
|
installing?={@installing?}
|
||||||
|
allowed_uri_schemes={@allowed_uri_schemes}
|
||||||
cell_view={cell_view}
|
cell_view={cell_view}
|
||||||
/>
|
/>
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
Loading…
Add table
Reference in a new issue