diff --git a/lib/camo/image.ex b/lib/camo/image.ex index a2d49655..e77f99e6 100644 --- a/lib/camo/image.ex +++ b/lib/camo/image.ex @@ -1,3 +1,8 @@ defmodule Camo.Image do + @doc """ + Convert a potentially untrusted external image URL into a trusted one + loaded through a gocamo proxy (specified by the environment). + """ + @spec image_url(String.t()) :: String.t() def image_url(input), do: Philomena.Native.camo_image_url(input) end diff --git a/lib/philomena/markdown.ex b/lib/philomena/markdown.ex index f953ce4a..be3d426b 100644 --- a/lib/philomena/markdown.ex +++ b/lib/philomena/markdown.ex @@ -1,12 +1,26 @@ defmodule Philomena.Markdown do @markdown_chars ~r/[\*_\[\]\(\)\^`\%\\~<>#\|]/ - # When your NIF is loaded, it will override this function. + @doc """ + Converts user-input Markdown to HTML, with the specified map of image + replacements (which converts ">>1234p" syntax to an embedded image). + """ + @spec to_html(String.t(), %{String.t() => String.t()}) :: String.t() def to_html(text, replacements), do: Philomena.Native.markdown_to_html(text, replacements) + @doc """ + Converts trusted-input Markdown to HTML, with the specified map of image + replacements (which converts ">>1234p" syntax to an embedded image). This + function does not strip any raw HTML embedded in the document. + """ + @spec to_html_unsafe(String.t(), %{String.t() => String.t()}) :: String.t() def to_html_unsafe(text, replacements), do: Philomena.Native.markdown_to_html_unsafe(text, replacements) + @doc """ + Escapes special characters in text which is to be rendered as Markdown. + """ + @spec escape(String.t()) :: String.t() def escape(text) do @markdown_chars |> Regex.replace(text, fn m -> diff --git a/lib/philomena/native.ex b/lib/philomena/native.ex index 04f700ab..89877030 100644 --- a/lib/philomena/native.ex +++ b/lib/philomena/native.ex @@ -1,10 +1,14 @@ defmodule Philomena.Native do + @moduledoc false + use Rustler, otp_app: :philomena - # Markdown + @spec markdown_to_html(String.t(), %{String.t() => String.t()}) :: String.t() def markdown_to_html(_text, _replacements), do: :erlang.nif_error(:nif_not_loaded) + + @spec markdown_to_html_unsafe(String.t(), %{String.t() => String.t()}) :: String.t() def markdown_to_html_unsafe(_text, _replacements), do: :erlang.nif_error(:nif_not_loaded) - # Camo + @spec camo_image_url(String.t()) :: String.t() def camo_image_url(_uri), do: :erlang.nif_error(:nif_not_loaded) end diff --git a/lib/philomena/scrapers/tumblr.ex b/lib/philomena/scrapers/tumblr.ex index ae87f7ae..8ce08999 100644 --- a/lib/philomena/scrapers/tumblr.ex +++ b/lib/philomena/scrapers/tumblr.ex @@ -22,7 +22,9 @@ defmodule Philomena.Scrapers.Tumblr do [post_id] = Regex.run(@url_regex, url, capture: :all_but_first) api_url = - "https://api.tumblr.com/v2/blog/#{uri.host}/posts/photo?id=#{post_id}&api_key=#{tumblr_api_key()}" + "https://api.tumblr.com/v2/blog/#{uri.host}/posts/photo?id=#{post_id}&api_key=#{ + tumblr_api_key() + }" Philomena.Http.get(api_url) |> json!() diff --git a/lib/philomena_web/controllers/image/description_controller.ex b/lib/philomena_web/controllers/image/description_controller.ex index 6fdfee7e..0d7f3442 100644 --- a/lib/philomena_web/controllers/image/description_controller.ex +++ b/lib/philomena_web/controllers/image/description_controller.ex @@ -34,8 +34,7 @@ defmodule PhilomenaWeb.Image.DescriptionController do Images.reindex_image(image) - body = - MarkdownRenderer.render_one(%{body: image.description}, conn) + body = MarkdownRenderer.render_one(%{body: image.description}, conn) conn |> put_view(PhilomenaWeb.ImageView) diff --git a/lib/philomena_web/controllers/profile_controller.ex b/lib/philomena_web/controllers/profile_controller.ex index d1d6f98b..456e68a9 100644 --- a/lib/philomena_web/controllers/profile_controller.ex +++ b/lib/philomena_web/controllers/profile_controller.ex @@ -134,11 +134,9 @@ defmodule PhilomenaWeb.ProfileController do |> MarkdownRenderer.render_collection(conn) |> Enum.zip(recent_comments) - about_me = - MarkdownRenderer.render_one(%{body: user.description || ""}, conn) + about_me = MarkdownRenderer.render_one(%{body: user.description || ""}, conn) - scratchpad = - MarkdownRenderer.render_one(%{body: user.scratchpad || ""}, conn) + scratchpad = MarkdownRenderer.render_one(%{body: user.scratchpad || ""}, conn) commission_information = commission_info(user.commission, conn) diff --git a/lib/philomena_web/controllers/tag_controller.ex b/lib/philomena_web/controllers/tag_controller.ex index 49049744..ce5a9b36 100644 --- a/lib/philomena_web/controllers/tag_controller.ex +++ b/lib/philomena_web/controllers/tag_controller.ex @@ -61,8 +61,7 @@ defmodule PhilomenaWeb.TagController do interactions = Interactions.user_interactions(images, user) - body = - MarkdownRenderer.render_one(%{body: tag.description || ""}, conn) + body = MarkdownRenderer.render_one(%{body: tag.description || ""}, conn) dnp_bodies = MarkdownRenderer.render_collection( diff --git a/lib/philomena_web/image_loader.ex b/lib/philomena_web/image_loader.ex index b037a75a..bfd7b6fd 100644 --- a/lib/philomena_web/image_loader.ex +++ b/lib/philomena_web/image_loader.ex @@ -139,8 +139,7 @@ defmodule PhilomenaWeb.ImageLoader do dnp_entries = Enum.zip(dnp_bodies, tag.dnp_entries) - description = - MarkdownRenderer.render_one(%{body: tag.description || ""}, conn) + description = MarkdownRenderer.render_one(%{body: tag.description || ""}, conn) [{tag, description, dnp_entries}] end diff --git a/lib/philomena_web/markdown_renderer.ex b/lib/philomena_web/markdown_renderer.ex index 593daea5..2b29729d 100644 --- a/lib/philomena_web/markdown_renderer.ex +++ b/lib/philomena_web/markdown_renderer.ex @@ -20,7 +20,7 @@ defmodule PhilomenaWeb.MarkdownRenderer do end) |> render_representations(conn) - Enum.map(collection, fn %{body: text} -> + Enum.map(collection, fn %{body: text} -> Markdown.to_html(text, representations) end) end diff --git a/native/philomena/README.md b/native/philomena/README.md index 803fb867..5070f1dc 100644 --- a/native/philomena/README.md +++ b/native/philomena/README.md @@ -1,4 +1,4 @@ -# NIF for Elixir.Philomena.Markdown +# NIF for Elixir.Philomena.Native ## To build the NIF module: @@ -9,8 +9,8 @@ ## To load the NIF: ```elixir -defmodule Philomena.Markdown do - use Rustler, otp_app: , crate: "philomena_markdown" +defmodule Philomena.Native do + use Rustler, otp_app: , crate: "philomena" # When your NIF is loaded, it will override this function. def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded) diff --git a/priv/repo/migrations/20210929181319_rename_body_fields.exs b/priv/repo/migrations/20210929181319_rename_body_fields.exs index ed9476a3..62e96e0a 100644 --- a/priv/repo/migrations/20210929181319_rename_body_fields.exs +++ b/priv/repo/migrations/20210929181319_rename_body_fields.exs @@ -20,7 +20,7 @@ defmodule Philomena.Repo.Migrations.RenameBodyFields do rename table("commission_items"), :add_ons, to: :add_ons_textile rename table("commission_items"), :description_md, to: :description rename table("commission_items"), :add_ons_md, to: :add_ons - + rename table("images"), :description, to: :description_textile rename table("images"), :scratchpad, to: :scratchpad_textile rename table("images"), :description_md, to: :description @@ -92,8 +92,14 @@ defmodule Philomena.Repo.Migrations.RenameBodyFields do execute("update images set description='' where description is null;") execute("update tags set description='' where description is null;") - execute("alter table images alter column description set default ''::character varying, alter column description set not null;") - execute("alter table tags alter column description set default ''::character varying, alter column description set not null;") + + execute( + "alter table images alter column description set default ''::character varying, alter column description set not null;" + ) + + execute( + "alter table tags alter column description set default ''::character varying, alter column description set not null;" + ) # Unneeded columns alter table("badges") do