{{ user.display_name }}
\nPassword reset token was generated: {{ passwordResetToken }}
\nYou can also use this link to reset password:\n {{ passwordResetLink }}\n
\n#{lines.value}
", attrs, [], lnb) + end + + ######## + # Html # + ######## + defp render_block(%Block.Html{html: html}, context) do + {context, html} + end + + defp render_block(%Block.HtmlComment{lines: lines}, context) do + {context, lines} + end + + defp render_block(%Block.HtmlOneline{html: html}, context) do + {context, html} + end + + ######### + # Ruler # + ######### + defp render_block(%Block.Ruler{lnb: lnb, attrs: attrs}, context) do + add_attrs(context, "#{body}" + add_attrs(context1, html, attrs, [], lnb) + end + + ######### + # Table # + ######### + + defp render_block( + %Block.Table{lnb: lnb, header: header, rows: rows, alignments: aligns, attrs: attrs}, + context + ) do + {context1, html} = add_attrs(context, "
]
+ lines = options.render_code.(block)
+ html = ~s[#{tag}#{lines}
]
+ add_attrs(context, html, attrs, [], lnb)
+ end
+
+ #########
+ # Lists #
+ #########
+
+ defp render_block(
+ %Block.List{lnb: lnb, type: type, blocks: items, attrs: attrs, start: start},
+ context
+ ) do
+ {context1, content} = render(items, context)
+ html = "<#{type}#{start}>#{content}#{type}>"
+ add_attrs(context1, html, attrs, [], lnb)
+ end
+
+ # format a single paragraph list item, and remove the para tags
+ defp render_block(
+ %Block.ListItem{lnb: lnb, blocks: blocks, spaced: false, attrs: attrs},
+ context
+ )
+ when length(blocks) == 1 do
+ {context1, content} = render(blocks, context)
+ content = Regex.replace(~r{?p>}, content, "")
+ html = "{:#{verbatim}}
"} + end + + #################### + # IDDef is ignored # + #################### + + defp render_block(%Block.IdDef{}, context), do: {context, ""} + + ##################################### + # And here are the inline renderers # + ##################################### + + defdelegate br, to: HtmlRenderer + defdelegate codespan(text), to: HtmlRenderer + defdelegate em(text), to: HtmlRenderer + defdelegate strong(text), to: HtmlRenderer + defdelegate strikethrough(text), to: HtmlRenderer + + defdelegate link(url, text), to: HtmlRenderer + defdelegate link(url, text, title), to: HtmlRenderer + + defdelegate image(path, alt, title), to: HtmlRenderer + + defdelegate footnote_link(ref, backref, number), to: HtmlRenderer + + # Table rows + defp add_trs(context, rows, tag, aligns, lnb) do + numbered_rows = + rows + |> Enum.zip(Stream.iterate(lnb, &(&1 + 1))) + + numbered_rows + |> Enum.reduce(context, fn {row, lnb}, ctx -> + append(add_tds(append(ctx, "Password reset token was generated: {{ passwordResetToken }}
\nYou can also use this link to reset password:\n {{ passwordResetLink }}\n
\nPassword reset token was generated: {{ passwordResetToken }}
\nYou can also use this link to reset password:\n {{ passwordResetLink }}\n
\n{{ $t('emoji.selectLocalPack') }}
\n{{ $t('emoji.specifyShortcode') }}
\n{{ $t('emoji.specifyFilename') }}
\nor
\n\n {{ $t('emoji.thisWillDownload') }} \"{{ name }}\" {{ $t('emoji.downloadToCurrentInstance') }}\n \"{{ downloadSharedAs.trim() === '' ? name : downloadSharedAs }}\" ({{ $t('emoji.canBeChanged') }}).\n {{ $t('emoji.willBeUsable') }}.\n
\n{{ $t('emoji.selectLocalPack') }}
\n{{ $t('emoji.specifyShortcode') }}
\n{{ $t('emoji.specifyFilename') }}
\nor
\n\n {{ $t('emoji.thisWillDownload') }} \"{{ name }}\" {{ $t('emoji.downloadToCurrentInstance') }}\n \"{{ downloadSharedAs.trim() === '' ? name : downloadSharedAs }}\" ({{ $t('emoji.canBeChanged') }}).\n {{ $t('emoji.willBeUsable') }}.\n
\nPassword reset token was generated: {{ passwordResetToken }}
\nYou can also use this link to reset password:\n {{ passwordResetLink }}\n
\n{{ this.$t('invites.token') }}: {{ newToken.token }}
\n{{ this.$t('invites.maxUse') }}: {{ newToken.maxUse }}
\n{{ this.$t('invites.expiresAt') }}: {{ newToken.expiresAt }}
\n{{ $t('invites.inviteViaEmailAlert') }}
\n{{ this.$t('invites.token') }}: {{ newToken.token }}
\n{{ this.$t('invites.maxUse') }}: {{ newToken.maxUse }}
\n{{ this.$t('invites.expiresAt') }}: {{ newToken.expiresAt }}
\n{{ $t('invites.inviteViaEmailAlert') }}
\nPassword reset token was generated: {{ passwordResetToken }}
\nYou can also use this link to reset password:\n {{ passwordResetLink }}\n
\n{{ $t('userProfile.noStatuses') }}
\nHello
World!
" + end + + test "raw HTML" do + code = ~s[OwO] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "#{code}
" + end + + test "rulers" do + code = ~s[before\n\n-----\n\nafter] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "before
after
" + end + + test "headings" do + code = ~s[# h1\n## h2\n### h3\n] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[" + end + + test "code" do + code = ~s[`mix`] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[whoms’t are you quoting?
mix
mix
puts "Hello World"
]
+ end
+
+ test "lists" do
+ code = ~s[- one\n- two\n- three\n- four]
+ result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer})
+ assert result == "#{code}
" + + code = ~s[*aaaa~*] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[aaaa~
] + + code = ~s[**aaaa~**] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[aaaa~
] + + # strikethrought + code = ~s[aaaa~
hello world!
\n\nsecond paragraph
" - expected = "hello world!
\n\nsecond paragraph
" + text = "hello world!
second paragraph
" + expected = "hello world!
second paragraph
" {output, [], []} = Utils.format_input(text, "text/html") @@ -99,14 +99,14 @@ test "works for bare text/html" do test "works for bare text/markdown" do text = "**hello world**" - expected = "hello world
\n" + expected = "hello world
" {output, [], []} = Utils.format_input(text, "text/markdown") assert output == expected text = "**hello world**\n\n*another paragraph*" - expected = "hello world
\nanother paragraph
\n" + expected = "hello world
another paragraph
" {output, [], []} = Utils.format_input(text, "text/markdown") @@ -118,7 +118,7 @@ test "works for bare text/markdown" do by someone """ - expected = "\ncool quote
\n
by someone
\n" + expected = "cool quote
by someone
" {output, [], []} = Utils.format_input(text, "text/markdown") @@ -134,7 +134,7 @@ test "works for bare text/bbcode" do assert output == expected text = "[b]hello world![/b]\n\nsecond paragraph!" - expected = "hello world!hello world
\nanother @user__test and @user__test google.com paragraph
\n) - {output, _, _} = Utils.format_input(text, "text/markdown") - assert output == expected + assert output == + ~s(hello world
another @user__test and @user__test google.com paragraph
) end end diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 57d0f4416..7efccd9c4 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -601,6 +601,8 @@ test "blocking / unblocking a user" do [valid_params: valid_params] end + clear_config([:instance, :account_activation_required]) + test "Account registration via Application", %{conn: conn} do conn = post(conn, "/api/v1/apps", %{ @@ -685,7 +687,7 @@ test "returns bad_request if missing required params", %{ assert json_response(res, 200) [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}] - |> Stream.zip(valid_params) + |> Stream.zip(Map.delete(valid_params, :email)) |> Enum.each(fn {ip, {attr, _}} -> res = conn @@ -697,6 +699,54 @@ test "returns bad_request if missing required params", %{ end) end + clear_config([:instance, :account_activation_required]) + + test "returns bad_request if missing email params when :account_activation_required is enabled", + %{conn: conn, valid_params: valid_params} do + Pleroma.Config.put([:instance, :account_activation_required], true) + + app_token = insert(:oauth_token, user: nil) + conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token) + + res = + conn + |> Map.put(:remote_ip, {127, 0, 0, 5}) + |> post("/api/v1/accounts", Map.delete(valid_params, :email)) + + assert json_response(res, 400) == %{"error" => "Missing parameters"} + + res = + conn + |> Map.put(:remote_ip, {127, 0, 0, 6}) + |> post("/api/v1/accounts", Map.put(valid_params, :email, "")) + + assert json_response(res, 400) == %{"error" => "{\"email\":[\"can't be blank\"]}"} + end + + test "allow registration without an email", %{conn: conn, valid_params: valid_params} do + app_token = insert(:oauth_token, user: nil) + conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token) + + res = + conn + |> Map.put(:remote_ip, {127, 0, 0, 7}) + |> post("/api/v1/accounts", Map.delete(valid_params, :email)) + + assert json_response(res, 200) + end + + test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do + app_token = insert(:oauth_token, user: nil) + conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token) + + res = + conn + |> Map.put(:remote_ip, {127, 0, 0, 8}) + |> post("/api/v1/accounts", Map.put(valid_params, :email, "")) + + assert json_response(res, 200) + end + test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do conn = put_req_header(conn, "authorization", "Bearer " <> "invalid-token") @@ -706,10 +756,6 @@ test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_ end describe "create account by app / rate limit" do - clear_config([Pleroma.Plugs.RemoteIp, :enabled]) do - Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true) - end - clear_config([:rate_limit, :app_account_creation]) do Pleroma.Config.put([:rate_limit, :app_account_creation], {10_000, 2}) end diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs index 2ce8f9fa3..2c999295a 100644 --- a/test/web/static_fe/static_fe_controller_test.exs +++ b/test/web/static_fe/static_fe_controller_test.exs @@ -110,6 +110,19 @@ test "single notice page", %{conn: conn} do assert html =~ "testing a thing!" end + test "filters HTML tags", %{conn: conn} do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => ""}) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{activity.id}") + + html = html_response(conn, 200) + assert html =~ ~s[<script>alert('xss')</script>] + end + test "shows the whole thread", %{conn: conn} do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "space: the final frontier"})